누군가가 게시 된 브랜치에 리베이스 또는 재설정을 푸시 한 후 복구 / 재 동기화하려면 어떻게해야합니까?


88

우리는 게시 된 작업을 절대로 리베이스해서는 안된다고, 위험하다는 등의 말을 들었습니다. 그러나 나는 리베이스 게시 된 경우 상황을 처리하는 방법에 대한 레시피가 게시 된 것을 보지 못했습니다 .

이제 저장소가 알려진 (가급적 작은) 그룹에 의해서만 복제되는 경우에만 실제로 가능하다는 점에 유의하십시오. 따라서 리베이스 또는 재설정을 푸시하는 사람은 다른 모든 사람에게 다음에주의를 기울여야한다고 알릴 수 있습니다. 술책(!).

내가 본 한 가지 분명한 솔루션은 로컬 커밋이없고 foo리베이스 되는 경우 작동합니다 .

git fetch
git checkout foo
git reset --hard origin/foo

이것은 단순히 foo원격 저장소에 따라 히스토리에 찬성하여의 로컬 상태를 버립니다 .

하지만 그 지점에서 상당한 지역적 변화를 일으킨다면 상황을 어떻게 다룰까요?


간단한 케이스 레시피의 경우 +1. 특히 OS가 다른 경우 컴퓨터 간의 개인 동기화에 이상적입니다. 매뉴얼에서 언급되어야 할 것입니다.
Philip Oakley

개인 동기화를위한 이상적인 방법은 git pull --rebase && git push. 만약 당신이 작업 master만한다면, 당신이 리베이스를하고 다른 쪽 끝을 밀 었다고하더라도 이것은 당신에게 거의 틀림없이 옳은 일을 할 것입니다.
Aristotle Pagaltzis 2013

PC와 Linux 시스템간에 동기화 및 개발 중이기 때문에 모든 리베이스 / 업데이트에 대해 새 브랜치를 사용하는 것이 잘 작동한다는 것을 알았습니다. 나는 또한 git reset --hard @{upstream}"내가 가지고있는 것을 잊어 버리고, 원격에서 가져온 것을 사용한다"에 대한 마법의 refspec 주문을 알고 있는 변종을 사용합니다. stackoverflow.com/a/15284176/717355
Philip Oakley

Git2.0을 사용하면 브랜치의 이전 출처를 찾을 수 있습니다 (업스트림 브랜치가 a로 다시 작성되기 전 push -f). 아래 내 답변 참조
VonC

답변:


75

푸시 된 리베이스 후에 다시 동기화하는 것은 대부분의 경우 그렇게 복잡하지 않습니다.

git checkout foo
git branch old-foo origin/foo # BEFORE fetching!!
git fetch
git rebase --onto origin/foo old-foo foo
git branch -D old-foo

즉. 먼저 원격 브랜치가 원래 있던 위치에 대한 북마크를 설정 한 다음이를 사용하여 해당 지점에서 리 기반 원격 브랜치로 로컬 커밋을 재생합니다.

Rebasing은 폭력과 같습니다. 문제가 해결되지 않으면 더 많이 필요합니다. ☺

물론 pre-rebase origin/foo커밋 ID 를 찾아서 사용한다면 북마크 없이도 가능합니다 .

또한 가져 오기 전에 북마크를 만드는 것을 잊은 상황을 처리하는 방법이기도합니다 . 손실되는 것은 없습니다. 원격 분기에 대한 리플 로그를 확인하기 만하면됩니다.

git reflog show origin/foo | awk '
    PRINT_NEXT==1 { print $1; exit }
    /fetch: forced-update/ { PRINT_NEXT=1 }'

이것은 origin/foo기록을 변경 한 가장 최근의 가져 오기 전에 가리키는 커밋 ID를 인쇄합니다 .

그런 다음 간단히

git rebase --onto origin/foo $commit foo

11
빠른 메모 : 꽤 직관적이라고 생각하지만, 만약 당신이 awk를 잘 모른다면 ... 그 한 줄은 단지 git reflog show origin/foo"fetch : forced-update"라고 말하는 첫 줄 의 출력을 살펴보고 있습니다 . 그것이 가져 오기로 인해 원격 분기가 빨리 감기가 아닌 다른 작업을 수행 할 때 git이 기록하는 내용입니다. (당신은 너무 손으로 그것을 할 수 - 강제 업데이트가 아마도 가장 최근의 일이다.)
Cascabel

