Rendering of reStructured text is not possible, please install Docutils.
**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
* **Classifications**:
* **Queues**:
* **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
Introduction
============
**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.)
Behaviour of tags
-----------------
Tags are ''versioned'': it's possible to see when a tag was placed, and by
whom. If a tag is later moved or deleted, that's also recorded.
Tags are defined by revisions; the current tags for a branch are those of
the branch's tip revision.
Tags ''propagate'' across push, pull, and merge. When a branch is pushed or
pulled to a mirror, the tags go across too. When branches are merged,
their tags are reconciled.
It's possible there will be a conflict in tag definitions during a merge.
This should be rare, as we assume each tag will only be placed once. If
there is a conflict, it can be handled through the usual cycle: the merge
command completes, the working tree is put in a conflicted state, the user
inspects and resolves the conflicts, and then commits a new revision
resolving the conflict.
The normal case is that tags are placed at the time the revision is
committed.
Unless the ``--force`` option is given, it is an error to try to place a tag
that already exists.
User stories
============
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::
vi .bzr/checkout/tags
See a list of all tags in a branch
----------------------------------
This should be available from the command line, but also probably from
within the gui or a web interface.
::
bzr lstags
Logs are displayed with tag names taken from the current dictionary next
to the relevant revisions::
bzr log
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.
Possible interface::
bzr log --tag TAGNAME
123
2006-04-01
user@domain
create tag TAGNAME pointing to 123
Copy tags between branch mirrors
--------------------------------
A tag is created in a branch, which is then pushed into a mirror. The tag
definition should then be visible in the mirror. If someone else pulls from
there into their own mirror, the same tag definition carries across.
Example::
cd a
bzr commit --tag FOO
bzr push ../b
cd ../b
bzr log -r tag:FOO
Merge tags between branches
---------------------------
A user is maintaining a long-running feature branch. From time to time they
merge in the project's mainline branch. After a merge, any new tags which
were added in the mainline (or branches merged into it) should also be
visible in the merge result.
Set per-user tags
-----------------
A user's doing some work and often comparing it to a particular previous
revision. They'd like to assign a name to that revision, but it won't be
meaningful to other people, or important beyond the scope of this particular
work session.
They might like to set a name that only exists within the working directory, or
in their ``~/.bazaar`` directory.
This case seems to require a different mechanism and the case seems
a bit contrived; it can be deferred for now.
Compare tag definitions in two branches
---------------------------------------
Perhaps run ``bzr tag --show`` in both branches, and compare the output.
Update tags from another branch
-------------------------------
In a fix branch it might be desired to update the tag definitions from the
main branch without bringing across any changes.
A manual way of doing this would be ``bzr tag --show | bzr tag --set -``
Or alternatively ``bzr merge --tags-only FROM_BRANCH``
Import from Subversion, preserving tags
---------------------------------------
Tags are made in Subversion through an idiom of making a copy of the whole
tree to a ``/tags/`` directory in the repository. The importer can look
in the tags directory for copies from the branch it's importing. When one
is detected, it should create a bzr tags at that point.
Import from CVS, preserving tags
--------------------------------
This is difficult because tags can be set on individual files in ways
that are not representable as whole-tree tags.
We might be able to steal the logic for this from Subversions' cvs2svn utility. -- JelmerVernooij
Implementation
==============
Tag dictionary
--------------
"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 tree
------------
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.)
Constraint on tags
------------------
The tags in a revision's dictionary should all point to ancestors of that
revision, or the revision itself. Tags in a working directory must point
to one of the basis revisions, or one of their parents.
This forbids several situations:
* tags cannot point to future revisions
* tags cannot point to unrelated revisions
* tags cannot point to nonexistent revisions
This means that a normal fetch of a revision and its ancestors will
implicitly ensure that all tags in that revision can be resolved. (It's
possible that the tag revisions may be ghosts or past a history horizon,
as for other revisions.)
This constraint should be enforced when a ``-r`` option is given to ``bzr
tag``.
Conflicts
---------
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.
Tags file in working tree
-------------------------
The working tree's tags are stored in ``.bzr/checkout/tags``.
Commit
------
Commit needs to store a new version of the tags dictionary, with the revision
id of the newly committed version.
Historic revisions
------------------
Historic revisions should be able to report the tags relevant to them.
Fetch
-----
Fetch (called by push, pull, merge) should move across tag definitions
corresponding to the copied revisions.
Tree comparison
---------------
Changes in tags should be reported by tree comparison. Changes can be
just addition or deletion.
Revision specifiers
-------------------
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
-------
Bundles must serialize the changes in tags described by tree comparisons.
Testaments
----------
Testaments should cover the tags in a revision. This requires a new
testament version.
Open questions
==============
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
Alternative mechanisms
----------------------
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.
Pros:
* 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.
Cons:
* 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.