Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
173 views
in Technique[技术] by (71.8m points)

"git merge -s theirs" needed -- but I know it doesn't exist

I have a number of remote repositories that I want to merge together. Some of the subtrees in those repositories are unique to the remote (they contain data that is host-specific), other subtrees contain data that is (supposed to be) common across all remotes.

What I want to do, essentially, is run "git pull " for each remote. This will fast-forward the local master branch along the tracking branch for the remote master for host-specific files that have changed on the remote, and will do nothing for the common files because they will not have changed.

A change in a common file (call it F, with the change being F') shouldn't be a problem, even if it only happens on one remote at first. git-merge will Do The Right Thing and give me a copy of F' in my composite workspace, which is what I want. The problem comes if the same common file changes in a different way on another remote (call it F"). git-merge will give me a composite of F' and F", which isn't what I want. All I want is F".

When I worked with ClearCase we called this a copy-merge. The result of the merge was always an exact copy of the contributor. This sounds a lot like "git merge -s theirs" would be, except that it doesn't exist.

I wondering whether I can cook something up with "git-read-tree -m --trivial" to get the fast-forward merges out of the way, then do some magic with git-merge and a custom mergetool that simply copies the $REMOTE file to $MERGED. But even with that I don't see how I can stop git-merge from compositing F' and F" if it things the merge is trivial.

I've read the link Is there a "theirs" version of "git merge -s ours"? on this site, and the post by Junio Hamano it references explaining why "git merge -s theirs" is such a bad idea, but this isn't the case for me. I do value the old history, but I need to jump ship and follow the change on the remote site when one happens. No new work is done on the local site. It simply needs to form a composite of all the remote sites, taking the latest "common" file from the last polled remote when one changes.

Thanks in advance for any help you can give me.

Question&Answers:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Many thanks to @VonC for suggesting the use of the merge=custom-driver attribute in the .gitattributes file. While this will work, I'm reluctant to pollute my workspace with .git files, and while I could use $GIT_DIR/info/attributes to avoid the pollution, I'm bothered by the need for 2 rules to catch dot-files and non-dot files.

After a bit of experimentation I managed to get a solution with the merge.default configuration variable (mentioned in the gitattributes(5) manpage) working. The trick I missed was that merge.default takes the name of a custom driver you have defined previously; you don't give it the custom command directly. Here's what works for me...

First define your copy-merge custom driver. You can use shell commands directly; there's no need for an external script (just make sure you get your shell meta-character quoting right):

git config merge.copy-merge.name   'Copy Merge'
git config merge.copy-merge.driver 'mv %B %A'

Note that mv returns 0 on success, 1 on failure, meeting the criteria for reporting merge "success" back to git.

Now tell git that ALL merges are copy-merges:

git config merge.default copy-merge

Hey Presto! Job done. git merge <branch> will now copy-merge everything so the branch you're on contains exact copies of all files on <branch>. QED.

If you want to do a non-copy-merge then simply reset the default merge driver:

git config --unset merge.default

If you do want to be more selective then leave merge.default unset and use attributes as @VonC says:

cd path/to/copy-merge/in
echo '* merge=copy-merge'  >  .gitattributes
echo '.* merge=copy-merge' >> .gitattributes

Do this at the top of every subtree you want to copy-merge in. If there's a sub-subtree that you DON'T want to copy-merge in, you can turn it off again:

cd path/to/copy-merge/in/path/to/normal-merge/in
echo '* merge'  >  .gitattributes
echo '.* merge' >> .gitattributes

WARNING: littering your working tree with lots of .gitattributes files is bound to lead to confusion, especially if you also use things like "*.bin -merge" in other directories to force all merges of .bin files to fail with conflicts. It may be better to use $GIT_DIR/info/attributes for this sort of thing, as it has the highest precedence.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...