Я понимаю сценарий, представленный в Pro Git об опасностях ребазинга . Автор в основном объясняет, как избежать дублирования коммитов:
Не перемещайте коммиты, которые вы отправили в публичный репозиторий.
Я собираюсь рассказать вам свою конкретную ситуацию, потому что я думаю, что она не совсем соответствует сценарию 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, поэтому я хотел бы знать две вещи:
- Почему Git дублирует эти коммиты при перемещении? Есть ли особая причина делать это вместо того, чтобы просто применять
C5
иC6
послеC7
? - Как мне этого избежать? Было бы разумно это сделать?
origin/dev
. Приdev
перебазировании его история изменяется (C5 / C6 временно удаляется и повторно применяется после C7). Изменение истории отправленных репозиториев обычно является действительно плохой идеей ™, если вы не знаете, что делаете. В этом простом случае проблему можно решить, выполнив принудительное нажатие сdev
наorigin/dev
после перебазирования и уведомив всех, кто работает надorigin/dev
этим, о том, что у них, вероятно, будет плохой день. И снова лучший ответ - «не делайте этого ... используйте вместо этого слияние»Короткий ответ
Вы пропустили тот факт, что вы запустили
git push
, получили следующую ошибку и затем продолжили работуgit pull
:Несмотря на то, что Git пытается быть полезным, его совет «git pull», скорее всего, не то, что вы хотите делать .
Если ты:
git push --force
чтобы обновить пульт с вашими после перебазирования фиксаций ( в соответствии с ответом user4405677 игровых ).git rebase
в первую очередь. Чтобы обновитьdev
с изменениями изmaster
, вы должны вместо запускаgit rebase master dev
запускатьgit merge master
во время работыdev
( согласно ответу Джастина ).Немного более длинное объяснение
Каждый хэш фиксации в Git основан на нескольких факторах, один из которых - хэш предшествующей фиксации.
Если вы измените порядок коммитов, вы измените хеши коммитов; rebase (когда он что-то делает) изменит хеши коммитов. При этом результат выполнения
git rebase master dev
, с которымdev
не синхронизированmaster
, создаст новые коммиты (и, следовательно, хэши) с тем же содержанием, что и те, которые включены,dev
но с коммитами,master
вставленными перед ними.Вы можете попасть в такую ситуацию разными способами. Я могу думать о двух способах:
master
которых вы хотите основывать своиdev
работуdev
, которые уже были отправлены на удаленный компьютер, которые вы затем переходите к изменению (перефразируйте сообщения коммитов, переупорядочивайте коммиты, сквош-коммиты и т.Давайте лучше разберемся, что произошло - вот пример:
У вас есть репозиторий:
Затем вы переходите к изменению коммитов.
(Здесь вам придется поверить мне на слово: есть несколько способов изменить коммиты в Git. В этом примере я изменил время
C3
, но вы будете вставлять новые коммиты, изменять сообщения коммитов, переупорядочивать коммиты, объединение коммитов и т. д.)Здесь важно заметить, что хеши коммитов разные. Это ожидаемое поведение, поскольку вы что-то (что-то) в них изменили. Это нормально, НО:
Попытка нажать покажет вам ошибку (и намекнет, что вам следует запустить
git pull
).Если мы запустим
git pull
, то увидим такой лог:Или, как показано по-другому:
И теперь у нас есть дублирующиеся коммиты локально. Если бы мы запустили,
git push
мы бы отправили их на сервер.Чтобы не попасть на этот этап, мы могли бежать
git push --force
(там, где мы бежалиgit pull
). Это без проблем отправило бы наши коммиты с новыми хешами на сервер. Чтобы решить проблему на этом этапе, мы можем вернуться к предыдущему состояниюgit pull
:Посмотрите на reflog (
git reflog
) , чтобы увидеть , что совершить хэш был до того мы побежалиgit pull
.Выше мы видим, что это
ba7688a
была фиксация, в которой мы были до запускаgit pull
. Имея в руках этот хеш фиксации, мы можем вернуться к этому (git reset --hard ba7688a
) и затем запуститьgit push --force
.И мы закончили.
Но подождите, я продолжал основывать работу на дублированных коммитах.
Если вы каким-то образом не заметили, что коммиты были дублированы, и продолжили работать поверх повторяющихся коммитов, вы действительно навредили себе. Размер беспорядка пропорционален количеству коммитов, которые у вас есть поверх дубликатов.
Как это выглядит:
Или, как показано по-другому:
В этом сценарии мы хотим удалить повторяющиеся коммиты, но сохранить основанные на них коммиты - мы хотим сохранить от C6 до C10. Как и в большинстве случаев, есть несколько способов сделать это:
Либо:
cherry-pick
каждую фиксацию (с C6 по C10 включительно) в эту новую ветку и рассматривайте эту новую ветвь как каноническую.git rebase --interactive $commit
, где$commit
находится фиксация перед обеими дублированными коммитами 2 . Здесь мы можем сразу удалить строки для дубликатов.1 Не имеет значения , какой из двух вы выбираете, либо
ba7688a
или2a2e220
нормально работать.2 В примере это было бы
85f59ab
.TL; DR
Набор
advice.pushNonFastForward
дляfalse
:источник
git push
«S в--force-with-lease
настоящее время , как это лучше по умолчаниюЯ думаю, вы упустили важную деталь при описании своих шагов. Точнее, ваш последний шаг
git push
в dev фактически дал бы вам ошибку, поскольку вы обычно не можете продвигать изменения, отличные от fastforward.Так вы сделали
git pull
перед последним нажатием, что привело к слиянию с C6 и C6 'в качестве родителей, поэтому оба останутся в журнале. Более красивый формат журнала мог бы сделать более очевидным, что они являются объединенными ветвями дублированных коммитов.Или вы вместо этого сделали
git pull --rebase
(или без явного,--rebase
если это подразумевается вашей конфигурацией), который вернул исходные C5 и C6 в ваш локальный разработчик (и затем повторно перебазировал следующие на новые хеши, C7 'C5' 'C6' «).Одним из выходов из этого могло быть
git push -f
принудительное нажатие, когда оно выдавало ошибку, и стирание C5 C6 из источника, но если бы кто-то еще вытащил их, прежде чем вы стерли их, у вас было бы намного больше проблем .. .. По сути, каждому, у кого есть C5 C6, нужно будет предпринять специальные шаги, чтобы избавиться от них. Именно поэтому они говорят, что никогда не следует перебазировать уже опубликованное. Тем не менее, это все еще выполнимо, если упомянуть, что «публикация» осуществляется небольшой командой.источник
git pull
имеет решающее значение. Ваша рекомендацияgit push -f
, хотя и опасна, вероятно, именно то, что ищут читатели.git push --force
, просто чтобы посмотреть, что Git собирается делать. С тех пор я много узнал о Git, и сейчасrebase
это часть моего обычного рабочего процесса. Однако я делаю это,git push --force-with-lease
чтобы не перезаписывать чужую работу.--force-with-lease
по умолчанию - хороший вариант, я также оставлю комментарий под своим ответомЯ обнаружил, что в моем случае эта проблема является следствием проблемы конфигурации Git. (С привлечением вытягивания и слияния)
Описание проблемы:
Симптомы: коммиты дублируются в дочерней ветке после перебазирования, что подразумевает многочисленные слияния во время и после перебазирования.
Рабочий процесс: вот шаги рабочего процесса, который я выполнял:
Как следствие этого рабочего процесса, дублирование всех коммитов «Feature-branch» с момента предыдущего перебазирования ... :-(
Проблема возникла из-за того, что дочерняя ветка внесла изменения перед перебазированием. Конфигурация вытягивания Git по умолчанию - «слияние». Это меняет индексы коммитов, выполненных в дочерней ветке.
Решение: в файле конфигурации Git настройте pull для работы в режиме перебазирования:
Надеюсь, это поможет JN Grx
источник
Возможно, вы использовали удаленную ветку, отличную от текущей. Например, вы могли использовать Master, когда ваша ветка разрабатывает отслеживание разработки. Git послушно выполнит дублирующиеся коммиты, если они будут извлечены из не отслеживаемой ветки.
В этом случае вы можете сделать следующее:
где
n == <number of duplicate commits that shouldn't be there.>
Затем убедитесь, что вы используете правильную ветку, а затем запустите:
Использование with
--rebase
гарантирует, что вы не добавите посторонние коммиты, которые могут запутать историю коммитов.Вот небольшой пример git rebase.
источник