37

I use Tortoise SVN usuallly, but I have been looking into Mercurial since it is a distributed revision control system.

What I am looking for, in both systems, is a tool that let me pick only parts of a file and commit them. If I want to do this now, I have to copy to a temp version of the file and keep only the changes I want to commit in the current version, and then copy the temp version to the current version again after committing. It's just such a hassle and the program should be able to do this for me.

I heard Git supports this, please let me know if this is correct.

bahrep
  • 29,961
  • 12
  • 103
  • 150
RobbieGee
  • 1,581
  • 4
  • 15
  • 17
  • 1
    It should be fairly easy to develop such a tool for SVN. I wonder why nobody did this yet. – Pavel Radzivilovsky Jun 16 '10 at 11:51
  • @PavelRadzivilovsky patches welcome. I mean that you can write a patch and send it to the dev@ mailing list. – bahrep Sep 08 '17 at 06:32
  • There is now a built-in `interactive` commit utility for this exact purpose which comes out of the box with Mercurial. See my answer below. – Oly Sep 18 '17 at 14:10

6 Answers6

40

Mercurial can do this with the record extension.

It'll prompt you for each file and each diff hunk. For example:

% hg record
diff --git a/prelim.tex b/prelim.tex
2 hunks, 4 lines changed
examine changes to 'prelim.tex'? [Ynsfdaq?]  
@@ -12,7 +12,7 @@
 \setmonofont[Scale=0.88]{Consolas}
 % missing from xunicode.sty
 \DeclareUTFcomposite[\UTFencname]{x00ED}{\'}{\i}
-\else
+\else foo
 \usepackage[pdftex]{graphicx}
 \fi

record this change to 'prelim.tex'? [Ynsfdaq?]  
@@ -1281,3 +1281,5 @@
 %% Local variables:
 %% mode: latex
 %% End:
+
+foo
\ No newline at end of file
record this change to 'prelim.tex'? [Ynsfdaq?]  n
Waiting for Emacs...

After the commit, the remaining diff will be left behind:

% hg di
diff --git a/prelim.tex b/prelim.tex
--- a/prelim.tex
+++ b/prelim.tex
@@ -1281,3 +1281,5 @@
 %% Local variables:
 %% mode: latex
 %% End:
+
+foo
\ No newline at end of file

Alternatively, you may find it easier to use MQ (Mercurial Queues) to separate the individual changes in your repository into patches. There is a MQ variant of record (qrecord), too.

Update: Also try the crecord extension, which provides a curses interface to hunk/line selection.

crecord screenshot

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Nicholas Riley
  • 43,532
  • 6
  • 101
  • 124
  • 1
    Shame there isn't a GUI tool for this. It's wonderful to do this with GitX. – mxcl Feb 10 '10 at 17:20
  • Amen. GitX is *the* reason I use Git; wish Murky was as capable. (Unfortunately I'm too overloaded with open source projects to fix it, though I have contributed to both GitX and Murky :-) – Nicholas Riley Feb 11 '10 at 00:16
  • 2
    There is a GUI tool for this: Tortoisehg supports hunk selection. – Hauge Mar 01 '10 at 16:55
  • …and having now used TortoiseHg, it lacks the ability to revert a hunk, which GitX provides, and in general provides a much less useful interface. The ability to easily remove unintended (e.g. whitespace-only) changes from a diff is pretty much essential... – Nicholas Riley Jun 23 '10 at 15:23
  • The Record extension also lacks the ability to edit the hunk before committing it. A very useful feature that is hard to live without, IMHO. – bambams Feb 29 '12 at 20:59
  • 5
    Not being able to edit the hunk is a good thing. I'm not a fan of "features" that let my developers commit code that they haven't actually built or tested. By the time they're running `hg record`, individual hunks _should_ be treated as immutable; if they need to be edited further, then it's time to abort the commit. – Ti Strga Feb 27 '14 at 20:41
12

Yes, git allows you to do this. The git add command has a -p (or --patch) option that allows you to review your changes hunk-by-hunk, select which to stage (you can also refine the hunks or, edit the patches in place). You can also use the interactive mode to git-add (git add -i) and use the "p" option.

Here's a screencast on interactive adding which also demonstrates the patch feature of git add.

Pat Notz
  • 208,672
  • 30
  • 90
  • 92
8

Check out TortoiseHG, which will do hunk selection and let you commit different changes to one file as to different commits.

It will even let you commit all changes to some files together with partial changes to other files in one commit.

http://tortoisehg.bitbucket.io/

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Hauge
  • 1,709
  • 15
  • 25
5

I asked a similar question just a little while ago, and the resulting answer of using the hgshelve extension was exactly what I was looking for.

Before you do a commit, you can put changes from different files (or hunks of changes within a file) on the "shelf" and then commit the things you want. Then you can unshelve the changes you didn't commit and continue working.

I've been using it the past few days and like it a lot. Very easy to visualize and use.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Ted Naleid
  • 26,511
  • 10
  • 70
  • 81
2

Mercurial now provides an option --interactive (or -i) to the commit command, which enables this functionality right out of the box.

This works directly from the command-line so it's perfect if you are a command-line enthusiast!

Running

> hg commit -i

begins an interactive session which allows examination, editing and recording of individual changes to create a commit.

This behaves very similarly to the --patch and --interactive options for the git add and git commit commands.

Oly
  • 2,329
  • 1
  • 18
  • 23
0

I would recommend not working like this.

If you have to sets of changes, set A which is ready to check in and set B which is not ready yet, how can you be sure that only checking in set A will not break your build/tests? You may miss some lines, forget about lines in a different file, or not realize a dependency that A has on B breaking the build for others.

Your commits should be discreet atomic changes that don't break the build for you or others on you team. If you are partially committing a file you are greatly increasing the chances you will break the build for others without knowing about it until you've got some unhappy coworker knocking on your door.

The big question is, why do you feel the need to work this way?

craigb
  • 16,827
  • 7
  • 51
  • 62
  • Git allows this by grouping adjacent lines together. It can be REALLY handy if you need to check in something simple and don't want to make a branch. – davetron5000 Sep 29 '08 at 16:15
  • 7
    Usually, it is because I write too much code before committing. I might change two different things in a file and want to commit them with different comments. – RobbieGee Sep 29 '08 at 17:13
  • 3
    Git supports (with git stash --keep-index) isolating only the changes that you are about to commit, which lets you run unit tests or whatever on them easily. – Greg Hewgill Sep 29 '08 at 18:43
  • 1
    Sound advise for code monkeys, but us smarter types can cope with splicing a diff up and committing it in chunks. – mxcl Feb 10 '10 at 17:12
  • 12
    From VisionMap development conventions: "There’s no worse reviewer’s nightmare than a combination of one dangerous change with a thousand trivial ones. In this sense, a meaningful change must be thoroughly purified of any obscuring litter" – Pavel Radzivilovsky Jun 16 '10 at 11:50
  • See Ryan Tomayko's (creator of GitHub) post about it: http://tomayko.com/writings/the-thing-about-git – Yawar Sep 06 '10 at 19:40
  • 2
    The case I always run into when I want to be able to do this is in .csproj files. Since every file included in a C# project is included in the .csproj file, if I want to make a commit that adds NewFile1.cs and NOT NewFile2.cs, then I have to split the .csproj file into 2 commits. – CodingWithSpike May 26 '11 at 19:57
  • @rally25rs -- Long time since I've looked at this question, but I'm in agreement with you. I guess I always put it down to my lack of self discipline or some such when running into these 'edge cases', but looking at it again I can see how this would have been really useful to be able to do. – craigb May 30 '11 at 19:15
  • 8
    This is a problem for Subversion because a commit affects everyone, but for distributed systems like Git and Mercurial it's not a problem at all. You can split your commits up and test them each before actually distributing your changes. If you're really 1337 you can even edit the history afterward to clean up any screw ups so nobody else needs to ever know they happened. As mentioned, this can still be done in Subversion if the person doing it is competent enough. The centralized model guarantees that things will inevitably be broken in Subversion repos though. – bambams Feb 29 '12 at 21:02
  • 3
    It's not always a choice. Sometimes it's late and you abandon a change until another day, get caught up in another feature/change, before realizing what's happened. We're human. Our VCS should handle our clumsy workings. This will happen, even if by mistake. Therefore, it is necessary to have such a feature. http://tomayko.com/writings/the-thing-about-git – ventaur Sep 16 '12 at 18:07
  • @craigb One, very common use case, is changing a config line in a file (server it points to, or turning debug logging on) and you don't want that line in the repo. I also like to move all my formatting changes out of changes that actually do something. As someone else pointed out, there's nothing worse than tring to find something in a patch littered with non-essential changes. Yes, you are correct that you shouldn't be shipping code until you've run what is in your repo's HEAD. – Jim Keener May 13 '14 at 14:08
  • 2
    I disagree that hunk based commits interfere with atomicity. Often they actually help that cause.Eg: a changed working copy that includes something not relevant to a commit. Hunking the commit actually encourages atomicity by keeping irrelevant change out of the commit. – grenade Nov 13 '15 at 20:00