All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/9 v6] difftool: teach command to perform directory diffs
@ 2012-03-22 19:52 Tim Henigan
  2012-03-22 19:52 ` [PATCH 1/9 v6] difftool: parse options using Getopt::Long Tim Henigan
                   ` (8 more replies)
  0 siblings, 9 replies; 10+ messages in thread
From: Tim Henigan @ 2012-03-22 19:52 UTC (permalink / raw)
  To: gitster, git, davvid; +Cc: Tim Henigan

Changes in v6:
  - Rebased series to insure Getopt::Long worked correctly from the start.
  - Squashed commits that added tests into the commits that added the
    new features.

Series Overview:

'git difftool' is a very useful command that allows git diffs to be opened
in an external tool. Currently, difftool opens a separate instance of the
external tool for each file that changed.  This can be tedious when many
files have changed.

This series teaches difftool to perform directory diffs, so that all file
changes can be opened/reviewed in a single instance of the external tool.

This is the second phase of development for this feature.  The first phase
was added as a separate command (git diffall) in 1252bbe (contrib: add
git-diffall script).  During review of that script on the Git developers
list, an informal development roadmap was suggested [1]. The next phase
of that plan is to integrate the 'git-diffall' feature into 'difftool'.
This series gets that done.

[1]: http://thread.gmane.org/gmane.comp.version-control.git/191297/focus=191383


Tim Henigan (9):
  difftool: parse options using Getopt::Long
  difftool: add '--no-gui' option
  difftool: exit(0) when usage is printed
  difftool: remove explicit change of PATH
  difftool: stop appending '.exe' to git
  difftool: eliminate setup_environment function
  difftool: replace system call with Git::command_noisy
  difftool: teach difftool to handle directory diffs
  difftool: print list of valid tools with '--tool-help'

 Documentation/git-difftool.txt |   17 ++-
 git-difftool--helper.sh        |   19 ++-
 git-difftool.perl              |  264 +++++++++++++++++++++++++++-------------
 t/t7800-difftool.sh            |   57 +++++++++
 4 files changed, 262 insertions(+), 95 deletions(-)

-- 
1.7.10.rc1.36.g15e879

^ permalink raw reply	[flat|nested] 10+ messages in thread

* [PATCH 1/9 v6] difftool: parse options using Getopt::Long
  2012-03-22 19:52 [PATCH 0/9 v6] difftool: teach command to perform directory diffs Tim Henigan
