Во время выполнения
git merge
— можно указать опцию --no-ff
, что бы гит сохранил историю коммитов в feature-бранче (или девелоп-бранче, кому как удобнее называть).
Рассмотрим пример.
Создаём каталог:
[simterm]
$ mkdir testrepo
[/simterm]
Создаём в нём репозиторий:
[simterm]
$ cd testrepo/ && git init . Initialized empty Git repository in /home/setevoy/Temp/testrepo/.git/
[/simterm]
Создаём и добавляем тестовый файл:
[simterm]
$ touch testfile $ git add testfile $ git commit -m "testfile added to master" [master (root-commit) 6bb0bd5] testfile added to master 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 testfile
[/simterm]
Проверяем:
[simterm]
$ git log --pretty=oneline --abbrev-commit 6bb0bd5 (HEAD -> master) testfile added to master=
[/simterm]
Создаём новый тестовый бранч testbranch:
[simterm]
$ git checkout -b testbranch Switched to a new branch 'testbranch'
[/simterm]
Вносим изменения в файл:
[simterm]
$ echo testchanges > testfile
[/simterm]
Коммитим их:
[simterm]
$ git add testfile $ git commit -m "testchanges" [testbranch 1901bcb] testchanges 1 file changed, 1 insertion(+)
[/simterm]
Проверяем:
[simterm]
$ git log --pretty=oneline --abbrev-commit 1901bcb (HEAD -> testbranch) testchanges 6bb0bd5 (master) testfile added to master
[/simterm]
Переключаемся на master:
[simterm]
$ git checkout master Switched to branch 'master'
[/simterm]
Мерджим без --no-ff
:
[simterm]
$ git merge testbranch Updating 6bb0bd5..1901bcb Fast-forward testfile | 1 + 1 file changed, 1 insertion(+)
[/simterm]
Вот оно — Fast-forward.
Проверяем историю:
[simterm]
$ git log --pretty=oneline --abbrev-commit 1901bcb (HEAD -> master, testbranch) testchanges 6bb0bd5 testfile added to master
[/simterm]
Т.е. HEAD
будет перенесён напрямую с master на master, и вся история коммитов будет иметь линейный вид, как будто все изменения выполнялись в одном бранче.
ОК, возвращаемся к тестовому бранчу:
[simterm]
$ git checkout testbranch Switched to branch 'testbranch'
[/simterm]
Вносим новые изменения:
[simterm]
$ echo newtestchanges >> testfile
[/simterm]
Коммитим:
[simterm]
$ git add testfile $ git commit -m "new testchanges" [testbranch 93e7773] new testchanges 1 file changed, 1 insertion(+)
[/simterm]
Переключаемся на master:
[simterm]
$ git checkout master Switched to branch 'master'
[/simterm]
И мержим без Fast Forward — добавляем --no-ff
:
[simterm]
$ git merge testbranch --no-ff Merge made by the 'recursive' strategy. testfile | 1 + 1 file changed, 1 insertion(+)
[/simterm]
Проверяем:
[simterm]
$ git log --pretty=oneline --abbrev-commit 2c4bab0 (HEAD -> master) Merge branch 'testbranch' 93e7773 (testbranch) new testchanges 1901bcb testchanges 6bb0bd5 testfile added to master
[/simterm]
Теперь в мастере есть отдельный объект коммита для new testchanges из testbranch, и отдельный объект коммита для самого слияния.
Наглядно можно посмотреть с помощью опции --graph
:
[simterm]
$ git log --graph --oneline --all * 2c4bab0 (HEAD -> master) Merge branch 'testbranch' |\ | * 93e7773 (testbranch) new testchanges |/ * 1901bcb testchanges * 6bb0bd5 testfile added to master
Но: git
применит fast forward только в том случае, если в master за время работы над testbranch не было изменений.
Переключаемся на testbranch:
[simterm]
$ git checkout testbranch Switched to branch 'testbranch'
[/simterm]
Вносим изменения:
[simterm]
$ echo "testchanges from testbrahc" >> testfile
[/simterm]
Коммитим изменения:
[simterm]
$ git add testfile $ git commit -m "new testchanges from testbrach" [testbranch 1ee6e60] new testchanges from testbrach 1 file changed, 1 insertion(+)
[/simterm]
Переключаемся на мастер:
[simterm]
$ git checkout master Switched to branch 'master'
[/simterm]
Вносим изменеия тут:
[simterm]
$ git commit -m "new testchanges from master" On branch master nothing to commit, working tree clean
[/simterm]
Коммитим их:
[simterm]
$ git add testfile $ git commit -m "new testchanges from master" On branch master nothing to commit, working tree clean
[/simterm]
Мерджим testbranch в master без --no-ff
:
[simterm]
$ git merge -m "testbrahc merge to master" testbranch Merge made by the 'recursive' strategy. testfile | 1 + 1 file changed, 1 insertion(+)
Merge made by the ‘recursive’ strategy.
[/simterm]
Проверяем дерево:
[simterm]
$ !671 git log --graph --oneline --all * 5318384 (HEAD -> master) testbrahc merge to master |\ | * 1ee6e60 (testbranch) new testchanges from testbrach * | 2c4bab0 Merge branch 'testbranch' |\ \ | |/ | * 93e7773 new testchanges |/ * 1901bcb testchanges * 6bb0bd5 testfile added to master
Теперь изменения бранча testbranch идут в отдельной ветке даже без указания no fast-forward
.
Т.е., основная идея в использовании fast forward git
-ом — это «не плодить лишних сущностей», если в этом нет необходимости (верный последователь принципа Бритвы Оккама): если в мастер не было изменений, то при мердже из девелопа все коммиты будут в ветке мастера.
Если же изменения были в мастере, и в девелоп-ветке — то git
сохранит две отдельных линии коммитов.