[git] 기존 Git 저장소를 다른 저장소로 가져 오는 방법은 무엇입니까?

XXX 라는 폴더에 Git 저장소가 있고 YYY 라는 두 번째 Git 저장소가 있습니다.

XXX 리포지토리를 YZZ 리포지토리로 ZZZ 라는 하위 디렉터리 로 가져오고 모든 XXX 의 변경 기록을 YYY에 추가 하려고 합니다.

이전 폴더 구조 :

├── XXX
│   ├── .git
│   └── (project files)
└── YYY
    ├── .git
    └── (project files)

다음의 폴더 구조 :

YYY
├── .git  <-- This now contains the change history from XXX
├──  ZZZ  <-- This was originally XXX
│    └── (project files)
└──  (project files)

이 작업을 수행 할 수 있습니까, 아니면 하위 모듈을 사용해야합니까?



답변

아마도 가장 간단한 방법은 XXX 항목YYY 의 지점으로 가져온 다음 마스터로 병합하는 것입니다.

YYY :

git remote add other /path/to/XXX
git fetch other
git checkout -b ZZZ other/master
mkdir ZZZ
git mv stuff ZZZ/stuff                      # repeat as necessary for each file/dir
git commit -m "Moved stuff to ZZZ"
git checkout master
git merge ZZZ --allow-unrelated-histories   # should add ZZZ/ to master
git commit
git remote rm other
git branch -d ZZZ                           # to get rid of the extra branch before pushing
git push                                    # if you have a remote, that is

나는 실제로 몇 가지 내 repos로 이것을 시도했고 작동합니다. Jörg의 답변 과 달리 다른 저장소를 계속 사용할 수는 없지만 어쨌든 지정하지 않았다고 생각합니다.

참고 : 이것은 2009 년에 처음 작성되었으므로 git은 아래 답변에 언급 된 하위 트리 병합을 추가했습니다. 물론이 방법은 여전히 ​​효과가 있지만 아마도 그 방법을 오늘 사용할 것입니다.


답변

두 번째 저장소의 정확한 커밋 히스토리를 유지하고 향후 업스트림 변경 사항을 쉽게 병합 할 수있는 기능을 유지하려면 원하는 방법이 있습니다. 병합 된 저장소를 서브 디렉토리로 이동시키기 위해 서브 트리의 수정되지 않은 히스토리가 리포지토리로 반입되고 하나의 병합 커밋이 발생합니다.

git remote add XXX_remote <path-or-url-to-XXX-repo>
git fetch XXX_remote
git merge -s ours --no-commit --allow-unrelated-histories XXX_remote/master
git read-tree --prefix=ZZZ/ -u XXX_remote/master
git commit -m "Imported XXX as a subtree."

다음과 같이 업스트림 변경 사항을 추적 할 수 있습니다.

git pull -s subtree XXX_remote master

Git은 병합을 수행하기 전에 루트가있는 위치를 자체적으로 파악하므로 후속 병합에 접두사를 지정할 필요가 없습니다.

단점은 병합의 역사에 파일이 (가 아닌 하위 디렉토리에) 접두어가 있다는 것입니다. 결과적으로 git log ZZZ/a병합 된 기록을 제외한 모든 변경 사항이 표시됩니다 (있는 경우). 넌 할 수있어:

git log --follow -- a

하지만 병합 된 기록 이외의 변경 사항은 표시되지 않습니다.

즉, ZZZrepository XXX에서 파일을 변경하지 않으면 --follow접두사 를 지정 하고 경로 를 지정해야 합니다. 두 저장소 모두에서 변경하면 두 가지 명령이 있으며 그 중 어느 것도 변경 사항을 모두 표시하지 않습니다.

2.9 이전의 힘내 버전 :에 --allow-unrelated-histories옵션을 전달할 필요가 없습니다 git merge.

단계 를 사용 read-tree하고 건너 뛰는 다른 대답의 방법은 merge -s ourscp로 파일을 복사하고 결과를 커밋하는 것과 사실상 다르지 않습니다.

