Project lifecycle support: how to manage maintenance branches

Summary

The project lifecycle commands allow for creating a set of maintenance branches on every checkout in a build tree.

Let us assume that we have a build tree with build name “Widget”, and that release version 1.0 of the Widget product has just been delivered. We want to continue working towards version 2 of the product in the “normal” build tree, but we may also need to make maintenance releases based on version 1.0 of the code.

Muddle allows us to branch all of the checkouts in the build tree, so that we can do the v1.0 maintenance work on its own branch. Thus:

$ muddle branch-tree Widget-v1.0-maintenance

will perform various checks, and then create a new branch called Widget-v1.0-maintenance in each checkout.

Note

It is worth naming maintenance branches carefully so that they are unlikely to clash with any existing branchs in any of the checkouts. Using the build name, the version number, and some additional descriptive text (here, just “maintenance”) seems sensible.

Note

muddle branch-tree -check will just do the checks, and muddle branch-tree -force will just do the branching, without any checks. See Coping with problems in muddle branch-tree below

We then need to tell the (branched) build description that all its checkouts should “follow” its branch (we’ll see why further below). So we add the line:

$ builder.follow_build_desc_branch = True

to the build description (normally in src/builds/01.py).

We now need to commit and push all of these changes. Unfortunately, we don’t yet have a way of doing a “muddle commit” that specifies a commit message, so the best we can do (assuming everything is using git) is:

$ muddle runin _all_checkouts 'git commit -a -m "Create maintenance branch"'

and then:

$ muddle push _all

Since the checkouts are on the branch requested by the build description (because we set the “follow” flag), this will push the new branch to the remote repositories, even though it didn’t exist before.

At which stage, our remotes all have the branch, and our checkouts are still on it. That’s probably a good time to record a stamp file:

$ muddle stamp version
$ muddle stamp push

Branching the build tree doesn’t branch the “versions/” directory, but because we have set “follow” in the build description, the stamp file created will be called:

versions/<build_name>.<branch_name>.stamp

instead of the normal versions/<build_name>.stamp - so in this case:

versions/ExampleBuild.Widget-v1.0-maintenance.stamp

We can now change to a new, empty directory, and make a new copy of our maintenance build tree:

$ cd ..
$ mkdir Widget-v1.0-maintenance
$ cd Widget-v1.0-maintenance

$ muddle init -branch Widget-v1.0-maintenance <repo> builds/01.py

(where <repo> is the remote repository).

This gets the build description and automatically checks out the requested branch. If we look in the .muddle/ directory, we can see that there is a new file:

.muddled/DescriptionBranch

which contains the name of the branch used at “muddle init” (it will only be present if “-branch” was used, and it is never altered by muddle itself).

We can now do:

$ muddle checkout _all

and, because the build description has “follow_build_desc_branch” set, all the checkouts will also be checked out with that branch.

Note

If we had not set builder.follow_build_desc_branch = True, then the other checkouts would just have their “normal” branch, as defined by the build description. It is perfectly legitimate to use muddle init -branch to retrieve a branch of the build description for other purposes, not to do with whole build tree branching.

You can use:

$ muddle query checkout-branches

to see the details of the branches being used, and why. For instance:

$ m3 query checkout-branches
--------  -----------------------  -----------------------  -----------------------
Checkout  Current branch           Original branch          Branch to follow
--------  -----------------------  -----------------------  -----------------------
builds    Widget-v0.1-maintenance  Widget-v0.1-maintenance  <it's own>
co1       Widget-v0.1-maintenance  master                   Widget-v0.1-maintenance

“Original branch” is the branch as requested by the build description. Normally that will be “master” - i.e., the default branch - but sometimes a build description specifies a particular branch for a checkout.

If a build description does specify a particular branch (or revision) for a checkout, or if a checkout is not using git, then muddle branch-tree will not be able to branch it for you, and you will have to decide what to do about that checkout in this context, a maintenance tree.

A more detailed look

Why do we need this?

