Mercurial > hgbook
diff en/ch04-daily.xml @ 749:7e7c47481e4f
Oops, this is the real merge for my hg's oddity
author | Dongsheng Song <dongsheng.song@gmail.com> |
---|---|
date | Fri, 20 Mar 2009 16:43:35 +0800 |
parents | en/ch05-daily.xml@cfdb601a3c8b |
children | 1c13ed2130a7 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/en/ch04-daily.xml Fri Mar 20 16:43:35 2009 +0800 @@ -0,0 +1,544 @@ +<!-- vim: set filetype=docbkxml shiftwidth=2 autoindent expandtab tw=77 : --> + +<chapter id="chap.daily"> + <?dbhtml filename="mercurial-in-daily-use.html"?> + <title>Mercurial in daily use</title> + + <sect1> + <title>Telling Mercurial which files to track</title> + + <para id="x_1a3">Mercurial does not work with files in your repository unless + you tell it to manage them. The <command role="hg-cmd">hg + status</command> command will tell you which files Mercurial + doesn't know about; it uses a + <quote><literal>?</literal></quote> to display such + files.</para> + + <para id="x_1a4">To tell Mercurial to track a file, use the <command + role="hg-cmd">hg add</command> command. Once you have added a + file, the entry in the output of <command role="hg-cmd">hg + status</command> for that file changes from + <quote><literal>?</literal></quote> to + <quote><literal>A</literal></quote>.</para> + + &interaction.daily.files.add; + + <para id="x_1a5">After you run a <command role="hg-cmd">hg commit</command>, + the files that you added before the commit will no longer be + listed in the output of <command role="hg-cmd">hg + status</command>. The reason for this is that <command + role="hg-cmd">hg status</command> only tells you about + <quote>interesting</quote> files&emdash;those that you have + modified or told Mercurial to do something with&emdash;by + default. If you have a repository that contains thousands of + files, you will rarely want to know about files that Mercurial + is tracking, but that have not changed. (You can still get this + information; we'll return to this later.)</para> + + <para id="x_1a6">Once you add a file, Mercurial doesn't do anything with it + immediately. Instead, it will take a snapshot of the file's + state the next time you perform a commit. It will then continue + to track the changes you make to the file every time you commit, + until you remove the file.</para> + + <sect2> + <title>Explicit versus implicit file naming</title> + + <para id="x_1a7">A useful behaviour that Mercurial has is that if you pass + the name of a directory to a command, every Mercurial command + will treat this as <quote>I want to operate on every file in + this directory and its subdirectories</quote>.</para> + + &interaction.daily.files.add-dir; + + <para id="x_1a8">Notice in this example that Mercurial printed the names of + the files it added, whereas it didn't do so when we added the + file named <filename>a</filename> in the earlier + example.</para> + + <para id="x_1a9">What's going on is that in the former case, we explicitly + named the file to add on the command line, so the assumption + that Mercurial makes in such cases is that you know what you + were doing, and it doesn't print any output.</para> + + <para id="x_1aa">However, when we <emphasis>imply</emphasis> the names of + files by giving the name of a directory, Mercurial takes the + extra step of printing the name of each file that it does + something with. This makes it more clear what is happening, + and reduces the likelihood of a silent and nasty surprise. + This behaviour is common to most Mercurial commands.</para> + + </sect2> + <sect2> + <title>Aside: Mercurial tracks files, not directories</title> + + <para id="x_1ab">Mercurial does not track directory information. Instead, + it tracks the path to a file. Before creating a file, it + first creates any missing directory components of the path. + After it deletes a file, it then deletes any empty directories + that were in the deleted file's path. This sounds like a + trivial distinction, but it has one minor practical + consequence: it is not possible to represent a completely + empty directory in Mercurial.</para> + + <para id="x_1ac">Empty directories are rarely useful, and there are + unintrusive workarounds that you can use to achieve an + appropriate effect. The developers of Mercurial thus felt + that the complexity that would be required to manage empty + directories was not worth the limited benefit this feature + would bring.</para> + + <para id="x_1ad">If you need an empty directory in your repository, there + are a few ways to achieve this. One is to create a directory, + then <command role="hg-cmd">hg add</command> a + <quote>hidden</quote> file to that directory. On Unix-like + systems, any file name that begins with a period + (<quote><literal>.</literal></quote>) is treated as hidden by + most commands and GUI tools. This approach is illustrated + below.</para> + +&interaction.daily.files.hidden; + + <para id="x_1ae">Another way to tackle a need for an empty directory is to + simply create one in your automated build scripts before they + will need it.</para> + + </sect2> + </sect1> + <sect1> + <title>How to stop tracking a file</title> + + <para id="x_1af">Once you decide that a file no longer belongs in your + repository, use the <command role="hg-cmd">hg remove</command> + command; this deletes the file, and tells Mercurial to stop + tracking it. A removed file is represented in the output of + <command role="hg-cmd">hg status</command> with a + <quote><literal>R</literal></quote>.</para> + + &interaction.daily.files.remove; + + <para id="x_1b0">After you <command role="hg-cmd">hg remove</command> a file, + Mercurial will no longer track changes to that file, even if you + recreate a file with the same name in your working directory. + If you do recreate a file with the same name and want Mercurial + to track the new file, simply <command role="hg-cmd">hg + add</command> it. Mercurial will know that the newly added + file is not related to the old file of the same name.</para> + + <sect2> + <title>Removing a file does not affect its history</title> + + <para id="x_1b1">It is important to understand that removing a file has + only two effects.</para> + <itemizedlist> + <listitem><para id="x_1b2">It removes the current version of the file + from the working directory.</para> + </listitem> + <listitem><para id="x_1b3">It stops Mercurial from tracking changes to + the file, from the time of the next commit.</para> + </listitem></itemizedlist> + <para id="x_1b4">Removing a file <emphasis>does not</emphasis> in any way + alter the <emphasis>history</emphasis> of the file.</para> + + <para id="x_1b5">If you update the working directory to a changeset in + which a file that you have removed was still tracked, it will + reappear in the working directory, with the contents it had + when you committed that changeset. If you then update the + working directory to a later changeset, in which the file had + been removed, Mercurial will once again remove the file from + the working directory.</para> + + </sect2> + <sect2> + <title>Missing files</title> + + <para id="x_1b6">Mercurial considers a file that you have deleted, but not + used <command role="hg-cmd">hg remove</command> to delete, to + be <emphasis>missing</emphasis>. A missing file is + represented with <quote><literal>!</literal></quote> in the + output of <command role="hg-cmd">hg status</command>. + Mercurial commands will not generally do anything with missing + files.</para> + + &interaction.daily.files.missing; + + <para id="x_1b7">If your repository contains a file that <command + role="hg-cmd">hg status</command> reports as missing, and + you want the file to stay gone, you can run <command + role="hg-cmd">hg remove <option + role="hg-opt-remove">--after</option></command> at any + time later on, to tell Mercurial that you really did mean to + remove the file.</para> + + &interaction.daily.files.remove-after; + + <para id="x_1b8">On the other hand, if you deleted the missing file by + accident, give <command role="hg-cmd">hg revert</command> the + name of the file to recover. It will reappear, in unmodified + form.</para> + +&interaction.daily.files.recover-missing; + + </sect2> + <sect2> + <title>Aside: why tell Mercurial explicitly to remove a + file?</title> + + <para id="x_1b9">You might wonder why Mercurial requires you to explicitly + tell it that you are deleting a file. Early during the + development of Mercurial, it let you delete a file however you + pleased; Mercurial would notice the absence of the file + automatically when you next ran a <command role="hg-cmd">hg + commit</command>, and stop tracking the file. In practice, + this made it too easy to accidentally remove a file without + noticing.</para> + + </sect2> + <sect2> + <title>Useful shorthand&emdash;adding and removing files in one + step</title> + + <para id="x_1ba">Mercurial offers a combination command, <command + role="hg-cmd">hg addremove</command>, that adds untracked + files and marks missing files as removed.</para> + + &interaction.daily.files.addremove; + + <para id="x_1bb">The <command role="hg-cmd">hg commit</command> command + also provides a <option role="hg-opt-commit">-A</option> + option that performs this same add-and-remove, immediately + followed by a commit.</para> + + &interaction.daily.files.commit-addremove; + + </sect2> + </sect1> + <sect1> + <title>Copying files</title> + + <para id="x_1bc">Mercurial provides a <command role="hg-cmd">hg + copy</command> command that lets you make a new copy of a + file. When you copy a file using this command, Mercurial makes + a record of the fact that the new file is a copy of the original + file. It treats these copied files specially when you merge + your work with someone else's.</para> + + <sect2> + <title>The results of copying during a merge</title> + + <para id="x_1bd">What happens during a merge is that changes + <quote>follow</quote> a copy. To best illustrate what this + means, let's create an example. We'll start with the usual + tiny repository that contains a single file.</para> + + &interaction.daily.copy.init; + + <para id="x_1be">We need to do some work in + parallel, so that we'll have something to merge. So let's + clone our repository.</para> + + &interaction.daily.copy.clone; + + <para id="x_1bf">Back in our initial repository, let's use the <command + role="hg-cmd">hg copy</command> command to make a copy of + the first file we created.</para> + + &interaction.daily.copy.copy; + + <para id="x_1c0">If we look at the output of the <command role="hg-cmd">hg + status</command> command afterwards, the copied file looks + just like a normal added file.</para> + + &interaction.daily.copy.status; + + <para id="x_1c1">But if we pass the <option + role="hg-opt-status">-C</option> option to <command + role="hg-cmd">hg status</command>, it prints another line of + output: this is the file that our newly-added file was copied + <emphasis>from</emphasis>.</para> + + &interaction.daily.copy.status-copy; + + <para id="x_1c2">Now, back in the repository we cloned, let's make a change + in parallel. We'll add a line of content to the original file + that we created.</para> + + &interaction.daily.copy.other; + + <para id="x_1c3">Now we have a modified <filename>file</filename> in this + repository. When we pull the changes from the first + repository, and merge the two heads, Mercurial will propagate + the changes that we made locally to <filename>file</filename> + into its copy, <filename>new-file</filename>.</para> + + &interaction.daily.copy.merge; + + </sect2> + <sect2 id="sec.daily.why-copy"> + <title>Why should changes follow copies?</title> + + <para id="x_1c4">This behaviour, of changes to a file propagating out to + copies of the file, might seem esoteric, but in most cases + it's highly desirable.</para> + + <para id="x_1c5">First of all, remember that this propagation + <emphasis>only</emphasis> happens when you merge. So if you + <command role="hg-cmd">hg copy</command> a file, and + subsequently modify the original file during the normal course + of your work, nothing will happen.</para> + + <para id="x_1c6">The second thing to know is that modifications will only + propagate across a copy as long as the repository that you're + pulling changes from <emphasis>doesn't know</emphasis> about + the copy.</para> + + <para id="x_1c7">The reason that Mercurial does this is as follows. Let's + say I make an important bug fix in a source file, and commit + my changes. Meanwhile, you've decided to <command + role="hg-cmd">hg copy</command> the file in your repository, + without knowing about the bug or having seen the fix, and you + have started hacking on your copy of the file.</para> + + <para id="x_1c8">If you pulled and merged my changes, and Mercurial + <emphasis>didn't</emphasis> propagate changes across copies, + your source file would now contain the bug, and unless you + remembered to propagate the bug fix by hand, the bug would + <emphasis>remain</emphasis> in your copy of the file.</para> + + <para id="x_1c9">By automatically propagating the change that fixed the bug + from the original file to the copy, Mercurial prevents this + class of problem. To my knowledge, Mercurial is the + <emphasis>only</emphasis> revision control system that + propagates changes across copies like this.</para> + + <para id="x_1ca">Once your change history has a record that the copy and + subsequent merge occurred, there's usually no further need to + propagate changes from the original file to the copied file, + and that's why Mercurial only propagates changes across copies + until this point, and no further.</para> + + </sect2> + <sect2> + <title>How to make changes <emphasis>not</emphasis> follow a + copy</title> + + <para id="x_1cb">If, for some reason, you decide that this business of + automatically propagating changes across copies is not for + you, simply use your system's normal file copy command (on + Unix-like systems, that's <command>cp</command>) to make a + copy of a file, then <command role="hg-cmd">hg add</command> + the new copy by hand. Before you do so, though, please do + reread section <xref linkend="sec.daily.why-copy"/>, and make + an informed + decision that this behaviour is not appropriate to your + specific case.</para> + + </sect2> + <sect2> + <title>Behaviour of the <command role="hg-cmd">hg copy</command> + command</title> + + <para id="x_1cc">When you use the <command role="hg-cmd">hg copy</command> + command, Mercurial makes a copy of each source file as it + currently stands in the working directory. This means that if + you make some modifications to a file, then <command + role="hg-cmd">hg copy</command> it without first having + committed those changes, the new copy will also contain the + modifications you have made up until that point. (I find this + behaviour a little counterintuitive, which is why I mention it + here.)</para> + + <para id="x_1cd">The <command role="hg-cmd">hg copy</command> command acts + similarly to the Unix <command>cp</command> command (you can + use the <command role="hg-cmd">hg cp</command> alias if you + prefer). The last argument is the + <emphasis>destination</emphasis>, and all prior arguments are + <emphasis>sources</emphasis>. If you pass it a single file as + the source, and the destination does not exist, it creates a + new file with that name.</para> + + &interaction.daily.copy.simple; + + <para id="x_1ce">If the destination is a directory, Mercurial copies its + sources into that directory.</para> + + &interaction.daily.copy.dir-dest; + + <para id="x_1cf">Copying a directory is + recursive, and preserves the directory structure of the + source.</para> + + &interaction.daily.copy.dir-src; + + <para id="x_1d0">If the source and destination are both directories, the + source tree is recreated in the destination directory.</para> + + &interaction.daily.copy.dir-src-dest; + + <para id="x_1d1">As with the <command role="hg-cmd">hg rename</command> + command, if you copy a file manually and then want Mercurial + to know that you've copied the file, simply use the <option + role="hg-opt-copy">--after</option> option to <command + role="hg-cmd">hg copy</command>.</para> + + &interaction.daily.copy.after; + + </sect2> + </sect1> + <sect1> + <title>Renaming files</title> + + <para id="x_1d2">It's rather more common to need to rename a file than to + make a copy of it. The reason I discussed the <command + role="hg-cmd">hg copy</command> command before talking about + renaming files is that Mercurial treats a rename in essentially + the same way as a copy. Therefore, knowing what Mercurial does + when you copy a file tells you what to expect when you rename a + file.</para> + + <para id="x_1d3">When you use the <command role="hg-cmd">hg rename</command> + command, Mercurial makes a copy of each source file, then + deletes it and marks the file as removed.</para> + + &interaction.daily.rename.rename; + + <para id="x_1d4">The <command role="hg-cmd">hg status</command> command shows + the newly copied file as added, and the copied-from file as + removed.</para> + + &interaction.daily.rename.status; + + <para id="x_1d5">As with the results of a <command role="hg-cmd">hg + copy</command>, we must use the <option + role="hg-opt-status">-C</option> option to <command + role="hg-cmd">hg status</command> to see that the added file + is really being tracked by Mercurial as a copy of the original, + now removed, file.</para> + + &interaction.daily.rename.status-copy; + + <para id="x_1d6">As with <command role="hg-cmd">hg remove</command> and + <command role="hg-cmd">hg copy</command>, you can tell Mercurial + about a rename after the fact using the <option + role="hg-opt-rename">--after</option> option. In most other + respects, the behaviour of the <command role="hg-cmd">hg + rename</command> command, and the options it accepts, are + similar to the <command role="hg-cmd">hg copy</command> + command.</para> + + <sect2> + <title>Renaming files and merging changes</title> + + <para id="x_1d7">Since Mercurial's rename is implemented as + copy-and-remove, the same propagation of changes happens when + you merge after a rename as after a copy.</para> + + <para id="x_1d8">If I modify a file, and you rename it to a new name, and + then we merge our respective changes, my modifications to the + file under its original name will be propagated into the file + under its new name. (This is something you might expect to + <quote>simply work,</quote> but not all revision control + systems actually do this.)</para> + + <para id="x_1d9">Whereas having changes follow a copy is a feature where + you can perhaps nod and say <quote>yes, that might be + useful,</quote> it should be clear that having them follow a + rename is definitely important. Without this facility, it + would simply be too easy for changes to become orphaned when + files are renamed.</para> + + </sect2> + <sect2> + <title>Divergent renames and merging</title> + + <para id="x_1da">The case of diverging names occurs when two developers + start with a file&emdash;let's call it + <filename>foo</filename>&emdash;in their respective + repositories.</para> + + &interaction.rename.divergent.clone; + + <para id="x_1db">Anne renames the file to <filename>bar</filename>.</para> + + &interaction.rename.divergent.rename.anne; + + <para id="x_1dc">Meanwhile, Bob renames it to + <filename>quux</filename>.</para> + + &interaction.rename.divergent.rename.bob; + + <para id="x_1dd">I like to think of this as a conflict because each + developer has expressed different intentions about what the + file ought to be named.</para> + + <para id="x_1de">What do you think should happen when they merge their + work? Mercurial's actual behaviour is that it always preserves + <emphasis>both</emphasis> names when it merges changesets that + contain divergent renames.</para> + + &interaction.rename.divergent.merge; + + <para id="x_1df">Notice that Mercurial does warn about the divergent + renames, but it leaves it up to you to do something about the + divergence after the merge.</para> + + </sect2> + <sect2> + <title>Convergent renames and merging</title> + + <para id="x_1e0">Another kind of rename conflict occurs when two people + choose to rename different <emphasis>source</emphasis> files + to the same <emphasis>destination</emphasis>. In this case, + Mercurial runs its normal merge machinery, and lets you guide + it to a suitable resolution.</para> + + </sect2> + <sect2> + <title>Other name-related corner cases</title> + + <para id="x_1e1">Mercurial has a longstanding bug in which it fails to + handle a merge where one side has a file with a given name, + while another has a directory with the same name. This is + documented as <ulink role="hg-bug" + url="http://www.selenic.com/mercurial/bts/issue29">issue + 29</ulink>.</para> + + &interaction.issue29.go; + + </sect2> + </sect1> + <sect1> + <title>Recovering from mistakes</title> + + <para id="x_1e2">Mercurial has some useful commands that will help you to + recover from some common mistakes.</para> + + <para id="x_1e3">The <command role="hg-cmd">hg revert</command> command lets + you undo changes that you have made to your working directory. + For example, if you <command role="hg-cmd">hg add</command> a + file by accident, just run <command role="hg-cmd">hg + revert</command> with the name of the file you added, and + while the file won't be touched in any way, it won't be tracked + for adding by Mercurial any longer, either. You can also use + <command role="hg-cmd">hg revert</command> to get rid of + erroneous changes to a file.</para> + + <para id="x_1e4">It's useful to remember that the <command role="hg-cmd">hg + revert</command> command is useful for changes that you have + not yet committed. Once you've committed a change, if you + decide it was a mistake, you can still do something about it, + though your options may be more limited.</para> + + <para id="x_1e5">For more information about the <command role="hg-cmd">hg + revert</command> command, and details about how to deal with + changes you have already committed, see chapter <xref + linkend="chap.undo"/>.</para> + + </sect1> +</chapter> + +<!-- +local variables: +sgml-parent-document: ("00book.xml" "book" "chapter") +end: +-->