Коммиты Git дублируются в той же ветке после выполнения перебазирования



я понимаю сценарий, представленный в Pro Git about риски git rebase. Автор в основном рассказывает вам, как избежать дублирования коммитов:




не переустанавливайте коммиты, которые вы переместили в общедоступный репозиторий.




я расскажу вам о своей конкретной ситуации, потому что я думаю, что это не совсем соответствует сценарию Pro Git, и я все равно получаю дублированные коммиты.



допустим, у меня есть два пульта дистанционного управления филиалы со своими местными коллегами:



origin/master    origin/dev
| |
master dev


все четыре ветви содержат одни и те же коммиты, и я собираюсь начать разработку в dev:



origin/master : C1 C2 C3 C4
master : C1 C2 C3 C4

origin/dev : C1 C2 C3 C4
dev : C1 C2 C3 C4


после нескольких коммитов я нажимаю изменения в origin/dev:



origin/master : C1 C2 C3 C4
master : C1 C2 C3 C4

origin/dev : C1 C2 C3 C4 C5 C6 # (2) git push
dev : C1 C2 C3 C4 C5 C6 # (1) git checkout dev, git commit


я должен вернуться к master для того чтобы сделать быстрое исправление:



origin/master : C1 C2 C3 C4 C7  # (2) git push
master : C1 C2 C3 C4 C7 # (1) git checkout master, git commit

origin/dev : C1 C2 C3 C4 C5 C6
dev : C1 C2 C3 C4 C5 C6


и обратно в dev Я переназначаю изменения, чтобы включить быстрое исправление в мою фактическую разработку:



origin/master : C1 C2 C3 C4 C7
master : C1 C2 C3 C4 C7

origin/dev : C1 C2 C3 C4 C5 C6
dev : C1 C2 C3 C4 C7 C5' C6' # git checkout dev, git rebase master


если я показываю история коммитов с GitX/gitk я замечаю, что origin/dev теперь содержит два идентичных совершает C5' и C6' которые отличаются от Git. Теперь, если я нажимаю изменения в origin/dev вот результат:



origin/master : C1 C2 C3 C4 C7
master : C1 C2 C3 C4 C7

origin/dev : C1 C2 C3 C4 C5 C6 C7 C5' C6' # git push
dev : C1 C2 C3 C4 C7 C5' C6'


может быть, я не совсем понимаю объяснение в Pro Git, поэтому я хотел бы знать две вещи:




  1. почему Git дублирует эти коммиты при перезагрузке? Есть ли особая причина сделать это вместо того, чтобы просто применять C5 и C6 после C7?

  2. как я могу избежать этого? Было бы разумно сделать это?

1429   4  

4 ответов:

вы не должны использовать rebase здесь, простое слияние будет достаточно. Книга Pro Git, которую вы связали, в основном объясняет эту точную ситуацию. Внутренняя работа может быть немного другой, но вот как я это представляю:

  • C5 и C6 временно вытащены из dev
  • C7 применяется dev
  • C5 и C6 воспроизводятся поверх C7, создавая новые различия и, следовательно, новые коммиты

Итак, в вашем dev филиала, C5 и C6 фактически больше не существует: они теперь C5' и C6'. Когда вы нажимаете на origin/dev, git видит C5' и C6' как новые коммиты и прикрепляет их к концу истории. Действительно, если вы посмотрите на различия между C5 и C5' на origin/dev, вы заметите, что хотя содержимое одно и то же, номера строк, вероятно, разные - что делает хэш фиксации отличающийся.

Я повторю правило Pro Git:никогда не перебазируйте коммиты, которые когда-либо существовали где-либо, кроме вашего локального репозитория. Вместо этого используйте слияние.

короткий ответ:

вы опустили тот факт, что вы запускали git push, получил следующую ошибку, а затем приступил к запуску git pull:

To [email protected]:username/test1.git
 ! [rejected]        dev -> dev (non-fast-forward)
error: failed to push some refs to '[email protected]:username/test1.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

несмотря на то, что Git пытается быть полезным,его совет "git pull", скорее всего, не то, что вы хотите сделать.

если вы:

  • работа над "веткой функций "или" веткой разработчика"только, тогда вы можете запустить git push --force для обновления пульта ДУ с помощью пост-ребаза коммиты (согласно ответу user4405677).
  • работа на ветке с несколькими разработчиками одновременно, то вы, вероятно, не должны использовать git rebase в первую очередь. Чтобы обновить dev С изменениями от master, вы должны, вместо git rebase master dev, используя git merge master на dev (согласно ответу Джастина).

