Как стать автором
Обновить

Комментарии 39

проект(это обьединение файлов - настройка, это может быть какой угодно формат, обычно набор опций и перечисление файлов со сборкой) состоит из файлов, каждый файл имеет состояния, но ветвистость не знаю как делать, ветка со своей историей скорее всего форк(тоесть когда откатились и стали изменять в текущем состоянии то от чего отказались уже не нужно поидее, а если нужно нужно сделать форк поидее (что-то типо явный форк и неявный с такой же механикой когда пользователь пошел ветку делать неосознанно после отката )), у него другое развитие и начало от точки снятия форка - поидее.

имя проекта это (зеркало проекта, тоесть зеркало состояний файлов), условно можно назвать это базой данных(или валидные ссылки на валидные файлы), в 1 таблице история файлов, чтобы создать ветку надо снять состояние этой таблицы(я так представляю покачто) или если о состоянии каждого файла в проекте просто каким-то образом организовать это на том же SQLite3 например, тоесть зеркалить проект (сериализировать версиями в титульник(ну тоесть настроить зеркало либо пофайлово либо на проект) и читать последний наверно) или титульник с history на каждый файл в пределах проекта и логом наверно

Почему именно файлов? Чем файл именно так отличается от записи в БД, или от картинки в памяти графического редактора?

потомучто если отойти от наворотов то мы в 1 момент времени по законам физики в 1 файле физически, поэтому если представить такой редактор как вьювер 1 файла с возможностью последовательного сеанса, то то на то и выходит у каждого файла своя история, которая метаинформацией(тоесть по которой можно понять что и где в файле изменилось) дублируется в историю проекта, а в истории проекта лог просто формируется из состояний его файлов, где проект это его имя по-сути файлик и директория, а в нем история проекта ну и сам проект

ничем не отличается, просто была 1 картинка в проекте, стала другая, хистори посмотрели там показало например изменилась картинка

У меня сто тысяч процессов, в состоянии каждого из которых что-то меняется, и я хочу иметь возможность это что-то атомарно откатывать. Мне сто тысяч файлов создать?

Понимаю, что так конечно так проще, создавать снапшот всего состояния проекта. Но если таковая история сильно разрастётся для проекта, который сам по себе может быть не столь прост и при этом объёмен, то получится нехилая такая куча файлов очнь сильно сжирающая место на носителе. В принципе, в git примерно такая же схема, но она вроде как все равно разностная. Мне думается, что все таки более идеальным решением было бы делать именно сохранение разностей между состояними, с указанием от какого состояния берется разность. Тогда можно более менее просто пройти по графу и сделать либо откаты, либо слияния.
По сути, в такой схеме нужно нечно наподобии того, что делается при ужатии видеоряда видеокодеками:
Есть ключевые р кадры которые практически не ужимаются (или ужимаются с минимальной потерей информации на них), а к ним строятся разностные i кадры, которые по сути наборы векторов смещений тех ил ииных полей на кадре по отношению к предыдущему состоянию. Таким образом потом можно делать перемотки в почти любой момент времени. В такой схеме и место экономится, и есть некие стабильные состояния, к которым можно откатиться безболезненно

ключевые р кадры которые практически не ужимаются (или ужимаются с минимальной потерей информации на них), а к ним строятся разностные i кадры

Супер, спасибо! Эта мысль мне в голову не приходила. Прям спасибище.

Да, незачто! Я сам давно страдаю подобными задачами, только до реализации универсальных решений так и не дошел, увы. Но мысли остались. Чего бы не поделиться с теми, кто может их воплотить :)

В гите как раз слепки хранятся, по которым уже вычисляются разности. Вот в меркуриале уже хранятся разности. Поэтому второй умеет трекать перемещения файлов, а первый - нет. Но всё это подходы прошлого века. Сейчас имеет смысл смотреть в сторону CRDT.

Меня в CRDT отталкивает R, которая мне в принципе не нужна.

Ну и git умеет, конечно, перемещения.

Вы сейчас привели текст, озаглавленный «How does git detect renames?» в качестве доказательства того, что гит этого не умеет.

Если имелось в виду, что нет таковой сущности «перемещение» где-то внутри в потрохах — разумеется, она и не нужна. Если можно что-то вычислить — без потери общности можно считать, что оно есть.

