Неопытный пользователь вносит изменения в release
, а затем выполняет слияние с dev
. О нет, конфликт слияния! Статус слияния показывает их собственные конфликтующие файлы, а также все другие исправления от других членов команды. Будучи параноиком и осторожным пользователем, они говорят:
Those aren't my files, did I accidentally alter them? Aha, I should
discard
them!
Коммит слияния теперь содержит только их изменения, отбрасывая все другие изменения в ветке (возможно, большой объем работы). Они подталкивают dev
вверх по течению, и ошибка ускользает от обнаружения, пока другой член команды не заметит что-то неладное.
Большинство рекомендаций предлагает:
git reset
git revert -m 1 <commitId>
. Однако это только возвращает данные (иначе говоря, только изменения незадачливого пользователя), это не отменяет историю. Любые будущие попытки merge
проигнорируют потерянные изменения, потому что история подразумевает, что они уже были интегрированы.rebase
или reset
, а затем git push origin -f
. Это означает, что остальная часть команды должна синхронизироваться с принудительной ветвью dev
. Если команда большая, или ошибка не была обнаружена быстро, или присутствует сложный инструментарий CI - это становится очень болезненным занятием.Имхо, это вызывает у меня критическую ошибку в дизайне git. Существует очень мало инструментов для выявления или выхода из этой ситуации. git diff
не показывает отклоненные изменения, а git revert
не отменяет эти отклоненные изменения. Есть ли лучший способ предотвратить и решить эту проблему?
Как подробно рассказал Линус (https://mirrors.edge.kernel.org/pub/software/scm/git/docs/howto/revert-a-faulty-merge.txt):
Reverting a regular commit just effectively undoes what that commit did, and is fairly straightforward. But reverting a merge commit also undoes the data that the commit changed, but it does absolutely nothing to the effects on history that the merge had.
So the merge will still exist, and it will still be seen as joining the two branches together, and future merges will see that merge as the last shared state - and the revert that reverted the merge brought in will not affect that at all.
So a "revert" undoes the data changes, but it's very much not an "undo" in the sense that it doesn't undo the effects of a commit on the repository history.
Хорошо, это объясняет, почему revert
не является эффективной стратегией, но что мы можем сделать? Рассмотрим следующее:
p---Q---r---s---M---t---u--- dev
\ /
A---B---C-------D---E feature
feature
dev
Пока M существует на dev
, предполагается, что история ABC интегрирована, даже если дельты AB отсутствуют. Чтобы восстановить их без изменения истории dev
, нам нужно воссоздать дельты в альтернативной истории (т.е. новых идентификаторах фиксации).
Если есть только несколько коммитов, вы можете индивидуально cherrypick
каждый на dev
, поскольку Cherrypicking копирует данные в новый идентификатор фиксации. Однако это плохо масштабируется для больших или сложных историй ветвей.
Следующий вариант - использовать rebase --no-ff
для воссоздания новой ветки feature
, из которой можно объединить потерянные изменения.
git checkout E
git rebase --no-ff Q
Что создает следующее:
A'--B'--C'-------D'--E' feature-fixed
/ \
p---Q---r---s---M---t---u---M2 dev
\ /
A---B---C--------D---E feature
Первоначальное слияние M, вероятно, стало проблемой только из-за конфликтов слияния. Одна из проблем этого подхода заключается в том, что вам нужно не только правильно разрешить исходный конфликт в ABC, но и теперь у вас есть новый возможный источник конфликта в DE и TU, с которым нужно бороться. В экстренной ситуации выяснять, что происходит, может быть непросто.
p---Q---r---S-------M---t---u-----M3 dev
\ \ / /
A---B---\---C----D---E / feature
\ \ /
----M2---------- fix
Более простая стратегия с использованием инструментов, с которыми вы, вероятно, знакомы, - правильно воссоздать слияние с помощью коммита squash
(M2). Это создает новый идентификатор фиксации (новую историю), и, таким образом, дельты из AB могут быть успешно интегрированы обратно в основную ветку. Этот метод также исключает возможные источники конфликта слияния, позволяет вам сначала исправить ошибку, а затем обработать исходные изменения.
Метод:
Ответвление от dev
до того, как приземлилось плохое слияние (M).
git checkout -b fix S
Теперь у вас есть чистый лист, из которого вы можете выполнить исправленное слияние. Флаг сквоша объединит эти изменения в одну фиксацию, но, что более важно, он сгенерирует новый идентификатор фиксации.
git merge --squash C
Вероятно, на этом этапе вам нужно будет разрешить конфликты, однако теперь M2 представляет все данные, которые M должен был изначально содержать. Теперь вы можете объединить это с разработчиком, как обычно
git checkout dev
git merge fix
Опять же, могут возникнуть конфликты слияния, но после этого момента (M3) вы восстановите недостающие данные. Теперь вы можете действовать как обычно, например, вы можете объединить DE из feature
в dev
или любые другие ваши обычные операции. Это также означает, что другим членам команды не нужно перезагружать свою локальную ветвь dev
, поскольку она восстановится, когда они в следующий раз выполнят pull
.
Насколько я нашел решение, мне все равно были бы интересны способы обнаружения и предупреждения об этой ошибке как о перехвате на стороне сервера?