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

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

Готово. Теперь состояние проекта на ветке alpha в точности такое же, как на ветке beta.

либо это делается простым reset на ветку beta.
либо правильнее написать «состояние проекта на ветке alpha содержит изменения ветки beta»

Если содержит, то включает ли получившийся коммит (Synced with branch beta) изменения «Added Linux Kernel» или нет?

reset переместит ветку alpha на тот же коммит, где находится ветка beta, при этом мы теряем все уникальные коммиты ветки alpha — в большинстве случаев это и требуется. Но в моем абстрактном примере ветки alpha и beta топологически остаются независимы.

Если говорить об изменениях, то можно сказать так: этот новый коммит ревертнул все уникальные изменения на ветке alpha и добавил все уникальные изменения из ветки beta (включая изменения коммита «Added Linux kernel»).
Спасибо. теперь понятнее, да и остальные примеры стали яснее по механике.

Спасибо. Я пока только читаю pro git, но и ваша статья очень кстати, правда я понял только самый первый случай про копирование дерева, а остальное пока для меня магия.

Синхронизация с другой веткой

Можно сделать проще, командами checkout, merge и branch:
$ git checkout target -b merge      # сделать ветку merge из целевой ветки target
$ git merge -s ours source          # смёржить в ветку merge исходную ветку source
                                    # игнорируя её содержимое
$ git branch -d source              # удалить ветку source
$ git branch -m source              # переименовать ветку merge в source
Нет, это уже будет другой способ синхронизации веток. Если выполнить эти команды вместо моей.

git checkout origin/beta -b merge
git merge -s ours --no-edit alpha
git branch -D alpha
git branch -m alpha

То получим так:
Скрытый текст


Да, ветка alpha теперь содержит дерево из ветки beta, но возник мердж коммит — ветка alpha теперь прямой потомок ветки beta. Если все-таки нужен мердж коммит, то я бы рекомендовал сделать так, чтобы ветка alpha была прямым потомком своих родных коммитов. Это легко делается как я описал тут:
git merge --ff $(git commit-tree beta^{tree} -m "Merge 'beta' into 'alpha', but take 'beta' tree" -p HEAD -p origin/beta)

Результат:
Скрытый текст


И для сравнения, как было сделано в самом примере
git merge --ff $(git commit-tree origin/beta^{tree} -m "Synced with branch 'beta'" -p HEAD)

Либо при наличии алиаса
git copy origin/beta

Результат:
Скрытый текст


Спасибо за описание подобных техник! На работе сейчас как частые откаты, реверсы и ребейзы, подчерпнул из статьи полезные для себя штуки, о которых даже не догадывался ранее. Завтра же опробую все на проекте.
Синхронизация с другой веткой

А зачем лезть в недра гита и вручную модифицировать коммиты и деревья?
Разве не проще сделать через reset?
git checkout -b temp
git reset --hard origin/beta
git reset --soft alpha
git commit
git checkout alpha
git merge --ff temp

Команд чуть больше, но все они в семантике git, без служебных команд.
Да, так тоже можно сделать. Но это не очевидно, на самом деле. Не все точно представляют, как работают разные опции reset. А если человек знает, что состояние проекта в коммите — это всего лишь дерево, то такая задача становится тривиальной.
Для меня, наоборот, reset — базовая функция гита, перенос указателя ветки на другой коммит в графе состояний — тривиальна и очевидна.
если человек знает, что состояние проекта в коммите — это всего лишь дерево

Знать, что коммит — это снимок состояния рабочей директории нужно и для reset. Это вообще одна из основы git. Но что бы воспользоваться rests, этих знаний достаточно, а вот что бы вручную создать коммит служебными утилитами, нужно ещё и знать о внутреннем устройстве, что излишне для нормальной работы с git.
Можно даже проще, без hard reset:
git checkout -B temp origin/beta ## создаём новую ветку на beta 
git reset --soft alpha ## перемещаем указатель новой ветки на alpha, не изменяя содержимое рабочей директории
git commit ## коммитим  ветку на alpha с рабочей директорией от beta
git checkout alpha
git merge --ff temp 
А если использовать stash, то можно даже не создавать временную ветку.

