큰 Git 저장소를 여러 개의 작은 저장소로 분할


86

SVN 리포지토리를 Git으로 성공적으로 변환 한 후 이제 여러 개의 작은 리포지토리로 나누고 기록을 유지하려는 매우 큰 Git 리포지토리를 갖게되었습니다.

따라서 누군가 다음과 같은 repo를 분해하는 데 도움을 줄 수 있습니까?

MyHugeRepo/
   .git/
   DIR_A/
   DIR_B/
   DIR_1/
   DIR_2/

다음과 같은 두 개의 저장소로 :

MyABRepo/
   .git
   DIR_A/
   DIR_B/

My12Repo/
   .git
   DIR_1/
   DIR_2/

이 이전 질문에서 지침을 따르려고 시도했지만 여러 디렉토리를 별도의 저장소에 넣으려고 할 때 실제로 적합하지 않습니다 ( Detach (move) 하위 디렉토리를 별도의 Git 저장소로 ).


11
답변이 만족 스러우면 수락 됨으로 표시하세요.
Ben Fowler

1
(일부 프로젝트에서는 더 어려울 수있는 여러 디렉토리를 제거하는 대신) 여러 (중첩 된) 디렉토리를 새 저장소로 분할하려는 모든 사람에게이 답변이 도움이되었습니다. stackoverflow.com/a/19957874/164439
thaddeusmt

답변:


80

그러면 MyABRepo가 설정됩니다. 물론 My12Repo도 비슷하게 할 수 있습니다.

git clone MyHugeRepo/ MyABRepo.tmp/
cd MyABRepo.tmp
git filter-branch --prune-empty --index-filter 'git rm --cached --ignore-unmatch DIR_1/* DIR_2/*' HEAD 

.git / refs / original / refs / heads / master에 대한 참조가 남아 있습니다. 다음과 같이 제거 할 수 있습니다.

cd ..
git clone MyABRepo.tmp MyABRepo

모든 것이 잘되면 MyABRepo.tmp를 제거 할 수 있습니다.


어떤 이유로 .git-rewrite와 관련된 오류가 발생하면 다음을 시도 할 수 있습니다.

git clone MyHugeRepo/ MyABRepo.tmp/
cd MyABRepo.tmp
git filter-branch -d /tmp/git-rewrite.tmp --prune-empty --index-filter 'git rm --cached --ignore-unmatch DIR_1/* DIR_2/*' HEAD 
cd ..
git clone MyABRepo.tmp MyABRepo

이렇게하면 /tmp/git-rewrite.tmp를 임시 디렉터리로 만들어 .git-rewrite. 당연히 /tmp/git-rewrite.tmp쓰기 권한이 있고 디렉토리가 아직 존재하지 않는 한 대신 원하는 경로로 대체 할 수 있습니다.


'git filter-branch'맨 페이지에서는 위에서 언급 한 마지막 단계 대신 재 작성된 저장소의 새 복제본을 만들 것을 권장합니다.
Jakub Narębski

나는 이것을 시도하고 끝에 .git-rewrite 폴더를 삭제하려고 할 때 오류가 발생했습니다.
MikeM

-d <path-on-another-physical-disk>가 나를 위해 일했고 --tree-filter 내에서 'mv'오류를 제거했습니다.
Vertigo

제외 된 경로 (예 :)와 관련된 경우 첫 번째 커밋을 얻는 방법을 알고 DIR_A있습니까?
bitmask

1
나는의 전체 결과를 깨닫지 못했습니다 filter-branch. 모르는 사람들을 위해 히스토리를 다시 작성하므로이 작업을 수행 한 후 리포지토리를 푸시하려는 경우 커밋 해시가 지금 달라지고 작동하지 않습니다.
thaddeusmt

10

git filter-branch --index-filterwith git rm --cached를 사용 하여 원래 저장소의 복제본 / 복사본에서 원하지 않는 디렉토리를 삭제할 수 있습니다 .

예를 들면 :

