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

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

Those aren't my files, did I accidentally alter them? Aha, I should discard them!

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

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

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

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

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
2
0
568
1

Ответы 1

Как подробно рассказал Линус (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
  • 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 должен был изначально содержать. Теперь вы можете объединить это с разработчиком, как обычно

git checkout dev
git merge fix

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

Насколько я нашел решение, мне все равно были бы интересны способы обнаружения и предупреждения об этой ошибке как о перехвате на стороне сервера?

Peter Cardwell-Gardner 25.10.2018 13:08

Другие вопросы по теме