git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: [PATCH] git-add --interactive (wip)

From
Junio C Hamano <junkio@cox.net>
Date
Dec 12, 2006, 21:51 UTC
Message-ID
<7vwt4wpytm.fsf@assigned-by-dhcp.cox.net>
In-Reply-To
<200612111147.44964.Josef.Weidendorfer@gmx.de>
Josef Weidendorfer <Josef.Weidendorfer@gmx.de> writes:
Show 7 quoted lines
> I just tried to work with this thing to separate my recent
> "branch.*.localmerge" changes to the simple version I posted
> afterwards.
>
> I noted that I need to manually change a few dependent hunks,
> so this "one hunk at a time" and not being able to edit changes
> in a hunk does not fit my workflow.

I've updated my "git add --interactive" in 'pu' and it now knows how to split a hunk into smaller pieces and recounting the diff offsets before applying (Johannes pointed out that his partial commit script, entirely written in bash using shell arrays, has a similar feature. I guess I should have stolen his code instead of lifting the logic from my own abandoned porcelain).

Hunk splitting allows you to start with this hunk:
        @@ -9,9 +9,10 @@
         #include "builtin.h"
         #include "dir.h"
         #include "cache-tree.h"
        +#include "exec_cmd.h"
         static const char builtin_add_usage[] =
        -"git-add [-n] [-v] <filepattern>...";
        +"git-add [-n] [-v] <filepattern>... | git-add --interactive";
         static void prune_directory(struct dir_struct *dir, const char **...
         {
and lets you split it into two, and you can pick each individually:
        @@ -9,5 +9,6 @@
         #include "builtin.h"
         #include "dir.h"
         #include "cache-tree.h"
        +#include "exec_cmd.h"
         static const char builtin_add_usage[] =
        @@ -12,6 +13,6 @@
         static const char builtin_add_usage[] =
        -"git-add [-n] [-v] <filepattern>...";
        +"git-add [-n] [-v] <filepattern>... | git-add --interactive";
         static void prune_directory(struct dir_struct *dir, const char **...
         {

The post-context of the first hunk and pre-context of the second hunk are duplicated to give you better context when reviewing each of them alone. If you choose to apply both, they are merged back again before applied.

Anyway, after I finished doing this, I started thinking...

One of the most beautifully done patch series in git.git history is this series by Sergey Vlasov:

	http://thread.gmane.org/gmane.comp.version-control.git/9057
The resulting commits are from e8d2a6d4 to 4041483e (inclusive).

What's interesting about this series is that its patches touch the same area of the code over and over again, refining a bit by bit as the series progresses.

Sergey might have done it exactly the way the final patches presented the evolution, but I somehow doubt it [*1*]. I often have the state that corresponds to [PATCH n/n] in my working tree after hacking around without making a single commit, and often would start wanting to commit the state as a clean multi-step series at that point. I think this is the same for everybody.

What I often end up doing is to make a snapshot of that "proposed final" state, reset to the original, and work toward the proposed final state:

	# snapshot
	$ git commit -a -m 'commit goal'
        $ git tag -f CG
        $ git reset --hard HEAD^
	# repeat for (i = 1; i <= n; i++)
        $ git diff HEAD CG >P.diff
        $ edit
        # use P.diff as a reference, and edit the working tree
        # files to produce [i/n] state.  I might remove pieces
        # that belong to [j/n] for j > i from  P.diff and run
	# "git apply --index", or hand edit the files directly
	# in the working tree.
	$ make test
        $ git commit -a -m "[PATCH i/n] title..."
        # end repeat
	# when finished the above cycle, this diff should only
	# show the improvements I made during the decomposition.
        $ git diff CG HEAD

For a very simple case where the patch munging in the "repeat" part of the above workflow only deletes hunks (or sub-hunks), the patch subcommand of "git add --interactive" with hunk splitting would work nicely, but in general it would not be enough. It certainly is far from usable to produce something like what Sergey gave us.

To make it easier, one possibility might be to add a subcommand to "git add --interactive" that lets you edit what is currently staged in the index by opening a temporary copy in your favorite editor, and stage the result of your edit in the index. But I feel quite uneasy to introduce ways to update the index with something _wildly_ different from what you ever had in your working tree as a whole.

I think it is wrong to commit partially, purely from the index, when you are building a series that has complex changes that come during the series but go away at the end. The user should be able to verify all the steps in the middle in such a complex series, but it is not easy if you have it only in the index.

You could do
	$ git checkout-index --prefix=testarea/ -f -q -u -a

and run your tests there, but that takes a discipline, and is cumbersome to do.

So in short, I think per-hunk update-index is a cute hack and may be useful in a narrow simple cases, but it would not be so useful in the real life.

> Just as a sidenote: after deciding to not apply hunks, you
> lose them in this WIP, as you will find nothing in "unstaged" mode
> afterwards :-(

I do not understand this part. You can 'revert' to match the index to HEAD and run 'patch' to pick what you want again.

[Footnote]

*1* Maybe Sergey indeed did things the way the series was presented, in which case that would only strengthen my respect to his ability even more. The quoted patch series made him one of three people on the git list whose patches I trust 100%; in fact, I trust their patches so much that I read them not to find bugs but to admire and enjoy.

Previous: Josef WeidendorferNext: Johannes Schindelin
Message 12 of 20 in “Re: git-commit: select which files to commit while editing the commit message”
  1. Jakub NarebskiDec 8, 2006
  2. Luben TuikovDec 8, 2006
  3. Josef WeidendorferDec 8, 2006
  4. Seth FalconDec 8, 2006
  5. Jakub NarebskiDec 9, 2006
  6. Seth FalconDec 9, 2006
  7. Junio C HamanoDec 9, 2006
  8. Josef WeidendorferDec 10, 2006
  9. Junio C HamanoDec 10, 2006
  10. git-add --interactive (wip)Junio C Hamano, Dec 11, 2006
  11. Josef WeidendorferDec 11, 2006
  12. Junio C HamanoDec 12, 2006
  13. Johannes SchindelinDec 12, 2006
  14. Andreas EricssonDec 13, 2006
  15. Josef WeidendorferDec 13, 2006
  16. Junio C HamanoDec 13, 2006
  17. Johannes SchindelinDec 13, 2006
  18. Shawn PearceDec 13, 2006
  19. Johannes SchindelinDec 13, 2006
  20. Josef WeidendorferDec 13, 2006

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.