git rebase --onto의 동작을 이해할 수 없습니다


169

나는 git 명령을 따르는 두 블록이 다른 동작을 가지고 있으며 그 이유를 이해하지 못합니다.

나는 하나와 분기 A하는 B지점과commit

---COMMIT--- (A)
\
 --- (B)

나는 리베이스하려는 B에게 최신에 지점을 A(그리고는 커밋이 B지점)

---COMMIT--- (A)
         \
          --- (B)

내가해도 문제가 없습니다.

checkout B
rebase A

그러나 내가하면 :

checkout B
rebase --onto B A

전혀 작동하지 않으며 아무 일도 일어나지 않습니다. 두 동작이 다른 이유를 이해하지 못합니다.

Phpstorm git 클라이언트는 두 번째 구문을 사용하므로 완전히 깨진 것처럼 보이 므로이 구문 문제를 묻습니다.


답변:


412

tl; dr

귀하의 경우 BA사용 하는 것 외에 rebase하는 올바른 구문 git rebase --onto은 다음과 같습니다.

git checkout B
git rebase --onto A B^

또는 REBASE B의 상단 A으로부터 시작하는 그의 부모 커밋B 로 참조 B^하거나 B~1.

당신 git rebase <branch>과 의 차이점에 관심이 있다면 git rebase --onto <branch>.

빠른 : 자식 리베이스

git rebase <branch>에 의해 참조 현재 체크 아웃 한 분기를, 리베이스 것입니다 HEAD의 상단에, 최신가에 도달 할 수 커밋 에서 <branch>하지만, 하지 에서 HEAD.
이것은 가장 일반적인 rebasing의 경우이며 아마도 계획을 덜 필요로하는 경우입니다.

          Before                           After
    A---B---C---F---G (branch)        A---B---C---F---G (branch)
             \                                         \
              D---E (HEAD)                              D---E (HEAD)

이 예에서 FG에서 도달 할 수 branch있지만에서 도달 할 수 없는 커밋입니다 HEAD. 말하는 git rebase branch소요됩니다 D(가) 먼저 분기 시점 이후 커밋입니다, 그리고 그것을 리베이스 (즉, 부모를 변경 에서 연결할 최신의 상단에) 커밋 branch아니라에서 HEAD입니다, G.

정확한 : git rebase --onto 2 arguments

git rebase --onto특정 커밋에서 시작 하여 리베이스 할 수 있습니다 . 리베이트 대상과 위치를 정확하게 제어 할 수 있습니다. 이것은 정확해야하는 시나리오를위한 것입니다.

예를 들어에서 시작하여 HEAD정확하게 리베이스해야한다고 가정 해 봅시다 . 우리는 작업 지점으로 가져 오는 데 관심이 있지만 동시에 호환되지 않는 변경 사항이 포함되어 있기 때문에 유지하고 싶지 않습니다.FEFD

          Before                           After
    A---B---C---F---G (branch)        A---B---C---F---G (branch)
             \                                     \
              D---E---H---I (HEAD)                  E---H---I (HEAD)

이 경우에는이라고 말할 것 git rebase --onto F D입니다. 이것은 다음을 의미합니다.

HEAD부모가 D에 도달 할 수있는 커밋을 리베이스합니다 F.

즉, 의 부모E에서 D변경하십시오F . 의 구문은 git rebase --onto입니다 git rebase --onto <newparent> <oldparent>.

이것이 유용한 또 다른 시나리오는 대화식 리베이스 를 수행하지 않고 현재 분기에서 일부 커밋을 빠르게 제거하려는 경우입니다 .

          Before                       After
    A---B---C---E---F (HEAD)        A---B---F (HEAD)

이 예에서 순서대로 제거하기 CE순서에서 당신은 말할 것입니다 git rebase --onto B E, 또는 리베이스 HEAD위에 B오래된 부모가 어디 E.

외과 의사 : git rebase --onto 3 arguments