@ 2012-03-22 19:52 ` Tim Henigan
  2012-03-22 19:52 ` [PATCH 2/9 v6] difftool: add '--no-gui' option Tim Henigan
                   ` (7 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Tim Henigan @ 2012-03-22 19:52 UTC (permalink / raw)
  To: gitster, git, davvid; +Cc: Tim Henigan

Replace custom option/argument parser with standard Getopt::Long
module.  This shortens the code and makes it easier to understand.

Signed-off-by: Tim Henigan <tim.henigan@gmail.com>
---

Changes in v6:
  - Patch 10/9 from the previous version of the series (difftool: fix
    regression in '--prompt' options) has been squashed into this commit.
    Now the '--[no-]prompt' options are properly handled from the start.


 git-difftool.perl |  111 ++++++++++++++++++++++-------------------------------
 1 file changed, 46 insertions(+), 65 deletions(-)

diff --git a/git-difftool.perl b/git-difftool.perl
index 09b65f1..f4e28a6 100755
--- a/git-difftool.perl
+++ b/git-difftool.perl
@@ -15,17 +15,15 @@ use strict;
 use warnings;
 use Cwd qw(abs_path);
 use File::Basename qw(dirname);
-
-require Git;
-
-my $DIR = abs_path(dirname($0));
-
+use Getopt::Long qw(:config pass_through);
+use Git;
 
 sub usage
 {
 	print << 'USAGE';
-usage: git difftool [-t|--tool=<tool>] [-x|--extcmd=<cmd>]
-                    [-y|--no-prompt]   [-g|--gui]
+usage: git difftool [-t|--tool=<tool>]
+                    [-x|--extcmd=<cmd>] [-g|--gui]
+                    [--prompt] [-y|--no-prompt]
                     ['git diff' options]
 USAGE
 	exit 1;
@@ -33,6 +31,7 @@ USAGE
 
 sub setup_environment
 {
+	my $DIR = abs_path(dirname($0));
 	$ENV{PATH} = "$DIR:$ENV{PATH}";
 	$ENV{GIT_PAGER} = '';
 	$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';
@@ -47,75 +46,57 @@ sub exe
 	return $exe;
 }
 
-sub generate_command
-{
-	my @command = (exe('git'), 'diff');
-	my $skip_next = 0;
-	my $idx = -1;
-	my $prompt = '';
-	for my $arg (@ARGV) {
-		$idx++;
-		if ($skip_next) {
-			$skip_next = 0;
-			next;
-		}
-		if ($arg eq '-t' || $arg eq '--tool') {
-			usage() if $#ARGV <= $idx;
-			$ENV{GIT_DIFF_TOOL} = $ARGV[$idx + 1];
-			$skip_next = 1;
-			next;
-		}
-		if ($arg =~ /^--tool=/) {
-			$ENV{GIT_DIFF_TOOL} = substr($arg, 7);
-			next;
-		}
-		if ($arg eq '-x' || $arg eq '--extcmd') {
-			usage() if $#ARGV <= $idx;
-			$ENV{GIT_DIFFTOOL_EXTCMD} = $ARGV[$idx + 1];
-			$skip_next = 1;
-			next;
-		}
-		if ($arg =~ /^--extcmd=/) {
-			$ENV{GIT_DIFFTOOL_EXTCMD} = substr($arg, 9);
-			next;
-		}
-		if ($arg eq '-g' || $arg eq '--gui') {
-			eval {
-				my $tool = Git::command_oneline('config',
-				                                'diff.guitool');
-				if (length($tool)) {
-					$ENV{GIT_DIFF_TOOL} = $tool;
-				}
-			};
-			next;
-		}
-		if ($arg eq '-y' || $arg eq '--no-prompt') {
-			$prompt = 'no';
-			next;
-		}
-		if ($arg eq '--prompt') {
-			$prompt = 'yes';
-			next;
-		}
-		if ($arg eq '-h') {
-			usage();
-		}
-		push @command, $arg;
+# parse command-line options. all unrecognized options and arguments
+# are passed through to the 'git diff' command.
+my ($difftool_cmd, $extcmd, $gui, $help, $prompt);
+GetOptions('g|gui' => \$gui,
+	'h' => \$help,
+	'prompt!' => \$prompt,
+	'y' => sub { $prompt = 0; },
+	't|tool:s' => \$difftool_cmd,
+	'x|extcmd:s' => \$extcmd);
+
+if (defined($help)) {
+	usage();
+} 
+if (defined($difftool_cmd)) {
+	if (length($difftool_cmd) > 0) {
+		$ENV{GIT_DIFF_TOOL} = $difftool_cmd;
+	} else {
+		print "No <tool> given for --tool=<tool>\n";
+		usage();
 	}
-	if ($prompt eq 'yes') {
+}
+if (defined($extcmd)) {
+	if (length($extcmd) > 0) {
+		$ENV{GIT_DIFFTOOL_EXTCMD} = $extcmd;
+	} else {
+		print "No <cmd> given for --extcmd=<cmd>\n";
+		usage();
+	}
+}
+if (defined($gui)) {
+	my $guitool = "";
+	$guitool = Git::config('diff.guitool');
+	if (length($guitool) > 0) {
+		$ENV{GIT_DIFF_TOOL} = $guitool;
+	}
+}
+if (defined($prompt)) {
+	if ($prompt) {
 		$ENV{GIT_DIFFTOOL_PROMPT} = 'true';
-	} elsif ($prompt eq 'no') {
+	} else {
 		$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
 	}
-	return @command
 }
 
 setup_environment();
+my @command = (exe('git'), 'diff', @ARGV);
 
 # ActiveState Perl for Win32 does not implement POSIX semantics of
 # exec* system call. It just spawns the given executable and finishes
 # the starting program, exiting with code 0.
 # system will at least catch the errors returned by git diff,
 # allowing the caller of git difftool better handling of failures.
-my $rc = system(generate_command());
+my $rc = system(@command);
 exit($rc | ($rc >> 8));
-- 
1.7.10.rc1.36.g15e879

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 2/9 v6] difftool: add '--no-gui' option
  2012-03-22 19:52 [PATCH 0/9 v6] difftool: teach command to perform directory diffs Tim Henigan
  2012-03-22 19:52 ` [PATCH 1/9 v6] difftool: parse options using Getopt::Long Tim Henigan