немного более длинное объяснение

каждый хэш фиксации в Git является на основе ряда факторов, одним из которых является хэш коммита, который идет перед ним.

если вы переупорядочите коммиты, вы измените хэши коммитов; ребазинг (когда он что-то делает) изменит хэши коммитов. При этом результат выполнения git rebase master dev, где dev совпадает с master, будет создавать новая коммиты (и, следовательно, хэши) с тем же содержанием, что и на dev но с фиксацией на master вставить перед ними.

вы можете оказаться в такой ситуации несколькими способами. Я могу придумать два способа:

  • вы могли бы совершает над master что вы хотите основать свой dev работы на
  • вы могли бы совершает над dev которые уже были перемещены на пульт дистанционного управления, который вы затем продолжите изменять (перефразировать сообщения фиксации, переупорядочить коммиты, сквош коммиты и т. д.)

давайте лучше разберемся, что произошло - вот пример:

у вас есть репозиторий:

2a2e220 (HEAD, master) C5
ab1bda4 C4
3cb46a9 C3
85f59ab C2
4516164 C1
0e783a3 C0

Initial set of linear commits in a repository

затем вы переходите к изменению коммитов.

git rebase --interactive HEAD~3 # Three commits before where HEAD is pointing

(здесь вам придется поверить мне на слово: есть несколько способов изменить коммиты в Git. В этом примере я изменил время C3, но вы вставляете новые коммиты, меняете сообщения о коммитах, переупорядочиваете коммиты, сжимаете коммиты вместе и т. д.)

ba7688a (HEAD, master) C5
44085d5 C4
961390d C3
85f59ab C2
4516164 C1
0e783a3 C0

The same commits with new hashes

вот где это важно обратите внимание, что хэши фиксации отличаются. Это ожидаемое поведение, так как вы изменили что-то (что угодно) о них. Это нормально, но:

A graph log showing that master is out-of-sync with the remote

попытка нажать покажет вам ошибку (и намек, что вы должны запустить git pull).

$ git push origin master
To [email protected]:username/test1.git
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to '[email protected]:username/test1.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Integrate the remote changes (e.g.
hint: 'git pull ...') before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

если мы запустим git pull, мы видим этот журнал:

7df65f2 (HEAD, master) Merge branch 'master' of bitbucket.org:username/test1
ba7688a C5
44085d5 C4
961390d C3
2a2e220 (origin/master) C5
85f59ab C2
ab1bda4 C4
4516164 C1
3cb46a9 C3
0e783a3 C0

или, показанный другой путь:

A graph log showing a merge commit

и теперь у нас есть дубликаты коммитов локально. Если бы мы были для запуска git push мы бы отправили их на сервер.

чтобы не попасть на этот этап, мы могли бы бежать git push --force (куда мы вместо этого побежали git pull). Это отправило бы наши коммиты с новыми хэшами на сервер без проблем. Чтобы устранить проблему на этом этапе, мы можем сбросить обратно до запуска git pull:

смотреть на reflog (git reflog), чтобы увидеть, что commit хэш был до мы побежали git pull.

070e71d HEAD@{1}: pull: Merge made by the 'recursive' strategy.
ba7688a HEAD@{2}: rebase -i (finish): returning to refs/heads/master
ba7688a HEAD@{3}: rebase -i (pick): C5
44085d5 HEAD@{4}: rebase -i (pick): C4
961390d HEAD@{5}: commit (amend): C3
3cb46a9 HEAD@{6}: cherry-pick: fast-forward
85f59ab HEAD@{7}: rebase -i (start): checkout HEAD~~~
2a2e220 HEAD@{8}: rebase -i (finish): returning to refs/heads/master
2a2e220 HEAD@{9}: rebase -i (start): checkout refs/remotes/origin/master
2a2e220 HEAD@{10}: commit: C5
ab1bda4 HEAD@{11}: commit: C4
3cb46a9 HEAD@{12}: commit: C3
85f59ab HEAD@{13}: commit: C2
4516164 HEAD@{14}: commit: C1
0e783a3 HEAD@{15}: commit (initial): C0

выше мы видим, что ba7688a был ли коммит, на котором мы были до запуска git pull. С этим хэшем фиксации в руке мы можем вернуться к этому (git reset --hard ba7688a), а затем запустить git push --force.

