한 분기를 다른 분기처럼 만드는 git 명령


81

변경 사항이있는 분기를 가져 와서 분기 된 업스트림과 동일하게 되돌리려 고합니다. 변경 사항은 로컬이며 github 로 푸시되었으므로 이미 푸시 된 브랜치에서는 나쁜 일인 기록을 변경 하기 때문에 둘 다 git reset또는 git rebase실제로 실행 가능 하지 않습니다 .

나는 또한 git merge다양한 전략을 시도했지만 그들 중 어느 것도 로컬 변경을 취소하지 않았습니다. 즉, 파일을 추가 한 경우 병합으로 다른 파일을 다시 가져올 수 있지만 업스트림이 수행하지 않는 파일은 계속 유지됩니다. 있다.

업스트림에서 새 분기를 만들 수는 있지만 수정 기록 측면에서 모든 변경 사항을 적용하여 분기를 가져와 다시 업스트림과 동일하게 만들어 해당 변경 사항을 안전하게 푸시 할 수 있도록 병합하고 싶습니다. 역사를 방해하지 않고. 그러한 명령이나 일련의 명령이 있습니까?


9
변경 사항을 보존하는 데 관심이 없다면 분기를 삭제하고 다시 생성하지 않는 이유는 무엇입니까? "프로젝트의 역사"는 신성 할 필요가 없습니다. Git은 개발자의 의사 소통을 돕는 도구입니다. 이러한 변경 사항이 도움이되지 않으면 버리십시오.
wnoise

+100 @wnoise-특히 변경 사항이 이미 병합 된 경우.
wuputah

7
공동 작업을 위해 출판 된 것이기 때문에 역사를 보존하는 데 관심이 있고 다시 돌아가고 싶을 수도 있습니다. 최신 버전 만 유지하는 경우 개정 제어를 사용하는 이유는 무엇입니까?
Arne Claassen

1
이것은 주관적인 주장이지만, VCS의 목적은 프로젝트 이력의 모든 사소한 부분을 기록하는 것이 아니라 내용 (커밋)의 변경 사항을 기록하는 것뿐입니다. 해당 커밋 (분기, 병합, 리베이스, 재설정 등)에 대해 기록하고 기록 (차이, 로그, 비난 등)을 기반으로 보고서를 볼 수 있습니다. git은 "멍청한 콘텐츠 추적기"입니다. 저는 이것을 타임머신이 아닌 소스 코드를 관리하는 도구로 봅니다.
wuputah

5
당신이 말했듯이 그것은 주관적입니다. 포기한 접근 방식을 검토하고 과거에 어떤 결정이 내려 졌는지 확인할 수 있는지에 관심이 있습니다. 그리고 나는 무언가를 포기하기로 결정한 것이 다른 사람들이 가리키는 병합 지점을 파괴하지 않는다는 점에 관심이 있습니다.
Arne Claassen

답변:


107

당신은 당신의 상류 지점을 병합 할 수 dev으로, 지점 사용자 정의 병합 드라이버 "keepTheirs" :
페이지의 " " git merge -s theirs"-하지만 존재하지 않는 알 필요 ".
귀하의 경우에는 하나만 .gitattributes필요하며 다음과 같은 keepTheirs스크립트 가 필요합니다 .

mv -f $3 $2
exit 0

git merge --strategy=theirs 시뮬레이션 # 1

첫 번째 상위가 업스트림 인 병합으로 표시됩니다.

Jefromimerge -s ours업스트림 (또는 업스트림에서 시작하는 임시 분기)에서 작업을 병합 한 다음 해당 병합의 결과로 분기를 빠르게 전달하여 주석에서를 언급합니다 .

git checkout -b tmp origin/upstream
git merge -s ours downstream         # ignoring all changes from downstream
git checkout downstream
git merge tmp                        # fast-forward to tmp HEAD
git branch -D tmp                    # deleting tmp

이는 업스트림 조상을 첫 번째 부모로 기록하는 이점이 있으므로 병합은 "이 주제 브랜치를 파괴하고 업스트림으로 교체"하는 대신 "이 오래된 주제 브랜치를 흡수"하는 것을 의미 합니다.