Я в курсе, что среди теоретиков иногда принято проводить дискуссии о том, нгасколько меркуриал правильный, а гит — нет. Удивлен, что вы не даркс какой-нибудь в пример привели.

Если можно что-то вычислить — без потери общности можно считать, что оно есть.

В том-то и дело, что нельзя. В зависимости от погоды на Марсе есть вероятность как ложнопложительного, так ложноотрицательного срабатывания "вычисления". Про шаманства вокруг копирования файлов с сохранением истории я даже не заикаюсь.

В том-то и дело, что нельзя.

Если хорошо подумать, то мы выясним, что на современных файловых системах, которые позволяют собирать дерево каталогов из разных томов, операция перемещения не очень-то и определена. Если у вас дерево расположено на двух разных разделах, то mv будет работать как копирование, а потом удаление.

Я понимаю, что это теоретизирование, но вы первый начали.

Поэтому в системах контроля версий есть свои команды mv и cp, чтобы смотреть на намерения пользователя, а не просто получившийся в фс результат.

В системах контроля версий есть куча никому не нужных команд, потому что на одного умного человека, что-то сделавшего — всегда находится пятьсот занудных теоретиков, точно знающих, как правильно; и вот по их-то указкам и появляются все эти бессмысленные «всевакуумно правильные» штуки, типа строгой типизации и команд «mv»/«cp».

Намерения пользователя — это бессмысленный артефакт. Да, человечество изобрело тикеты, системы контроля тикетов и системы контроля систем контроля тикетов. Тем не менее, единственным источником правды — был, есть и будет оставаться всегда — исходный код.

