Mercurial cherry picking changes for commit
Asked Answered
J

10

61

Say, I made many changes to my code and only need to commit a few of those changes. Is there a way to do it in mercurial? I know that darcs has a feature like this one.

I know hg transplant can do this between branches, but I need something like this for committing code in the present branch and not when adding change sets from some other branch.

Jorge answered 12/5, 2009 at 21:23 Comment(1)
I asked this recently as a duplicate. I wasn't aware of the terminology so I did not find this when searching. See the accepted answer on my question which involves hg graft, new in Mercurial 2.0. #10288982Felizio
A
34

MQ as Chad mentioned are one way. There's also more lightweight solutions:

  • Record extension which works roughly the same way as darcs record. It's distributed with mercurial.
  • Shelve extension which allows you to "shelve" certain changes, allowing you to commit only a subset of your changes (the ones that are not shelved)
Avelar answered 13/5, 2009 at 12:4 Comment(1)
see additional information in this answer: https://mcmap.net/q/324101/-hg-equivalent-of-git-add-pColorist
F
37

If you are using TortoiseHg 1.x for Windows, this feature is implemented beautifully right out of the box (no extensions required).

  1. Run the TortoiseHg Commit Tool.
  2. Choose a file for which you only want to commit a subset of its changes.
  3. Click on the Hunk Selection tab in the preview pane.
  4. Double-click or use the spacebar to toggle which change hunks should be included in the commit.

In TortoiseHg 2, the Hunk Selection tab was temporarily removed. In its place was the Shelve tool. It has a few more features than the old hunk selection. Those new features come at the cost of some added complexity.

enter image description here

Note that there is no need to explicitly enable the Mercurial Shelve extension when using this feature. According to Steve Borho (lead TortoiseHg developer) in response to a different TortoiseHg question: "We have a local copy of the shelve extension and call into it directly."


For TortoiseHg 2.7+, this functionality has been improved and re-introduced. It is now built directly into the Commit tool:

example of change selection in the commit tool

Notice in the file list on the left that the top file is checked to indicate it will be included, the second file is unchecked because it will not be included, and the third file, Sample.txt, is filled (the Null checkbox indicator) because only select changes from that file will be included in the commit.

The change to Sample.txt that will be included is checked in the lower-right change selection portion of the image. The change that will be excluded is unchecked and the diff view is grayed out. Also notice that the icon for the shelve tool is still readily available.

Fawkes answered 5/10, 2010 at 13:35 Comment(7)
Aha, so that's why I didn't get why this was a question. This isn't easily done if you're not using TortoiseHg. I didn't know that.Yetta
What about those of us that are using version 2? tortoisehg.bitbucket.org/manual/2.0/commit.html doesn't have a hunk selection tab that I can see.Flareup
@Flareup and others: Starting with TortoiseHg 2.7, the old workflow from TortoiseHg 1.x has been re-introduced. See updated answer for details.Fawkes
Has anyone seen the hunk selection failing to show hunks with files that have a lot of changes? Instead of multiple hunks, the commit and shelve diffs are showing one big diff. External diff applications show the individual diffs just fine. This only started happening in the past month.Cuprum
Bit of a resurrect but I noticed in THG 3.3 this is now removed from commit and is back in shelve. Out of curiousity anybody know the reason for this?Bisque
I just updated from THG 3.1.2 to THG 3.4.1 and the commit functionality with hunk selection appears unchanged. @Bisque if you update to 3.4.1 (latest stable as of this writing) do you see the functionality in commit? Perhaps it was removed and re-introduced somewhere between 3.1.2 and 3.4.1, but I'm not curious enough to run through several old installs...Fawkes
Embarassing - I last time I checked (on 3.3) I was using patches (mq). The commit tool does work as you've said (just not in patches), and probably did in the last version too.ThanksBisque
A
34

MQ as Chad mentioned are one way. There's also more lightweight solutions:

  • Record extension which works roughly the same way as darcs record. It's distributed with mercurial.
  • Shelve extension which allows you to "shelve" certain changes, allowing you to commit only a subset of your changes (the ones that are not shelved)
Avelar answered 13/5, 2009 at 12:4 Comment(1)
see additional information in this answer: https://mcmap.net/q/324101/-hg-equivalent-of-git-add-pColorist
H
19

I feel like I'm missing something because nobody has suggested this already.