(2011 년 편집) :

이 워크 플로는 OP에 의해이 블로그 게시물에 보고되었습니다 .

왜 이걸 다시 원합니까?

내 저장소가 공개 버전과 관련이없는 한 괜찮 았지만 이제는 WIP에서 다른 팀 구성원 및 외부 기여자와 공동 작업 할 수있는 기능을 원했기 때문에 공개 분기가 다른 사람들이 분기하고 가져올 수 있습니다. 즉, 이제 GitHub 및 공개에 있으므로 원격 백업에 푸시 한 항목을 더 이상 리베이스하거나 재설정 할 필요가 없습니다.

그래서 내가 어떻게 진행해야할지 남깁니다.
99 %의 시간 동안 내 사본이 업스트림 마스터로 이동하므로 대부분의 시간 동안 내 마스터를 작업하고 업스트림으로 푸시하고 싶습니다.
그러나 가끔씩, 내가 가진 것은 wip업스트림으로 들어가는 것에 의해 무효화되고 내 wip.
이 시점에서 마스터를 업스트림과 다시 동기화하고 싶지만 공개적으로 푸시 된 마스터의 커밋 포인트를 파괴하지는 않습니다. 즉, 업스트림과 동일한 복사본을 만드는 변경 집합으로 끝나는 업스트림과의 병합을 원합니다 .
그리고 그것이 git merge --strategy=theirs해야 할 일입니다.


git merge --strategy=theirs 시뮬레이션 # 2

첫 번째 부모로 우리와 병합으로 표시됩니다.

( jcwenger 제안 )

git checkout -b tmp upstream
git merge -s ours thebranch         # ignoring all changes from downstream
git checkout downstream
git merge --squash tmp               # apply changes from tmp but not as merge.
git rev-parse upstream > .git/MERGE_HEAD #record upstream 2nd merge head
git commit -m "rebaselined thebranch from upstream" # make the commit.
git branch -D tmp                    # deleting tmp

git merge --strategy=theirs 시뮬레이션 # 3

블로그 게시물에 언급 된 내용 :

git merge -s ours ref-to-be-merged
git diff --binary ref-to-be-merged | git apply -R --index
git commit -F .git/COMMIT_EDITMSG --amend

때때로 당신은 이것을 하기를 원합니다. 당신의 역사에 "쓰레기"가있어서가 아니라 아마도 리베이스를 피해야하는 공용 저장소에서 개발을위한 기준을 변경하기를 원하기 때문일 것 입니다.


git merge --strategy=theirs 시뮬레이션 # 4

(동일한 블로그 게시물)

또는 로컬 업스트림 분기를 빨리 감기 가능하게 유지하려는 경우 잠재적 인 타협은 sid / 불안정한 경우 업스트림 분기가 수시로 재설정 / 재 기반 될 수 있다는 이해와 함께 작업하는 것입니다 (최종적으로 종료되는 이벤트를 기반으로 함). 업스트림 프로젝트 측에 대한 제어).
이것은 큰 문제가 아니며 그 가정에 따라 작업한다는 것은 로컬 업스트림 분기를 빠른 업데이트 만받는 상태로 유지하는 것이 쉽다는 것을 의미합니다.

git branch -m upstream-unstable upstream-unstable-save
git branch upstream-unstable upstream-remote/master
git merge -s ours upstream-unstable
git diff --binary ref-to-be-merged | git apply -R --index --exclude="debian/*"
git commit -F .git/COMMIT_EDITMSG --amend

git merge --strategy=theirs 시뮬레이션 # 5

( Barak A. Pearlmutter 제안 ) :

git checkout MINE
git merge --no-commit -s ours HERS
git rm -rf .
git checkout HERS -- .
git checkout MINE -- debian # or whatever, as appropriate
git gui # edit commit message & click commit button

git merge --strategy=theirs 시뮬레이션 # 6

(동일한 Michael Gebetsroither가 제안 ) :

Michael Gebetsroither는 내가 "속임수"라고 주장하면서 차를 댔고;) 낮은 수준의 배관 명령으로 또 다른 해결책을 제시했습니다.

