{"thread":{"id":"31072","subject":"[PATCH v2 0/5] difftool: Use symlinks in dir-diff mode","startedAt":"2012-07-23T03:57:06Z","lastAt":"2012-07-24T15:57:10Z","messageCount":13,"participants":["David Aguilar","Junio C Hamano","Tim Henigan"],"isPatch":true,"patchVersion":2,"patchTotal":5},"messages":[{"id":"195478","messageId":"1343015831-17498-1-git-send-email-davvid@gmail.com","threadId":"31072","inReplyTo":null,"subject":"[PATCH v2 0/5] difftool: Use symlinks in dir-diff mode","fromName":"David Aguilar","fromEmail":"davvid@gmail.com","sentAt":"2012-07-23T03:57:06Z","receivedAt":"2012-07-23T03:57:06Z","isPatch":true,"sender":{"key":"davvid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13196?v=4"},"body":"Teach the difftool script to use symlinks when doing\ndirectory diffs in --dir-diff mode.\n\nThis is v2 of the patch because I had a typo in one of the\ncommit messages and gmail ate 4/5 in the last round.\n\nDavid Aguilar (5):\n  difftool: Simplify print_tool_help()\n  difftool: Eliminate global variables\n  difftool: Move option values into a hash\n  difftool: Call the temp directory \"git-difftool\"\n  difftool: Use symlinks when diffing against the worktree\n\n Documentation/git-difftool.txt |   8 ++\n git-difftool.perl              | 184 ++++++++++++++++++++++++-----------------\n 2 files changed, 115 insertions(+), 77 deletions(-)\n\n-- \n1.7.11.2.255.g5f133da\n"},{"id":"195479","messageId":"1343015831-17498-2-git-send-email-davvid@gmail.com","threadId":"31072","inReplyTo":"1343015831-17498-1-git-send-email-davvid@gmail.com","subject":"[PATCH v2 1/5] difftool: Simplify print_tool_help()","fromName":"David Aguilar","fromEmail":"davvid@gmail.com","sentAt":"2012-07-23T03:57:07Z","receivedAt":"2012-07-23T03:57:07Z","isPatch":true,"sender":{"key":"davvid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13196?v=4"},"body":"Eliminate a global variable and File::Find usage by building upon\nbasename() and glob() instead.\n\nSigned-off-by: David Aguilar <davvid@gmail.com>\n---\n\nSame as before, resending because gmail ate patch 4/5\n\n git-difftool.perl | 25 +++++--------------------\n 1 file changed, 5 insertions(+), 20 deletions(-)\n\ndiff --git a/git-difftool.perl b/git-difftool.perl\nindex c079854..ac0ed63 100755\n--- a/git-difftool.perl\n+++ b/git-difftool.perl\n@@ -13,17 +13,15 @@\n use 5.008;\n use strict;\n use warnings;\n-use File::Basename qw(dirname);\n+use File::Basename qw(basename dirname);\n use File::Copy;\n use File::Compare;\n-use File::Find;\n use File::stat;\n use File::Path qw(mkpath);\n use File::Temp qw(tempdir);\n use Getopt::Long qw(:config pass_through);\n use Git;\n \n-my @tools;\n my @working_tree;\n my $rc;\n my $repo = Git->repository();\n@@ -65,26 +63,13 @@ sub find_worktree\n \n my $workdir = find_worktree();\n \n-sub filter_tool_scripts\n-{\n-\tif (-d $_) {\n-\t\tif ($_ ne \".\") {\n-\t\t\t# Ignore files in subdirectories\n-\t\t\t$File::Find::prune = 1;\n-\t\t}\n-\t} else {\n-\t\tif ((-f $_) && ($_ ne \"defaults\")) {\n-\t\t\tpush(@tools, $_);\n-\t\t}\n-\t}\n-}\n-\n sub print_tool_help\n {\n \tmy ($cmd, @found, @notfound);\n \tmy $gitpath = Git::exec_path();\n \n-\tfind(\\&filter_tool_scripts, \"$gitpath/mergetools\");\n+\tmy @files = map { basename($_) } glob(\"$gitpath/mergetools/*\");\n+\tmy @tools = sort(grep { !m{^defaults$} } @files);\n \n \tforeach my $tool (@tools) {\n \t\t$cmd  = \"TOOL_MODE=diff\";\n@@ -99,10 +84,10 @@ sub print_tool_help\n \t}\n \n \tprint \"'git difftool --tool=<tool>' may be set to one of the following:\\n\";\n-\tprint \"\\t$_\\n\" for (sort(@found));\n+\tprint \"\\t$_\\n\" for (@found);\n \n \tprint \"\\nThe following tools are valid, but not currently available:\\n\";\n-\tprint \"\\t$_\\n\" for (sort(@notfound));\n+\tprint \"\\t$_\\n\" for (@notfound);\n \n \tprint \"\\nNOTE: Some of the tools listed above only work in a windowed\\n\";\n \tprint \"environment. If run in a terminal-only session, they will fail.\\n\";\n-- \n1.7.11.2.255.g5f133da\n"},{"id":"195482","messageId":"1343015831-17498-3-git-send-email-davvid@gmail.com","threadId":"31072","inReplyTo":"1343015831-17498-2-git-send-email-davvid@gmail.com","subject":"[PATCH 2/5] difftool: Eliminate global variables","fromName":"David Aguilar","fromEmail":"davvid@gmail.com","sentAt":"2012-07-23T03:57:08Z","receivedAt":"2012-07-23T03:57:08Z","isPatch":true,"sender":{"key":"davvid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13196?v=4"},"body":"Organize the script so that it has a single main() function which\ncalls out to dir_diff() and file_diff() functions. This eliminates\n\"dir-diff\"-specific variables that do not need to be calculated when\nperforming a regular file-diff.\n\nSigned-off-by: David Aguilar <davvid@gmail.com>\n---\nSame as before, resending because gmail ate patch 4/5\n\n git-difftool.perl | 128 ++++++++++++++++++++++++++++++++----------------------\n 1 file changed, 75 insertions(+), 53 deletions(-)\n\ndiff --git a/git-difftool.perl b/git-difftool.perl\nindex ac0ed63..41ba932 100755\n--- a/git-difftool.perl\n+++ b/git-difftool.perl\n@@ -22,11 +22,6 @@ use File::Temp qw(tempdir);\n use Getopt::Long qw(:config pass_through);\n use Git;\n \n-my @working_tree;\n-my $rc;\n-my $repo = Git->repository();\n-my $repo_path = $repo->repo_path();\n-\n sub usage\n {\n \tmy $exitcode = shift;\n@@ -43,6 +38,8 @@ USAGE\n \n sub find_worktree\n {\n+\tmy ($repo) = @_;\n+\n \t# Git->repository->wc_path() does not honor changes to the working\n \t# tree location made by $ENV{GIT_WORK_TREE} or the 'core.worktree'\n \t# config variable.\n@@ -61,8 +58,6 @@ sub find_worktree\n \treturn $worktree;\n }\n \n-my $workdir = find_worktree();\n-\n sub print_tool_help\n {\n \tmy ($cmd, @found, @notfound);\n@@ -97,10 +92,13 @@ sub print_tool_help\n \n sub setup_dir_diff\n {\n+\tmy ($repo, $workdir) = @_;\n+\n \t# Run the diff; exit immediately if no diff found\n \t# 'Repository' and 'WorkingCopy' must be explicitly set to insure that\n \t# if $GIT_DIR and $GIT_WORK_TREE are set in ENV, they are actually used\n \t# by Git->repository->command*.\n+\tmy $repo_path = $repo->repo_path();\n \tmy $diffrepo = Git->repository(Repository => $repo_path, WorkingCopy => $workdir);\n \tmy $diffrtn = $diffrepo->command_oneline('diff', '--raw', '--no-abbrev', '-z', @ARGV);\n \texit(0) if (length($diffrtn) == 0);\n@@ -121,6 +119,7 @@ sub setup_dir_diff\n \tmy $rindex = '';\n \tmy %submodule;\n \tmy %symlink;\n+\tmy @working_tree = ();\n \tmy @rawdiff = split('\\0', $diffrtn);\n \n \tmy $i = 0;\n@@ -188,7 +187,7 @@ sub setup_dir_diff\n \t($inpipe, $ctx) = $repo->command_input_pipe(qw/update-index -z --index-info/);\n \tprint($inpipe $lindex);\n \t$repo->command_close_pipe($inpipe, $ctx);\n-\t$rc = system('git', 'checkout-index', '--all', \"--prefix=$ldir/\");\n+\tmy $rc = system('git', 'checkout-index', '--all', \"--prefix=$ldir/\");\n \texit($rc | ($rc >> 8)) if ($rc != 0);\n \n \t$ENV{GIT_INDEX_FILE} = \"$tmpdir/rindex\";\n@@ -238,7 +237,7 @@ sub setup_dir_diff\n \t\t}\n \t}\n \n-\treturn ($ldir, $rdir);\n+\treturn ($ldir, $rdir, @working_tree);\n }\n \n sub write_to_file\n@@ -261,54 +260,70 @@ sub write_to_file\n \tclose($fh);\n }\n \n-# parse command-line options. all unrecognized options and arguments\n-# are passed through to the 'git diff' command.\n-my ($difftool_cmd, $dirdiff, $extcmd, $gui, $help, $prompt, $tool_help);\n-GetOptions('g|gui!' => \\$gui,\n-\t'd|dir-diff' => \\$dirdiff,\n-\t'h' => \\$help,\n-\t'prompt!' => \\$prompt,\n-\t'y' => sub { $prompt = 0; },\n-\t't|tool:s' => \\$difftool_cmd,\n-\t'tool-help' => \\$tool_help,\n-\t'x|extcmd:s' => \\$extcmd);\n-\n-if (defined($help)) {\n-\tusage(0);\n-}\n-if (defined($tool_help)) {\n-\tprint_tool_help();\n-}\n-if (defined($difftool_cmd)) {\n-\tif (length($difftool_cmd) > 0) {\n-\t\t$ENV{GIT_DIFF_TOOL} = $difftool_cmd;\n-\t} else {\n-\t\tprint \"No <tool> given for --tool=<tool>\\n\";\n-\t\tusage(1);\n+sub main\n+{\n+\t# parse command-line options. all unrecognized options and arguments\n+\t# are passed through to the 'git diff' command.\n+\tmy ($difftool_cmd, $dirdiff, $extcmd, $gui, $help, $prompt, $tool_help);\n+\tGetOptions('g|gui!' => \\$gui,\n+\t\t'd|dir-diff' => \\$dirdiff,\n+\t\t'h' => \\$help,\n+\t\t'prompt!' => \\$prompt,\n+\t\t'y' => sub { $prompt = 0; },\n+\t\t't|tool:s' => \\$difftool_cmd,\n+\t\t'tool-help' => \\$tool_help,\n+\t\t'x|extcmd:s' => \\$extcmd);\n+\n+\tif (defined($help)) {\n+\t\tusage(0);\n \t}\n-}\n-if (defined($extcmd)) {\n-\tif (length($extcmd) > 0) {\n-\t\t$ENV{GIT_DIFFTOOL_EXTCMD} = $extcmd;\n-\t} else {\n-\t\tprint \"No <cmd> given for --extcmd=<cmd>\\n\";\n-\t\tusage(1);\n+\tif (defined($tool_help)) {\n+\t\tprint_tool_help();\n \t}\n-}\n-if ($gui) {\n-\tmy $guitool = '';\n-\t$guitool = Git::config('diff.guitool');\n-\tif (length($guitool) > 0) {\n-\t\t$ENV{GIT_DIFF_TOOL} = $guitool;\n+\tif (defined($difftool_cmd)) {\n+\t\tif (length($difftool_cmd) > 0) {\n+\t\t\t$ENV{GIT_DIFF_TOOL} = $difftool_cmd;\n+\t\t} else {\n+\t\t\tprint \"No <tool> given for --tool=<tool>\\n\";\n+\t\t\tusage(1);\n+\t\t}\n+\t}\n+\tif (defined($extcmd)) {\n+\t\tif (length($extcmd) > 0) {\n+\t\t\t$ENV{GIT_DIFFTOOL_EXTCMD} = $extcmd;\n+\t\t} else {\n+\t\t\tprint \"No <cmd> given for --extcmd=<cmd>\\n\";\n+\t\t\tusage(1);\n+\t\t}\n+\t}\n+\tif ($gui) {\n+\t\tmy $guitool = '';\n+\t\t$guitool = Git::config('diff.guitool');\n+\t\tif (length($guitool) > 0) {\n+\t\t\t$ENV{GIT_DIFF_TOOL} = $guitool;\n+\t\t}\n+\t}\n+\n+\t# In directory diff mode, 'git-difftool--helper' is called once\n+\t# to compare the a/b directories.  In file diff mode, 'git diff'\n+\t# will invoke a separate instance of 'git-difftool--helper' for\n+\t# each file that changed.\n+\tif (defined($dirdiff)) {\n+\t\tdir_diff($extcmd);\n+\t} else {\n+\t\tfile_diff($prompt);\n \t}\n }\n \n-# In directory diff mode, 'git-difftool--helper' is called once\n-# to compare the a/b directories.  In file diff mode, 'git diff'\n-# will invoke a separate instance of 'git-difftool--helper' for\n-# each file that changed.\n-if (defined($dirdiff)) {\n-\tmy ($a, $b) = setup_dir_diff();\n+sub dir_diff\n+{\n+\tmy ($extcmd) = @_;\n+\n+\tmy $rc;\n+\tmy $repo = Git->repository();\n+\n+\tmy $workdir = find_worktree($repo);\n+\tmy ($a, $b, @working_tree) = setup_dir_diff($repo, $workdir);\n \tif (defined($extcmd)) {\n \t\t$rc = system($extcmd, $a, $b);\n \t} else {\n@@ -327,7 +342,12 @@ if (defined($dirdiff)) {\n \t\t\tchmod(stat(\"$b/$file\")->mode, \"$workdir/$file\") or die $!;\n \t\t}\n \t}\n-} else {\n+}\n+\n+sub file_diff\n+{\n+\tmy ($prompt) = @_;\n+\n \tif (defined($prompt)) {\n \t\tif ($prompt) {\n \t\t\t$ENV{GIT_DIFFTOOL_PROMPT} = 'true';\n@@ -347,3 +367,5 @@ if (defined($dirdiff)) {\n \tmy $rc = system('git', 'diff', @ARGV);\n \texit($rc | ($rc >> 8));\n }\n+\n+main();\n-- \n1.7.11.2.255.g5f133da\n"},{"id":"195481","messageId":"1343015831-17498-4-git-send-email-davvid@gmail.com","threadId":"31072","inReplyTo":"1343015831-17498-3-git-send-email-davvid@gmail.com","subject":"[PATCH 3/5] difftool: Move option values into a hash","fromName":"David Aguilar","fromEmail":"davvid@gmail.com","sentAt":"2012-07-23T03:57:09Z","receivedAt":"2012-07-23T03:57:09Z","isPatch":true,"sender":{"key":"davvid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13196?v=4"},"body":"Shorten the \"my\" declaration for all of the option-specific variables\nby wrapping all of them in a hash.  This also gives us a place to\nspecify default values, should we need them.\n\nSigned-off-by: David Aguilar <davvid@gmail.com>\n---\nFixed typo in the commit message\n\n git-difftool.perl | 55 +++++++++++++++++++++++++++++++------------------------\n 1 file changed, 31 insertions(+), 24 deletions(-)\n\ndiff --git a/git-difftool.perl b/git-difftool.perl\nindex 41ba932..0ce6168 100755\n--- a/git-difftool.perl\n+++ b/git-difftool.perl\n@@ -264,41 +264,48 @@ sub main\n {\n \t# parse command-line options. all unrecognized options and arguments\n \t# are passed through to the 'git diff' command.\n-\tmy ($difftool_cmd, $dirdiff, $extcmd, $gui, $help, $prompt, $tool_help);\n-\tGetOptions('g|gui!' => \\$gui,\n-\t\t'd|dir-diff' => \\$dirdiff,\n-\t\t'h' => \\$help,\n-\t\t'prompt!' => \\$prompt,\n-\t\t'y' => sub { $prompt = 0; },\n-\t\t't|tool:s' => \\$difftool_cmd,\n-\t\t'tool-help' => \\$tool_help,\n-\t\t'x|extcmd:s' => \\$extcmd);\n-\n-\tif (defined($help)) {\n+\tmy %opts = (\n+\t\tdifftool_cmd => undef,\n+\t\tdirdiff => undef,\n+\t\textcmd => undef,\n+\t\tgui => undef,\n+\t\thelp => undef,\n+\t\tprompt => undef,\n+\t\ttool_help => undef,\n+\t);\n+\tGetOptions('g|gui!' => \\$opts{gui},\n+\t\t'd|dir-diff' => \\$opts{dirdiff},\n+\t\t'h' => \\$opts{help},\n+\t\t'prompt!' => \\$opts{prompt},\n+\t\t'y' => sub { $opts{prompt} = 0; },\n+\t\t't|tool:s' => \\$opts{difftool_cmd},\n+\t\t'tool-help' => \\$opts{tool_help},\n+\t\t'x|extcmd:s' => \\$opts{extcmd});\n+\n+\tif (defined($opts{help})) {\n \t\tusage(0);\n \t}\n-\tif (defined($tool_help)) {\n+\tif (defined($opts{tool_help})) {\n \t\tprint_tool_help();\n \t}\n-\tif (defined($difftool_cmd)) {\n-\t\tif (length($difftool_cmd) > 0) {\n-\t\t\t$ENV{GIT_DIFF_TOOL} = $difftool_cmd;\n+\tif (defined($opts{difftool_cmd})) {\n+\t\tif (length($opts{difftool_cmd}) > 0) {\n+\t\t\t$ENV{GIT_DIFF_TOOL} = $opts{difftool_cmd};\n \t\t} else {\n \t\t\tprint \"No <tool> given for --tool=<tool>\\n\";\n \t\t\tusage(1);\n \t\t}\n \t}\n-\tif (defined($extcmd)) {\n-\t\tif (length($extcmd) > 0) {\n-\t\t\t$ENV{GIT_DIFFTOOL_EXTCMD} = $extcmd;\n+\tif (defined($opts{extcmd})) {\n+\t\tif (length($opts{extcmd}) > 0) {\n+\t\t\t$ENV{GIT_DIFFTOOL_EXTCMD} = $opts{extcmd};\n \t\t} else {\n \t\t\tprint \"No <cmd> given for --extcmd=<cmd>\\n\";\n \t\t\tusage(1);\n \t\t}\n \t}\n-\tif ($gui) {\n-\t\tmy $guitool = '';\n-\t\t$guitool = Git::config('diff.guitool');\n+\tif ($opts{gui}) {\n+\t\tmy $guitool = Git::config('diff.guitool');\n \t\tif (length($guitool) > 0) {\n \t\t\t$ENV{GIT_DIFF_TOOL} = $guitool;\n \t\t}\n@@ -308,10 +315,10 @@ sub main\n \t# to compare the a/b directories.  In file diff mode, 'git diff'\n \t# will invoke a separate instance of 'git-difftool--helper' for\n \t# each file that changed.\n-\tif (defined($dirdiff)) {\n-\t\tdir_diff($extcmd);\n+\tif (defined($opts{dirdiff})) {\n+\t\tdir_diff($opts{extcmd});\n \t} else {\n-\t\tfile_diff($prompt);\n+\t\tfile_diff($opts{prompt});\n \t}\n }\n \n-- \n1.7.11.2.255.g5f133da\n"},{"id":"195480","messageId":"1343015831-17498-5-git-send-email-davvid@gmail.com","threadId":"31072","inReplyTo":"1343015831-17498-4-git-send-email-davvid@gmail.com","subject":"[PATCH 4/5] difftool: Call the temp directory \"git-difftool\"","fromName":"David Aguilar","fromEmail":"davvid@gmail.com","sentAt":"2012-07-23T03:57:10Z","receivedAt":"2012-07-23T03:57:10Z","isPatch":true,"sender":{"key":"davvid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13196?v=4"},"body":"The \"diffall\" name was left over from when this functionality was part of\nthe \"git-diffall\" script in contrib/.  Make the naming consistent.\n\nSigned-off-by: David Aguilar <davvid@gmail.com>\n---\nReworded the commit message to get through gmail filters.\n\n git-difftool.perl | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/git-difftool.perl b/git-difftool.perl\nindex 0ce6168..2ae344c 100755\n--- a/git-difftool.perl\n+++ b/git-difftool.perl\n@@ -104,7 +104,7 @@ sub setup_dir_diff\n \texit(0) if (length($diffrtn) == 0);\n \n \t# Setup temp directories\n-\tmy $tmpdir = tempdir('git-diffall.XXXXX', CLEANUP => 1, TMPDIR => 1);\n+\tmy $tmpdir = tempdir('git-difftool.XXXXX', CLEANUP => 1, TMPDIR => 1);\n \tmy $ldir = \"$tmpdir/left\";\n \tmy $rdir = \"$tmpdir/right\";\n \tmkpath($ldir) or die $!;\n-- \n1.7.11.2.255.g5f133da\n"},{"id":"195483","messageId":"1343015831-17498-6-git-send-email-davvid@gmail.com","threadId":"31072","inReplyTo":"1343015831-17498-5-git-send-email-davvid@gmail.com","subject":"[PATCH 5/5] difftool: Use symlinks when diffing against the worktree","fromName":"David Aguilar","fromEmail":"davvid@gmail.com","sentAt":"2012-07-23T03:57:11Z","receivedAt":"2012-07-23T03:57:11Z","isPatch":true,"sender":{"key":"davvid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13196?v=4"},"body":"Teach difftool's --dir-diff mode to use symlinks to represent\nfiles from the working copy, and make it the default behavior\nfor the non-Windows platforms.\n\nUsing symlinks is simpler and safer since we do not need to\nworry about copying files back into the worktree.\nThe old behavior is still available as --no-symlinks.\n\nSigned-off-by: David Aguilar <davvid@gmail.com>\n---\nSame as before, resending because gmail ate patch 4/5\n\n Documentation/git-difftool.txt |  8 ++++++++\n git-difftool.perl              | 30 +++++++++++++++++++++++-------\n 2 files changed, 31 insertions(+), 7 deletions(-)\n\ndiff --git a/Documentation/git-difftool.txt b/Documentation/git-difftool.txt\nindex 31fc2e3..313d54e 100644\n--- a/Documentation/git-difftool.txt\n+++ b/Documentation/git-difftool.txt\n@@ -66,6 +66,14 @@ of the diff post-image.  `$MERGED` is the name of the file which is\n being compared. `$BASE` is provided for compatibility\n with custom merge tool commands and has the same value as `$MERGED`.\n \n+--symlinks::\n+--no-symlinks::\n+\t'git difftool''s default behavior is create symlinks to the\n+\tworking tree when run in `--dir-diff` mode.\n++\n+\tSpecifying `--no-symlinks` instructs 'git difftool' to create\n+\tcopies instead.  `--no-symlinks` is the default on Windows.\n+\n --tool-help::\n \tPrint a list of diff tools that may be used with `--tool`.\n \ndiff --git a/git-difftool.perl b/git-difftool.perl\nindex 2ae344c..b8f8057 100755\n--- a/git-difftool.perl\n+++ b/git-difftool.perl\n@@ -92,7 +92,7 @@ sub print_tool_help\n \n sub setup_dir_diff\n {\n-\tmy ($repo, $workdir) = @_;\n+\tmy ($repo, $workdir, $symlinks) = @_;\n \n \t# Run the diff; exit immediately if no diff found\n \t# 'Repository' and 'WorkingCopy' must be explicitly set to insure that\n@@ -209,8 +209,13 @@ sub setup_dir_diff\n \t\tunless (-d \"$rdir/$dir\") {\n \t\t\tmkpath(\"$rdir/$dir\") or die $!;\n \t\t}\n-\t\tcopy(\"$workdir/$file\", \"$rdir/$file\") or die $!;\n-\t\tchmod(stat(\"$workdir/$file\")->mode, \"$rdir/$file\") or die $!;\n+\t\tif ($symlinks) {\n+\t\t\tsymlink(\"$workdir/$file\", \"$rdir/$file\") or die $!;\n+\t\t} else {\n+\t\t\tcopy(\"$workdir/$file\", \"$rdir/$file\") or die $!;\n+\t\t\tmy $mode = stat(\"$workdir/$file\")->mode;\n+\t\t\tchmod($mode, \"$rdir/$file\") or die $!;\n+\t\t}\n \t}\n \n \t# Changes to submodules require special treatment. This loop writes a\n@@ -271,6 +276,7 @@ sub main\n \t\tgui => undef,\n \t\thelp => undef,\n \t\tprompt => undef,\n+\t\tsymlinks => $^O ne 'MSWin32' && $^O ne 'msys',\n \t\ttool_help => undef,\n \t);\n \tGetOptions('g|gui!' => \\$opts{gui},\n@@ -278,6 +284,8 @@ sub main\n \t\t'h' => \\$opts{help},\n \t\t'prompt!' => \\$opts{prompt},\n \t\t'y' => sub { $opts{prompt} = 0; },\n+\t\t'symlinks' => \\$opts{symlinks},\n+\t\t'no-symlinks' => sub { $opts{symlinks} = 0; },\n \t\t't|tool:s' => \\$opts{difftool_cmd},\n \t\t'tool-help' => \\$opts{tool_help},\n \t\t'x|extcmd:s' => \\$opts{extcmd});\n@@ -316,7 +324,7 @@ sub main\n \t# will invoke a separate instance of 'git-difftool--helper' for\n \t# each file that changed.\n \tif (defined($opts{dirdiff})) {\n-\t\tdir_diff($opts{extcmd});\n+\t\tdir_diff($opts{extcmd}, $opts{symlinks});\n \t} else {\n \t\tfile_diff($opts{prompt});\n \t}\n@@ -324,13 +332,13 @@ sub main\n \n sub dir_diff\n {\n-\tmy ($extcmd) = @_;\n+\tmy ($extcmd, $symlinks) = @_;\n \n \tmy $rc;\n \tmy $repo = Git->repository();\n \n \tmy $workdir = find_worktree($repo);\n-\tmy ($a, $b, @working_tree) = setup_dir_diff($repo, $workdir);\n+\tmy ($a, $b, @working_tree) = setup_dir_diff($repo, $workdir, $symlinks);\n \tif (defined($extcmd)) {\n \t\t$rc = system($extcmd, $a, $b);\n \t} else {\n@@ -340,15 +348,23 @@ sub dir_diff\n \n \texit($rc | ($rc >> 8)) if ($rc != 0);\n \n+\t# Do not copy back files when symlinks are used\n+\tif ($symlinks) {\n+\t\texit(0);\n+\t}\n+\n \t# If the diff including working copy files and those\n \t# files were modified during the diff, then the changes\n \t# should be copied back to the working tree\n+\n \tfor my $file (@working_tree) {\n \t\tif (-e \"$b/$file\" && compare(\"$b/$file\", \"$workdir/$file\")) {\n \t\t\tcopy(\"$b/$file\", \"$workdir/$file\") or die $!;\n-\t\t\tchmod(stat(\"$b/$file\")->mode, \"$workdir/$file\") or die $!;\n+\t\t\tmy $mode = stat(\"$b/$file\")->mode;\n+\t\t\tchmod($mode, \"$workdir/$file\") or die $!;\n \t\t}\n \t}\n+\texit(0);\n }\n \n sub file_diff\n-- \n1.7.11.2.255.g5f133da\n"},{"id":"195489","messageId":"7vzk6rnkgq.fsf@alter.siamese.dyndns.org","threadId":"31072","inReplyTo":"1343015831-17498-6-git-send-email-davvid@gmail.com","subject":"Re: [PATCH 5/5] difftool: Use symlinks when diffing against the worktree","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-23T04:57:09Z","receivedAt":"2012-07-23T04:57:09Z","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> +\t# Do not copy back files when symlinks are used\n> +\tif ($symlinks) {\n> +\t\texit(0);\n> +\t}\n> +\n\nIsn't this a bit risky, depending on the behaviour of the tool that\neventually lead the user to invoke his favorite editor to muck with\nthe files in the temporary directory?  I think most sane people and\ntheir editors would follow symlinks and update the file the symlink\npoints at when writing out the modified contents, but it should not\nbe too much trouble to detect the case in which the editor unlinked\nthe symlink and recreated a regular file in its place, and copy the\nfile back when that happened, to make it even safer, no?\n\nThe most lazy solution would be to just remove the above block, and\nlet the compare() compare the symlink $b/$file and the working tree\nfile $workdir/$file that is pointed by it. We will find data losing\ncase where the editor unlinks and creates that way automatically.\n\nOptionally, you can update\n\n\tif (-e \"$b/$file\" && compare(\"$b/$file\", \"$workdir/$file\")) {\n\nwith\n\n\tif (! -l \"$b/$file\" && -f _ && compare(\"$b/$file\", \"$workdir/$file\")) {\n\nto avoid the cost of comparison.\n\n>  \t# If the diff including working copy files and those\n>  \t# files were modified during the diff, then the changes\n>  \t# should be copied back to the working tree\n> +\n>  \tfor my $file (@working_tree) {\n>  \t\tif (-e \"$b/$file\" && compare(\"$b/$file\", \"$workdir/$file\")) {\n>  \t\t\tcopy(\"$b/$file\", \"$workdir/$file\") or die $!;\n> -\t\t\tchmod(stat(\"$b/$file\")->mode, \"$workdir/$file\") or die $!;\n> +\t\t\tmy $mode = stat(\"$b/$file\")->mode;\n> +\t\t\tchmod($mode, \"$workdir/$file\") or die $!;\n>  \t\t}\n>  \t}\n> +\texit(0);\n>  }\n\nOther than that, the series looked well thought-out.\n\nThanks.\n"},{"id":"195493","messageId":"7vfw8jnjn2.fsf@alter.siamese.dyndns.org","threadId":"31072","inReplyTo":"1343015831-17498-1-git-send-email-davvid@gmail.com","subject":"Re: [PATCH v2 0/5] difftool: Use symlinks in dir-diff mode","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-23T05:14:57Z","receivedAt":"2012-07-23T05:14:57Z","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> Teach the difftool script to use symlinks when doing\n> directory diffs in --dir-diff mode.\n>\n> This is v2 of the patch because I had a typo in one of the\n> commit messages and gmail ate 4/5 in the last round.\n\nFWIW, I received all including 4/5 in my inboxes (at pobox and\ngmail---I am doubly subscribed).  I still haven't figured out what\nin the original 4/5 was so special to be dropped somewhere in\nbetween.\n\n> David Aguilar (5):\n>   difftool: Simplify print_tool_help()\n>   difftool: Eliminate global variables\n>   difftool: Move option values into a hash\n>   difftool: Call the temp directory \"git-difftool\"\n>   difftool: Use symlinks when diffing against the worktree\n>\n>  Documentation/git-difftool.txt |   8 ++\n>  git-difftool.perl              | 184 ++++++++++++++++++++++++-----------------\n>  2 files changed, 115 insertions(+), 77 deletions(-)\n"},{"id":"195495","messageId":"CAJDDKr54yYXVWupDFQsx4b8UiqHRL1hsnAvpZGehDxDPYcfXpw@mail.gmail.com","threadId":"31072","inReplyTo":"7vfw8jnjn2.fsf@alter.siamese.dyndns.org","subject":"Re: [PATCH v2 0/5] difftool: Use symlinks in dir-diff mode","fromName":"David Aguilar","fromEmail":"davvid@gmail.com","sentAt":"2012-07-23T05:34:21Z","receivedAt":"2012-07-23T05:34:21Z","isPatch":true,"sender":{"key":"davvid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13196?v=4"},"body":"On Sun, Jul 22, 2012 at 10:14 PM, Junio C Hamano <gitster@pobox.com> wrote:\n> David Aguilar <davvid@gmail.com> writes:\n>\n>> Teach the difftool script to use symlinks when doing\n>> directory diffs in --dir-diff mode.\n>>\n>> This is v2 of the patch because I had a typo in one of the\n>> commit messages and gmail ate 4/5 in the last round.\n>\n> FWIW, I received all including 4/5 in my inboxes (at pobox and\n> gmail---I am doubly subscribed).  I still haven't figured out what\n> in the original 4/5 was so special to be dropped somewhere in\n> between.\n\nI hastily blamed gmail but of course it was vger's spam filters.\nThe original subject said \"git-difftool.XXXXX\".\nThe exes triggered it.\n-- \nDavid\n"},{"id":"195497","messageId":"1343023530-31463-1-git-send-email-davvid@gmail.com","threadId":"31072","inReplyTo":"7vzk6rnkgq.fsf@alter.siamese.dyndns.org","subject":"[PATCH v3 4/5] difftool: Use symlinks when diffing against the worktree","fromName":"David Aguilar","fromEmail":"davvid@gmail.com","sentAt":"2012-07-23T06:05:30Z","receivedAt":"2012-07-23T06:05:30Z","isPatch":true,"sender":{"key":"davvid@gmail.com","avatar":"https://avatars.githubusercontent.com/u/13196?v=4"},"body":"Teach difftool's --dir-diff mode to use symlinks to represent\nfiles from the working copy, and make it the default behavior\nfor the non-Windows platforms.\n\nUsing symlinks is simpler and safer since we do not need to\nworry about copying files back into the worktree.\nThe old behavior is still available as --no-symlinks.\n\nSigned-off-by: David Aguilar <davvid@gmail.com>\n---\nHandles the case where an editor unlinks the original symlink,\nreplacing it with a file.\n\n Documentation/git-difftool.txt |    8 ++++++++\n git-difftool.perl              |   33 +++++++++++++++++++++++----------\n 2 files changed, 31 insertions(+), 10 deletions(-)\n\ndiff --git a/Documentation/git-difftool.txt b/Documentation/git-difftool.txt\nindex 31fc2e3..313d54e 100644\n--- a/Documentation/git-difftool.txt\n+++ b/Documentation/git-difftool.txt\n@@ -66,6 +66,14 @@ of the diff post-image.  `$MERGED` is the name of the file which is\n being compared. `$BASE` is provided for compatibility\n with custom merge tool commands and has the same value as `$MERGED`.\n \n+--symlinks::\n+--no-symlinks::\n+\t'git difftool''s default behavior is create symlinks to the\n+\tworking tree when run in `--dir-diff` mode.\n++\n+\tSpecifying `--no-symlinks` instructs 'git difftool' to create\n+\tcopies instead.  `--no-symlinks` is the default on Windows.\n+\n --tool-help::\n \tPrint a list of diff tools that may be used with `--tool`.\n \ndiff --git a/git-difftool.perl b/git-difftool.perl\nindex 2ae344c..a5b371f 100755\n--- a/git-difftool.perl\n+++ b/git-difftool.perl\n@@ -92,7 +92,7 @@ sub print_tool_help\n \n sub setup_dir_diff\n {\n-\tmy ($repo, $workdir) = @_;\n+\tmy ($repo, $workdir, $symlinks) = @_;\n \n \t# Run the diff; exit immediately if no diff found\n \t# 'Repository' and 'WorkingCopy' must be explicitly set to insure that\n@@ -209,8 +209,13 @@ sub setup_dir_diff\n \t\tunless (-d \"$rdir/$dir\") {\n \t\t\tmkpath(\"$rdir/$dir\") or die $!;\n \t\t}\n-\t\tcopy(\"$workdir/$file\", \"$rdir/$file\") or die $!;\n-\t\tchmod(stat(\"$workdir/$file\")->mode, \"$rdir/$file\") or die $!;\n+\t\tif ($symlinks) {\n+\t\t\tsymlink(\"$workdir/$file\", \"$rdir/$file\") or die $!;\n+\t\t} else {\n+\t\t\tcopy(\"$workdir/$file\", \"$rdir/$file\") or die $!;\n+\t\t\tmy $mode = stat(\"$workdir/$file\")->mode;\n+\t\t\tchmod($mode, \"$rdir/$file\") or die $!;\n+\t\t}\n \t}\n \n \t# Changes to submodules require special treatment. This loop writes a\n@@ -271,6 +276,7 @@ sub main\n \t\tgui => undef,\n \t\thelp => undef,\n \t\tprompt => undef,\n+\t\tsymlinks => $^O ne 'MSWin32' && $^O ne 'msys',\n \t\ttool_help => undef,\n \t);\n \tGetOptions('g|gui!' => \\$opts{gui},\n@@ -278,6 +284,8 @@ sub main\n \t\t'h' => \\$opts{help},\n \t\t'prompt!' => \\$opts{prompt},\n \t\t'y' => sub { $opts{prompt} = 0; },\n+\t\t'symlinks' => \\$opts{symlinks},\n+\t\t'no-symlinks' => sub { $opts{symlinks} = 0; },\n \t\t't|tool:s' => \\$opts{difftool_cmd},\n \t\t'tool-help' => \\$opts{tool_help},\n \t\t'x|extcmd:s' => \\$opts{extcmd});\n@@ -316,7 +324,7 @@ sub main\n \t# will invoke a separate instance of 'git-difftool--helper' for\n \t# each file that changed.\n \tif (defined($opts{dirdiff})) {\n-\t\tdir_diff($opts{extcmd});\n+\t\tdir_diff($opts{extcmd}, $opts{symlinks});\n \t} else {\n \t\tfile_diff($opts{prompt});\n \t}\n@@ -324,13 +332,13 @@ sub main\n \n sub dir_diff\n {\n-\tmy ($extcmd) = @_;\n+\tmy ($extcmd, $symlinks) = @_;\n \n \tmy $rc;\n \tmy $repo = Git->repository();\n \n \tmy $workdir = find_worktree($repo);\n-\tmy ($a, $b, @working_tree) = setup_dir_diff($repo, $workdir);\n+\tmy ($a, $b, @worktree) = setup_dir_diff($repo, $workdir, $symlinks);\n \tif (defined($extcmd)) {\n \t\t$rc = system($extcmd, $a, $b);\n \t} else {\n@@ -342,13 +350,18 @@ sub dir_diff\n \n \t# If the diff including working copy files and those\n \t# files were modified during the diff, then the changes\n-\t# should be copied back to the working tree\n-\tfor my $file (@working_tree) {\n-\t\tif (-e \"$b/$file\" && compare(\"$b/$file\", \"$workdir/$file\")) {\n+\t# should be copied back to the working tree.\n+\t# Do not copy back files when symlinks are used and the\n+\t# external tool did not replace the original link with a file.\n+\tfor my $file (@worktree) {\n+\t\tnext if $symlinks && -l \"$b/$file\";\n+\t\tif (-f \"$b/$file\" && compare(\"$b/$file\", \"$workdir/$file\")) {\n \t\t\tcopy(\"$b/$file\", \"$workdir/$file\") or die $!;\n-\t\t\tchmod(stat(\"$b/$file\")->mode, \"$workdir/$file\") or die $!;\n+\t\t\tmy $mode = stat(\"$b/$file\")->mode;\n+\t\t\tchmod($mode, \"$workdir/$file\") or die $!;\n \t\t}\n \t}\n+\texit(0);\n }\n \n sub file_diff\n-- \n1.7.7.2.448.gee6df\n"},{"id":"195529","messageId":"7vzk6qmo02.fsf@alter.siamese.dyndns.org","threadId":"31072","inReplyTo":"1343023530-31463-1-git-send-email-davvid@gmail.com","subject":"Re: [PATCH v3 4/5] difftool: Use symlinks when diffing against the worktree","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-23T16:38:21Z","receivedAt":"2012-07-23T16:38:21Z","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> Teach difftool's --dir-diff mode to use symlinks to represent\n> files from the working copy, and make it the default behavior\n> for the non-Windows platforms.\n>\n> Using symlinks is simpler and safer since we do not need to\n> worry about copying files back into the worktree.\n> The old behavior is still available as --no-symlinks.\n>\n> Signed-off-by: David Aguilar <davvid@gmail.com>\n> ---\n> Handles the case where an editor unlinks the original symlink,\n> replacing it with a file.\n\nThanks; will replace.\n"},{"id":"195629","messageId":"CAFouethz=7Ed-=AsnC2Fh+-hZcpjzmSLB9aCWd4tBw8GvH1K-w@mail.gmail.com","threadId":"31072","inReplyTo":"1343023530-31463-1-git-send-email-davvid@gmail.com","subject":"Re: [PATCH v3 4/5] difftool: Use symlinks when diffing against the worktree","fromName":"Tim Henigan","fromEmail":"tim.henigan@gmail.com","sentAt":"2012-07-24T13:35:36Z","receivedAt":"2012-07-24T13:35:36Z","isPatch":true,"sender":{"key":"tim.henigan@gmail.com","avatar":"https://avatars.githubusercontent.com/u/42022?v=4"},"body":"I'm sorry I am so late to see and comment on this...I am just getting\ncaught up after a few busy weeks due to $dayjob and vacation.\n\n\nOn Mon, Jul 23, 2012 at 2:05 AM, David Aguilar <davvid@gmail.com> wrote:\n>\n> diff --git a/git-difftool.perl b/git-difftool.perl\n> index 2ae344c..a5b371f 100755\n> --- a/git-difftool.perl\n> +++ b/git-difftool.perl\n>\n> @@ -271,6 +276,7 @@ sub main\n>                 gui => undef,\n>                 help => undef,\n>                 prompt => undef,\n> +               symlinks => $^O ne 'MSWin32' && $^O ne 'msys',\n\nShould this test for cygwin as well?\n\n\n> @@ -342,13 +350,18 @@ sub dir_diff\n>\n>         # If the diff including working copy files and those\n>         # files were modified during the diff, then the changes\n> -       # should be copied back to the working tree\n> -       for my $file (@working_tree) {\n> -               if (-e \"$b/$file\" && compare(\"$b/$file\", \"$workdir/$file\")) {\n> +       # should be copied back to the working tree.\n> +       # Do not copy back files when symlinks are used and the\n> +       # external tool did not replace the original link with a file.\n> +       for my $file (@worktree) {\n> +               next if $symlinks && -l \"$b/$file\";\n> +               if (-f \"$b/$file\" && compare(\"$b/$file\", \"$workdir/$file\")) {\n\ncompare returns '-1' if an error is encountered while reading a file.\nIn this (unlikely) case, should it still overwrite the working copy\nfile?  I think the answer is 'yes', but thought it was worth\nmentioning.\n"},{"id":"195634","messageId":"7v7gttgnjd.fsf@alter.siamese.dyndns.org","threadId":"31072","inReplyTo":"CAFouethz=7Ed-=AsnC2Fh+-hZcpjzmSLB9aCWd4tBw8GvH1K-w@mail.gmail.com","subject":"Re: [PATCH v3 4/5] difftool: Use symlinks when diffing against the worktree","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2012-07-24T15:57:10Z","receivedAt":"2012-07-24T15:57:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Tim Henigan <tim.henigan@gmail.com> writes:\n\n> I'm sorry I am so late to see and comment on this...I am just getting\n> caught up after a few busy weeks due to $dayjob and vacation.\n>\n>\n> On Mon, Jul 23, 2012 at 2:05 AM, David Aguilar <davvid@gmail.com> wrote:\n>>\n>> diff --git a/git-difftool.perl b/git-difftool.perl\n>> index 2ae344c..a5b371f 100755\n>> --- a/git-difftool.perl\n>> +++ b/git-difftool.perl\n>>\n>> @@ -271,6 +276,7 @@ sub main\n>>                 gui => undef,\n>>                 help => undef,\n>>                 prompt => undef,\n>> +               symlinks => $^O ne 'MSWin32' && $^O ne 'msys',\n>\n> Should this test for cygwin as well?\n>\n>\n>> @@ -342,13 +350,18 @@ sub dir_diff\n>>\n>>         # If the diff including working copy files and those\n>>         # files were modified during the diff, then the changes\n>> -       # should be copied back to the working tree\n>> -       for my $file (@working_tree) {\n>> -               if (-e \"$b/$file\" && compare(\"$b/$file\", \"$workdir/$file\")) {\n>> +       # should be copied back to the working tree.\n>> +       # Do not copy back files when symlinks are used and the\n>> +       # external tool did not replace the original link with a file.\n>> +       for my $file (@worktree) {\n>> +               next if $symlinks && -l \"$b/$file\";\n>> +               if (-f \"$b/$file\" && compare(\"$b/$file\", \"$workdir/$file\")) {\n>\n> compare returns '-1' if an error is encountered while reading a file.\n> In this (unlikely) case, should it still overwrite the working copy\n> file?  I think the answer is 'yes', but thought it was worth\n> mentioning.\n\nIt probably is safer to report the error, not touch anything and let\nthe user take an appropriate action.\n"}]}