여러 자식 리포지토리 결합


207

다음과 같은 설정이 있다고 가정 해 봅시다.

phd/code/
phd/figures/
phd/thesis/

역사적 이유로, 이들은 모두 자신의 자식 저장소를 가지고 있습니다. 그러나 나는 그것들을 하나의 것으로 결합하여 사물을 조금 단순화하고 싶습니다. 예를 들어 지금 당장 두 가지 설정을 변경하고 다음과 같은 작업을 수행해야합니다.

cd phd/code
git commit 
cd ../figures
git commit

그냥 수행하는 것이 좋을 것입니다.

cd phd
git commit

하위 모듈을 사용하거나 하위 리포지토리에서 가져 오는 두 가지 방법이 있지만 내가 찾는 것보다 조금 더 복잡합니다. 최소한 나는 행복 할 것이다

cd phd
git init
git add [[everything that's already in my other repositories]]

그러나 그것은 하나의 라이너처럼 보이지 않습니다. 거기에 무엇인가 git가 나를 도울 수 있습니까?


: 또한이 위대한 접근 방식을 고려 stackoverflow.com/questions/1425892/...
요한 소 버그


join-git-repos.py 별도의 저장소가있는 경우 스크립트는 결합하려는 마스터 가지 각, 좋은 일을한다.
Mark

답변:


149

여기 내가 준 해결책이 있습니다 .

  1. 먼저 phd 디렉토리의 전체 백업을 수행하십시오. 수년간의 노력으로 잃어버린 책임을지지 않기를 바랍니다! ;-)

    $ cp -r phd phd-backup
    
  2. 의 내용 이동 phd/code에를 phd/code/code, 그리고 그것이 항상 (이 용도의 자식의이 계속 것처럼 보이도록 역사를 수정 필터 분기 명령) :

    $ cd phd/code
    $ git filter-branch --index-filter \
        'git ls-files -s | sed "s#\t#&code/#" |
         GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
         git update-index --index-info &&
         mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' HEAD
    
  3. 의 내용에 대해 동일 phd/figures하고 phd/thesis(다만 교체 codefigures하고 thesis).

    이제 디렉토리 구조는 다음과 같아야합니다.

    phd
      |_code
      |    |_.git
      |    |_code
      |         |_(your code...)
      |_figures
      |    |_.git
      |    |_figures
      |         |_(your figures...)
      |_thesis
           |_.git
           |_thesis
                |_(your thesis...)
    
  4. 그런 다음 루트 디렉토리에 git 저장소를 작성하고 모든 것을 가져 와서 이전 저장소를 제거하십시오.

    $ cd phd
    $ git init
    
    $ git pull code
    $ rm -rf code/code
    $ rm -rf code/.git
    
    $ git pull figures --allow-unrelated-histories
    $ rm -rf figures/figures
    $ rm -rf figures/.git
    
    $ git pull thesis --allow-unrelated-histories
    $ rm -rf thesis/thesis
    $ rm -rf thesis/.git
    

    마지막으로 원하는 것을 갖추어야합니다.

    phd
      |_.git
      |_code
      |    |_(your code...)
      |_figures
      |    |_(your figures...)
      |_thesis
           |_(your thesis...)
    

이 절차의 한 가지 좋은 점은 버전이 지정되지 않은 파일과 디렉토리를 그대로 두는 것입니다.

도움이 되었기를 바랍니다.


귀하의 경우 : 비록 경고의 한 단어 code디렉토리가 이미이 code하위 디렉토리 나 파일을, 일이 아주 잘못 될 수도 있습니다 (에 대한 동일 figures하고 thesis물론). 이 경우이 전체 절차를 수행하기 전에 해당 디렉토리 또는 파일의 이름을 바꾸십시오.

$ cd phd/code
$ git mv code code-repository-migration
$ git commit -m "preparing the code directory for migration"

절차가 완료되면이 마지막 단계를 추가하십시오.

$ cd phd
$ git mv code/code-repository-migration code/code
$ git commit -m "final step for code directory migration"

물론, code서브 디렉토리 또는 파일의 버전이 지정되지 않은 경우 mv대신을 사용 git mv하고 git commits를 잊어 버리십시오 .


13
이 스 니펫에 대해 감사합니다. 정확히 필요한 것입니다 (한 번 Mac OS X에서 "\ t"를 처리하지 않은 것으로 간주했습니다 (대신 ^ V ^ I를 사용해야했습니다)
Craig Trader

6
나는 이것을 처음에는 작동시킬 수 없었고 궁극적으로 다른 오래된 메시지 보드에서 문제에 대한 해결책을 찾았습니다. 마지막 줄에서 파일 이름을 따옴표로 묶어야 mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"' HEAD했습니다.
Jorin

3
펑키 filter-branch 명령은 git의 filter-branch 매뉴얼 페이지에 있습니다. 당신은 다음과 같이 말해야합니다. 그것이 맨 페이지에서 나왔음을 알았습니다.
tymtam

5
조심해! MacOS X은 sed의 GNU 확장을 사용하지 않으므로 \ t 시퀀스를 알 수 없습니다. 결과는 엉망인 역사입니다! 내 해결책은 코드를 스크립트 파일에 붙여 넣고 실제 <TAB> 문자를 작성하는 것입니다. 터미널에서 ctrl + v를 누른 다음 <TAB>을 쓰는 탭을 입력 할 수 있습니다. 나는 크레이그의 솔루션을 시도하지 않은
길 Vegliach에게

4
조심해 (2)! 또한 일부 파일 또는 디렉토리에 하이픈 ( '-')이 포함 된 경우 sed 명령이 실패합니다. 이 경우 's ~ \ t ~ & code / ~'와 같은 것으로 대체 할 수 있습니다. 여기서 동일한 논리를 적용하여 이름에서 '~'를 조심하십시오
Gil Vegliach

75

git-stitch-repogit-fast-export --all --date-order명령 줄에 주어진 git 저장소 의 출력을 처리하고 git-fast-import모든 소스 저장소의 기록을 존중하는 새 커밋 트리의 모든 커밋을 포함하는 새 저장소를 만드는 데 적합한 스트림 을 만듭니다.


33
어, 그것은 타사 도구, 자식의 일부입니다 ... :-)
아리스토텔레스 Pagaltzis

1
실제로, 지금 당신은 나에게 말해 :) 아 글쎄, 나는 언젠가 CPAN 패키지를 설치하는 방법을 배워야한다고 생각합니다 ...
Will Robertson

