...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!

Posted Thu Feb 24 06:08:22 2011 Tags: git

continuing on the trend of finding novel ways to make life more interesting with git, here's the description from a new hook:

# prepare-commit-msg hook for debian package git repositories
#
# this script scans the diff that is going into a commit, and automatically
# injects some "proposed" comments based on what it finds in the diff.  this
# can be used to avoid a few extra keystrokes when performing some of the
# more standard/boring tasks.

(see below for how to fetch the script)

this one definitely falls in the "carrot" category, as it encourages properly isolated (and thus automatically identifiable) changes. the proposed comments are then given to the standard editor, so one can easily amend them, append "Closes:" lines, etc.

some sample use-cases currently implemented:

  • changelog entries

    • detect when a new version is introduced in debian/changelog

      prepare release information for <dist>/<vers>
      
    • detect when an existing version is updated in debian/changelog

      release information for <dist>/<vers>
      
  • debconf translations

    • detect when one or more po files are modified

      updates to (<lang1>,<lang2>...) debconf translations
      
      
       * <lang1>: <translator for lang1>
       * <lang2>: <translator for lang1>
      
  • updates in debian/patches

    • detect when a new patch is introduced

      new debian patch <patchname>
      
      
      <patch header (anything before the diff)>
      
    • detect when an existing patch is modified

      update debian patch <patchname>
      
      
      <patch header>
      
    • detect when several patches are imported simultaneously

      import <n> files in debian/patches
      
      
      <patch1>:
        <patch header>
      <patch2>:
        <patch header>
      <non-patch-file (series/etc)>:
        no patch description/comments found
      ...
      
    • detect when several patches are created/modified simultaneously

      create/update <n> files in debian/patches
      ...
      

as always, comments/feedback/suggestions/etc welcome :)

using this new hook

note this is the same repo as the previous hooks i've blogged about, so if you already have that set up you can skip the clone and instead just pull in the changes. also note that this is in your local repo, not the remote one.

to set it up:

REPO_PATH=/path/to/your/repo.git
HOOK_REPO_PATH=/somewhere/you/want/to/put/it
git clone git://git.debian.org/users/seanius/vcs-hooks/git-hooks.git $HOOK_REPO_PATH
ln -sf $HOOK_REPO_PATH/debian/git-hooks/prepare-commit-msg-guess-message.py $REPO_PATH/.git/hooks/prepare-commit-msg

there aren't currently any configurable options in this hook.

Posted Fri May 15 15:58:26 2009 Tags: git

previously i threw together a small hook to make it a bit easier to avoid duplicate work while maintaining packages, as well as easily keep the BTS up to date with relevant information.

now i've commonized the code just a bit and have a second hook which can be used to maintain certian (what i believe to be) good practices for keeping packages in git. admittedly, it's a bit more "stick" than "carrot" with respect to streamlining workflows, but i feel the justifications and the resulting benefits are worth it.

so the hook does basically two things, either of which can be customized and/or disabled.

prevent "non-debian" changes on a "debian" branch

assuming that there are seperate branches for "debian" packaging and for "upstream" development, this hook prevents upstream-style changes on the debian branches. that is to say changes to files outside ./debian are not permitted on a "debian" branch, unless of course you're merging from an upstream branch.

instead, changes to the source in a debian branch should be managed by quilt-style "feature patches", or a more advanced branch topology using some kind of feature branches (topgit or similar).

prevent changelog modifications from being mixed in with other commits

this one might be a bit more controversial for some. basically, the idea is that:

  • stuff is fixed in a commit
  • there is a log message for this commit, which contains a description of the fix
  • the changelog update is redundant information to this log
  • it's impossible to merge/cherry-pick/revert the fix later if the changelog gets tangled into the commit.
  • there are nice tools (git dch) for managing debian/changelog updates anyway.

therefore, this hook "declines" commits which modify debian/changelog, unless it is the only file being changed.

using this new hook

note this is the same repo as the other hook, so if you already have that set up you can skip the clone and instead just pull in the changes.

to set it up:

REPO_PATH=/path/to/your/repo.git
HOOK_REPO_PATH=/somewhere/you/want/to/put/it
git clone git://git.debian.org/users/seanius/vcs-hooks/git-hooks.git $HOOK_REPO_PATH
ln -sf $HOOK_REPO_PATH/debian/git-hooks/pre-receive-fileset-fascism.py $REPO_PATH/hooks/pre-receive

the config options for controlling this hook (these are git config options just like the other hook):

# hooks.debianbranches (default: 'debian-.*')
#    a regular expression which indicates which branches are "debian" branches.
#    in the context of this hook such branches are not allowed to have changes
#    in files outside of the ./debian directory.
# hooks.sacredchangelog (default: True)
#    if set to True, debian/changelog can not be changed in a commit that
#    also modifies other files.  this helps ensure changes that are easily
#    merged/cherry-picked/reverted.

examples of the hook in action

rejecting a commit that has an entangled changelog:

rangda[~/debian/php] git push                                  :)
Counting objects: 9, done.
Delta compression using 2 threads.
Compressing objects: 100% (5/5), done.
Writing objects: 100% (5/5), 442 bytes, done.
Total 5 (delta 4), reused 0 (delta 0)
fileset-fascism rejecting commit 2fab3269b3d9daedd7013a576483a11ccb4cb86a
    debian/changelog must be changed seperately from other files.
    changed files in this commit:
        debian/changelog
        debian/control
error: hooks/pre-receive exited with error code 1
To ssh://git.debian.org/git/pkg-php/php.git
 ! [remote rejected] debian-sid -> debian-sid (pre-receive hook declined)
error: failed to push some refs to 'ssh://git.debian.org/git/pkg-php/php.git'

rejecting a commit that has non ./debian changes:

rangda[~/debian/php] git push                                  :)
Counting objects: 5, done.
Delta compression using 2 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 309 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
fileset-fascism rejecting commit d96669c0780e33b250af81404e263fc181fe0547
    non ./debian changes on a debian branch in this commit.
error: hooks/pre-receive exited with error code 1
To ssh://git.debian.org/git/pkg-php/php.git
 ! [remote rejected] debian-sid -> debian-sid (pre-receive hook declined)
error: failed to push some refs to 'ssh://git.debian.org/git/pkg-php/php.git'

as always, comments/feedback/suggestions/etc welcome :)

Posted Sun Apr 26 08:31:53 2009 Tags: git

(note that i've just tried enabling the comments feature of ikiwiki, so feel free to use it :) )

(update: okay so i apparently failed to enable comments earlier... but it should be working now :) )

compiz(-fusion) 0.8.2 uploaded to unstable

for all you lovers of desktop bling, the latest stable release of compiz-fusion has been uploaded to unstable. new features of note:

  • new kde4 based decorator and kconfig4 module for the kde4.2 desktop
  • better stability for suspend/resume (that might be a driver improvement though)
  • gnomecompat support for importing keyboard shortcuts
  • fix for xdamage/refresh problems on some nvidia cards
  • zomg more bling plugins

new one-off script for building compiz packages

the biggest problem with trying to maintain compiz packages is that they're for some reason distributed as almost a dozen different source projects upstream, but extremely interdependant wrt their API. therefore updating the packages to a new upstream version is a Big Pain. i've numbed that pain a bit with a new helper script, which should amongst other things make it easier to manage backports to lenny, which i've already seen people requesting.

compiz 0.8.2 lenny packages available for testing

..which brings us to our next topic. before i upload them to somewhere a little more official, i figure i could put a call out to teh lazywebs to do some QA work and make sure that it doesn't totally explode on your system (unless you're using the explode animation plugin, in which case it's functioning properly)

sources.list entry to try it out on an amd64 lenny system:

deb http://people.debian.org/~seanius/compiz/lenny-backports/amd64 ./

sources.list entry to try it out on an i386 lenny system:

deb http://people.debian.org/~seanius/compiz/lenny-backports/i386 ./

and then apt-get install compiz compiz-fusion-plugins-main (etc)

so give it a try and let me know how it goes!

pkg-php moving to git

with some initial legwork from Mark Hershberger we've migrated to a git repository for the php packaging vcs. due to the long and convoluted history of our svn repository, we decided it wasn't worth the effort of trying to get every single svn commit mapped into the new repo. as a compromise to having to start from scratch, we used the existing branches/tags on top of a series of upstream tarball imports to get an accurate representation of the release history.

new git<->bts integration hook for debian packaging

i've been dabbling with a new hook to make for more useful integration from a git-buildpackage oriented workflow. in this workflow the changelog is often the last thing prepared, possibly days or even weeks after a fix has been committed for a bug. therefore the standard tagpending and typical changelog-scanning hooks aren't incredibly useful.

instead, this hook scans for "Closes:" meta-info in the commits, similar to how "git dch" does, and then sends a notification and/or control commands to the bts. here's an example. note that it also mentions the branch that recieved the fix in the notification, so it's easier to see which branches have a fix at a quick glance.

how to use this for your own git repo:

REPO_PATH=/path/to/your/repo.git
HOOK_REPO_PATH=/somewhere/you/want/to/put/it
git clone git://git.debian.org/users/seanius/vcs-hooks/git-hooks.git $HOOK_REPO_PATH
ln -sf $HOOK_REPO_PATH/git-post-receive-url-notifications.py $REPO_PATH/hooks/post-receive

there's a number of configurable options (take a look at the top of the file for some fairly verbose comments), but it should work with some fairly reasonable defaults out of the box.

it can also be configured to do the more traditional changelog-scanning, but i'm finding it to be a better workflow to avoid combining changelog entries with the actual fixes (makes them harder to cherry-pick later), and a notification with a link to the changelog entry really isn't that useful beyond what "bts tag nnn pending" can already do. plus i don't like the typical duplication involved in manually managing the changelog. i find it better to generate the changelog via git-dch and automatically get all the "closes:" tags, and then just do a bit of editorial touchups before preparing/releasing the upload.

so give it a try if you like, feel free to send feedback/fixes/etc. for example i think the utf-8 support might still be a bit dodgy, as well as possibly the automatic gitweb url detection and changelog scanning.

Posted Mon Apr 13 03:57:40 2009 Tags: git