...For the cases where, yes, you actually do want it.
For the impatient
assuming you're in a clean checkout on the tip of your current branch:
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
you can then double check that everything is as you expect with
git diff ref-to-be-merged, which should be empty.
The longer story
It's noted from time to time that git does not have a -s theirs
option to complement the -s ours strategy when merging (or, it did,
but it was removed long ago, see below). For those not savvy the
idea with git merge -s ours is to pretend as though a merge from some
other branch has occurred, though the result remains the current branch's
contents, unchanged. This is a neat trick that can be used to show that
all changes in some branch have been superceded by the current branch,
which can help ease merges further down the line as parallel devlopment
continues.
However, there is no corresponding -s theirs option, which would
conversely say that "everything developed up to here is superceded by
this other branch". This feature was
discussed and discarded
by one of the git authors, who instead advised a developer to throw away
the previous work and start on a fresh branch:
One big problem "-s theirs" has, compared to the above "reset to origin, discarding or setting aside the failed history" is that your 'master' history that your further development is based on will keep your failed crap in it forever if you did "-s theirs". Hopefully you will become a better programmer over time, and you may eventually have something worth sharing with the world near the tip of your master branch. When that happens, however, you cannot offer your master branch to be pulled by the upstream, as the wider world will not be interested in your earlier mistakes at all.
In what would probably qualify as "most cases", he's right, but sometimes you do want to do this, and not because you have "crap" in your history, but perhaps because you want to change the baseline for development in a public repository where rebasing should be avoided.
In debian, for example, it's pretty common for maintainers who use git-buildpackage to build debian packages from git to have two branches: an "upstream" branch and a "debianized" branch. The upstream branch will follow the changes from the upstream project (either by importing tarballs, or by pulling directly from the project's git repo if they also use git), while the debianized branch contains all the packaging related changes as well as any debian-specific changes to the code.
In this context, if the upstream project uses git and rebases their
history (out of your control), or if they shift development to a different
branch, you might want to have a way to merge with their latest changes
without rebasing/abandoning your current debian branch. Hence, a "theirs"
merge.
The author is totally right that the upstream project may be less interested
in pulling from such a repository (since they would drag in the entire
previous history), and if that's a problem you might need to
splinter off the old branch and start a fresh branch instead. But at
least for the projects with which I work, I find that it's more likely
that pulling is pretty much one way upstream to debian, and that the
patches flow back to the upstream project by way of bug trackers,
git format-patch | sendmail, or quilt patches sitting in the
patch-tracker. And if it becomes
a problem later you're no worse off, you can still split off a new
branch or take some other action.
So anyway, I asked Teh Internetz and found a few hits in the right direction, such as this guy at stack overflow, or this guy here. But from an aesthetic point of view I thought their solutions still left some room for improvement--they were both more complicated, involved setting up temporary branches, and I don't know, just weren't pretty on the eyes. So, ending where I started, this is the relatively clean and easy to follow 3 lines I conjured up, which I will give to teh internetz for posterity.
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
Alternatively, if you want to keep the local upstream branches
fast-forwardable, a potential compromise is to work with the
understanding that for sid/unstable, the upstream branch can
from time to time be reset/rebased (based on events that are
ultimately out of your control on the upstream project's side).
This isn't a big deal and working with that assumption means that
it's easy to keep the local upstream branch in a state where it
only takes fast-forward updates. However, on the debian branch
your less interested in the clean upstream development, and instead
want to keep a sane history of the debianization work, so on this
branch you still do something like a merge -s theirs (though in
this case, you probably want to amend the previous version's ./debian
dir back in post-merge). So applying the same approach as above
with the slight modification, in practice (assuming you're on the clean
tip of the debian unstable branch, that'd be):
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
PS
In spirit, this is a similar approach to what's done by the XSF team, though I would argue that with what I'm describing both the approach and the resulting history are a bit cleaner and easier to follow.
Also, thanks to ron, mrvn, jcristau, and nthykier on #debian-devel for some quick reviewing of this.
PPS
From further usage, I discovered if you have non-text files involved you'll
need a --binary thrown into the above example, so I've updated them
accordingly.
I also got a bit more feedback that I'd like to share, just for the sake of being complete and showing some alternatives (TMTOWTDI).
Barak A. Pearlmutter shared his version, which from a quick read would also do the trick:
When I want "git merge -s theirs HERS" onto MINE, I do this:
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
Michael Gebetsroither chimed in, claiming i was "cheating"
and gave
another solution with lower-level plumbing commands:
(it wouldn't be git if it wouldn't be possible with git only commands, everything in git with diff/patch/apply isn't a real solution ;).
e.g
# 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
Thanks for the feedback!