1
그 명령을 지적 해 주셔서 감사합니다. SVN에서 Git으로 몇 번의 repos를 옮기는 데 도움이되었습니다.
signine

1
분기 / 병합이있는 경우 경고가 작동하지 않을 수 있습니다! 로부터 자식-스티치-의 repo . 페이지 : "자식 - 스티치 - REPO는 선형 역사 (NO 병합)가 저장소와 완벽하게 작동합니다 .. 확인해야합니다 버전 0.06에서 추가 바느질 알고리즘에 대한 개선 저장소가 작동하도록 적합 분기하고 병합합니다. "
Bryan P

6
이것은 외부 스크립트이며 답변이 너무 짧고 실제로 도움이되지 않습니다.이 스크립트는 병합 커밋에 문제가 있으며 많은 사람들이 Perl 또는 CPAN을 처리하지 않으므로 답변에 잘 설명되어 있지 않습니다. 그러니까 ... -1, 죄송합니다
Haralan Dobrev

20

아마도 (이전 답변과 비슷하지만 더 간단한 명령을 사용하여) 단순히 별도의 이전 리포지토리 각각에 내용을 적절한 이름의 하위 디렉토리로 이동시키는 커밋을 만듭니다.

$ cd phd/code
$ mkdir code
# This won't work literally, because * would also match the new code/ subdir, but you understand what I mean:
$ git mv * code/
$ git commit -m "preparing the code directory for migration"

다음과 같이 smth를 수행하여 세 개의 개별 저장소를 하나의 새로운 저장소로 병합합니다.

$ cd ../..
$ mkdir phd.all
$ cd phd.all
$ git init
$ git pull ../phd/code
...

그런 다음 기록을 저장하지만 단일 저장소로 진행합니다.


괜찮습니다.하지만 한 레포를 다른 레포로 병합하는 경우 (즉, phd는 이미 존재하지 않는 레포지토리가 아니 었습니다) phd에 코드 디렉토리의 하위 폴더와 동일한 이름을 가진 폴더가있는 경우 'git pull.' / phd / code '는 orignal 경로로 모든 커밋을 가져오고 마지막에 mv 커밋을 적용합니다.
tymtam

1
@Tymek : 그러나 이것은 여전히 ​​문제없이 작동합니다. 좋지 않은 것은 역사의 경로가 "정확하지 않음"(새 경로에 해당)하지 않는다는 것입니다.
imz-Ivan Zakharyaschev

19

하위 트리 병합 전략을 시도 할 수 있습니다. 리포지토리 B를 리포지토리 A로 병합 할 수 있습니다. 장점 git-filter-branch은 리포지토리 A (이전 SHA1 합계) 기록을 다시 쓰지 않아도된다는 것입니다.