git rebase --onto정밀도 측면에서 한 단계 더 나아갈 수 있습니다. 실제로, 임의의 커밋 범위 를 다른 커밋 위에 리베이스 할 수 있습니다 .

예를 들면 다음과 같습니다.

          Before                                     After
    A---B---C---F---G (branch)                A---B---C---F---G (branch)
             \                                             \
              D---E---H---I (HEAD)                          E---H (HEAD)

이 경우, 우리는 정확한 범위 리베이스 할 E---H위에을 F위치를 무시하고, HEAD현재 가리키고 있습니다. 우리는 다음과 같이 말함으로써 그렇게 할 수 있습니다 git rebase --onto F D H.

부모 커밋의 범위 리베이스 D까지 H의 위에을 F.

그러면 커밋 범위git rebase --onto 가있는 구문 이됩니다 . 트릭은 여기에 의해 참조 커밋 것을 기억한다 됩니다 포함 범위와 새로운 될 것 REBASE가 완료된 후.git rebase --onto <newparent> <oldparent> <until><until>HEAD


1
좋은 대답입니다. 일반적인 경우에 대한 작은 추가 사항 : <oldparent>범위의 두 부분이 다른 가지에 있으면 이름이 분해됩니다. 일반적으로 : "연결 가능한 모든 커밋을 포함 <until>하지만 에서 도달 가능한 모든 커밋은 제외합니다 <oldparent>."
musiKk

50
git rebase --onto <newparent> <oldparent>내가 본 행동에 대한 최고의 설명입니다!
ronkot

4
감사! 나는 --onto옵션 에 다소 어려움을 겪고 있었지만 이것은 분명히 수정되었습니다! :-) D 우수 "튜토리얼"에 대한 감사 : 나는 심지어 내가 전에 그것을 이해하지 수 있는지 이해가 안가
grongor

3
이 답변은 훌륭하지만 가능한 모든 경우를 다루지는 않습니다. 마지막 구문 형식을 사용하여보다 미묘한 유형의 리베이스를 표현할 수도 있습니다. Pro Git (2nd Ed.)에서 예제를 벗어나면 D는 반드시 H의 조상일 필요는 없습니다. 대신 D와 H도 공통 조상과 커밋 할 수 있습니다.이 경우 Git은 공통 조상을 알아냅니다. 그리고 그 조상에서 H로 F로 다시 재생합니다.
Pastafarian

1
도움이되었습니다. 매뉴얼 페이지에는 인수에 대한 설명이 전혀 없습니다.
a544jh

61

이것은 당신이 알아야 할 모든 것입니다 --onto:

git rebase --onto <newparent> <oldparent>

커밋에서 부모를 전환하고 있지만 커밋의 샤를 제공하지 않고 현재 (오래된) 부모의 샤만 제공합니다.


4
짧고 쉽습니다. 실제로, 그 커밋 대신 내가 rebase하고 싶은 부모 커밋 을 제공해야한다는 것을 깨닫는 데 가장 오랜 시간이 걸렸습니다.
Antoniossss

1
중요한 세부 사항은 하나의 커밋이 많은 커밋의 부모가 될 수 있지만 현재 브랜치로 자신을 제한하면 커밋이 하나의 커밋에만 부모가 될 수 있기 때문에 현재 브랜치에서 oldparent의 자식을 선택한다는 것입니다. 즉, 상위 관계 선박은 지점에서 고유하지만 지점을 지정하지 않은 경우 필요하지 않습니다.
Trismegistos

2
참고 : 브랜치에 있거나 브랜치 이름을 3 번째 파라미터 git rebase --onto <newparent> <oldparent> <feature-branch>로 추가해야합니다.
Jason Portnoy

1
이 답변은이 글에서 필요로하는 시점까지 훌륭합니다
John Culviner

13

다음과 같이 주어진다 :

      Before rebase                             After rebase
