Как переустановить после git -subtree add?
Я пытаюсь изучить новую команду git-subtree, которая была добавлена в Git 1.7.11. Кажется, я теряю способность переустанавливать после добавления поддерева. У меня есть первичный репозиторий с файлом README и репозиторием библиотеки, который также имеет файл README. Я добавляю его в каталог lib с помощью subtree add
:
$ git subtree add -P lib/mylib myliborigin master
Это отлично работает, но теперь история выглядит так:
* 22c1fe6 (HEAD, master) Merge commit 'b6e698d9f4985825efa06dfdd7bba8d2930cd40e' as 'lib/mylib' -
|\
| * b6e698d Squashed 'lib/mylib/' content from commit d7dbd3d
* b99d55b Add readme
* 020e372 Initial
Теперь, когда я хочу переустановить свое репо против origin/master
, и он терпит неудачу, потому что squash commit применяется непосредственно против его родительского коммита, который не применяется, поскольку он применяется к корню репо, а не к префиксу, который я дал это ему при добавлении поддерева.
Причина этого довольно ясна, если я смотрю на фиксацию сквоша. Информация о префиксе отсутствует. Это только оригинальная mylib совершает раздавленные вместе. Только следующее коммит слияния знает что-нибудь об этом, но rebase не учитывает его здесь.
Есть ли какие-либо обходные пути (кроме того, что никогда не перезагружается над поддеревом)?
Ответы
Ответ 1
Это не решение, но его текущая работа вокруг я использую...
Используя ваш первоначальный пример:
* 22c1fe6 (HEAD, master) Merge commit 'b6e698d9f4985825efa06dfdd7bba8d2930cd40e' as 'lib/mylib' -
|\
| * b6e698d Squashed 'lib/mylib/' content from commit d7dbd3d
* b99d55b Add readme
* 020e372 Initial
Интерактивно добавьте 2-й фиксатор до добавления поддерева:
$ git rebase -i 020e372
Удалите две записи поддерева и отметьте правки для предыдущего фиксации:
e b99d55b Add readme
Сохраните файл/закрыть, затем, когда он дойдет до фиксации "Добавить чтение", запустите команду "Изменить":
$ git commit --amend
Затем добавьте новое поддерево:
$ git subtree add -P lib/mylib myliborigin master
Продолжить rebase:
$ git rebase --continue
Затем ваша ветка должна быть отключена от мастера, а поддерево будет "нормальным", если сквош + слияние нетронутым:
* 22c1fe6 (HEAD, master) Merge commit 'b6e698d9f4985825efa06dfdd7bba8d2930cd40e' as 'lib/mylib' -
|\
| * b6e698d Squashed 'lib/mylib/' content from commit d7dbd3d
Ответ 2
Это старый вопрос, но у меня была такая же проблема с моим репо, и я, наконец, нашел полное решение, которое (надеюсь) сохраняет все метаданные поддерева.
Предположим, что у нас есть это дерево фиксации:
B (master) Add README.md
|
A Initial commit
и мы разветкили ветвь feature
с поддеревом, находящимся в lib/
:
git remote add -f githublib https://github.com/lib/lib.git
git subtree add --prefix lib/ githublib master --squash
Создает слияние с двумя родителями: наш текущий master
(B) и несвязанный commit F
с сжатой историей внешнего репо. Эта фиксация также содержит некоторые метаданные git subtree
в сообщении о ее совершении (а именно git-subtree-dir
и git-subtree-split
).
D (feature) Merged commit 'F' as 'lib/'
/ \
/ F Squashed 'lib/' content from GGGGGG
B (master) Add README.md
|
A Initial commit
Позже мы добавим некоторые фиксации к обоим ветвям независимо.
E (feature) Remove .gitignore from lib/
C | (master) Add LICENSE.md
| D Merged commit 'F' as 'lib/'
| / \
|/ F Squashed 'lib/' content from GGGGGG
B Add README.md
|
A Initial commit
Теперь мы хотим переустановить feature
на master
. Вот как:
1. Черри-выберите коммит из feature
один за другим, чтобы создать новую копию ветки feature
поверх master
.
git branch -f feature C
git checkout feature
git cherry-pick D E
E' (feature) Remove .gitignore from lib/
|
D' Merged commit 'F' as 'lib/'
|
| E Remove .gitignore from lib/
C | (master) Add LICENSE.md
| D Merged commit 'F' as 'lib/'
| / \
|/ F Squashed 'lib/' content from GGGGGG
B Add README.md
|
A Initial commit
Теперь у нас есть эквивалент rebase, но мы потеряли всю информацию о внешнем репо, требуемом для git subtree
. Чтобы восстановить его:
git checkout feature
git replace --graft D' C F
git filter-branch --tag-name-filter cat -- master..
И теперь мы получаем изображение, в точности эквивалентное картинке с начатым. Старые коммиты D и E все еще там, но они могут быть собраны позже.
E' (feature) Remove .gitignore from lib/
|
D' Merged commit 'F' as 'lib/'
|\
| \
C \ (master) Add LICENSE.md
| \
| \
| F Squashed 'lib/' content from GGGGGG
B Add README.md
|
A Initial commit
Предупреждение: Это перезаписывает историю feature
, поэтому будьте осторожны при публикации, если кто-то другой сотрудничает с вами в этой ветке. Однако, поскольку вы хотели сделать rebase в первую очередь, вы, вероятно, знаете об этом: -)
Ответ 3
По-видимому, это ожидаемое поведение (для некоторого извращенного определения "ожидаемого поведения".) См.: http://git.661346.n2.nabble.com/subtree-merges-lose-prefix-after-rebase-td7332850.html.
Не то, чтобы это помогло кому угодно. Мне также хотелось бы найти обходное решение для этого.
Ответ 4
Вам нужно использовать
git rebase --preserve-merges --preserve-committer --onto new_place start end
Ответ 5
У меня была аналогичная проблема: я хотел переустановить после добавления поддерева, и использование -preserve-merges все равно оставило меня с конфликтом слияния (из-за противоречивых файлов .gitignore
и других).
В моем случае я не обязательно планировал использовать любую функцию поддерева: я просто занимался репо, которое первоначально должно было быть частью суперпроекта. В случае, если это помогает кому-то еще, вот что я сделал, основываясь на других связанных ответах, которые я нашел.
Предположим, что у меня есть два проекта: main_project и sub_project в том же каталоге. Я хочу вытащить sub_project в каталог с именем sub_project в main_project, если ни один из репо никогда не имел каталог с именем sub_project:
cd main_project
git fetch ../sub_project
git checkout -b sub_project FETCH_HEAD
git filter-branch --prune-empty --tree-filter '
if [[ ! -e sub_project ]]; then
mkdir -p sub_project
git ls-tree --name-only $GIT_COMMIT | xargs -I files mv files sub_project
fi'
git checkout branch-to-merge-within
git merge sub_project
git branch -d sub_project
Я обновлю, если найду какие-либо проблемы с этим подходом.