@ 2012-03-22 19:52 ` Tim Henigan
  2012-03-22 19:52 ` [PATCH 3/9 v6] difftool: exit(0) when usage is printed Tim Henigan
                   ` (6 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Tim Henigan @ 2012-03-22 19:52 UTC (permalink / raw)
  To: gitster, git, davvid; +Cc: Tim Henigan

This commit teaches difftool to handle the '--no-gui' option. This option
negates the existing '--gui' option. The last setting given on the command
line wins.

This allows a user to configure "[alias] mdt = difftool --gui", but still
have the ability to override the setting without error:

$ git mdt --no-gui

Signed-off-by: Tim Henigan <tim.henigan@gmail.com>
---

Changes in v6:
  - This was patch 13/9 in the previous version of the series. It has
    simply been moved to 2/9.


 git-difftool.perl   |    7 ++++---
 t/t7800-difftool.sh |   13 +++++++++++++
 2 files changed, 17 insertions(+), 3 deletions(-)

diff --git a/git-difftool.perl b/git-difftool.perl
index f4e28a6..bf51b79 100755
--- a/git-difftool.perl
+++ b/git-difftool.perl
@@ -22,7 +22,8 @@ sub usage
 {
 	print << 'USAGE';
 usage: git difftool [-t|--tool=<tool>]
-                    [-x|--extcmd=<cmd>] [-g|--gui]
+                    [-x|--extcmd=<cmd>]
+                    [-g|--gui] [--no-gui]
                     [--prompt] [-y|--no-prompt]
                     ['git diff' options]
 USAGE
@@ -49,7 +50,7 @@ sub exe
 # parse command-line options. all unrecognized options and arguments
 # are passed through to the 'git diff' command.
 my ($difftool_cmd, $extcmd, $gui, $help, $prompt);
-GetOptions('g|gui' => \$gui,
+GetOptions('g|gui!' => \$gui,
 	'h' => \$help,
 	'prompt!' => \$prompt,
 	'y' => sub { $prompt = 0; },
@@ -75,7 +76,7 @@ if (defined($extcmd)) {
 		usage();
 	}
 }
-if (defined($gui)) {
+if ($gui) {
 	my $guitool = "";
 	$guitool = Git::config('diff.guitool');
 	if (length($guitool) > 0) {
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
index 4fb4c93..e716d06 100755
--- a/t/t7800-difftool.sh
+++ b/t/t7800-difftool.sh
@@ -94,6 +94,19 @@ test_expect_success PERL 'difftool honors --gui' '
 	restore_test_defaults
 '
 
+test_expect_success PERL 'difftool --gui last setting wins' '
+	git config diff.guitool bogus-tool &&
+	git difftool --no-prompt --gui --no-gui &&
+
+	git config merge.tool bogus-tool &&
+	git config diff.tool bogus-tool &&
+	git config diff.guitool test-tool &&
+	diff=$(git difftool --no-prompt --no-gui --gui branch) &&
+	test "$diff" = "branch" &&
+
+	restore_test_defaults
+'
+
 test_expect_success PERL 'difftool --gui works without configured diff.guitool' '
 	git config diff.tool test-tool &&
 
-- 
1.7.10.rc1.36.g15e879

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 3/9 v6] difftool: exit(0) when usage is printed
  2012-03-22 19:52 [PATCH 0/9 v6] difftool: teach command to perform directory diffs Tim Henigan
  2012-03-22 19:52 ` [PATCH 1/9 v6] difftool: parse options using Getopt::Long Tim Henigan
  2012-03-22 19:52 ` [PATCH 2/9 v6] difftool: add '--no-gui' option Tim Henigan
@ 2012-03-22 19:52 ` Tim Henigan
  2012-03-22 19:52 ` [PATCH 4/9 v6] difftool: remove explicit change of PATH Tim Henigan
                   ` (5 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Tim Henigan @ 2012-03-22 19:52 UTC (permalink / raw)
  To: gitster, git, davvid; +Cc: Tim Henigan

Prior to this commit, the script exited with an error whenever the
usage string was printed, regardless of the reason it was done. In
cases where usage was printed due to a user request (e.g. '-h'
option), the script should exit without error (exit 0).

This commit adds an argument to the usage function that allows the
exit code to be specified when the function is called.

Signed-off-by: Tim Henigan <tim.henigan@gmail.com>
---
 git-difftool.perl |    9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/git-difftool.perl b/git-difftool.perl
index bf51b79..33ae30a 100755
--- a/git-difftool.perl
+++ b/git-difftool.perl
@@ -20,6 +20,7 @@ use Git;
 
 sub usage
 {
+	my $exitcode = shift;
 	print << 'USAGE';
 usage: git difftool [-t|--tool=<tool>]
                     [-x|--extcmd=<cmd>]
@@ -27,7 +28,7 @@ usage: git difftool [-t|--tool=<tool>]
                     [--prompt] [-y|--no-prompt]
                     ['git diff' options]
 USAGE
-	exit 1;
+	exit($exitcode);
 }
 
 sub setup_environment
@@ -58,14 +59,14 @@ GetOptions('g|gui!' => \$gui,
 	'x|extcmd:s' => \$extcmd);
 
 if (defined($help)) {
-	usage();
+	usage(0);
 } 
 if (defined($difftool_cmd)) {
 	if (length($difftool_cmd) > 0) {
 		$ENV{GIT_DIFF_TOOL} = $difftool_cmd;
 	} else {
 		print "No <tool> given for --tool=<tool>\n";
-		usage();
+		usage(1);
 	}
 }
 if (defined($extcmd)) {
@@ -73,7 +74,7 @@ if (defined($extcmd)) {
 		$ENV{GIT_DIFFTOOL_EXTCMD} = $extcmd;
 	} else {
 		print "No <cmd> given for --extcmd=<cmd>\n";
-		usage();
+		usage(1);
 	}
 }
 if ($gui) {
-- 
1.7.10.rc1.36.g15e879

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 4/9 v6] difftool: remove explicit change of PATH
  2012-03-22 19:52 [PATCH 0/9 v6] difftool: teach command to perform directory diffs Tim Henigan
                   ` (2 preceding siblings ...)
  2012-03-22 19:52 ` [PATCH 3/9 v6] difftool: exit(0) when usage is printed Tim Henigan
@ 2012-03-22 19:52 ` Tim Henigan
  2012-03-22 19:52 ` [PATCH 5/9 v6] difftool: stop appending '.exe' to git Tim Henigan
                   ` (4 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Tim Henigan @ 2012-03-22 19:52 UTC (permalink / raw)
  To: gitster, git, davvid; +Cc: Tim Henigan

Adding the script directory to PATH is not needed. The script is
located at '$(git --exec-path)', which is already on the PATH.

Signed-off-by: Tim Henigan <tim.henigan@gmail.com>
---
 git-difftool.perl |    4 ----
 1 file changed, 4 deletions(-)

diff --git a/git-difftool.perl b/git-difftool.perl
index 33ae30a..77c9907 100755
--- a/git-difftool.perl
+++ b/git-difftool.perl
@@ -13,8 +13,6 @@
 use 5.008;
 use strict;
 use warnings;
-use Cwd qw(abs_path);
-use File::Basename qw(dirname);
 use Getopt::Long qw(:config pass_through);
 use Git;
 
@@ -33,8 +31,6 @@ USAGE
 
 sub setup_environment
 {
-	my $DIR = abs_path(dirname($0));
-	$ENV{PATH} = "$DIR:$ENV{PATH}";
 	$ENV{GIT_PAGER} = '';
 	$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';
 }
-- 
1.7.10.rc1.36.g15e879

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 5/9 v6] difftool: stop appending '.exe' to git
  2012-03-22 19:52 [PATCH 0/9 v6] difftool: teach command to perform directory diffs Tim Henigan
                   ` (3 preceding siblings ...)
  2012-03-22 19:52 ` [PATCH 4/9 v6] difftool: remove explicit change of PATH Tim Henigan
@ 2012-03-22 19:52 ` Tim Henigan
  2012-03-22 19:52 ` [PATCH 6/9 v6] difftool: eliminate setup_environment function Tim Henigan
                   ` (3 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Tim Henigan @ 2012-03-22 19:52 UTC (permalink / raw)
  To: gitster, git, davvid; +Cc: Tim Henigan

The system call to Git works the same whether or not ".exe" is
appended to "git". The extra code is not necessary.

Signed-off-by: Tim Henigan <tim.henigan@gmail.com>
---
 git-difftool.perl |   11 +----------
 1 file changed, 1 insertion(+), 10 deletions(-)

diff --git a/git-difftool.perl b/git-difftool.perl
index 77c9907..493260d 100755
--- a/git-difftool.perl
+++ b/git-difftool.perl
@@ -35,15 +35,6 @@ sub setup_environment
 	$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';
 }
 
-sub exe
-{
-	my $exe = shift;
-	if ($^O eq 'MSWin32' || $^O eq 'msys') {
-		return "$exe.exe";
-	}
-	return $exe;
-}
-
 # parse command-line options. all unrecognized options and arguments
 # are passed through to the 'git diff' command.
 my ($difftool_cmd, $extcmd, $gui, $help, $prompt);
@@ -89,7 +80,7 @@ if (defined($prompt)) {
 }
 
 setup_environment();
-my @command = (exe('git'), 'diff', @ARGV);
+my @command = ('git', 'diff', @ARGV);
 
 # ActiveState Perl for Win32 does not implement POSIX semantics of
 # exec* system call. It just spawns the given executable and finishes
-- 
1.7.10.rc1.36.g15e879

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 6/9 v6] difftool: eliminate setup_environment function
  2012-03-22 19:52 [PATCH 0/9 v6] difftool: teach command to perform directory diffs Tim Henigan
                   ` (4 preceding siblings ...)
  2012-03-22 19:52 ` [PATCH 5/9 v6] difftool: stop appending '.exe' to git Tim Henigan