A---B---C---F---G (branch)                A---B---C---F---G (branch)
         \                                         \   \
          D---E---H---I (HEAD)                      \   E'---H' (HEAD)
                                                     \
                                                      D---E---H---I

git rebase --onto F D H

--onto하나의 인수를 취하기 때문에 다음과 같습니다.

git rebase D H --onto F

수단 범위에 커밋을 리베이스 (D, H] F. 공지 사항 상단의 범위 왼손 전용. 이 1 일 입력 예에 의해 저지 지정 쉽게 때문에 독점의 branch수 있도록 git에서 커밋 분기 1을 찾을 branch즉, D어떤 리드 H.

OP 사례

    o---o (A)
     \
      o (B)(HEAD)

git checkout B
git rebase --onto B A

단일 명령으로 변경 가능

git rebase --onto B A B

여기에 오류가있는 것의 배치는 B"일부 커밋을 이동 B시켜 B" 위로 분기 "하는 것을 의미 합니다. 문제는 "일부 커밋"입니다. -i플래그 를 추가 하면로 표시된 단일 커밋임을 알 수 HEAD있습니다. 커밋은 이미 --onto대상에 적용 B되어 아무 일도 일어나지 않아서 건너 뜁니다 .

분기 이름이 이와 같이 반복되는 경우 명령은 의미가 없습니다. 이는 커밋 범위가 이미 해당 브랜치에있는 일부 커밋이므로 리베이스하는 동안 모든 커밋이 생략되기 때문입니다.

에 대한 추가 설명 및 해당 사용법 git rebase <upstream> <branch> --onto <newbase>.

git rebase 기본값.

git rebase master

다음 중 하나로 확장됩니다.

git rebase --onto master master HEAD
git rebase --onto master master current_branch

리베이스 후 자동 결제.

다음과 같이 표준 방식으로 사용하는 경우 :

git checkout branch
git rebase master

리베이스가 가장 최근의 리베이스 된 커밋으로 git이동 한 후에 branch는 히스토리를 git checkout branch볼 수 git reflog있습니다. 두 번째 인수가 커밋 해시 인 경우 흥미로운 점은 분기 이름 rebase가 여전히 작동하지만 이동할 분기가 없으므로 이동 된 분기로 체크 아웃되는 대신 "분리 된 HEAD"로 끝납니다.

1 차 분기 커밋을 생략하십시오.

master에서는 --onto1에서 가져 git rebase인수입니다.

                   git rebase master
                              /    \
         git rebase --onto master master

따라서 실제로 다른 커밋 또는 브랜치가 될 수 있습니다. 이 방법으로 최신 커밋을 수행하고 기본 분기 커밋을 남겨 두어 리베이스 커밋 수를 제한 할 수 있습니다.

git rebase --onto master HEAD~
git rebase --onto master HEAD~ HEAD  # Expanded.

하나에 의해 지적 커밋 리베이스 것인가 HEADmaster와 "분리 된 HEAD"에서 끝난다.

명시적인 체크 아웃을 피하십시오.

기본 HEAD또는 current_branch인수는 상황에 따라 상황에 따라 결정됩니다. 이것이 대부분의 사람들이 리베이스하려는 지점으로 체크 아웃하는 이유입니다. 그러나 두 번째 rebase 인수가 명시 적으로 주어지면 암시적인 방식으로 전달하기 위해 rebase 전에 체크 아웃 할 필요가 없습니다.

(branch) $ git rebase master
(branch) $ git rebase master branch  # Expanded.
(branch) $ git rebase master $(git rev-parse --abbrev-ref HEAD)  # Kind of what git does.

이것은 어느에서나 커밋과 브랜치를 리베이스 할 수 있음을 의미합니다 . 리베이스 후 자동 결제 와 함께 . 리베이스 전후에 리베이스 브랜치를 별도로 체크 아웃 할 필요는 없습니다.

(master) $ git rebase master branch
(branch) $ # Rebased. Notice checkout.

8

