program story

git-subtree 추가 후 rebase하는 방법은 무엇입니까?

inputbox 2020. 12. 26. 10:00
반응형

git-subtree 추가 후 rebase하는 방법은 무엇입니까?


Git 1.7.11에 추가 된 새로운 git-subtree 명령 을 배우려고합니다 . 하위 트리를 추가 한 후 리베이스 기능을 잃는 것 같습니다. 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스쿼시 커밋이 적용되지 않는 부모 커밋에 대해 직접 적용되기 때문에 실패합니다. 왜냐하면 리포지토리의 루트에 적용되고 추가 할 때 내가 준 접두사에 적용되기 때문입니다. 하위 트리.

그 이유는 스쿼시 커밋을 보면 아주 분명합니다. 접두사에 대한 정보가 없습니다. 함께 압축 된 원래 mylib 커밋 일뿐입니다. 다음 병합 커밋 만이 그것에 대해 알고 있지만 rebase는 여기에서 고려하지 않습니다.

해결 방법이 있습니까 (하위 트리 커밋을 리베이스하지 않는 것 외에)?


이것은 해결책은 아니지만 내가 사용하는 현재 작업 ...

초기 예제 사용 :

*   22c1fe6 (HEAD, master) Merge commit 'b6e698d9f4985825efa06dfdd7bba8d2930cd40e' as 'lib/mylib' - 
|\                                                                                                                
| * b6e698d Squashed 'lib/mylib/' content from commit d7dbd3d
* b99d55b Add readme
* 020e372 Initial

하위 트리가 추가되기 전에 대화식으로 두 번째 커밋으로 리베이스합니다.

$ git rebase -i 020e372

두 개의 하위 트리 항목을 삭제하고 이전 커밋에 대한 편집 표시 :

e b99d55b Add readme

파일을 저장 / 닫은 다음 "Readme 추가"커밋에 도달하면 amend 명령을 실행합니다.

$ git commit --amend

그런 다음 새 하위 트리를 다시 추가하십시오.

$ git subtree add -P lib/mylib myliborigin master

리베이스를 계속하십시오.

$ git rebase --continue

그런 다음 브랜치는 마스터를 기반으로해야하며 하위 트리는 Squash + Merge가 그대로 유지 된 상태에서 "일반"이됩니다.

*   22c1fe6 (HEAD, master) Merge commit 'b6e698d9f4985825efa06dfdd7bba8d2930cd40e' as 'lib/mylib' - 
|\                                                                                                                
| * b6e698d Squashed 'lib/mylib/' content from commit d7dbd3d

이것은 오래된 질문이지만 내 저장소에 동일한 문제가 있었고 마침내 모든 하위 트리 메타 데이터를 보존하는 완전한 솔루션을 찾았습니다.

이 커밋 트리가 있다고 가정합니다.

B   (master) Add README.md
|     
A            Initial commit

그리고 우리 featurelib/다음 위치에 있는 하위 트리가 있는 분기를 분기했습니다 .

git remote add -f githublib https://github.com/lib/lib.git
git subtree add --prefix lib/ githublib master --squash

현재 master(B) 두 개의 부모가있는 병합 커밋 D와 F외부 저장소의 스쿼시 된 기록이 있는 관련없는 커밋 을 생성합니다. 이 커밋에는 git subtree커밋 메시지 (즉, git-subtree-dirgit-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

이제 우리는 리베이스 할 featuremaster. 방법은 다음과 같습니다.

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

Now we have the equivalent of a rebase, but we've lost all information about the external repo, required for git subtree. To restore it:

2. Add the missing parent link as a graft, and rewrite the history of feature to make it permanent.

git checkout feature
git replace --graft D' C F
git filter-branch --tag-name-filter cat -- master..

And now we get a picture exactly equivalent to the one with started with. The old commits D and E are still out there, but they can be garbage-collected later.

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

Warning: This rewrites the history of feature, so be wary of publishing it if anybody else collaborates with you on this branch. However, since you wanted to make a rebase in the first place, you are probably aware of that :-)


This works in simple cases:

git rebase --preserve-merges master

Thanks to @Techlive Zheng in the comments.


You may see

fatal: refusing to merge unrelated histories
Error redoing merge a95986e...

Which means that git failed to automatically apply your subtree. This puts you in the situation @ericpeters described in his answer. Solution:

Re-add your subtree (use the same command you originally used):

git subtree add -P lib lib-origin master

Continue the rebase:

git rebase --continue

And you're all set!


If you're wondering if it worked successfully, you can compare with your original version after rebasing to make sure you didn't change anything:

git diff <ref-before-rebase> <ref-after-rebase> -- .

(the -- . at the end instructs git to only diff the files in your current directory).


If all else fails and you don't care about preserving the commits themselves, you can simply git cherry-pick the original subtree commit.

The commit message will look something like Add 'lib/' from commit '9767e6...' -- that's the one you want.


Apparently this is expected behaviour (for some perverse definition of "expected behaviour.") See: http://git.661346.n2.nabble.com/subtree-merges-lose-prefix-after-rebase-td7332850.html.

Not that this is much help to anyone. I'd love to find a workaround for this too.


I had a similar issue: I wanted to rebase after doing a subtree add, and using --preserve-merges still left me with a merge conflict (due to conflicting .gitignore files and others).

In my case, I didn't necessarily plan on using any subtree functionality: I was simply pulling in a repo that should have been part of the superproject originally. In case it helps anyone else, here's what I ended up doing, based off of other related answers I found.

Suppose I have two projects, main_project and sub_project, in the same directory. I want to pull sub_project into a directory named sub_project within main_project, assuming neither repo has ever had a directory named 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

I'll update if I find any problems with this approach.


You need to use

git rebase --preserve-merges --preserve-committer --onto new_place start end

ReferenceURL : https://stackoverflow.com/questions/12858199/how-to-rebase-after-git-subtree-add

반응형