다양한 커밋을 선택하고 다른 지점으로 병합하는 방법은 무엇입니까?


640

다음과 같은 저장소 레이아웃이 있습니다.

  • 마스터 지점 (생산)
  • 완성

내가 달성하고자하는 것은 작업 지점에서 다양한 커밋을 선택하여 통합 지점으로 병합하는 것입니다. 나는 git을 처음 접했고 저장소를 엉망으로 만들지 않고 정확하게 수행하는 방법 (한 작업에서 커밋 범위의 체리 선택)을 알 수 없습니다. 이것에 대한 조언이나 생각이 있습니까? 감사!


답변:


808

다양한 커밋에 관해서는 체리 따기 이다 이었다 실용적이지.

마찬가지로 아래 언급 에 의해 키스 김 , 힘내 1.7.2+은 커밋의 범위를-선택 벚꽃 할 수있는 기능을 도입 (그러나 당신은 여전히 알 필요가 미래의 병합에 따기 체리의 결과 )

git cherry-pick "은 다양한 커밋
(예 :" cherry-pick A..B"및" cherry-pick --stdin") 을 고르는 방법을 배웠습니다 git revert." rebase [-i]"도 마찬가지입니다. 그러나 " "는 더 나은 시퀀싱 컨트롤" "을 지원하지 않습니다 .

데미안 의견 과 경고 :

" cherry-pick A..B"형식에서 A보다 오래된 것이어야합니다B .
순서가 잘못되면 명령이 자동으로 실패 합니다.

당신은 선택하려면 범위 B를 통해 D(포함)을 그 것이다 B^..D.
그림은 " 이전 커밋 범위에서 분기를 생성합니까? "를 참조하십시오 .

Jubobs 가 주석에서 언급 것처럼 :

이것은 B루트 커밋이 아니라고 가정합니다 . unknown revision그렇지 않으면 " "오류가 발생합니다.

참고 : Git 2.9.x / 2.10 (2016 년 3 분기) 기준으로 고아 브랜치 (빈 헤드)에서 직접 커밋 범위를 직접 선택할 수 있습니다 . " git에서 기존 브랜치를 고아로 만드는 방법 "을 참조하십시오 .


원래 답변 (2010 년 1 월)

A는 rebase --onto당신이 당신의 통합 지점의 상부에 커밋의 지정된 범위를 재생 곳으로, 더 나은 것 찰스 베일리는 여기에 설명 .
(또한, 대한 모습 "여기에 한 가지를 기반으로 주제 분기를 이식 얼마나 또 다른"는에서 자식 REBASE man 페이지 의 실제적인 예를 보려면 git rebase --onto)

현재 지점이 통합 인 경우 :

# Checkout a new temporary branch at the current location
git checkout -b tmp

# Move the integration branch to the head of the new patchset
git branch -f integration last_SHA-1_of_working_branch_range

# Rebase the patchset onto tmp, the old location of integration
git rebase --onto tmp first_SHA-1_of_working_branch_range~1 integration

그것은 다음 사이의 모든 것을 재생합니다 :

  • 의 부모 뒤에 first_SHA-1_of_working_branch_range(따라서 ~1) : 재생하려는 첫 번째 커밋
  • 최대 " integration"( working분기 에서 재생하려는 마지막 커밋을 가리킴 )

" tmp"( integration이전에 가리키는 위치 를 가리킴)

해당 커밋 중 하나가 재생 될 때 충돌이있는 경우 :

  • 해결하고 " git rebase --continue"를 실행하십시오 .
  • 또는이 패치를 건너 뛰고 대신 " git rebase --skip" 를 실행하십시오 .
  • 또는 " git rebase --abort"로 모든 것을 취소하십시오 (그리고 integration지점에 지점을 다시 넣으십시오 tmp)

그 후 rebase --onto, integration다시 마지막에 될 것입니다 ( "입니다 통합 브랜치의 커밋 tmp"지점 + 모든 재생 커밋)

체리 따기 또는를 사용하면 여기에 설명 된대로rebase --onto 후속 병합에 영향을 미칩니다 .


순수한 " cherry-pick"솔루션은 여기 에서 논의 되며 다음과 같은 내용이 포함됩니다.

패치 방식을 사용하려면 "git format-patch | git am"및 "git cherry"을 선택할 수 있습니다.
현재 git cherry-pick커밋 단 하나의 수락,하지만 당신은 범위를 선택하려면 B를 통해 D그 것이다 B^..D자식 용어에서, 그래서

git rev-list --reverse --topo-order B^..D | while read rev 
do 
  git cherry-pick $rev || break 
done 

어쨌든, 커밋 범위를 "재생"해야 할 때 "replay"라는 단어 rebase는 Git 의 " "기능 을 사용하도록 강요해야합니다 .


1
-m옵션 이 필요한 부모가있는 커밋이있는 경우 해당 커밋을 어떻게 처리합니까? 또는 이러한 커밋을 필터링하는 방법이 있습니까?
aug