원본 소스는 github의 “Subtree Merge”도움말에서 제공 됩니다. 그리고 또 다른 유용한 링크 .


답변

git-subtree은 히스토리를 유지하면서 (그리고이 질문과 관련이없는 것처럼 하위 트리의 히스토리를 분할하면서) 여러 리포지토리를 하나로 병합하는이 유스 케이스를 위해 설계된 스크립트입니다. 릴리스 1.7.11 이후 git 트리의 일부로 배포됩니다 .

<repo>개정시 저장소 를 <rev>서브 디렉토리로 병합하려면 다음과 같이 <prefix>사용하십시오 git subtree add.

git subtree add -P <prefix> <repo> <rev>

git-subtree 는보다 사용자 친화적 인 방식으로 하위 트리 병합 전략 을 구현합니다 .

귀하의 경우 YYY 저장소 내에서 다음을 실행합니다.

git subtree add -P ZZZ /path/to/XXX.git master

단점은 병합의 역사에 파일이 (가 아닌 하위 디렉토리에) 접두어가 있다는 것입니다. 결과적으로git log ZZZ/a병합 된 기록을 제외한 모든 변경 사항이 표시됩니다 (있는 경우). 넌 할 수있어:

git log --follow -- a

하지만 병합 된 기록 이외의 변경 사항은 표시되지 않습니다.

즉, ZZZrepository XXX에서 파일을 변경하지 않으면 --follow접두사 를 지정 하고 경로 를 지정해야 합니다. 두 저장소 모두에서 변경하면 두 가지 명령이 있으며 그 중 어느 것도 변경 사항을 모두 표시하지 않습니다.

여기 에 더 있습니다 .


답변

Git 저장소 자체에는 잘 알려진 인스턴스가 있습니다. Git 커뮤니티에서 Git 커뮤니티에서 ” 가장 멋진 병합 “으로 알려져 있습니다 (Litus Torvalds가 전자 메일에서 Git 메일 링리스트에 사용 된 제목 줄 다음에이를 설명 함). 병합). 이 경우 gitkGit GUI의 일부인 Git GUI는 실제로 별도의 프로젝트였습니다. Linus는 해당 리포지토리를 Git 리포지토리에 병합하여

  • Git 저장소에 항상 Git의 일부로 개발 된 것처럼 보입니다.
  • 모든 역사는 그대로 유지되고
  • 기존 저장소에서 독립적으로 개발할 수 있으며 변경 사항은 간단하게 변경 git pull됩니다.

전자 메일에는 재생산에 필요한 단계가 포함되어 있지만 마음이 희미하지는 않습니다. 첫째, Linus Git을 으므로 아마 당신이나 나보다 그것에 대해 조금 더 알고있을 것입니다. Git은 그 이후로 상당히 향상 되었으므로 훨씬 쉬울 것입니다.

특히 요즘에는 특정 상황에서 gitk 서브 모듈을 사용한다고 생각합니다.


답변

이를 수행하는 간단한 방법은 git format-patch를 사용하는 것입니다.

우리가 2 개의 git 저장소 foobar 가 있다고 가정하십시오 .

foo 는 다음을 포함합니다 :

  • foo.txt
  • .git

내용 :

  • bar.txt
  • .git

그리고 우리 는 히스토리 와이 파일들을 포함하는 foo 로 끝내고 싶습니다 :

  • foo.txt
  • .git
  • foobar / bar.txt