@ 2012-03-22 19:52 ` Tim Henigan
  2012-03-22 19:52 ` [PATCH 7/9 v6] difftool: replace system call with Git::command_noisy Tim Henigan
                   ` (2 subsequent siblings)
  8 siblings, 0 replies; 10+ messages in thread
From: Tim Henigan @ 2012-03-22 19:52 UTC (permalink / raw)
  To: gitster, git, davvid; +Cc: Tim Henigan

Removing this function shortens the code and makes it easier to read.
Now all environment variables are set as part of procedural operation.

Signed-off-by: Tim Henigan <tim.henigan@gmail.com>
---
 git-difftool.perl |    9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)

diff --git a/git-difftool.perl b/git-difftool.perl
index 493260d..d4fe998 100755
--- a/git-difftool.perl
+++ b/git-difftool.perl
@@ -29,12 +29,6 @@ USAGE
 	exit($exitcode);
 }
 
-sub setup_environment
-{
-	$ENV{GIT_PAGER} = '';
-	$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';
-}
-
 # parse command-line options. all unrecognized options and arguments
 # are passed through to the 'git diff' command.
 my ($difftool_cmd, $extcmd, $gui, $help, $prompt);
@@ -79,7 +73,8 @@ if (defined($prompt)) {
 	}
 }
 
-setup_environment();
+$ENV{GIT_PAGER} = '';
+$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';
 my @command = ('git', 'diff', @ARGV);
 
 # ActiveState Perl for Win32 does not implement POSIX semantics of
-- 
1.7.10.rc1.36.g15e879

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 7/9 v6] difftool: replace system call with Git::command_noisy
  2012-03-22 19:52 [PATCH 0/9 v6] difftool: teach command to perform directory diffs Tim Henigan
                   ` (5 preceding siblings ...)
  2012-03-22 19:52 ` [PATCH 6/9 v6] difftool: eliminate setup_environment function Tim Henigan
@ 2012-03-22 19:52 ` Tim Henigan
  2012-03-22 19:52 ` [PATCH 8/9 v6] difftool: teach difftool to handle directory diffs Tim Henigan
  2012-03-22 19:52 ` [PATCH 9/9 v6] difftool: print list of valid tools with '--tool-help' Tim Henigan
  8 siblings, 0 replies; 10+ messages in thread