@aug -m-m이 체리 피킹을 위해 선택한 매개 변수가 참조하는 메인 라인을 선택하여 처리합니다 .
VonC

문제는 커밋 범위를 선택하는 체리라면 부모 커밋을 올바르게 선택하지만 정상적인 커밋에 도달하면 실패하고 커밋은 병합이 아니라는 것입니다. -m체리 피킹 범위의 커밋 범위에서 부모 커밋에 도달했을 때만 옵션을 전달하는 방법이 내 질문에 더 잘 표현되어 있다고 생각 합니까? 지금 내가 전달하는 경우 -m처럼 git cherry-pick a87afaaf..asfa789 -m 1그것은 범위 내의 모든 커밋에 적용됩니다.
aug

@ aug Strange, 나는 문제를 재현하지 못했습니다. git 버전은 무엇이며 whayt는 정확한 오류 메시지입니까?
VonC

아 git 버전 2.6.4 (Apple Git-63)를 실행 중입니다. 내가 본 오류는 error: Commit 8fcaf3b61823c14674c841ea88c6067dfda3af48 is a merge but no -m option was given.실제로 당신이 그냥 할 수 있다는 것을 깨달았을 git cherry-pick --continue것입니다.하지만 괜찮을 것입니다 (그러나 부모 커밋은 포함되지 않을 것입니다)
aug

136

git v1.7.2부터 cherry pick은 다양한 커밋을 허용 할 수 있습니다.

git cherry-pick커밋 범위를 선택하는 법을 배웠습니다 (예 : cherry-pick A..Bcherry-pick --stdin) git revert. 그러나 이것들은 더 나은 시퀀싱 컨트롤 rebase [-i]이 지원하지 않습니다 .


103
참고 cherry-pick A..B커밋되지 않습니다 (당신이 필요 A~1..B그것을 위해), 자동 REBASE처럼 계속되지 않습니다 자식 충돌이있는 경우 수행 (1.7.3.1의 같은 이상)
게이브 Moothart

3
또한 git cherry-pick A..B C순진하게 예상대로 작동하지 않는다는 점에 유의하는 것이 좋습니다 . 범위 내의 모든 것을 고르지 않고 A..B커밋 하지 않습니다 C! 이렇게하려면 먼저, 두 라인으로 분할해야 git cherry-pick A..B하고 git cherry-pick C. 따라서 범위가있을 때마다 별도로 실행해야합니다.
MicroVirus 2016 년

42

2 개의 지점이 있다고 가정 해 봅시다.

"branchA": 복사하려는 커밋을 포함합니다 ( "commitA"에서 "commitB"로)

"branchB": 커밋을 "branchA"에서 전송할 지점

1)

 git checkout <branchA>

2) "commitA"및 "commitB"의 ID를 얻습니다.

삼)

git checkout <branchB>

4)

git cherry-pick <commitA>^..<commitB>

5) 충돌이있는 경우 해결하고 입력하십시오.

git cherry-pick --continue

체리 픽 프로세스를 계속합니다.


매력처럼 일했다! 고마워요!
snukone

28

실제로 분기를 병합하고 싶지 않습니까? 작업 브랜치에 원하지 않는 최근 커밋이있는 경우 원하는 시점에 HEAD를 사용하여 새 브랜치를 만들 수 있습니다.

이제 어떤 이유로 든 커밋 범위를 선택하고 싶다면 우아한 방법은 패치 세트를 가져 와서 새로운 통합 브랜치에 적용하는 것입니다.

git format-patch A..B
git checkout integration
git am *.patch

이것은 기본적으로 git-rebase 가하는 일이지만 게임을 할 필요가 없습니다. 병합해야 할 경우 추가 --3way할 수 있습니다 git-am. 지침을 그대로 따르는 경우,이 작업을 수행하는 디렉토리에 다른 * .patch 파일이 없는지 확인하십시오.


1
다른 리비전 범위와 마찬가지로 A^포함 해야합니다 A.
지노 Mempin

9

VonC의 코드 를 간단한 bash 스크립트로 감싸서 git-multi-cherry-pick쉽게 실행했습니다.

#!/bin/bash

if [ -z $1 ]; then
    echo "Equivalent to running git-cherry-pick on each of the commits in the range specified.";
    echo "";
    echo "Usage:  $0 start^..end";
    echo "";
    exit 1;
fi

git rev-list --reverse --topo-order $1 | while read rev 
do 
  git cherry-pick $rev || break 
done 

현재 타사 코드와 사용자 정의가 모두 동일한 svn 트렁크에 혼합 된 프로젝트 기록을 재구성 할 때 이것을 사용하고 있습니다. 이제 핵심 사용자 지정 코드, 타사 모듈 및 사용자 지정 내용을 자체 자식 분기로 분할하여 향후 사용자 지정 내용을 더 잘 이해하고 있습니다. git-cherry-pick같은 저장소에 두 개의 나무가 있지만 공유 조상이 없기 때문에이 상황에서 도움이됩니다.