그렇게하려면 :

 1. create a temporary directory eg PATH_YOU_WANT/patch-bar
 2. go in bar directory
 3. git format-patch --root HEAD --no-stat -o PATH_YOU_WANT/patch-bar --src-prefix=a/foobar/ --dst-prefix=b/foobar/
 4. go in foo directory
 5. git am PATH_YOU_WANT/patch-bar/*

그리고 우리가 bar에서 모든 메시지 커밋을 다시 작성하려면 예를 들어 Linux에서 할 수 있습니다.

git filter-branch --msg-filter 'sed "1s/^/\[bar\] /"' COMMIT_SHA1_OF_THE_PARENT_OF_THE_FIRST_BAR_COMMIT..HEAD

각 커밋 메시지의 시작 부분에 “[bar]”가 추가됩니다.


답변

이 함수는 원격 저장소를 로컬 저장소 디렉토리에 복제하고, 모든 커밋을 병합 한 후 git log원래 커밋과 적절한 경로를 보여줍니다.

function git-add-repo
{
    repo="$1"
    dir="$(echo "$2" | sed 's/\/$//')"
    path="$(pwd)"

    tmp="$(mktemp -d)"
    remote="$(echo "$tmp" | sed 's/\///g'| sed 's/\./_/g')"

    git clone "$repo" "$tmp"
    cd "$tmp"

    git filter-branch --index-filter '
        git ls-files -s |
        sed "s,\t,&'"$dir"'/," |
        GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
        mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
    ' HEAD

    cd "$path"
    git remote add -f "$remote" "file://$tmp/.git"
    git pull "$remote/master"
    git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
    git remote remove "$remote"
    rm -rf "$tmp"
}

사용하는 방법:

cd current/package
git-add-repo https://github.com/example/example dir/to/save

약간 변경하면 병합 된 저장소의 파일 / 디렉토리를 다른 경로로 이동할 수도 있습니다. 예를 들면 다음과 같습니다.

repo="https://github.com/example/example"
path="$(pwd)"

tmp="$(mktemp -d)"
remote="$(echo "$tmp" | sed 's/\///g' | sed 's/\./_/g')"

git clone "$repo" "$tmp"
cd "$tmp"

GIT_ADD_STORED=""

function git-mv-store
{
    from="$(echo "$1" | sed 's/\./\\./')"
    to="$(echo "$2" | sed 's/\./\\./')"

    GIT_ADD_STORED+='s,\t'"$from"',\t'"$to"',;'
}

# NOTICE! This paths used for example! Use yours instead!
git-mv-store 'public/index.php' 'public/admin.php'
git-mv-store 'public/data' 'public/x/_data'
git-mv-store 'public/.htaccess' '.htaccess'
git-mv-store 'core/config' 'config/config'
git-mv-store 'core/defines.php' 'defines/defines.php'
git-mv-store 'README.md' 'doc/README.md'
git-mv-store '.gitignore' 'unneeded/.gitignore'

git filter-branch --index-filter '
    git ls-files -s |
    sed "'"$GIT_ADD_STORED"'" |
    GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
    mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
' HEAD

GIT_ADD_STORED=""

cd "$path"
git remote add -f "$remote" "file://$tmp/.git"
git pull "$remote/master"
git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
git remote remove "$remote"
rm -rf "$tmp"

공지 사항
경로는 via를 대체 sed하므로 병합 후 경로가 올바른 경로로 이동했는지 확인하십시오.
--allow-unrelated-histories매개 변수는 git> = 2.9 이후에만 존재합니다.


답변

이 기사를 기반 으로 하위 트리를 사용하면 나에게 효과적이며 적용 가능한 기록 만 전송되었습니다. 단계가 필요한 경우 여기에 게시하십시오 (자리 표시자를 해당하는 값으로 바꾸십시오).

소스 리포지토리에서 하위 폴더를 새 분기로 분할

git subtree split --prefix=<source-path-to-merge> -b subtree-split-result

분할 결과 분기의 대상 리포지토리 병합

git remote add merge-source-repo <path-to-your-source-repository>
git fetch merge-source-repo
git merge -s ours --no-commit merge-source-repo/subtree-split-result
git read-tree --prefix=<destination-path-to-merge-into> -u merge-source-repo/subtree-split-result

변경 사항을 확인하고 커밋

git status
git commit

잊지 마세요

subtree-split-result분기 를 삭제하여 정리

git branch -D subtree-split-result

소스 저장소에서 데이터를 가져 오기 위해 추가 한 리모컨을 제거하십시오.

git remote rm merge-source-repo