Восстановить git историю из устаревшей версии проекта - PullRequest
2 голосов
/ 02 марта 2020

Короче говоря:

  1. Проект переносится на новый git сервер хранилища
  2. Кто-то только копирует файлы проекта и передает весь проект на новый сервер в качестве начального коммита
  3. Работа с этим новым начальным коммитом продолжается довольно долго
  4. Мне удалось найти старую локальную копию устаревшего проекта (до перехода на новый сервер) и хочу вставить старую git история в текущей версии проекта (до начала текущей истории). В старом локальном проекте есть несколько лишних коммитов

Ветви по сути выглядят так:

                 old_master
                /
A--B--C--D--E--F

                  origin/new_master
                 /
init--G--H--I--J

где commit: new_master -> init = old_master -> D

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

                       origin/new_master
                      /
A--B--C--D--G--H--I--J

Как перебазировать коммиты из другого репозитория с другой историей? имеет похожая дилемма истории, которая решается с помощью сбора вишни. В моей ситуации существует огромное количество коммитов со сложным ветвлением, которые может быть трудно выбрать из вишни. Есть ли эффективный способ сделать это, используя rebase или rebase --onto?

1 Ответ

3 голосов
/ 02 марта 2020

Существует ли эффективный способ сделать это, используя rebase или rebase --onto?

Не в общем случае, где могут быть ветвления и слияния. (Если история в новом репозитории строго линейна, то вы можете сделать это с помощью простого git rebase --onto. Это не совсем эффективно , но это просто машинное время, так кого волнует, насколько оно эффективно?)

Общее решение для этого - прививка через git replace.

Давайте посмотрим, что произойдет, если вы git fetch и исходные, и новые репозитории в третий (иначе полностью пустой) репозиторий, используя рисунки выше. В итоге вы получите:

A--B--C--D--E--F   <-- old/master

D'--G--H--I--J   <-- new/master

(обратите внимание, что третий репозиторий еще не имеет master). Вместо того, чтобы вызывать первый коммит в new/master цепочке init, я назвал его D', потому что предположительно он имеет тот же снимок , что и коммит D в old/master, но он имеет другой га sh.

Ничего - никакой энергии на Земле - не может изменить любой этих существующих коммитов. Но что, если мы скопируем коммит G в новый коммит G', чей родитель D? Затем мы получаем следующее:

A--B--C--D--E--F   <-- old/master
          \
           G'

       D'--G--H--I--J   <-- new/master

В данный момент новый коммит G' просто висит в репозитории, и мы не можем найти его. Давайте добавим имя , по которому мы можем найти G'. А теперь давайте назовем это graft:

A--B--C--D--E--F   <-- old/master
          \
           G'  <-- graft

       D'--G--H--I--J   <-- new/master

Теперь, что, если бы мы могли каким-то образом получить Git, когда он идет назад по J - затем - I - затем- H - затем - G - затем - D' (и затем остановка) цепи, чтобы, в самый последний момент, он может переключить с G на его трансплантат G'? То есть, мы сделаем соединение по пунктирной линии некоторого вида:

A--B--C--D--E--F   <-- old/master
          \
           G'  <-- graft
           :
       D'--G--H--I--J   <-- new/master

и убедим Git запустить git log как , покажем J, затем I, затем H затем G', затем D, затем C, затем B, затем A.

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

Это именно то, что делает git replace. заменяет объекты . В случае коммита замена может принимать форму трансплантата , например G'. Вместо того, чтобы использовать маги c name graft, Git использует даже более-маги c name, refs/replace/<em>hash</em>, где ha sh - идентификатор ha sh фактического коммита G. Иногда вам не нужно об этом знать, а иногда - вам.

Проблема с этим типом взяточничества с фиксацией замены заключается в том, что git clone не клон замен, по умолчанию. 2 Так что ваш третий репозиторий немного странен при клонировании. Иногда это именно то, что вы хотите, и если так, это нормально. Иногда это не так, и если это так, рассмотрите возможность использования git filter-branch или аналогичного конвертирования трансплантата в четвертый репозиторий, в котором трансплантат теперь является постоянным, поскольку коммиты копируются в new фиксирует (с новыми и разными идентификаторами ha sh), в которых real - но переписанный - история использует привитую историю, а не исходную историю.


1 Философский вопрос: или нет? Читает ли история то, как история равна , или история читает то, как Git читает ее вам?

2 Это, по сути, Git ответ на философский вопрос в сноске 1. История читает то, как Git читает ее вам, но записывается как реальная история. При клонировании Git клонирует историю real и игнорирует трансплантат. После клонирования вы можете попросить Git скопировать трансплантаты тоже , но это не по умолчанию.

Кроме этого, вы можете запустить git --no-replace-objects log и посмотреть реальную историю, и при просмотре привитой истории на каждом трансплантате отмечается дополнительное украшение, так что вы можете увидеть его, если присмотритесь.

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