This specification has been implemented and will be released in bzr 0.15. This page remains for historical interest. Some of the refinements which are not yet implemented have been recorded as bugs.
Tagging in Bazaar
- Assignee: MartinPool
- Created: 2005-10-27 by JamesBlackwell
- Contributors: MichaelEllerman, RobertCollins, JohnArbashMeinel, MartinPool, JelmerVernooij
- Status: Released
- Implementation branch: http://people.samba.org/bzr/jelmer/bzr/tags
- Implementation branch: (plugin) http://michael.ellerman.id.au/files/bzr/tags
- Malone bugs:
- Specification page: https://features.launchpad.net/products/bzr/+spec/tags
Tags provide meaningful user-assigned names for revisions. Tags are typically used to mark release versions, or other points of interest.
The common case is that each tag name is written only once, when the tag is "placed". It's also possible to delete or replace tags, but this will typically only be done if a tag was incorrectly placed, and typically requires a --force option. (If a name needs to point to different code as development proceeds it may be more appropriate to use a branch.)
Tag names are non-whitespace Unicode strings starting with a letter. It's recommended that the tag name start with a project identifier: for example, bzr-release-0.9 or hp-ijs-1.2.12.
(Rationale: other systems disallow whitespace in tag names, and it makes representation in text files or on command lines easier.)
A revision can be tagged as it's created
bzr commit --tag TAG_NAME ...
This adds the new revision id to the tag dictionary in the working directory before committing it.
The option can be repeated to add multiple tags pointing to the same revision.
If the commit is interrupted, the tag name is removed (i.e. changes to the tag dictionary are rolled back.)
This is the recommended way to place tags, because the tag will be present in the revision it describes.
An existing revision can be tagged
bzr tag -r 123 TAG_NAME
This commits a new revision adding a tag. If the location contains a working directory, it is rebased onto the newly-committed revision.
The location of a branch or working directory can be given to create a tag in that branch:
bzr tag -r 123 TAG_NAME -d LOCATION
Diff against a tagged revision
Compare working directory to an existing tag in this branch:
bzr diff -r tag:TAG_NAME
Make a new branch from an existing tag
bzr branch -r tag:TAG_NAME SOURCE NEW_BRANCH
This looks up the tag within the source's working tree or tip revision.
Look up up the tag name in the branch's tip revision. Make a new branch from this revision.
Within the new branch and its working tree, the tag dictionary will be the one from the previously recorded revision.
If that revisions tag was placed with commit --tag, then the definition of the tag will be present in the revision.
If the tag was created or changed after the revision was written out, then the tag's own definition won't be visible in the new branch. In either case, no later tags will be visible within that space. The new tag definitions can be seen by looking at the later branch.
Revert a working directory to the tree of an existing tag
The tag name is mapped to a revision id, and the tree is reverted to that revision. This implies reverting the working tree's tag revisions.
Within a branch from older tag, compare to newer tag
A branch made from an older tag contains the definition of tags from that older time, as do working directories created from that branch.
To compare to a newer tag it is necessary to point to a branch that contains the new branch. So it would work to do:
bzr branch -r bzr-0.9 ./bzr.dev ./bzr-0.9-fixes cd bzr-0.9-fixes bzr diff -r ..bzr-0.10 . ../bzr.dev
The tag bzr-0.10 is looked up within the relevant context, which is ../bzr.dev.
Remove an existing tag
bzr tag --delete TAG_NAME bzr tag -d TAG_NAME
Move an incorrectly-placed tag
bzr tag --force -r 1234 TAG_NAME
Resolve conflicting tag definitions
The same name is assigned to different revisions in two branches. This is allowed, even if the two branches are stored in the same repository. When the two branches are merged, the user doing the merge must choose one definition (or remove the tag).
In the initial implementation users might be required to just edit the file directly to make a choice:
Show the history of a tag
It's possible to find out when and by whom a tag was initially created, and the history of any changes.
bzr log --tag TAGNAME 123 2006-04-01 user@domain create tag TAGNAME pointing to 123
Compare tag definitions in two branches
Perhaps run bzr tag --show in both branches, and compare the output.
"Tag dictionaries" are a dictionary of tag_name -> revision_id.
Each revision contains tag dictionaries current for that revision. By default, and in the empty revision, this set is empty. This is written when the revision is created and (as for other revision metadata) is not semantically changed after it's first written. These are the tags current for that revision.
A working tree also contains a tag dictionary. Unlike the stored revisions, this can be changed by user operations.
Tags are "looked up" to map a name to a revision_id. This lookup is done in the context of a working directory or a branch. If the context has a working tree, the tag is looked up in the working tree's dictionary. Otherwise, the tag is looked up in the dictionary of the last revision of the corresponding branch.
A tag dictionary can be represented by a UTF-8 text file containing lines of:
tag TAG_NAME REVISION_ID
There is a literal word tag at the start. Lines starting with # are ignored. Everything else is reserved for future use.
This representation can be presented to the user when displaying tags or editing the entire list.
Working trees contain their current tag dictionary. This is updated from branches when the working tree is built, updated, merged, etc.
The tag dictionary can be held in a file either at the top level (.bzrtags) or inside the control directory (.bzr/checkout/tags). In either case, the working tags file should not be added to the inventory.
XXX: Should this require a new working directory format? It seems that it will, otherwise we won't know whether the tags file is up to date or not. There may not be any mechanism yet to upgrade working trees. Perhaps there should be done through bzr upgrade, or perhaps it can be automatically triggered through operations such as upgrade.
Updating or reverting the tree should update the tags. (This behaviour should fall out through having the tree delta describe tag changes, and updates apply them.)
The conflict system should be able to record that there is a tags conflict.
Conflict indicators should be written into the working tree's tag file.
Commit needs to store a new version of the tags dictionary, with the revision id of the newly committed version.
Historic revisions should be able to report the tags relevant to them.
Fetch (called by push, pull, merge) should move across tag definitions corresponding to the copied revisions.
Changes in tags should be reported by tree comparison. Changes can be just addition or deletion.
Tag lookup is initiated through the revision spec mechanism currently used to look up revisions by revno, revision_id, date, etc. Tags create a new tag: revision namespace.
We have the option in the future to use tag names as a catchall to interpret revision specs that don't have a namespace prefix and don't look like a revision number.
Bundles must serialize the changes in tags described by tree comparisons.
Testaments should cover the tags in a revision. This requires a new testament version.
Rather than all these options, perhaps we should have 'bzr rmtag', 'bzr lstags', etc?
I think the command to list tags should be "bzr tags" (and not "bzr lstags") to be consistent with other working directory related commands, such as "bzr unknowns". --JohanRydberg
There are four main possible approaches:
[type 0] Tags are not versioned (ie once they are changed, you can't see the previous value.)
This is OK for some uses, but is insufficient because it loses historical data, and because tags can't propagate into mirrors without a special mechanism.
[type 1] Tags are versioned in the same timeline as revisions. (Changing tags requires creating a new revision, possibly implicitly.)
[type 2] Tags are versioned in a different timeline from revisions.
[type 3] There is no tagging mechanism; we just suggest to use branches instead.
This is like svn's model, and has the advantage of not introducing a new primitive. However, this design in svn is widely disliked, because users do seem to think of tags and branches differently. It hides informtion about when the tag was created or modified: you only see the revisions which were pulled or committed into the branch.
This can perform well if the branches are stored in a repository, but if they're standalone creating a tag will be expensive.
Dot file versus special mechanism
Rather than handling this as a special mechanism, why not simply put it in a dot file in the root directory?
We can add a file-specific merge helper to do special reconciliation of tags.
- Smaller changes: no need to modify the working tree, bundles, testaments, etc.
- No need for a new working tree or repository format.
- This is consistent with how ignore patterns are stored in /.bzrignore. Both are mutable within the working directory and then stored into history.
- Merges and conflicts are handled in the usual way. Conflicts don't need to be specially represented.
- Less need for explicit code to list or remove tags. Commands can be added in the future but users can look directly at the future at first.
- Preserves the idea that users should not touch anything under .bzr/ even for advanced uses.
- Keeping tags out of the inventory may make it easier to translate them semantically to and from other systems. There's no question that the working-directory file should be written into the destination system.
- The file is treated as a text file; there's no straightforward way for format upgrades to rewrite the file.
- The tags file intrudes on the user's working directory. Again, this is no worse than the ignore file, and in general files matching .bzr* should be considered reserved.
- To get the tags for a branch, we need to indirect through its inventory to get the right version of the tags file. This may be somewhat slower or more complex. On the other hand, for any high level operation we only need to look at one tag dictionary so the cost should not be too high. General efforts to speed up access to files of a particular name in a particular tree will be faster.
- Having started recording that it exists as a plain file we probably have to preserve that idea in future.
- Although this is consistent with .bzrignore, the current representation of ignore patterns within that file is not completely satisfactory.