trinity-devel@lists.pearsoncomputing.net

Message: previous - next
Month: May 2012

Re: [trinity-devel] Preparing updates for 3.5.13

From: Nix <nix@...>
Date: Wed, 02 May 2012 13:46:25 +0100
On 2 May 2012, Slávek Banko told this:
> With Git I still do not know how much. But I was in CVS can be tagged branch, 
> in which the patches can grow independently of the main branch. I suppose it 
> is possible also in the GIT. And so - to separate branch - I thought the 
> inclusion of patches for 3.5.13.1 (or how to be named).

All branches in git have this property: in fact every single commit has
this property, since all a branch is is a name (git parlance: a
reference, or 'ref') pointing to a commit which can move forward to
point at it's child if you hang a new commit off it. (CVS's branches
are, well, I'm not sure there *is* a conceptual model for them other
than 'we tried to make per-file RCS branching into something
per-repository and it nearly worked', which isn't much of a model.)

Even better for patch tracking is 'git rebase', because it solves a
perennial problem trying to use branches to track changes that apply to
one release when the next one comes out. Say you have a tree with a
release, and you have a branch with patches based on that that you
applied:

- A - release 1.0 - B - C - D       (trunk)
       \
        -- patch -- patch -- patch  (patch-branch)

Now a release 2.0 comes out... but you don't want to throw all those
patches away, or run around reapplying them and damaging the history.
So your tree currently looks like this:

- A - release 1.0 - B - C - D - release 2.0 (trunk)
       \
        -- patch -- patch -- patch          (patch-branch)

In this situation,

git rebase trunk patch-branch

will rewrite the patch-branch so it depends on the head of 'trunk' (if
you want to rebase on some earlier commit in 'trunk', you can make a
temporary branch at the appropriate point in 'trunk' and rebase off
that).

This converts the history to look like this:

- A - release 1.0 - B - C - D - release 2.0 (trunk)
                                   \
                                    -- patch -- patch -- patch (patch-branch)

If git finds conflicts while doing this -- patches that applied to
release 1.0 that no longer applied to release 2.0 for some reason other
than their being applied to upstream -- it'll stop you halfway, with all
patches up to the conflicting patch applied and the conflicting one in a
merge-conflict state, and let you fix the problem, resolve the merge
conflict as usual (hack the files shown as conflicting by 'git status',
'git add' and 'git commit'), then 'git rebase --continue' or 'git rebase
--abort' if you want to give up).

But note that while this is often thought of as changing the history of
the patch-branch, it really isn't. Commits in git are pretty much
immutable[1], so what it actually does is creates a *new* patch-branch
with rewritten commits on it derived from the commits on the old branch,
and gives it the same name as the old branch had. The old branch no
longer has a name, but is still visible in e.g. 'git log -g' until
garbage-collected in a few weeks time. (The existence of commands like
this that can generate whole branches'-worth of garbage at once is one
reason why garbage collection is so important in git's design, though it
happens so automatically these days that you can mostly ignore it.)

Big caveat here, though: if anyone else was doing work on the tip of the
patch-branch and you push your rewritten branch, all their work is gone
unless they take special and annoying measures to preserve it -- and
indeed git warns you of this by forcing you to push with 'git push -f'
to emphasise to you that you might be about to spoil someone's day. So
in general either one never rebases public branches, or one advertises
that 'this branch is regularly rebased' and people take care not to base
work on it directly. (Git's own 'pu' branch, and the linux-next tree,
are examples of the latter).

One thing you *can* do if you want to rebase a branch full of changes
against release 1.0 to changes against release 2.0 without messing up
other people is

git branch patch-branch-2 patch-branch
git rebase trunk patch-branch-2

and now the branch that got rebased (the patch branch for release 2.0)
has a new name, and the old branch still exists, the same as ever; so
the newly-named patch-branch-2 can be pushed without any worry about
messing people up who happen to be playing with the patches against
release 1.0 on the original patch-branch.


I also commend to you 'git rebase -i', which is a simply stunning way of
reshuffling, merging, fixing up and magically manipulating commits
before pushing them. Again, to play with this stuff on a branch, make a
new branch out of it and play on that: the old branch will be quite safe
from your history-defiling experiments.

Most of this stuff is thoroughly git-specific magic. I know of no other
version control system that has anything really like 'git rebase':
Mercurial is trying but has a long way to go to catch up. (I'd say that
Mercurial's advantage these days lies in a much nicer little language
for selecting changes to do things to, beside which git's looks rather
like white noise.)


[1] 'pretty much' because if someone breaks SHA-1's security badly
    enough, they'll not be immutable anymore

-- 
NULL && (void)