간단히, git rebase --onto커밋 범위를 선택하고 커밋을 매개 변수로 지정하여 리베이스합니다.

의 매뉴얼 페이지를 읽고 git rebase"onto"를 검색하십시오. 예제는 매우 좋습니다 :

example of --onto option is to rebase part of a branch. If we have the following situation:

                                   H---I---J topicB
                                  /
                         E---F---G  topicA
                        /
           A---B---C---D  master

   then the command

       git rebase --onto master topicA topicB

   would result in:

                        H'--I'--J'  topicB
                       /
                       | E---F---G  topicA
                       |/
           A---B---C---D  master

이 경우 git에게 커밋을에서 rebase로 리베이스하도록 topicA지시 topicB합니다 master.


8

더 나은에 차이를 이해 git rebase하고 git rebase --onto이 두 명령에 사용할 수있는 행동이 무엇인지 알고하는 것이 좋다. git rebase커밋을 선택된 브랜치 위로 옮길 수 있습니다. 여기처럼 :

git rebase master

결과는 다음과 같습니다.

Before                              After
A---B---C---F---G (master)          A---B---C---F---G (master)
         \                                           \
          D---E (HEAD next-feature)                   D'---E' (HEAD next-feature)

git rebase --onto더 정확합니다. 시작하고 싶은 곳과 끝낼 곳을 지정할 수 있습니다. 여기처럼 :

git rebase --onto F D

결과는 다음과 같습니다.

Before                                    After
A---B---C---F---G (branch)                A---B---C---F---G (branch)
         \                                             \
          D---E---H---I (HEAD my-branch)                E'---H'---I' (HEAD my-branch)

자세한 내용을 보려면 git rebase --onto overview에 대한 내 기사를 확인하는 것이 좋습니다.


@Makyen 물론, 나는 미래에 그것을 명심할 것이다 :)
womanonrails

그래서 우리는 읽을 수 git rebase --onto F DF로 D' 부모의 세트 아이 우리는 할 수 없습니다?
Prihex

2

를 들어 onto두 개의 추가 가지가 필요합니다. 이 명령을 사용하면 다른 분기를 branchB기반으로하는 커밋을 적용 할 수 있습니다 ( branchA예 :) master. 아래의 샘플에서는 branchB기반으로 branchA당신은의 변경 적용 할 branchB에을 master의 변경 사항을 적용하지 않고 branchA.

o---o (master)
     \
      o---o---o---o (branchA)
                   \
                    o---o (branchB)

명령을 사용하여 :

checkout branchB
rebase --onto master branchA 

커밋 계층 구조를 따릅니다.

      o'---o' (branchB)
     /
o---o (master)
     \
      o---o---o---o (branchA)

1
우리가 마스터에게 리베이스하기를 원한다면 어떻게 그것이 현재의 지사가되는지 좀 더 설명해 주시겠습니까? 그렇다면 rebase --onto branchA branchB전체 마스터 브랜치를 branchA의 헤드에 두십시오.
Polymerase

8
이러면 안되나요 checkout branchB: rebase --onto master branchA?
goldenratio

4
이것이 왜 상향 조정 되었습니까? 이것은 그것이 말하는 것을하지 않습니다.
De Novo

나는 답을 편집하고 고쳤다. 그래서 사람들은 먼저 그들의 repo 브랜치를 깰 필요가없고 그 직후 에 코멘트를 읽을 필요가 없다 ... 🙄
Kamafeather

0

git rebase --onto파악하기 어려운 또 다른 경우가 있습니다 . 대칭 차이 선택기 (세 개의 점 ' ...')로 인해 커밋을 수행 할 때

Git 2.24 (2019 년 4 분기)는 해당 사례를보다 잘 관리합니다.