The normal "hg commit" command can be used to selectively choose what to commit (you don't have to commit all pending changes in the local working directory).

If you have a set of changes like so:

M ext-web/docroot/WEB-INF/liferay-display.xml
M ext-web/docroot/WEB-INF/liferay-portlet-ext.xml
M ext-web/docroot/WEB-INF/portlet-ext.xml

You can commit just two of those changes with...

hg commit -m "partial commit of working dir changes" ext-web/docroot/WEB-INF/liferay-display.xml ext-web/docroot/WEB-INF/liferay-portlet-ext.xml

Not super convenient from the command line because you have to hand-type the files to selectively commit (vs a GUI check-box process like tortoise) but it's about as straightforward as it gets and requires no extensions. And file-globbing can probably help reduce typing (as it would above, both committed files uniquely share "liferay" in their pathnames.

Hedi answered 26/12, 2011 at 21:8 Comment(1)
Many of the other answers deal with only select changes from within each file (though it's not clear if this is required from the question, so have an upvote)Pulpwood
R
16

The Mercurial Queues tutorial is terrible for this use case. All the examples I have seen assume you have yet to make a commit and you are refreshing a single patch. Most of the time this is not the case, and you have 2 or 3 commits that you want to squash together or change in some other way.

Lets say you have this sort of history:

---O---O---A---B---C

The first example is to squash commits A, B, and C. First init mq:

$ hg qinit

Now we need to "import" the commits A, B and C into the patch queue. Lets assume they are the last 3 commits. We can use the "-N" revision syntax to import them like so:

$ hg qimport -r -3:-1

That means import as patches from 3 patches back up to the last commit. You can check the status of these patches with hg qseries. It should show something like this:

$ hg qseries
101.diff
102.diff
103.diff

Where the numbers 101, 102 and 103 correspond to the local revision numbers of the commits A, B and C. Now these patches are applied, which means the changes that they describe are already in the working copy. You can get rid of the changes the working copy and remove them from the history of commits, saving them in patch form only, by using hg qpop. You can either say hg qpop; hg qpop to pop changes C and B off the stack, or specify a patch to "pop to". In this case, it would be something like this:

$ hg qpop 101.diff
now at: 101.diff

You now have the patches for commits B and C in the patch queue, but they are not applied (their changes have been "lost" - they only exist in the patch queue area). Now you can fold these patches into the last one, i.e. we create a new commit that is the equivalent of the sum of the changes A+B+C.

$ hg qfold -e 102.diff 103.diff

This will show your editor so you can change the commit message. By default the message will be the concatenation of the commit messages for the changes A, B and C, separated by asterisks. The nice thing here is that hg qfold will tab-complete the patches if you are using bash and have the hg-completion script sourced. This leaves the history like this, where A+B+C is a single commit that is the combination of the 3 patches that interest us:

---O---O---A+B+C

Another use case is if we have the same sort of history as before, but we want to drop patch B and merge A+C. This is pretty similar to above actually. When you get to the qfold step, you would simply fold in the last commit rather than the last 2 commits:

$ hg qfold -e 103.diff

This leaves the change for B in the patch queue, but it is not applied to the working copy and its commit is not in the history. You can see this by running:

$ hg qunapplied
102.diff

The history now looks like this, where A+C is a single commit that combines changes A and C:

---O---O---A+C

A final use case might be that you need to apply only commit C. You'd do this by running the qimport as above, and you would pop off all patches you didn't want:

$ hg qpop -a

The -a flag means pop off all patches. Now you can apply just the one you do want:

$ hg qpush 103.diff

This leaves you with this history:

---O---O---C

Once you are done with all this, you need to finish off the queue fiddling. This can be done with:

$ hg qfinish -a

So there we are. You can now run hg push and only commit exactly what you want, or hg email a coherent patch to the mailing list.

Rosas answered 13/5, 2009 at 20:35 Comment(0)
M
10

Some time has passed. Seems the best option now is hg commit --interactive

Minus answered 7/2, 2017 at 7:28 Comment(0)
P
7

You can use the record extension, which is distributed with Mercurial.

You need to enable it in your ~/.hgrc file first, by adding it to the [extensions] section:

[extensions]
record=

Then, just type hg record instead of hg commit, and you will be able to select which changes to which files you want to commit.

You can also use the crecord extension which provides a nicer interface to review and select the changes. (It is not distributed with Mercurial, though, and I've seen it occasionally mess up a commit so it's not completely bug-free.)

Pavis answered 18/1, 2012 at 11:44 Comment(2)
crecord has an advantage over record in that you can select parts of a hunk. And the interface is much nicer too :DToupee
Record extension page says This extensions is deprecated, the feature is now part of Mercurial core as hg commit --interactive. See this answer.Luff
U
3

I believe Mercurial Queues fills this role for Mercurial. There's a pretty good tutorial linked there.

Unbated answered 12/5, 2009 at 21:28 Comment(1)
This seems to do what I want, however it would have been great if the process were more interactive like the hg transplant command.Jorge
L
2

Try qct (Qt Commit Tool). It has a "select changes" feature that starts up a 3-way merge tool for you to undo individual changes. After you commit, those changes you "undid" come back.

Loom answered 24/5, 2009 at 22:51 Comment(0)
M
2

I use commit-patch. It's a script that lets you edit the diff before committing. It's really nice with Emacs's diff-mode and vc-mode.

In the past I used crecord, but it has bugs related to unicode (actually the record extension has the bugs, which crecord depends on).

Malek answered 7/10, 2010 at 21:42 Comment(0)
I
-1

First you must forget everything you ever knew about GUI's and return to the commandline. Next from the commandline do this:

hg stat > filelist.txt

This pipes all your modified files into a text file called filelist.txt

Next edit your filelist to include only the files you wish to commit.

Finally commit using the fileset sytnax:

hg commit "set: 'listfile:test.txt'"

Irving answered 17/4, 2014 at 19:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.