3

위의 모든 옵션은 병합 충돌을 해결하라는 메시지를 표시합니다. 팀에 적용한 변경 사항을 병합하는 경우 개발자의 병합 충돌을 해결하고 진행하기가 어렵습니다. 그러나 "git merge"는 한 번에 병합을 수행하지만 다양한 수정 버전을 인수로 전달할 수 없습니다. "git diff"및 "git apply"명령을 사용하여 rev의 병합 범위를 수행해야합니다. 패치 파일에 파일이 너무 많으면 "git apply"가 실패한다는 것을 알았습니다. 따라서 파일 당 패치를 만든 다음 적용해야합니다. 스크립트는 소스 분기에서 삭제 된 파일을 삭제할 수 없습니다. 드문 경우이지만 대상 브랜치에서 해당 파일을 수동으로 삭제할 수 있습니다. 패치를 적용 할 수없는 경우 "git apply"의 종료 상태는 0이 아닙니다.

아래는 스크립트입니다.

enter code here



  #!/bin/bash

    # This script will merge the diff between two git revisions to checked out branch
    # Make sure to cd to git source area and checkout the target branch
    # Make sure that checked out branch is clean run "git reset --hard HEAD"


    START=$1
    END=$2

    echo Start version: $START
    echo End version: $END

    mkdir -p ~/temp
    echo > /tmp/status
    #get files
    git --no-pager  diff  --name-only ${START}..${END} > ~/temp/files
    echo > ~/temp/error.log
    # merge every file
    for file in `cat  ~/temp/files`
    do
      git --no-pager diff --binary ${START}..${END} $file > ~/temp/git-diff
      if [ $? -ne 0 ]
      then
#      Diff usually fail if the file got deleted 
        echo Skipping the merge: git diff command failed for $file >> ~/temp/error.log
        echo Skipping the merge: git diff command failed for $file
        echo "STATUS: FAILED $file" >>  /tmp/status
        echo "STATUS: FAILED $file"
    # skip the merge for this file and continue the merge for others
        rm -f ~/temp/git-diff
        continue
      fi

      git apply  --ignore-space-change --ignore-whitespace  --3way --allow-binary-replacement ~/temp/git-diff

      if [ $? -ne 0 ]
       then
#  apply failed, but it will fall back to 3-way merge, you can ignore this failure
         echo "git apply command filed for $file"
       fi
       echo
       STATUS=`git status -s $file`


       if [ ! "$STATUS" ]
       then
#   status is null if the merged diffs are already present in the target file
         echo "STATUS:NOT_MERGED $file"
         echo "STATUS: NOT_MERGED $file$"  >>  /tmp/status
       else
#     3 way merge is successful
         echo STATUS: $STATUS
         echo "STATUS: $STATUS"  >>  /tmp/status
       fi
    done

    echo GIT merge failed for below listed files

    cat ~/temp/error.log

    echo "Git merge status per file is available in /tmp/status"

2

Vonc에 대한 명확한 설명을 읽은 후 며칠 전에 테스트했습니다.

내 단계

스타트

  • 지점 dev: ABCDEFGHIJ
  • 지점 target: ABCD
  • 나는 원하지 E도 않고H

분기에서 단계 E 및 H가없는 형상을 복사하는 단계 dev_feature_wo_E_H

  • git checkout dev
  • git checkout -b dev_feature_wo_E_H
  • git rebase --interactive --rebase-merges --no-ff D내가 rebase 편집기의 dropEH뒤에 놓은 곳
  • 갈등을 해결하고 계속하고 commit

dev_feature_wo_E_H대상에 분기를 복사하는 단계 입니다.

  • git checkout target
  • git merge --no-ff --no-commit dev_feature_wo_E_H
  • 갈등을 해결하고 계속하고 commit

비고

  • cherry-pick전날 에 너무 많이했기 때문에
  • git cherry-pick 강력하고 단순하지만

    • 중복 커밋을 만듭니다.
    • merge초기 커밋과 중복 커밋의 충돌을 해결해야 할 때 하나 또는 두 개의 cherry-pick경우 "체리 피킹"해도 괜찮지 만 더 자세한 것은 너무 복잡하고 지점이 너무 복잡해집니다.
  • 내 마음 속에 내가 한 걸음이 더 분명해 git rebase --onto

1
좋은 소식입니다. 공감. stackoverflow.com/a/38418941/6309을 상기시킵니다 . 2012 년에 체리 따기의 단점을 정리했습니다 : stackoverflow.com/a/13524494/6309 .
VonC

1

또 다른 옵션은 범위 전에 커밋에 대한 전략과 병합 한 다음 해당 범위의 마지막 커밋 (또는 마지막 커밋 인 지점)과 '정상'병합입니다. 따라서 마스터의 2345 및 3456 커밋 만 기능 분기에 병합한다고 가정하십시오.

석사:
1234
2345
3456
4567

기능 분기에서 :

git merge -s 우리 4567
자식 병합 2345
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.