Если файл перенесли из «controllers» во «views» — значит, старый файл именно что удалили там и создали новый файл здесь. Человек, набравшийся смелости перенести — и должен нести за новый файл ответственность (посредством откликов на mailto: `git blame`.

Нет такого понятия «перенос функции». В API функция либо есть, либо её нет. Так же и тут.

Они для удобства.

Вообще‐то хранение слепков/разностей и возможность сохранения информации о перемещении файлов несколько ортогональны. Git вполне мог сохранять информацию о факте перемещения отдельно от самих слепков. Более того, hg-git отлично сохраняет куда‐то информацию о перемещениях. Я у себя проверил вот таким скриптом:

#!/bin/sh

set -e

TGT=$1 ; shift

if test "$TGT" = "--help" ; then
    echo "Usage: $0 NEWDIR"
    exit 1
fi

mkdir -p "$TGT"
cd "$TGT"
for dir in mv nomv ; do
    hg init "$dir"
    cd "$dir"
        echo abc > a
        hg add a
        hg commit -m 'Add a' --date=2000-01-01 --user=test
        hg rm a
        echo def > b
        hg add b
        if test "$dir" = mv ; then
            hg mv -A a b
        fi
        hg commit -m 'Move a to b' --date=2000-01-01 --user=test
        hg bookmark master
        hg gexport

        cd .hg/git
            git fast-export master > "../../../$dir-fe"
            git bundle create "../../../$dir-bundle" master
            git log > "../../../$dir-log"
        cd ../..
    cd ..
done

Разница в том, что оказалось в git видна только в файлах $dir-bundle, $dir-log содержит только различающиеся хэши второго commit’а, а $dir-fe идентичны, так что как достать эту информацию из git и как она вообще сохраняется мне не очевидно. Возможно, hg-git просто нашёл какой‐то способ хранения своих метаданных и git понятия не имеет, что они означают. Код [hg-]git я сейчас как‐то смотреть не хочу. Но факт тот, что в git вполне возможно сохранять информацию о переименованиях, хранение слепков этому не мешает.

Ваша ссылка относится к commit’ам, сделаным в git. У меня commit’ы, сделанные в mercurial, в которых уже есть информация о переименованиях и она должна быть частью commit’а и на стороне mercurial, и на стороне git, чтобы hg-git вообще работал: хэши commit’ов должны сохраняться при конвертации в любую сторону, чтобы можно было нормально работать, а не постоянно решать проблемы рассинхронизации. Можете проверить, например, так:

Скрипт
#!/bin/sh

set -e
# set -x

TGT=$1 ; shift

if test "$TGT" = "--help" ; then
    echo "Usage: $0 NEWDIR"
    exit 1
fi

export SCREENDIR="$(realpath "$TGT")/screen"

mkdir -p "$TGT"
mkdir -m 0700 -p "$SCREENDIR"
cd "$TGT"
for dir in mv nomv ; do
    hg init "$dir"
    cd "$dir"
        echo abc > a
        hg add a
        hg commit -m 'Add a' --date=2000-01-01 --user=test
        hg rm a
        echo def > b
        hg add b
        if test "$dir" = mv ; then
            hg mv -A a b
        fi
        hg commit -m 'Move a to b' --date=2000-01-01 --user=test
        hg bookmark master
        hg gexport

        cd .hg/git
            git fast-export master > "../../../$dir-fe"
            git bundle create "../../../$dir-bundle" master
            git log > "../../../$dir-log"
            screen -d -m -S git-daemon git daemon --export-all --base-path=.
            hg clone "git://127.0.0.1/" "../../../$dir-clone"
            screen -X quit
        cd ../..

        hg log --follow b > "../$dir-log-follow"
    cd ..
    cd "$dir-clone"
        hg log --follow b > "../$dir-clone-log-follow"
    cd ..
    diff --report-identical-files "$dir-log-follow" "$dir-clone-log-follow"
done

Здесь я клонировал репозиторий git «по сети» и сравнил вывод hg log --follow оригинального и склонированного репозитория. Они совпадают.

Такие костыли я видел. Они хорошо заметны, и они точно не могут привести к идентичным $dir-fe (выводам git fast-export).

И в любом случае, напомню, что ветка началась с того, что я указал возможность сохранения информации о перемещениях в СКВ, хранящей слепки, и hg-git — это просто пример хранения этой информации в СКВ, хранящей слепки. Какими костылями он это делает неважно — это просто пример, что это сделать возможно, соответственно из «в гите как раз слепки хранятся» не может следовать, что гит «не умеет трекать перемещения» — он не умеет трекать перемещения, потому что его авторы решили не добавлять сохранение перемещений, а не потому что он хранит слепки.

Сразу дам disclaimer, что целостной картинки в голове у меня по этому вопросу нет, так, отдельные мысли.

Но вообще undo/redo, даже с описанными в статье улучшениями, - это некий паллиатив. Полноценный функционал - это полная история изменений состояния документа/сущности/whatever с возможностью откатиться на любой этап, удалить/скорректировать индивидуально любое предшествующее действие, не затрагивая остальные, поменять их порядок и т.п.

Из прикладного софта, с которым я активно работаю, такое делает Autodesk Fusion (с оговорками, но это нюансы предметной области) - внизу экрана лента всех совершённых действий, пользователь может передвинуть ползунок на любое место в истории, скорректировать, навсегда удалить или временно suppress'нуть любое действие, а также вставить новое действие не в конец истории, а в любое место. При этом scope этой ленты изменений зависит от выбранного в дереве проекта уровня иерархии (для всего проекта или отдельного компонента любого уровня вложенности). Если правка истории приводит к конфликту - затронутые им последующие действия выполняются только в части, в которой это возможно, и подсвечиваются, чтобы пользователь мог вмешаться и скорректировать и их тоже, устранив конфликт.

Архитектурно тут напрашивается event sourcing. Если у нас текущий стейт объекта/сущности/актора/т.п. определяется последовательностью полученных сообщений/команд/выполненных методов/т.п., то достаточно где-то организовать persistence этой последовательности, чтобы можно было при любых правках пересчитать стейт заново. А вот вопрос с разрешением конфликтов гораздо более сложный, но тут вряд ли какие-то универсальные рецепты есть, всё от предметной области зависит. Если речь про редактирование текста - это одно, а в том же Fusion всё куда замысловатее.

P.S. Впрочем, в описанном мной варианте всё равно сохраняется простой undo/redo на неком метауровне, что ли. Скажем, мы совершили действия A -> B -> C -> D, а потом скорректировали действие B и удалили действие C. Вот эти корректировку и удаление мы тоже должны иметь возможность откатить, но при этом для пользователя в ленте событий не появится действия E "корректировка B" и действия F "удаление C", иначе это будет каша, а не лента изменений (хотя внутри своего журнала событий мы, безусловно, будем сохранять именно что два новых события). Но в интерфейсе для пользователя для таких откатов у нас остаётся только простая кнопочка undo. И если мы при этом захотим корректировку действия B откатить, а сделанное позже удаление C - не откатывать, у нас проблема. Сделать-то мы это можем, но представить пользователю адекватный интерфейс для такого сценария проблематично.

вставить новое действие не в конец истории, а в любое место

А можете чуть подробнее рассказать, зачем?

event sourcing

Ага, до этого я тоже дошел, спасибо, что подтвердили.

речь про редактирование текста

Нет, конечно, с текстом можно буквально посимвольно работать.

представить пользователю адекватный интерфейс для такого сценария проблематично

20 лет назад мы это подкостылили специальной кнопочкой «объединить вот эти пять действий в одно и переписать историю», я это упоминал в тексте. Кроме того, в моей текущей задаче пользователя, как такового, нет.

———

Огромное спасибо, что не поленились и не прошли мимо.

Я не работаю с текстами, напишите в микрософт.

Я и не с вами разговариваю, расслабьтесь.

А можете чуть подробнее рассказать, зачем?

Ну это от предметной области, опять же, зависит.

Вот в том же Fusion я моделирую некую детальку, и сначала, допустим, "вытягиваю" из 2D-чертежа трёхмерное тело, а потом его размножаю в нескольких экземплярах, это у меня отдельные действия. А потом соображаю, что перед размножением надо было в нём ещё отверстие исполнить - и вставляю это действие после вытягивания, но до размножения, чтобы все копии оказались тоже с отверстиями (вообще так не надо делать, но тут не про лучшие практики 3D-моделирования, просто пример получается понятный).

Или возьмём Excel, где этого нет, а не помешало бы. Я забиваю какую-то формулу в ячейку, а потом копирую её вниз на следующие ячейки в столбце. Excel "размножает" формулу, корректируя ссылки. А потом я исходную формулу хочу поменять, а ещё покрасить ячейку зелёным - но все скопированные останутся неизменными и белыми, и мне придётся их заново копировать или менять вручную. А была бы такая фича - они бы обновились и покрасились автоматически.

Кроме того, в моей текущей задаче пользователя, как такового, нет.

Ну тогда мой комментарий в существенной степени нерелевантен. Меня на мысли навёл пример с UML-редактором, который приведён в статье, а о текущей Вашей задаче я не осведомлён. Если не надо придумывать пользовательский интерфейс хитрой отмены/корректировки/возврата действий - задача изрядно упрощается.

Ага, кристально ясно, спасибо!

А это не переусложняет интерфейс?

Простой интерфейс поверх сложного ядра сделать куда как проще, чем наоборот.

Пользовательский интерфейс придумывать всё равно надо: кнопки и вызовы API — изоморфны же.

Спасибо, такие мысли были, но для реализации не было времени. Да и не запутает ли это людей?

Ну гит же как-то не запутал: люди выучили pull+commit+push и живут припеваючи, а раз в триста лет идут читать документацию по бисекту.

Хороший интерфейс может решить часть проблем (показывать только фиксированные точки отката, например). Кроме того, всегда есть «генеральная линия партии» — где Undo и Redo прибиты гвоздями к ожидаемому поведению.

Вместо git советую посмотреть на fossil.
Тем более что он на базе SQLite создан. Так что можно брать и пользовать напрямую - тут и база для хранения и все в одни файле в отличии от git-a где сам черт ногу сломит

Спасибо!

Я и на darcs и на fossil, и на многое разное другое — уже смотрел; моя основная проблема не в том, как хранить диффы, а в том, что считать диффом. В отличие от любой VCS/SCM — у меня в общем случае нет «пользователя», который нажмет кнопку.

У вас вроде errand/elixir и 100к процессов состояние которых надо хранить.
Как правило состояние будет изменяться при обработке сообщения. Как уже писали выше - чистый event soursing. Типа пишем все события в лог и потом проигрываем :) при этом продолжая писать события в лог:) «вот дом, который построили Джек …»

Event sourcing линейный. Мне нужны мёрджи, которые чистый event sourcing не подарит.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации