Git Rebase Conflict: кто такой HEAD?
У меня есть этот проект, где удаленное репо имеет основную ветку разработки, и у меня есть форк, содержащий экспериментальную ветку. Я обязан rebase
переключается с ветки разработки на мою экспериментальную ветку, прежде чем я перейду к своей ветке. Итак, это выглядит так:
git checkout experimentalbranch
git fetch remoterepo
git rebase remoterepo/developmentbranch
К этому времени я столкнулся с конфликтами. Тем не менее, я не знаком ни с одним из этих изменений (я переоцениваю изменения за несколько недель, потому что они не слили мои изменения сразу). Кроме того, я впервые делаю rebase
, Я больше привык merge
,
В соединении это обычно <<LOCAL||REMOTE>>
за merge
, что звучит очень интуитивно. Но в rebase
, его <<HEAD||COMMIT MESSAGE>>
, Кто HEAD
? Это HEAD
развития отрасли? Это последний код в ветке разработки или где-то еще?
2 ответа
TL;DR (добавлено в мае 2018 г.)
Все это, по крайней мере, немного сбивает с толку, потому что Git позволяет понять всю его внутреннюю работу.
Обратите внимание, что случаи, которые нас интересуют, возникают при запуске:
git checkout somebranch; git rebase origin/their-branch
или похожие. Перебазирование временно приостановлено, чтобы заставить вас разрешить конфликт слияния, после чего вы должны git add
разрешенный конфликт и бежать git rebase --continue
, (Если вы используете какой-либо инструмент слияния с git mergetool
или причудливый интерфейс с графическим интерфейсом, этот интерфейс может сделать некоторые или все это для вас каким-то другим способом, но под git add
в разрешенных файлах и работает git rebase --continue
.)
В самом начале HEAD
commit это их ветка, так что если вы используете git checkout --ours
или же git checkout --theirs
, --ours
означает их - окончательное совершение origin/their-branch
-в то время как --theirs
означает твой, первый коммит ты перебазируешь. Это обычная повседневная путаница в Git (см. Что означает "наши" и "их" в git?), И это не то, что привело к первоначальному вопросу.
Позже, однако, HEAD
коммит на самом деле является своего рода смесью. Это результат копирования некоторого количества ваших коммитов поверх их последнего коммита. Теперь вы сталкиваетесь с конфликтом между вашими новыми частями коммитов и вашими оригинальными коммитами. Источником этого конфликта обычно является то, что "они" сделали (что-то, что изменилось по пути в origin/their-branch
). Вы все еще должны разрешить этот конфликт. Когда вы это сделаете, вы можете увидеть тот же самый конфликт, повторяющийся в последующих коммитах.
Снова, HEAD
или же local
или же --ours
это коммит, созданный rebase путем объединения ваших изменений и их изменений и другого коммита (remote
или же >>>>>>>
или же --theirs
) это ваш собственный коммит, который rebase пытается скопировать поверх HEAD
,
дольше
При слиянии (включая перебазирование, которое является частным случаем повторного "слияния" внутри), присутствуют две "головы" (две конкретные ветви). Давайте назовем это your-branch
а также origin/their-branch
:
G - H -------- <-- HEAD=your-branch
/ \
... - E - F M <-- desired merge commit [requires manual merge]
\ /
I - J - K - L <-- origin/their-branch
Этот момент обычно (и неудивительно) сбивает с толку, хотя, когда обозначено так, это достаточно ясно.
Что еще хуже, Git использует --ours
а также --theirs
ссылаться на два главных коммита во время слияния, при этом "наш" - тот, на котором вы были (коммит H
) когда ты побежал git merge
и "их" существо, ну их L
). Но когда вы делаете ребаз, две головы меняются местами, так что "наша" - это голова, на которую вы перебазируете, то есть их обновленный код, а "их" - это коммит, который вы в настоящее время опровергаете, т. е. ваш собственный код.
Это потому, что на самом деле rebase использует серию операций выбора вишни. Вы начинаете с почти одинаковой картины:
G - H <-- HEAD=your-branch
/
... - E - F
\
I - J - K - L <-- origin/their-branch
Git должен сделать здесь, чтобы скопировать эффект коммитов G
а также H
т.е. git cherry-pick
совершить G
, затем сделайте это снова с коммитом H
, Но для этого git должен переключиться на коммит L
во-первых, внутренне (используя режим "отсоединенная голова"):
G - H <-- your-branch
/
... - E - F
\
I - J - K - L <-- HEAD, origin/their-branch
Теперь он может начать операцию rebase, сравнивая деревья для коммитов F
а также G
(чтобы увидеть, что вы изменили), затем сравнивая F
против L
(чтобы увидеть, если некоторые из ваших работ уже в L
) и принимая любые изменения, которые еще не L
и добавь это. Это операция "слияния", внутренне.
G - H <-- your-branch
/
... - E - F G' <-- HEAD
\ /
I - J - K - L <-- origin/their-branch
Если слияние не идет хорошо, HEAD
все еще остается на коммите L
(потому что совершают G'
еще не существует). Таким образом, да, HEAD
является руководителем их отдела разработки - по крайней мере, сейчас.
Однажды копия G
существует, хотя, HEAD
движется к G'
и git пытается скопировать изменения из H
таким же образом G
против H
затем F
против G'
и объединить результаты):
G - H <-- your-branch
/
... - E - F G' - H' <-- HEAD
\ /
I - J - K - L <-- origin/their-branch
Опять же, если слияние не удается и вам нужна помощь, вы остаетесь с HEAD
указывая на G'
вместо H'
как H'
еще не существует.
После того, как слияния все удастся и фиксирует G'
а также H'
существует, git удаляет ярлык your-branch
от коммита H
и указывает на то, чтобы совершить H'
вместо:
G - H
/
... - E - F G' - H' <-- HEAD=your-branch
\ /
I - J - K - L <-- origin/their-branch
и теперь вы перебазированы и HEAD
еще раз, что вы ожидаете. Но во время перебазирования, HEAD
это либо их ветвление L
) или один из новых коммитов скопирован и добавлен после их кончика ветки; а также --ours
означает ветвь, выращиваемую в конце L
в то время как --theirs
означает, что коммит копируется из (G
или же H
выше).
(Это в основном git, разоблачающий необработанный механизм того, как он делает то, что он делает, что довольно часто случается в git.)
Определения
В этом разделе мы увидим недостатки, которые нам задают в ответ:
Кто глава?
HEAD
: текущий коммит вашего репо включен. В большинстве случаев HEAD указывает на последний коммит в вашей ветке, но это не обязательно так. HEAD
на самом деле просто означает "на что сейчас указывает мой репо".
В случае, если совершить HEAD
относится не к вершине какой-либо ветви, это называется " detached head
".
Это руководитель отдела развития?
В момент, когда производится слияние или перебазирование HEAD
немедленно переходит к указателю на созданный или реорганизованный коммит и, следовательно, будет указывать на ветку разработки.
В git bash
мы можем увидеть HEAD
ситуация, листинг коммит:
# Normal commit list
git log
# List of commit in a single line
git log --oneline
# All commits graphically-linear (Recommended as alias)
git log --all --graph --decorate --oneline
практика
В этом разделе мы увидим, как _how_ работает некоторые действия, выполняемые пользователем.
Когда пользователь переходит к:
# Command to change from the branch to the current one to experimentalbranch
git checkout experimentalbranch
# Command that traverses the typical workflow to synchronize its local repository with the main branch of the central repository (remoterepo)
git fetch remoterepo
# git fetch origin
# git fetch origin branch:branch
# With the command git rebase, you can take all the changes confirmed in one branch (remoterepo), and reapply them over another developmentbranch
git rebase remoterepo/developmentbranch
К этому времени я столкнулся с конфликтами. Тем не менее, я не знаком ни с одним из этих изменений (я переоцениваю изменения за несколько недель, потому что они не слили мои изменения сразу). Кроме того, я впервые делаю ребаз. Я больше привык к слиянию.
Объединение ветвей осуществляется двумя способами:
мерзавец слияния
git rebase.
Примечание:
Для примеров мы будем использовать следующее дерево:
* a122f6d (HEAD -> remoterepo) Commit END
* 9667bfb Commit MASTER
| * b9bcaf0 (origin/experimentalbranch, experimentalbranch) Commit 3
| * 110b2fb Commit 2
| * e597c60 Commit 1
|/
* 0e834f4 (origin/remoterepo) First commit
мерзавец слияния
Самая известная форма git merge
, который выполняет объединение трех полос между двумя последними снимками каждой ветви и общим предком обоих, создавая новый коммит со смешанными изменениями.
Например:
git checkout remoterepo
git merge experimentalbranch
Это произвело бы нас:
* 003e576 (HEAD -> remoterepo) Merge branch 'experimentalbranch' in remoterepo
|\
| * b9bcaf0 (origin/experimentalbranch, experimentalbranch) Commit 3
| * 110b2fb Commit 2
| * e597c60 Commit 1
* | a122f6d Commit END
* | 9667bfb Commit MASTER
|/
* 0e834f4 (origin/remoterepo) First commit
мерзавец
git rebase
в основном он собирает поочередно подтвержденные изменения в одной ветви и повторно применяет их в другой.
Использование rebase может помочь нам избежать конфликтов всякий раз, когда он применяется к коммитам, которые являются локальными и не были загружены в какой-либо удаленный репозиторий. Если вы не будете осторожны с последним, и партнер использует затронутые изменения, убедитесь, что у вас возникнут проблемы, так как эти типы конфликтов обычно трудно исправить.
Например:
git checkout remoterepo
git rebase experimentalbranch
* f8a74be (HEAD -> remoterepo) Commit END
* 4293e9d Commit MASTER
* b9bcaf0 (origin/experimentalbranch, experimentalbranch) Commit 3
* 110b2fb Commit 2
* e597c60 Commit 1
* 0e834f4 (origin/remoterepo) First commit
Каково происхождение?
origin
: имя по умолчанию, которое git дает вашему главному удаленному репо. У вашего ящика есть свое собственное репо, и вы, скорее всего, продвигаетесь к какому-либо удаленному репо, к которому вы и все ваши коллеги продвигаетесь. Это удаленное хранилище почти всегда называется источником, но это не обязательно.