Изменить родителя коммита - PullRequest
0 голосов
/ 16 февраля 2019

Я пытаюсь переместить коммит поверх другой ветви, игнорируя все изменения, сделанные в целевой ветке.

Текущая ситуация такова:

      master
        ↓
--A--B--C
   \
    D--E

Я хочу переместить коммит E передC

      master
        ↓
--A--B--C--E'
   \
    D--E

с E ', идентичным E, за исключением того, что parrent теперь C вместо D (это означает, что дерево E и E' должно быть идентичным).

Чтобы быть более точным:

"git cat-file -p E" shows e.g.
tree b98c9a9f9501ddcfcbe02a9de52964ed7dd76d5a
parent D

"git cat-file -p E'" should show
tree b98c9a9f9501ddcfcbe02a9de52964ed7dd76d5a
parent C

Я пробовал перебазировать с другими параметрами, а также вишневым выбором, но все они наконец пытаются объединить любые изменения, сделанные в C, в новый коммит E ': (

Только решениепредотвращение массивного слияния, которое я обнаружил до сих пор:

check out C
copy over all stuff from E to C
commit and get E'

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

1 Ответ

0 голосов
/ 16 февраля 2019

Yay, ваша диаграмма точна!100

Увы, в Git нет очевидного инструмента для достижения желаемого результата.Проблема в том, что rebase - это просто автоматический выбор вишни, а вишневый сбор - это преобразование коммитов и их снимков в наборы изменений и объединение этих изменений с другим коммитом для создания нового коммита, который вам не нуженхочу: вы хотите сохранить исходный снимок.

К счастью, есть несколько довольно простых способов сделать это.К сожалению, некоторые из них используют по крайней мере одну команду слежения , то есть не предназначенную для пользователей, не блестящий фарфор, внутреннюю команду Git.

Во-первых, отметим, что вашарешение верное:

Единственное решение, предотвращающее массовое слияние, которое я нашел до сих пор, это

check out C
copy over all stuff from E to C
commit and get E'

, что в реальных командах Git, например:

$ git checkout -b new-branch master
$ git rm -r .                     # in case there are files in C that aren't in E at all
$ git checkout <hash-of-E> -- .   # overwrite using E
$ git commit

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

Более простой способ # 1

Первый более простой способ сделать это:

$ git checkout -b new-branch master
$ git read-tree -u <hash-of-E>
$ git commit

Операция read-tree заменяет содержимое index тем, которое было взято из коммита E. The -uфлаг сообщает Git: Когда вы выполняете это обновление индекса, обновляйте и рабочее дерево: если файл полностью удален из индекса, удалите его тоже из рабочего дерева или, если файл будет заменен в индексе,замените его и в рабочем дереве. Этот флаг на самом деле не требуется, потому что git commit будет использовать то, что в индексе, но это хорошая идея для здравомыслия.

Более простой способ # 2

Второй более простой метод:

$ git commit-tree -p master -m "<message>" <hash-of-E>^{tree}

выводит хеш-идентификатор нового коммита;Затем нам нужно установить что-то, чтобы указать на этот новый хэш-идентификатор:

$ git update-ref refs/heads/new-branch <hash-ID>

Или в одной строке:

$ git update-ref refs/heads/new-branch $(git commit-tree -p master -m "<message>" <hash-of-E>^{tree})

Обратите внимание, что -m "<message>" можетзаменить на -F <file>, чтобы прочитать сообщение из файла, или даже -F -, чтобы прочитать сообщение из стандартного ввода.Затем вы можете скопировать сообщение фиксации из коммита E, используя git log --no-walk --format=%B <hash-of-E>, и передать его остальной части однострочной команды.

Убедитесь, что new-branch действительно является новым именем ветви, или еслиНет, это ветвь, которую вы хотите переустановить, а не текущая ветвь, потому что git update-ref по умолчанию не проверяет ошибки.

Более простой способ # 3

Вы также можетевыполните эту работу следующим образом:

$ git checkout -b new-branch <hash-of-E>   # now at E, with E in index and work-tree
$ git reset --soft master                  # make new-branch identify C, without
                                           # touching index or work-tree
$ git commit -c <hash-of-E>                # make new commit using E's message

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

...