From: Tim Henigan @ 2012-03-22 19:52 UTC (permalink / raw)
  To: gitster, git, davvid; +Cc: Tim Henigan

The Git.pm module includes functions intended to standardize working
with Git repositories in Perl scripts. This commit teaches difftool
to use Git::command_noisy rather than a system call to run the diff
command.

Signed-off-by: Tim Henigan <tim.henigan@gmail.com>
---
 git-difftool.perl |   10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/git-difftool.perl b/git-difftool.perl
index d4fe998..7366bf5 100755
--- a/git-difftool.perl
+++ b/git-difftool.perl
@@ -75,12 +75,4 @@ if (defined($prompt)) {
 
 $ENV{GIT_PAGER} = '';
 $ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';
-my @command = ('git', 'diff', @ARGV);
-
-# ActiveState Perl for Win32 does not implement POSIX semantics of
-# exec* system call. It just spawns the given executable and finishes
-# the starting program, exiting with code 0.
-# system will at least catch the errors returned by git diff,
-# allowing the caller of git difftool better handling of failures.
-my $rc = system(@command);
-exit($rc | ($rc >> 8));
+git_cmd_try { Git::command_noisy(('diff', @ARGV)) } 'exit code %d';
-- 
1.7.10.rc1.36.g15e879

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 8/9 v6] difftool: teach difftool to handle directory diffs
  2012-03-22 19:52 [PATCH 0/9 v6] difftool: teach command to perform directory diffs Tim Henigan
                   ` (6 preceding siblings ...)
  2012-03-22 19:52 ` [PATCH 7/9 v6] difftool: replace system call with Git::command_noisy Tim Henigan
@ 2012-03-22 19:52 ` Tim Henigan
  2012-03-22 19:52 ` [PATCH 9/9 v6] difftool: print list of valid tools with '--tool-help' Tim Henigan
  8 siblings, 0 replies; 10+ messages in thread
From: Tim Henigan @ 2012-03-22 19:52 UTC (permalink / raw)
  To: gitster, git, davvid; +Cc: Tim Henigan

Prior to this commit, the difftool utility could only open files one
at a time.  The new '--dir-diff' option copies all the modified files
to a temporary location and runs a directory diff on them.

Signed-off-by: Tim Henigan <tim.henigan@gmail.com>
---

Changes in v6:
  - Squashed "teach --dir-diff to copy modified files back" into this
    commit.
  - Squashed "add tests for difftool --dir-diff" into this commit.


 Documentation/git-difftool.txt |    6 ++
 git-difftool--helper.sh        |   19 +++--
 git-difftool.perl              |  151 +++++++++++++++++++++++++++++++++++++---
 t/t7800-difftool.sh            |   39 +++++++++++
 4 files changed, 198 insertions(+), 17 deletions(-)

diff --git a/Documentation/git-difftool.txt b/Documentation/git-difftool.txt
index fe38f66..aba5e76 100644
--- a/Documentation/git-difftool.txt
+++ b/Documentation/git-difftool.txt
@@ -19,6 +19,12 @@ linkgit:git-diff[1].
 
 OPTIONS
 -------
+-d::
+--dir-diff::
+	Copy the modified files to a temporary location and perform
+	a directory diff on them. This mode never prompts before
+	launching the diff tool.
+
 -y::
 --no-prompt::
 	Do not prompt before launching a diff tool.
diff --git a/git-difftool--helper.sh b/git-difftool--helper.sh
index e6558d1..3d0fe0c 100755
--- a/git-difftool--helper.sh
+++ b/git-difftool--helper.sh
@@ -73,9 +73,16 @@ then
 	fi
 fi
 
-# Launch the merge tool on each path provided by 'git diff'
-while test $# -gt 6
-do
-	launch_merge_tool "$1" "$2" "$5"
-	shift 7
-done
+if test -n "$GIT_DIFFTOOL_DIRDIFF"
+then
+	LOCAL="$1"
+	REMOTE="$2"
+	run_merge_tool "$merge_tool" false
+else
+	# Launch the merge tool on each path provided by 'git diff'
+	while test $# -gt 6
+	do
+		launch_merge_tool "$1" "$2" "$5"
+		shift 7
+	done
+fi
diff --git a/git-difftool.perl b/git-difftool.perl
index 7366bf5..0fa131c 100755
--- a/git-difftool.perl
+++ b/git-difftool.perl
@@ -1,21 +1,29 @@
 #!/usr/bin/env perl
 # Copyright (c) 2009, 2010 David Aguilar
+# Copyright (c) 2012 Tim Henigan
 #
 # This is a wrapper around the GIT_EXTERNAL_DIFF-compatible
 # git-difftool--helper script.
 #
 # This script exports GIT_EXTERNAL_DIFF and GIT_PAGER for use by git.
-# GIT_DIFFTOOL_NO_PROMPT, GIT_DIFFTOOL_PROMPT, and GIT_DIFF_TOOL
-# are exported for use by git-difftool--helper.
+# GIT_DIFFTOOL_NO_PROMPT, GIT_DIFFTOOL_PROMPT, GIT_DIFFTOOL_DIRDIFF,
+# and GIT_DIFF_TOOL are exported for use by git-difftool--helper.
 #
 # Any arguments that are unknown to this script are forwarded to 'git diff'.
 
 use 5.008;
 use strict;
 use warnings;