trim_repo() { : trim_repo src dst dir-to-trim-out...
  : uses printf %q: needs bash, zsh, or maybe ksh
  git clone "$1" "$2" &&
  (
    cd "$2" &&
    shift 2 &&

    : mirror original branches &&
    git checkout HEAD~0 2>/dev/null &&
    d=$(printf ' %q' "$@") &&
    git for-each-ref --shell --format='
      o=%(refname:short) b=${o#origin/} &&
      if test -n "$b" && test "$b" != HEAD; then 
        git branch --force --no-track "$b" "$o"
      fi
    ' refs/remotes/origin/ | sh -e &&
    git checkout - &&
    git remote rm origin &&

    : do the filtering &&
    git filter-branch \
      --index-filter 'git rm --ignore-unmatch --cached -r -- '"$d" \
      --tag-name-filter cat \
      --prune-empty \
      -- --all
  )
}
trim_repo MyHugeRepo MyABRepo DIR_1 DIR_2
trim_repo MyHugeRepo My12Repo DIR_A DIR_B

각 저장소의 불필요한 브랜치 또는 태그를 수동으로 삭제해야합니다 (예 : feature-x-for-AB 브랜치가있는 경우 "12"저장소에서 삭제할 수 있음).


1
:bash의 주석 문자가 아닙니다. #대신 사용해야 합니다.
Daenyth 2010 년

4
@Daenyth :는 기존의 기본 제공 명령입니다 ( POSIX에도 지정됨 ). bash에 포함되어 있지만 주석은 아닙니다. #모든 셸이 #모든 컨텍스트 (예 : INTERACTIVE_COMMENTS 옵션이 활성화되지 않은 대화 형 zsh) 에서 주석 소개 자로 사용되는 것은 아니기 때문에 나는 특히이를 선호하여 사용했습니다 . 를 사용 :하면 전체 텍스트를 대화 형 셸에 붙여넣고 스크립트 파일에 저장하는 데 적합합니다.
Chris Johnsen

1
훌륭한! 모든 가지를 그대로 유지하는 유일한 해결책
pheelicks

이 멈 춥니 다 나를 위해 홀수, git remote rm origin항상 1을 반환하는 것, 그러므로 나는 대체 &&하여 ;이 선합니다.
kynan

좋네요, $ @는 필요할 때 두 개 이상의 dirs에서 작동합니다. 완료되면 전화를 겁니다 git remote add origin $TARGET; git push origin master.
Walter A

6

git_split 프로젝트는 원하는 것을 정확히 수행하는 간단한 스크립트입니다. https://github.com/vangorra/git_split

git 디렉토리를 자체 위치에있는 자체 저장소로 전환합니다. 하위 트리 재미있는 사업이 없습니다. 이 스크립트는 git 저장소의 기존 디렉토리를 가져 와서 해당 디렉토리를 자체 독립 저장소로 바꿉니다. 그 과정에서 사용자가 제공 한 디렉토리의 전체 변경 내역을 복사합니다.

./git_split.sh <src_repo> <src_branch> <relative_dir_path> <dest_repo>
        src_repo  - The source repo to pull from.
        src_branch - The branch of the source repo to pull from. (usually master)
        relative_dir_path   - Relative path of the directory in the source repo to split.
        dest_repo - The repo to push to.


1

귀하의 답변에 감사하지만 저장소를 두 번 복사 한 다음 각각에서 원하지 않는 파일을 삭제했습니다. 나중에 다른 곳에서 버전이 제어되기 때문에 삭제 된 파일에 대한 모든 커밋을 제거하기 위해 나중에 filter-branch를 사용할 것입니다.

cp -R MyHugeRepo MyABRepo
cp -R MyHugeRepo My12Repo

cd MyABRepo/
rm -Rf DIR_1/ DIR_2/
git add -A
git commit -a

이것은 내가 필요한 것을 위해 일했습니다.

편집 : 물론 A 및 B 디렉토리에 대해 My12Repo에서 동일한 작업이 수행되었습니다. 이것은 원치 않는 디렉토리를 삭제할 때까지 동일한 기록을 가진 두 개의 저장소를 제공했습니다.


1
이것은 커밋 기록을 보존하지 않습니다.
Daenyth 2010 년

어떻게? 삭제 된 파일에 대해서도 여전히 모든 기록이 있습니다.
MikeM 2010 년

1
당신의 요구 사항은 repo A가 repo B가 존재하지 않는 것처럼해야한다는 것이 아니었기 때문에 이것이 (B에만 영향을 미치는 커밋 기록을 남기는) 적절한 해결책이라고 생각합니다. 그것을 엉망으로 만드는 것보다 약간의 역사를 복제하는 것이 낫습니다.
Steve Clay
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.