과거에 한 부모를 가리키는 커밋이 있지만 부모가 가리키는 부모를 변경하고 싶다면 어떻게해야합니까?
답변
사용 git rebase
. Git의 일반적인 “커밋을 가져 와서 다른 부모 (베이스)에서 실행”명령입니다.
그러나 알아야 할 사항 :
-
커밋 SHA는 부모를 포함하기 때문에 주어진 커밋의 부모를 변경하면 SHA는 모두 의 SHA와 마찬가지로 변경됩니다. 개발 라인에서 커밋 이후에 오는 커밋 .
-
다른 사람들과 함께 일하고 있고 문제의 커밋을 끌어온 위치로 이미 푸시 한 경우 커밋을 수정하는 것은 아마도 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_OBJECTS
refs/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/grafts
gitk
git log --graph
git filter-branch
git 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
자세한 정보 : 문서의 “재 이식 기록”섹션을 확인하십시오.