+use File::Basename qw(dirname);
+use File::Copy;
+use File::stat;
+use File::Path qw(mkpath);
+use File::Temp qw(tempdir);
 use Getopt::Long qw(:config pass_through);
 use Git;
 
+my @working_tree;
+
 sub usage
 {
 	my $exitcode = shift;
@@ -24,15 +32,109 @@ usage: git difftool [-t|--tool=<tool>]
                     [-x|--extcmd=<cmd>]
                     [-g|--gui] [--no-gui]
                     [--prompt] [-y|--no-prompt]
+                    [-d|--dir-diff]
                     ['git diff' options]
 USAGE
 	exit($exitcode);
 }
 
+sub setup_dir_diff
+{
+	# Run the diff; exit immediately if no diff found
+	my $repo = Git->repository();
+	my $diffrtn = $repo->command_oneline(['diff', '--raw', '--no-abbrev', '-z', @ARGV]);
+	exit(0) if (length($diffrtn) == 0);
+
+	# Setup temp directories
+	my $tmpdir = tempdir('git-diffall.XXXXX', CLEANUP => 1, TMPDIR => 1);
+	my $ldir = "$tmpdir/left";
+	my $rdir = "$tmpdir/right";
+	mkpath($ldir) or die $!;
+	mkpath($rdir) or die $!;
+
+	# Build index info for left and right sides of the diff
+	my $submodule_mode = "160000";
+	my $null_mode = 0 x 6;
+	my $null_sha1 = 0 x 40;
+	my $lindex = "";
+	my $rindex = "";
+	my %submodule;
+	my @rawdiff = split('\0', $diffrtn);
+
+	for (my $i=0; $i<@rawdiff; $i+=2) {
+		my ($lmode, $rmode, $lsha1, $rsha1, $status) = split(' ', substr($rawdiff[$i], 1));
+		my $path = $rawdiff[$i + 1];
+
+		if (($lmode eq $submodule_mode) or ($rmode eq $submodule_mode)) {
+			$submodule{$path}{left} = $lsha1;
+			$submodule{$path}{right} = $rsha1;
+			next;
+		}
+
+		if ($lmode ne $null_mode) {
+			$lindex .= "$lmode $lsha1\t$path\0";
+		}
+
+		if ($rmode ne $null_mode) {
+			if ($rsha1 ne $null_sha1) {
+				$rindex .= "$rmode $rsha1\t$path\0";
+			} else {
+				push(@working_tree, $path);
+			}
+		}
+	}
+
+	# Populate the left and right directories based on each index file
+	my ($inpipe, $ctx);
+	$ENV{GIT_DIR} = $repo->repo_path();
+	$ENV{GIT_INDEX_FILE} = "$tmpdir/lindex";
+	($inpipe, $ctx) = $repo->command_input_pipe(qw/update-index -z --index-info/);
+	print($inpipe $lindex);
+	$repo->command_close_pipe($inpipe, $ctx);
+	system(('git', 'checkout-index', '--all', "--prefix=$ldir/"));
+
+	$ENV{GIT_INDEX_FILE} = "$tmpdir/rindex";
+	($inpipe, $ctx) = $repo->command_input_pipe(qw/update-index -z --index-info/);
+	print($inpipe $rindex);
+	$repo->command_close_pipe($inpipe, $ctx);
+	system(('git', 'checkout-index', '--all', "--prefix=$rdir/"));
+
+	# Changes in the working tree need special treatment since they are
+	# not part of the index
+	my $workdir = $repo->repo_path() . "/..";
+	for (@working_tree) {
+		my $dir = dirname($_);
+		unless (-d "$rdir/$dir") {
+			mkpath("$rdir/$dir") or die $!;
+		}
+		copy("$workdir/$_", "$rdir/$_") or die $!;
+		chmod(stat("$workdir/$_")->mode, "$rdir/$_") or die $!;
+	}
+
+	# Changes to submodules require special treatment. This loop writes a
+	# temporary file to both the left and right directories to show the
+	# change in the recorded SHA1 for the submodule.
+	foreach my $path (keys %submodule) {
+		if (defined $submodule{$path}{left}) {
+			open(my $fh, ">", "$ldir/$path") or die $!;
+			print($fh "Subproject commit $submodule{$path}{left}");
+			close($fh);
+		}
+		if (defined $submodule{$path}{right}) {
+			open(my $fh, ">", "$rdir/$path") or die $!;
+			print($fh "Subproject commit $submodule{$path}{right}");
+			close($fh);
+		}
+	}
+
+	return ($ldir, $rdir);
+}
+
 # parse command-line options. all unrecognized options and arguments
 # are passed through to the 'git diff' command.
-my ($difftool_cmd, $extcmd, $gui, $help, $prompt);
+my ($difftool_cmd, $dirdiff, $extcmd, $gui, $help, $prompt);
 GetOptions('g|gui!' => \$gui,
+	'd|dir-diff' => \$dirdiff,
 	'h' => \$help,
 	'prompt!' => \$prompt,
 	'y' => sub { $prompt = 0; },
@@ -65,14 +167,41 @@ if ($gui) {
 		$ENV{GIT_DIFF_TOOL} = $guitool;
 	}
 }
