Programming

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

procodes 2020. 2. 17. 22:25
반응형

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

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 저장소 자체에서이의 잘 알려진 예입니다 어느 멋진 병합 이 설명 리누스 토발즈 (Linus Torvalds)가 망할 놈의 메일 링리스트에 전자 메일에 사용되는 제목 후에는"( 병합). 이 경우 gitkGit GUI의 일부인 Git GUI는 실제로 별도의 프로젝트였습니다. Linus는 해당 리포지토리를 Git 리포지토리에 병합하여

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

전자 메일에는 재생산에 필요한 단계가 포함되어 있지만 마음이 희미하지는 않습니다. 첫째, Linus Git을 작성 했으므로 아마도 Git에 대해 조금 더 알고있을 것입니다. 두번째는 거의 5 년 전입니다. 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 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


이 함수는 원격 저장소를 로컬 저장소 디렉토리로 복제하고, 모든 커밋을 병합 한 후 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 이후에만 존재합니다.


조금 더 간단하다고 생각되는 다른 대답을 추가하십시오. repo_dest 풀은 repo_to_import로 완료된 후 push --set-upstream url : repo_dest 마스터가 수행됩니다.

이 방법은 여러 개의 작은 저장소를 더 큰 저장소로 가져 오는 데 효과적이었습니다.

가져 오는 방법 : repo1_to_import to repo_dest

# checkout your repo1_to_import if you don't have it already 
git clone url:repo1_to_import repo1_to_import
cd repo1_to_import

# now. pull all of repo_dest
git pull url:repo_dest
ls 
git status # shows Your branch is ahead of 'origin/master' by xx commits.
# now push to repo_dest
git push --set-upstream url:repo_dest master

# repeat for other repositories you want to import

가져 오기 전에 파일과 디렉토리의 이름을 원래 위치에서 원하는 위치로 바꾸거나 이동하십시오. 예 :

cd repo1_to_import
mkdir topDir
git add topDir
git mv this that and the other topDir/
git commit -m"move things into topDir in preparation for exporting into new repo"
# now do the pull and push to import

다음 링크에서 설명 된 방법이이 답변에 영감을주었습니다. 더 단순 해 보였기 때문에 마음에 들었습니다. 그러나 조심하십시오! 용이있다! https://help.github.com/articles/importing-an-external-git-repository git push --mirror url:repo_dest 는 로컬 리포지토리 기록 및 상태를 원격 (url : repo_dest)으로 푸시합니다. 그러나 원격의 이전 기록과 상태를 삭제합니다. 재미있다! :-이자형


필자의 경우 다른 저장소 (XXX)에서 일부 파일 만 가져오고 싶었습니다. 하위 트리가 너무 복잡하여 다른 솔루션이 작동하지 않았습니다. 이것이 내가 한 일입니다.

ALL_COMMITS=$(git log --reverse --pretty=format:%H -- ZZZ | tr '\n' ' ')

이것은 가져오고 자하는 파일 (ZZZ)에 영향을 미치는 모든 커밋의 공백으로 구분 된 목록을 역순으로 제공합니다 (이름 변경을 캡처하려면 --follow를 추가해야 할 수도 있음). 그런 다음 대상 리포지토리 (YYY)로 이동하여 다른 리포지토리 (XXX)를 원격으로 추가하고 가져 왔습니다.

git cherry-pick $ALL_COMMITS

이것은 모든 커밋을 브랜치에 추가하므로 히스토리가있는 모든 파일을 갖게되며 마치이 저장소에 항상있는 것처럼 원하는 파일을 처리 할 수 ​​있습니다.


나는 찾고 있던 상황에 -s theirs있었지만 물론이 전략은 존재하지 않습니다. 내 역사는 내가 GitHub에서 프로젝트를 분기 한 것이 었는데, 어떤 이유로 든 이 지점에 로컬 변경을하지 않았지만 어떤 이유로 로컬을 master병합 할 수 upstream/master없었습니다. (실제로 무슨 일이 있었는지 모릅니다. 업스트림이 무대 뒤에서 더러운 푸시를 한 것 같습니다.)

내가 끝낸 것은

# as per https://help.github.com/articles/syncing-a-fork/
git fetch upstream
git checkout master
git merge upstream/master
....
# Lots of conflicts, ended up just abandonging this approach
git reset --hard   # Ditch failed merge
git checkout upstream/master
# Now in detached state
git branch -d master # !
git checkout -b master   # create new master from upstream/master

이제 내 master동기화가 다시 동기화됩니다 upstream/master(동기화하려는 다른 지점에 대해서도 위의 과정을 반복 할 수 있습니다).


참조 기본 예제 에서 이 문서 와 저장소에 같은 매핑을 고려 :

  • A<-> YYY,
  • B <-> XXX

