git — установка родителя коммита без перебазирования

Я использовал git-svn для создания git-зеркала репозитория SVN. Структура внутри SVN была немного нестандартной, поэтому git создал ветку, которая не имеет общего коммита с веткой master.

      A---B---C topic

D---E---F---G master

Я знаю, что коммит A основан на коммите E, и я уверен, что исправил проблемы, из-за которых git не распознавал этот факт (используя filter-branch). Что я хочу сделать, так это повторно прикрепить topic к ветке master, установив E в качестве родителя A:

      A---B---C topic
     /
D---E---F---G master

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


person Marvin Killing    schedule 12.11.2010    source источник
comment
Есть ли шанс повторно инициализировать зеркало git для веток svn, указывающих на правильный каталог? Или сначала исправить структуру svn?   -  person stefanw    schedule 12.11.2010
comment
на самом деле репо использовало стандартный макет ствола/тегов/веток. Однако ветка, которую я пытался исправить, была создана путем копирования только подпути к стволу — думаю, это было слишком сложно для обработки git-svn.   -  person Marvin Killing    schedule 25.11.2010
comment
rebase имеет параметр root. Используйте это с onto и preserve-merges, если вам нужно.   -  person Adam Dymitruk    schedule 23.10.2012
comment
См. также: stackoverflow .com/questions/3810348/   -  person Alexander Bird    schedule 11.03.2014


Ответы (3)


Взгляните на прививки (файл прививки можно найти в .git/info/grafts). Формат довольно простой:

<commit sha1> <parent1 sha1> <parent2 sha1> … <parentN sha1>

Это заставляет git полагать, что у коммита другие родители, чем на самом деле. Используйте filter-branch, чтобы сделать трансплантаты постоянными (чтобы файл трансплантатов можно было удалить):

git filter-branch --tag-name-filter cat -- --all

Обратите внимание, что это переписывает историю репозитория, поэтому его нельзя использовать в общих репозиториях!


Если вы хотите переписать только историю коммитов, которые прививаются, например, в основную ветку, используйте эту команду:

git filter-branch --tag-name-filter cat -- master..
person knittl    schedule 12.11.2010
comment
не используйте прививки или фильтрующие ветки, если в этом нет необходимости. - person Adam Dymitruk; 24.11.2010
comment
Прививки невероятно полезны, если они вам нужны, но, как и некоторые другие функции в git, их следует использовать до того, как вы поделитесь репозиторием (или вам нужно будет заставить всех остальных повторно клонировать). Как говорится в ответе, просто создайте файл прививок (хэш-A, пробел, хэш-E, новая строка) и используйте git filter-branch --tag-name-filter cat -- --all, чтобы переписать историю без изменение других данных фиксации - person JodaStephen; 24.06.2011
comment
@JodaStephen, вы должны добавить это к ответу. Знаете ли вы, можно ли это сделать, если репозиторий опубликован, но прививки, с которыми вы возитесь, касаются только новых, неопубликованных коммитов? - person naught101; 22.06.2012
comment
TIL git трансплантаты. Спасибо! - person ulidtko; 13.01.2014
comment
По какой-то причине в недавнем коммите слияния в моем репо отсутствовал родитель. Это спасло положение. Спасибо! - person CrouZ; 12.03.2014
comment
В моем случае я хотел превратить это в прямую линию: A-B-C-D-E-F-G. Я смог сделать это с помощью файла прививок (который git теперь жалуется на то, что он устарел), а затем с помощью вашего первого примера ветки фильтра, чтобы сделать изменения постоянными. - person kwill; 14.12.2018

Основываясь на ваших диаграммах (хотя меня беспокоит то, что вы подразумеваете под «Я совершенно уверен, что я исправил проблемы, из-за которых git не распознает этот факт (используя filter-branch)»), вы должны быть в состоянии сделать что-то как следующее.

# checkout A
git checkout A

# Reset the branch pointer to E so that E is the parent of the next commit
# --soft ensures that the index stays the same
git reset --soft E

# Remake the commit with the E as the parent, re-using the old commit metadata
git commit -C HEAD@{1}

# Rebase the topic branch onto the modified A commit (current HEAD)
git rebase --onto HEAD A topic
person CB Bailey    schedule 12.11.2010
comment
Работает почти нормально, но после этой операции тема не отслеживает удаленный источник/тему! - person Fabien Benoit-Koch; 03.03.2017

Все, что вам нужно, это:

git rebase --root --onto master^^ topic^^ topic

опция root позволяет вам включить A.

ОБНОВИТЬ:

Добавьте параметр --preserve-merges, если вы хотите сохранить ветвление и слияние части, которую вы перемещаете.

person Adam Dymitruk    schedule 24.11.2010
comment
Это прекрасно работает, если у вас линейная история, если в ней есть коммиты слияния, вам нужно использовать --preserve-merges, и вам нужно повторно разрешать конфликты слияния вручную. - person Flimm; 07.01.2014
comment
да. Я обновлю ответ. В другой раз, отвечая на тот же вопрос, я включил этот вариант. - person Adam Dymitruk; 08.01.2014
comment
@AdamDymitruk: вопрос относительно вашего ответа: вы используете Windows? ^^ кажется мне странным. Это родитель родителя или первый ^ экран DOS для второго ^? (PS. Если это последнее, то git rebase --root --onto "master^" "topic^" topic может быть лучшим синтаксисом.) С другой стороны, возможно, он действительно предназначен для дедушки и бабушки из-за использования --root. Спасибо. - person Rhubbarb; 20.04.2015
comment
в bash это делает 1-й родитель 1-го родителя - person Adam Dymitruk; 13.05.2015