Size: 1971
Comment: as posted by Matt Mackall on Sat Mar 29 13:21:06 CDT 2008 to mercurial-devel
|
Size: 9170
Comment: minor clarifications
|
Deletions are marked like this. | Additions are marked like this. |
Line 1: | Line 1: |
''From the posting "Bookmark concept" by Matt Mackall, Sat Mar 29 13:21:06 CDT 2008 (http://selenic.com/pipermail/mercurial-devel/2008-March/005602.html)'' | #pragma section-numbers 2 = Bookmarks = |
Line 3: | Line 4: |
Currently we have three ways of labeling revisions: | Working with Mercurial's bookmark feature. |
Line 5: | Line 6: |
* tags: point to single revisions, modifiable, stored in history, shared * local tags (tag -l): point to single revisions, modifiable, not stored or shared * branches: stored in changesets themselves, not modifiable, stored in history and shared |
<<TableOfContents(3)>> |
Line 9: | Line 8: |
Our branch concept is fairly similar to the one in CVS and similar systems, but very different from the one in git. Git's notion of a branch is instead much more like our local tag: a modifiable pointer to a branch head that isn't stored in history. This has the advantage that it's very easy to make private branches, but the disadvantage that there's no recorded history of branching for public branches. |
== Overview == |
Line 16: | Line 10: |
One obvious approach to git-like functionality here is to add a "local branch" analogous to local tags. Unfortunately, that's problematic as currently a changeset can only be on a single branch |
Bookmarks are references to commits that can be automatically updated when new commits are made. If you run `hg bookmark feature`, the `feature` bookmark refers to the current changeset. As you work and commit changes the bookmark will move forward with every commit you do. The bookmark will always point to the latest revision in your line of work. |
Line 20: | Line 12: |
So I've been tinkering with a new concept which I call a 'bookmark'. A bookmark would be like a local tag that gets updated every commit. So to work on a feature, you'd run 'hg bookmark myfeature', do a bunch of commits, and the myfeature bookmark would follow along. Switching between features (or branches, etc.) would be an 'hg update <id>'. |
Bookmarks can be used as an alternative to [[NamedBranches]] for tracking multiple lines of development. Systems like Mercurial, CVS, and Subversion store their branch information as a permanent part of each commit. This is useful for future auditing of long-lived branches, as it's always possible to identify which branch a commit was introduced on. Git, by contrast, has "branches" that are not stored in history, which is useful for working with numerous short-lived feature branches, but makes future auditing impossible. Mercurial's bookmark feature is analogous to Git's branching scheme, but can also be used in conjunction with Mercurial's traditional named branches. |
Line 26: | Line 14: |
This works fine until we want to share with other people. Pushing branches to a server would simply be 'hg push <bookmark>'. But because our bookmarks live outside the history and push/pull -only- transmit history, the server won't have any knowledge of our bookmark. This is both a good and a bad thing: in general, we don't want to spread our bookmarks all over the world. But we probably do want a way to be able to make our bookmarks visible on a server so that our branch can be pulled by name. |
== Working with bookmarks == |
Line 35: | Line 16: |
---- CategoryNewFeatures |
Bookmarks can be ''active'' or ''inactive''. Only a single bookmark can be active at any given time, and only a bookmark that points to the current revision can be active. Mercurial only tracks and updates the currently active bookmark (if there is any). This is similar to Git's approach to branching and can be used to replicate the traditional Git branching workflow. When a bookmark is created it is ''active'' by default, but an inactive bookmark can be created by passing the `--inactive` (or `-i`) flag. The currently active bookmark can be inactivated by executing `hg bookmark --inactive`. A given bookmark can be activated by updating to that bookmark (e.g. `hg update --rev feature` updates to the revision pointed to by `feature` and activates the `feature` bookmark as well). Note that updating to a revision that has a bookmark without using the bookmark name will not activate the bookmark (e.g. if the `feature` bookmark points to revision #20 and you do `hg update --rev 20` the `feature` bookmark will ''not'' be activated). Since active bookmarks are automatically updated when committing to the changeset they are pointing to, they are especially useful to keep track of different heads. They can therefore be used for trying out new features or pulling changes that have yet to be reviewed. Bookmarks can be used to access the commit whenever a usual lookup is allowed (wherever a command takes `-r revision`, `revision` can be a bookmark name), therefore you can merge and update bookmarks by their names. If the new features don't work out, development can be re-started from an old changeset. Note however that while bookmarks can be deleted using `hg bookmark --delete feature`, this merely removes the bookmark `feature`, not the changes it points to. The unwanted changes will remain in the repository. The bookmarked head can be stripped (using `hg strip`, supplied with the [[MqExtension|mq extension]]). == Transferring bookmarks == Bookmarks are stored in an untracked file called ''`.hg/bookmarks`''. They are not part of committed changes but they can be transferred between repositories using the pushkey protocol. When you clone a repository, all remote bookmarks are transferred. However only bookmarks that are present on both the local and the remote repositories are updated on push. This is done to keep your local bookmarks local until you manually publish them. As of Mercurial 2.3, all remote bookmarks are updated on pull. Prior to that, only bookmarks present on both the local and remote repositories were updated on pull. If there is a divergence between shared remote and local bookmarks, Mercurial will mark the incoming bookmark either with the path alias (e.g. `feature@alice`), or with a number if the remote isn't listed in `[paths]` (e.g. `feature@1`, `feature@2`, etc.). This is new as of Mercurial 2.1. == Example usage == Let’s give you a short example how bookmarks work. Start with an existing Mercurial repository. For this example we will use the [[http://mercurial.selenic.com/hg/hg|main mercurial repository]]. Let’s start with a basic listing of available bookmarks: {{{ $ hg bookmarks no bookmarks set }}} Create a bookmark on the current tip of the repository. This isn't a bookmark we're going to work on, instead it's meant to reference where tip was on the server-side repository when we started, a baseline for our work. Notice how the current bookmark is preceded by "`*`". {{{ $ hg bookmark my-tip $ hg bookmarks * my-tip 7348:1a5c9ca2bfd5 }}} Create a bookmark on another revision in the history. This is a bookmark we'll actually do work on. Note that creating a new bookmark does ''not'' create a new head. {{{ $ hg bookmark -r 7300 hgweb-fix $ hg bookmarks * my-tip 7348:1a5c9ca2bfd5 hgweb-fix 7300:591767e6ea7a $ hg heads -q 7348:1a5c9ca2bfd5 }}} We can then update to the bookmark we want to work on. {{{ $ hg update hgweb-fix 82 files updated, 0 files merged, 31 files removed, 0 files unresolved $ hg bookmarks my-tip 7348:1a5c9ca2bfd5 * hgweb-fix 7300:591767e6ea7a }}} The fact that we updated to the bookmark means that the bookmark became active. If we had done `hg update 7300` the bookmark `hgweb-fix` would not have been active after the update. If we commit at this point in the graph we'll create a new head. Note since the bookmark was active the bookmark will follow along, continuing to point to our working head. {{{ ...hack...hack... $ hg commit -m ’Another hgweb bugfix’ $ hg bookmarks my-tip 7348:1a5c9ca2bfd5 * hgweb-fix 7349:ca3fbad32554 $ hg heads -q 7349:ca3fbad32554 7348:1a5c9ca2bfd5 }}} If we go back to the bookmarked tip we can merge our change into it. Note that the merge doesn't get rid of either bookmark. Note also how only `my-tip` has moved forward with the merge changeset. `hgweb-fix` continues to point to the last work done while on that bookmark. {{{ $ hg update -c my-tip $ hg merge hgweb-fix $ hg commit -m ’Merge hgweb-fix’ $ hg bookmarks * my-tip 7350:4d3b1ced5c40 hgweb-fix 7349:ca3fbad32554 $ hg heads -q 7350:4d3b1ced5c40 }}} If we decide we want to continue working on our feature from the new tip, we can move the feature bookmark directly to match the tip. Note the need to use `-f` to force an existing bookmark to move. {{{ $ hg book -f hgweb-fix $ hg bookmarks my-tip 7350:4d3b1ced5c40 * hgweb-fix 7350:4d3b1ced5c40 }}} If we're done we can just delete our working bookmark. Note that this doesn't delete the changeset. This doesn't matter much if this is a line of work that gets merged (i.e. we decide to keep it), but it doesn't do everything we want for a branch we decide to discard. {{{ $ hg bookmark -d hgweb-fix $ hg bookmarks * my-tip 7350:3acda44343da }}} To actually delete the changeset as well requires the `mq` extension. {{{ $ hg tag -r hgweb-fix hgweb-fix-tag $ hg bookmark -d hgweb-fix $ hg strip hgweb-fix-tag }}} You can use bookmarks in every rev lookup. This means you can also do `hg log -prf my-tip:0` or `hg qimport -r my-tip`. It is even possible to look them up using `hg id -r`. == Working with remote repositories == Bookmarks in remote repositories are "visible" as identifiers for pull, for instance with `hg pull -r web-fix`. Bookmarks can also be pushed and pulled between repositories. By default all bookmarks on the server are updated on the client on pull, and all bookmarks that are already present on both the client and server are updated on the server on push. Prior to 2.3, only bookmarks present on both the client and server were updated on pull. To start sharing a bookmark present on a server, use `hg pull -B bookmark` and the bookmark along with the relevant changesets will be pulled to the client. To publish a local bookmark to a server, use `hg push -B bookmark` and the bookmark along with the relevant changesets will be pushed to the server. To delete a bookmark from a server, delete it locally first, then use push -B on the deleted name. To check which bookmarks exist on the remote repository but not locally use `hg incoming -B`. Use `hg outgoing -B` to check which bookmarks exists locally but not yet on the remote repository. /!\ Be aware that there is only one namespace for bookmarks - it is advised to prefix your local-only bookmarks to avoid conflicts with other users. == See also == * StandardBranching * [[http://mercurial.aragost.com/kick-start/en/bookmarks/|Bookmarks guide in the Mercurial Kick Start]] * [[http://blog.experimentalworks.net/2008/11/mercurial-bookmarks/|BookmarksExtensionExplained]] |
Bookmarks
Working with Mercurial's bookmark feature.
Contents
1. Overview
Bookmarks are references to commits that can be automatically updated when new commits are made. If you run hg bookmark feature, the feature bookmark refers to the current changeset. As you work and commit changes the bookmark will move forward with every commit you do. The bookmark will always point to the latest revision in your line of work.
Bookmarks can be used as an alternative to NamedBranches for tracking multiple lines of development. Systems like Mercurial, CVS, and Subversion store their branch information as a permanent part of each commit. This is useful for future auditing of long-lived branches, as it's always possible to identify which branch a commit was introduced on. Git, by contrast, has "branches" that are not stored in history, which is useful for working with numerous short-lived feature branches, but makes future auditing impossible. Mercurial's bookmark feature is analogous to Git's branching scheme, but can also be used in conjunction with Mercurial's traditional named branches.
2. Working with bookmarks
Bookmarks can be active or inactive. Only a single bookmark can be active at any given time, and only a bookmark that points to the current revision can be active. Mercurial only tracks and updates the currently active bookmark (if there is any). This is similar to Git's approach to branching and can be used to replicate the traditional Git branching workflow.
When a bookmark is created it is active by default, but an inactive bookmark can be created by passing the --inactive (or -i) flag. The currently active bookmark can be inactivated by executing hg bookmark --inactive. A given bookmark can be activated by updating to that bookmark (e.g. hg update --rev feature updates to the revision pointed to by feature and activates the feature bookmark as well). Note that updating to a revision that has a bookmark without using the bookmark name will not activate the bookmark (e.g. if the feature bookmark points to revision #20 and you do hg update --rev 20 the feature bookmark will not be activated).
Since active bookmarks are automatically updated when committing to the changeset they are pointing to, they are especially useful to keep track of different heads. They can therefore be used for trying out new features or pulling changes that have yet to be reviewed. Bookmarks can be used to access the commit whenever a usual lookup is allowed (wherever a command takes -r revision, revision can be a bookmark name), therefore you can merge and update bookmarks by their names.
If the new features don't work out, development can be re-started from an old changeset. Note however that while bookmarks can be deleted using hg bookmark --delete feature, this merely removes the bookmark feature, not the changes it points to. The unwanted changes will remain in the repository. The bookmarked head can be stripped (using hg strip, supplied with the mq extension).
3. Transferring bookmarks
Bookmarks are stored in an untracked file called .hg/bookmarks. They are not part of committed changes but they can be transferred between repositories using the pushkey protocol.
When you clone a repository, all remote bookmarks are transferred. However only bookmarks that are present on both the local and the remote repositories are updated on push. This is done to keep your local bookmarks local until you manually publish them. As of Mercurial 2.3, all remote bookmarks are updated on pull. Prior to that, only bookmarks present on both the local and remote repositories were updated on pull.
If there is a divergence between shared remote and local bookmarks, Mercurial will mark the incoming bookmark either with the path alias (e.g. feature@alice), or with a number if the remote isn't listed in [paths] (e.g. feature@1, feature@2, etc.). This is new as of Mercurial 2.1.
4. Example usage
Let’s give you a short example how bookmarks work.
Start with an existing Mercurial repository. For this example we will use the main mercurial repository. Let’s start with a basic listing of available bookmarks:
$ hg bookmarks no bookmarks set
Create a bookmark on the current tip of the repository. This isn't a bookmark we're going to work on, instead it's meant to reference where tip was on the server-side repository when we started, a baseline for our work. Notice how the current bookmark is preceded by "*".
$ hg bookmark my-tip $ hg bookmarks * my-tip 7348:1a5c9ca2bfd5
Create a bookmark on another revision in the history. This is a bookmark we'll actually do work on. Note that creating a new bookmark does not create a new head.
$ hg bookmark -r 7300 hgweb-fix $ hg bookmarks * my-tip 7348:1a5c9ca2bfd5 hgweb-fix 7300:591767e6ea7a $ hg heads -q 7348:1a5c9ca2bfd5
We can then update to the bookmark we want to work on.
$ hg update hgweb-fix 82 files updated, 0 files merged, 31 files removed, 0 files unresolved $ hg bookmarks my-tip 7348:1a5c9ca2bfd5 * hgweb-fix 7300:591767e6ea7a
The fact that we updated to the bookmark means that the bookmark became active. If we had done hg update 7300 the bookmark hgweb-fix would not have been active after the update.
If we commit at this point in the graph we'll create a new head. Note since the bookmark was active the bookmark will follow along, continuing to point to our working head.
...hack...hack... $ hg commit -m ’Another hgweb bugfix’ $ hg bookmarks my-tip 7348:1a5c9ca2bfd5 * hgweb-fix 7349:ca3fbad32554 $ hg heads -q 7349:ca3fbad32554 7348:1a5c9ca2bfd5
If we go back to the bookmarked tip we can merge our change into it. Note that the merge doesn't get rid of either bookmark. Note also how only my-tip has moved forward with the merge changeset. hgweb-fix continues to point to the last work done while on that bookmark.
$ hg update -c my-tip $ hg merge hgweb-fix $ hg commit -m ’Merge hgweb-fix’ $ hg bookmarks * my-tip 7350:4d3b1ced5c40 hgweb-fix 7349:ca3fbad32554 $ hg heads -q 7350:4d3b1ced5c40
If we decide we want to continue working on our feature from the new tip, we can move the feature bookmark directly to match the tip. Note the need to use -f to force an existing bookmark to move.
$ hg book -f hgweb-fix $ hg bookmarks my-tip 7350:4d3b1ced5c40 * hgweb-fix 7350:4d3b1ced5c40
If we're done we can just delete our working bookmark. Note that this doesn't delete the changeset. This doesn't matter much if this is a line of work that gets merged (i.e. we decide to keep it), but it doesn't do everything we want for a branch we decide to discard.
$ hg bookmark -d hgweb-fix $ hg bookmarks * my-tip 7350:3acda44343da
To actually delete the changeset as well requires the mq extension.
$ hg tag -r hgweb-fix hgweb-fix-tag $ hg bookmark -d hgweb-fix $ hg strip hgweb-fix-tag
You can use bookmarks in every rev lookup. This means you can also do hg log -prf my-tip:0 or hg qimport -r my-tip. It is even possible to look them up using hg id -r.
5. Working with remote repositories
Bookmarks in remote repositories are "visible" as identifiers for pull, for instance with hg pull -r web-fix.
Bookmarks can also be pushed and pulled between repositories. By default all bookmarks on the server are updated on the client on pull, and all bookmarks that are already present on both the client and server are updated on the server on push. Prior to 2.3, only bookmarks present on both the client and server were updated on pull.
To start sharing a bookmark present on a server, use hg pull -B bookmark and the bookmark along with the relevant changesets will be pulled to the client.
To publish a local bookmark to a server, use hg push -B bookmark and the bookmark along with the relevant changesets will be pushed to the server. To delete a bookmark from a server, delete it locally first, then use push -B on the deleted name.
To check which bookmarks exist on the remote repository but not locally use hg incoming -B. Use hg outgoing -B to check which bookmarks exists locally but not yet on the remote repository.
Be aware that there is only one namespace for bookmarks - it is advised to prefix your local-only bookmarks to avoid conflicts with other users.