{"thread":{"id":"18482","subject":"[PATCH] difftool: add support for an extended revision syntax","startedAt":"2009-03-23T10:15:48Z","lastAt":"2009-03-27T18:16:15Z","messageCount":12,"participants":["David Aguilar","Michael J Gruber","Junio C Hamano","Jakub Narebski"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"109025","messageId":"1237803348-9329-1-git-send-email-davvid@gmail.com","threadId":"18482","inReplyTo":null,"subject":"[PATCH] difftool: add support for an extended revision syntax","fromName":"David Aguilar","fromEmail":"davvid@gmail.com","sentAt":"2009-03-23T10:15:48Z","receivedAt":"2009-03-23T10:15:48Z","isPatch":true,"sender":{"key":"davvid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13196?v=4"},"body":"This adds an extended revision syntax to git-difftool.\nUsers often ask \"how do I compare a file against its\nprevious version\" and the answer is typically a combination\nof 'git log <file>' and 'git difftool <sha1> <sha1> <file>'.\n\nThis makes answering that question considerably easier.\nUsers can now simply say:\n\n\t$ git difftool <file>~\n\nto compare <file> in the worktree against its\nprevious version, and:\n\n\t$ git difftool <file>~2 <file>~\n\nto compare <file> from 2 versions ago to <file>'s\nprevious version, etc.\n\nThe extended revision syntax also expands revisions\nthat are suffixed with '!' as a convenient way to\nsee commit diffs.  Specifying only '!' is equivalent\nto specifying 'HEAD!'.\n\nThis makes the following statements equivalent:\n\n\t$ git difftool !\n\t$ git difftool HEAD!\n\t$ git difftool HEAD~ HEAD\n\nSigned-off-by: David Aguilar <davvid@gmail.com>\n---\n\nThis patch is based on top of the \"add tests to difftool\"\npatch which itself is based upon the \"move difftool out of contrib\"\npatch currently in the \"pu\" and \"da/difftool\" branches.\n\nThis feature is incredibly useful but it also adds a brand new\nway of specifying revisions that is only understood by git-difftool.\n\nThis was specifically asked for by a git-difftool user\nwho was disapointed to learn that they had to dig through\ngit-log just to use git-difftool effectively.\n\nI do not think git-diff should know anything about the\n\"extended revision syntax\" which is why this behavior\nis best suited for a porcelain such as difftool.\nI can imagine that 'file~' would be a useful construct\nin core git, but changing the plumbing just for something\nlike that seems both daunting and misguided.\n\nSome of the conditional expressions were modified to match\nthe style used in git-add--interactive.perl.  I can split\nthis patch into two if needed, but I figured they were\ntrivial and didn't warrant a separate patch.\n\nI had to escape some tilde characters in the documentation\nbecause asciidoc kept generating invalid html when the\n{tilde} notation was used within single quotes multiple times.\n\nIn case anyone asks, git-diff understands this new syntax too\n(though we did have to twist its arm ;))\n\n\t$ git config alias.ddiff 'difftool --no-ext-diff'\n\t$ git ddiff Makefile~\n\nAny thoughts on whether adding this syntax to git-diff/rev-parse\nwould be feasible/sane/worth it?\n\n Documentation/git-difftool.txt |   55 ++++++++++++++++++-\n git-difftool.perl              |  119 ++++++++++++++++++++++++++++++++++++++--\n t/t7800-difftool.sh            |   43 ++++++++++++++\n 3 files changed, 210 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-difftool.txt b/Documentation/git-difftool.txt\nindex 5ae02f8..2911b84 100644\n--- a/Documentation/git-difftool.txt\n+++ b/Documentation/git-difftool.txt\n@@ -7,7 +7,9 @@ git-difftool - Show changes using common diff tools\n \n SYNOPSIS\n --------\n-'git difftool' [--tool=<tool>] [--no-prompt] [<'git diff' options>]\n+'git difftool' [--tool=<tool>] [--no-prompt]\n+               [<'git diff' options>]\n+               [<extended revision syntax>]\n \n DESCRIPTION\n -----------\n@@ -54,6 +56,57 @@ with custom merge tool commands and has the same value as `$LOCAL`.\n \n See linkgit:git-diff[1] for the full list of supported options.\n \n+EXTENDED REVISION SYNTAX\n+------------------------\n+'git-difftool' understands an extended syntax for specifying revisions.\n+\n+* A suffix '{tilde}' to a file means the previous commit that touched file.\n+\n+* A suffix '{tilde}<n>' to a file means the <n>th previous commit that\n+touched file.  E.g. 'file\\~3' is equivalent to 'file\\~\\~\\~'.\n+\n+* A revision suffixed with an exclamation mark '!' expands to\n+'revision\\~..revision', i.e. the commit diff for that revision.\n+\n+'git-difftool' recognizes this syntax and passes the corresponding\n+commits to 'git-diff'.\n+\n+Examples\n+~~~~~~~~\n+\n+---------------------------------------------------\n+# File Revision Specifiers\n+$ git difftool Makefile~2 <1>\n+$ git difftool Makefile~4 Makefile~2 <2>\n+$ git difftool Makefile~~~~ Makefile~~ <3>\n+\n+# Commit Diff Specifiers\n+$ git difftool origin/next~! <4>\n+$ git difftool HEAD! <5>\n+$ git difftool ! <6>\n+---------------------------------------------------\n+\n+<1> compare 'Makefile' in the worktree against 'Makefile'\n+as it existed two `versions` ago.  `Versions` here means\n+\"changes to Makefile\" such that only commits that touch\n+'Makefile' are considered when finding commits.\n+\n+<2> compare 'Makefile' as it existed four versions ago to\n+'Makefile' as it existed two versions ago.\n+\n+<3> equivalent to example 2 and illustrates what happens when\n+multiple '{tilde}' characters are used.\n+\n+<4> show the commit diff for 'origin/next\\~'.\n+Specifying '!' expands 'origin/next\\~' to\n+'origin/next\\~\\~..origin/next\\~'.\n+\n+<5> show the commit diff for the most recent commit.\n+'HEAD!' is equivalent to 'HEAD\\~..HEAD'.\n+\n+<6> '!' is a shorthand for 'HEAD!' and is equivalent to example 5.\n+\n+\n CONFIG VARIABLES\n ----------------\n 'git-difftool' falls back to 'git-mergetool' config variables when the\ndiff --git a/git-difftool.perl b/git-difftool.perl\nindex 0deda3a..4845f9b 100755\n--- a/git-difftool.perl\n+++ b/git-difftool.perl\n@@ -12,13 +12,17 @@ use warnings;\n use Cwd qw(abs_path);\n use File::Basename qw(dirname);\n \n+binmode(STDOUT, \":raw\");\n+\n my $DIR = abs_path(dirname($0));\n \n \n sub usage\n {\n \tprint << 'USAGE';\n-usage: git difftool [--tool=<tool>] [--no-prompt] [\"git diff\" options]\n+usage: git difftool [--tool=<tool>] [--no-prompt]\n+                    [<'git diff' options>]\n+                    [<extended revision specifier>]\n USAGE\n \texit 1;\n }\n@@ -33,12 +37,18 @@ sub setup_environment\n sub exe\n {\n \tmy $exe = shift;\n-\treturn defined $ENV{COMSPEC} ? \"$exe.exe\" : $exe;\n+\tif ($^O eq 'MSWin32' || $^O eq 'msys') {\n+\t\treturn \"$exe.exe\";\n+\t}\n+\treturn  $exe;\n }\n \n sub generate_command\n {\n+\t# Generate a git-diff command line and set environment\n+\t# variables recognized by git-difftool-helper\n \tmy @command = (exe('git'), 'diff');\n+\tmy @args = ();\n \tmy $skip_next = 0;\n \tmy $idx = -1;\n \tfor my $arg (@ARGV) {\n@@ -47,7 +57,7 @@ sub generate_command\n \t\t\t$skip_next = 0;\n \t\t\tnext;\n \t\t}\n-\t\tif ($arg eq '-t' or $arg eq '--tool') {\n+\t\tif ($arg eq '-t' || $arg eq '--tool') {\n \t\t\tusage() if $#ARGV <= $idx;\n \t\t\t$ENV{GIT_DIFF_TOOL} = $ARGV[$idx + 1];\n \t\t\t$skip_next = 1;\n@@ -61,12 +71,109 @@ sub generate_command\n \t\t\t$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';\n \t\t\tnext;\n \t\t}\n-\t\tif ($arg eq '-h' or $arg eq '--help') {\n+\t\tif ($arg eq '-h' || $arg eq '--help') {\n \t\t\tusage();\n \t\t}\n-\t\tpush @command, $arg;\n+\t\tpush @args, $arg;\n+\t}\n+\treturn (@command, interpolate_args(@args));\n+}\n+\n+\n+sub interpolate_args\n+{\n+\t# Interpolates arguments that should be expanded out to\n+\t# corresponding commits, e.g. 'file~3' or 'master!'.\n+\tmy @args = @_;\n+\tmy $file = undef;\n+\tmy @command = ();\n+\tfor my $arg (@args) {\n+\t\tif (defined $file && $arg eq $file) {\n+\t\t\t# This allows 'git difftool file~ file'\n+\t\t\tnext;\n+\t\t}\n+\t\tif ($arg =~ /^(.+?)(~+\\d*)$/) {\n+\t\t\t# This arg might be a file-revision specifier\n+\t\t\tmy $cur_file = $1;\n+\t\t\tmy $rev_spec = $2;\n+\t\t\tif (defined $file && $file ne $cur_file) {\n+\t\t\t\t# We don't currently support comparing\n+\t\t\t\t# file~ to other_file~\n+\t\t\t\tusage();\n+\t\t\t}\n+\t\t\tif (!-e $cur_file) {\n+\t\t\t\t# This arg is a revision parameter and should\n+\t\t\t\t# be passed along to git-diff as-is\n+\t\t\t\tpush @command, $arg;\n+\t\t\t\tnext;\n+\t\t\t}\n+\t\t\t# This arg is a file-revision specifier so find the\n+\t\t\t# corresponding commits\n+\t\t\t$file = $cur_file;\n+\t\t\tpush @command,\n+\t\t\t\tfind_commit_for_file($cur_file, $rev_spec);\n+\t\t}\n+\t\telsif ($arg =~ /^(.*?)!$/) {\n+\t\t\t# Expand 'sha1!' to 'sha1~ sha1'\n+\t\t\tmy $head = $1;\n+\t\t\tif (length($head) == 0) {\n+\t\t\t\t# Expand '!' to 'HEAD~ HEAD'\n+\t\t\t\t$head = 'HEAD';\n+\t\t\t}\n+\t\t\tpush @command, $head.'~';\n+\t\t\tpush @command, $head;\n+\t\t}\n+\t\telse {\n+\t\t\t# This is a regular arg so just pass it along\n+\t\t\tpush @command, $arg;\n+\t\t}\n+\t}\n+\tif (defined $file) {\n+\t\t# We're using a 'file~' revision specifier so\n+\t\t# automatically limit git-diff to just a single file\n+\t\t$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';\n+\t\tpush @command, '--', $file;\n+\t}\n+\treturn @command;\n+}\n+\n+sub find_commit_for_file\n+{\n+\t# Searches back in $file's history according to $rev_spec\n+\t# and finds the corresponding commits.\n+\t# $rev_spec usually looks like '~' or '~2'.\n+\tmy ($file, $rev_spec) = @_;\n+\tmy $num = 0;\n+\tif ($rev_spec =~ /^(~+)(\\d+)$/) {\n+\t\t$num = length($1);\n+\t\t$num += $2;\n+\t}\n+\telse {\n+\t\t$num = 1;\n+\t\t$num += length($rev_spec);\n+\t}\n+\tmy @cmd = (exe('git'), 'log');\n+\tmy @opts = ('--reverse', '--pretty=format:%H', '--max-count='.$num);\n+\tmy @args = ('--', $file);\n+\treturn read_first_line(@cmd, @opts, @args);\n+}\n+\n+sub read_first_line\n+{\n+\t# Runs a command in a child process and returns the first line\n+\tmy @command = @_;\n+\tmy $pid = open(CHILD, '-|');\n+\tif ($pid) {\n+\t\t# Grab the first line and loop over stdout until we're done\n+\t\tmy $line = <CHILD>;\n+\t\twhile(<CHILD>) {};\n+\t\tclose(CHILD);\n+\t\tchomp $line;\n+\t\treturn $line;\n+\t} else {\n+\t\t# Execute the command and pipe output to our parent\n+\t\texec(@command) or exit 1;\n \t}\n-\treturn @command\n }\n \n setup_environment();\ndiff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh\nindex c7cd2b1..88af30a 100755\n--- a/t/t7800-difftool.sh\n+++ b/t/t7800-difftool.sh\n@@ -136,4 +136,47 @@ test_expect_success 'difftool + mergetool config variables' '\n \trestore_test_defaults\n '\n \n+test_expect_success 'extended file revision syntax' '\n+\tgit checkout branch &&\n+\n+\tdiff=$(git difftool file~) &&\n+\ttest \"$diff\" = \"master\" &&\n+\n+\tdiff=$(git difftool file~ file) &&\n+\ttest \"$diff\" = \"master\" &&\n+\n+\techo branch 2 >file &&\n+\tgit commit -a -m \"branch changes again\" &&\n+\n+\tdiff=$(git difftool file~~ file) &&\n+\ttest \"$diff\" = \"master\" &&\n+\n+\tdiff=$(git difftool file~2 file) &&\n+\ttest \"$diff\" = \"master\" &&\n+\n+\tgit reset --hard HEAD~ &&\n+\tgit checkout master\n+'\n+\n+test_expect_success 'extended commit-ish revision syntax' '\n+\tgit checkout branch &&\n+\n+\tdiff=$(git difftool --no-prompt HEAD!) &&\n+\ttest \"$diff\" = \"master\" &&\n+\n+\tdiff=$(git difftool --no-prompt !) &&\n+\ttest \"$diff\" = \"master\" &&\n+\n+\techo branch again >file &&\n+\tgit commit -a -m \"branch again\" &&\n+\tgit checkout master &&\n+\n+\tdiff=$(git difftool --no-prompt branch!) &&\n+\ttest \"$diff\" = \"branch\" &&\n+\n+\tgit checkout branch &&\n+\tgit reset --hard HEAD~ &&\n+\tgit checkout master\n+'\n+\n test_done\n-- \n1.6.2.1.303.g63699\n"},{"id":"109076","messageId":"49C7A20E.7060503@drmicha.warpmail.net","threadId":"18482","inReplyTo":"1237803348-9329-1-git-send-email-davvid@gmail.com","subject":"Re: [PATCH] difftool: add support for an extended revision syntax","fromName":"Michael J Gruber","fromEmail":"git@drmicha.warpmail.net","sentAt":"2009-03-23T14:51:58Z","receivedAt":"2009-03-23T14:51:58Z","isPatch":true,"sender":{"key":"git@grubix.eu","avatar":"https://avatars.githubusercontent.com/u/233215?v=4"},"body":"David Aguilar venit, vidit, dixit 23.03.2009 11:15:\n> This adds an extended revision syntax to git-difftool.\n> Users often ask \"how do I compare a file against its\n> previous version\" and the answer is typically a combination\n> of 'git log <file>' and 'git difftool <sha1> <sha1> <file>'.\n> \n> This makes answering that question considerably easier.\n> Users can now simply say:\n> \n> \t$ git difftool <file>~\n> \n> to compare <file> in the worktree against its\n> previous version, and:\n> \n> \t$ git difftool <file>~2 <file>~\n> \n> to compare <file> from 2 versions ago to <file>'s\n> previous version, etc.\n> \n> The extended revision syntax also expands revisions\n> that are suffixed with '!' as a convenient way to\n> see commit diffs.  Specifying only '!' is equivalent\n> to specifying 'HEAD!'.\n> \n> This makes the following statements equivalent:\n> \n> \t$ git difftool !\n> \t$ git difftool HEAD!\n> \t$ git difftool HEAD~ HEAD\n> \n> Signed-off-by: David Aguilar <davvid@gmail.com>\n> ---\n> \n> This patch is based on top of the \"add tests to difftool\"\n> patch which itself is based upon the \"move difftool out of contrib\"\n> patch currently in the \"pu\" and \"da/difftool\" branches.\n> \n> This feature is incredibly useful but it also adds a brand new\n> way of specifying revisions that is only understood by git-difftool.\n> \n> This was specifically asked for by a git-difftool user\n> who was disapointed to learn that they had to dig through\n> git-log just to use git-difftool effectively.\n> \n> I do not think git-diff should know anything about the\n> \"extended revision syntax\" which is why this behavior\n> is best suited for a porcelain such as difftool.\n> I can imagine that 'file~' would be a useful construct\n> in core git, but changing the plumbing just for something\n> like that seems both daunting and misguided.\n> \n> Some of the conditional expressions were modified to match\n> the style used in git-add--interactive.perl.  I can split\n> this patch into two if needed, but I figured they were\n> trivial and didn't warrant a separate patch.\n> \n> I had to escape some tilde characters in the documentation\n> because asciidoc kept generating invalid html when the\n> {tilde} notation was used within single quotes multiple times.\n> \n> In case anyone asks, git-diff understands this new syntax too\n> (though we did have to twist its arm ;))\n> \n> \t$ git config alias.ddiff 'difftool --no-ext-diff'\n> \t$ git ddiff Makefile~\n> \n> Any thoughts on whether adding this syntax to git-diff/rev-parse\n> would be feasible/sane/worth it?\n\nI like the idea of having a shortcut like that a lot, but I'm sorry I'm\nnot supportive of a tool-specific rev syntax extension; for principal\nreasons, but also because diff, checkout and maybe others could make\ngood use of it.\n\nWork is underway on clearing out the issue of forbidden characters in\nrevision specifiers. We already have commit:file. I think something like\n~2:file would be short enough as well as in line with the usual\nsemantics. Similarly,\n\ngit diff ~3 ~2 file\ngit diff ~3 file\n\nwould be short and simple as soon as ~3 is implemented to be a shortcut\nfor HEAD~3. (I'm not sure we can actually use ~, even though it would\nfit in with the usual \"if it's not specified it's HEAD\".) This would\nonly need a shortcut for HEAD, such as not even specifying it (as above)\nor c~ with c being our new fancy character for that.\n\n>  Documentation/git-difftool.txt |   55 ++++++++++++++++++-\n>  git-difftool.perl              |  119 ++++++++++++++++++++++++++++++++++++++--\n>  t/t7800-difftool.sh            |   43 ++++++++++++++\n>  3 files changed, 210 insertions(+), 7 deletions(-)\n> \n> diff --git a/Documentation/git-difftool.txt b/Documentation/git-difftool.txt\n> index 5ae02f8..2911b84 100644\n> --- a/Documentation/git-difftool.txt\n> +++ b/Documentation/git-difftool.txt\n> @@ -7,7 +7,9 @@ git-difftool - Show changes using common diff tools\n>  \n>  SYNOPSIS\n>  --------\n> -'git difftool' [--tool=<tool>] [--no-prompt] [<'git diff' options>]\n> +'git difftool' [--tool=<tool>] [--no-prompt]\n> +               [<'git diff' options>]\n> +               [<extended revision syntax>]\n>  \n>  DESCRIPTION\n>  -----------\n> @@ -54,6 +56,57 @@ with custom merge tool commands and has the same value as `$LOCAL`.\n>  \n>  See linkgit:git-diff[1] for the full list of supported options.\n>  \n> +EXTENDED REVISION SYNTAX\n> +------------------------\n> +'git-difftool' understands an extended syntax for specifying revisions.\n> +\n> +* A suffix '{tilde}' to a file means the previous commit that touched file.\n> +\n> +* A suffix '{tilde}<n>' to a file means the <n>th previous commit that\n> +touched file.  E.g. 'file\\~3' is equivalent to 'file\\~\\~\\~'.\n> +\n> +* A revision suffixed with an exclamation mark '!' expands to\n> +'revision\\~..revision', i.e. the commit diff for that revision.\n> +\n> +'git-difftool' recognizes this syntax and passes the corresponding\n> +commits to 'git-diff'.\n> +\n> +Examples\n> +~~~~~~~~\n> +\n> +---------------------------------------------------\n> +# File Revision Specifiers\n> +$ git difftool Makefile~2 <1>\n> +$ git difftool Makefile~4 Makefile~2 <2>\n> +$ git difftool Makefile~~~~ Makefile~~ <3>\n> +\n> +# Commit Diff Specifiers\n> +$ git difftool origin/next~! <4>\n> +$ git difftool HEAD! <5>\n> +$ git difftool ! <6>\n> +---------------------------------------------------\n> +\n> +<1> compare 'Makefile' in the worktree against 'Makefile'\n> +as it existed two `versions` ago.  `Versions` here means\n> +\"changes to Makefile\" such that only commits that touch\n> +'Makefile' are considered when finding commits.\n> +\n> +<2> compare 'Makefile' as it existed four versions ago to\n> +'Makefile' as it existed two versions ago.\n> +\n> +<3> equivalent to example 2 and illustrates what happens when\n> +multiple '{tilde}' characters are used.\n> +\n> +<4> show the commit diff for 'origin/next\\~'.\n> +Specifying '!' expands 'origin/next\\~' to\n> +'origin/next\\~\\~..origin/next\\~'.\n> +\n> +<5> show the commit diff for the most recent commit.\n> +'HEAD!' is equivalent to 'HEAD\\~..HEAD'.\n> +\n> +<6> '!' is a shorthand for 'HEAD!' and is equivalent to example 5.\n> +\n> +\n>  CONFIG VARIABLES\n>  ----------------\n>  'git-difftool' falls back to 'git-mergetool' config variables when the\n> diff --git a/git-difftool.perl b/git-difftool.perl\n> index 0deda3a..4845f9b 100755\n> --- a/git-difftool.perl\n> +++ b/git-difftool.perl\n> @@ -12,13 +12,17 @@ use warnings;\n>  use Cwd qw(abs_path);\n>  use File::Basename qw(dirname);\n>  \n> +binmode(STDOUT, \":raw\");\n> +\n>  my $DIR = abs_path(dirname($0));\n>  \n>  \n>  sub usage\n>  {\n>  \tprint << 'USAGE';\n> -usage: git difftool [--tool=<tool>] [--no-prompt] [\"git diff\" options]\n> +usage: git difftool [--tool=<tool>] [--no-prompt]\n> +                    [<'git diff' options>]\n> +                    [<extended revision specifier>]\n>  USAGE\n>  \texit 1;\n>  }\n> @@ -33,12 +37,18 @@ sub setup_environment\n>  sub exe\n>  {\n>  \tmy $exe = shift;\n> -\treturn defined $ENV{COMSPEC} ? \"$exe.exe\" : $exe;\n> +\tif ($^O eq 'MSWin32' || $^O eq 'msys') {\n> +\t\treturn \"$exe.exe\";\n> +\t}\n> +\treturn  $exe;\n>  }\n>  \n>  sub generate_command\n>  {\n> +\t# Generate a git-diff command line and set environment\n> +\t# variables recognized by git-difftool-helper\n>  \tmy @command = (exe('git'), 'diff');\n> +\tmy @args = ();\n>  \tmy $skip_next = 0;\n>  \tmy $idx = -1;\n>  \tfor my $arg (@ARGV) {\n> @@ -47,7 +57,7 @@ sub generate_command\n>  \t\t\t$skip_next = 0;\n>  \t\t\tnext;\n>  \t\t}\n> -\t\tif ($arg eq '-t' or $arg eq '--tool') {\n> +\t\tif ($arg eq '-t' || $arg eq '--tool') {\n>  \t\t\tusage() if $#ARGV <= $idx;\n>  \t\t\t$ENV{GIT_DIFF_TOOL} = $ARGV[$idx + 1];\n>  \t\t\t$skip_next = 1;\n> @@ -61,12 +71,109 @@ sub generate_command\n>  \t\t\t$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';\n>  \t\t\tnext;\n>  \t\t}\n> -\t\tif ($arg eq '-h' or $arg eq '--help') {\n> +\t\tif ($arg eq '-h' || $arg eq '--help') {\n>  \t\t\tusage();\n>  \t\t}\n> -\t\tpush @command, $arg;\n> +\t\tpush @args, $arg;\n> +\t}\n> +\treturn (@command, interpolate_args(@args));\n> +}\n> +\n> +\n> +sub interpolate_args\n> +{\n> +\t# Interpolates arguments that should be expanded out to\n> +\t# corresponding commits, e.g. 'file~3' or 'master!'.\n> +\tmy @args = @_;\n> +\tmy $file = undef;\n> +\tmy @command = ();\n> +\tfor my $arg (@args) {\n> +\t\tif (defined $file && $arg eq $file) {\n> +\t\t\t# This allows 'git difftool file~ file'\n> +\t\t\tnext;\n> +\t\t}\n> +\t\tif ($arg =~ /^(.+?)(~+\\d*)$/) {\n> +\t\t\t# This arg might be a file-revision specifier\n> +\t\t\tmy $cur_file = $1;\n> +\t\t\tmy $rev_spec = $2;\n> +\t\t\tif (defined $file && $file ne $cur_file) {\n> +\t\t\t\t# We don't currently support comparing\n> +\t\t\t\t# file~ to other_file~\n> +\t\t\t\tusage();\n> +\t\t\t}\n> +\t\t\tif (!-e $cur_file) {\n> +\t\t\t\t# This arg is a revision parameter and should\n> +\t\t\t\t# be passed along to git-diff as-is\n> +\t\t\t\tpush @command, $arg;\n> +\t\t\t\tnext;\n> +\t\t\t}\n> +\t\t\t# This arg is a file-revision specifier so find the\n> +\t\t\t# corresponding commits\n> +\t\t\t$file = $cur_file;\n> +\t\t\tpush @command,\n> +\t\t\t\tfind_commit_for_file($cur_file, $rev_spec);\n> +\t\t}\n> +\t\telsif ($arg =~ /^(.*?)!$/) {\n> +\t\t\t# Expand 'sha1!' to 'sha1~ sha1'\n> +\t\t\tmy $head = $1;\n> +\t\t\tif (length($head) == 0) {\n> +\t\t\t\t# Expand '!' to 'HEAD~ HEAD'\n> +\t\t\t\t$head = 'HEAD';\n> +\t\t\t}\n> +\t\t\tpush @command, $head.'~';\n> +\t\t\tpush @command, $head;\n> +\t\t}\n> +\t\telse {\n> +\t\t\t# This is a regular arg so just pass it along\n> +\t\t\tpush @command, $arg;\n> +\t\t}\n> +\t}\n> +\tif (defined $file) {\n> +\t\t# We're using a 'file~' revision specifier so\n> +\t\t# automatically limit git-diff to just a single file\n> +\t\t$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';\n> +\t\tpush @command, '--', $file;\n> +\t}\n> +\treturn @command;\n> +}\n> +\n> +sub find_commit_for_file\n> +{\n> +\t# Searches back in $file's history according to $rev_spec\n> +\t# and finds the corresponding commits.\n> +\t# $rev_spec usually looks like '~' or '~2'.\n> +\tmy ($file, $rev_spec) = @_;\n> +\tmy $num = 0;\n> +\tif ($rev_spec =~ /^(~+)(\\d+)$/) {\n> +\t\t$num = length($1);\n> +\t\t$num += $2;\n> +\t}\n> +\telse {\n> +\t\t$num = 1;\n> +\t\t$num += length($rev_spec);\n> +\t}\n> +\tmy @cmd = (exe('git'), 'log');\n> +\tmy @opts = ('--reverse', '--pretty=format:%H', '--max-count='.$num);\n> +\tmy @args = ('--', $file);\n> +\treturn read_first_line(@cmd, @opts, @args);\n> +}\n> +\n> +sub read_first_line\n> +{\n> +\t# Runs a command in a child process and returns the first line\n> +\tmy @command = @_;\n> +\tmy $pid = open(CHILD, '-|');\n> +\tif ($pid) {\n> +\t\t# Grab the first line and loop over stdout until we're done\n> +\t\tmy $line = <CHILD>;\n> +\t\twhile(<CHILD>) {};\n> +\t\tclose(CHILD);\n> +\t\tchomp $line;\n> +\t\treturn $line;\n> +\t} else {\n> +\t\t# Execute the command and pipe output to our parent\n> +\t\texec(@command) or exit 1;\n>  \t}\n> -\treturn @command\n>  }\n>  \n>  setup_environment();\n> diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh\n> index c7cd2b1..88af30a 100755\n> --- a/t/t7800-difftool.sh\n> +++ b/t/t7800-difftool.sh\n> @@ -136,4 +136,47 @@ test_expect_success 'difftool + mergetool config variables' '\n>  \trestore_test_defaults\n>  '\n>  \n> +test_expect_success 'extended file revision syntax' '\n> +\tgit checkout branch &&\n> +\n> +\tdiff=$(git difftool file~) &&\n> +\ttest \"$diff\" = \"master\" &&\n> +\n> +\tdiff=$(git difftool file~ file) &&\n> +\ttest \"$diff\" = \"master\" &&\n> +\n> +\techo branch 2 >file &&\n> +\tgit commit -a -m \"branch changes again\" &&\n> +\n> +\tdiff=$(git difftool file~~ file) &&\n> +\ttest \"$diff\" = \"master\" &&\n> +\n> +\tdiff=$(git difftool file~2 file) &&\n> +\ttest \"$diff\" = \"master\" &&\n> +\n> +\tgit reset --hard HEAD~ &&\n> +\tgit checkout master\n> +'\n> +\n> +test_expect_success 'extended commit-ish revision syntax' '\n> +\tgit checkout branch &&\n> +\n> +\tdiff=$(git difftool --no-prompt HEAD!) &&\n> +\ttest \"$diff\" = \"master\" &&\n> +\n> +\tdiff=$(git difftool --no-prompt !) &&\n> +\ttest \"$diff\" = \"master\" &&\n> +\n> +\techo branch again >file &&\n> +\tgit commit -a -m \"branch again\" &&\n> +\tgit checkout master &&\n> +\n> +\tdiff=$(git difftool --no-prompt branch!) &&\n> +\ttest \"$diff\" = \"branch\" &&\n> +\n> +\tgit checkout branch &&\n> +\tgit reset --hard HEAD~ &&\n> +\tgit checkout master\n> +'\n> +\n>  test_done\n"},{"id":"109085","messageId":"20090323163343.GB16258@gmail.com","threadId":"18482","inReplyTo":"49C7A20E.7060503@drmicha.warpmail.net","subject":"Re: [PATCH] difftool: add support for an extended revision syntax","fromName":"David Aguilar","fromEmail":"davvid@gmail.com","sentAt":"2009-03-23T16:33:44Z","receivedAt":"2009-03-23T16:33:44Z","isPatch":true,"sender":{"key":"davvid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13196?v=4"},"body":"On  0, Michael J Gruber <git@drmicha.warpmail.net> wrote:\n> David Aguilar venit, vidit, dixit 23.03.2009 11:15:\n> > \n> > This makes answering that question considerably easier.\n> > Users can now simply say:\n> > \n> > \t$ git difftool <file>~\n> > \n> > to compare <file> in the worktree against its\n> > previous version, and:\n> > \n> > \t$ git difftool <file>~2 <file>~\n> > \n> > to compare <file> from 2 versions ago to <file>'s\n> > previous version, etc.\n> > \n> \n> I like the idea of having a shortcut like that a lot, but I'm sorry I'm\n> not supportive of a tool-specific rev syntax extension; for principal\n> reasons, but also because diff, checkout and maybe others could make\n> good use of it.\n\nI completely agree.  I want a convenient shortcut to use everywhere.\n\n\n> Work is underway on clearing out the issue of forbidden characters in\n> revision specifiers. We already have commit:file. I think something like\n> ~2:file would be short enough as well as in line with the usual\n> semantics. Similarly,\n> \n> git diff ~3 ~2 file\n> git diff ~3 file\n> \n> would be short and simple as soon as ~3 is implemented to be a shortcut\n> for HEAD~3. (I'm not sure we can actually use ~, even though it would\n> fit in with the usual \"if it's not specified it's HEAD\".) This would\n> only need a shortcut for HEAD, such as not even specifying it (as above)\n> or c~ with c being our new fancy character for that.\n\n\nOne of my ulterior motives for sending this patch was to\nstart a discussion on this topic.  I missed the discussion\nabout clearing out forbidden characters so I'll see if I can\nfind it in the archives.  Do you happen to have a link?\n\nKeep in mind that the syntax that this patch added does\nnot have file~3 = HEAD~3.  file~3 means finding file as it\nexisted 3 changes-to-file ago, which is != to HEAD~3 if file\ndid not change in the last 3 commits.\n\nIt basically runs 'git log -- file' so that only the commits\nthat touch file are considered.\n\nAre you suggesting that ~3:file could be the shortcut for\nthis, or would we need a different syntax since you just\nsaid that ~3 would be a shortcut for HEAD~3?\n\nIf there's work on syntax-sugarfying diff/checkout/etc. with\nthese kind of shortcuts then I would like to help out with\npatches, testing, etc.\n\nThis is a feature that lots of users would enjoy--I know that\nfor sure.\n\n-- \n\n\tDavid\n"},{"id":"109090","messageId":"49C7BCEA.1060309@drmicha.warpmail.net","threadId":"18482","inReplyTo":"20090323163343.GB16258@gmail.com","subject":"Re: [PATCH] difftool: add support for an extended revision syntax","fromName":"Michael J Gruber","fromEmail":"git@drmicha.warpmail.net","sentAt":"2009-03-23T16:46:34Z","receivedAt":"2009-03-23T16:46:34Z","isPatch":true,"sender":{"key":"git@grubix.eu","avatar":"https://avatars.githubusercontent.com/u/233215?v=4"},"body":"David Aguilar venit, vidit, dixit 23.03.2009 17:33:\n> On  0, Michael J Gruber <git@drmicha.warpmail.net> wrote:\n>> David Aguilar venit, vidit, dixit 23.03.2009 11:15:\n>>>\n>>> This makes answering that question considerably easier.\n>>> Users can now simply say:\n>>>\n>>> \t$ git difftool <file>~\n>>>\n>>> to compare <file> in the worktree against its\n>>> previous version, and:\n>>>\n>>> \t$ git difftool <file>~2 <file>~\n>>>\n>>> to compare <file> from 2 versions ago to <file>'s\n>>> previous version, etc.\n>>>\n>>\n>> I like the idea of having a shortcut like that a lot, but I'm sorry I'm\n>> not supportive of a tool-specific rev syntax extension; for principal\n>> reasons, but also because diff, checkout and maybe others could make\n>> good use of it.\n> \n> I completely agree.  I want a convenient shortcut to use everywhere.\n> \n> \n>> Work is underway on clearing out the issue of forbidden characters in\n>> revision specifiers. We already have commit:file. I think something like\n>> ~2:file would be short enough as well as in line with the usual\n>> semantics. Similarly,\n>>\n>> git diff ~3 ~2 file\n>> git diff ~3 file\n>>\n>> would be short and simple as soon as ~3 is implemented to be a shortcut\n>> for HEAD~3. (I'm not sure we can actually use ~, even though it would\n>> fit in with the usual \"if it's not specified it's HEAD\".) This would\n>> only need a shortcut for HEAD, such as not even specifying it (as above)\n>> or c~ with c being our new fancy character for that.\n> \n> \n> One of my ulterior motives for sending this patch was to\n> start a discussion on this topic.  I missed the discussion\n> about clearing out forbidden characters so I'll see if I can\n> find it in the archives.  Do you happen to have a link?\n> \n> Keep in mind that the syntax that this patch added does\n> not have file~3 = HEAD~3.  file~3 means finding file as it\n> existed 3 changes-to-file ago, which is != to HEAD~3 if file\n> did not change in the last 3 commits.\n> \n> It basically runs 'git log -- file' so that only the commits\n> that touch file are considered.\n> \n> Are you suggesting that ~3:file could be the shortcut for\n> this, or would we need a different syntax since you just\n> said that ~3 would be a shortcut for HEAD~3?\n\nI think both would be useful. Depending on the way it's done, only\nHEAD~3 maybe be realizable.\n\n> If there's work on syntax-sugarfying diff/checkout/etc. with\n> these kind of shortcuts then I would like to help out with\n> patches, testing, etc.\n\nhttp://article.gmane.org/gmane.comp.version-control.git/114257 is the\nnewest installment, although\nhttp://article.gmane.org/gmane.comp.version-control.git/114076 and\nespecially\nhttp://article.gmane.org/gmane.comp.version-control.git/113647 is very\nworthwhile reading :)\n\n[Isn't there a better way to link to posts? I think I used to take\nmessage ids.]\n\n> This is a feature that lots of users would enjoy--I know that\n> for sure.\n> \n"},{"id":"109114","messageId":"7vhc1j6czf.fsf@gitster.siamese.dyndns.org","threadId":"18482","inReplyTo":"20090323163343.GB16258@gmail.com","subject":"Re: [PATCH] difftool: add support for an extended revision syntax","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-03-23T23:22:44Z","receivedAt":"2009-03-23T23:22:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"David Aguilar <davvid@gmail.com> writes:\n\n> Keep in mind that the syntax that this patch added does not have file~3\n> = HEAD~3.  file~3 means finding file as it existed 3 changes-to-file\n> ago, which is != to HEAD~3 if file did not change in the last 3 commits.\n\nIf your motive is to introduce inconsistency to the UI by adding this kind\nof new notation _only to difftool_, I have to reconsider moving it out of\ncontrib/ area.\n\nWhile I do not fundamentally oppose to add convenient notations for useful\nconcepts, you need to start at making sure if this \"three changes ago\" is\na well defined concept to begin with.\n\nAnd it is not a well defined concept in a merge-heavy environment, unless\nyou define what you mean by \"three changes ago\".\n\nIf you consider this history:\n\n ---Y---o---X---M---o mainline = HEAD\n               /\n   ---A---B---C topic\n\nwhere A, B, C and X, Y are the only commits that touched the file you are\ninterested in, how do you define 3-changes-ago?\n\nMaybe X was just a totally uninteresting typofix to a comment, while A, B\nand C were adding a very interesting new feature.  Don't forget that M\nalso changes the file from either of its parents (X or C).  Does M count\nas the last change?  Or does it not count because it is just a mechanical\nunconflicting merge?  Which one of X or C is the penultimate change?  The\none with an earlier committer timestamp?  Tiebreaking with timestamps is\nknown to be flawed in the presense of clock skew.\n\nFor the consistency of the UI, \"starting at HEAD, following first-parent\nancestry, find N-th commit that touches the path, ignoring all the side\nbranches\" MUST be the semantics of a notation that uses tilde followed by\nnumber (so file~3 must mean Y in the above picture), because HEAD~3 is\ndefined as \"three parents ago, only following the first parent ancestry\".\nAnything else will invite user confusion.\n\nBut I do not think it is necessarily useful to follow only the first\nparent ancestry to find \"three-changes ago\" (if such a concept exists).\nIf you want a notation that means something else, such as X (because\nchronologically the commits that touched the file are M, C and X in the\nideal world that everybody has well synchronized clock), you shouldn't use\ntilde-number notation but use something else.\n"},{"id":"109117","messageId":"402731c90903231633x32611e07je2df36a4b994f338@mail.gmail.com","threadId":"18482","inReplyTo":"7vhc1j6czf.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] difftool: add support for an extended revision syntax","fromName":"David Aguilar","fromEmail":"davvid@gmail.com","sentAt":"2009-03-23T23:33:16Z","receivedAt":"2009-03-23T23:33:16Z","isPatch":true,"sender":{"key":"davvid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13196?v=4"},"body":"On Mon, Mar 23, 2009 at 4:22 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> David Aguilar <davvid@gmail.com> writes:\n>\n>> Keep in mind that the syntax that this patch added does not have file~3\n>> = HEAD~3.  file~3 means finding file as it existed 3 changes-to-file\n>> ago, which is != to HEAD~3 if file did not change in the last 3 commits.\n>\n> If your motive is to introduce inconsistency to the UI by adding this kind\n> of new notation _only to difftool_, I have to reconsider moving it out of\n> contrib/ area.\n\n\nI totally agree that this patch was half-baked, not fully defined,\nand should be dropped.  I hope that doesn't influence\nyour decision for the rest of the patches that came before it.\n\nThe notion of \"3 commits ago\" is ambiguous in a merge-heavy work flow\nand using ~ would only confuse things, so I'll see if we can find a better\nway to do it, but it shouldn't happen in difftool (if it happens at all).\n\n\n>\n> While I do not fundamentally oppose to add convenient notations for useful\n> concepts, you need to start at making sure if this \"three changes ago\" is\n> a well defined concept to begin with.\n>\n> And it is not a well defined concept in a merge-heavy environment, unless\n> you define what you mean by \"three changes ago\".\n>\n> If you consider this history:\n>\n>  ---Y---o---X---M---o mainline = HEAD\n>               /\n>   ---A---B---C topic\n>\n> where A, B, C and X, Y are the only commits that touched the file you are\n> interested in, how do you define 3-changes-ago?\n>\n> Maybe X was just a totally uninteresting typofix to a comment, while A, B\n> and C were adding a very interesting new feature.  Don't forget that M\n> also changes the file from either of its parents (X or C).  Does M count\n> as the last change?  Or does it not count because it is just a mechanical\n> unconflicting merge?  Which one of X or C is the penultimate change?  The\n> one with an earlier committer timestamp?  Tiebreaking with timestamps is\n> known to be flawed in the presense of clock skew.\n>\n> For the consistency of the UI, \"starting at HEAD, following first-parent\n> ancestry, find N-th commit that touches the path, ignoring all the side\n> branches\" MUST be the semantics of a notation that uses tilde followed by\n> number (so file~3 must mean Y in the above picture), because HEAD~3 is\n> defined as \"three parents ago, only following the first parent ancestry\".\n> Anything else will invite user confusion.\n>\n> But I do not think it is necessarily useful to follow only the first\n> parent ancestry to find \"three-changes ago\" (if such a concept exists).\n> If you want a notation that means something else, such as X (because\n> chronologically the commits that touched the file are M, C and X in the\n> ideal world that everybody has well synchronized clock), you shouldn't use\n> tilde-number notation but use something else.\n>\n\nUnderstood and thanks for the help,\n\n-- \n    David\n"},{"id":"109121","messageId":"7v1vsn6boc.fsf@gitster.siamese.dyndns.org","threadId":"18482","inReplyTo":"1237803348-9329-1-git-send-email-davvid@gmail.com","subject":"Re: [PATCH] difftool: add support for an extended revision syntax","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-03-23T23:50:59Z","receivedAt":"2009-03-23T23:50:59Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"David Aguilar <davvid@gmail.com> writes:\n\n> This makes answering that question considerably easier.\n> Users can now simply say:\n>\n> \t$ git difftool <file>~\n>\n> to compare <file> in the worktree against its\n> previous version, and:\n>\n> \t$ git difftool <file>~2 <file>~\n>\n> to compare <file> from 2 versions ago to <file>'s\n> previous version, etc.\n\nThese two examples are not that interesting.  Because you can say:\n\n\t$ git log -p -1 file\n\t$ git log -p -2 file\n\n(admittedly you need to skip the first entry of the output in the latter\none to get to what you are interested in).\n\nWhat existing syntax does not allow you to say easily is something like:\n\n\t$ git difftool <file>~4 <file>\n\nThat is, \"I do not care about the intermediate states, but want to see the\n4 changes consolidated in one\".\n\nAs I told you in my previous message, I am not convinced a short-and-sweet\nnotation such as tilde-four is expressive enough for most user's needs (I\nsuspect most users use git as a fast CVS and have rather linear history,\nin which case \"Nth commit that changes the file, following only the first\nparent chain down from the HEAD\" _could_ be a perfectly fine and useful\nsemantics), but if it is, I think it would not be too involved to patch\nrevision.c::handle_revision_arg() to make it available to everybody.\n\n> This makes the following statements equivalent:\n>\n> \t$ git difftool !\n> \t$ git difftool HEAD!\n> \t$ git difftool HEAD~ HEAD\n\nWhich would be:\n\n\t$ git log -p -1 file\n\nright?\n\nPerhaps we would want a convenient way for \"log -p\" or \"show -p\" to drive\ndifftool as a backend?\n"},{"id":"109657","messageId":"m33acz2byv.fsf@localhost.localdomain","threadId":"18482","inReplyTo":"1237803348-9329-1-git-send-email-davvid@gmail.com","subject":"Re: [PATCH] difftool: add support for an extended revision syntax","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2009-03-27T15:59:39Z","receivedAt":"2009-03-27T15:59:39Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"David Aguilar <davvid@gmail.com> writes:\n\n> This adds an extended revision syntax to git-difftool.\n> Users often ask \"how do I compare a file against its\n> previous version\" and the answer is typically a combination\n> of 'git log <file>' and 'git difftool <sha1> <sha1> <file>'.\n> \n> This makes answering that question considerably easier.\n> Users can now simply say:\n> \n> \t$ git difftool <file>~\n> \n> to compare <file> in the worktree against its\n> previous version, and:\n> \n> \t$ git difftool <file>~2 <file>~\n> \n> to compare <file> from 2 versions ago to <file>'s\n> previous version, etc.\n\nBecause <rev>~<n> means (see git-rev-parse(1) for example) n-th parent\nin _first parent_ line, then <file>~<n> should also use first-parent\nline, otherwise you invite confusion.\n\nAlso I am not sure about the syntax, as there are no restrictions on\nfilenames, and filenames can contain '~'.  Perhaps (proposed in this\nthread) ~<n>:<file> == \n  \"$(git rev-list -n <n> --first-parent HEAD -- <file> | tail):<file>\"\n\nBut this might be mistaken for HEAD~<n>:<file>, which can be something\nelse...\n \n> The extended revision syntax also expands revisions\n> that are suffixed with '!' as a convenient way to\n> see commit diffs.  Specifying only '!' is equivalent\n> to specifying 'HEAD!'.\n> \n> This makes the following statements equivalent:\n> \n> \t$ git difftool !\n> \t$ git difftool HEAD!\n> \t$ git difftool HEAD~ HEAD\n\nErrr... there already exists such syntax, and it is called HEAD^!\n(if git-difftool can accept a..b revision specification)... or not,\nas git-rev-parse(1) states:\n\n  r1^! includes commit r1 but excludes all of its parents.\n\nSo depending on how \"r1 --not r1^\" and \"r1 --not r1^1 r1^2\" is\ninterpreted by diff (which accpets points, not ranges) it might be, or\nmight be not what you wanted by introducying '!' / <rev>:!\nspecification...\n\n-- \nJakub Narebski\nPoland\nShadeHawk on #git\n"},{"id":"109662","messageId":"20090327162849.GA2853@gmail.com","threadId":"18482","inReplyTo":"m33acz2byv.fsf@localhost.localdomain","subject":"Re: [PATCH] difftool: add support for an extended revision syntax","fromName":"David Aguilar","fromEmail":"davvid@gmail.com","sentAt":"2009-03-27T16:28:49Z","receivedAt":"2009-03-27T16:28:49Z","isPatch":true,"sender":{"key":"davvid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13196?v=4"},"body":"On  0, Jakub Narebski <jnareb@gmail.com> wrote:\n> David Aguilar <davvid@gmail.com> writes:\n> \n> > This adds an extended revision syntax to git-difftool.\n> > Users often ask \"how do I compare a file against its\n> > previous version\" and the answer is typically a combination\n> > of 'git log <file>' and 'git difftool <sha1> <sha1> <file>'.\n> > \n> > [snip]\n> > \n> > The extended revision syntax also expands revisions\n> > that are suffixed with '!' as a convenient way to\n> > see commit diffs.  Specifying only '!' is equivalent\n> > to specifying 'HEAD!'.\n> > \n> > This makes the following statements equivalent:\n> > \n> > \t$ git difftool !\n> > \t$ git difftool HEAD!\n> > \t$ git difftool HEAD~ HEAD\n> \n> Errr... there already exists such syntax, and it is called HEAD^!\n\n\nYup, this patch was a mistake ;)\n\n^! does exactly what I needed and difftool supports it since\ngit-diff does.\n\nI'm still interested in the file~<n> idea [though maybe not\nthat exact syntax] and have been reading revision.c (as Junio\nsuggested) to see if it can be done in a good way.\n\nI'm still not sure if it is a good idea since the types of\nusers who would want it are probably better off with tighter\nintegration between gitk and difftool as opposed to a\nconvenient command-line syntax (tho I do see how it would be\nuseful every now and then).  Ideally, we'd want to select two\ncommits in gitk and compare them.  I think qgit can do that,\nbut it doesn't yet know about difftool (since, of course,\ndifftool is not in master yet).\n\nI noticed that my patches for \"add tests to difftool\" and \"add\nthe -y shortcut for --no-prompt\" were in git.git's pu branch\nthe other day but aren't there anymore.  Was that intentional?\n\n\n\n> (if git-difftool can accept a..b revision specification)... or not,\n> as git-rev-parse(1) states:\n> \n>   r1^! includes commit r1 but excludes all of its parents.\n> \n> So depending on how \"r1 --not r1^\" and \"r1 --not r1^1 r1^2\" is\n> interpreted by diff (which accpets points, not ranges) it might be, or\n> might be not what you wanted by introducying '!' / <rev>:!\n> specification...\n> \n> -- \n> Jakub Narebski\n> Poland\n> ShadeHawk on #git\n\n-- \n\n\tDavid\n"},{"id":"109667","messageId":"7vab76evaj.fsf@gitster.siamese.dyndns.org","threadId":"18482","inReplyTo":"20090327162849.GA2853@gmail.com","subject":"Re: [PATCH] difftool: add support for an extended revision syntax","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-03-27T17:21:24Z","receivedAt":"2009-03-27T17:21:24Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"David Aguilar <davvid@gmail.com> writes:\n\n> I'm still interested in the file~<n> idea [though maybe not\n> that exact syntax] and have been reading revision.c (as Junio\n> suggested) to see if it can be done in a good way.\n\nI'll send a comment in a separate message.\n\n> I noticed that my patches for \"add tests to difftool\" and \"add\n> the -y shortcut for --no-prompt\" were in git.git's pu branch\n> the other day but aren't there anymore.  Was that intentional?\n\nYes, it makes 'pu' not pass the self test.\n"},{"id":"109668","messageId":"7vvdpudg1c.fsf@gitster.siamese.dyndns.org","threadId":"18482","inReplyTo":"20090327162849.GA2853@gmail.com","subject":"Re: [PATCH] difftool: add support for an extended revision syntax","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2009-03-27T17:36:15Z","receivedAt":"2009-03-27T17:36:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"David Aguilar <davvid@gmail.com> writes:\n\n> I'm still interested in the file~<n> idea [though maybe not\n> that exact syntax] and have been reading revision.c (as Junio\n> suggested) to see if it can be done in a good way.\n\nModern git programs use setup_revisions() to parse object names and range\nnotation given on the command line, and leave results in revs->pending\narray.  This array holds pointers to \"struct object\" instances, and they\nare often (always?) already parsed.  This gives the caller an easy access\nto the parse results.\n\nRecently there have been two topics that made me suspect that solving them\ncleanly may require breaking this model.  I haven't thought things through\nfor the second one yet, so please do not take this as a criticism or\npointing-out-concrete-problems in the latter series.  At least for the\nlatter one, it is still an unsubstantiated fear.\n\n(1) \"The commit that touched this path the last time\"\n\nThe objective of this extension is to accept a pathspec and come up with\nthe name of the commit object that touched the given pathspec recently.\nThere can be some variations to the parameter the feature could take, and\nin the most generic form, the query would be:\n\n * set of commits: start digging from these;\n * pathspecs: find commits whose change touches some path that matches them;\n * N: do not stop at the latest change, but find the Nth one.\n\nI am not good at coming up with a notation, so I'll leave the actual\nsyntax to other people, but you can say things like:\n\n\tgit diff <<--all, Documentation t, 4>>\n\tgit log <<master, Makefile, 10>>..next -- Makefile\n\nThe problem with this feature is this.  In order to come up with the\nanswer, you need to internally run:\n\n\tgit rev-list --all -- Documentation t\n\tgit rev-list master -- Makefile\n\nand in order to compute this, the revision traversal machinery has to\nnot only open and parse commit objects but _rewrite_ the parent field of\nthese commits for history simplification purposes.  This is a destructive\noperation, and after it comes back, we would need to clean them up by\nunparsing these commits so that the original command that asked for its\ncommand line arguments to be parsed can operate correctly.\n\nAt least, this can be fixed by unparsing all the objects parsed so far\nwhen the revision command line parser finishes each such argument, and\nforce the caller parse them again when they read from rev->pending.\n\n\n(2) \"refs/replace/SHA-1 holds name of the object that replaces SHA-1\"\n\nThe objective of this extension is to generalize the graft mechanism, so\nthat they can be shared across the usual object transfer mechanism, the\nordinary revision traversal mechanism can honor the replacement just like\nthey honor grafts file, while reachability machinery used by object\ntransfer, fsck and prune can ignore the replacement.  When refs/replace/A\nhas value B, a commit C that records A as its parent would behave as if B\nis its parent (so \"git log C\" will show B as the second entry in its\noutput), while pushing C will notice C's true parent is A and sends A to\nthe other end.  By pushing refs/replace/A to the other end, you can share\nthe same \"fake\" history across repositories.\n\nThis feature has similar problem as (1).  If you say\n\n\tgit rev-list master..next^^\n\nhow should next^^ be interpreted?  Because it is an input coming from the\nuser, we may want to honor the \"graft/replace\", and we need to read and\nparse next, next^, and next^^ while possibly reading from refs/replace/\nhiearchy.  But after getting the end result, the name of the commit object\nthe notation refers to, we would need to unparse the objects involved so\nthat the main parts of the command can start from the clean slate.\n\nThere are three issues with this:\n\n (1) When you ask for the data for a concrete SHA-1 (i.e. not \"next^\", but\n     something like \"9856dd811ee0f256d067b89cbccb58d944aa9c8c\"), with and\n     without grafts/replacements, the actual object data changes.\n\n     This again needs at least unparsing of all the objects as in the\n     <<commits, pathspecs, Nth>> case above to deal with.\n\n (2) Depending on the use of grafts/replacements, the interpretation of\n     \"next^^\" changes; should we follow the true parenthood, or the\n     replaced one?\n\n     In order to give Porcelains the same freedom to honor or ignore the\n     replacements, we would eventually need to expose --ignore-replace\n     option to \"git rev-list\":\n\n\tgit rev-list --ignore-replace master..next^^\n\n     It gets tricky when --ignore-replace should take effect.  Before or\n     after master..next^^ is interpreted?\n\n (3) Depending on the use of replacements, even the type of the object an\n     extended SHA-1 expression refers to can change.  tag-foo may point at\n     an object 9856dd811ee0f256d067b89cbccb58d944aa9c8c, and without\n     replacements it may be a commit but the replacement mechanism is\n     allowed to say it is another tag that points at a commit.\n\nNot that the \"replacement\" feature is bad, it is just that the feature is\ntoo flexible for the current codebase to handle sanely.\n\nIn order to solve these issues safely (not necessarily efficiently nor\ncleanly), I think we might need to change the setup_revisions() to:\n\n (1) operate the way it currently does, leaving the parsed objects in\n     revs->pending;\n\n (2) remember the objects' SHA-1 and flags in the revs->pending array;\n\n (3) discard all parsed objects, as revision traversals may have rewritten\n     commits for history simplification purposes; and\n\n (4) rebuild revs->pending list by parsing the objects again, using the\n     SHA-1 values we remembered in step (2)\n\nor something like that.\n"},{"id":"109672","messageId":"20090327181547.GA4933@gmail.com","threadId":"18482","inReplyTo":"7vab76evaj.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH] difftool: add support for an extended revision syntax","fromName":"David Aguilar","fromEmail":"davvid@gmail.com","sentAt":"2009-03-27T18:16:15Z","receivedAt":"2009-03-27T18:16:15Z","isPatch":true,"sender":{"key":"davvid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13196?v=4"},"body":"On  0, Junio C Hamano <gitster@pobox.com> wrote:\n> David Aguilar <davvid@gmail.com> writes:\n> \n> > I noticed that my patches for \"add tests to difftool\" and \"add\n> > the -y shortcut for --no-prompt\" were in git.git's pu branch\n> > the other day but aren't there anymore.  Was that intentional?\n> \n> Yes, it makes 'pu' not pass the self test.\n\n\nOops, was this related to your suggestions about the patch\nthat added the tests?  I sent a v2 patch that included the\nfixups.  I'll try running the tests when I get back home to my\nLinux machine on Monday (I'm currently in Chicago on a trip)\nthough I'm not sure if I'd be able to reproduce the failure if\nit's platform-specific.\n\nSorry 'bout that.\n\n-- \n\n\tDavid\n"}]}