Let us assume that we have been developing a build tree, and we reach the point of releasing version 1 of the product that it builds. We can do the normal steps of generating stamp files (muddle stamp), perhaps producing source releases (muddle distribute), and quite likely producing an actual release tarball (muddle release), but then we want to continue development towards the next version of the product.

The problem is that we almost certainly also want to be able to go back and produce later “bugfix” releases based on version 1, and ideally we would like muddle to make this easy for us.

One rather cumbersome way to do this would be make a branch of our build description describing, explicitly, what is in release 1. So we could look at the stamp file we produced for our release (of course we made a stamp file), and for each checkout, explicitly specify its revision in the build description. If we committed that (on its branch), then we could later build our tree again, making sure we’re using the branch of the build description. If we needed to fix a bug in a particular package, we could then branch that package, determine the new revision, update the build description, and so on.

This would be extremely tedious, and definitely error prone.

A slightly (but only slightly) better approach would still specify all of the revisions “by hand”, but when a package was changed for a bugfix, we would perofrm the change on an appropriate branch, and amend the build description for that package to specify this new branch instead of the revision.

Which is really not that much better.

So instead we provide a mechanism to branch all checkouts in a build tree, and to use the same branch (i.e., branch name) for the build description and the checkouts.

Note

This assumes that all the checkouts support this mechanism. At time of writing, muddle supports this for git. If most checkouts use git, and a few use a VCS that is not supported for this purpose, then one of the “solutions” above may be adopted for the less able VCS checkouts.

How we do it

This means we would:

  1. Decide we’re going to make release 1. If our build is called “Widget”, we might thus decide to use a branch called “Widget-v1.0-maintenance”.

    (It seems sensible to put the name of our build tree into the branch name as a way of avoiding clashes with any previous branches in the individual checkouts. It is quite likely that some checkout of source code from elsewhere might have already have a branch called “v1.0”, for instance. Whilst muddle can warn if this is the case, it is ultimately up to the user to choose an appropriate branch name.)

  2. We then use:

    $ muddle branch-tree Widget-v1.0-maintenance
    

    to make various checks, and then branch all the checkouts.

  3. We then edit the build description (which is now on the new branch) and add a line to the describe_to() function:

    builder.follow_build_desc_branch = True
    

    This tells muddle that checkouts should use the same branch as the build description. Of course, if the build description explicitly specifies a revision or branch for a checkout, then that takes precedence, so please remember to check.

    A build description with this value set is described as having set “following”, and the build tree “follows” the build description.

    Note

    Muddle tries to be a little bit helpful by also recognising the (mistaken):

    builder.follows_build_desc_branch = True
    

    and treating that as an error - otherwise the nature of Python would allow this, with no effect.

  4. We then need to commit everything. We really need a:

    $ muddle commit _all -m <message>
    

    but we don’t have one yet, but we can do:

    $ muddle runin _all_checkouts 'git commit -a -m "Create maintenance branch"'
    

    if we are careful with the quoting. Of course, if any of our checkouts are not using git, that’s going to search up through the directories looking for a .git directory, which can sometimes lead to surprising effects, so be a little bit careful.

  5. We can then do:

    $ muddle push _all
    

    to push the build description and all the branched checkouts.

  6. As one might expect, it’s then generally a good idea to take a stamp:

    $ muddle stamp version
    $ muddle stamp push
    

    Since our build description sets “following”, the version stamp file will be called:

    versions/<build-name>.<build-desc-branch>.stamp
    

    The “versions/” directory is not branched by muddle branch-tree, since it is meant to contain all the version stamps for the project. Thus naming the stamp file with the branch name (which should indicate the purpose of the branch) is a sensible solution.

If anyone does a muddle init of the build tree, they will still get the normal (“master”) branch, which we have not altered, and can muddle checkout and continue mainstream development just as normal.

However, if we want to do a bugfix on version 1, we can do:

$ muddle init -b Widget-v1.0-maintenance <repo> src/builds
$ muddle checkout _all

