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, возможно, лучший.