и мы закончили.

но подождите, я продолжал работать с дублированными коммитами

если вы каким-то образом не заметили, что коммиты были дублированы и продолжили работать поверх дубликатов коммитов, вы действительно сделали беспорядок для себя. Размер беспорядка пропорционален количество коммитов у вас есть поверх дубликатов.

как это выглядит:

3b959b4 (HEAD, master) C10
8f84379 C9
0110e93 C8
6c4a525 C7
630e7b4 C6
070e71d (origin/master) Merge branch 'master' of bitbucket.org:username/test1
ba7688a C5
44085d5 C4
961390d C3
2a2e220 C5
85f59ab C2
ab1bda4 C4
4516164 C1
3cb46a9 C3
0e783a3 C0

Git log showing linear commits atop duplicated commits

или, показанный другой путь:

A log graph showing linear commits atop duplicated commits

в этом сценарии мы хотим удалить дубликаты коммитов, но сохранить коммиты, которые мы основали на них-мы хотим сохранить C6 через C10. Как и в большинстве случаев, есть несколько способов сделать это:

либо:

  • создать новый ответвление при последней дублированной фиксации1,cherry-pick каждая фиксация (C6 через C10 включительно) на эту новую ветвь и рассматривает эту новую ветвь как каноническую.
  • выполнить git rebase --interactive $commit, где $commit - это совершение до для обоих дублированных коммитов2. Здесь мы можем прямо удалить строки для дубликатов.

1 не имеет значения, какой из двух вы выберете, либо ba7688a или 2a2e220 работать нормально.

2 в примере это будет 85f59ab.

TL; DR

Set advice.pushNonFastForward до false:

git config --global advice.pushNonFastForward false

Я думаю, что вы пропустили важную деталь, описывая свои действия. Более конкретно, ваш последний шаг, git push на dev, на самом деле дал бы вам ошибку, так как вы не можете нормально нажимать не-fastforward изменения.

так ты git pull перед последним нажатием, что привело к слиянию фиксации с C6 и C6 ' в качестве родителей, поэтому оба останутся в списке журнала. Более красивый формат журнала мог бы сделать его более очевидным, что они являются объединенными ветвями дублированных совершает.

или вы сделали git pull --rebase (или без явной --rebase если это подразумевается вашей конфигурацией) вместо этого, который вытащил исходный C5 и C6 обратно в ваш локальный dev (и далее повторно перебазировал следующие в новые хэши, C7' C5" C6").

один из выходов из этого мог быть git push -f чтобы заставить толчок, когда он дал ошибку, и стереть C5 C6 из origin, но если бы кто-то еще также вытащил их, прежде чем вы их вытерли, у вас было бы гораздо больше проблем... в основном все, у кого есть C5 C6, должны будут сделать специальные шаги, чтобы избавиться от них. Именно поэтому они говорят, что вы никогда не должны перебазировать что-либо, что уже опубликовано. Однако это все еще выполнимо, если упомянутая "публикация" находится в небольшой команде.

я узнал, что в моем случае, эта проблема является следствием проблемы конфигурации ЖКТ. (Включая вытягивание и слияние)

описание проблемы:

Sympthoms: коммиты дублируются на дочерней ветви после перебазирования, что подразумевает многочисленные слияния во время и после перебазирования.

документооборот: Вот шаги рабочего процесса, который я выполнял:

  • работа над "Features-branch" (дочерний элемент "Развивай-филиал")
  • зафиксируйте и нажмите изменения на "Features-branch"
  • оформить заказ "разработка-ветвь" (материнская ветвь функций) и работать с ней.
  • commit и push изменения на "развитие-филиал"
  • проверка "функции-ветвь" и вытащить изменения из репозитория (в случае, если кто-то другой совершил работу)
  • перебазировать "особенности-филиала" на "развитие-филиал"
  • сила нажима изменений дальше "Feature-branch"

как следствие этого рабочего процесса, дублирование всех коммитов "Feature-branch" с момента предыдущей перебазировки... : - (

проблема была из-за тяги изменений дочерней ветви перед перебазированием. конфигурация по умолчанию Git-это "слияние". Это изменение индексов коммитов, выполняемых на дочерней ветви.

решение: в файле конфигурации Git настройте pull для работы в режиме rebase:

...
[pull]
    rebase = preserve
...

Надежда это может помочь Ин Арабские бани

Comments

    Ничего не найдено.