where <repo> is the same repository spec as used in the original muddle init (and remembered in .muddle.RootRepository).

This will checkout the build description on the named branch, and then, because the build description sets “follow”, will checkout the same branch of each checkout.

We can use:

$ muddle query checkout-branches

to show that the correct branches are checked out (see muddle help query checkout-branches for what the columns output by that command mean).

We can also use muddle query build-desc-branch to see if we are “following”:

$ muddle query build-desc-branch
Build description checkout:builds/* is on branch Widget-v1.0-maintenance
  This WILL be used as the default branch for other checkouts

One last useful command, introduced as part of the support for branched builds, is muddle sync. This is used to put the branch of a checkout (or checkouts) back to what the build description says it should be. This can be useful if you’ve been switching branches to look at alternative implementations of a checkout. How it actually decides what to do is slightly complex - see muddle help sync for details.

A worked example

This is basically following an example in the tests/test_lifecycle.py test script.

Imagine that we have done:

$ muddle init git+<repo> builds/01.py

and the build description 01.py looks like:

def describe_to(builder):
    builder.build_name = 'ExampleBuild'
    add_package(builder, 'package1', 'x86', co_name='co1')
    # ... more normal packages ...
    add_package(builder, 'package2', 'x86', co_name='co.branch1',
                branch='branch1')
    add_package(builder, 'package3', 'x86', co_name='co.fred',
                revision='fred')
    add_package(builder, 'package4', 'x86', co_name='co.branch1.fred',
                branch='branch1', revision='fred')
    add_bzr_package(builder, 'package5', 'x86', co_name='co.bzr')

where add_package and add_bzr_package do appropriate things, and the necessary import statements have been omitted.

We have several checkouts:

  • “co1” is a “normal” checkout - we haven’t specified an explicit branch or revision, and are expecting to work with HEAD of master. We would normally expect to have more of these, as indicated by the comment.
  • “co.branch1” specifies an explicit branch.
  • “co.fred” specifies an explicit revision - in this case, a tag name.
  • “co.branch1.fred” specifies a branch and a revision. When cloning it out, muddle will first checkout the branch, and then go to the revision if it is not already there (i.e., if it is not HEAD of the branch).
  • Finally, “co.bzr” is a checkout using Bazaar, taken from some other repository.

We’ll assume that the user has done:

$ muddle checkout _all

and probably built everything, to check that the build tree works.

We can try branching the tree, but this will fail as follows:

$ muddle branch-tree Widget-v1.0-maintenance

Unable to branch-tree to Widget-v1.0-maintenance, because:
  checkout:co.branch1.fred/checked_out explicitly specifies revision "fred" in the build description
  checkout:co.branch1/checked_out explicitly specifies branch "branch1" in the build description
  checkout:co.bzr/checked_out uses bzr, which does not support lightweight branching
  checkout:co.fred/checked_out explicitly specifies revision "fred" in the build description

all of which make sense, if one looks at the build description.

As discussed in Coping with problems in muddle branch-tree (below), our best option in this particular case is to force a branch, aiming to fix things afterwards:

$ muddle branch-tree -force Widget-v1.0-maintenance
> git remote set-branches --add origin Widget-v1.0-maintenance
checkout:co.branch1.fred/checked_out explicitly specifies revision "fred" in the build description
checkout:co.bzr/checked_out uses bzr, which does not support lightweight branching
> git remote set-branches --add origin Widget-v1.0-maintenance
checkout:co.branch1/checked_out explicitly specifies branch "branch1" in the build description
checkout:co.fred/checked_out explicitly specifies revision "fred" in the build description
Successfully created  branch Widget-v1.0-maintenance in 2 out of 6 checkouts
Successfully selected branch Widget-v1.0-maintenance in 2 out of 6 checkouts
Unable to branch the following:
  checkout:co.branch1.fred/checked_out (specific revision fred)
  checkout:co.bzr/checked_out (VCS %s not supported)
  checkout:co.branch1/checked_out (specific branch branch1)
  checkout:co.fred/checked_out (specific revision fred)

If you want the tree branching to be persistent, remember to edit
the branched build description,
  /home/tibs/sw/m3/tests/transient/example/src/builds/01.py
and add:

  builder.follow_build_desc_branch = True

to the describe_to() function, and check it in/push it.

That reiterates the problems we already knew we had, and reminds us to amend the build description.

So, we edit the build description, doing the following:

  1. Add the builder.follow_build_desc_branch = True line to the end.
  2. Amend the Bazaar checkout, “co.bzr”, to set the “no_follow” option (for a real project, we’d also change the repository it refers to, but muddle can’t tell the diference, so we shall ignore that for now).

The build description now looks like:

def describe_to(builder):
    builder.build_name = 'ExampleBuild'
    add_package(builder, 'package1', 'x86', co_name='co1')
    # ... more normal packages ...
    add_package(builder, 'package2', 'x86', co_name='co.branch1',
                branch='branch1')
    add_package(builder, 'package3', 'x86', co_name='co.fred',
                revision='fred')
    add_package(builder, 'package4', 'x86', co_name='co.branch1.fred',
                branch='branch1', revision='fred')
    add_bzr_package(builder, 'package5', 'x86', co_name='co.bzr',
                    no_follow=True)
    builder.follow_build_desc_branch = True

where add_bzr_package is defined as:

def add_bzr_package(builder, pkg_name, role, co_name=None, revision=None, no_follow=False):
    base_repo = builder.build_desc_repo
    if co_name is None:
        co_name = pkg_name
    repo = Repository('bzr', base_repo.base_url, co_name, revision=revision)
    checkout_from_repo(builder, checkout(co_name), repo)
    muddled.pkgs.make.simple(builder, pkg_name, role, co_name)
    if no_follow:
        builder.db.set_checkout_vcs_option(checkout(co_name), 'no_follow', True)

We can check that branches are being handled as expected:

$ muddle query checkout-branches
---------------  -----------------------  ---------------  -----------------------
Checkout         Current branch           Original branch  Branch to follow
---------------  -----------------------  ---------------  -----------------------
builds           Widget-v1.0-maintenance  <none>           <it's own>
co.branch1       branch1                  branch1          <none>
co.branch1.fred  <none>                   branch1          <none>
co.bzr           <not supported>          ...              ...
co.fred          <none>                   <none>           <none>
co1              Widget-v1.0-maintenance  <none>           Widget-v1.0-maintenance

The build description is on the right branch, so is our “normal” checkout, “co1”. We can also see that both of those are now following the build description branch, as we wanted.

As to the other checkouts:

  • “co.branch1” still names a specific branch in the build description, which is thus its “original” and “current” branch. It is not “following” because of that.
  • “co.branch1.fred” still names a specific revision and branch in the build description. The “current” branch being <none> means that it is on a detached HEAD. Again, it is not “following”.
  • “co.fred” still names (just) a specific revision, and thus it too has a “current” branch of <none>, and is not “following”.
  • “co.bzr” is using Bazaar, and muddle does not support branching it, so it says that it does not support it.

We can now commit - I’m only going to commit the two checkouts we’ve actually branched, not least because trying to do git commit in “co.bzr” will not generally end well:

$ muddle runin co1 'git commit -a -m "Create maintenance branch"'
++ pushd to /home/tibs/sw/m3/tests/transient/example/src/co1
# On branch Widget-v1.0-maintenance
nothing to commit (working directory clean)
$ muddle runin builds 'git commit -a -m "Create maintenance branch"'
++ pushd to /home/tibs/sw/m3/tests/transient/example/src/builds
[Widget-v1.0-maintenance 726189c] Create maintenance branch
 1 file changed, 34 insertions(+), 2 deletions(-)

(obviously I could have used muddle commit directly here, which would have opened an editor for me to type the commit comments).

And we can now push:

$ muddle push _all
> Building checkout:builds/changes_pushed[T]
++ pushd to /home/tibs/sw/m3/tests/transient/example/src/builds
> git push origin HEAD
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 794 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
To file:///stuff/tibs/sw/m3/tests/transient/01.init.branch.repo/src/builds
 * [new branch]      HEAD -> Widget-v1.0-maintenance
> Building checkout:co.branch1/changes_pushed[T]
++ pushd to /home/tibs/sw/m3/tests/transient/example/src/co.branch1
> git push origin HEAD
Everything up-to-date
> Building checkout:co.branch1.fred/changes_pushed[T]
Checkout co.branch1.fred in directory src/co.branch1.fred
is on branch None, but should be on branch branch1.
Use "muddle query checkout-branches" for more information.
Refusing to push. Use git directly if that is what you really meant
> Building checkout:co.bzr/changes_pushed[T]
++ pushd to /home/tibs/sw/m3/tests/transient/example/src/co.bzr
> bzr push file:///stuff/tibs/sw/m3/tests/transient/01.init.branch.repo/src/co.bzr
No new revisions or tags to push.
> Building checkout:co.fred/changes_pushed[T]
++ pushd to /home/tibs/sw/m3/tests/transient/example/src/co.fred
Failure pushing checkout:co.fred/changes_pushed[T] in src/co.fred:
This checkout is in "detached HEAD" state, it is not
on any branch, and thus "muddle push" is not alllowed.
If you really want to push, first choose a branch,
e.g., "git checkout -b <new-branch-name>"
> Building checkout:co1/changes_pushed[T]
++ pushd to /home/tibs/sw/m3/tests/transient/example/src/co1
> git push origin HEAD
Total 0 (delta 0), reused 0 (delta 0)
To file:///stuff/tibs/sw/m3/tests/transient/01.init.branch.repo/src/co1
 * [new branch]      HEAD -> Widget-v1.0-maintenance

The following problems occurred:

Checkout co.branch1.fred in directory src/co.branch1.fred
is on branch None, but should be on branch branch1.
Use "muddle query checkout-branches" for more information.
Refusing to push. Use git directly if that is what you really meant

Failure pushing checkout:co.fred/changes_pushed[T] in src/co.fred:
This checkout is in "detached HEAD" state, it is not
on any branch, and thus "muddle push" is not alllowed.
If you really want to push, first choose a branch,
e.g., "git checkout -b <new-branch-name>"

We can see the new branch being pushed to the “builds” and “co1” repositories. As expected, neither “co.branch1.fred” nor “co.fred” could be pushed.

If we make a version stamp:

$ muddle stamp version

we end up with an appropriately named file:

$ ls versions
ExampleBuild.Widget-v1.0-maintenance.stamp

which we should remember to push:

$ muddle stamp push

Since we’ve pushed our changes, we can create a new build using them:

$ cd ..
$ mkdir example2
$ cd example2
$ muddle init -branch Widget-v1.0-maintenance git+<repo> builds/01.py

Now, when we do:

$ muddle checkout _all
> Building checkout:co.branch1/checked_out
...
> Building checkout:co.branch1.fred/checked_out
...
> Building checkout:co.bzr/checked_out
...
> git checkout fred
...
> Building checkout:co1/checked_out
++ pushd to /home/tibs/sw/m3/tests/transient/example2/src
> git clone -b Widget-v1.0-maintenance file:///stuff/tibs/sw/m3/tests/transient/01.init.branch.repo/src/co1 co1
Cloning into 'co1'...
remote: Counting objects: 9, done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 9 (delta 1), reused 0 (delta 0)
Receiving objects: 100% (9/9), done.
Resolving deltas: 100% (1/1), done.
> Make directory /home/tibs/sw/m3/tests/transient/example2/.muddle/tags/checkout/co1

we can see that “co1” is being checked out with the correct branch (I’ve left out the text for the other checkouts, but we’d have seen “co.branch1.fred” and “co.fred” ending up in detached HEAD states).

Thus:

$ muddle query checkout-branches
---------------  -----------------------  -----------------------  -----------------------
Checkout         Current branch           Original branch          Branch to follow
---------------  -----------------------  -----------------------  -----------------------
builds           Widget-v1.0-maintenance  Widget-v1.0-maintenance  <it's own>
co.branch1       branch1                  branch1                  <none>
co.branch1.fred  <none>                   branch1                  <none>
co.bzr           <not supported>          ...                      ...
co.fred          <can't tell>             <none>                   <none>
co1              <can't tell>             <none>                   Widget-v1.0-maintenance

Note that this is nearly identical to the output in the original build tree, except that now the “Original branch” for the build description is the branch we requested on the muddle init command line.

Note

If we hadn’t added the “no_follow” option to the Bazaar checkout, we would have had errors of the form:

The build description wants checkouts to follow branch 'Widget-v1.0-maintnance',
but checkout co.bzr uses VCS Bazaar for which we do not support branching.
The build description should specify a revision for checkout co.bzr,
or specify the 'no_follow' option.

whenever we tried to do any checkout-related operations.

Coping with problems in muddle branch-tree

muddle branch-tree moves all of the checkouts in the current build tree to a specified branch, if possible.

It does this in two phases:

  1. Check that there are no problems.
  2. Perform the branching.

The -c or -check switch makes it just do the first, and the -f or -force switch makes it just do the second.

The problems that might occur are as follows:

  • A checkout uses a VCS that muddle does not know how to branch. Broadly, this means “anything but git”.

    There are two sensible solutions for this. First “force” a branch of the build tree, and then edit the build description in one of the two following ways:

    1. Specify a particular revision for the checkout. This is sensible if the particular checkout is not going to be developed anymore on this new branch.
    2. Specify a new “branch” for the checkout (that is, a new repository location), and mark the checkout as having the “no_follow” option. This last tells muddle that it is OK not to try to make the checkout “follow” the build description. Muddle needs to be told this explicitly because it has no way of telling that this (new) Bazaar repository is not the same as the original.

    Note

    The “no_follow” option is supported for both Bazaar and Subversion.

  • The build description explicitly specifies a particular revision and/or branch for a checkout.

    If a specific revision is named, then this can generally be left as it is. Care must, of course, be taken if it is ever changed, but the specific revision was presumably named because it was not expected to change.

    If a specific revision and branch are named, this is essentially the same case.

    If a specific branch is named, but without a revision, then there are two possible solutions.

    In both cases, first “force” a branch of the build tree, and then edit the build description, to either:

    • add a specific revision (the current revision on the named branch seems most likely), or
    • branch the checkout, by hand, to the same branch that is being “followed” by everything else. Then either amend the build description to name that branch instead of the original branch, or remove the explicit branch entirely. The latter has the disadvantage that it is no longer clear that this checkout was being “special cased” with an explicit branch.
  • A checkout is shallow - that is, it does not contain all of its history.

    The best solution for this is probably to find the current revision id of the shallow checkout using muddle query checkout-id, and amend the build description to specify that exact revision. This can then be changed to later revision ids as necessary.

  • A checkout already has a branch of the name you wanted to use.

    Either that’s a true clash, and you need to come up with a new name for this branch, or it’s because the tree was already branched earlier, and you are using branch-tree as a convenient way to move all the checkouts in a non-branched tree to the (already extant) branch.

Can I use muddle branch-tree to go to an existing branch?

As implied by the last item above (in Coping with problems in muddle branch-tree), this is perfectly possible - using the -force switch will cause it to overcome its worries that the branch already exists.

However, you’re probably better off doing:

$ cd src/builds  # or wherever the top-level build description is
$ git checkout branch.to.follow
$ muddle sync _all

If the build description on branch “branch.to.follow” has the “follows” flag set, then this will produce the same result in a simpler fashion.

Muddle commands in branched build trees

In the following, I’m assuming that the build description sets “following”, and is on a branch, and that the checkouts being discussed are using git (because muddle doesn’t support branching in other version control systems).

In a branched build tree, the build description sets “following”. A checkouts branch is determined as follows:

  1. If a specific revision and a specific branch are specified in the build description, then muddle “goes to” that branch and then, if necessary changes to the specified revision. If the revision is not HEAD of the branch, this will leave the checkout on a detached HEAD (see http://alblue.bandlem.com/2011/08/git-tip-of-week-detached-heads.html for a useful explanation of this).
  2. If a specific revision alone is given in the build description, then that will be used. This will result in a detached HEAD.
  3. If a specific branch alone is given in the build description, then that branch will be used.
  4. Otherwise, the build description branch will be used.

Note

This is identical to how things normally work, except that in a non-“follow” build tree, step 4 chooses “master” instead of the build description branch.

There is one caveat: if the version control system being used for the checkout is not one that muddle knows how to branch (i.e., at the moment, is not git), then revisions are followed, but branches are not.

You can use muddle query checkout-branches or read the build description to see what applies for each checkout.

So, looking at specific commands, and remembering these descriptions are for checkouts using git:

  • muddle checkout will clone the appropriate branch of the checkout, as described above.
  • muddle pull and muddle merge will first go to the appropriate branch, and then do their work.
  • muddle push will push the current branch of the checkout, creating that branch at the far end if necessary. It does not check if this is the “appropriate” branch or not.
  • muddle pull-upstream and muddle push-upstream both act as their “normal” versions, but beware that the branch you are on may not exist in the upstream repositories.
  • muddle sync sets the checkout to the “appropriate” branch. muddle sync -verbose tells you how it is deciding this, and muddle sync -show tells you but doesn’t actually do it. These can be useful when trying to decide why one is not on the branch one expected.
  • muddle stamp version will use a stamp name that incorporates the build description branch name.
  • muddle stamp release does not itself know about branched trees. However, the user should take care to specify a sensible version number for the release stamp when creating it. The -next switch is unlikely to be useful in this case.

Using muddle sync

Muddle build descriptions implicitly or explicitly describe a branch for each checkout. When branched build trees are in use, that can be even more implicit than normal. Particularly in a branched build tree, it is quite possible to have changed into another branch in one or more checkouts (perhaps to revert to “master” or some other maintenance branch, for comparison). It therefore becomes useful to have a muddle comamnd that puts the build tree back to the “expected” branches.

That’s what muddle sync is for.

The rules of what it does are necessarily a little complex. They are summarised in muddle help sync, or in the docstring for the sync() method on the VersionControlHandler class (try muddle doc sync).

Useful query commands

muddle query build-desc-branch

Reports the branch of the build description, and whether it is being used as the (default) branch for other checkouts. For instance:

$ muddle query build-desc-branch
Build description checkout:builds/* is on branch v1-maintenance
  This WILL be used as the default branch for other checkouts

muddle query checkout-id

Reports the VCS revision id (or equivalent) for a checkout.

For instance:

$ muddle query checkout-id builds
4c209d3e1ed449d1a5721552671d0506fdcb3de0
$ muddle query checkout-id '(subdomain2)co_repo1.1'
bbf0d5ef5000a88c3d9e6f52283af51f71ad6d1e
$ muddle query checkout-id co.bzr
tibs@kynesim.co.uk-20130212165003-notzt8cbn2n7vi2e

These are the same ids that are used in stamp files.

muddle query checkout-vcs

Report on the VCS (version control system) being used for each checkout, and also any VCS options set thereon.

For instance:

$ muddle query checkout-vcs
> Checkout version control systems ..
checkout:builds/*          -> VCS git
checkout:co.branch1/*      -> VCS git
checkout:co.branch1.fred/* -> VCS git
checkout:co.bzr/*          -> VCS bzr {'no_follow': True}
checkout:co.fred/*         -> VCS git
checkout:co1/*             -> VCS git

muddle query checkout-branches

Report on the branches associated with each checkout:

  • the current branch
  • the branch that the build description requests (or, for the build description itself, the branch requested at muddle init)
  • the branch to follow (if the build description has requested “follow”)

For instance:

$ muddle query checkout-branches
----------------------  --------------  ---------------  ----------------
Checkout                Current branch  Original branch  Branch to follow
----------------------  --------------  ---------------  ----------------
builds                  v1-maintenance  v1-maintenance   <it's own>
co_repo1                v1-maintenance  <none>           v1-maintenance
(subdomain1)builds      v1-maintenance  <none>           v1-maintenance
(subdomain1)co_repo1    v1-maintenance  <none>           v1-maintenance
(subdomain2)builds      v1-maintenance  <none>           v1-maintenance
(subdomain2)co_repo1.1  v1-maintenance  <none>           v1-maintenance

The help text (muddle help query checkout-branches) has some more examples, and more explanation of the content of the columns.

muddle query checkout-repos

For instance:

$ muddle query checkout-repos
> Checkout repositories ..
checkout:builds/*          -> Repository('git', '<repo>', 'builds', branch='branch.follow')
checkout:co.branch1/*      -> Repository('git', '<repo>', 'co.branch1', branch='branch1')
checkout:co.branch1.fred/* -> Repository('git', '<repo>', 'co.branch1.fred', revision='fred', branch='branch1')
checkout:co.bzr/*          -> Repository('bzr', '<repo>', 'co.bzr')
checkout:co.fred/*         -> Repository('git', '<repo>', 'co.fred', revision='fred')
checkout:co1/*             -> Repository('git', '<repo>', 'co1')

(I’ve replaced the actual URL of the repository with <repo> to save space across the page), or, just showing the URL and not VCS, branch or revision:

$ muddle query checkout-repos -url
> Checkout repositories ..
checkout:builds/*          -> file:///.../repo/src/builds
checkout:co.branch1/*      -> file:///.../repo/src/co.branch1
checkout:co.branch1.fred/* -> file:///.../repo/src/co.branch1.fred
checkout:co.bzr/*          -> file:///.../repo/src/co.bzr
checkout:co.fred/*         -> file:///.../repo/src/co.fred
checkout:co1/*             -> file:///.../repo/src/co1

(again, I’ve truncated the middle of the repository URLs to save space).

muddle sync -show

Whilst not strictly a query command, muddle sync -show can be useful for showing what muddle believes about a checkout and its branches:

$ muddle sync -show _all
Synchronising for checkout:builds/changes_committed
  This is the build description
  The build description has "follow" set
  Following ourselves - so we're already there
Synchronising for checkout:co.branch1/changes_committed
  We have a specific branch in the build description, "branch1"
  We do NOT want to follow the build description
  We want branch "branch1"
Synchronising for checkout:co.branch1.fred/changes_committed
  We have a specific revision in the build description, "fred"
  We do NOT want to follow the build description
  We want revision "fred", branch "branch1"
Synchronising for checkout:co.bzr/changes_committed
  Checkout is marked "no_follow"
  We do NOT want to follow the build description
  We want branch "master"
Synchronising for checkout:co.fred/changes_committed
  We have a specific revision in the build description, "fred"
  We do NOT want to follow the build description
  We want revision "fred"
Synchronising for checkout:co1/changes_committed
  The build description has "follow" set
  The build description is checkout:builds/*
  We want to follow the build description onto branch "branch.follow"

What happens if there are sub-domains?

Subdomains always follow the top level build description.

Setting the “follow” flag in a subdomain’s build description will have no effect. It is only the top-level build description that can be followed.

What happens with upstream repositories?

A local repository can be linked to several upstream repositories. Checkouts that use that local repository can then do push-upstream and/or pull-upstream commands to those upstreams.

muddle push-upstream and muddle pull-upstream always use refer to the repository as defined in the build description. Thus they continue to work in the same manner for a “following” build and a normal build.

However, “in the same manner” does mean that pushing a branched checkout will push the branch, and pulling to a branched checkout will try to pull that branch, so whether these commands succeed depends on what branches exist in the remote repositories.