view en/ch05-daily.xml @ 838:d1f676a6a4b3 default tip

update mq chapter. propagate ef53d025f410.
author Yoshiki Yazawa <yaz@honeyplanet.jp>
date Thu, 03 Dec 2009 01:26:08 +0900
parents acf9dc5f088d
children
line wrap: on
line source

<!-- 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 by default, <command
	role="hg-cmd">hg status</command> only tells you about
      <quote>interesting</quote> files&emdash;those that you have (for
      example) modified, removed, or renamed.  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 behavior 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>myfile.txt</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.  The assumption
	that Mercurial makes in such cases is that we know what we
	are 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 behavior is common to most Mercurial commands.</para>
    </sect2>

    <sect2>
      <title>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 (which will occur at the next
      commit).  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 that was committed when it was still tracking a file
	that you later removed, the file 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 id="chap:daily.copy">
    <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 behavior&emdash;of changes to a file
	propagating out to copies of the file&emdash;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 changeset that you're
	merging changes from <emphasis>hasn't yet seen</emphasis> 
	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 new source file would now contain the bug, and unless you
	knew 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
	at the first merge, and not afterwards.</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 <xref linkend="sec:daily:why-copy"/>, and make
	an informed
	decision that this behavior is not appropriate to your
	specific case.</para>

    </sect2>
    <sect2>
      <title>Behavior 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
	behavior 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).  We must supply two or
	more arguments, of which the last is treated as the
	<emphasis>destination</emphasis>, and all others are
	<emphasis>sources</emphasis>.</para>

      <para id="x_685">If you pass <command role="hg-cmd">hg copy</command> 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 remove</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 behavior 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>

    <para id="x_686">If you're familiar with the Unix command line, you'll be
      glad to know that <command role="hg-cmd">hg rename</command>
      command can be invoked as <command role="hg-cmd">hg
	mv</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>. (Remember that <command
	  role="hg-cmd">hg mv</command> is an alias for <command
	  role="hg-cmd">hg rename</command>.)</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 behavior 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 while Mercurial warns about the divergent
	renames, 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 is helpful 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 <xref
	linkend="chap:undo"/>.</para>
  </sect1>

  <sect1>
    <title>Dealing with tricky merges</title>

    <para id="x_687">In a complicated or large project, it's not unusual for a
      merge of two changesets to result in some headaches.  Suppose
      there's a big source file that's been extensively edited by each
      side of a merge: this is almost inevitably going to result in
      conflicts, some of which can take a few tries to sort
      out.</para>

    <para id="x_688">Let's develop a simple case of this and see how to deal with
      it.  We'll start off with a repository containing one file, and
      clone it twice.</para>

    &interaction.ch04-resolve.init;

    <para id="x_689">In one clone, we'll modify the file in one way.</para>

    &interaction.ch04-resolve.left;

    <para id="x_68a">In another, we'll modify the file differently.</para>

    &interaction.ch04-resolve.right;

    <para id="x_68b">Next, we'll pull each set of changes into our original
      repo.</para>

    &interaction.ch04-resolve.pull;

    <para id="x_68c">We expect our repository to now contain two heads.</para>

    &interaction.ch04-resolve.heads;

    <para id="x_68d">Normally, if we run <command role="hg-cmd">hg
	merge</command> at this point, it will drop us into a GUI that
      will let us manually resolve the conflicting edits to
      <filename>myfile.txt</filename>.  However, to simplify things
      for presentation here, we'd like the merge to fail immediately
      instead.  Here's one way we can do so.</para>

    &interaction.ch04-resolve.export;

    <para id="x_68e">We've told Mercurial's merge machinery to run the command
      <command>false</command> (which, as we desire, fails
      immediately) if it detects a merge that it can't sort out
      automatically.</para>

    <para id="x_68f">If we now fire up <command role="hg-cmd">hg
	merge</command>, it should grind to a halt and report a
	failure.</para>

    &interaction.ch04-resolve.merge;

    <para id="x_690">Even if we don't notice that the merge failed, Mercurial
      will prevent us from accidentally committing the result of a
      failed merge.</para>

    &interaction.ch04-resolve.cifail;

    <para id="x_691">When <command role="hg-cmd">hg commit</command> fails in
      this case, it suggests that we use the unfamiliar <command
	role="hg-cmd">hg resolve</command> command.  As usual,
	<command role="hg-cmd">hg help resolve</command> will print a
      helpful synopsis.</para>

    <sect2>
      <title>File resolution states</title>

      <para id="x_692">When a merge occurs, most files will usually remain
	unmodified.  For each file where Mercurial has to do
	something, it tracks the state of the file.</para>

      <itemizedlist>
	<listitem>
	  <para id="x_693">A <emphasis>resolved</emphasis> file has been
	    successfully merged, either automatically by Mercurial or
	    manually with human intervention.</para>
	</listitem>
	<listitem>
	  <para id="x_694">An <emphasis>unresolved</emphasis> file was not merged
	    successfully, and needs more attention.</para>
	</listitem>
      </itemizedlist>

      <para id="x_695">If Mercurial sees <emphasis>any</emphasis> file in the
	unresolved state after a merge, it considers the merge to have
	failed.  Fortunately, we do not need to restart the entire
	merge from scratch.</para>

      <para id="x_696">The <option role="hg-opt-resolve">--list</option> or
	<option role="hg-opt-resolve">-l</option> option to <command
	  role="hg-cmd">hg resolve</command> prints out the state of
	each merged file.</para>

      &interaction.ch04-resolve.list;

      <para id="x_697">In the output from <command role="hg-cmd">hg
	  resolve</command>, a resolved file is marked with
	<literal>R</literal>, while an unresolved file is marked with
	<literal>U</literal>.  If any files are listed with
	<literal>U</literal>, we know that an attempt to commit the
	results of the merge will fail.</para>
    </sect2>

    <sect2>
      <title>Resolving a file merge</title>

      <para id="x_698">We have several options to move a file from the unresolved
	into the resolved state.  By far the most common is to rerun
	<command role="hg-cmd">hg resolve</command>.  If we pass the
	names of individual files or directories, it will retry the
	merges of any unresolved files present in those locations. We
	can also pass the <option role="hg-opt-resolve">--all</option>
	or <option role="hg-opt-resolve">-a</option> option, which
	will retry the merges of <emphasis>all</emphasis> unresolved
	files.</para>

      <para id="x_699">Mercurial also lets us modify the resolution state of a
	file directly.  We can manually mark a file as resolved using
	the <option role="hg-opt-resolve">--mark</option> option, or
	as unresolved using the <option
	  role="hg-opt-resolve">--unmark</option> option.  This allows
	us to clean up a particularly messy merge by hand, and to keep
	track of our progress with each file as we go.</para>
    </sect2>
  </sect1>

  <sect1>
    <title>More useful diffs</title>

    <para id="x_6c7">The default output of the <command role="hg-cmd">hg
	diff</command> command is backwards compatible with the
      regular <command>diff</command> command, but this has some
      drawbacks.</para>

    <para id="x_6c8">Consider the case where we use <command role="hg-cmd">hg
	rename</command> to rename a file.</para>

    &interaction.ch04-diff.rename.basic;

    <para id="x_6c9">The output of <command role="hg-cmd">hg diff</command> above
      obscures the fact that we simply renamed a file.  The <command
	role="hg-cmd">hg diff</command> command accepts an option,
      <option>--git</option> or <option>-g</option>, to use a newer
      diff format that displays such information in a more readable
      form.</para>

    &interaction.ch04-diff.rename.git;

    <para id="x_6ca">This option also helps with a case that can otherwise be
      confusing: a file that appears to be modified according to
      <command role="hg-cmd">hg status</command>, but for which
      <command role="hg-cmd">hg diff</command> prints nothing. This
      situation can arise if we change the file's execute
      permissions.</para>

    &interaction.ch04-diff.chmod;

    <para id="x_6cb">The normal <command>diff</command> command pays no attention
      to file permissions, which is why <command role="hg-cmd">hg
	diff</command> prints nothing by default.  If we supply it
      with the <option>-g</option> option, it tells us what really
      happened.</para>

    &interaction.ch04-diff.chmod.git;
  </sect1>

  <sect1>
    <title>Which files to manage, and which to avoid</title>

    <para id="x_6cc">Revision control systems are generally best at managing text
      files that are written by humans, such as source code, where the
      files do not change much from one revision to the next.  Some
      centralized revision control systems can also deal tolerably
      well with binary files, such as bitmap images.</para>

    <para id="x_6cd">For instance, a game development team will typically manage
      both its source code and all of its binary assets (e.g. geometry
      data, textures, map layouts) in a revision control
      system.</para>

    <para id="x_6ce">Because it is usually impossible to merge two conflicting
      modifications to a binary file, centralized systems often
      provide a file locking mechanism that allow a user to say
      <quote>I am the only person who can edit this
	file</quote>.</para>

    <para id="x_6cf">Compared to a centralized system, a distributed revision
      control system changes some of the factors that guide decisions
      over which files to manage and how.</para>

    <para id="x_6d0">For instance, a distributed revision control system cannot,
      by its nature, offer a file locking facility.  There is thus no
      built-in mechanism to prevent two people from making conflicting
      changes to a binary file.  If you have a team where several
      people may be editing binary files frequently, it may not be a
      good idea to use Mercurial&emdash;or any other distributed
      revision control system&emdash;to manage those files.</para>

    <para id="x_6d1">When storing modifications to a file, Mercurial usually
      saves only the differences between the previous and current
      versions of the file.  For most text files, this is extremely
      efficient. However, some files (particularly binary files) are
      laid out in such a way that even a small change to a file's
      logical content results in many or most of the bytes inside the
      file changing.  For instance, compressed files are particularly
      susceptible to this. If the differences between each successive
      version of a file are always large, Mercurial will not be able
      to store the file's revision history very efficiently.  This can
      affect both local storage needs and the amount of time it takes
      to clone a repository.</para>

    <para id="x_6d2">To get an idea of how this could affect you in practice,
      suppose you want to use Mercurial to manage an OpenOffice
      document.  OpenOffice stores documents on disk as compressed zip
      files. Edit even a single letter of your document in OpenOffice,
      and almost every byte in the entire file will change when you
      save it. Now suppose that file is 2MB in size.  Because most of
      the file changes every time you save, Mercurial will have to
      store all 2MB of the file every time you commit, even though
      from your perspective, perhaps only a few words are changing
      each time.  A single frequently-edited file that is not friendly
      to Mercurial's storage assumptions can easily have an outsized
      effect on the size of the repository.</para>

    <para id="x_6d3">Even worse, if both you and someone else edit the OpenOffice
      document you're working on, there is no useful way to merge your
      work. In fact, there isn't even a good way to tell what the
      differences are between your respective changes.</para>

    <para id="x_6d4">There are thus a few clear recommendations about specific
      kinds of files to be very careful with.</para>

    <itemizedlist>
      <listitem>
	<para id="x_6d5">Files that are very large and incompressible, e.g. ISO
	  CD-ROM images, will by virtue of sheer size make clones over
	  a network very slow.</para>
      </listitem>
      <listitem>
	<para id="x_6d6">Files that change a lot from one revision to the next
	  may be expensive to store if you edit them frequently, and
	  conflicts due to concurrent edits may be difficult to
	  resolve.</para>
      </listitem>
    </itemizedlist>
  </sect1>

  <sect1>
    <title>Backups and mirroring</title>

    <para id="x_6d7">Since Mercurial maintains a complete copy of history in each
      clone, everyone who uses Mercurial to collaborate on a project
      can potentially act as a source of backups in the event of a
      catastrophe.  If a central repository becomes unavailable, you
      can construct a replacement simply by cloning a copy of the
      repository from one contributor, and pulling any changes they
      may not have seen from others.</para>

    <para id="x_6d8">It is simple to use Mercurial to perform off-site backups
      and remote mirrors.  Set up a periodic job (e.g. via the
      <command>cron</command> command) on a remote server to pull
      changes from your master repositories every hour.  This will
      only be tricky in the unlikely case that the number of master
      repositories you maintain changes frequently, in which case
      you'll need to do a little scripting to refresh the list of
      repositories to back up.</para>

    <para id="x_6d9">If you perform traditional backups of your master
      repositories to tape or disk, and you want to back up a
      repository named <filename>myrepo</filename>, use <command>hg
	clone -U myrepo myrepo.bak</command> to create a
      clone of <filename>myrepo</filename> before you start your
      backups.  The <option>-U</option> option doesn't check out a
      working directory after the clone completes, since that would be
      superfluous and make the backup take longer.</para>

    <para id="x_6da">If you then back up <filename>myrepo.bak</filename> instead
      of <filename>myrepo</filename>, you will be guaranteed to have a
      consistent snapshot of your repository that won't be pushed to
      by an insomniac developer in mid-backup.</para>
  </sect1>
</chapter>

<!--
local variables: 
sgml-parent-document: ("00book.xml" "book" "chapter")
end:
-->