243 lines
13 KiB
HTML
243 lines
13 KiB
HTML
|
<!DOCTYPE html>
|
||
|
<html lang='en'><head><meta charset='utf-8' /><meta name='pinterest' content='nopin' /><link href='../../../../static/css/style.css' rel='stylesheet' type='text/css' /><link href='../../../../static/css/print.css' rel='stylesheet' type='text/css' media='print' /><title>A Git User's Guide to Mercurial Queues / Steve Losh</title></head><body><header><a id='logo' href='https://stevelosh.com/'>Steve Losh</a><nav><a href='../../../index.html'>Blog</a> - <a href='https://stevelosh.com/projects/'>Projects</a> - <a href='https://stevelosh.com/photography/'>Photography</a> - <a href='https://stevelosh.com/links/'>Links</a> - <a href='https://stevelosh.com/rss.xml'>Feed</a></nav></header><hr class='main-separator' /><main id='page-blog-entry'><article><h1><a href='index.html'>A Git User's Guide to Mercurial Queues</a></h1><p class='date'>Posted on August 10th, 2010.</p><p>I've been using <a href="http://mercurial.selenic.com/wiki/MqExtension">Mercurial Queues</a> more and more lately. At the last
|
||
|
Mercurial sprint <a href="http://cs.ubc.ca/~brendan/">Brendan Cully</a> said something that made me realize
|
||
|
that MQ behaves very much like a souped-up version of <a href="http://git-scm.com/">git</a>'s index.</p>
|
||
|
|
||
|
<p>I wanted to write a blog post about the similarities between the two concepts
|
||
|
so that git users could understand <a href="http://mercurial-scm.org/">Mercurial</a>'s MQ extension a bit better
|
||
|
and see how it can take that concept even further than git does.</p>
|
||
|
|
||
|
<p>This post is <em>not</em> intended to be a guide to MQ's day-to-day commands — it's
|
||
|
simply trying to explain MQ in a way that git users might find more
|
||
|
understandable. For a primer on MQ commands you can check out <a href="http://hgbook.red-bean.com/read/managing-change-with-mercurial-queues.html">the MQ chapter
|
||
|
in the hg book</a>.</p>
|
||
|
|
||
|
<ol class="table-of-contents"><li><a href="index.html#s1-git-basics">Git Basics</a></li><li><a href="index.html#s2-mercurial-basics">Mercurial Basics</a></li><li><a href="index.html#s3-using-mq-with-a-single-patch">Using MQ with a Single Patch</a></li><li><a href="index.html#s4-using-mq-with-two-or-more-patches">Using MQ with Two (or More) Patches</a></li><li><a href="index.html#s5-multiple-patch-queues">Multiple Patch Queues</a></li><li><a href="index.html#s6-versioned-patch-queues">Versioned Patch Queues</a></li><li><a href="index.html#s7-problems-with-mq">Problems with MQ</a></li></ol>
|
||
|
|
||
|
<h2 id="s1-git-basics"><a href="index.html#s1-git-basics">Git Basics</a></h2>
|
||
|
|
||
|
<p>Let's take a few moments to review how git works so we're all on the same page
|
||
|
with our terminology.</p>
|
||
|
|
||
|
<p>When you're working with a git repository you have three "layers" to work with:</p>
|
||
|
|
||
|
<ul>
|
||
|
<li>The working directory</li>
|
||
|
<li>The index</li>
|
||
|
<li>The git repository</li>
|
||
|
</ul>
|
||
|
|
||
|
<p>You use <code>git add</code> to shove changes from the working directory into the index
|
||
|
and <code>git commit</code> to shove changes from the index into the repository:</p>
|
||
|
|
||
|
<p><img class="diagram" src="../../../../static/images/blog/2010/08/git-basics.png" alt="Git Basics"></p>
|
||
|
|
||
|
<p>This is a very powerful model because it lets you build your changesets
|
||
|
piece-by-piece and commit them permanently only when you're ready.</p>
|
||
|
|
||
|
<h2 id="s2-mercurial-basics"><a href="index.html#s2-mercurial-basics">Mercurial Basics</a></h2>
|
||
|
|
||
|
<p>With basic, stock Mercurial you only have two "layers" to work with:</p>
|
||
|
|
||
|
<ul>
|
||
|
<li>The working directory</li>
|
||
|
<li>The Mercurial repository</li>
|
||
|
</ul>
|
||
|
|
||
|
<p>You use <code>hg commit</code> to shove changes from the working directory into the
|
||
|
repository:</p>
|
||
|
|
||
|
<p><img class="diagram" src="../../../../static/images/blog/2010/08/mercurial-basics.png" alt="Mercurial Basics"></p>
|
||
|
|
||
|
<p>This model doesn't give you as much flexibility in creating changesets as
|
||
|
git's does. You can use the <a href="http://mercurial.selenic.com/wiki/RecordExtension">record extension</a> to get closer, but it's still
|
||
|
not the same.</p>
|
||
|
|
||
|
<p>Let's take a look at MQ to see how it can give us everything git's index does
|
||
|
<em>and more.</em></p>
|
||
|
|
||
|
<h2 id="s3-using-mq-with-a-single-patch"><a href="index.html#s3-using-mq-with-a-single-patch">Using MQ with a Single Patch</a></h2>
|
||
|
|
||
|
<p>The most basic way to use MQ is to create a single patch with <code>hg qnew NAME</code>.
|
||
|
You can make changes in your working directory and use <code>hg qrefresh</code> (or <code>hg
|
||
|
qrecord</code>) to put them into the patch. Once you're done with your patch and
|
||
|
ready for it to become a commit you can run <code>hg qfinish</code>:</p>
|
||
|
|
||
|
<p><img class="diagram" src="../../../../static/images/blog/2010/08/mq-one.png" alt="MQ with One Patch"></p>
|
||
|
|
||
|
<p>This looks a lot like the diagram of how git works, doesn't it? MQ gives you an
|
||
|
"intermediate" area to put changes, similar to how git's index works.</p>
|
||
|
|
||
|
<h2 id="s4-using-mq-with-two-or-more-patches"><a href="index.html#s4-using-mq-with-two-or-more-patches">Using MQ with Two (or More) Patches</a></h2>
|
||
|
|
||
|
<p>This single "intermediate" area is where git stops. For many workflows it's
|
||
|
enough, but if you want more power MQ has you covered.</p>
|
||
|
|
||
|
<p>MQ is called Mercurial <em>Queues</em> for a reason. You can have more than one patch
|
||
|
in your queue, which means you can have multiple "intermediate" areas if you
|
||
|
need them.</p>
|
||
|
|
||
|
<p>For example: say you're adding a feature that requires some API changes to your
|
||
|
project. You'd like to commit the changes to the API in one changeset, and the
|
||
|
changes to the interface in another changeset. You can do this by creating two
|
||
|
patches with <code>hg qnew api-changes; hg qnew interface-changes</code>:</p>
|
||
|
|
||
|
<p><img class="diagram" src="../../../../static/images/blog/2010/08/mq-two.png" alt="MQ with Two Patches"></p>
|
||
|
|
||
|
<p>You can move back and forth between these patches with <code>hg qpop</code> and <code>hg
|
||
|
qpush</code>. If you're working on the interface and realize you forgot to make
|
||
|
a necessary change to the API you can:</p>
|
||
|
|
||
|
<ul>
|
||
|
<li><code>hg qpop</code> the <code>interface-changes</code> patch to get to the <code>api-changes</code> patch.</li>
|
||
|
<li>Make your API changes.</li>
|
||
|
<li><code>hg qrefresh</code> to put those changes into the <code>api-changes</code> patch.</li>
|
||
|
<li><code>hg qpush</code> to get back to work on the interface.</li>
|
||
|
</ul>
|
||
|
|
||
|
<p>Using multiple patches is like having multiple git indexes to store related
|
||
|
changes until you're ready to commit them permanently.</p>
|
||
|
|
||
|
<h2 id="s5-multiple-patch-queues"><a href="index.html#s5-multiple-patch-queues">Multiple Patch Queues</a></h2>
|
||
|
|
||
|
<p>What happens when you want to work on two features, each with two patches, at
|
||
|
the same time? You <em>could</em> simply create four patches and let the second
|
||
|
feature live on top of the first, but there's a better way.</p>
|
||
|
|
||
|
<p>Mercurial 1.6 (I think) added the <code>hg qqueue</code> command, which lets you create
|
||
|
<em>multiple</em> patch queues, each one living in its own directory. That means you
|
||
|
can create a separate queue (with its own set of patches) with <code>hg qqueue -c
|
||
|
NAME</code> for each feature:</p>
|
||
|
|
||
|
<p><img class="diagram" src="../../../../static/images/blog/2010/08/mq-multiple.png" alt="MQ with Multiple Queues"></p>
|
||
|
|
||
|
<p>You can switch patch queues with <code>hg qqueue NAME</code>. This gives you multiple
|
||
|
sets of "intermediate" areas like git's index to work with. This is probably
|
||
|
not something you'll need very often, but it's there when you <em>do</em> need it.</p>
|
||
|
|
||
|
<p>You can see that MQ is already quite a bit more flexible than git's index, but
|
||
|
it has one more trick up its sleeve.</p>
|
||
|
|
||
|
<h2 id="s6-versioned-patch-queues"><a href="index.html#s6-versioned-patch-queues">Versioned Patch Queues</a></h2>
|
||
|
|
||
|
<p>Let me prefix this section by saying: "You might think you need to use versioned
|
||
|
patch queues, but you probably don't." Versioned queues can be tricky to wrap
|
||
|
your head around, but once you understand them you'll realize how powerful they
|
||
|
can be.</p>
|
||
|
|
||
|
<p>Let's pause a second to look at how MQ actually stores its patches.</p>
|
||
|
|
||
|
<p>When you create a new patch with <code>hg qnew interface-changes</code> Mercurial will
|
||
|
create a <code>patches</code> folder inside the <code>.hg</code> folder of your project. Your new
|
||
|
patch is stored in a file inside that folder (along with some other metadata
|
||
|
files):</p>
|
||
|
|
||
|
<pre><code>yourproject/
|
||
|
|
|
||
|
+-- .hg/
|
||
|
| |
|
||
|
| +-- patches/
|
||
|
| | |
|
||
|
| | +-- api-changes
|
||
|
| | +-- interface-changes
|
||
|
| | `-- ... other MQ-related files ...
|
||
|
| |
|
||
|
| `-- ... other Mercurial-related files ...
|
||
|
|
|
||
|
`-- ... your project's files ...</code></pre>
|
||
|
|
||
|
<p>When you create a new patch queue with <code>hg qqueue -c some-feature</code> Mercurial
|
||
|
creates a completely separate <code>patches-some-feature</code> folder in <code>.hg</code>:</p>
|
||
|
|
||
|
<pre><code>yourproject/
|
||
|
|
|
||
|
+-- .hg/
|
||
|
| |
|
||
|
| +-- patches/
|
||
|
| | |
|
||
|
| | +-- api-changes
|
||
|
| | +-- interface-changes
|
||
|
| | `-- ... other MQ-related files ...
|
||
|
| |
|
||
|
| +-- patches-some-feature/
|
||
|
| | |
|
||
|
| | +-- api-changes
|
||
|
| | +-- interface-changes
|
||
|
| | `-- ... other MQ-related files ...
|
||
|
| |
|
||
|
| `-- ... other mercurial-related files ...
|
||
|
|
|
||
|
`-- ... your project's files ...</code></pre>
|
||
|
|
||
|
<p>These folders are normal filesystem folders. The patches inside them are
|
||
|
plain-text files.</p>
|
||
|
|
||
|
<p>This gives them a very important property:</p>
|
||
|
|
||
|
<p><strong>They can be turned into Mercurial repositories to track changes.</strong></p>
|
||
|
|
||
|
<p>Once you understand what this means, you should have several "oh my god"
|
||
|
moments where you realize several very interesting things:</p>
|
||
|
|
||
|
<ul>
|
||
|
<li>Not only can you have multiple sets of "intermediate" areas to work with, you
|
||
|
can <em>version</em> them to keep track of the changes!</li>
|
||
|
<li>You can share queues with other people with Mercurial's vanilla push/pull
|
||
|
commands.</li>
|
||
|
<li>You can collaborate with other people and merge your changes to these
|
||
|
"intermediate" areas with Mercurial's vanilla push/pull/merge commands.</li>
|
||
|
<li>You can <code>hg serve</code> these patch repositories so other people can see what your
|
||
|
patches look like before they've been permanently committed to your project's
|
||
|
repository.</li>
|
||
|
</ul>
|
||
|
|
||
|
<p>Versioning patch queues means you can end up with a (hard to read) diagram like
|
||
|
this:</p>
|
||
|
|
||
|
<p><img class="diagram" src="../../../../static/images/blog/2010/08/mq-versioned.png" alt="Versioned Queues"></p>
|
||
|
|
||
|
<p>To facilitate working with versioned patch queues all Mercurial commands come
|
||
|
with a <code>--mq</code> option to apply the command to the queue repository instead of
|
||
|
the current one (so you don't need to <code>cd</code> to the queue repository all the
|
||
|
time).</p>
|
||
|
|
||
|
<p>Versioning patch queues is an <em>incredibly</em> powerful concept, and most of the
|
||
|
time you won't need it, but it's nice to have it when you do.</p>
|
||
|
|
||
|
<p>It's also nice to know that <a href="http://bitbucket.org/">BitBucket</a> has <a href="http://ches.nausicaamedia.com/articles/technogeekery/using-mercurial-queues-and-bitbucket-org">special support</a> for version
|
||
|
patch queues.</p>
|
||
|
|
||
|
<h2 id="s7-problems-with-mq"><a href="index.html#s7-problems-with-mq">Problems with MQ</a></h2>
|
||
|
|
||
|
<p>Despite how powerful MQ is (or perhaps <em>because</em> of it) it has some problems.
|
||
|
Most could be fixed if someone had a week or two to spend on it, but so far no
|
||
|
one has stepped up.</p>
|
||
|
|
||
|
<p>I'd do it if I could afford to take a week away from full-time/freelance work,
|
||
|
but I can't right now. If you want to be the hero of at least one MQ user (me)
|
||
|
here's what sucks about MQ:</p>
|
||
|
|
||
|
<ul>
|
||
|
<li>There's no easy way to pull changes <em>out</em> of an MQ patch.</li>
|
||
|
<li>There's no easy way to split an MQ patch into two patches (the current
|
||
|
horrible "workaround" is to empty the current patch, <code>hg qrecord</code> part one,
|
||
|
and <code>hg qnew</code> to make a new patch with part two).</li>
|
||
|
<li>You can't pop a patch once you've made changes in your working directory. You
|
||
|
need to <a href="http://mercurial.selenic.com/wiki/ShelveExtension">shelve</a> the changes, pop the patch, and then unshelve the changes.</li>
|
||
|
</ul>
|
||
|
|
||
|
<p>In addition there's another MQ-related change that would be very, very nice:</p>
|
||
|
|
||
|
<ul>
|
||
|
<li>Refactor the record extension and put it into core Mercurial, so that
|
||
|
<code>hg qrecord</code> could be used without an extra extension (and we could have <code>hg
|
||
|
commit --interactive</code>).</li>
|
||
|
</ul>
|
||
|
|
||
|
<p>If someone takes the time to fix these problems I'll love them forever. Until
|
||
|
then we're stuck with the powerful-but-sometimes-clumsy MQ interface.</p>
|
||
|
|
||
|
<p>Hopefully this post has given git users (and Mercurial users) an idea of how
|
||
|
powerful MQ can be. If you have any questions please find me on
|
||
|
<a href="http://twitter.com/stevelosh/">Twitter</a> and me know!</p>
|
||
|
</article></main><hr class='main-separator' /><footer><nav><a href='https://github.com/sjl/'>GitHub</a> ・ <a href='https://twitter.com/stevelosh/'>Twitter</a> ・ <a href='https://instagram.com/thirtytwobirds/'>Instagram</a> ・ <a href='https://hg.stevelosh.com/.plan/'>.plan</a></nav></footer></body></html>
|