(git 전용 명령으로 가능하지 않다면 git이 아닙니다. diff / patch / apply를 사용하는 git의 모든 것은 실제 솔루션이 아닙니다.)

# get the contents of another branch
git read-tree -u --reset <ID>
# selectivly merge subdirectories
# e.g superseed upstream source with that from another branch
git merge -s ours --no-commit other_upstream
git read-tree --reset -u other_upstream     # or use --prefix=foo/
git checkout HEAD -- debian/
git checkout HEAD -- .gitignore
git commit -m 'superseed upstream source' -a

git merge --strategy=theirs 시뮬레이션 # 7

필요한 단계는 다음과 같이 설명 할 수 있습니다.

  1. 작업 트리를 업스트림으로 교체
  2. 인덱스에 변경 사항 적용
  3. 두 번째 상위 항목으로 업스트림 추가
  4. 범하다

이 명령 git read-tree두 번째 단계를 수행하는 다른 트리로 색인을 덮어 쓰고 첫 번째 단계를 수행하는 작업 트리를 업데이트하는 플래그를 갖습니다 . 커밋 할 때 git은 .git / MERGE_HEAD의 SHA1을 두 번째 부모로 사용하므로이를 채워 병합 커밋을 만들 수 있습니다. 따라서 다음을 사용하여 수행 할 수 있습니다.

git read-tree -u --reset upstream                 # update files and stage changes
git rev-parse upstream > .git/MERGE_HEAD          # setup merge commit
git commit -m "Merge branch 'upstream' into mine" # commit

7
당신은 항상 그들의 것 대신 우리를 사용할 수 있습니다 : 다른 브랜치를 확인하고, 당신의 브랜치를 그것으로 병합 한 다음, 머지로 빨리 감습니다. git checkout upstream; git merge -s ours downstream; git checkout downstream; git merge upstream. (필요한 경우 업스트림에서 임시 브랜치를 사용합니다.) 이렇게하면 업스트림 조상을 첫 번째 상위 항목으로 기록하는 이점이 있으므로 병합은 "이 주제 브랜치를 파괴하고 교체하는 대신이 오래된 주제 브랜치를 흡수"하는 것을 의미합니다. 그것은 업스트림과 함께 ".
Cascabel

@Jefromi : 평소처럼 훌륭한 지적입니다. 나는 그것을 내 대답에 포함시켰다.
VonC

또 다른 옵션-git merge --strategy = theirs Simulation # 1-이 옵션은 brange를 첫 번째 병합 부모로 유지한다는 점을 제외하고 : git checkout -b tmp origin / upstream git merge -s ours downstream # 다운 스트림 git의 모든 변경 사항 무시 checkout downstream git merge --squash tmp # tmp에서 변경 사항을 적용하지만 병합으로는 적용하지 않습니다. git rev-parse upstream> .git / MERGE_HEAD # 두 번째 병합 헤드 로 업스트림 기록 git commit -m "rebaselined ours from upstream"# 커밋을 만듭니다. git branch -D tmp # tmp 삭제
jcwenger 2011-06-30

2
와우, 누가 --strategy = theirs가 그렇게 많은 방법으로 구현 될 수 있다고 생각했을까요. 이제 git의 다음 버전에있을 수 있다면
Arne Claassen

VonC와 그의 지식은 놀랍습니다. 그는 git의 JonSkeet과 같습니다. :)
sjas 2013 년

13

당신이해야 할 일처럼 들립니다.

$ git reset --hard origin/master

업스트림을 푸시 할 변경 사항이없고 단순히 업스트림 분기를 현재 분기로 설정하려는 경우 이렇게하면됩니다. 로컬에서이 작업을 수행하는 것은 해롭지 않지만 마스터로 푸시되지 않은 로컬 변경 **은 손실됩니다.

** 실제로 로컬로 커밋 한 경우 변경 사항은 여전히 ​​남아 있습니다. 커밋은 git reflog일반적으로 최소 30 일 동안.


