Git's petty little irritation for me

July 15, 2013

Perhaps the most common thing for me to do with people's source code is to add my own purely local changes. When the source code is in a source repo, the simplest way of doing this is to just pull a copy of the source repo and then make my own changes on top. Then sooner or later I want to update my local repo by pulling in the latest central changes.

With CVS, SVN, and even Mercurial this more or less just works, however theoretically unclean and evil it is; all three detect that I have uncommitted local changes and attempt to re-apply them on top of the updates. Usually this succeeds and if it doesn't, the convention is to stick glaring markers in the files and leave it to me to fix them up. Git insists on doing the right thing in that at least by default it utterly refuses to try to merge my uncommitted changes with the remote updates it's just fetched.

It turns out that this unusual strictness on git's part is irritatingly inconvenient for me. I definitely don't want to actually commit my changes because that would contaminate what is otherwise a pure upstream repo history with a tangle of back and forth merges (even if I did it on a branch). There is 'git stash', but it has two aspects that I don't like; I have to remember (or be reminded) to do it explicitly and it makes changes to the repo itself (not just the checked-out files). I really like my repos to be exact, unchanged duplicates of the upstream.

(Yes, the changes 'git stash' makes should be harmless and should get fixed up when I do 'git stash clear' or the like. That's two 'shoulds'.)

This is a completely petty irritation (especially given 'git stash') and I fully acknowledge that. But I've never claimed to be entirely rational about this stuff and yes, it irritates me.

Sidebar: When this doesn't work in Mercurial

There is one circumstance when Mercurial will not do this sort of update at all, namely when the main branch in the repo shifts. The Mercurial repo for official Firefox releases hops branches this way when a new major release comes out and as a result I get to save a patch of my changes, overwrite them all by forcing a clean checkout, and then reapply them. Fortunately this is rare.

(Also these days I've found up switching to building my local Firefox from the main development repository, which doesn't branch this way.)


Comments on this page:

From 99.184.254.149 at 2013-07-15 01:33:45:

What about 'git pull --rebase'?

-Brad http://augmentedfourth.com

From 87.79.78.105 at 2013-07-15 06:47:56:

Yes, git pull --rebase is what you’re after. And you can set branch.master.rebase to true to have this automatically happen even on a bare git pull. Furthermore you can set branch.autosetuprebase to true to have the branch.<name>.rebase flag set by Git on any newly created tracking branches. Which is what I do.

The upshot is that you get a replica of the svn update behaviour, except that it runs the merge logic on recorded commits instead of inextricably modifying your working copy. So unlike with SVN/CVS, you can always safely get back to your previous state.

Now that does mean you have to commit your changes, and therefore your checked-out branch no longer exactly reflects its upstream. But I’d think that’s what the origin/* branches are for… not your local branches. No?

Aristotle Pagaltzis

By cks at 2013-07-15 15:06:45:

My problem with doing commits and rebases and so on is that this is going to clutter up 'git log' output and so on (unless I do even more work to purge old versions of the rebased code). I rely on 'git log' to show me upstream changes, although I suppose there's a magic Git way of showing only the remote branch.

All of this is getting complicated, which is where the irritation comes in for me.

From 97.113.10.211 at 2013-07-15 18:40:41:

It may be a petty irritation, but it's one I feel as well. It's pretty much the last thing that I actively miss from Darcs: since it's patch-oriented, has first-class token change patches, and can happily commute patches to reduce conflicts you can pretty easily just keep a private set. If they only conflict because subsequent changes renamed or moved the code the patch was recorded against Darcs can usually bubble it up the history to before the rename and avoid conflicts. It's a little automagical for most people's taste and git has so many advantages that it's not really worth going back, but keeping private local changes with near zero effort was really pleasant.

- Kate

From 97.113.10.211 at 2013-07-15 18:48:53:

I forgot the flip side of how Darcs manages local changes - you can pretty easily push a subset of your changes and keep others private, something git really wants you to do with the thicket of irrelevant merge & rebase commits you mentioned. There's probably extra room to shoot yourself in the foot if you're not testing carefully against the upstream version, but it's a nice win for keeping a few local changes (file paths, say) without needing to worry about them when you feed things back upstream.

Perhaps a little oddly this is also a win for prose if you're doing partial rewrites or discarding some of your changes. In my experience Git really, really doesn't handle most long-form prose writing styles very well.

- Kate

From 87.79.78.105 at 2013-07-16 00:26:03:

My problem with doing commits and rebases and so on is that this is going to clutter up 'git log' output and so on (unless I do even more work to purge old versions of the rebased code).

I really don’t understand where this clutter would come from? In fact you seem to be using “rebase” to refer to something other than what the word means to me. Can you illustrate the problem that git pull --rebase would cause in your scenario?

Aristotle Pagaltzis

From 87.79.78.105 at 2013-07-16 00:49:44:

Or maybe I should illustrate what would happen if you were using git pull --rebase the way I think you are looking for.

Namely, suppose you commit all your local changes – git add -A, then git commit -m 'local changes'. You’re looking at this:

               origin/master
--A---B---C---D.
                \  master
                 `L

Now there are upstream changes, and you do a git fetch, resulting in this:

                           origin/master
                 .E---F---G
                / 
--A---B---C---D<
                \  master
                 `L

Now while on master you do git rebase origin/master. You get this:

                           origin/master
--A---B---C---D---E---F---G.
                            \  master
                             `L*

What git pull --rebase does for you is combine this git fetch and git rebase for you, the way git pull normally combines git fetch and git merge.

As for any other changes you make, you can just amend that “local changes” commit, so you’ll only ever have the one.

Now can you point out to me where the clutter you want to avoid would be?

(You can do git log origin/master to browse the upstream history, which is the, uh :-), super magical way you alluded to.)

Aristotle Pagaltzis

From 91.198.246.131 at 2013-07-16 10:26:20:

@cks:

My problem with doing commits and rebases and so on is that this is going to clutter up 'git log' output and so on (unless I do even more work to purge old versions of the rebased code). I rely on 'git log' to show me upstream changes, although I suppose there's a magic Git way of showing only the remote branch.

Well, it's not magic. While you remember there are two branches, local and remote, you just ask for history for the remote branch: git log origin/master. I know it's one more parameter to pass, but in exchange you get all the version control goodies: history of (your) changes.

-- dozzie

Written on 15 July 2013.
« Why single vendor solutions are a hard sell
Systemd needs sensible, non-truncated output »

Page tools: View Source, View Normal, Add Comment.
Search:
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Mon Jul 15 00:43:30 2013
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.