Я перерисовал ваш паттерн здесь, используя заглавные буквы для обозначения коммита ha sh Идентификаторы:
A------------F--------J <-- master
\ / /
B--C--D--E--G--H--I--K <-- apprentice
Обратите внимание, что имена ветвей - это метки, которые указывают на ( или прикрепить к) указать c совершает . Всякий раз, когда вы находитесь на ветке и делаете новый коммит, Git делает новый коммит таким образом, чтобы он указывал на старый кончик ветки, а затем настраивает ветку name так что он указывает на новый коммит, который теперь является новым tip-tip. Тот факт, что коммиты A
, F
и J
находятся в режиме «мастер», подразумевается тем фактом, что, начиная с фиксации слияния J
и возвращаясь к его первому родителю, вы получаете F
, который также является коммитом слияния. Возвращаясь к первому родителю F
, вы получаете коммит A
. Коммит A
является самым первым коммитом, поэтому у него вообще нет родителя, и процесс останавливается.
Команда git log
сделает эту работу в обратном направлении. Вам нужно --first-parent
, чтобы предотвратить , а также , работающее в обратном направлении с J
до I
.
Команды, которые производят этот шаблон, являются повседневными Git , Единственные особые случаи: * ie при первоначальном создании master
, затем при первоначальном создании apprentice
и выполнении слияния first :
$ git init # makes the new, empty repository
... create files and `git add` them all ...
$ git commit # creates commit `A` and thereby allows the name `master` to exist
На данный момент у вас есть:
A <-- master (HEAD)
в вашем хранилище. Затем:
git checkout -b apprentice
приводит к:
A <-- apprentice (HEAD), master
Теперь вы можете создавать коммит B
обычным способом: редактировать файлы в рабочем дереве, git add
их и и так далее, и запустить git commit
. Это создает коммит B
, который заставляет два имени ветви указывать на разные коммиты:
A <-- master
\
B <-- apprentice (HEAD)
Обратите внимание, как коммит B
указывает на существующий коммит A
, а Git установил новый коммит ha sh ID в имя apprentice
, к которому прикреплено специальное имя HEAD
.
Теперь вы создаете коммиты C-D-E
таким же (обычным, повседневным) способом:
A <-- master
\
B--C--D--E <-- apprentice (HEAD)
Чтобы сделать коммит F
, вы должны git checkout master
:
A <-- master (HEAD)
\
B--C--D--E <-- apprentice
(обратите внимание, как вы сейчас используете коммит A
вместо коммита E
и HEAD
теперь присоединен к master
), а затем git merge apprentice
. Однако в этот момент вы должны заставить Git не использовать тут что-то Git мгновенно.
Способ git merge
работает так, что он находит объединить базу коммит вашего текущего коммита A
и выбранного вами коммита E
. Это лучший общий коммит, который начинается с E
и работает в обратном направлении, начинается с A
и работает в обратном направлении одновременно. Но это, очевидно, сам коммит A
, который не только является общим, но и текущий коммит. На этом этапе git merge
будет использовать ярлык: на самом деле он не будет объединять вообще. Вместо этого он выполнит операцию fast-forward , которая не является слиянием. Это заставит имя master
просто указывать непосредственно на существующий коммит E
и проверить коммит E
. Это не то, что мы хотим: мы хотим заставить Git сделать фактический коммит слияния F
.
Для этого мы запускаем git merge --no-ff apprentice
. Git затем сравнивает снимок в A
, базовом коммите слияния, с снимком в A
, текущим коммитом. Это, конечно, не показывает никаких изменений. Затем Git сравнивает снимок в A
с снимком в E
. Git теперь объединяет два набора изменений - это объединение , конечно, тривиально - и применяет объединенные изменения к базовому снимку слияния в A
и делает наш новый коммит слияния F
:
A------------F <-- master (HEAD)
\ /
B--C--D--E <-- apprentice
Снимок в коммите F
совпадает с коммитом в E
. Но F
- это новый, другой коммит, один с двумя родителями: коммит слияния. Его два родителя - A
и E
, в этом порядке.
Теперь мы можем git checkout apprentice
и возобновить работу. Сделав еще три коммита, мы имеем:
A------------F <-- master
\ /
B--C--D--E--G--H--I <-- apprentice (HEAD)
Теперь мы можем git checkout master
, переместить HEAD
в master
и выбрать коммит F
, а затем запустить git merge apprentice
. Опция --no-ff
больше не требуется. Вы можете использовать его, если хотите; это не имеет никакого эффекта. база слияния из F
и I
является коммитом E
: один шаг назад, вдоль второго родителя, от F
достигает E
, и три шага назад, вдоль единственного родителя доступно каждый раз, с I
достигает E
. Коммит E
не является коммитом F
, поэтому у git merge
нет возможности выполнить быструю перемотку вместо слияния.
Git теперь будет отличаться E
против F
до посмотрите, что изменилось «вы» (опять ничего), а затем diff A
против I
, чтобы увидеть, что изменилось «они» (все ваши изменения, чтобы добраться до I
). Затем процесс слияния объединяет их - ничего с чем-то - и применяет объединенные изменения к снимку в базе слияния E
. Результат, конечно, такой же, как у снимка в I
, и Git делает новый коммит с двумя родителями J
, чтобы сохранить этот снимок и указать на существующие коммиты F
и I
:
A------------F--------J <-- master (HEAD)
\ / /
B--C--D--E--G--H--I <-- apprentice
Теперь вы можете git checkout apprentice
и продолжить работу. В любой момент вы повторяете «переключение на master
и слияние», чтобы сделать новый крупный коммит (слияние). Вы переключаетесь обратно на apprentice
, чтобы выполнять новые незначительные (ежедневные) коммиты, и шаблон продолжает расти.
На следующих нескольких рисунках мы можем опустить специальное имя HEAD
, так как мы просто ищем в общем хранилище, а не там, где вы собираетесь сделать свой следующий коммит. Со временем ваш репозиторий продолжает расти:
A------------F--------J <-- master
\ / /
B--C--D--E--G--H--I--K--L <-- apprentice
A------------F--------J-----M <-- master
\ / / /
B--C--D--E--G--H--I--K--L <-- apprentice
A------------F--------J-----M <-- master
\ / / /
B--C--D--E--G--H--I--K--L--N--O--P <-- apprentice
Шаблон "сшивания" здесь означает, что если вы выполните все коммиты - как git log
делает по умолчанию - из кончика master
, вы видите все, но если вы переходите только по ссылкам first-parent , как git log --first-parent
, вы видите только основные коммиты, каждая из которых является коммитом слияния (и затем на конец, начальный коммит A
).
Некоторым людям нравится делать абсолютно пустой начальный коммит. Если вы используете GitHub, они скорее поощряют первоначальный коммит, содержимое которого представляет собой файл README
и / или LICENSE
, что, как правило, является хорошим способом сделать что-либо.