2017-05-19 17 views
0

svn 저장소에 3 년이 넘는 기록이있는 프로젝트가 있습니다. 그것은 git로 마이그레이션되었지만 이것을 한 사람은 지난 버전을 가져와이 3 년의 역사를 모두 버립니다.루트 커밋 부모를 다른 커밋 (두 개의 독립적 인 자식 저장소 연결)을 가리 키도록 변경하십시오.

프로젝트는 지난 3 개월에서 4 개월의 히스토리를 하나의 저장소에 가지고 있으며 다른 3 년간의 svn 히스토리를 새로운 git 저장소로 가져 왔습니다.

두 번째 저장소의 루트 커밋을 첫 번째 커밋의 마지막 커밋에 연결할 수있는 방법이 있습니까?

그것은이 같은 것입니다 :

* 2017-04-21 - last commit on master 
    | 
    * 2017-03-20 - merge branch Y into master 
    |\ 
    | * 2017-03-19 - commit on branch Y 
    | | 
    * | 2017-03-18 - merge branch X into master 
/| * 2017-02-17 - commit on another new branch Y 
* |/ 2017-02-16 - commit on branch X 
| * 2017-02-15 - commit on master branch 
* | 2017-01-14 - commit on new branch X 
\| 
    * 2017-01-13 - first commit on new repository 
    | 
    * 2017-01-12 - init new git project with the last version of the code in svn repository 
    . 
    . 
There is no relationship between the two different repositories yet, this is what I wanna 
do. I want to connect the root commit of 2nd repository with the last commit of the first 
one. 
    . 
    . 
    * 2017-01-09 - commit 
    | 
    * 2017-01-08 - commit 
    | 
    * 2017-01-07 - merge 
/| 
* | 2016-01-06 - 2nd commit the other branch 
| * 2016-01-05 - commit on trunk 
* | 2016-01-04 - commit on new branch 
\| 
    * 2015-01-03 - first commit 
    | 
    * 2015-01-02 - beggining of the project 

업데이트 :

난 그냥 내가 git rebase하지만, 어떻게해야 할 것을 알게

? SHA-1 코드와 같은 커밋 날짜를 고려해 보겠습니다. 대답은 --parent-filter 옵션을 사용하는 git filter-branch 옵션을 사용하는 것이고 git rebase이 아닌 것입니다.

업데이트 2 :

PS D:\git\rebase-test\rep2cc> git filter-branch --parent-filter 'test $GIT_COMMIT = 443aec8880e898710796a1c4fb4decea1ca5ff66 && echo "-p 98e2b95e07b84ad1e40c3231e66840ea910e9d66" || cat' HEAD 
fatal: ambiguous argument '98e2b95e07b84ad1e40c3231e66840ea910e9d66 || cat': unknown revision or path not in the working tree. 
Use '--' to separate paths from revisions, like this: 
'git <command> [<revision>...] -- [<file>...]' 

업데이트 3 : I 명령 git filter-branch --parent-filter 'test $GIT_COMMIT = 443aec8880e898710796a1c4fb4decea1ca5ff66 && echo "-p 98e2b95e07b84ad1e40c3231e66840ea910e9d66" || cat' HEAD을 시도하고 작동하지 않았다

그것은 윈도우 CMD에서 작동하지 않았거나 PowerShell,하지만 창에 힘내 배쉬에서 작동하지 않았다.

+2

글쎄, 같은 저장소 내에서 양쪽 모두를 가져 와서 다른 하나의 리포지토리를 리베이스하는 것을 고려 해보았습니까?이것은 물론 리베이스하는 히스토리의 모든 커밋을 다시 작성합니다. –

+1

Lasse가 올바르게 처리했습니다. 그냥 정확하게 svn 복제 된 svn 새 repo 설정,이 파열 된 repo 함께 원격 추가 fetch 및 체리 - 올바르게 복제 된 svn repo svn에서 복제 후 자식에서 수행 된 기록을 선택하십시오. – eftshift0

+0

나는 자식에 대해 새로운 데, 나는 찾고 있던 마법의 단어가 'rebase'라는 것을 배웠다. – lmcarreiro

답변

2

첫 번째 사항 : 사용 가능한 모든 기록이있는 단일 저장소가 필요합니다.

최근 기록으로 복제본을 만듭니다. 이전 기록이 포함 된 저장소를 원격 저장소로 추가합니다. 나는이 복제품을 "거울"이라고 추천하고 원산지 repo를이 복제품으로 교체하는 것으로 끝내는 것이 좋습니다. 그러나 번갈아 가면 --mirror을 끈 상태로 유지할 수 있으며, 모든 참조가 원점으로 돌아가도록 푸시 (어느 접근법에 따라 강제 푸시) 할 수 있습니다.