참조 414d924 커밋 , 4effc5b 커밋 , c0efb4c 커밋 , 2b318aa 커밋 (2019년 8월 27일을), 및 793ac7e 커밋 , 359eceb을 투입 하여 (2019년 8월 25일) 덴튼 리우 ( Denton-L) .
도움 : Eric Sunshine ( sunshineco) , Junio ​​C gitsterHamano avar( ) , Ævar Arnfjörð Bjarmason ( )Johannes Schindelin ( dscho) .
참조 6,330,209 커밋 , c9efc21 커밋 (2019년 8월 27일)를, 그리고 4336d36 커밋 에 의해 (2019 8월 25일)을 (라고 Ævar Arnfjörð Bjarmason avar).
도움 : Eric Sunshine ( sunshineco) , Junio ​​C gitsterHamano avar( ) , Ævar Arnfjörð Bjarmason ( )Johannes Schindelin ( dscho) .
( Junio ​​C gitsterHamano 에 의해 병합 -- 커밋 640f9cd , 2019 년 9 월 30 일)

rebase: --onto더 많은 경우에 빨리 감기

이전에 다음과 같은 그래프를 볼 때

A---B---C (master)
     \
      D (side)

' git rebase --onto master... master side'을 (를) 실행 하면 D무엇이든 항상 리베이스됩니다.

이 시점 에서 Git diff commit 범위에서 " double dot ' ..'과 triple dot" ..."의 차이점은 무엇입니까? "

https://sphinx.mythic-beasts.com/~mark/git-diff-help.png

여기에 " master..."를 의미 master...HEADB: HEAD 측면 HEAD (현재 체크 아웃)입니다 : 당신이에 리베이스됩니다 B.
당신은 무엇을 rebasing입니까? 모든 커밋 되지 마스터에, 그리고에서 연결할 side지점 : 하나는 그 설명 피팅 커밋이 D... 위에 이미 B!

또, 힘내 2.24 전에 같은이 rebase --onto초래 D항상 무슨 일이 있어도으로 업데이트되고 있지.

그러나 원하는 동작은 rebase가 이것이 빨리 전달 될 수 있음을 인식하고 대신 수행하는 것입니다.

그것은 rebase --onto B A아무것도하지 않은 OP의 것과 유사합니다 .

can_fast_forward이 경우를 감지하고 빨리 감기를 수행 할 수 있도록 감지를 추가하십시오 .
우선, 논리를 단순화하는 gotos를 사용하도록 함수를 다시 작성하십시오.
다음으로

options.upstream &&
!oidcmp(&options.upstream->object.oid, &options.onto->object.oid)

에서 조건을 제거하고에서 cmd_rebase대체를 다시 도입했습니다 can_fast_forward.
특히, 병합 기지의 검사 upstreamhead수정에 실패한 케이스 t3416.

t3416의 약어 그래프는 다음과 같습니다.

        F---G topic
       /
  A---B---C---D---E master

실패한 명령은

git rebase --onto master...topic F topic

이전에 Git은 하나의 병합 기반 ( C, 결과 master...topic)이 있고 병합과 동일이 같으므로 1을 잘못 반환하여 빨리 감을 수 있음을 나타냅니다. 이로 인해 ' ABCFG'을 (를) 기대할 때 리베이스 된 그래프가 ' '가됩니다 ABCG.

rebase --onto C F topic수단은 커밋 이후 F 로 연결할, topic수 있습니다 : HEAD G만하지 F그 자체.
이 경우 빨리 감기 F는 리베이스 브랜치에 포함 되며 이는 잘못된 것입니다.

추가 논리를 통해 업스트림 및 헤드의 병합 기반이 F입니다. 에가 아니기 때문에 F, 우리가에서 커밋의 전체 세트를 리베이스하지 않는 의미합니다 master..topic.
커밋을 제외하기 때문에 빨리 감기 수행 할 수 없으므로 0을 올바르게 반환합니다.

-f이 변경의 결과로 실패한 사례를 테스트하려면 ' '를 추가 하여 빨리 감기를 기대하지 않아 리베이스가 강제 실행되도록합니다.

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