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:
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.)
We then use:
$ muddle branch-tree Widget-v1.0-maintenance
to make various checks, and then branch all the checkouts.
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.
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.We can then do:
$ muddle push _all
to push the build description and all the branched checkouts.
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:
- Add the
builder.follow_build_desc_branch = True
line to the end. - 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:
- Check that there are no problems.
- 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:
- 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.
- 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:
- 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).
- If a specific revision alone is given in the build description, then that will be used. This will result in a detached HEAD.
- If a specific branch alone is given in the build description, then that branch will be used.
- 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
andmuddle 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
andmuddle 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, andmuddle 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.