Git: Отменить слияние со случайно удаленными файлами после их удаления. - PullRequest
0 голосов
/ 25 октября 2018

Неопытный пользователь вносит изменения release и затем сливается до dev.О нет, конфликт слияния!Статус объединения показывает их собственные конфликтующие файлы, а также все другие исправления от других членов команды.Будучи параноиком и осторожным пользователем, они говорят:

Это не мои файлы, я случайно их не изменил?Ага, я должен discard их!

Коммит слияния теперь содержит только их изменения, отбрасывая все другие изменения в ветке (возможно, большой объем работы).Они выдвигают dev вверх по течению, и ошибка не может быть обнаружена, пока другой член команды не заметит, что что-то не так.

Большинство рекомендаций предлагает:

  1. Если ветвь еще не была сдвинута: git reset
  2. Отменить объединение: git revert -m 1 <commitId>.Однако это только возвращает данные (то есть, только несчастные изменения пользователя), это не отменяет историю.Любые будущие попытки merge будут игнорировать потерянные изменения, потому что история подразумевает, что они уже были интегрированы.
  3. Переписать историю, rebase или reset с последующим git push origin -f.Это означает, что остальная часть команды должна синхронизироваться с принудительной веткой dev.Если команда большая, или ошибка не была обнаружена быстро, или присутствует сложный инструментарий CI - это становится очень болезненным упражнением.

Imho, это поражает меня критическим упущением в дизайне git.Существует очень мало инструментов для выявления или выхода из этой ситуации.git diff не показывает отклоненные изменения, а git revert не отменяет эти отклоненные изменения.Есть ли лучший способ предотвратить и исправить эту проблему?

1 Ответ

0 голосов
/ 25 октября 2018

Как подробно объяснил Линус (https://mirrors.edge.kernel.org/pub/software/scm/git/docs/howto/revert-a-faulty-merge.txt):

Отмена обычного коммита просто эффективно отменяет то, что сделал этот коммит, и довольно проста. Но отмена коммита слияния также отменяет данные , что фиксация изменилась, но это абсолютно ничего не влияет на history , которое имело слияние.

Таким образом, слияние все еще будет существовать, и оно все равно будет рассматриваться как присоединение кдве ветви вместе, и будущие слияния увидят, что слияние является последним общим состоянием - и возвращение, которое отменило введенное слияние, не повлияет на это вообще.

Таким образом, «возврат» отменяет изменения данных, ноэто не"отмена" в том смысле, что он не отменяет влияния коммита на историю репозитория.

Хорошо, так что это объясняет, почему revert не эффективная стратегия, но что мы можем сделать? Давайте рассмотрим следующее:

p---Q---r---s---M---t---u---   dev
     \         /
      A---B---C-------D---E    feature
  • ABC - это функция / ветка релиза
  • M - плохое слияние, былигоИзменения от AB были отброшены, но C был сохранен
  • DE позже работает на 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должен был изначально содержаться.Теперь вы можете объединить это с dev, как обычно

git checkout dev
git merge fix

Снова могут возникнуть конфликты слияния, но после этого момента (M3) вы восстановили свои недостающие данные.Теперь вы можете действовать как обычно, например, вы можете объединить DE из feature в dev или любые другие ваши обычные операции.Это также означает, что другим членам команды не нужно переустанавливать свою локальную ветвь dev, поскольку она восстановится при следующем выполнении pull.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...