Как я могу восстановить / повторно синхронизировать после того, как кто-то отправит ребаз или сброс в опубликованную ветку? - PullRequest
82 голосов
/ 03 ноября 2010

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

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

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

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

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

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

Ответы [ 3 ]

72 голосов
/ 03 ноября 2010

Синхронизация после принудительной перебазировки в большинстве случаев не так уж и сложна.

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

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

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

Конечно, вы можете сделать это без закладки, если вы посмотрите предварительный ребаз 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 <i>$commit</i> foo
11 голосов
/ 03 ноября 2010

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

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

10 голосов
/ 06 декабря 2013

Начиная с git 1.9 / 2.0 Q1 2014, вам не нужно будет отмечать свое предыдущее происхождение ветви, прежде чем перебазировать его в переписанной ветке upstream, как описано в 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 используется для указания на коммиты B3, B2, B1 и теперь он указывает на B, и ваша ветвь topic была запущена поверх него, когда origin/master был на B3.

Этот режим использует reflog из origin/master, чтобы найти B3 в качестве точки разветвления, так что 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«команда вычисляет точку ветвления перебазируемой ветви с использованием записей reflog ветви« base »(обычно ветки с удаленным отслеживанием), на которой была основана работа ветви, чтобы справиться со случаем, в котором« base »Филиал был перемотан и перестроен.

Например, если история выглядела следующим образом:

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

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

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


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

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

    C --- D --- E  <- dev
   /
  B  <- master@{1}
 /
o --- B' --- C* --- D*  <- master

где:

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

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

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

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

...