2
폭력과는 다릅니다. 폭력은 때때로 재미 있습니다
Iolo 2013-02-05

5
@iolo 사실, 리베이스는 항상 재미 있습니다.
Dan Bechard 2013 년

1
폭력과 마찬가지로 거의 항상 리베이스를 피하십시오. 그러나 방법을 알 수 있습니다.
Bob Stein

2
글쎄, 다른 사람들이 영향을받을 수있는 리베이스를 추진하지 마십시오.
Aristotle Pagaltzis

11

내가 말하고 싶지만 상류 REBASE의 복구 힘내 - REBASE 매뉴얼 페이지 커버 섹션 거의 모든의를.

자신의 리베이스에서 복구하는 것과 실제로 다르지 않습니다. 하나의 브랜치를 이동하고 기록에있는 모든 브랜치를 새로운 위치로 리베이스합니다.


4
아, 그렇습니다. 그러나 나는 지금 그것이 말하는 것을 이해하지만, 이것을 스스로 알아 내기 전에는 전에는 없었을 것입니다. 그리고 요리 책 레시피가 없습니다 (아마도 그러한 문서에서 그렇습니다). 나는 또한“하드 케이스”라고 부르는 것이 FUD라고 말할 것이다. 나는 재 작성된 역사가 대부분의 사내 개발 규모에서 사소하게 관리 될 수 있다고 제출한다. 이 주제가 항상 다루어지는 미신적 인 방식은 나를 괴롭 힙니다.
Aristotle Pagaltzis 2010

4
@Aristotle : 모든 개발자가 git을 사용하는 방법을 알고 있고 모든 개발자와 효과적으로 의사 소통 할 수 있다는 점을 감안할 때 매우 관리하기 쉽다는 것이 맞습니다. 완벽한 세상에서 그것은 이야기의 끝이 될 것입니다. 그러나 많은 프로젝트는 업스트림 리베이스가 정말 무서운 일이 될만큼 충분히 크다. (그리고 대부분의 개발자가 리베이스에 대해 들어 본 적이없는 직장 같은 곳이 있습니다 .) 저는 "미신"이 가능한 가장 안전하고 일반적인 조언을 제공하는 방법이라고 생각합니다. 누구도 다른 사람의 저장소에서 재난을 일으키는 사람이되고 싶지 않습니다.
Cascabel

2
네, 동기를 이해합니다. 그리고 나는 그것에 완전히 동의합니다. 그러나“결과를 이해하지 못하면 시도하지 마십시오”와“악하기 때문에 절대로하지 말 것”사이에는 차이가 있습니다. 그리고 이것만으로도 문제가됩니다. 두려움을 불러 일으키는 것보다 가르치는 것이 항상 낫습니다.
Aristotle Pagaltzis 2010

@Aristotle : 동의합니다. 나는 "당신이 무엇을하고 있는지 확인하라"는 쪽을 향하도록 노력하지만, 특히 온라인에서는 구글의 평범한 방문자가 주목할 수 있도록 충분한 무게를 주려고 노력합니다. 당신 말이 맞아요. 아마 많은 부분을 줄여야합니다.
Cascabel

11

자식과 함께 시작하는 것은 2014 년 1 분기 1.9 / 2.0, 당신은에 설명 된대로 다시 상류 지점에 그것을 리베이스하기 전에 이전 분기 원을 표시 할 필요가 없습니다 아리스토텔레스 Pagaltzis대답 :
참조가 07d406b 커밋d96855f 커밋 :

topic만든 브랜치 에서 작업 한 후 git checkout -b topic origin/master원격 추적 브랜치의 기록을 origin/master되 감고 다시 빌드하여 다음과 같은 형태의 기록을 만들 수 있습니다.

                   o---B1
                  /
  ---o---o---B2--o---o---o---B (origin/master)
          \
           B3
            \
             Derived (topic)

여기서 origin/master커밋에 포인트로 사용 B3, B2, B1지금은에 점 B, 그리고 topic때 백업의 지점은 정상에 시작되었다 origin/master이었다 B3.

이 모드는의 reflog를 사용하여 분기점 origin/master으로 찾기 B3때문에 다음 topic을 통해 업데이트 된 항목origin/master기반으로 할 수 있습니다 .

$ fork_point=$(git merge-base --fork-point origin/master topic)
$ git rebase --onto origin/master $fork_point topic

이것이 git merge-base명령에 새로운 옵션이있는 이유입니다 .

--fork-point::

