Bazaar

Bazaar

 




Wiki Tools

  • Find Page
  • Recent Changes
  • Page History
  • Attachments

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.