Избегайте последствий повторной фиксации в другой ветке Git при слиянии

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

Develop имеет довольно много функций, которые запускаются с нашей следующей версией, требуя возврата на слияние. Это создало коммит, который отменяет все изменения. Когда мы объединим мастера обратно в разработку, revert commit удаляет код, сгенерированный нашими функциями.

Каков наилучший способ работы с синхронизацией с основными исправлениями при сохранении новых функций?

- Изменить - Чтобы уточнить, возврат был возвратом. И.Е. git revert -m 1 <sha>, поскольку фиксация уже была перенесена в удаленный репозиторий.

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

Ответ 1

Вариант 1: Жесткий reset и принудительный толчок

Если вы можете выполнить принудительное обновление без быстрого переадресации на свою ветвь master в вашем восходящем репозитории, вместо того, чтобы возвращать слияние develop в master, вы можете просто выполнить жесткий reset master:

# On master branch, do a hard reset back to the commit before the merge
git reset --hard <commit of master before the merge>

# Force push to upstream ONLY IF IT OK WITH OTHER DEVELOPERS
git push <remote> master --force

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

Вариант 2: Отменить возврат

Я проверил это с помощью быстрого тестового репо. Я должен подчеркнуть, что он может работа, я не уверен на 100%, что нет никаких случаев, когда я не рассматривать. Поэтому не забудьте проверить его локально с помощью резервного копирования вашего репо первый. Если вы решите использовать это в своем реальном репо, сделайте это самостоятельно риск.

Кроме того, это может быть не простое и простое решение. Его преимущество перед hard-reset, однако, заключается в том, что он не заставляет разработчиков переделывать работа над вершиной ветки reset master.

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

# Coworker accidentally merges develop into master before it ready
git merge --no-ff develop

# You revert the merge in the master branch (this creates commit "ABCDEFG"
git revert -m 1 <sha of merge commit>

# You want to merge fixes from master into develop
git checkout develop
git merge --no-ff master

# But now all that work in develop is reverted, so revert the revert "ABCDEFG"
git revert ABCDEFG

# When you're ready to merge develop into master...
git checkout master
git merge --no-ff develop

Здесь последовательность команд, которые я использовал для проверки этого в тестовом репо:

mkdir practice
cd practice/
git init

touch readme.txt
git add practice.txt
git commit -m "Add practice.txt"

git checkout -b develop

touch feature1.txt
git add feature1.txt
git commit -m "Add feature 1"

touch feature2.txt
git add feature2.txt
git commit -m "Add feature 2"

git checkout master

touch hotfix1.txt
git add hotfix1.txt
git commit -m "Fix issue 1"

git merge --no-ff develop

# Creates commit "ABCDEFG" that reverts the merge
git revert -m 1 head
git checkout develop
git merge --no-ff master
git revert ABCDEFG
git checkout master
git merge --no-ff develop

Вы можете узнать больше о методе "Восстановить возвращение" на официальном Linux-сервере Документация ядра Git для git revert:

-m parent-number

--mainline parent-number

Обычно вы не можете вернуть слияние, потому что не знаете, с какой стороны Слияние должно рассматриваться как магистраль. Этот параметр указывает родительский номер (начиная с 1) основной линии и позволяет вернуться назад изменить относительно указанного родителя.

Возвращение транзакции слияния объявляет, что вы никогда не захотите, чтобы изменения дерева вызванный слиянием. В результате последующие слияния приведут только к дереву изменения, внесенные коммитами, которые не являются предками ранее повторное слияние. Это может быть или не быть тем, что вы хотите.

Подробнее см. revert-a-faulty-merge How-To.

Ссылка на Как вернуть ошибочное слияние настоятельно рекомендуется, если вы полностью хотят понять, как работает эта техника, это не сложно понять и это на самом деле интересное и увлекательное.

Ответ 2

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

Вот как я его исправляю, предполагая, что дочерняя ветвь ( "развить" ) была обновлена ​​(commit M1) до "плохого" слияния (commit M2) с мастером:

Состояние проблемы

           ... <-- Work that needs merged to develop
            |
            R  <-- Revert Bad Merge
            |
            A  <-- Commits after merge,
            |    /   but before revert
           ... </
            |
          M2  <-"bad" merge
  ... ____/ |
   | /      |
   M1       |
   | \____  |
  ...     \...
develop   master 

Шаг 1

# Get latest from both parent and child branches locally

git checkout master
git pull
git checkout develop
git pull


# Merge all code from before revert in master branch to develop
# (not necessary if "bad" merge into master was immediately reverted)

git merge A

Состояние после шага 1:

           ... <-- Work that needs merged to develop
   M3       |
   | \____  R  <-- Revert Bad Merge
   |      \ |
   |        A  <-- Commits after merge,
   |        |    /   but before revert
   |       ... </
   |        |
   |      M2  <-"bad" merge
  ... ____/ |
   | /      |
   M1       |
   | \____  |
  ...     \...
develop   master 

Шаг 2 - ВАЖНАЯ ЧАСТЬ!

# Use "ours" strategy to merge revert commit to develop.
# This doesn't change any files in develop. 
# It simplly tells git that we've already accounted for that change.

git merge R -s ours

Состояние после шага 2

   M4
   | \____  ... <-- Work that needs merged to develop
   M3     \ |
   | \____  R  <-- Revert Bad Merge
   |      \ |
   |        A  <-- Commits after merge,
   |        |    /   but before revert
   |       ... </
   |        |
   |      M2  <-"bad" merge
  ... ____/ |
   | /      |
   M1       |
   | \____  |
  ...     \...
develop   master 

Шаг 3

# Merge as normal, from the tip of master to develop.
# This should now be an "easy" merge, with only "real" conflicts.
#  (Those that have changed in both branches)
#
# Note: I've had issues using origin master to merge from latest on remote, 
#   so instead I just ensure I've pulled the latest from master locally and 
#   merge from there

git merge master

Состояние после шага 3

   M5
   | \_____
   M4      \
   | \____  ... <-- Work that needs merged to develop
   M3     \ |
   | \____  R  <-- Revert Bad Merge
   |      \ |
   |        A  <-- Commits after merge,
   |        |    /   but before revert
   |       ... </
   |        |
   |      M2  <-"bad" merge
  ... ____/ |
   | /      |
   M1       |
   | \____  |
  ...     \...
develop   master 

Теперь разработка обновляется последним от мастера, не имея необходимости разрешать повторяющиеся или бессмысленные конфликты слияния. Будущие слияния будут вести себя как обычно.