version 1.13, 2014/12/18 00:58:55
|
version 1.14, 2016/02/15 23:02:07
|
Line 1
|
Line 1
|
## Mercurial usage for NetBSD |
## Mercurial usage for NetBSD |
(hypothetically assuming the repositories have been converted) |
(hypothetically assuming the repositories have been converted) |
|
|
Here are some directions for how one would use Mercurial after a |
This page has been [[moved|http://www.netbsd.org/~dholland/notes/hg-migration/usage.html]]. |
NetBSD transition from CVS to Mercurial. |
|
Most of this is pretty trivial (especially for anyone who's used |
|
Mercurial before...) |
|
|
|
There is a lot of FUD circulating about using a distributed version |
|
control system, what with talk of "workflows" and "topic branches" and |
|
other unfamiliar terms. |
|
Most of this arises from git user communities and git advocates; |
|
because git is screwy, using git is more complicated than using other |
|
better designed tools. |
|
Also, those suffering from Stockholm syndrome with respect to git tend |
|
to believe that the complexities of git are inherent to distributed |
|
version control, which is not the case; and many other people have been |
|
alarmed (or scared, or confused) by things such people have told them. |
|
|
|
### Basic usage |
|
|
|
First, NetBSD will go on using a central master repository. There is |
|
nothing to be gained by changing this; we have the project |
|
infrastructure to support it, and ultimately there has to be some tree |
|
somewhere that constitutes the master copy regardless. |
|
|
|
Therefore, the basic usage is almost entirely unchanged: |
|
|
|
CVS Mercurial |
|
|
|
cvs checkout hg clone |
|
cvs update -dP hg pull && hg update |
|
cvs -n update hg status |
|
cvs log file hg log file [or just hg log] |
|
|
|
cvs update -p file hg cat file |
|
cvs annotate hg annotate |
|
cvs diff -u hg diff |
|
cvs add hg add |
|
cvs rm hg rm |
|
[no can do] hg cp |
|
[no can do] hg mv |
|
cvs commit hg commit && hg push |
|
cvs tag hg tag |
|
|
|
You will notice that CVS's update and commit have been divided into |
|
two now-separable actions: in Mercurial, pull fetches changes from a |
|
remote repository but doesn't affect your working tree, and update |
|
updates your working tree to (by default) the latest new changes. |
|
Similarly, commit integrates changes from your working tree, but |
|
locally only; push publishes those changes to the remote repository. |
|
|
|
This means that you can commit many times before pushing; this is |
|
often desirable if you're working on something nontrivial and you want |
|
to wait until it's ready before shipping it out. |
|
|
|
There is one catch, which is that other people can commit (and push to |
|
the master repository) while you're working. You can mostly avoid |
|
this, if you haven't committed anything locally yet, by doing "hg pull |
|
&& hg update" before committing, which will merge into your |
|
uncommitted changes and works exactly like updating before committing |
|
in CVS. However, if you've committed a number of changes, or someone |
|
got a new change in right between when you last pulled and when you |
|
committed, you need to do an explicit merge instead, and then you can |
|
push. |
|
|
|
In the simple case, you do an explicit merge as follows: |
|
|
|
hg pull |
|
hg merge |
|
hg commit |
|
|
|
When you get a merge conflict, you first need to resolve it (in the |
|
usual way by editing) and then you must tag it resolved in hg before |
|
hg will let you commit, like this: |
|
|
|
hg resolve -m file |
|
|
|
You can list unresolved conflicts thus: |
|
|
|
hg resolve -l |
|
|
|
Note that even with the explicit merge this is almost exactly |
|
equivalent to the CVS behavior when someone commits ahead of you. |
|
The chief difference is that because Mercurial does whole-tree |
|
commits, *any* change ahead of you needs to be merged, not just one |
|
that touches the same files you've edited. |
|
|
|
There is one gotcha, which is that you can't do explicit merges in a |
|
tree with uncommitted changes. The best way around this is to stash |
|
your changes: |
|
|
|
hg stash |
|
hg merge |
|
...whatever merge stuff... |
|
hg unstash |
|
|
|
You can also do the merge in another tree; because Mercurial is a |
|
distributed tool, you can create a temporary copy of your tree, or |
|
push the changes you need to another tree you already have, do the |
|
work there, and push the results back. Let's suppose you have two |
|
trees, "src" and "scratch", where you never keep uncommitted changes |
|
in "scratch" so it can be used for this kind of thing. Then you can do |
|
the following (starting at the top of src): |
|
|
|
hg push ../scratch |
|
cd ../scratch |
|
hg update |
|
hg merge |
|
...whatever merge stuff, including commit... |
|
cd ../src |
|
hg pull ../scratch |
|
hg update |
|
|
|
### Disconnected operation |
|
|
|
Mercurial is a distributed system, and works by cloning the entire |
|
history into each tree you create. |
|
This has its downsides; but it means that you get disconnected |
|
operation for free. |
|
The only operations that need to contact the master repository are |
|
push, pull, incoming, and outgoing. |
|
|
|
### Some other bits |
|
|
|
A commit with no descendents (that is, the most recent commit on any |
|
line of development) is called a "head". |
|
You can list these as follows: |
|
|
|
hg heads |
|
|
|
This will include commits that have descendents only on other |
|
branches, e.g. the last commit on a development branch that's been |
|
merged but not closed. Use "-t" ("topological heads") to hide these. |
|
|
|
You can see what "hg pull" and "hg push" are going to do via "hg |
|
incoming" and "hg outgoing" respectively. |
|
(FWIW, git can't do this.) |
|
|
|
If you interrupt Mercurial (or Mercurial gets interrupted, e.g. by a |
|
system crash) you want to do this afterwards: |
|
|
|
hg recover |
|
|
|
and if you have reason to think the repository might be corrupt you |
|
can check it like this: |
|
|
|
hg verify |
|
|
|
### Development branches |
|
|
|
A development branch is one where you're working on some new feature |
|
and you expect to merge the branch into the trunk later. |
|
Unlike in CVS, this is very cheap in Mercurial. |
|
The following are the operations you need, using "libc13" as an |
|
example branch name. |
|
|
|
Note that even if you're working on something nontrivial that will |
|
take a number of commits, if you aren't intending to push the changes |
|
out before they're done you don't need to make a branch and there's |
|
nothing gained by doing so. |
|
However, if you expect to be working over a long period of time on a |
|
major effort (such as the mythical libc version bump), and/or you |
|
want or expect other developers to contribute or at least test your |
|
changes before they're done, go ahead and create a branch. |
|
|
|
Create a new branch: |
|
|
|
cvs update -dP hg pull && hg update (if needed) |
|
update doc/BRANCHES update doc/BRANCHES (if appropriate) |
|
cvs commit doc/BRANCHES hg commit doc/BRANCHES (if needed) |
|
cvs tag libc13-base hg tag libc13-base |
|
cvs ph'tagn hg branch libc13 |
|
[make first change] [make first change] |
|
cvs commit hg commit |
|
hg push |
|
|
|
Mercurial warns you that branches are permanent and expensive; this |
|
warning is aimed at git users who ought to be creating bookmarks |
|
instead and not something you need to be concerned about. |
|
|
|
Check out a new tree on a branch: |
|
|
|
cvs co -P -rlibc13 hg clone [url] |
|
cd src |
|
hg update -r libc13 |
|
|
|
Switch to a new tree on a branch: |
|
|
|
cvs up -dP -A -rlibc13 hg pull (if needed) |
|
hg update -r libc13 |
|
|
|
Note that if you have uncommitted changes, Mercurial will balk at |
|
crossing from one branch to another because it doesn't know how to |
|
merge them. |
|
In that case do this: |
|
|
|
hg update -r libc13-base |
|
[resolve conflicts if needed] |
|
hg update -r libc13 |
|
[resolve conflicts if needed] |
|
|
|
Check which branch you're currently on: |
|
|
|
cat CVS/Tag hg branch |
|
|
|
See list of branches: |
|
|
|
[no can do reliably] hg branches |
|
|
|
Note that unlike with CVS there's no version-control-related reason to |
|
get a new tree just to work on a branch. |
|
(Although of course it's still possible to commit to the wrong branch |
|
by accident.) |
|
Get a new tree if and only if you want to have a different tree for |
|
administrative reasons. |
|
|
|
Sync your branch with the trunk ("HEAD" in CVS): |
|
|
|
cvs ph'tagn hg merge default |
|
[resolve conflicts] [resolve conflicts] |
|
cvs commit hg commit |
|
hg push |
|
|
|
When you're done with your branch, in Mercurial you can "close" it so |
|
it's no longer active. |
|
This causes it to disappear from some reports, reduces some internal |
|
management overheads, and prevents accidental commits on it. |
|
|
|
[no can do] hg commit --close-branch |
|
|
|
Don't forget to update doc/BRANCHES too. |
|
|
|
### Vendor branches |
|
|
|
A vendor branch is one where code from a third party is committed in |
|
unmodified state, so it can be updated easily from upstream later. |
|
|
|
Note that in CVS vendor branches are magic (in a bad way); in |
|
Mercurial we'll just use an ordinary branch. We'll start it from the |
|
empty revision so it doesn't contain any unwanted rubbish. |
|
|
|
To start a new vendor branch for the upstream package "frobozz", |
|
assuming you've already written frobozz2netbsd if one's needed: |
|
|
|
mkdir tmp |
|
cd tmp |
|
hg update -r0000 |
|
mkdir external && cd external |
|
mkdir bsd && cd bsd |
|
mkdir frobozz && cd frobozz |
|
tar -xvzf frobozz-1.0.tgz tar -xvzf frobozz-1.0.tgz |
|
mv frobozz-1.0 dist mv frobozz-1.0 dist |
|
cp .../frobozz2netbsd . cp .../frobozz2netbsd . |
|
./frobozz2netbsd ./frobozz2netbsd (if needed) |
|
cvs import src/distrib/bsd/frobozz \ |
|
FROBOZZ frobozz-1-0 |
|
hg add |
|
hg branch FROBOZZ |
|
hg commit |
|
hg tag frobozz-1-0 |
|
cd ../src |
|
cvs update -dP |
|
hg update -r default |
|
hg merge FROBOZZ |
|
hg commit |
|
[hack as needed] [hack as needed] |
|
cvs commit hg commit |
|
hg push |
|
cd .. |
|
rm -r tmp |
|
|
|
Note that in both cases this imports frobozz2netbsd on the branch; |
|
this seems the most convenient but I'm not sure if it's been our |
|
standard procedure. |
|
|
|
To update "frobozz" to 1.1: |
|
|
|
mkdir tmp |
|
cd tmp |
|
hg update -rFROBOZZ |
|
cd external/bsd/frobozz |
|
tar -xvzf frobozz-1.1.tgz tar -xvzf frobozz-1.1.tgz |
|
rm -r dist |
|
mv frobozz-1.1 dist mv frobozz-1.1 dist |
|
./frobozz2netbsd ./frobozz2netbsd |
|
cvs import src/distrib/bsd/frobozz \ |
|
FROBOZZ frobozz-1-0 |
|
hg addremove |
|
hg commit |
|
hg tag frobozz-1-1 |
|
cd .. |
|
mkdir tmp2 && cd tmp2 |
|
cvs ph'tagn |
|
hg update -r default |
|
hg merge FROBOZZ |
|
[resolve conflicts] [resolve conflicts] |
|
cvs commit hg commit |
|
cd ../src |
|
cvs update -dP |
|
[hack as needed] [hack as needed] |
|
cvs commit hg commit |
|
hg push |
|
cd .. |
|
rm -r tmp tmp2 |
|
|
|
### Release branches |
|
|
|
A release branch is one that diverges from the main branch and is not |
|
expected to be merged back into it. |
|
However, changes from the main branch are (individually) merged into |
|
it after review. |
|
|
|
Creating a release branch in Mercurial is the same as creating a |
|
feature branch; see above. |
|
So is checking it out. |
|
Committing a change to a release branch is no different from |
|
committing to the default branch or any other branch. |
|
|
|
TODO: we should probably use the Mercurial cherrypick extension for at |
|
least some release branch pullups; I don't know how to do that offhand |
|
without looking it up. |
|
|
|
Tagging a release: |
|
|
|
cvs rtag -r netbsd-7 \ hg tag -r netbsd-7 \ |
|
netbsd-7-0-RELEASE netbsd-7-0-RELEASE |
|
|
|
Viewing the changes on a branch: |
|
|
|
cvs log > file hg log -b netbsd-7 |
|
[page through and curse] |
|
|
|
Extracting tarballs: |
|
|
|
mkdir tmp |
|
cd tmp |
|
cvs export -r netbsd-7-0-RELEASE \ hg archive -r netbsd-7-0-RELEASE \ |
|
src ../netbsd-7.0.tar.gz |
|
mv src netbsd-7.0 |
|
tar -cvzf ../netbsd-7.0.tar.gz \ |
|
netbsd-7.0 |
|
cd .. |
|
rm -r tmp |
|
|
|
|
|
### Reverting a bad commit |
|
|
|
Sometimes somebody commits something that needs to be unwound later. |
|
In CVS you have to track down each per-file change and undo each one |
|
separately, then commit them all. |
|
In Mercurial, because Mercurial has whole-tree commits, you can do it |
|
with a single command. |
|
|
|
cvs update -j1.6 -j1.5 foo.c |
|
cvs update -j1.9 -j1.8 bar.c |
|
cvs update -j1.15 -j1.14 baz.c |
|
hg backout -r 101abcde |
|
[resolve conflicts] [resolve conflicts] |
|
cvs commit hg commit |
|
hg push |
|
|
|
Note that apparently if you use hg backout to back out the most recent |
|
commit, it auto-commits. |
|
(This seems to me like a UI bug.) |
|
|
|
### Carrying local changes |
|
|
|
In CVS you can keep uncommitted changes in your tree indefinitely with |
|
no ill effects. |
|
(Or at least, no ill effects until you want to commit other changes to |
|
the same files, run into merge conflicts, or hit [[!template id=pr |
|
number=42961]].) |
|
|
|
In Mercurial having uncommitted changes keeps you from doing explicit |
|
merges, which you need to do much more often than in CVS. |
|
There are several ways around this: |
|
|
|
* You can stash your uncommitted changes any time you need to merge. |
|
This works fine but it quickly becomes a nuisance. |
|
* You can use different trees for hacking and for building the system |
|
for install, since presumably you only need the local changes in |
|
the latter case. |
|
This works fine until you need to shift partially-completed hacking to |
|
the installable tree, and then becomes painful. |
|
* You can commit your local changes as "secret" using the evolve |
|
extension (I recommend reading the docs for the evolve extension); |
|
then they're committed and can be merged and so on, but won't get |
|
pushed back to the master repository. |
|
The downside of this is that you can't readily distribute your local |
|
changes among your own repositories. |
|
* You can use the mq patch queue extension and store your local |
|
changes as patches against the tree; then they can be popped off |
|
easily for other work. |
|
The downside of this is that merging stuff into your local changes |
|
becomes awkward. |
|
* You can finish your local changes so they can be committed upstream :-) |
|
|
|
None of these solutions is perfect, but one or the other of these |
|
approaches is probably good enough in most cases. |
|
|
|
### Reverting stuff locally |
|
|
|
In CVS you can use "cvs update" to pin a subtree down to a specific |
|
point in history, where it will stay while you update the rest of the |
|
tree around it. |
|
(Accidental engagement of this feature is probably as common as |
|
intentional use...) |
|
|
|
There is no direct equivalent in Mercurial. |
|
However, you can easily alter a file or subtree to roll it back to a |
|
specific point in history, and then carry the resulting diff as a |
|
local modification until whatever issue prompted you to do this gets |
|
sorted out. |
|
|
|
To revert to a specific version: |
|
|
|
hg revert -r rev subtree |
|
To revert to a specific date: |
|
|
|
hg revert -d date subtree |
|
|
|
|
|
### Other stuff |
|
|
|
Have I forgotten anything? |
|
Email me questions... |
|