이것은 어떤 가격 으로든 변경이 필요한 경우 작동합니다. 브랜치의 기록을 변경할 수 있기 때문입니다 (-f로 푸시해야 함). 차단되도록 구성 할 수 있으므로 기본적으로 사용자 소유의 개인 저장소에 대해서만 실제로 작동합니다.
ribamar

12

이제 쉽게 할 수 있습니다.

$ git fetch origin
$ git merge origin/master -s recursive -Xtheirs

이렇게하면 로컬 저장소가 오리진과 동기화되고 기록이 보존됩니다.


5
git merge -s recursive -Xtheirs바이너리 파일을 자동으로 병합하지 않으므로 수동으로 해결해야하는 충돌 상황이 발생합니다. 를 기반으로하는 워크 플로는 git merge -s ours이 문제를 겪지 않습니다.
Stefaan 2013 년

이것은 빈 커밋을 만드는 것처럼 보입니다.
Robin Green

내 사과-실제로 작동하지만 git show병합 커밋에서는 충돌 해결 만 표시 -Xtheirs되며 사용되는 경우 충돌 해결 은 분명히 없습니다.
로빈 그린

5

다음에 대한 또 다른 시뮬레이션 git merge -s theirs ref-to-be-merged:

git merge --no-ff -s ours ref-to-be-merged         # enforce a merge commit; content is still wrong
git reset --hard HEAD^2; git reset --soft HEAD@{1} # fix the content
git commit --amend

이중 재설정의 대안은 리버스 패치를 적용하는 것입니다.

git diff --binary ref-to-be-merged | git apply -R --index

리버스 패치의 흥미로운 사용. +1
VonC

재설정이 작동하지 않았습니다. "치명적 : 모호한 인수 'HEAD2': 알 수없는 개정 또는 작업 트리에 경로가 없습니다."라는 메시지가 표시됩니다. . (예, 입력했습니다 HEAD^2) 패치 방법이 작동했습니다.
user247702 apr

@Stijn : 아마도 당신은 실제로 ^올바르게 입력하지 않았을 것입니다. 때때로 "ctrl-c"와 같은 다른 키 입력은 "^ C"로 표시됩니다. -올바른 "^"를 입력했다면 git 버전에서 심각한 버그를 발견 한 것입니다.
michas

@michas Windows에서는 ^ 문자가 이스케이프에 사용되므로 문자로 사용하려면 자체적으로 이스케이프해야합니다. git reset --hard HEAD^^2; git reset --soft HEAD@{1}
여호수아 월시

4

배관 명령의 도움이 거의없는 방법도 있습니다. IMHO가 가장 간단합니다. 2 개의 브랜치 케이스에 대해 "theirs"를 에뮬레이트하고 싶다고 가정 해 보겠습니다.

head1=$(git show --pretty=format:"%H" -s foo)
head2=$(git show --pretty=format:"%H" -s bar)
tree=$(git show --pretty=format:"%T" -s bar)
newhead=$(git commit-tree $tree -p $head1 -p $head2 <<<"merge commit message")
git reset --hard $newhead

이것은 임의의 수의 헤드 (위의 예에서 2)를 병합하고, diff / file 문제를 무시하고 (커밋 트리는 저수준 명령이므로) 그중 하나의 트리 (위의 예에서는 bar, '그들의'트리 제공)를 사용합니다. 그것들은 신경 쓰지 않습니다). head는 1 일 수 있습니다 ( "theirs"가 포함 된 cherry-pick과 동일).

어떤 부모 헤드가 먼저 지정되는지는 일부 항목에 영향을 미칠 수 있습니다 (예 : git-log 명령의 --first-parent 참조). 따라서이를 염두에 두십시오.

git-show 대신, 트리 및 커밋 해시를 출력 할 수있는 다른 모든 것이 사용될 수 있습니다. 어떤 것이 든 파싱에 사용됩니다 (cat-file, rev-list, ...). 커밋 메시지를 대화식으로 아름답게하기 위해 git commit --amend로 모든 것을 따를 수 있습니다.


