Mercurial_ The Definitive Guide - Bryan O'Sullivan [28]
Figure 4-5. The working directory can have two parents
Figure 4-5 shows the normal state of the working directory, where it has a single changeset as parent. That changeset is the tip, the newest changeset in the repository that has no children.
Figure 4-6. The working directory gains new parents after a commit
It’s useful to think of the working directory as “the changeset I’m about to commit.” Any files that you tell Mercurial that you’ve added, removed, renamed, or copied will be reflected in that changeset, as will modifications to any files that Mercurial is already tracking; the new changeset will have the parents of the working directory as its parents.
After a commit, Mercurial will update the parents of the working directory, so that the first parent is the ID of the new changeset, and the second is the null ID. This is shown in Figure 4-6. Mercurial doesn’t touch any of the files in the working directory when you commit; it just modifies the dirstate to note its new parents.
Creating a New Head
It’s perfectly normal to update the working directory to a changeset other than the current tip. For example, you might want to know what your project looked like last Tuesday, or you could be looking through changesets to see which one introduced a bug. In cases like this, the natural thing to do is update the working directory to the changeset you’re interested in, and then examine the files in the working directory directly to see their contents as they were when you committed that changeset. The effect of this is shown in Figure 4-7.
Figure 4-7. The working directory, updated to an older changeset
Having updated the working directory to an older changeset, what happens if you make some changes, and then commit? Mercurial behaves in the same way as I outlined above. The parents of the working directory become the parents of the new changeset. This new changeset has no children, so it becomes the new tip. And the repository now contains two changesets that have no children; we call these heads. You can see the structure that this creates in Figure 4-8.
Figure 4-8. After a commit made while synced to an older changeset
* * *
Note
If you’re new to Mercurial, you should keep in mind a common “error,” which is to use the hg pull command without any options. By default, the hg pull command does not update the working directory, so you’ll bring new changesets into your repository, but the working directory will stay synced at the same changeset as before the pull. If you make some changes and commit afterwards, you’ll thus create a new head, because your working directory isn’t synced to whatever the current tip is. To combine the operation of a pull, followed by an update, run hg pull -u.
I put the word “error” in quotes because all that you need to do to rectify the situation where you created a new head by accident is hg merge, then hg commit. In other words, this almost never has negative consequences; it’s just something of a surprise for newcomers. I’ll discuss other ways to avoid this behavior, and why Mercurial behaves in this initially surprising way, later on.
* * *
Merging Changes
When you run the hg merge command, Mercurial leaves the first parent of the working directory unchanged, and sets the second parent to the changeset you’re merging with, as shown in Figure 4-9.
Figure 4-9. Merging two heads
Mercurial also has to modify the working directory, to merge the files managed in the two changesets. Simplified a little, the merging process goes like this, for every file in the manifests of both changesets:
If neither changeset has modified a file, do nothing with that file.
If one changeset has modified a file and the other hasn’t, create the modified copy of the file in the working directory.
If one changeset has removed a file and the other hasn’t (or has also deleted it), delete the file from the working directory.
If one changeset has removed