링크가 작동하지 않아 역사가 보존되지 않습니까?
tymtam

3
@Tymek (kernel.org의 죄송합니다 부분은 보안 위반 후에도 여전히 다운됩니다). 들어오는 리포지토리 B의 SHA1을 중단하지만 A는 그대로 유지됩니다.
Leif Gruenwoldt


1
@LeifGruenwoldt 첫 번째 링크가 작동 중입니다. 그리고 미러 링크가 사라 졌다고 생각합니다.
Vadim Kotov

9

git-filter-branch 솔루션은 잘 작동하지만 git repo가 ​​SVN 가져 오기에서 오는 경우 다음과 같은 메시지와 함께 실패 할 수 있습니다.

Rewrite 422a38a0e9d2c61098b98e6c56213ac83b7bacc2 (1/42)mv: cannot stat `/home/.../wikis/nodows/.git-rewrite/t/../index.new': No such file or directory

이 경우 필터 브랜치에서 초기 개정을 제외해야합니다. 즉 HEAD, 끝 부분을 다음 과 같이 변경 [SHA of 2nd revision]..HEAD하십시오.

http://www.git.code-experiments.com/blog/2010/03/merging-git-repositories.html


2
감사합니다! 왜 이것이 작동하지 않는지 머리를 긁었습니다. 레포는 실제로 SVN에서 나왔습니다.
Arthur Maltson

1
그렇게 할 때도 같은 오류가 발생합니다. 내 희망을 얻었다. 또한 링크가 끊어졌습니다.
Ryan

"...에서 머리를 바꾸다"라는 의미를 정교하게 설명해 주시겠습니까? 제 레포는 SVN 가져 오기에서 나 왔으며이 문제에 정확히 직면하고 있습니다. 많은 도움을 주셔서 감사합니다!

5

@MiniQuark 솔루션은 많은 도움이되었지만 불행히도 소스 리포지토리에있는 태그는 고려하지 않았습니다 (적어도 제 경우에는). 아래는 @MiniQuark 답변에 대한 개선 사항입니다.

  1. 먼저 구성된 리포지토리 및 병합 된 리포지토리를 포함 할 디렉토리를 만들고 병합 된 각 리포지토리에 대한 디렉토리를 만듭니다.

    $ mkdir new_phd
    $ mkdir new_phd / code
    $ mkdir new_phd / figures
    $ mkdir new_phd / thesis

  2. 각 저장소를 가져 와서 모든 태그를 가져옵니다. (에 대한 프리젠 테이션 지시 사항 만code 하위 디렉토리에 )

    $ cd new_phd / code
    $ git init
    $ git pull ../../original_phd/code master
    $ git fetch ../../original_phd/code refs / tags / * : refs / tags / *

  3. 의 내용을 이동 (이 MiniQuark의 대답 점 2 개선) new_phd/code에을new_phd/code/code 추가합니다 code_각각 전에 prefeix를 태그

    $ git filter-branch --index-filter 'git ls-files -s | sed "s- \ t \"*-& code /- "| GIT_INDEX_FILE = $ GIT_INDEX_FILE.new git update-index --index-info && mv $ GIT_INDEX_FILE.new $ GIT_INDEX_FILE '--tag-name-filter sed" -. *-code _ &- " 'HEAD

  4. 이렇게하면 필터 브랜치를 수행하기 전보다 두 배 많은 태그가 생깁니다. 이전 태그는 repo에 남아 있고 새 태그는code_ 접두사가있는 가 추가됩니다.

    $ 자식 태그
    mytag1
    code_mytag1

    이전 태그를 수동으로 제거하십시오.

    $ ls .git / refs / tags / * | grep -v "/ code_"| xargs rm

    다른 서브 디렉토리에 대해 반복 지점 2,3,4

  5. 이제 @MiniQuark anwser point 3에서와 같이 디렉토리 구조를 갖습니다.

  6. MiniQuark anwser의 포인트 4에서와 같이 수행하지만 당기기를 한 후 .gitdir 을 제거하기 전에 태그를 가져옵니다.

    $ git fetch 카탈로그 refs / tags / * : refs / tags / *

    계속하다..

이것은 또 다른 해결책입니다. 그것이 누군가를 돕는 희망, 그것은 나를 도왔습니다 :)


5

Aristotle Pagaltzis의 답변 에서 git-stitch-repo는 단순하고 선형적인 기록을 가진 리포지토리에서만 작동합니다.

MiniQuark의 답변 은 모든 리포지토리에서 작동하지만 태그 및 분기는 처리하지 않습니다.

MiniQuark에서 설명한 것과 같은 방식으로 작동하는 프로그램을 만들었지 만 N 개의 부모와 함께 하나의 병합 커밋을 사용하고 이러한 병합 커밋을 가리 키도록 모든 태그와 분기를 다시 만듭니다.

사용 방법에 대한 예제 는 git-merge-repos 저장소 를 참조하십시오 .



3

실제로 git-stitch-repo는 이제 주석이 달린 태그를 포함하여 브랜치와 태그를 지원합니다 (보고 된 버그가 발견되어 수정되었습니다). 내가 유용하다고 생각한 것은 태그입니다. 태그가 커밋에 첨부되고 Eric Lee의 접근 방식과 같은 일부 솔루션이 태그를 처리하지 못하기 때문에. 가져온 태그에서 브랜치를 만들려고하면 git merge / moves가 실행 취소되고 통합 리포지토리가 태그의 리포지토리와 거의 같은 것처럼 다시 전송됩니다. 또한 '병합 / 통합'한 여러 리포지토리에서 동일한 태그를 사용하는 경우 문제가 있습니다. 예를 들어 리포지토리의 A ad B가 있고 태그가 rel_1.0 인 경우 리포지토리 A와 리포지토리 B를 리포지토리 AB에 병합합니다. rel_1.0 태그는 두 개의 다른 커밋 (하나는 B, 하나는 B)에 있으므로 AB에 어떤 태그가 표시됩니까? 가져온 리포지토리 A 또는 가져온 리포지토리 B의 태그 (둘다는 아님)

git-stitch-repo는 rel_1.0-A 및 rel_1.0-B 태그를 생성하여 해당 문제를 해결하는 데 도움이됩니다. rel_1.0 태그를 체크 아웃하지 못하고 둘 다 예상 할 수는 있지만 적어도 둘 다 볼 수 있으며 이론적으로는 공통 로컬 브랜치로 병합 한 다음 병합 된 브랜치에서 rel_1.0 태그를 만들 수 있습니다 ( 소스 코드를 병합하고 변경하지 마십시오). 각 리포지토리의 브랜치처럼 로컬 브랜치로 병합 할 수 있으므로 브랜치를 사용하는 것이 좋습니다. (dev-a와 dev-b는 로컬 dev 브랜치로 병합되어 원점으로 푸시 될 수 있습니다).


2

제안한 순서

git init
git add *
git commit -a -m "import everything"

작동하지만 커밋 기록을 잃게됩니다.


역사를 잃어 버리는 것은 그리 나쁘지는 않지만 저장소가 내 작업 (개인용)이기 때문에 버전 관리를 원하지 않거나 아직 버전 화되지 않은 것들이 많이 있습니다.
윌 로버트슨

1

mainProject 내에서 secondProject를 병합하려면 :

A) 두 번째 프로젝트에서

git fast-export --all --date-order > /tmp/secondProjectExport

B) mainProject에서 :

git checkout -b secondProject
git fast-import --force < /tmp/secondProjectExport

이 브랜치에서 필요한 모든 변환 작업을 수행하고 커밋하십시오.

C) 그런 다음 마스터로 돌아가서 두 지점 사이의 고전적인 병합 :

git checkout master
git merge secondProject

이것은 두 자식 프로젝트의 루트에있는 모든 파일과 폴더를 하나의 프로젝트로 병합합니다. _anyone_이이 일을 원할 것 같아 의심합니다.
Clintm

0

내 솔루션도 여기에 넣을 것입니다. 기본적으로 상당히 간단한 bash 스크립트 래퍼 git filter-branch입니다. 다른 솔루션과 마찬가지로 마스터 브랜치 만 마이그레이션하고 태그는 마이그레이션하지 않습니다. 그러나 전체 마스터 커밋 기록은 마이그레이션되며 짧은 bash 스크립트이므로 사용자가 검토하거나 조정하기가 비교적 쉽습니다.

https://github.com/Oakleon/git-join-repos


0

이 bash 스크립트는 sed 탭 문자 문제 (예 : MacOS) 및 누락 된 파일 문제를 해결합니다.

export SUBREPO="subrepo"; # <= your subrepository name here
export TABULATOR=`printf '\t'`;
FILTER='git ls-files -s | sed "s#${TABULATOR}#&${SUBREPO}/#" |
  GIT_INDEX_FILE=$GIT_INDEX_FILE.new \
  git update-index --index-info &&
  if [ -f "$GIT_INDEX_FILE.new" ]; then mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE; else echo "git filter skipped missing file: $GIT_INXEX_FILE.new"; fi'

git filter-branch --index-filter "$FILTER" HEAD

이것은 miniquark , marius-butucryan 의 게시물 조합입니다 . 그들에게 건배!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.