이 장에서 설명 된 모든 활동 후 (병합 후) branch를 제거하십시오 B-master.

$ git branch -d B-master

그런 다음 변경 사항을 푸시하십시오.

그것은 나를 위해 작동합니다.


문제에 대한 다른 해결책 ( git-submodules 대신)을 제안 할 수 있습니다 - 길 (git links) 도구

복잡한 자식 리포지토리 종속성을 설명하고 관리 할 수 ​​있습니다.

또한 git 재귀 하위 모듈 종속성 문제에 대한 솔루션을 제공합니다 .

다음과 같은 프로젝트 종속성이 있다고 가정하십시오. 샘플 git 저장소 종속성 그래프

그런 다음 .gitlinks리포지토리 관계 설명으로 파일을 정의 할 수 있습니다 .

# Projects
CppBenchmark CppBenchmark https://github.com/chronoxor/CppBenchmark.git master
CppCommon CppCommon https://github.com/chronoxor/CppCommon.git master
CppLogging CppLogging https://github.com/chronoxor/CppLogging.git master

# Modules
Catch2 modules/Catch2 https://github.com/catchorg/Catch2.git master
cpp-optparse modules/cpp-optparse https://github.com/weisslj/cpp-optparse.git master
fmt modules/fmt https://github.com/fmtlib/fmt.git master
HdrHistogram modules/HdrHistogram https://github.com/HdrHistogram/HdrHistogram_c.git master
zlib modules/zlib https://github.com/madler/zlib.git master

# Scripts
build scripts/build https://github.com/chronoxor/CppBuildScripts.git master
cmake scripts/cmake https://github.com/chronoxor/CppCMakeScripts.git master

각 줄은 git link를 다음 형식으로 설명합니다.

  1. 저장소의 고유 이름
  2. 저장소의 상대 경로 (.gitlinks 파일 경로에서 시작)
  3. git clone 명령에서 사용할 Git 저장소 저장소 브랜치
  4. 빈 줄이나 #으로 시작하는 줄은 구문 분석되지 않습니다 (주석으로 처리).

마지막으로 루트 샘플 저장소를 업데이트해야합니다.

# Clone and link all git links dependencies from .gitlinks file
gil clone
gil link

# The same result with a single command
gil update

결과적으로 필요한 모든 프로젝트를 복제하고 적절한 방식으로 서로 연결합니다.

하위 링크 리포지토리의 모든 변경 사항이있는 일부 리포지토리의 모든 변경 내용을 커밋하려면 단일 명령으로 수행 할 수 있습니다.

gil commit -a -m "Some big update"

풀, 푸시 명령은 비슷한 방식으로 작동합니다.

gil pull
gil push

Gil (git links) 도구는 다음 명령을 지원합니다.

usage: gil command arguments
Supported commands:
    help - show this help
    context - command will show the current git link context of the current directory
    clone - clone all repositories that are missed in the current context
    link - link all repositories that are missed in the current context
    update - clone and link in a single operation
    pull - pull all repositories in the current directory
    push - push all repositories in the current directory
    commit - commit all repositories in the current directory

자식 재귀 하위 모듈 종속성 문제 에 대해 자세히 알아보십시오 .


나는 그것을하는 쉬운 방법을 모른다. 당신은 이것을 할 수 있습니다 :

  1. git filter-branch를 사용하여 XXX 저장소에 ZZZ 슈퍼 디렉토리를 추가하십시오.
  2. 새 분기를 YYY 저장소로 푸시하십시오.
  3. 푸시 된 브랜치를 YYY의 트렁크와 병합합니다.

매력적으로 들리면 세부 사항으로 편집 할 수 있습니다.


'git mv'와 'git pull'을 사용 하여이 작업을 수행 할 수 있다고 생각합니다.

나는 공정한 멍청한 놈이다-그래서 당신의 주요 저장소에주의하십시오-그러나 나는 단지 임시 디렉토리에서 이것을 시도했고 그것은 효과가있는 것 같습니다.

먼저-YYY 내에있을 때의 모양과 일치하도록 XXX의 구조 이름을 바꿉니다.

cd XXX
mkdir tmp
git mv ZZZ tmp/ZZZ
git mv tmp ZZZ

이제 XXX는 다음과 같습니다.

XXX
 |- ZZZ
     |- ZZZ

이제 'git pull'을 사용하여 변경 사항을 가져옵니다.

cd ../YYY
git pull ../XXX

이제 YYY는 다음과 같습니다.

YYY
 |- ZZZ
     |- ZZZ
 |- (other folders that already were in YYY)

참고 URL : https://stackoverflow.com/questions/1683531/how-to-import-existing-git-repository-into-another

반응형