git clone --mirror url/of/current/repo 
cd repo 
git remote add history url/of/historical/repo 
git fetch history 

다음으로 수행해야 할 작업은 연결을 어디에 연결 할지를 파악하는 것입니다. 이것을 설명하는 용어는 약간 퍼지라고 생각합니다 ... 당신이 원하는 것은 두 개의 히스토리 모두 커밋 된 최신 SVN 리비전에 해당하는 두 개의 커밋을 찾는 것입니다. 예를 들어 당신의 SVN의 repo는 버전 1, 2, 3에 포함하고, 4. 이제 A 버전 1을 나타냅니다

Recent-History Repo 

C --- D --- E --- F <--(master) 

Old-History Repo 

A --- B --- C' --- D' 

이, B는 버전 2, CC' 버전 3을 나타내며, DD' 버전을 나타냅니다를 나타냅니다 4. EF은 원래 마이그레이션 후에 작성된 작업입니다. 따라서 부모가 D (이 예에서는 E) 인 커밋을 D'에 연결하고 싶습니다.

이제는 장단점이있는 두 가지 접근 방식을 생각해 볼 수 있습니다.새의 repo 모든 개발자의 한 컷 오버를 조정할 수있는 경우

'최근 역사 IMO

에게 가장 좋은 방법 을 다시 쓰기 (모두 모두 뛰어난 작품이 동의 할 때 시간을 마련 의미 푸시 된, 그래서 그들은 그들의 복제품을 버리고, 당신은 변환을하고, 다시 복제하는 것은 최근 역사를 오래된 역사에 재배치하는 것입니다.

정말 단 하나의 지점이 있다면, 당신은 문자 그대로 (DD'이 커밋의 SHA ID로 대체됩니다)

git rebase --onto D' D master 

리베이스 사용할 수 있습니다.

최근 기록에 일부 분기 및 병합 가능성이 더 높습니다. 이 경우 rebase 작업은 매우 빨리 문제가되기 시작합니다. 반면에 D에는 D'과 동일한 트리가 있으므로 rebase와 re-parent가 다소 비슷합니다.

git filter-branch--parent-filter을 사용하여 다시 쓰면됩니다. https://git-scm.com/docs/git-filter-branch에서 문서의 예제를 바탕으로 당신은 (DD'이 커밋의 SHA ID로 대체됩니다 다시)

git filter-branch --parent-filter 'test $GIT_COMMIT = D && echo "-p D'" || cat' HEAD 

같은 일을 할 것입니다.

이렇게하면 정리해야 할 "백업"참조가 생성됩니다. 결국 당신은 그것은 F 하드 컷 오버 (자세한 이하)의 필요성을 생성 F'에 의해 대체 있다는 사실이다

A --- B --- C' --- D' --- E' --- F' <--(master) 

얻을 것이다.

이제 1 단계에서 미러 복제본을 만들었다면 reflog를 지우고 리모컨을 삭제하고 gc을 실행하는 것이 좋습니다. 그러면 바로 사용할 수있는 새로운 원본 저장소입니다.

일반 클론을 만든 경우 원점에 대한 모든 참조가 push -f 일 필요가 있습니다. 그러면 원본 원고에 약간의 혼란이 생길 ​​수 있습니다. 사용

다른 옵션은 하드 컷 오버를 만들지 않습니다 "대체 커밋",하지만 영원히 다루는 작은 두통 당신을 떠난다. git replace을 사용할 수 있습니다. 로그 출력 또는 무엇이든을 생성 할 때 자식이 D을 발견 한 경우 결합의 repo 기본적으로

git replace `D` `D'` 

에서, 그것은 출력에 D' (의 역사)를 대체합니다.

몇 가지 알려진 글리치가 있습니다. 알 수없는 결함이있을 수 있습니다. 그리고 기본적으로이 모든 작업을하는 "대체 참조"는 공유되지 않으므로 의도적으로 밀어 넣고 가져와야합니다.

+0

나는 자식을 대체하려고했지만, 내가 원하는 것은 아니다. 그리고 저는 역사상 많은 지류와 합병점을 가지고 있습니다, 그래서 rebase operation은 문제가되었습니다. – lmcarreiro

+0

이 세 번째 옵션 인 'P'와 'P'는 무엇인가요? 명령 'git filter-branch --parent-filter'테스트 $ GIT_COMMIT = D && echo "-p D '"|| 고양이 'HEAD'가 창문에서 작동할까요? – lmcarreiro

+0

죄송합니다. "P"와 "P"는 "다른 곳에서와 마찬가지로"D "와"D "였어야합니다. 나는 갱신했다. 명령은'git bash' 쉘의 창에서 작동합니다; 'D'와'D''를 적절한 커밋 참조 (예 : SHA ids)로 대체하는 것을 잊지 마십시오. –