분기 (또는 이로 이어지는 모든 기록 <commit>)가 다른 분기 (또는 참조)에서 분기 된 지점을 찾습니다 <ref>.
이것은 두 커밋의 공통 조상을 찾을뿐만 아니라 분기의 이전 화신에서 분기 된 <ref>역사를 확인하기 위해 의 리플 로그를 고려합니다<commit><ref> .


" git pull --rebase"명령은 " base"가있는 경우에 대처하기 위해 분기 작업의 기반이 된 " "분기 (일반적으로 원격 추적 분기)의 reflog 항목을 사용하여 리베이스되는 분기의 분기점을 계산합니다. 가지가 되 감고 재건되었습니다.

예를 들어, 기록이 다음과 같은 경우 :

  • 현재의 끝은 " base"지점에 있습니다 B만, 이전의 팁 예전 관찰을 불러오는 B3다음과 B2다음 B1 , 커밋 현재에 도착하기 전에
  • 최신 "base"위에 리베이스되는 브랜치는 commit을 기반으로합니다 B3.

이 찾습니다 B3"의 출력을 통해 이동하여 git rev-list --reflog base"(즉 B, B1, B2, B3) 그것은 그 현재의 팁의 조상 커밋 찾을 때까지 " Derived (topic)".

내부적으로 get_merge_bases_many()는 일회성으로 이것을 계산할 수 있습니다. " " 의 모든 역사적 팁을 병합하여 결과가되는 가상의 병합 커밋
사이의 병합 기반을 원할 Derivedbase (origin/master)입니다.
그러한 커밋이 존재하면 " base" 의 reflog 항목 중 하나와 정확히 일치하는 단일 결과를 얻어야합니다 .


힘내 2.1 (2014 년 3 분기)이이 기능이 더 강력하게 추가 할 것입니다 : 참조 1e0dacd 커밋 에 의해 존 키핑 ( johnkeeping)

다음 토폴로지가있는 시나리오를 올바르게 처리하십시오.

    C --- D --- E  <- dev
   /
  B  <- master@{1}
 /
o --- B' --- C* --- D*  <- master

어디:

  • B'B패치와 동일하지 않은 수정 된 버전입니다 B.
  • C*D*패치와 동일한다 CD각각 잘못된 순서로 적용될 경우, 텍스트로 충돌;
  • E텍스트는 D.

정확한 결과 git rebase master devIS B의 포크 포인트로 식별 dev하고 master, 그래서 C, D, E필요에이 재생 될 것을 커밋이다 master; 하지만, CD패치와 동일하다 C*D*최종 결과가되도록, 따라서 감소 될 수있다 :

o --- B' --- C* --- D* --- E  <- dev

분기점이 식별되지 않은 경우 B포함 된 분기 를 선택 B'하면 충돌이 발생하고 패치와 동일한 커밋이 올바르게 식별되지 않은 경우 C포함 D(또는 동등하게 D*)을 포함하는 분기 를 선택 하면 충돌이 발생합니다.


" --fork-point"모드는 git rebaseGit 2.27 (2020 년 2 분기)에서 수정 된 2.20 시대에 C에서 명령을 다시 작성했을 때 퇴보했습니다.

참조는 f08132f 커밋 에 의해 (2019 12월 9일)를 Junio C 하마노 ( gitster)을 .
(Merged by Junio ​​C gitsterHamano -- in commit fb4175b , 27 Mar 2020)

rebase: --fork-point회귀 수정

서명자 : Alex Torok
[jc : 수정 사항 수정 및 Alex의 테스트 사용]
서명자 : Junio ​​C Hamano

" git rebase --fork-point master"은 (는) 잘 작동했습니다. 내부적으로 " git merge-base --fork-point" 라는 이름으로 짧은 참조 이름을 처리하는 방법을 알고 기본 get_fork_point()함수 를 호출하기 전에 전체 참조 이름으로 변경했습니다 .

명령이 C로 다시 작성된 후에는 더 이상 해당되지 않습니다. 직접 내부 호출이 get_fork_point()짧은 참조를 dwim하지 않기 때문입니다.

"git merge-base"에서 사용되는 "refname 인수를 전체 참조 이름으로 변경"논리를 기본 get_fork_point()함수로 이동하여 "git rebase"구현에서 함수의 다른 호출자가 동일한 방식으로 수정하도록합니다. 이 회귀.


1
이제 git push --force (git 1.8.5)를 더 신중하게 수행 할 수 있습니다. stackoverflow.com/a/18505634/6309
VonC
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.