+ my $fd;
+ my @difftree;
+ my %diffinfo;
+
+ # preparing $fd and %diffinfo for git_patchset_body
+ # new style URI
+ if (defined $hash_base && defined $hash_parent_base) {
+ if (defined $file_name) {
+ # read raw output
+ open $fd, "-|", $GIT, "diff-tree", '-r', '-M', '-C', $hash_parent_base, $hash_base,
+ "--", $file_name
+ or die_error(undef, "Open git-diff-tree failed");
+ @difftree = map { chomp; $_ } <$fd>;
+ close $fd
+ or die_error(undef, "Reading git-diff-tree failed");
+ @difftree
+ or die_error('404 Not Found', "Blob diff not found");
+
+ } elsif (defined $hash) { # try to find filename from $hash
+ if ($hash !~ /[0-9a-fA-F]{40}/) {
+ $hash = git_to_hash($hash);
+ }
+
+ # read filtered raw output
+ open $fd, "-|", $GIT, "diff-tree", '-r', '-M', '-C', $hash_parent_base, $hash_base
+ or die_error(undef, "Open git-diff-tree failed");
+ @difftree =
+ # ':100644 100644 03b21826... 3b93d5e7... M ls-files.c'
+ # $hash == to_id
+ grep { /^:[0-7]{6} [0-7]{6} [0-9a-fA-F]{40} $hash/ }
+ map { chomp; $_ } <$fd>;
+ close $fd
+ or die_error(undef, "Reading git-diff-tree failed");
+ @difftree
+ or die_error('404 Not Found', "Blob diff not found");
+
+ } else {
+ die_error('404 Not Found', "Missing one of the blob diff parameters");
+ }
+
+ if (@difftree > 1) {
+ die_error('404 Not Found', "Ambiguous blob diff specification");
+ }
+
+ %diffinfo = parse_difftree_raw_line($difftree[0]);
+ $file_parent ||= $diffinfo{'from_file'} || $file_name || $diffinfo{'file'};
+ $file_name ||= $diffinfo{'to_file'} || $diffinfo{'file'};
+
+ $hash_parent ||= $diffinfo{'from_id'};
+ $hash ||= $diffinfo{'to_id'};
+
+ # open patch output
+ open $fd, "-|", $GIT, "diff-tree", '-r', '-p', '-M', '-C', $hash_parent_base, $hash_base,
+ "--", $file_name
+ or die_error(undef, "Open git-diff-tree failed");
+ }
+
+ # old/legacy style URI
+ if (!%diffinfo && # if new style URI failed
+ defined $hash && defined $hash_parent) {
+ # fake git-diff-tree raw output
+ $diffinfo{'from_mode'} = $diffinfo{'to_mode'} = "blob";
+ $diffinfo{'from_id'} = $hash_parent;
+ $diffinfo{'to_id'} = $hash;
+ if (defined $file_name) {
+ if (defined $file_parent) {
+ $diffinfo{'status'} = '2';
+ $diffinfo{'from_file'} = $file_parent;
+ $diffinfo{'to_file'} = $file_name;
+ } else { # assume not renamed
+ $diffinfo{'status'} = '1';
+ $diffinfo{'from_file'} = $file_name;
+ $diffinfo{'to_file'} = $file_name;
+ }
+ } else { # no filename given
+ $diffinfo{'status'} = '2';
+ $diffinfo{'from_file'} = $hash_parent;
+ $diffinfo{'to_file'} = $hash;
+ }
+
+ #open $fd, "-|", $GIT, "diff", '-p', $hash_parent, $hash
+ open $fd, "-|", $GIT, "diff", '-p', $hash, $hash_parent
+ or die_error(undef, "Open git-diff failed");
+ } else {
+ die_error('404 Not Found', "Missing one of the blob diff parameters")
+ unless %diffinfo;
+ }
+
+ # header