GIT: манипуляция коммитами


git merge

git merge COMMIT — команда, которая ищет общего предка у ГОЛОВЫ и COMMIT и переводит ГОЛОВУ в состояние репозитория, в котором есть изменения как из головного коммита, так и из COMMIT, то есть осуществляет слияние двух наборов изменений. См git-merge(1), там картинка.

Если COMMIT является потомком головного, то для слияния не нужно никаких изысков: сам COMMIT является результатом слияния, достаточно переписать ГОЛОВУ на коммит. См. раздел FAST-FORWARD MERGE.

Если же COMMIT не является потомком ГОЛОВЫ, происходит true merge.


True merge

См. раздел TRUE MERGE из git-merge(1) и разделы ниже.

True merge создаёт коммит (merge commit), у которого два родительских: COMMIT и головной. Этот коммит представляет состояние репозитория, в котором есть изменения и из COMMIT, и из головного.

Иногда изменения конфликтуют и сделать merge commit автоматически не получается, и пользователь должен руками разрешить конфликты. В таком случае создаётся указатель MERGE_HEAD, который как HEAD, но указывает на COMMIT. Файлы, в которых возникли конфликты, записываются в нескольких копиях в index с особыми stage number-ами (1 — общий предок, 2 — HEAD, 3 — MERGE_HEAD), а в рабочем дереве лежат смешанные вариации на тему.

git merge --continue создаёт merge commit после того, как в index добавлены файлы с 0 как stage number.


git rebase

git rebase — команда, осуществляющая смену родительского коммита для набора изменений. См. git-rebase(1), там картинка.

Пусть головной коммит — и вызвана команда git rebase . Команда найдёт ближайшего общего предка у и — пусть это . Затем команда будет проходиться по пути от к и пытаться последовательно merge-ить изменения в каждом из коммитов по пути к . Так выстроится новая цепочка, идентичная цепочке от к , но начинающаяся с .

Можно достраивать цепочку не к , а к произвольному , используя флаг --onto.


git rebase -i

См. INTERACTIVE MODE страницы git-rebase(i).

git rebase -i (--interactive) позволяет не просто перенести цепочку от к на , но и модифицировать эту цепочку на лету.

Откроется текстовый редактор, где можно выбрать, что делать с каждым коммитом в цепочке: отредактировать, изменить commit-сообщение, сделать его изменения частью идущего перед ним коммита, вообще пропустить…

git rebase -i кроет великую мощь, не пренебрегайте ей.


git cherry-pick

git cherry-pick — команда, которая применяет наборы изменений из перечисленных коммитов к ГОЛОВЕ.

Например, git cherry-pick КОММИТ1 КОММИТ2 создаст два новых коммита:

  • КОММИТ1', аналог применения изменений из КОММИТ1
  • к ГОЛОВЕ, и КОММИТ2', аналог применения изменений из КОММИТ2 к КОММИТ1’. ГОЛОВА станет КОММИТ2.

git reflog

git reflog не создаёт изменения, а спасает от уже сделанных. reflog хранит сведения о том, на что указывали HEAD и ветки в последнее время. Со временем эта информация удаляется1.

В случае, например, неудачного git rebase относительно можно посмотреть в git reflog, какой коммит недавно был головным. Один из них будет указывать на .


Куда деваются ненужные вещи?

Пусть мы сделали git rebase из . Это создало коммит с предком . Если мы верим в это действие, нам больше не нужен.

Когда и как ненужны вещи исчезают?

  1. git-gc(1) — команда, которая время от времени исполняется сама по себе и удаляет ненужные объекты. Ненужные — те, которые не упоминаются в reflog, до которых нельзя дойти по родительским ссылкам от веток, тегов, HEAD и index.