git checkout --detach origin/beta
git reset --soft alpha
git stash
git checkout alpha
git stash pop --index
git commit -m "Synced with beta"

коммит — это снимок состояния рабочей директории
Именно поэтому когда впервые возникла такая необходимость я подумал: значит, должен существовать прямой и элегантный способ копирования этого снимка. И теперь я поделился этим способом со всеми читателями хабра.
Точно, у checkout же detach есть. Тогда ещё проще и без stash.
git checkout --detach origin/beta
git reset --soft alpha
git checkout -B alpha
git commit
И правда, stash был лишним. Получилось всего 4 команды, это здорово. Но все равно это было головоломкой )

Статья полезная в плане понимания, как git работает на более низком уровне, чем обычный пользовательский UI (CLI). Но с практической частью я совсем не согласен – все описанные задачи можно решать обычными, высокоуровневыми командами, т.е. более понятно и наглядно.


1. Синхронизация с другой веткой

Низкоуровневые операции с tree почти всегда можно заменить на обычные манипуляции с ветками. Человеческая версия:


$ git reset --hard origin/beta
$ git reset --soft @{1}
$ git commit

2. Сравнение двух веток

Не понял, зачем это, если сравнить можно просто git diff alpha origin/beta


3. Реверт ветки

Аналогично п. 1. Но вообще это странное желание смешивать в кучу разные изменения, я бы всегда предпочёл наглядную историю с отдельными ревертами:


@ git revert --no-edit @~2..

– сделает все реверт-коммиты в правильном порядке за один вызов


4. Частичный реверт

Всё это делает просто checkout с коммитом и путём:


$ git checkout 7a714bf -- Bill.txt
$ git commit

5. Искуственный merge

Аналогично п. 1


$ git merge -X ours alpha
$ git reset --hard alpha
$ git reset --soft @{1}
$ git commit --amend --no-edit

На практике я бы такое никогда не использовал:


  • в master могут быть и другие полезные изменения, которые можно не заметить и случайно уничтожить
  • merge коммиты лучше оставлять "чистыми" – только разрешение конфликтов и никаких логических изменений
  • лучше просто сделать revert этого изменения перед merge – история будет более понятной и наглядной

6a. Метод rebase через merge — описание

Ну что сказать, хреновый workflow, который заставляет всегда делать rebase. Когда конфликты в ветке "фантомные", это хороший пример, когда нужно делать merge и избежать всех недостатков. Главный из которых – несобирающиеся промежуточные версии.

Спасибо за полезный комментарий. Я согласен, что это здорово — мастерски владеть стандартными командами (git reset, git checkout ...) и собирать из них крутые комбо. Но чтобы стать таким мастером, нужно отличное понимание внутренней структуры гита. Я написал статью именно для этого, мне хотелось поделиться своим опытом и видением. Если человек хочет понимать гит, то ему надо понимать, что коммиты — это деревья, но не изменения. И на примере git commit-tree я хочу дать это понимание.
Согласен, это одна из самых важных черт дизайна git. Разобраться со всеми ними не так и сложно, а уровень понимания всей системы и доступные возможности это поднимает колоссально.
Но чтобы стать таким мастером, нужно отличное понимание внутренней структуры гита.

В том-то и дело, что для этого достаточно понимать, что ветка — всего лишь указатель на коммит, коммит — это снимок состояния рабочей директории, а сам репозиторий — граф состояний. А вот знать, что внутри там есть ещё записи tree, например, совершенно не нужно. Git на удивление хорошо скрывает свою внутреннюю реализацию и его абстракции не протекают.
2. Сравнение двух веток
Не понял, зачем это, если сравнить можно просто git diff alpha origin/beta
Например, можно запушать специальную ветку с этим коммитом, и все сразу увидят этот diff.

Why this article is in top for all my queries? I wanted to find my favorite one, but this one always pop-ups...

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

Публикации