programing tip

“git merge -s ours”의“그들의”버전이 있습니까?

itbloger 2020. 9. 29. 07:23
반응형

“git merge -s ours”의“그들의”버전이 있습니까?


를 사용하여 주제 분기 "B"를 "A"로 병합 할 때 git merge충돌이 발생합니다. 모든 충돌은 "B"버전을 사용하여 해결할 수 있다는 것을 알고 있습니다.

나는 알고있다 git merge -s ours. 그러나 내가 원하는 것은 git merge -s theirs.

왜 존재하지 않습니까? 기존 git명령 과 충돌하는 병합 후 어떻게 동일한 결과를 얻을 수 있습니까? ( git checkoutB에서 병합되지 않은 모든 파일)

업데이트 : 분기 A (트리의 B 버전에 대한 병합 커밋 지점)에서 아무것도 버리는 "솔루션"은 내가 찾고있는 것이 아닙니다.


-X옵션을 추가합니다 theirs. 예를 들면 :

git checkout branchA
git merge -X theirs branchB

모든 것이 원하는 방식으로 병합됩니다.

내가 본 유일한 문제는 branchB에서 파일이 삭제 된 경우입니다. git 이외의 다른 작업이 제거되면 충돌로 표시됩니다.

수정은 쉽습니다. git rm삭제 된 파일의 이름으로 실행 하십시오.

git rm {DELETED-FILE-NAME}

그 후 -X theirs예상대로 작동합니다.

물론 git rm명령 을 사용하여 실제 제거를 수행 하면 처음에 충돌이 발생하는 것을 방지 할 수 있습니다.


참고 : 더 긴 양식 옵션도 있습니다. 사용하려면 다음을 교체하십시오.

-X theirs

와:

--strategy-option=theirs

branchB를 체크 아웃 된 branchA에 병합하기위한 가능한 테스트 솔루션 :

# in case branchA is not our current branch
git checkout branchA

# make merge commit but without conflicts!!
# the contents of 'ours' will be discarded later
git merge -s ours branchB    

# make temporary branch to merged commit
git branch branchTEMP         

# get contents of working tree and index to the one of branchB
git reset --hard branchB

# reset to our merged commit but 
# keep contents of working tree and index
git reset --soft branchTEMP

# change the contents of the merged commit
# with the contents of branchB
git commit --amend

# get rid off our temporary branch
git branch -D branchTEMP

# verify that the merge commit contains only contents of branchB
git diff HEAD branchB

이를 자동화하려면 branchA 및 branchB를 인수로 사용하여 스크립트로 래핑 할 수 있습니다.

이 솔루션은 예상대로 병합 커밋의 첫 번째 및 두 번째 부모를 유지합니다 git merge -s theirs branchB.


이전 버전의 git에서는 "자신의"병합 전략을 사용할 수있었습니다.

git pull --strategy=theirs remote_branch

그러나 Junio ​​Hamano (Git 관리자) 의이 메시지에 설명 된대로 이후 제거되었습니다 . 링크에 표시된대로 대신 다음을 수행합니다.

git fetch origin
git reset --hard origin

그러나 이것은 실제 병합과 다르다는 점에 유의하십시오. 귀하의 솔루션은 아마도 귀하가 정말로 찾고있는 옵션 일 것입니다.


지금부터 Paul Pladijs의 답변을 사용했습니다. 나는 당신이 "정상적인"병합을 할 수 있다는 것을 알아 냈다. 충돌이 발생한다.

git checkout --theirs <file>

다른 지점의 개정판을 사용하여 충돌을 해결합니다. 각 파일에 대해이 작업을 수행하면 예상 한 것과 동일한 동작이 발생합니다.

git merge <branch> -s theirs

어쨌든, 그 노력은 합병 전략보다 더 많습니다! (이것은 git 버전 1.8.0으로 테스트되었습니다)


원하는 결과가 무엇인지 완전히 명확하지 않으므로 답변과 의견에서 "올바른"방법에 대해 약간의 혼란이 있습니다. 개요를 제공하고 다음 세 가지 옵션을 확인하려고합니다.

병합을 시도하고 충돌에 B를 사용하십시오.

입니다 하지 은 "에 대한 그들의 버전 git merge -s ours"하지만 "에 대한 그들의 버전 git merge -X ours(대한 짧은" git merge -s recursive -X ours) :

git checkout branchA
# also uses -s recursive implicitly
git merge -X theirs branchB

