The goal here is to create something in the spirit of GitStyleBranches or ColocatedBranches as a component of the 3.0 UI refresh, while not significantly injuring the ability to use "bzr style branches". I think these goals can be simultaneously met.
This is largely covered elsewhere. However, a thumbnail of a few of the reasons why it's useful:
- Switching one workspace among branches at will is a useful workflow in some cases.
- It builds in cheap branching without extraneous setup.
- With proper support, it includes the ability to copy/sync multiple branches at once.
I'm writing this because, while those capabilities are useful enough that I don't think we can perpetually dispense with them, there are also a number of advantages to our current obvious workflow (dir per branch), as well as to our splitting up of responsibilities among repos/branches/WTs that I don't want to see lost. And I believe we can do both at once.
Terminology, Notes, and Assumptions
I intend to avoid getting bogged down in terminology discussions here. So I will decide on terms by sheer fiat, with no expectation that these will be the final terms for bzr. They will, however, be the final terms for this document.
I feel that using some variant of 'repo' or 'branch' terminology for describing the entities we're talking about here leads to confusion (this is why I'm avoiding saying 'git-style branches' or 'colocated branches'), so I want to create a new term. So, a central entity in this system will be a bundle of multiple branches that are relatively bound together, and will be called a fascis.
I want to emphasize this; a fascis is not a type of branch, or a type of repository. It's a separate entity. I think this is a very important piece to keep here, which is why I'm not using a name like 'Foo Branches' or 'Bar Repos'. Keeping the Branch and Repository entities separate from fasces is instrumental in maintaining our existing flexibility in workspace arrangement. I feel like our existing separation of Repo/Branch/WT is a significant improvement over the rather muddied and melded handling of e.g. git. I don't think we need to step backward to make this ease-of-use/flexibility gain.
One probably-contentious issue has to do with branch identification.
At present, branches by definition do not have names; they merely have locations, and are always referred to by location. For dir-per-branch layouts, this is obvious and just peachy. For branches as internal entities in a fascis, this doesn't work well. When dealing with multiple branches inside a single entity, addressing-by-name is the only reasonable option. Attempting to hack something that looks like address by name but winds up translating into address by location, as switch currently does, is just going to lead to more pain than necessary IMO.
However, we certainly want to retain address-by-location for independent branches. So we will retain both. It should be reasonable to, when in a situation (e.g., inside a fascis) where named branches can exist, look for a branch by the given name, and then proceed to try it as a location if that fails. Possibly restrictions can be placed on names of branches (e.g., no leading '.' or '/', or no internal '://') to ensure locations can always be disambiguated if necessary. This is an implementation details, and is assumed to be addressable there; it will not be treated further here.
A new top-level entity called a fascis is created. This entity contains a set of 1 or more branches, with one of them currently selected. It will generally (though not necessarily) contain a working tree, as branches do today. It may contain an internal repo, or use an external shared repo, as branches do today (see mention in init section below). As with current branches, the working tree may be created or destroyed at will, and reconfigure options will be able to move its internal repository out into a shared one or vice versa. We are not to lose any of the flexibility of having the components be able to exist independently, just gain the ability to treat multiple branches together.
Working in a fascis will be similar to working in a git repo today; a single branch is 'active' at any given time, with the working tree referring to it. Branches may be switched among at will, with the working tree following. Branches may be created or deleted as needed. Generally, action will be much like git, or current setup with a single lightweight checkout (except for much greater integration of inter-branch handling).
Within fasces, branches are identified by name. When pointing at fasces, branches are either given as the location of the fascis (in which case the active branch is assumed), or some syntax in the location URL is given to specify a branch within it. With legacy dir branches, the location is still the branch identifier, but with fasces branches are identified by name (implied by location specs as described).
init as a command will be changed to create a fascis rather than a branch. That fascis will contain an initial branch with some name, whether it be 'master' like git, or the same name as the directory (echoing the nick heuristic), or what have you. The result is that the current 'normal' workflow will result in everybody using fasces rather than branches, but as long as no additional named branches are created within a given fascis, nobody should notice or care about the difference.
This highlights a point; it is intentional that I created the fascis as a separate entity, and not as some sort of variant of a branch or repository. As with a current branch, a fascis will create and use an internal repository if necessary, but can also use an external shared repository if one is created. So, a set of single-branch fasces, in a common shared repo (or not) will act just as a set of branches (in a common repo or not) do today. This is an ingredient that allows the current workflow to Just Happen.
branch does what it currently does; creates a branch.
If run inside a fascis (or pseudo-inside, a la -d in various commands), it should create a named branch rather than a dir branch if the given destination doesn't looks like a location (x-ref earlier discussion of prohibited branch name characters).
- Perhaps special syntax should be created for basing the branch off the currently active branch within the fascis. But perhaps just using '.' as the source will work just as well.
When run with the location of a fascis as the source, the currently active branch will be chosen as the source branch to branch from. Syntax will be chosen to allow selecting another branch within the fascis if necessary.
When run somewhere other than inside a fascis, it will create a new fascis with a single branch (the newly created one), similarly to init's proposed action. The alternative would be to create a conventional dir branch, but consistency with init is more desirable, as well as moving everything to fasces to gain the advantages described up top for this change.
The clone command will be reclaimed from being an alias for branch. When given a fascis as the source, it will create a full clone of the fascis with all its branches. If run outside an existing fascis, it will create a new one with that clone. If run within a fascis, it will create new branches as mirrors/clones of the source.
- This probably calls for prepending some sort of prefix to the created branches. I suggest / as a delimiter for such things. Whether we need a full-blown remotes-style system is an open question; I think we can delay the answer for some time.
General branch-info comments (revno, log, etc)
When run on a fascis (whether from 'inside', or by URL reference from outside), deal with the currently active branch. URL syntax should exist for selecting a different branch.
Nothing too unexpected. Given source branch tried as named and then location, URL's to fasces resolve as repeated in previous commands. Nice, simple, boring.
push and pull
It is very valuable to be able to push/pull only single branches, and that ability is a key plus to the current dir-as-branch setup that I miss when using VCSen that take a more blobby approach. We definitely don't want to lose this, even with a set of branches in a fascis.
On the other hand, a key feature we want from fasces is to be able to throw them around as units. Maybe we want push-fascis and pull-fascis commands? No, that gives us only the two granularities of 'one branch' and 'every branch here'. I suggest
push and pull retain their current meaning, acting by default on the current active branch. Slight syntactic extensions to supply source/destination for other branches (like -d currently does on both commands, but probably slightly changed since they're not really -directories anymore).
Both also gain some support for sets of branches at the same time, via wildcarding. Strawman illustrations: bzr pull -b 'upstream/*', bzr push -b branch1 -b branch2 -b branch3, etc.
Misc bits and questions
This probably calls for a new metadir format. For minimal change, the addition of a branches/ dir under .bzr/, containing a dir for each branch which will simple contain all the stuff currently in .bzr/branch/ with no changes. .bzr/branch/ then becomes a branch reference (much like in current light checkouts) to the currently active one. Better schemes may be devised; this is just the most obvious.
- It would be nice to be able to have a checkout as one stick in a fascis (presumably as a pseudo-branch, as they're implemented now).
Obviously we now need a command to list the branches in a fascis; time to pull in bzrtools' branches, with updates for branches being named.
Various other commands will grow more fascis-related stuff; info for example. Needs should be fairly obvious as it's implemented.
reconfigure will probably need some changes; a lot of its transformations shouldn't be attempted on fasces I think, and others will need to be reconsidered for exactly what they should do in this NWO.
What about upgrade? For instance, should we simply assume/require that all branches in the fascis be the same format? I tend to think not, though it should probably be the default case.
- What do we do with nick? Should it now rename the branch, or should we keep the nick as a separate entity from the branch name? Renaming could be troublesome in cases where the nick isn't something you'd commonly use as a dir name in the current scheme, so probably keep independent of name.
Shelves are an attribute of the working tree, so need no change. But it's now easy to shelve something, then unshelve it on a different branch. Bonus!
- Various importers (cvsps, fast-import, etc) will need to be updated to create a fascis full of named branches, rather than a shared repo full of dir branches.
The end result of the transistion is that we will not longer, in normal circumstances, create [dir] branches; we will create fasces instead. If a user does not create additional named branches inside them, though, they can be used as dir branches are nowadays, including in sharing repositories. No workflow changes are needed for people who intend to continue working that way.
They can also be used in a git style, with only a single fascis for a given project, and all branches a user cares about internal to it.
Or (in true bzr flexi-fashion), a user can do both at once; with a shared repo, have several fasces with one or a few branches each, working in parallel. Or one fascis with, say, two dozen branches, containing most of the project, but simultaneously have a single-branch fascis beside it (probably sharing a repo to avoid extra storage use) for a branch being worked on that a separate working tree is desired for (for continual back and forth comparisons or the like). Alternately, an external [light] checkout of a branch inside a fascis could be used for this case too.
For projects that have more than one active 'central' branch, where devs or dev-happy users will commonly want more than a single one of (e.g., FreeBSD, bzr now that we have bzr.dev and 2. branches), we gain the ability to clone-initial and pull-update a mirror of the bulk at once.