이것은 내 눈에 가장 간단합니다. 배관 명령을 사용하여 "이 트리,이 첫 번째 부모,이 두 번째 부모로 새 커밋 개체를 만듭니다. 그런 다음 머리를이 새 커밋으로 가리 킵니다."라고 말하고 있습니다. 이것이 바로 "git merge -s theirs"입니다. 할 것. 단점은이 작업을 수행하려면 4 개의 다른 해시를 저장해야한다는 것입니다.
Michael R

2

헤비 핸드이지만 지옥, 무엇이 잘못 될 수 있습니까?

  • Y처럼 보이고 싶은 지점 X를 확인하십시오.
  • cp -r .git /tmp
  • 지점 Y 확인 git checkout y
  • rm -rf .git && cp -r /tmp/.git .
  • 차이를 약속하고 추진하십시오
  • 끝난.

이것은 병합 기록을 유지하는 데 신경 쓰지 않는다고 가정 할 때 두 브랜치를 동일하게 만드는 가장 간단하고 무차별적인 방법입니다.
Damon D

1

원격 업스트림 분기로 변경 git merge하고 병합 전략을 ours.

git checkout origin/master
git merge dev --strategy=ours
git commit ...
git push

모든 기록은 여전히 ​​존재하지만 추가 병합 커밋이 있습니다. 여기서 중요한 것은 원하는 버전에서 시작하여 oursgithub가 실제로있는 지점 과 병합 하는 것입니다.


1
나는 그 반대가 필요합니다. 그것은 내 지점을 가져 와서 업스트림에 통합하지만 업스트림 헤드는 변경되지 않습니다. 그러나 나는 업스트림을 가져 와서 내 지점에 통합해야하며 머리는 업스트림처럼 보입니다. --strategy=theirs가장 가까운 사람 --strategy=recursive -X=theirs이 그렇게하지 않는 것을 제외하고는 기본적으로 .
Arne Claassen

--strategy=theirs의 반대입니다 --strategy=ours. 반대쪽 끝에서 시작합니다 (그러므로 github에서 시작하고 다른 방법으로 병합).
kelloti

--strategy=theirs문제 가 없습니다 . 가장 가까운 것은 --strategy=recursive -X theirs충돌하지 않는 경우 외부 로컬 변경을 제거하지 않기 때문에 정반대가 아닙니다.
Arne Claassen

:이 두 정반대 git checkout dev; git merge origin/master --strategy=oursgit checkout origin/master; git merge dev --strategy=ours
wuputah

2
@Arne : VonC의 답변에 대한 내 의견을 참조하십시오. ours전략 의 존재는 전략을 수행하는 것을 완전히 가능하게합니다 theirs.
Cascabel

1

git reset BACKWARDS를 사용하십시오!

를 사용하여 브랜치를 다른 커밋처럼 보이게 만들 수 git reset있지만 라운드 어바웃 방식으로해야합니다.

커밋에서 브랜치를 커밋 <old>처럼 보이게 하려면 <new>다음을 수행 할 수 있습니다.

git reset --hard <new>

<new>작업 트리의 내용 을 만들기 위해 .

그런 다음

git reset --mixed <old> 

분기를 원래 커밋으로 다시 변경 하지만 작업 트리를 <new> 상태로 유지 합니다.

그런 다음 분기를 <new>커밋 내용과 정확히 일치시키기 위해 변경 사항을 추가하고 커밋 할 수 있습니다 .

그것은의 반 직관적에서 이동하는 것을 <old>에 상태는 <new>당신이 할 필요가 git reset 에서 <new> <old> . 그러나 옵션을 사용 --mixed하면 작업 트리가 남아 <new>있고 분기 포인터가로 설정되어 <old>변경 사항이 커밋 될 때 분기가 원하는대로 보입니다.

경고

커밋을 추적하지 마십시오. 예를 들어 <old>할 때 무엇을하는지 잊어 버리십시오 git reset --hard <new>.


0

나는 그 역할을 따랐다.

원점 가져 오기, 분기에서 하드 재설정 한 다음 해당 분기에서 재귀 한 다음 강제로 분기로 푸시

자신의 위험 부담

git fetch origin
git reset --hard origin/<branch>
git merge origin/<branch> -s recursive -Xtheirs
git push -f <remote> <branch>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.