-if (defined($prompt)) {
-	if ($prompt) {
-		$ENV{GIT_DIFFTOOL_PROMPT} = 'true';
+
+# In directory diff mode, 'git-difftool--helper' is called once
+# to compare the a/b directories.  In file diff mode, 'git diff'
+# will invoke a separate instance of 'git-difftool--helper' for
+# each file that changed.
+if (defined($dirdiff)) {
+	my ($a, $b) = setup_dir_diff();
+	if (defined($extcmd)) {
+		system(($extcmd, $a, $b));
 	} else {
-		$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
+		$ENV{GIT_DIFFTOOL_DIRDIFF} = 'true';
+		git_cmd_try {
+			Git::command_noisy(('difftool--helper', $a, $b))
+		} 'exit code %d';
+	}
+
+	# If the diff including working copy files and those
+	# files were modified during the diff, then the changes
+	# should be copied back to the working tree
+	my $repo = Git->repository();
+	my $workdir = $repo->repo_path() . "/..";
+	for (@working_tree) {
+		copy("$b/$_", "$workdir/$_") or die $!;
+		chmod(stat("$b/$_")->mode, "$workdir/$_") or die $!;
+	}
+} else {
+	if (defined($prompt)) {
+		if ($prompt) {
+			$ENV{GIT_DIFFTOOL_PROMPT} = 'true';
+		} else {
+			$ENV{GIT_DIFFTOOL_NO_PROMPT} = 'true';
+		}
 	}
-}
 
-$ENV{GIT_PAGER} = '';
-$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';
-git_cmd_try { Git::command_noisy(('diff', @ARGV)) } 'exit code %d';
+	$ENV{GIT_PAGER} = '';
+	$ENV{GIT_EXTERNAL_DIFF} = 'git-difftool--helper';
+	git_cmd_try { Git::command_noisy(('diff', @ARGV)) } 'exit code %d';
+}
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
index e716d06..478c1be 100755
--- a/t/t7800-difftool.sh
+++ b/t/t7800-difftool.sh
@@ -319,4 +319,43 @@ test_expect_success PERL 'say no to the second file' '
 	echo "$diff" | stdin_doesnot_contain br2
 '
 
+test_expect_success PERL 'setup change in subdirectory' '
+	git checkout master &&
+	mkdir sub &&
+	echo master >sub/sub &&
+	git add sub/sub &&
+	git commit -m "added sub/sub" &&
+	echo test >>file &&
+	echo test >>sub/sub &&
+	git add . &&
+	git commit -m "modified both"
+'
+
+test_expect_success PERL 'difftool -d' '
+	diff=$(git difftool -d --extcmd ls branch) &&
+	echo "$diff" | stdin_contains sub &&
+	echo "$diff" | stdin_contains file
+'
+
+test_expect_success PERL 'difftool --dir-diff' '
+	diff=$(git difftool --dir-diff --extcmd ls branch) &&
+	echo "$diff" | stdin_contains sub &&
+	echo "$diff" | stdin_contains file
+'
+
+test_expect_success PERL 'difftool --dir-diff ignores --prompt' '
+	diff=$(git difftool --dir-diff --prompt --extcmd ls branch) &&
+	echo "$diff" | stdin_contains sub &&
+	echo "$diff" | stdin_contains file
+'
+
+test_expect_success PERL 'difftool --dir-diff from subdirectory' '
+	(
+		cd sub &&
+		diff=$(git difftool --dir-diff --extcmd ls branch) &&
+		echo "$diff" | stdin_contains sub &&
+		echo "$diff" | stdin_contains file
+	)
+'
+
 test_done
-- 
1.7.10.rc1.36.g15e879

^ permalink raw reply related	[flat|nested] 10+ messages in thread

* [PATCH 9/9 v6] difftool: print list of valid tools with '--tool-help'
  2012-03-22 19:52 [PATCH 0/9 v6] difftool: teach command to perform directory diffs Tim Henigan
                   ` (7 preceding siblings ...)
  2012-03-22 19:52 ` [PATCH 8/9 v6] difftool: teach difftool to handle directory diffs Tim Henigan
@ 2012-03-22 19:52 ` Tim Henigan
  8 siblings, 0 replies; 10+ messages in thread
From: Tim Henigan @ 2012-03-22 19:52 UTC (permalink / raw)
  To: gitster, git, davvid; +Cc: Tim Henigan

Since bc7a96a (mergetool--lib: Refactor tools into separate files,
2011-08-18), it is possible to add a new diff tool by creating a simple
script in the '$(git --exec-path)/mergetools' directory.  Updating the
difftool help text is still a manual process, and the documentation can
easily go out of sync.

Teach the command to read the list of valid tools from the 'mergetools'
directory and print them for the user when the '--tool-help' option is
given.

Signed-off-by: Tim Henigan <tim.henigan@gmail.com>
---

Changes in v6:
  - Squashed "added test for difftool --tool-help" into this commit.


 Documentation/git-difftool.txt |   11 ++++++-----
 git-difftool.perl              |   16 +++++++++++++---
 t/t7800-difftool.sh            |    5 +++++
 3 files changed, 24 insertions(+), 8 deletions(-)

diff --git a/Documentation/git-difftool.txt b/Documentation/git-difftool.txt
index aba5e76..31fc2e3 100644
--- a/Documentation/git-difftool.txt
+++ b/Documentation/git-difftool.txt
@@ -36,11 +36,9 @@ OPTIONS
 
 -t <tool>::
 --tool=<tool>::
-	Use the diff tool specified by <tool>.
-	Valid diff tools are:
-	araxis, bc3, deltawalker, diffuse, emerge, ecmerge, gvimdiff,
-	kdiff3,	kompare, meld, opendiff, p4merge, tkdiff, vimdiff and
-	xxdiff.
+	Use the diff tool specified by <tool>.  Valid values include
+	emerge, kompare, meld, and vimdiff. Run `git difftool --tool-help`
+	for the list of valid <tool> settings.
 +
 If a diff tool is not specified, 'git difftool'
 will use the configuration variable `diff.tool`.  If the
@@ -68,6 +66,9 @@ of the diff post-image.  `$MERGED` is the name of the file which is
 being compared. `$BASE` is provided for compatibility
 with custom merge tool commands and has the same value as `$MERGED`.
 
+--tool-help::
+	Print a list of diff tools that may be used with `--tool`.
+
 -x <command>::
 --extcmd=<command>::
 	Specify a custom command for viewing diffs.
diff --git a/git-difftool.perl b/git-difftool.perl
index 0fa131c..d9be7d6 100755
--- a/git-difftool.perl
+++ b/git-difftool.perl
@@ -14,7 +14,7 @@
 use 5.008;
 use strict;
 use warnings;
-use File::Basename qw(dirname);
+use File::Basename qw(dirname basename);
 use File::Copy;
 use File::stat;
 use File::Path qw(mkpath);
@@ -28,7 +28,7 @@ sub usage
 {
 	my $exitcode = shift;
 	print << 'USAGE';
-usage: git difftool [-t|--tool=<tool>]
+usage: git difftool [-t|--tool=<tool>] [--tool-help]
                     [-x|--extcmd=<cmd>]
                     [-g|--gui] [--no-gui]
                     [--prompt] [-y|--no-prompt]
@@ -132,18 +132,28 @@ sub setup_dir_diff
 
 # parse command-line options. all unrecognized options and arguments
 # are passed through to the 'git diff' command.
-my ($difftool_cmd, $dirdiff, $extcmd, $gui, $help, $prompt);
+my ($difftool_cmd, $dirdiff, $extcmd, $gui, $help, $prompt, $tool_help);
 GetOptions('g|gui!' => \$gui,
 	'd|dir-diff' => \$dirdiff,
 	'h' => \$help,
 	'prompt!' => \$prompt,
 	'y' => sub { $prompt = 0; },
 	't|tool:s' => \$difftool_cmd,
+	'tool-help' => \$tool_help,
 	'x|extcmd:s' => \$extcmd);
 
 if (defined($help)) {
 	usage(0);
 } 
+if (defined($tool_help)) {
+	my $gitpath = Git::exec_path();
+	print "'git difftool --tool=<tool>' may be set to one of the following:\n";
+	for (glob "$gitpath/mergetools/*") {
+		next if /defaults$/;
+		print "\t" . basename($_) . "\n";
+	}
+	exit(0);
+}
 if (defined($difftool_cmd)) {
 	if (length($difftool_cmd) > 0) {
 		$ENV{GIT_DIFF_TOOL} = $difftool_cmd;
diff --git a/t/t7800-difftool.sh b/t/t7800-difftool.sh
index 478c1be..bbe71e5 100755
--- a/t/t7800-difftool.sh
+++ b/t/t7800-difftool.sh
@@ -319,6 +319,11 @@ test_expect_success PERL 'say no to the second file' '
 	echo "$diff" | stdin_doesnot_contain br2
 '
 
+test_expect_success PERL 'difftool --tool-help' '
+	tool_help=$(git difftool --tool-help) &&
+	echo "$tool_help" | stdin_contains tool
+'
+
 test_expect_success PERL 'setup change in subdirectory' '
 	git checkout master &&
 	mkdir sub &&
-- 
1.7.10.rc1.36.g15e879

^ permalink raw reply related	[flat|nested] 10+ messages in thread

end of thread, other threads:[~2012-03-22 19:54 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-03-22 19:52 [PATCH 0/9 v6] difftool: teach command to perform directory diffs Tim Henigan
2012-03-22 19:52 ` [PATCH 1/9 v6] difftool: parse options using Getopt::Long Tim Henigan
2012-03-22 19:52 ` [PATCH 2/9 v6] difftool: add '--no-gui' option Tim Henigan
2012-03-22 19:52 ` [PATCH 3/9 v6] difftool: exit(0) when usage is printed Tim Henigan
2012-03-22 19:52 ` [PATCH 4/9 v6] difftool: remove explicit change of PATH Tim Henigan
2012-03-22 19:52 ` [PATCH 5/9 v6] difftool: stop appending '.exe' to git Tim Henigan
2012-03-22 19:52 ` [PATCH 6/9 v6] difftool: eliminate setup_environment function Tim Henigan
2012-03-22 19:52 ` [PATCH 7/9 v6] difftool: replace system call with Git::command_noisy Tim Henigan
2012-03-22 19:52 ` [PATCH 8/9 v6] difftool: teach difftool to handle directory diffs Tim Henigan
2012-03-22 19:52 ` [PATCH 9/9 v6] difftool: print list of valid tools with '--tool-help' Tim Henigan

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.