Comments 20
Готово. Теперь состояние проекта на ветке alpha в точности такое же, как на ветке beta.
либо это делается простым reset на ветку beta.
либо правильнее написать «состояние проекта на ветке alpha содержит изменения ветки beta»
Если содержит, то включает ли получившийся коммит (Synced with branch beta) изменения «Added Linux Kernel» или нет?
Если говорить об изменениях, то можно сказать так: этот новый коммит ревертнул все уникальные изменения на ветке 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
То получим так:
![](https://habrastorage.org/webt/pl/gh/82/plgh82rldwfli5rfric7locuk3m.png)
Да, ветка 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)
Результат:
![](https://habrastorage.org/webt/mm/3v/wv/mm3vwvjyowrwl4de5gorsd1b-t4.png)
И для сравнения, как было сделано в самом примере
git merge --ff $(git commit-tree origin/beta^{tree} -m "Synced with branch 'beta'" -p HEAD)
Либо при наличии алиаса
git copy origin/beta
Результат:
![](https://habrastorage.org/webt/g-/it/uj/g-ituj-t_jt5vnbjlzvpak9ow80.png)
Синхронизация с другой веткой
А зачем лезть в недра гита и вручную модифицировать коммиты и деревья?
Разве не проще сделать через 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. Это вообще одна из основы git. Но что бы воспользоваться rests, этих знаний достаточно, а вот что бы вручную создать коммит служебными утилитами, нужно ещё и знать о внутреннем устройстве, что излишне для нормальной работы с git.
git checkout -B temp origin/beta ## создаём новую ветку на beta
git reset --soft alpha ## перемещаем указатель новой ветки на alpha, не изменяя содержимое рабочей директории
git commit ## коммитим ветку на alpha с рабочей директорией от beta
git checkout alpha
git merge --ff temp
git checkout --detach origin/beta
git reset --soft alpha
git stash
git checkout alpha
git stash pop --index
git commit -m "Synced with beta"
коммит — это снимок состояния рабочей директорииИменно поэтому когда впервые возникла такая необходимость я подумал: значит, должен существовать прямой и элегантный способ копирования этого снимка. И теперь я поделился этим способом со всеми читателями хабра.
Статья полезная в плане понимания, как 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 и избежать всех недостатков. Главный из которых – несобирающиеся промежуточные версии.
Но чтобы стать таким мастером, нужно отличное понимание внутренней структуры гита.
В том-то и дело, что для этого достаточно понимать, что ветка — всего лишь указатель на коммит, коммит — это снимок состояния рабочей директории, а сам репозиторий — граф состояний. А вот знать, что внутри там есть ещё записи tree, например, совершенно не нужно. Git на удивление хорошо скрывает свою внутреннюю реализацию и его абстракции не протекают.
2. Сравнение двух ветокНапример, можно запушать специальную ветку с этим коммитом, и все сразу увидят этот diff.
Не понял, зачем это, если сравнить можно просто git diff alpha origin/beta
Why this article is in top for all my queries? I wanted to find my favorite one, but this one always pop-ups...
Как и зачем красть деревья в git