Как восстановить/повторить синхронизацию после того, как кто-то нажал на rebase или reset на опубликованную ветку?

Мы все слышали, что никогда не следует переустанавливать опубликованную работу, ее опасную и т.д. Однако я не видел рецептов, опубликованных для того, как справляться с ситуацией в случае публикации перестановки.

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

Одно очевидное решение, которое я видел, будет работать, если у вас нет локальных коммитов на foo, и он получает rebased:

git fetch
git checkout foo
git reset --hard origin/foo

Это просто выкинет локальное состояние foo в пользу его истории в удаленном репозитории.

Но как вы справляетесь с ситуацией, если в этой ветке произошли существенные локальные изменения?

Ответ 1

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

git checkout foo
git branch old-foo origin/foo # BEFORE fetching!!
git fetch
git rebase --onto origin/foo old-foo foo
git branch -D old-foo

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

Rebasing похож на насилие: если он не решает вашу проблему, вам просто нужно больше. ☺

Вы можете сделать это без закладки, конечно, если вы просмотрите идентификатор фиксации pre-rebase origin/foo и используйте это.

Это также то, как вы имеете дело с ситуацией, когда вы забыли сделать закладку перед извлечением. Ничего не потеряно - вам просто нужно проверить reflog для удаленной ветки:

git reflog show origin/foo | awk '
    PRINT_NEXT==1 { print $1; exit }
    /fetch: forced-update/ { PRINT_NEXT=1 }'

При этом будет напечатан идентификатор фиксации, указанный origin/foo перед самым последним извлечением, изменившим его историю.

Затем вы можете просто

git rebase --onto origin/foo $commit foo

Ответ 2

Я бы сказал, что раздел восстановления из восходящей перегруппировки раздела страницы git -rebase охватывает почти все это.

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

Ответ 3

Начиная с git 1.9/2.0 Q1 2014, вам не нужно будет отмечать ваше предыдущее происхождение ветки, прежде чем перезагрузить его на перезаписанной ветке вверх, как описано в Aristotle Pagaltzis answer:
См. commit 07d406b и commit d96855f:

После работы с ветвью topic, созданной с помощью git checkout -b topic origin/master, история удаленной отслеживания ветки origin/master, возможно, была перемотана и перестроена, что привело к истории этой формы:

                   o---B1
                  /
  ---o---o---B2--o---o---o---B (origin/master)
          \
           B3
            \
             Derived (topic)

где origin/master используется для указания на commits B3, B2, B1 и теперь он указывает на B, а ваша ветвь topic была запущена поверх нее, когда origin/master была при B3.

Этот режим использует reflog origin/master, чтобы найти B3 как точку fork, так что topic можно переустановить поверх обновленного origin/master:

$ fork_point=$(git merge-base --fork-point origin/master topic)
$ git rebase --onto origin/master $fork_point topic

Вот почему команда git merge-base имеет новую опцию:

--fork-point::

Найдите точку, в которой ветка (или любая история, которая ведет к <commit>), разветвляется из другой ветки (или любой ссылки) <ref>.
Это не просто поиск общего предка двух коммитов, но также учитывает reflog <ref>, чтобы увидеть, если история, ведущая к <commit>, разветвленная от более раннего воплощения ветки <ref>.


Команда "git pull --rebase" вычисляет точку fork ветки, которая переустанавливается, используя записи reflog ветки "base" (обычно ветвь удаленного отслеживания), на которой была основана работа ветки, чтобы справиться в случае, когда "базовая" ветвь была перемотана и перестроена.

Например, если история выглядела так:

  • текущая вершина ветки "base" находится в B, но более ранняя выборка показала, что ее наконечник имел значение B3, а затем B2, а затем B1перед тем, как перейти к текущей фиксации, и
  • ветвь, переустановленная поверх последней "базы", ​​основана на commit B3,

он пытается найти B3, просмотрев вывод "git rev-list --reflog base" (т.е. B, B1, B2, B3), пока не найдет фиксацию, являющуюся предком текущий наконечник "Derived (topic)".

Внутри мы имеем get_merge_bases_many(), который может вычислить это с помощью one-go.
Мы хотели бы получить базу слияния между Derived и фиктивным фиксацией слияния, результатом которой будет слияние всех исторических советов "base (origin/master)".
Когда такая фиксация существует, мы должны получить единственный результат, который точно соответствует одной из записей reflog "base" .


Git 2.1 (Q3 2014) добавит эту функцию более надежной: см. commit 1e0dacd John Keeping (johnkeeping)

правильно обрабатывать сценарий, где у нас есть следующая топология:

    C --- D --- E  <- dev
   /
  B  <- [email protected]{1}
 /
o --- B' --- C* --- D*  <- master

где:

  • B' - это фиксированная версия B, которая не совпадает с патчем с B;
  • C* и D* являются патчами, идентичными C и D соответственно, и конфликты текстовое, если оно применяется в неправильном порядке;
  • E зависит от текста на D.

Правильный результат git rebase master dev заключается в том, что B идентифицируется как fork-point из dev и master, так что C, D, E являются коммитами, которые должны переигрывать на master; но C и D являются патч-идентичными с C* и D* и поэтому могут быть отброшены, так что конечный результат:

o --- B' --- C* --- D* --- E  <- dev

Если точка fork не идентифицирована, то выбор B на ветку, содержащую B', приводит к конфликту, и если идентичные по патчу коммиты не определены правильно, выбирая C на ветку, содержащую D (или эквивалентно D*) приводит к конфликту.