Подтвердить что ты не робот

Как переустановить после 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 не учитывает его здесь.

Есть ли какие-либо обходные пути (кроме того, что никогда не перезагружается над поддеревом)?

4b9b3361

Ответ 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. Чтобы восстановить его:

2. Добавьте отсутствующую родительскую ссылку в качестве трансплантата и перепишите историю feature, чтобы сделать ее постоянной.

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

Я обновлю, если найду какие-либо проблемы с этим подходом.