[PATCH] Use git pull with rebase in build script

Linus Torvalds torvalds at linux-foundation.org
Sun Jul 12 12:20:57 PDT 2015


On Sun, Jul 12, 2015 at 11:07 AM, Dirk Hohndel <dirk at hohndel.org> wrote:
>
> It's just that I don't know how else to do it and keep the branches
> reasonably useful.

So I guess the current model *works*, it just causes nasty problems
occasionally.

The best model is likely one where you have two separate branches:

 - you have the "ugly working branch" that is never ever rebased, and
that people who want to track libdivecomputer as a _user_ would be (so
this is what the build.sh script would pull).

   Because it's never rebased, "git pull" works fine, with or without
rebase (and with "--rebase" might be better, because *if* somebody has
their own work going on, they most definitely wouldn't want to have
that work ever get mixed into that branch.

 - you have the "cleanup" branch, which ends up occasionally rebasing
on top of upstream libdivecomputer, and has a clean history that you
can hope that Jef will eventually take more and more of.

  This "cleanup" branch you don't necessarily work on very actively,
it's more of a "update when libdivecomputer has done something we
really want or care about".

Now, that all sounds easy ("just have two branches"), but the magic is
that you want to keep them in sync. And *that* is fairly simple, but
it's simple only if you follow a few basic rules.

 (a) they start out with the exact same contents (you're a
mathematician, this is going to be an inductive proof, and this is the
starting point - and the trivial case is by having the exact same tree
with the same history).

 (b) You need to remember this place in the ugly working tree.  The
*contents* are the same, but the history may not be, remember, so you
might tag that location with "sync-point" or something like that.

 (c) when *you* do libdivecomputer development, you probably want to
work in the ugly working tree, and you just keep adding commits to it.
Ignore the cleanup tree, it's not what people really *use*, it's more
of a nice marker of what used to be clean development ready for
merging back upstream. So work work work in this tree normally, and
the two trees diverge both in history and in content. But the clean
tree stays the same.

 (d) you decide that you want to synchronize the two trees after
having done *your* work, you can now do a complex rebase

        git rebase -i --onto cleanup sync-point

      which should rebase all your work you did in (c) since your last
sync-point onto the cleanup branch.

     NOTE! It's important that "cleanup" and "sync-point" have the
exact same tree: that guarantees that the rebase will be trivial.
You're applying the same patches onto the same tree state - it's just
a different history. You're now moving your ugly tree work to be on
top of the cleaned-up history.

 (e) you may choose to clean up history further now (it maybe you
weren't so careful when you were working in your ugly tree, but now
you want things to be tip-top, so you do some cleanup and further
testing in this state, wanting to make it be something that you could
send upstream to Jef.

 (f) in fact, it's now so nice and clean that you update the "cleanup"
branch with the new state:

        git branch -f cleanup HEAD

     because you now have a new cleanup state.

 (g) but now you've rebased your ugly tree, and it might not even
match the old state of your working tree because of the cleanups you
did in (e), so we've really screwed up! The whole point was to make
the ugly tree always be a fast-forward! And the whole point was to
keep the two in sync (see step (a))! What kind of idiotic working
model is this!

 (h) to the rescue. At this point, you just do "git pull --strategy
ours <publicrepo>" to say "pull in all the stuff I've pushed out, but
use the merge strategy of picking the exact current state".

     This is what makes your tree the "ugly tree": you just created a
nice clean branch (to use as your cleaned-up version), and then you
use the state of that branch (to make sure it matches the cleanup -
see point (a)), but you merged in all the ugly history, including all
the old ugly merges, so that your branch is always a fast-forward.

 (i) Now remember this state as the new "sync-point":

        git tag -f sync-point

     (or I guess you could keep "sync-point" as just a branch instead
of a tag, that way you'll have the branch history in the reflog)

And now we're back to (a).

What I didn't describe above is what you should do when you actually
want to update from Jef's upstream. When you do that, make sure that
you synchronize like the above first, so that your clean branch and
your ugly branch have the same contents. Then you just check out your
clean branch, and rebase it on top of its upstream (ie Jef's branch).
The clean branch you rebase, after all, and it has simple clean
history so rebasing it on top of Jef's should be fairly simple too,
and keeps the history clean:

        git checkout cleanup
        gti pull --rebase

And after you've rebased the clean branch, you go back to your ugly
branch, and since the clean and ugly started out the same, you just
want to take the new clean base:

        git checkout ugly
        git merge --strategy theirs cleanup

which merges the new cleanup state into your ugly branch and uses the
tree from that clear version. So now the two trees have the same
contents, and your ugly branch is a fast-forward and has all the
changes from Jef's tree. And since the two trees now match again, you
do

        git tag -f sync-point

again to show that you've synced.

The above sounds really complicated, but it's not really. I just wrote
it out in really small steps to make everything clear. And notice that
(c) is the only *normal* thing. The whole "keep in sync" thing is
something you migth do every month or two. You want to minimize the
crazy merges, just to make sure your ugly branch isn't *too* damn
ugly. So all the sync-point stuff is stuff that you do very seldom,
and notice how all the steps are designed to never have any conflicts
or anything like that in them, because the base trees were all the
same (ie the only place you can get conflicts is when you do the
rebase to get Jef's code - that's inevitable - or when you decide to
do the cleanup in (e), but that's all your own commits that you're
just cleaning up, so that's purely a matter of how much cleanup you
want to do and how painful you want to make it).

                    Linus


More information about the subsurface mailing list