[git] 자식 부모 포인터를 다른 부모로 설정

과거에 한 부모를 가리키는 커밋이 있지만 부모가 가리키는 부모를 변경하고 싶다면 어떻게해야합니까?



답변

사용 git rebase. Git의 일반적인 “커밋을 가져 와서 다른 부모 (베이스)에서 실행”명령입니다.

그러나 알아야 할 사항 :

  1. 커밋 SHA는 부모를 포함하기 때문에 주어진 커밋의 부모를 변경하면 SHA는 모두 의 SHA와 마찬가지로 변경됩니다. 개발 라인에서 커밋 이후에 오는 커밋 .

  2. 다른 사람들과 함께 일하고 있고 문제의 커밋을 끌어온 위치로 이미 푸시 한 경우 커밋을 수정하는 것은 아마도 Bad Idea ™ 일 것입니다. 이것은 # 1 때문입니다. 따라서 SHA가 “동일한”커밋에 대해 더 이상 일치하지 않기 때문에 다른 사용자의 리포지토리에서 발생할 수있는 혼란이 발생합니다. (자세한 내용은 링크 된 매뉴얼 페이지의 “UPSTREAM REBASE 복구”섹션을 참조하십시오.)

즉, 현재 새로운 커밋으로 옮기고 싶은 커밋이있는 지점에 있다면 다음과 같이 보일 것입니다.

git rebase --onto <new-parent> <old-parent>

즉 모든 이동 <old-parent> 위에 앉아 현재의 지점에서 <new-parent>대신.


답변

후속 커밋을 리베이스하지 않아야하는 경우 (예 : 히스토리 다시 쓰기를 유지할 수 없기 때문에) git replace (Git 1.6.5 이상에서 사용 가능)를 사용할 수 있습니다.

# …---o---A---o---o---…
#
# …---o---B---b---b---…
#
# We want to transplant B to be "on top of" A.
# The tree of descendants from B (and A) can be arbitrarily complex.

replace_first_parent() {
    old_parent=$(git rev-parse --verify "${1}^1") || return 1
    new_parent=$(git rev-parse --verify "${2}^0") || return 2
    new_commit=$(
      git cat-file commit "$1" |
      sed -e '1,/^$/s/^parent '"$old_parent"'$/parent '"$new_parent"'/' |
      git hash-object -t commit -w --stdin
    ) || return 3
    git replace "$1" "$new_commit"
}
replace_first_parent B A

# …---o---A---o---o---…
#          \
#           C---b---b---…
#
# C is the replacement for B.

위의 대체가 설정되면 오브젝트 B에 대한 모든 요청은 실제로 오브젝트 C를 리턴합니다. C의 컨텐츠는 첫 번째 상위 (동일한 상위 (첫 번째 제외), 동일한 트리, 같은 커밋 메시지).

대체는 기본적으로 활성화되지만 git--no-replace-objects 옵션 (명령 이름 이전)을 사용하거나 환경 변수 를 설정하여 활성화 할 수 있습니다 . (정상 이외에도)를 눌러 교체를 공유 할 수 있습니다 .GIT_NO_REPLACE_OBJECTSrefs/replace/*refs/heads/*

commit-munging이 마음에 들지 않으면 ( 위의 sed로 완료 ) 상위 명령을 사용하여 대체 커밋을 만들 수 있습니다.

git checkout B~0
git reset --soft A
git commit -C B
git replace B HEAD
git checkout -

큰 차이점은 B가 병합 커밋 인 경우이 시퀀스가 ​​추가 부모를 전파하지 않는다는 것입니다.


답변

Git에서 커밋을 변경하려면 그 뒤에 오는 모든 커밋을 변경해야합니다. 이 부분의 역사를 발표 한 경우에는 권장하지 않으며 누군가 변경 이전의 역사에 대한 작업을 수행했을 수도 있습니다.

Amber의 답변git rebase언급 된 다른 해결책 은 이식 메커니즘 ( Git 용어집 의 Git 이식 정의 및 Git Repository Layout 문서 의 파일 문서 참조)을 사용하여 커밋의 부모를 변경하고 일부 히스토리 뷰어 ( , 등)을 사용한 다음 맨 페이지의 “예제”섹션에 설명 된대로이를 사용하여 영구적으로 이식 한 다음 이식편을 제거하고 선택적으로 원본 참조를 제거합니다..git/info/graftsgitkgit log --graphgit filter-branchgit filter-branch 하거나 저장소를 복제하십시오.

echo "$ commit-id $ graft-id">> .git / info / grafts
git filter-branch $ graft-id..HEAD

노트 !!! 이 솔루션은 rebase 솔루션다릅니다.git rebase 이식 기반 솔루션은 변경 사항을 rebase / transplant 하는 반면, 이식 기반 솔루션은 기존 부모와 새 부모의 차이점을 고려하지 않고 커밋을 그대로 다시 지정합니다!


답변

위의 답변을 명확히하고 내 자신의 스크립트를 뻔뻔스럽게 연결하십시오.

“rebase”또는 “reparent”여부에 따라 다릅니다. REBASE는 에 의해 제안, 황색 , 주위 이동 차이점을 . reparent는 에 의해 제안, 야쿱크리스 , 주위 이동 스냅 샷 전체 트리를. 부모님을 원한다면git reparent 수동으로 작업하는 대신 하는 .

비교

왼쪽에 그림이 있고 오른쪽 그림처럼 보이길 원한다고 가정하십시오.

                   C'
                  /
A---B---C        A---B---C

rebasing과 reparenting은 같은 그림을 만들지 만 그 정의는 C'다릅니다. 로 git rebase --onto A B,에 C'의해 도입 된 변경 사항이 포함되지 않습니다 B. 함께 git reparent -p A, C'동일 할 것이다 C(즉, 제외 B역사상되지 않음).


답변

확실히 @Jakub의 대답은 몇 시간 전에 OP와 정확히 같은 것을 시도했을 때 도움이되었습니다.
그러나 git replace --graft이제 이식편에 관한 더 쉬운 해결책입니다. 또한 그 솔루션의 주요 문제점은 필터 브랜치가 HEAD의 브랜치로 병합되지 않은 모든 브랜치를 느슨하게 만들었습니다. 그런 다음 git filter-repo완벽하고 완벽하게 작업을 수행했습니다.

$ git replace --graft <commit> <new parent commit>
$ git filter-repo --force

자세한 정보 : 문서의 “재 이식 기록”섹션을 확인하십시오.


답변