{"thread":{"id":"43083","subject":"Re: git-commit: select which files to commit while editing the commit message","startedAt":"2006-12-08T19:32:47Z","lastAt":"2006-12-13T20:31:13Z","messageCount":20,"participants":["Luben Tuikov","Shawn Pearce","Johannes Schindelin","Junio C Hamano","Jakub Narebski","Josef Weidendorfer","Andreas Ericsson","Seth Falcon"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"297953","messageId":"elcegl$qfh$1@sea.gmane.org","threadId":"43083","inReplyTo":"7vpsaui4cn.fsf@assigned-by-dhcp.cox.net","subject":"Re: git-commit: select which files to commit while editing the commit message","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-12-08T19:32:47Z","receivedAt":"2006-12-08T19:32:47Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote:\n\n> Pazu <pazu@pazu.com.br> writes:\n> \n>> # Please enter the commit message for your changes.\n>> # (Comment lines starting with '#' will not be included)\n>> # On branch refs/heads/next\n>> # Updated but not checked in:\n>> #   (will commit)\n>> #\n>> #    modified:   perl/Makefile\n>> #    modified:   var.c\n>>\n>> Here's where the magic would happen. Removing the line \"modified: var.c\" would\n>> remove var.c from this commit. Of course, the template message should be\n>> modified to tell the user he can do that.\n>>\n>> So, what do you think about this?\n> \n> Personally, I would refuse to use such a modified git, because\n> often the first thing I would do in the commit log buffer is\n> check the listed files and remove the '# ...' lines while\n> typing.  I do not want that to affect the set of changes I\n> staged in any way.\n> \n> But maybe that is just me.\n\nI was to raise the same objection. \n\nBut this can be solved by using magic _only_ if the template with exception\nof \"modified:\" lines matches, and if there is at least one file\nin \"modified:\" section.\n\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n\n"},{"id":"293824","messageId":"360959.72234.qm@web31809.mail.mud.yahoo.com","threadId":"43083","inReplyTo":"elcegl$qfh$1@sea.gmane.org","subject":"Re: git-commit: select which files to commit while editing the commit message","fromName":"Luben Tuikov","fromEmail":"ltuikov@yahoo.com","sentAt":"2006-12-08T20:45:59Z","receivedAt":"2006-12-08T20:45:59Z","isPatch":false,"sender":{"key":"ltuikov@yahoo.com","avatar":null},"body":"--- Jakub Narebski <jnareb@gmail.com> wrote:\n> Junio C Hamano wrote:\n> > Pazu <pazu@pazu.com.br> writes:\n> > \n> >> # Please enter the commit message for your changes.\n> >> # (Comment lines starting with '#' will not be included)\n> >> # On branch refs/heads/next\n> >> # Updated but not checked in:\n> >> #   (will commit)\n> >> #\n> >> #    modified:   perl/Makefile\n> >> #    modified:   var.c\n> >>\n> >> Here's where the magic would happen. Removing the line \"modified: var.c\" would\n> >> remove var.c from this commit. Of course, the template message should be\n> >> modified to tell the user he can do that.\n> >>\n> >> So, what do you think about this?\n> > \n> > Personally, I would refuse to use such a modified git, because\n> > often the first thing I would do in the commit log buffer is\n> > check the listed files and remove the '# ...' lines while\n> > typing.  I do not want that to affect the set of changes I\n> > staged in any way.\n> > \n> > But maybe that is just me.\n> \n> I was to raise the same objection. \n> \n> But this can be solved by using magic _only_ if the template with exception\n> of \"modified:\" lines matches, and if there is at least one file\n> in \"modified:\" section.\n\nI raise the same objection as Junio.\n\nThis is how perforce does it*, and while it is useful, git is NOT perforce,\nand I agree with Junio and Jakub.\n\nIf you want to commit only few files, update the index for only\nthe ones you want to commit.  If you did update the index for all\nof them, \"git-read-tree -m -i HEAD\" is your friend.\n\n    Luben\n\n* The reason being is that there is no \"index-cache\" and the commit\nmessage needs to be scanned to determine which of the edited files\nyou actually intend to commit to the server at this time.\n"},{"id":"297171","messageId":"200612082310.24140.Josef.Weidendorfer@gmx.de","threadId":"43083","inReplyTo":"360959.72234.qm@web31809.mail.mud.yahoo.com","subject":"Re: git-commit: select which files to commit while editing the commit message","fromName":"Josef Weidendorfer","fromEmail":"josef.weidendorfer@gmx.de","sentAt":"2006-12-08T22:10:23Z","receivedAt":"2006-12-08T22:10:23Z","isPatch":false,"sender":{"key":"josef.weidendorfer@gmx.de","avatar":null},"body":"On Friday 08 December 2006 21:45, Luben Tuikov wrote:\n> This is how perforce does it*, and while it is useful, git is NOT perforce,\n> and I agree with Junio and Jakub.\n\nHowever, the idea itself is not bad. AFAIK, cogito does it this way.\nIt could be done as separate command, e.g. \"git add --interactive\",\nand would only update the index.\n\n"},{"id":"298616","messageId":"m2lkli9bwv.fsf@ziti.local","threadId":"43083","inReplyTo":"200612082310.24140.Josef.Weidendorfer@gmx.de","subject":"Re: git-commit: select which files to commit while editing the commit message","fromName":"Seth Falcon","fromEmail":"sethfalcon@gmail.com","sentAt":"2006-12-08T23:59:44Z","receivedAt":"2006-12-08T23:59:44Z","isPatch":false,"sender":{"key":"sethfalcon@gmail.com","avatar":"https://gravatar.com/avatar/fd62fe73d3013b12fce71d5269ec52eaca3c4cee479efc6eea3603d6f1d8bf8d?d=mp&s=160"},"body":"Josef Weidendorfer <Josef.Weidendorfer@gmx.de> writes:\n\n> On Friday 08 December 2006 21:45, Luben Tuikov wrote:\n>> This is how perforce does it*, and while it is useful, git is NOT perforce,\n>> and I agree with Junio and Jakub.\n>\n> However, the idea itself is not bad. AFAIK, cogito does it this way.\n> It could be done as separate command, e.g. \"git add --interactive\",\n> and would only update the index.\n\nI think such a feature could be quite useful and it would seem to be\nan easy thing to provide in an optional or configurable fashion so\nthat those that don't like it could avoid it.  \n\nSpelling out a bunch of files spread around your tree for update-index\ncan be annoying.  Some way of marking a list seems natural.  Maybe\nthat is a separate issue.\n\n+ seth\n"},{"id":"295856","messageId":"elcujo$g61$1@sea.gmane.org","threadId":"43083","inReplyTo":"m2lkli9bwv.fsf@ziti.local","subject":"Re: git-commit: select which files to commit while editing the commit message","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-12-09T00:07:32Z","receivedAt":"2006-12-09T00:07:32Z","isPatch":false,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Seth Falcon wrote:\n\n> Josef Weidendorfer <Josef.Weidendorfer@gmx.de> writes:\n> \n>> On Friday 08 December 2006 21:45, Luben Tuikov wrote:\n>>> This is how perforce does it*, and while it is useful, git is NOT perforce,\n>>> and I agree with Junio and Jakub.\n>>\n>> However, the idea itself is not bad. AFAIK, cogito does it this way.\n>> It could be done as separate command, e.g. \"git add --interactive\",\n>> and would only update the index.\n> \n> I think such a feature could be quite useful and it would seem to be\n> an easy thing to provide in an optional or configurable fashion so\n> that those that don't like it could avoid it.  \n> \n> Spelling out a bunch of files spread around your tree for update-index\n> can be annoying.  Some way of marking a list seems natural.  Maybe\n> that is a separate issue.\n\nPerhaps git-commit should also accept --exclude=<pattern> option?\nWould that help?\n\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n\n"},{"id":"297417","messageId":"m2d56taoqu.fsf@ziti.local","threadId":"43083","inReplyTo":"elcujo$g61$1@sea.gmane.org","subject":"Re: git-commit: select which files to commit while editing the commit message","fromName":"Seth Falcon","fromEmail":"sethfalcon@gmail.com","sentAt":"2006-12-09T00:37:13Z","receivedAt":"2006-12-09T00:37:13Z","isPatch":false,"sender":{"key":"sethfalcon@gmail.com","avatar":"https://gravatar.com/avatar/fd62fe73d3013b12fce71d5269ec52eaca3c4cee479efc6eea3603d6f1d8bf8d?d=mp&s=160"},"body":"Jakub Narebski <jnareb@gmail.com> writes:\n\n> Seth Falcon wrote:\n>> Spelling out a bunch of files spread around your tree for update-index\n>> can be annoying.  Some way of marking a list seems natural.  Maybe\n>> that is a separate issue.\n>\n> Perhaps git-commit should also accept --exclude=<pattern> option?\n> Would that help?\n\nI don't think I understand what an --exclude=<pattern> option would\ndo, but I'm pretty sure it doesn't help the use case I'm thinking of:\n\n   Editing away, you've made changes in 8 files.\n\n   Reviewing diff, you want to commit 6 of those and continue working\n   on the other two.\n\n   It seems that there could be a less manual way than \n   git update-index f1 f2 ... f6\n\n\nHmm, maybe I could do:\n\n   git diff --name-only > changed\n   ## edit changed\n   cat changed|xargs git update-index\n\nI suppose this could be wrapped in a simple way to bring up an editor.\n\n"},{"id":"296884","messageId":"7vfybpg9yx.fsf@assigned-by-dhcp.cox.net","threadId":"43083","inReplyTo":"m2d56taoqu.fsf@ziti.local","subject":"Re: git-commit: select which files to commit while editing the commit message","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-12-09T00:59:50Z","receivedAt":"2006-12-09T00:59:50Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Seth Falcon <sethfalcon@gmail.com> writes:\n\n> I don't think I understand what an --exclude=<pattern> option would\n> do, but I'm pretty sure it doesn't help the use case I'm thinking of:\n>\n>    Editing away, you've made changes in 8 files.\n>\n>    Reviewing diff, you want to commit 6 of those and continue working\n>    on the other two.\n>\n>    It seems that there could be a less manual way than \n>    git update-index f1 f2 ... f6\n>\n>\n> Hmm, maybe I could do:\n>\n>    git diff --name-only > changed\n>    ## edit changed\n>    cat changed|xargs git update-index\n>\n> I suppose this could be wrapped in a simple way to bring up an editor.\n\nNote that output of cat piped to anything is almost always a bad\nprogramming ;-)\n\nMaybe the \"git add --interactive\" would give you a transcript\nlike this:\n\n        $ git add --interactive '*.c' '*.h'\n\tshowing list of modified files...\n         1) bozbar.c 2) filfre.c 3) frotz.h 4) nitfol.c 5) rezrov.h\n         6) xyzzy.c  7) yomin.h  8) z.c\n        choice> 2 3 5 6 7\n\tshowing list of modified files...\n         1) bozbar.c 2* filfre.c 3* frotz.h 4) nitfol.c 5* rezrov.h\n         6* xyzzy.c  7* yomin.h  8) z.c\n        choice> 4+\n\tshowing \"git diff -- nitfol.c\"...\n        diff --git a/nitfol.c b/nitfol.c\n        @@ -22,x +22,y @@\n         {\n        -\tint i;\n        +\tlong i;\n        +\ti = 314;\n\t \n        stage this hunk [Yes/No/All/Done]? y\n        ...\n \nThe latter half is to come up with a subset of diff and then run\n\"git-apply --cached\" to update only the index with the chosen\nhunks.\n"},{"id":"296216","messageId":"200612100146.09175.Josef.Weidendorfer@gmx.de","threadId":"43083","inReplyTo":"7vfybpg9yx.fsf@assigned-by-dhcp.cox.net","subject":"Re: git-commit: select which files to commit while editing the commit message","fromName":"Josef Weidendorfer","fromEmail":"josef.weidendorfer@gmx.de","sentAt":"2006-12-10T00:46:09Z","receivedAt":"2006-12-10T00:46:09Z","isPatch":false,"sender":{"key":"josef.weidendorfer@gmx.de","avatar":null},"body":"On Saturday 09 December 2006 01:59, Junio C Hamano wrote:\n> Maybe the \"git add --interactive\" would give you a transcript\n> like this:\n> \n>         $ git add --interactive '*.c' '*.h'\n> \tshowing list of modified files...\n>          1) bozbar.c 2) filfre.c 3) frotz.h 4) nitfol.c 5) rezrov.h\n>          6) xyzzy.c  7) yomin.h  8) z.c\n>         choice> 2 3 5 6 7\n> \tshowing list of modified files...\n>          1) bozbar.c 2* filfre.c 3* frotz.h 4) nitfol.c 5* rezrov.h\n>          6* xyzzy.c  7* yomin.h  8) z.c\n>         choice> 4+\n> \tshowing \"git diff -- nitfol.c\"...\n>         diff --git a/nitfol.c b/nitfol.c\n>         @@ -22,x +22,y @@\n>          {\n>         -\tint i;\n>         +\tlong i;\n>         +\ti = 314;\n> \t \n>         stage this hunk [Yes/No/All/Done]? y\n\nWow.\nSomething like this really would be good.\nAnd please a way to \"unstage\" accidently added files/hunks, too ;-)\n\nI even miss such a feature in stgit (\"specify hunks to break up one\npatch into two\").\n\n\n"},{"id":"295692","messageId":"7vd56s8t9w.fsf@assigned-by-dhcp.cox.net","threadId":"43083","inReplyTo":"200612100146.09175.Josef.Weidendorfer@gmx.de","subject":"Re: git-commit: select which files to commit while editing the commit message","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-12-10T00:54:35Z","receivedAt":"2006-12-10T00:54:35Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josef Weidendorfer <Josef.Weidendorfer@gmx.de> writes:\n\n> On Saturday 09 December 2006 01:59, Junio C Hamano wrote:\n>> Maybe the \"git add --interactive\" would give you a transcript\n>> like this:\n> ...\n> Something like this really would be good.\n\nI think I already had the code in jit --- I need to dig it out.\n\nBut the above is trivial for anybody who knows how to use\ngit-diff-index and \"git-apply --cached\", so...\n"},{"id":"298555","messageId":"7vslfm23co.fsf_-_@assigned-by-dhcp.cox.net","threadId":"43083","inReplyTo":"7vd56s8t9w.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH] git-add --interactive (wip)","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-12-11T09:23:19Z","receivedAt":"2006-12-11T09:23:19Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"A script to be driven when the user says \"git add --interactive\"\nis introduced.\n\nWhen it is run, first it runs its internal 'status' command to\nshow the current status, and then goes into its internactive\ncommand loop.\n\nThe command loop shows the list of subcommands available, and\ngives a prompt \"What now> \".  In general, when the prompt ends\nwith a single '>', you can pick only one of the choices given\nand type return, like this:\n\n    *** Commands ***\n      1: status       2: update       3: revert       4: add untracked\n      5: patch        6: diff         7: quit         8: help\n    What now> 1\n\nYou also could say \"s\" or \"sta\" or \"status\" above as long as the\nchoice is unique.\n\nThe main command loop has 6 subcommands (plus help and quit).\n\n * 'status' shows the change between HEAD and index (i.e. what\n   will be committed if you say \"git commit\"), and between index\n   and working tree files (i.e. what you could stage further\n   before \"git commit\" using \"git-add\") for each path.  A sample\n   output looks like this:\n\n              staged     unstaged path\n     1:       binary      nothing foo.png\n     2:     +403/-35        +1/-1 git-add--interactive.perl\n\n   It shows that foo.png has differences from HEAD (but that is\n   binary so line count cannot be shown) and there is no\n   difference between indexed copy and the working tree\n   version (if the working tree version were also different,\n   'binary' would have been shown in place of 'nothing').  The\n   other file, git-add--interactive.perl, has 403 lines added\n   and 35 lines deleted if you commit what is in the index, but\n   working tree file has further modifications (one addition and\n   one deletion).\n\n * 'update' shows the status information and gives prompt\n   \"Update>>\".  When the prompt ends with double '>>', you can\n   make more than one selection, concatenated with whitespace or\n   comma.  Also you can say ranges.  E.g. \"2-5 7,9\" to choose\n   2,3,4,5,7,9 from the list.  You can say '*' to choose\n   everything.\n\n   What you chose are then highlighted with '*', like this:\n\n              staged     unstaged path\n     1:       binary      nothing foo.png\n   * 2:     +403/-35        +1/-1 git-add--interactive.perl\n\n   To remove selection, prefix the input with - like this:\n\n        Update>> -2\n\n   After making the selection, answer with an empty line to\n   stage the contents of working tree files for selected paths\n   in the index.\n\n * 'revert' has a very similar UI to 'update', and the staged\n   information for selected paths are reverted to that of the\n   HEAD version.  Reverting new paths makes them untracked.\n\n * 'add untracked' has a very similar UI to 'update' and\n   'revert', and lets you add untracked paths to the index.\n\n * 'patch' lets you choose one path out of 'status' like\n   selection.  After choosing the path, it presents diff between\n   the index and the working tree file and asks you if you want\n   to stage the change of each hunk.  You can say:\n\n        y - add the change from that hunk to index\n        n - do not add the change from that hunk to index\n        a - add the change from that hunk and all the rest to index\n        d - do not the change from that hunk nor any of the rest to index\n        j - do not decide on this hunk now, and view the next\n            undecided hunk\n        J - do not decide on this hunk now, and view the next hunk\n        k - do not decide on this hunk now, and view the previous\n            undecided hunk\n        K - do not decide on this hunk now, and view the previous hunk\n\n   After deciding the fate for all hunks, if there is any hunk\n   that was chosen, the index is updated with the selected hunks.\n\n * 'diff' lets you review what will be committed (i.e. between\n   HEAD and index).\n\nThis is still rough, but does everything except a few things I\nthink are needed.\n\n * 'patch' should be able to allow splitting a hunk into\n   multiple hunks.\n\n * 'patch' does not adjust the line offsets @@ -k,l +m,n @@\n   in the hunk header.  This does not have major problem in\n   practice, but it _should_ do the adjustment.\n\n * It does not have any explicit support for a merge in\n   progress; it may not work at all.\n\nAs is any of my initial WIP, the UI of this presents what it\ndoes in a horribly crappy way.  Improvement patches are\nappreciated, but wishlist without code are not really...\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n .gitignore                |    1 +\n Makefile                  |    1 +\n builtin-add.c             |   15 ++\n git-add--interactive.perl |  534 +++++++++++++++++++++++++++++++++++++++++++++\n 4 files changed, 551 insertions(+), 0 deletions(-)\n\ndiff --git a/.gitignore b/.gitignore\nindex 7f2cd55..edf93a6 100644\n--- a/.gitignore\n+++ b/.gitignore\n@@ -2,6 +2,7 @@ GIT-CFLAGS\n GIT-VERSION-FILE\n git\n git-add\n+git-add--interactive\n git-am\n git-annotate\n git-apply\ndiff --git a/Makefile b/Makefile\nindex a1861de..cdd4bf0 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -174,6 +174,7 @@ SCRIPT_SH = \\\n \tgit-lost-found.sh git-quiltimport.sh\n \n SCRIPT_PERL = \\\n+\tgit-add--interactive.perl \\\n \tgit-archimport.perl git-cvsimport.perl git-relink.perl \\\n \tgit-rerere.perl \\\n \tgit-cvsserver.perl \\\ndiff --git a/builtin-add.c b/builtin-add.c\nindex febb75e..37a236e 100644\n--- a/builtin-add.c\n+++ b/builtin-add.c\n@@ -8,6 +8,7 @@\n #include \"cache.h\"\n #include \"builtin.h\"\n #include \"dir.h\"\n+#include \"exec_cmd.h\"\n #include \"cache-tree.h\"\n \n static const char builtin_add_usage[] =\n@@ -89,6 +90,20 @@ int cmd_add(int argc, const char **argv, const char *prefix)\n \tint verbose = 0, show_only = 0;\n \tconst char **pathspec;\n \tstruct dir_struct dir;\n+\tint add_interactive = 0;\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tif (!strcmp(\"--interactive\", argv[i]))\n+\t\t\tadd_interactive++;\n+\t}\n+\tif (add_interactive) {\n+\t\tconst char *args[] = { \"add--interactive\", NULL };\n+\n+\t\tif (add_interactive != 1 || argc != 2)\n+\t\t\tdie(\"add --interactive does not take any parameters\");\n+\t\texecv_git_cmd(args);\n+\t\texit(1);\n+\t}\n \n \tgit_config(git_default_config);\n \ndiff --git a/git-add--interactive.perl b/git-add--interactive.perl\nnew file mode 100755\nindex 0000000..7b42308\n--- /dev/null\n+++ b/git-add--interactive.perl\n@@ -0,0 +1,534 @@\n+#!/usr/bin/perl -w\n+\n+use strict;\n+\n+sub run_cmd_pipe {\n+\tmy $fh = undef;\n+\topen($fh, '-|', @_) or die;\n+\treturn <$fh>;\n+}\n+\n+my ($GIT_DIR) = run_cmd_pipe(qw(git rev-parse --git-dir));\n+\n+if (!defined $GIT_DIR) {\n+\texit(1); # rev-parse would have already said \"not a git repo\"\n+}\n+chomp($GIT_DIR);\n+\n+sub refresh {\n+\tsystem('git update-index --refresh 2>/dev/null');\n+}\n+\n+sub list_untracked {\n+\tmap {\n+\t\tchomp $_;\n+\t\t$_;\n+\t}\n+\trun_cmd_pipe(qw(git ls-files --others\n+\t\t\t--exclude-per-directory=.gitignore),\n+\t\t     \"--exclude-from=$GIT_DIR/info/exclude\",\n+\t\t     '--', @_);\n+}\n+\n+my $status_fmt = '%12s %12s %s';\n+my $status_head = sprintf($status_fmt, 'staged', 'unstaged', 'path');\n+\n+# Returns list of five tuples:\n+# 0: print message\n+# 1: pathname\n+# 2: add/del between HEAD and index\n+# 3: add/del between index and working tree\n+# 4: binary\n+sub list_modified {\n+\tmy ($only) = @_;\n+\tmy (%data, @return);\n+\tmy ($add, $del, $adddel, $file);\n+\n+\tif (!$only || ($only eq 'index-only')) {\n+\t\tfor (run_cmd_pipe(qw(git diff-index --cached\n+\t\t\t\t     --numstat --summary HEAD))) {\n+\t\t\tif (($add, $del, $file) =\n+\t\t\t    /^([-\\d]+)\t([-\\d]+)\t(.*)/) {\n+\t\t\t\tmy ($change, $bin);\n+\t\t\t\tif ($add eq '-' && $del eq '-') {\n+\t\t\t\t\t$change = 'binary';\n+\t\t\t\t\t$bin = 1;\n+\t\t\t\t}\n+\t\t\t\telse {\n+\t\t\t\t\t$change = \"+$add/-$del\";\n+\t\t\t\t}\n+\t\t\t\t$data{$file} =\n+\t\t\t\t    [$change, 'nothing', undef, undef, $bin];\n+\t\t\t}\n+\t\t\telsif (($adddel, $file) =\n+\t\t\t       /^ (create|delete) mode [0-7]+ (.*)$/) {\n+\t\t\t\t$data{$file}[2] = $adddel;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tif (!$only || ($only eq 'file-only')) {\n+\t\tfor (run_cmd_pipe(qw(git diff-files --numstat --summary))) {\n+\t\t\tif (($add, $del, $file) =\n+\t\t\t    /^([-\\d]+)\t([-\\d]+)\t(.*)/) {\n+\t\t\t\tif (!exists $data{$file}) {\n+\t\t\t\t\t$data{$file} =\n+\t\t\t\t\t    ['unchanged', undef, undef, undef];\n+\t\t\t\t}\n+\t\t\t\tmy ($change, $bin);\n+\t\t\t\tif ($add eq '-' && $del eq '-') {\n+\t\t\t\t\t$change = 'binary';\n+\t\t\t\t\t$bin = 1;\n+\t\t\t\t}\n+\t\t\t\telse {\n+\t\t\t\t\t$change = \"+$add/-$del\";\n+\t\t\t\t}\n+\t\t\t\t$data{$file}[1] = $change;\n+\t\t\t\tif ($bin) {\n+\t\t\t\t\t$data{$file}[4] = 1;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\telsif (($adddel, $file) =\n+\t\t\t       /^ (create|delete) mode [0-7]+ (.*)$/) {\n+\t\t\t\t$data{$file}[3] = $adddel;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tfor (sort keys %data) {\n+\t\tmy $it = $data{$_};\n+\t\tpush @return, [(sprintf $status_fmt, @{$it}[0,1], $_), $_,\n+\t\t\t       @{$it}[2,3,4]];\n+\t}\n+\treturn @return;\n+}\n+\n+\n+sub find_unique {\n+\tmy ($string, @stuff) = @_;\n+\tmy $found = undef;\n+\tfor (my $i = 0; $i < @stuff; $i++) {\n+\t\tmy $it = $stuff[$i];\n+\t\tmy $hit = undef;\n+\t\tif (ref $it) { $it = $it->[0]; }\n+\t\teval {\n+\t\t\tif ($it =~ /^$string/) {\n+\t\t\t\t$hit = 1;\n+\t\t\t};\n+\t\t};\n+\t\tif (defined $hit && defined $found) {\n+\t\t\treturn undef;\n+\t\t}\n+\t\tif ($hit) {\n+\t\t\t$found = $i + 1;\n+\t\t}\n+\t}\n+\treturn $found;\n+}\n+\n+sub list_and_choose {\n+\tmy ($opts, @stuff) = @_;\n+\tmy (@chosen, @return);\n+\tmy $i;\n+\n+      TOPLOOP:\n+\twhile (1) {\n+\t\tmy $last_lf = 0;\n+\n+\t\tif ($opts->{HEADER}) {\n+\t\t\tif (!$opts->{LIST_FLAT}) {\n+\t\t\t\tprint \"     \";\n+\t\t\t}\n+\t\t\tprint \"$opts->{HEADER}\\n\";\n+\t\t}\n+\t\tfor ($i = 0; $i < @stuff; $i++) {\n+\t\t\tmy $chosen = $chosen[$i] ? '*' : ' ';\n+\t\t\tmy $print = $stuff[$i];\n+\t\t\tif (ref $print) {\n+\t\t\t\t$print = $print->[0];\n+\t\t\t}\n+\t\t\tprintf(\"%s%2d: %s\", $chosen, $i+1, $print);\n+\t\t\tif (($opts->{LIST_FLAT}) &&\n+\t\t\t    (($i + 1) % ($opts->{LIST_FLAT}))) {\n+\t\t\t\tprint \"\\t\";\n+\t\t\t\t$last_lf = 0;\n+\t\t\t}\n+\t\t\telse {\n+\t\t\t\tprint \"\\n\";\n+\t\t\t\t$last_lf = 1;\n+\t\t\t}\n+\t\t}\n+\t\tif (!$last_lf) {\n+\t\t\tprint \"\\n\";\n+\t\t}\n+\n+\t\treturn if ($opts->{LIST_ONLY});\n+\n+\t\tprint $opts->{PROMPT};\n+\t\tif ($opts->{SINGLETON}) {\n+\t\t\tprint \"> \";\n+\t\t}\n+\t\telse {\n+\t\t\tprint \">> \";\n+\t\t}\n+\t\tmy $line = <STDIN>;\n+\t\tlast if (!$line);\n+\t\tchomp $line;\n+\t\tmy $donesomething = 0;\n+\t\tfor my $choice (split(/[\\s,]+/, $line)) {\n+\t\t\tmy $choose = 1;\n+\t\t\tmy ($bottom, $top);\n+\n+\t\t\t# Input that begins with '-'; unchoose\n+\t\t\tif ($choice =~ s/^-//) {\n+\t\t\t\t$choose = 0;\n+\t\t\t}\n+\t\t\t# A range can be specified like 5-7\n+\t\t\tif ($choice =~ /^(\\d+)-(\\d+)$/) {\n+\t\t\t\t($bottom, $top) = ($1, $2);\n+\t\t\t}\n+\t\t\telsif ($choice =~ /^\\d+$/) {\n+\t\t\t\t$bottom = $top = $choice;\n+\t\t\t}\n+\t\t\telsif ($choice eq '*') {\n+\t\t\t\t$bottom = 1;\n+\t\t\t\t$top = 1 + @stuff;\n+\t\t\t}\n+\t\t\telse {\n+\t\t\t\t$bottom = $top = find_unique($choice, @stuff);\n+\t\t\t\tif (!defined $bottom) {\n+\t\t\t\t\tprint \"Huh ($choice)?\\n\";\n+\t\t\t\t\tnext TOPLOOP;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif ($opts->{SINGLETON} && $bottom != $top) {\n+\t\t\t\tprint \"Huh ($choice)?\\n\";\n+\t\t\t\tnext TOPLOOP;\n+\t\t\t}\n+\t\t\tfor ($i = $bottom-1; $i <= $top-1; $i++) {\n+\t\t\t\tnext if (@stuff <= $i);\n+\t\t\t\t$chosen[$i] = $choose;\n+\t\t\t\t$donesomething++;\n+\t\t\t}\n+\t\t}\n+\t\tlast if (!$donesomething || $opts->{IMMEDIATE});\n+\t}\n+\tfor ($i = 0; $i < @stuff; $i++) {\n+\t\tif ($chosen[$i]) {\n+\t\t\tpush @return, $stuff[$i];\n+\t\t}\n+\t}\n+\treturn @return;\n+}\n+\n+sub status_cmd {\n+\tlist_and_choose({ LIST_ONLY => 1, HEADER => $status_head },\n+\t\t\tlist_modified());\n+\tprint \"\\n\";\n+}\n+\n+sub say_n_paths {\n+\tmy $did = shift @_;\n+\tmy $cnt = scalar @_;\n+\tprint \"$did \";\n+\tif (1 < $cnt) {\n+\t\tprint \"$cnt paths\\n\";\n+\t}\n+\telse {\n+\t\tprint \"one path\\n\";\n+\t}\n+}\n+\n+sub update_cmd {\n+\tmy @mods = list_modified('file-only');\n+\treturn if (!@mods);\n+\n+\tmy @update = list_and_choose({ PROMPT => 'Update',\n+\t\t\t\t       HEADER => $status_head, },\n+\t\t\t\t     @mods);\n+\tif (@update) {\n+\t\tsystem(qw(git update-index --add --), map { $_->[1] } @update);\n+\t\tsay_n_paths('updated', @update);\n+\t}\n+\tprint \"\\n\";\n+}\n+\n+sub revert_cmd {\n+\tmy @update = list_and_choose({ PROMPT => 'Revert',\n+\t\t\t\t       HEADER => $status_head, },\n+\t\t\t\t     list_modified());\n+\tif (@update) {\n+\t\tmy @lines = run_cmd_pipe(qw(git ls-tree HEAD --),\n+\t\t\t\t\t map { $_->[1] } @update);\n+\t\tmy $fh;\n+\t\topen $fh, '|-', qw(git update-index --index-info)\n+\t\t    or die;\n+\t\tfor (@lines) {\n+\t\t\tprint $fh $_;\n+\t\t}\n+\t\tclose($fh);\n+\t\tfor (@update) {\n+\t\t\tif ($_->[2] && $_->[2] eq 'create') {\n+\t\t\t\tsystem(qw(git update-index --force-remove --),\n+\t\t\t\t       $_->[1]);\n+\t\t\t\tprint \"note: $_->[1] is untracked now.\\n\";\n+\t\t\t}\n+\t\t}\n+\t\trefresh();\n+\t\tsay_n_paths('reverted', @update);\n+\t}\n+\tprint \"\\n\";\n+}\n+\n+sub add_untracked_cmd {\n+\tmy @add = list_and_choose({ PROMPT => 'Add untracked' },\n+\t\t\t\t  list_untracked());\n+\tif (@add) {\n+\t\tsystem(qw(git update-index --add --), @add);\n+\t\tsay_n_paths('added', @add);\n+\t}\n+\tprint \"\\n\";\n+}\n+\n+sub parse_diff {\n+\tmy ($path) = @_;\n+\tmy @diff = run_cmd_pipe(qw(git diff-files -p --), $path);\n+\tmy (@hunk) = { TEXT => [] };\n+\n+\tfor (@diff) {\n+\t\tif (/^@@ /) {\n+\t\t\tpush @hunk, { TEXT => [] };\n+\t\t}\n+\t\tpush @{$hunk[-1]{TEXT}}, $_;\n+\t}\n+\treturn @hunk;\n+}\n+\n+sub help_patch_cmd {\n+\tprint <<\\EOF ;\n+y - stage this hunk\n+n - do not stage this hunk\n+a - stage this and all the remaining hunks\n+d - do not stage this hunk nor any of the remaining hunks\n+j - leave this hunk undecided, see next undecided hunk\n+J - leave this hunk undecided, see next hunk\n+k - leave this hunk undecided, see previous undecided hunk\n+K - leave this hunk undecided, see previous hunk\n+EOF\n+}\n+\n+sub patch_update_cmd {\n+\tmy @mods = list_modified('file-only');\n+\t@mods = grep { !($_->[4]) } @mods;\n+\treturn if (!@mods);\n+\n+\tmy ($it) = list_and_choose({ PROMPT => 'Patch update',\n+\t\t\t\t     SINGLETON => 1,\n+\t\t\t\t     IMMEDIATE => 1,\n+\t\t\t\t     HEADER => $status_head, },\n+\t\t\t\t   @mods);\n+\treturn if (!$it);\n+\n+\tmy ($ix, $num);\n+\tmy $path = $it->[1];\n+\tmy ($head, @hunk) = parse_diff($path);\n+\tfor (@{$head->{TEXT}}) {\n+\t\tprint;\n+\t}\n+\t$num = scalar @hunk;\n+\t$ix = 0;\n+\n+\twhile (1) {\n+\t\tmy ($prev, $next, $other, $undecided);\n+\t\t$other = '';\n+\n+\t\tif ($num <= $ix) {\n+\t\t\t$ix = 0;\n+\t\t}\n+\t\tfor (my $i = 0; $i < $ix; $i++) {\n+\t\t\tif (!defined $hunk[$i]{USE}) {\n+\t\t\t\t$prev = 1;\n+\t\t\t\t$other .= '/k';\n+\t\t\t\tlast;\n+\t\t\t}\n+\t\t}\n+\t\tif ($ix) {\n+\t\t\t$other .= '/K';\n+\t\t}\n+\t\tfor (my $i = $ix + 1; $i < $num; $i++) {\n+\t\t\tif (!defined $hunk[$i]{USE}) {\n+\t\t\t\t$next = 1;\n+\t\t\t\t$other .= '/j';\n+\t\t\t\tlast;\n+\t\t\t}\n+\t\t}\n+\t\tif ($ix < $num - 1) {\n+\t\t\t$other .= '/J';\n+\t\t}\n+\t\tfor (my $i = 0; $i < $num; $i++) {\n+\t\t\tif (!defined $hunk[$i]{USE}) {\n+\t\t\t\t$undecided = 1;\n+\t\t\t\tlast;\n+\t\t\t}\n+\t\t}\n+\t\tlast if (!$undecided);\n+\n+\t\tfor (@{$hunk[$ix]{TEXT}}) {\n+\t\t\tprint;\n+\t\t}\n+\t\tprint \"Stage this hunk [y/n/a/d$other/?]? \";\n+\t\tmy $line = <STDIN>;\n+\t\tif ($line) {\n+\t\t\tif ($line =~ /^y/i) {\n+\t\t\t\t$hunk[$ix]{USE} = 1;\n+\t\t\t}\n+\t\t\telsif ($line =~ /^n/i) {\n+\t\t\t\t$hunk[$ix]{USE} = 0;\n+\t\t\t}\n+\t\t\telsif ($line =~ /^a/i) {\n+\t\t\t\twhile ($ix < $num) {\n+\t\t\t\t\tif (!defined $hunk[$ix]{USE}) {\n+\t\t\t\t\t\t$hunk[$ix]{USE} = 1;\n+\t\t\t\t\t}\n+\t\t\t\t\t$ix++;\n+\t\t\t\t}\n+\t\t\t\tnext;\n+\t\t\t}\n+\t\t\telsif ($line =~ /^d/i) {\n+\t\t\t\twhile ($ix < $num) {\n+\t\t\t\t\tif (!defined $hunk[$ix]{USE}) {\n+\t\t\t\t\t\t$hunk[$ix]{USE} = 0;\n+\t\t\t\t\t}\n+\t\t\t\t\t$ix++;\n+\t\t\t\t}\n+\t\t\t\tnext;\n+\t\t\t}\n+\t\t\telsif ($other =~ /K/ && $line =~ /^K/) {\n+\t\t\t\t$ix--;\n+\t\t\t\tnext;\n+\t\t\t}\n+\t\t\telsif ($other =~ /J/ && $line =~ /^J/) {\n+\t\t\t\t$ix++;\n+\t\t\t\tnext;\n+\t\t\t}\n+\t\t\telsif ($other =~ /k/ && $line =~ /^k/) {\n+\t\t\t\twhile (1) {\n+\t\t\t\t\t$ix--;\n+\t\t\t\t\tlast if (!$ix ||\n+\t\t\t\t\t\t !defined $hunk[$ix]{USE});\n+\t\t\t\t}\n+\t\t\t\tnext;\n+\t\t\t}\n+\t\t\telsif ($other =~ /j/ && $line =~ /^j/) {\n+\t\t\t\twhile (1) {\n+\t\t\t\t\t$ix++;\n+\t\t\t\t\tlast if ($ix >= $num ||\n+\t\t\t\t\t\t !defined $hunk[$ix]{USE});\n+\t\t\t\t}\n+\t\t\t\tnext;\n+\t\t\t}\n+\t\t\telse {\n+\t\t\t\thelp_patch_cmd($other);\n+\t\t\t\tnext;\n+\t\t\t}\n+\t\t\t# soft increment\n+\t\t\twhile (1) {\n+\t\t\t\t$ix++;\n+\t\t\t\tlast if ($ix >= $num ||\n+\t\t\t\t\t !defined $hunk[$ix]{USE});\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tmy ($o_lno, $n_lno);\n+\tmy @result = ();\n+\tfor (@hunk) {\n+\t\tmy $text = $_->{TEXT};\n+\t\tmy ($o_ofs, $o_cnt, $n_ofs, $n_cnt) =\n+\t\t    $text->[0] =~ /^@@ -(\\d+)(?:,(\\d+)) \\+(\\d+)(?:,(\\d+)) @@/;\n+\t\tif (!$_->{USE}) {\n+\t\t\t# Adjust offset here.\n+\t\t\tnext;\n+\t\t}\n+\t\telse {\n+\t\t\tfor (@$text) {\n+\t\t\t\tpush @result, $_;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tif (@result) {\n+\t\tmy $fh;\n+\n+\t\topen $fh, '|-', qw(git apply --cached);\n+\t\tfor (@{$head->{TEXT}}, @result) {\n+\t\t\tprint $fh $_;\n+\t\t}\n+\t\tclose $fh;\n+\t\trefresh();\n+\t}\n+\n+\tprint \"\\n\";\n+}\n+\n+sub diff_cmd {\n+\tmy @mods = list_modified('index-only');\n+\t@mods = grep { !($_->[4]) } @mods;\n+\treturn if (!@mods);\n+\tmy ($it) = list_and_choose({ PROMPT => 'Review diff',\n+\t\t\t\t     SINGLETON => 1,\n+\t\t\t\t     IMMEDIATE => 1,\n+\t\t\t\t     HEADER => $status_head, },\n+\t\t\t\t   @mods);\n+\treturn if (!$it);\n+\tsystem(qw(git diff-index -p --cc --cached HEAD --), $it->[1]);\n+}\n+\n+sub quit_cmd {\n+\tprint \"Bye.\\n\";\n+\texit(0);\n+}\n+\n+sub help_cmd {\n+\tprint <<\\EOF ;\n+status        - show paths with changes\n+update        - add working tree state to the staged set of changes\n+revert        - revert staged set of changes back to the HEAD version\n+patch         - pick hunks and update selectively\n+diff\t      - view diff between HEAD and index\n+add untracked - add contents of untracked files to the staged set of changes\n+EOF\n+}\n+\n+sub main_loop {\n+\tmy @cmd = ([ 'status', \\&status_cmd, ],\n+\t\t   [ 'update', \\&update_cmd, ],\n+\t\t   [ 'revert', \\&revert_cmd, ],\n+\t\t   [ 'add untracked', \\&add_untracked_cmd, ],\n+\t\t   [ 'patch', \\&patch_update_cmd, ],\n+\t\t   [ 'diff', \\&diff_cmd, ],\n+\t\t   [ 'quit', \\&quit_cmd, ],\n+\t\t   [ 'help', \\&help_cmd, ],\n+\t);\n+\twhile (1) {\n+\t\tmy ($it) = list_and_choose({ PROMPT => 'What now',\n+\t\t\t\t\t     SINGLETON => 1,\n+\t\t\t\t\t     LIST_FLAT => 4,\n+\t\t\t\t\t     HEADER => '*** Commands ***',\n+\t\t\t\t\t     IMMEDIATE => 1 }, @cmd);\n+\t\tif ($it) {\n+\t\t\teval {\n+\t\t\t\t$it->[1]->();\n+\t\t\t};\n+\t\t\tif ($@) {\n+\t\t\t\tprint \"$@\";\n+\t\t\t}\n+\t\t}\n+\t}\n+}\n+\n+my @z;\n+\n+refresh();\n+status_cmd();\n+main_loop();\n-- \n1.4.4.2.gdfd96\n\n"},{"id":"296106","messageId":"200612111147.44964.Josef.Weidendorfer@gmx.de","threadId":"43083","inReplyTo":"7vslfm23co.fsf_-_@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] git-add --interactive (wip)","fromName":"Josef Weidendorfer","fromEmail":"josef.weidendorfer@gmx.de","sentAt":"2006-12-11T10:47:44Z","receivedAt":"2006-12-11T10:47:44Z","isPatch":true,"sender":{"key":"josef.weidendorfer@gmx.de","avatar":null},"body":"On Monday 11 December 2006 10:23, Junio C Hamano wrote:\n> A script to be driven when the user says \"git add --interactive\"\n> is introduced.\n\nCool.\nJust allowing to unstage files this way makes me happy, thanks.\n\nI just tried to work with this thing to separate my recent\n\"branch.*.localmerge\" changes to the simple version I posted\nafterwards.\n\nI noted that I need to manually change a few dependent hunks,\nso this \"one hunk at a time\" and not being able to edit changes\nin a hunk does not fit my workflow.\nJust as a sidenote: after deciding to not apply hunks, you\nlose them in this WIP, as you will find nothing in \"unstaged\" mode\nafterwards :-(\n\nDespite not having a patch for it right now, it would work out\nbetter for me to open the editor with the patch for one\nfile, and allow to edit it to *replace* the staged version.\n\nNeverless, thanks for this WIP,\n"},{"id":"298651","messageId":"7vwt4wpytm.fsf@assigned-by-dhcp.cox.net","threadId":"43083","inReplyTo":"200612111147.44964.Josef.Weidendorfer@gmx.de","subject":"Re: [PATCH] git-add --interactive (wip)","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-12-12T21:51:49Z","receivedAt":"2006-12-12T21:51:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josef Weidendorfer <Josef.Weidendorfer@gmx.de> writes:\n\n> I just tried to work with this thing to separate my recent\n> \"branch.*.localmerge\" changes to the simple version I posted\n> afterwards.\n>\n> I noted that I need to manually change a few dependent hunks,\n> so this \"one hunk at a time\" and not being able to edit changes\n> in a hunk does not fit my workflow.\n\nI've updated my \"git add --interactive\" in 'pu' and it now knows\nhow to split a hunk into smaller pieces and recounting the diff\noffsets before applying (Johannes pointed out that his partial\ncommit script, entirely written in bash using shell arrays, has\na similar feature.  I guess I should have stolen his code\ninstead of lifting the logic from my own abandoned porcelain).\n\nHunk splitting allows you to start with this hunk:\n\n        @@ -9,9 +9,10 @@\n         #include \"builtin.h\"\n         #include \"dir.h\"\n         #include \"cache-tree.h\"\n        +#include \"exec_cmd.h\"\n\n         static const char builtin_add_usage[] =\n        -\"git-add [-n] [-v] <filepattern>...\";\n        +\"git-add [-n] [-v] <filepattern>... | git-add --interactive\";\n\n         static void prune_directory(struct dir_struct *dir, const char **...\n         {\n\nand lets you split it into two, and you can pick each individually:\n\n        @@ -9,5 +9,6 @@\n         #include \"builtin.h\"\n         #include \"dir.h\"\n         #include \"cache-tree.h\"\n        +#include \"exec_cmd.h\"\n\n         static const char builtin_add_usage[] =\n        @@ -12,6 +13,6 @@\n\n         static const char builtin_add_usage[] =\n        -\"git-add [-n] [-v] <filepattern>...\";\n        +\"git-add [-n] [-v] <filepattern>... | git-add --interactive\";\n\n         static void prune_directory(struct dir_struct *dir, const char **...\n         {\n\nThe post-context of the first hunk and pre-context of the\nsecond hunk are duplicated to give you better context when\nreviewing each of them alone.  If you choose to apply both, they\nare merged back again before applied.\n\nAnyway, after I finished doing this, I started thinking...\n\nOne of the most beautifully done patch series in git.git history\nis this series by Sergey Vlasov:\n\n\thttp://thread.gmane.org/gmane.comp.version-control.git/9057\n\nThe resulting commits are from e8d2a6d4 to 4041483e (inclusive).\n\nWhat's interesting about this series is that its patches touch\nthe same area of the code over and over again, refining a bit by\nbit as the series progresses.\n\nSergey might have done it exactly the way the final patches\npresented the evolution, but I somehow doubt it [*1*].  I often\nhave the state that corresponds to [PATCH n/n] in my working\ntree after hacking around without making a single commit, and\noften would start wanting to commit the state as a clean\nmulti-step series at that point.  I think this is the same for\neverybody.\n\nWhat I often end up doing is to make a snapshot of that\n\"proposed final\" state, reset to the original, and work toward\nthe proposed final state:\n\n\t# snapshot\n\t$ git commit -a -m 'commit goal'\n        $ git tag -f CG\n        $ git reset --hard HEAD^\n\n\t# repeat for (i = 1; i <= n; i++)\n        $ git diff HEAD CG >P.diff\n        $ edit\n        # use P.diff as a reference, and edit the working tree\n        # files to produce [i/n] state.  I might remove pieces\n        # that belong to [j/n] for j > i from  P.diff and run\n\t# \"git apply --index\", or hand edit the files directly\n\t# in the working tree.\n\t$ make test\n        $ git commit -a -m \"[PATCH i/n] title...\"\n        # end repeat\n\n\t# when finished the above cycle, this diff should only\n\t# show the improvements I made during the decomposition.\n        $ git diff CG HEAD\n\nFor a very simple case where the patch munging in the \"repeat\"\npart of the above workflow only deletes hunks (or sub-hunks),\nthe patch subcommand of \"git add --interactive\" with hunk\nsplitting would work nicely, but in general it would not be\nenough.  It certainly is far from usable to produce something\nlike what Sergey gave us.\n\nTo make it easier, one possibility might be to add a subcommand\nto \"git add --interactive\" that lets you edit what is currently\nstaged in the index by opening a temporary copy in your favorite\neditor, and stage the result of your edit in the index.  But I\nfeel quite uneasy to introduce ways to update the index with\nsomething _wildly_ different from what you ever had in your\nworking tree as a whole.\n\nI think it is wrong to commit partially, purely from the index,\nwhen you are building a series that has complex changes that\ncome during the series but go away at the end.  The user should\nbe able to verify all the steps in the middle in such a complex\nseries, but it is not easy if you have it only in the index.\n\nYou could do\n\n\t$ git checkout-index --prefix=testarea/ -f -q -u -a\n\nand run your tests there, but that takes a discipline, and is\ncumbersome to do.\n\nSo in short, I think per-hunk update-index is a cute hack and\nmay be useful in a narrow simple cases, but it would not be so\nuseful in the real life.\n\n\n> Just as a sidenote: after deciding to not apply hunks, you\n> lose them in this WIP, as you will find nothing in \"unstaged\" mode\n> afterwards :-(\n\nI do not understand this part.  You can 'revert' to match the\nindex to HEAD and run 'patch' to pick what you want again.\n\n\n[Footnote]\n\n*1* Maybe Sergey indeed did things the way the series was\npresented, in which case that would only strengthen my respect\nto his ability even more.  The quoted patch series made him one\nof three people on the git list whose patches I trust 100%; in\nfact, I trust their patches so much that I read them not to find\nbugs but to admire and enjoy.\n"},{"id":"295044","messageId":"Pine.LNX.4.63.0612122311400.2807@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"43083","inReplyTo":"7vwt4wpytm.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] git-add --interactive (wip)","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-12-12T22:15:56Z","receivedAt":"2006-12-12T22:15:56Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 12 Dec 2006, Junio C Hamano wrote:\n\n> I've updated my \"git add --interactive\" in 'pu' and it now knows how to \n> split a hunk into smaller pieces and recounting the diff offsets before \n> applying (Johannes pointed out that his partial commit script, entirely \n> written in bash using shell arrays, has a similar feature.  I guess I \n> should have stolen his code instead of lifting the logic from my own \n> abandoned porcelain).\n\nWell, not completely true. I do not split hunks into subhunks.\n\nNote that you _could_ split hunks at arbitrary lines; if it is a line \ncommon between the two files, it is easier though.\n\nAnd you could not have ripped from my script: I make heavy use of bash \narrays (in fact, I wrote this script partly to learn how to work with bash \narrays). If I could think of a clean way to spawn processes piped into \n\"less -FS\" several times from a C program, I would already have rewritten \nit in C.\n\n> So in short, I think per-hunk update-index is a cute hack and may be \n> useful in a narrow simple cases, but it would not be so useful in the \n> real life.\n\nTo the contrary! In spite of having written git-hunk-commit to please \nthose poor souls coming from the darcs side, I already used it myself \nquite often! I think it is a killer feature.\n\nCiao,\nDscho\n"},{"id":"295899","messageId":"200612130415.59038.Josef.Weidendorfer@gmx.de","threadId":"43083","inReplyTo":"7vwt4wpytm.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] git-add --interactive (wip)","fromName":"Josef Weidendorfer","fromEmail":"josef.weidendorfer@gmx.de","sentAt":"2006-12-13T03:15:58Z","receivedAt":"2006-12-13T03:15:58Z","isPatch":true,"sender":{"key":"josef.weidendorfer@gmx.de","avatar":null},"body":"On Tuesday 12 December 2006 22:51, Junio C Hamano wrote:\n> I've updated my \"git add --interactive\" in 'pu' and it now knows\n> how to split a hunk into smaller pieces and recounting the diff\n> offsets before applying\n\nNice.\nYes, this seems to be a missing piece in this hunk-wise staging.\n\n> To make it easier, one possibility might be to add a subcommand\n> to \"git add --interactive\" that lets you edit what is currently\n> staged in the index by opening a temporary copy in your favorite\n> editor, and stage the result of your edit in the index.\n\nYes. Sounds interesting.\nAnother would be to open the editor with the diff of this hunk.\n\n> But I \n> feel quite uneasy to introduce ways to update the index with\n> something _wildly_ different from what you ever had in your\n> working tree as a whole.\n\nHmm... yes.\nIs this not a general problem with staging, of course to\na lesser degree if you work at file granularity, because\nthe probability of dependence of changes in multiple files,\nwhich could lead to a wrong commit, is less.\n\n> I think it is wrong to commit partially, purely from the index,\n> when you are building a series that has complex changes that\n> come during the series but go away at the end.  The user should\n> be able to verify all the steps in the middle in such a complex\n> series, but it is not easy if you have it only in the index.\n\nWhat you really want is to test the commit afterwards. If\nyou did it wrong, you always can do \"git reset --mixed HEAD^\"\nor \"git commit --amend\".\n\nHowever, testing a commit with a dirty working tree is not\npossible. For this to work, you would want that\n\"git-checkout --store\" can store away a dirty working state when\ngoing to another revision, e.g. store it into a temporary ref\n\"<current branch>.dirtywork\".\nYou would need a \"git-checkout --restore\" which would restore\nthe dirty state of the branch you are switching too.\n\nThen, to check the commit of staged things, it should be enough\nto do \"git-checkout --store\", check the commit, and do a\n\"git-checkout --restore\" afterwards to get the dirty state back.\n\n> You could do\n> \n> \t$ git checkout-index --prefix=testarea/ -f -q -u -a\n> \n> and run your tests there, but that takes a discipline, and is\n> cumbersome to do.\n\nIMHO that is too tricky for the average git user.\n\n> So in short, I think per-hunk update-index is a cute hack and\n> may be useful in a narrow simple cases, but it would not be so\n> useful in the real life.\n\nNo. It currently is starting to get useful. With the ability\nto temporarily store away a dirty state of the working directory,\nit really could become very good.\n \n> > Just as a sidenote: after deciding to not apply hunks, you\n> > lose them in this WIP, as you will find nothing in \"unstaged\" mode\n> > afterwards :-(\n> \n> I do not understand this part.  You can 'revert' to match the\n> index to HEAD and run 'patch' to pick what you want again.\n\nHmmm...\nI lost my changes in the working directory; there was nothing to\npick again any more.\nPerhaps I did something wrong.\n\n"},{"id":"297965","messageId":"7v1wn4mk9i.fsf@assigned-by-dhcp.cox.net","threadId":"43083","inReplyTo":"200612130415.59038.Josef.Weidendorfer@gmx.de","subject":"Re: [PATCH] git-add --interactive (wip)","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-12-13T05:34:33Z","receivedAt":"2006-12-13T05:34:33Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Josef Weidendorfer <Josef.Weidendorfer@gmx.de> writes:\n\n> No. It currently is starting to get useful. With the ability\n> to temporarily store away a dirty state of the working directory,\n> it really could become very good.\n\nHmm, a way to easily stash away local changes and restoring\nwould lead to a system where you can easily stash and unstash\nmultiple snapshots and switch between them, and such a model\nsounds vaguely familiar...\n\n>> > Just as a sidenote: after deciding to not apply hunks, you\n>> > lose them in this WIP, as you will find nothing in \"unstaged\" mode\n>> > afterwards :-(\n>> \n>> I do not understand this part.  You can 'revert' to match the\n>> index to HEAD and run 'patch' to pick what you want again.\n>\n> I lost my changes in the working directory; there was nothing to\n> pick again any more.\n\nThat's worrysome.  By design, \"git add\" should not touch working\ntree at all (only read from there), so if you find cases that\nviolates it that should be fixed; please let me know.\n"},{"id":"296094","messageId":"457FC5E2.9040709@op5.se","threadId":"43083","inReplyTo":"Pine.LNX.4.63.0612122311400.2807@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] git-add --interactive (wip)","fromName":"Andreas Ericsson","fromEmail":"ae@op5.se","sentAt":"2006-12-13T09:20:34Z","receivedAt":"2006-12-13T09:20:34Z","isPatch":true,"sender":{"key":"ae@op5.se","avatar":"https://gravatar.com/avatar/426e89595c75a8f5252dd0c989e5fabe5bcac616e68557427ad9aef6b0ca342a?d=mp&s=160"},"body":"Johannes Schindelin wrote:\n> Hi,\n> \n> On Tue, 12 Dec 2006, Junio C Hamano wrote:\n> \n>> So in short, I think per-hunk update-index is a cute hack and may be \n>> useful in a narrow simple cases, but it would not be so useful in the \n>> real life.\n> \n> To the contrary! In spite of having written git-hunk-commit to please \n> those poor souls coming from the darcs side, I already used it myself \n> quite often! I think it is a killer feature.\n> \n\nI've tried it a couple of times as well, and it really is very, very \nnice, especially for young projects where you know you have to make \nloads of changes (often basic and codewise small features).\n\n-- \nAndreas Ericsson                   andreas.ericsson@op5.se\nOP5 AB                             www.op5.se\n"},{"id":"298116","messageId":"Pine.LNX.4.63.0612131137100.3635@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"43083","inReplyTo":"7v1wn4mk9i.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] git-add --interactive (wip)","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-12-13T10:42:41Z","receivedAt":"2006-12-13T10:42:41Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Tue, 12 Dec 2006, Junio C Hamano wrote:\n\n> Josef Weidendorfer <Josef.Weidendorfer@gmx.de> writes:\n> \n> > No. It currently is starting to get useful. With the ability\n> > to temporarily store away a dirty state of the working directory,\n> > it really could become very good.\n> \n> Hmm, a way to easily stash away local changes and restoring\n> would lead to a system where you can easily stash and unstash\n> multiple snapshots and switch between them, and such a model\n> sounds vaguely familiar...\n\nHmm, what might that be? :-)\n\nA more simple approach than to buy into Python would be to introduce a \nvery simple program, which exchanges work directory contents with index \ncontents. So,\n\n$ git revolve-stage\n$ [test the staged revision]\n$ [possibly fix a thing or two]\n$ git revolve-stage\n$ git commit\n\nOpinions?\n\nCiao,\nDscho\n"},{"id":"294253","messageId":"20061213105754.GC9484@spearce.org","threadId":"43083","inReplyTo":"Pine.LNX.4.63.0612131137100.3635@wbgn013.biozentrum.uni-wuerzburg.de","subject":"Re: [PATCH] git-add --interactive (wip)","fromName":"Shawn Pearce","fromEmail":"spearce@spearce.org","sentAt":"2006-12-13T10:57:54Z","receivedAt":"2006-12-13T10:57:54Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> On Tue, 12 Dec 2006, Junio C Hamano wrote:\n> > Hmm, a way to easily stash away local changes and restoring\n> > would lead to a system where you can easily stash and unstash\n> > multiple snapshots and switch between them, and such a model\n> > sounds vaguely familiar...\n> \n> Hmm, what might that be? :-)\n> \n> A more simple approach than to buy into Python \n\nHmmm... last I heard Git doesn't depend on Python anymore, thanks\nlargely to you and Alex.  But it does stash and unstash snapshots\nof my working directory wicked fast using these things called\nbranches...  :-)\n\n-- \n"},{"id":"294466","messageId":"Pine.LNX.4.63.0612131216350.3635@wbgn013.biozentrum.uni-wuerzburg.de","threadId":"43083","inReplyTo":"20061213105754.GC9484@spearce.org","subject":"Re: [PATCH] git-add --interactive (wip)","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2006-12-13T11:20:13Z","receivedAt":"2006-12-13T11:20:13Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Wed, 13 Dec 2006, Shawn Pearce wrote:\n\n> Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> > On Tue, 12 Dec 2006, Junio C Hamano wrote:\n> > > Hmm, a way to easily stash away local changes and restoring\n> > > would lead to a system where you can easily stash and unstash\n> > > multiple snapshots and switch between them, and such a model\n> > > sounds vaguely familiar...\n> > \n> > Hmm, what might that be? :-)\n> > \n> > A more simple approach than to buy into Python \n> \n> Hmmm... last I heard Git doesn't depend on Python anymore, thanks\n> largely to you and Alex.\n\nI referred to StGit, which is pure Python AFAIK.\n\n> But it does stash and unstash snapshots of my working directory wicked \n> fast using these things called branches...  :-)\n\nThe PEBCAK. I cannot hardly type the commands as fast as Git need to stash \nsnapshots into branches.\n\nSo, git-revolve-stage would help me. Or maybe I create bash aliases\n\n$ alias stash='stash_tree=$(git-write-tree)'\n$ alias unstash='git-read-tree $stash_tree'\n\nCiao,\nDscho\n"},{"id":"298047","messageId":"200612132131.15081.Josef.Weidendorfer@gmx.de","threadId":"43083","inReplyTo":"7v1wn4mk9i.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH] git-add --interactive (wip)","fromName":"Josef Weidendorfer","fromEmail":"josef.weidendorfer@gmx.de","sentAt":"2006-12-13T20:31:13Z","receivedAt":"2006-12-13T20:31:13Z","isPatch":true,"sender":{"key":"josef.weidendorfer@gmx.de","avatar":null},"body":"On Wednesday 13 December 2006 06:34, Junio C Hamano wrote:\n> >> > Just as a sidenote: after deciding to not apply hunks, you\n> >> > lose them in this WIP, as you will find nothing in \"unstaged\" mode\n> >> > afterwards :-(\n> >> \n> >> I do not understand this part.  You can 'revert' to match the\n> >> index to HEAD and run 'patch' to pick what you want again.\n> >\n> > I lost my changes in the working directory; there was nothing to\n> > pick again any more.\n> \n> That's worrysome.  By design, \"git add\" should not touch working\n> tree at all (only read from there), so if you find cases that\n> violates it that should be fixed; please let me know.\n\nI just tried to reproduce the failure with exact the same hunk I used\nthe first time. I wasn't able to get any wrong result.\nSo perhaps I did something wrong the first time.\n\nHowever, if I see something suspect, I will tell you ;-)\n\nThanks,\n"}]}