mackyle/topgit

two questions on topgit workflow and [BASE] branches

ede-extern opened this issue · 6 comments

We are following an upstream repo (that we don't maintain, of course) with our clone and would manage our patches with topgit. For a complete minimal example see the bash commands below.

Our first, general question: is this the "correct" workflow or are we missing something? The topgit manual is very detailed regarding the individual commands, but we couldn't find something like a "big picture" or "recipes" for typical usage.

The second question is: what is the whole purpose of [BASE] branches (as recommended in #7 )? It is fine for creating topic branches and the release branch on a plain code base. But as you can see in the last three lines, after updating the [BASE] branch to a new release, it is gone from topgit. To continue work after this release we would have to create a new [BASE] branch (with a different name, because mybase still exists in plain git). From the example given in the tg create man page we would think that the release branch used there still "lives" after v2.1 and v2.2.

bash commands for our test:

# create upstream repo with a version U1.0
git init upstream
cd upstream/
echo "README for upstream" > README
echo "/* tiny c file */" > tiny.c
echo "/* tiny h file */" > tiny.h
git add .
git commit -m "initial checkin of upstream"
git tag "U1.0"
cd ..

# clone upstream, init tg and create two topic branches
git clone upstream tgtest
cd tgtest/
git checkout -b mymaster U1.0
tg create --base --topmsg "create my tg base" mybase
tg tag -m "tg-base-tag-as-recommended" tg-base-tag-as-recommended --all
#tg checkout mybase
tg create --topmsg "create t/feat1" t/feat1
echo "/* reformat to my conventions */" >> tiny.c
git commit -am "reformat to my conventions"
tg checkout mybase
tg create --topmsg "create t/feat2" t/feat2
echo "/* reformat to my conventions */" >> tiny.h
git commit -am "reformat to my conventions"

# create a release S1.0 including the two features
tg checkout mybase
tg create --topmsg "create release" release t/feat1 t/feat2
tg tag -m "create tag S1.0" S1.0

# update the [BASE] branch and it is gone
tg summary
tg update --base -m "try to tg update --base" mybase S1.0
tg summary

Using the tg summary --rdeps command you can see
a dependency graph of your TopGit branches:

release
  t/feat1
    mybase
  t/feat2
    mybase

Make sense? t/feat1 is built on top of mybase (similarly
for t/feat2) and then release is built from both of the
features.

Initially mybase is pointing at the same commit as U1.0.

You built the two feature branches (t/feat1 and t/feat2)
on top of it where each of those branches represents a single
"logical patch" to TopGit.

TopGit considers the patch to be the difference between the
TopGit branch's base and the tip of that TopGit branch.

The tg patch t/feat1 and tg patch t/feat2 commands will
show you what TopGit thinks the "logical patch" is for each
of those two branches.

The tg base t/feat1 and tg base t/feat2 commands will
show you what commit TopGit is using for each of those
branches when computing its "logical patch".

To more easily see the entire Git commit graph you might
try:

git log --oneline --graph --decorate --all

Then there's this sequence:

tg tag -m "create tag S1.0" S1.0 release
tg update --base -m "try to tg update --base" mybase S1.0

The tg update --base told TopGit to move the mybase branch from
pointing at the same commit as U1.0 to now pointing at the same
commit as S1.0 and then merge all those changes up through the
TopGit dependency graph.

Look again at the --rdeps output above. The values output by tg base t/feat1 and tg base t/feat2 will be changed after the tg update --base and as a result the base of each of the TopGit
branches has the changes from the freshly updated mybase branch
merged into it so each of the feature branches no longer introduces
any new changes of their own compared to their base which is why
everything seems to go away.

Look at this simplified example:

A -- B -- C -- D

Where initially mybase points at A and t/feat1 points at D.

The patch is just the diff from A to D. Now D is tagged as
the release and mybase is updated to point to it (which is
essentially what those last two commands above did). The patch is
now the diff from D to D which is nothing.

The intent of the tg update --base functionality is that once
upstream releases a U2.0 for example, you come along and run this:

tg update --base -m "move patches to U2.0" mybase U2.0

And now your release staging branch has all the patches built
up on top of U2.0 instead of U1.0.

If you want to tag the staging branch before you run the base update
you can do that using a normal git tag command. You only need
to use tg tag command if you might want to use the TopGit -w
option or the TopGit tg revert command at some point in the future.

thanks for your exhaustive answer. With your explanations i found the problem: i was using a tg tag in the update command. Initially my last three lines give:

$ tg summary
 0       mybase                        	[BASE] create my tg base
>0       release                       	[PATCH] create release
         t/feat1                       	[PATCH] create t/feat1
         t/feat2                       	[PATCH] create t/feat2
$ tg update --base -m "try to tg update --base" mybase S1.0
Merge made by the 'recursive' strategy.
$ tg summary
>0       release                       	[PATCH] create release
         t/feat1                       	[PATCH] create t/feat1
         t/feat2                       	[PATCH] create t/feat2

That is what i meant with "the [BASE] branch is gone". The important thing to know is, that the <ref> argument must be an upstream (i.e. non-tg) tag:

$ tg summary
 0       mybase                        	[BASE] create my tg base
>0       release                       	[PATCH] create release
         t/feat1                       	[PATCH] create t/feat1
         t/feat2                       	[PATCH] create t/feat2
$ tg update --base -m "try to tg update --base" mybase U1.0   # not useful here, but shows the point
No change
$ tg summary
 0       mybase                        	[BASE] create my tg base
>0       release                       	[PATCH] create release
         t/feat1                       	[PATCH] create t/feat1
         t/feat2                       	[PATCH] create t/feat2

When using tags not maintained by tg, the tg update --base works like a charm. This issue can be closed then.

I think there might be a bug here...

I'd like to play with it a little bit more but I want
to make sure I'm using the same versions you are. Could
you please report the output of:

tg version

and

git version

Thanks.

Also the mybase branch isn't really gone. You should be
able to see it with this:

tg summary -vvl

(That's short for --verbose --verbose --list) Or with:

tg info mybase

I suspect it ends up having a tip and base with the same
tree after the merge which is why it doesn't normally show up.

No problem. The machine is a VM with Ubuntu 16.04.5 LTS. The commands give:

$ tg version
TopGit version 0.19.11-5-g4656
$ git version
git version 2.7.4
$ tg summary -vvl
mybase                                 	branch mybase (annihilated)
release                                	[PATCH] create release
t/feat1                                	[PATCH] create t/feat1
t/feat2                                	[PATCH] create t/feat2
$ tg info mybase
Topic Branch: mybase (1/0 commit)
Subject: branch mybase (annihilated)
Base: 4c2a4fb
Up-to-date.

thanks. sure looks like a bug to me. using it the way you did was not envisioned, but still it's not supposed to do what it did (zap out the .topmsg and .topdeps files). The code that prevents that exact thing from happening needs to be factored out for use in other places as well, so when that happens this should get corrected. In the meanwhile I'll be adding an expect-fail test so this doesn't get lost.

The necessary code refactoring finally has been completed and with the release of topgit-0.19.13 this bug has been squashed. There's even a new test in t5002 to make sure this continues to work properly.

What will happen if you do this now (including the tg update) and run a plain tg summary afterwards, all your branches will be there and they will all show a "0" as they will all then have no changes, but none of them will disappear.