이것은 예를 들어 Alan W. Smith의 답변 입니다.

B의 콘텐츠 만 사용

이렇게하면 두 분기 모두에 대한 병합 커밋이 생성되지만의 모든 변경 사항이 삭제 branchA되고에서 내용 만 유지됩니다 branchB.

# Get the content you want to keep.
# If you want to keep branchB at the current commit, you can add --detached,
# else it will be advanced to the merge commit in the next step.
git checkout branchB

# Do the merge an keep current (our) content from branchB we just checked out.
git merge -s ours branchA

# Set branchA to current commit and check it out.
git checkout -B branchA

Note that the merge commits first parent now is that from branchB and only the second is from branchA. This is what e.g. Gandalf458's answer does.

Use content from B only and keep correct parent order

This is the real "theirs version for git merge -s ours". It has the same content as in the option before (i.e. only that from branchB) but the order of parents is correct, i.e. the first parent comes from branchA and the second from branchB.

git checkout branchA

# Do a merge commit. The content of this commit does not matter,
# so use a strategy that never fails.
# Note: This advances branchA.
git merge -s ours branchB

# Change working tree and index to desired content.
# --detach ensures branchB will not move when doing the reset in the next step.
git checkout --detach branchB

# Move HEAD to branchA without changing contents of working tree and index.
git reset --soft branchA

# 'attach' HEAD to branchA.
# This ensures branchA will move when doing 'commit --amend'.
git checkout branchA

# Change content of merge commit to current index (i.e. content of branchB).
git commit --amend -C HEAD

This is what Paul Pladijs's answer does (without requiring a temporary branch).


I solved my problem using

git checkout -m old
git checkout -b new B
git merge -s ours old

If you are on branch A do:

git merge -s recursive -X theirs B

Tested on git version 1.7.8


When merging topic branch "B" in "A" using git merge, I get some conflicts. I >know all the conflicts can be solved using the version in "B".

I am aware of git merge -s ours. But what I want is something like git merge >-s their.

I'm assuming that you created a branch off of master and now want to merge back into master, overriding any of the old stuff in master. That's exactly what I wanted to do when I came across this post.

Do exactly what it is you want to do, Except merge the one branch into the other first. I just did this, and it worked great.

git checkout Branch
git merge master -s ours

Then, checkout master and merge your branch in it (it will go smoothly now):

git checkout master
git merge Branch

To really properly do a merge which takes only input from the branch you are merging you can do

git merge --strategy=ours ref-to-be-merged

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

git commit --amend

There will be no conflicts in any scenario I know of, you don't have to make additional branches, and it acts like a normal merge commit.

This doesn't play nice with submodules however.


See Junio Hamano's widely cited answer: if you're going to discard committed content, just discard the commits, or at any rate keep it out of the main history. Why bother everyone in the future reading commit messages from commits that have nothing to offer?

But sometimes there are administrative requirements, or perhaps some other reason. For those situations where you really have to record commits that contribute nothing, you want:

(edit: wow, did I manage to get this wrong before. This one works.)

git update-ref HEAD $(
        git commit-tree -m 'completely superseding with branchB content' \
                        -p HEAD -p branchB    branchB:
)
git reset --hard

Why doesn't it exist?

While I mention in "git command for making one branch like another" how to simulate git merge -s theirs, note that Git 2.15 (Q4 2017) is now clearer:

The documentation for '-X<option>' for merges was misleadingly written to suggest that "-s theirs" exists, which is not the case.

See commit c25d98b (25 Sep 2017) by Junio C Hamano (gitster).
(Merged by Junio C Hamano -- gitster -- in commit 4da3e23, 28 Sep 2017)

merge-strategies: avoid implying that "-s theirs" exists

The description of -Xours merge option has a parenthetical note that tells the readers that it is very different from -s ours, which is correct, but the description of -Xtheirs that follows it carelessly says "this is the opposite of ours", giving a false impression that the readers also need to be warned that it is very different from -s theirs, which in reality does not even exist.

-Xtheirs is a strategy option applied to recursive strategy. This means that recursive strategy will still merge anything it can, and will only fall back to "theirs" logic in case of conflicts.

That debate for the pertinence or not of a theirs merge strategy was brought back recently in this Sept. 2017 thread.
It acknowledges older (2008) threads

In short, the previous discussion can be summarized to "we don't want '-s theirs' as it encourages the wrong workflow".

It mentions the alias:

mtheirs = !sh -c 'git merge -s ours --no-commit $1 && git read-tree -m -u $1' -

Yaroslav Halchenko tries to advocate once more for that strategy, but Junio C. Hamano adds:

The reason why ours and theirs are not symmetric is because you are you and not them---the control and ownership of our history and their history is not symmetric.

Once you decide that their history is the mainline, you'd rather want to treat your line of development as a side branch and make a merge in that direction, i.e. the first parent of the resulting merge is a commit on their history and the second parent is the last bad one of your history. So you would end up using "checkout their-history && merge -s ours your-history" to keep the first-parenthood sensible.

And at that point, use of "-s ours" is no longer a workaround for lack of "-s theirs".
It is a proper part of the desired semantics, i.e. from the point of view of the surviving canonical history line, you want to preserve what it did, nullifying what the other line of history did.

Junio adds, as commented by Mike Beaton:

git merge -s ours <their-ref> effectively says 'mark commits made up to <their-ref> on their branch as commits to be permanently ignored';
and this matters because, if you subsequently merge from later states of their branch, their later changes will be brought in without the ignored changes ever being brought in.


This one uses a git plumbing command read-tree, but makes for a shorter overall workflow.

git checkout <base-branch>

git merge --no-commit -s ours <their-branch>
git read-tree -u --reset <their-branch>
git commit

# Check your work!
git diff <their-branch>

This will merge your newBranch in existing baseBranch

git checkout <baseBranch> // this will checkout baseBranch
git merge -s ours <newBranch> // this will simple merge newBranch in baseBranch
git rm -rf . // this will remove all non references files from baseBranch (deleted in newBranch)
git checkout newBranch -- . //this will replace all conflicted files in baseBranch

I think what you actually want is:

git checkout -B mergeBranch branchB
git merge -s ours branchA
git checkout branchA
git merge mergeBranch
git branch -D mergeBranch

This seems clumsy, but it should work. The only think I really dislike about this solution is the git history will be confusing... But at least the history will be completely preserved and you won't need to do something special for deleted files.


The equivalent(which keep parent order) to 'git merge -s theirs branchB'

Before merge:enter image description here

!!! Make sure you are in clean state !!!

Do the merge:

git commit-tree -m "take theirs" -p HEAD -p branchB 'branchB^{tree}'
git reset --hard 36daf519952 # is the output of the prev command

What we did ? We created a new commit which two parents ours and theirs and the contnet of the commit is branchB - theirs

After merge:enter image description here

More precisely:

git commit-tree -m "take theirs" -p HEAD -p 'SOURCE^{commit}' 'SOURCE^{tree}'

This answer was given by Paul Pladijs. I just took his commands and made a git alias for convenience.

Edit your .gitconfig and add the following:

[alias]
    mergetheirs = "!git merge -s ours \"$1\" && git branch temp_THEIRS && git reset --hard \"$1\" && git reset --soft temp_THEIRS && git commit --amend && git branch -D temp_THEIRS"

Then you can "git merge -s theirs A" by running:

git checkout B (optional, just making sure we're on branch B)
git mergetheirs A

I just recently needed to do this for two separate repositories that share a common history. I started with:

  • Org/repository1 master
  • Org/repository2 master

I wanted all the changes from repository2 master to be applied to repository1 master, accepting all changes that repository2 would make. In git's terms, this should be a strategy called -s theirs BUT it does not exist. Be careful because -X theirs is named like it would be what you want, but it is NOT the same (it even says so in the man page).

The way I solved this was to go to repository2 and make a new branch repo1-merge. In that branch, I ran git pull git@gitlab.com:Org/repository1 -s ours and it merges fine with no issues. I then push it to the remote.

Then I go back to repository1 and make a new branch repo2-merge. In that branch, I run git pull git@gitlab.com:Org/repository2 repo1-merge which will complete with issues.

Finally, you would either need to issue a merge request in repository1 to make it the new master, or just keep it as a branch.


A simple and intuitive (in my opinion) two-step way of doing it is

git checkout branchB .
git commit -m "Picked up the content from branchB"

followed by

git merge -s ours branchB

(which marks the two branches as merged)

The only disadvantage is that it does not remove files which have been deleted in branchB from your current branch. A simple diff between the two branches afterwards will show if there are any such files.

This approach also makes it clear from the revision log afterwards what was done - and what was intended.

참고URL : https://stackoverflow.com/questions/173919/is-there-a-theirs-version-of-git-merge-s-ours

반응형