git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 16/19] gitweb: Use git-diff-tree or git-diff patch output for blobdiff

From
Jakub Narebski <jnareb@gmail.com>
Date
Aug 25, 2006, 19:13 UTC
Message-ID
<200608252113.34731.jnareb@gmail.com>
In-Reply-To
<200608240015.15071.jnareb@gmail.com>

This is second part of removing gitweb dependency on external diff (used in git_diff_print).

Get rid of git_diff_print invocation in git_blobdiff, and use either git-diff-tree (when both hash_base and hash_parent_base are provided) patch format or git-diff patch format (when only hash and hash_parent are provided) for output.

Supported URI schemes, and output formats:
* New URI scheme: both hash_base and hash_parent_base (trees-ish
  containing blobs versions we want to compare) are provided.
  Also either filename is provided, or hash (of blob) is provided
  (we try to find filename then).
  For this scheme we have copying and renames detection, mode changes,
  file types etc., and information extended diff header is correct.
* Old URI scheme: hash_parent_base is not provided, we use hash and
  hash_parent to directly compare blobs using git-diff. If no filename
  is given, blobs hashes are used in place of filenames.
  This scheme has always "blob" as file type, it cannot detect mode
  changes, and we rely on CGI parameters to provide name of the file.

Added git_to_hash subroutine, which transforms symbolic name or list of symbolic name to hash or list of hashes using git-rev-parse.

To have "blob" instead of "unknown" (or "file" regardless of the type) in "gitweb diff header" for legacy scheme, file_type function now returns its argument if it is not octal string.

Added support for fake "2" status code in git_patchset_body. Such code is generated by git_blobdiff in legacy scheme case.

ATTENTION: The order of arguments (operands) to git-diff is reversed
(sic!) to have correct diff in the legacy (no hash_parent_base) case.
$hash_parent, $hash ordering is commented out, as it gives reversed
patch (at least for git version 1.4.1.1) as compared to output in new
scheme and output of older gitweb version.
Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
P.S. This is the place to mention that it would be nice to have in 
gitweb/README specification what minimal version of git is needed
for some gitweb features; e.g. "history" relies on having --full-history
option to git-rev-list, "blobdiff" (and later "blobdiff_plain") relies
on git-diff which accepts blob hashes, and relies on bug in git-diff...
 gitweb/gitweb.perl |  149 ++++++++++++++++++++++++++++++++++++++++++++++------
 1 files changed, 132 insertions(+), 17 deletions(-)
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index a866922..9be2b2c 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -454,7 +454,13 @@ sub mode_str {
 
 # convert file mode in octal to file type string
 sub file_type {
-	my $mode = oct shift;
+	my $mode = shift;
+
+	if ($mode !~ m/^[0-7]+$/) {
+		return $mode;
+	} else {
+		$mode = oct $mode;
+	}
 
 	if (S_ISDIR($mode & S_IFMT)) {
 		return "directory";
@@ -618,6 +624,26 @@ sub git_get_hash_by_path {
 	return $3;
 }
 
+# converts symbolic name to hash
+sub git_to_hash {
+	my @params = @_;
+	return undef unless @params;
+
+	open my $fd, "-|", $GIT, "rev-parse", @params
+		or return undef;
+	my @hashes = map { chomp; $_ } <$fd>;
+	close $fd;
+
+	if (wantarray) {
+		return @hashes;
+	} elsif (scalar(@hashes) == 1) {
+		# single hash
+		return $hashes[0];
+	} else {
+		return \@hashes;
+	}
+}
+
 ## ......................................................................
 ## git utility functions, directly accessing git repository
 
@@ -1672,7 +1698,8 @@ sub git_patchset_body {
 				      "</div>\n"; # class="diff_info"
 
 			} elsif ($diffinfo->{'status'} eq "R" || # renamed
-			         $diffinfo->{'status'} eq "C") { # copied
+			         $diffinfo->{'status'} eq "C" || # copied
+			         $diffinfo->{'status'} eq "2") { # with two filenames (from git_blobdiff)
 				print "<div class=\"diff_info\">" .
 				      file_type($diffinfo->{'from_mode'}) . ":" .
 				      $cgi->a({-href => href(action=>"blob", hash_base=>$hash_parent,
@@ -2788,14 +2815,102 @@ sub git_commit {
 }
 
 sub git_blobdiff {
-	mkdir($git_temp, 0700);
-	git_header_html();
+	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
 	my $formats_nav =
 		$cgi->a({-href => href(action=>"blobdiff_plain",
 		                       hash=>$hash, hash_parent=>$hash_parent,
 		                       hash_base=>$hash_base, hash_parent_base=>$hash_parent_base,
 		                       file_name=>$file_name, file_parent=>$file_parent)},
 		        "plain");
+	git_header_html();
 	if (defined $hash_base && (my %co = parse_commit($hash_base))) {
 		git_print_page_nav('','', $hash_base,$co{'tree'},$hash_base, $formats_nav);
 		git_print_header_div('commit', esc_html($co{'title'}), $hash_base);
@@ -2803,19 +2918,19 @@ sub git_blobdiff {
 		print "<div class=\"page_nav\"><br/>$formats_nav<br/></div>\n";
 		print "<div class=\"title\">$hash vs $hash_parent</div>\n";
 	}
-	git_print_page_path($file_name, "blob", $hash_base);
-	print "<div class=\"page_body\">\n" .
-	      "<div class=\"diff_info\">blob:" .
-	      $cgi->a({-href => href(action=>"blob", hash=>$hash_parent,
-	                             hash_base=>$hash_parent_base, file_name=>($file_parent || $file_name))},
-	              $hash_parent) .
-	      " -> blob:" .
-	      $cgi->a({-href => href(action=>"blob", hash=>$hash,
-	                             hash_base=>$hash_base, file_name=>$file_name)},
-	              $hash) .
-	      "</div>\n";
-	git_diff_print($hash_parent, $file_name || $hash_parent, $hash, $file_name || $hash);
-	print "</div>"; # page_body
+	if (defined $file_name) {
+		git_print_page_path($file_name, "blob", $hash_base);
+	} else {
+		print "<div class=\"page_path\"></div>\n";
+	}
+
+	# patch
+	print "<div class=\"page_body\">\n";
+
+	git_patchset_body($fd, [ \%diffinfo ], $hash_base, $hash_parent_base);
+	close $fd;
+
+	print "</div>\n"; # class="page_body"
 	git_footer_html();
 }
 
-- 
1.4.1.1
Previous: Jakub NarebskiNext: Jakub Narebski
Message 26 of 53 in “gitweb: Use git-diff-tree patch output for commitdiff”
  1. gitweb: Use git-diff-tree patch output for commitdiffJakub Narebski, Aug 23, 2006
  2. gitweb: Replace git_commitdiff_plain by anonymous subroutineJakub Narebski, Aug 23, 2006
  3. gitweb: Show information about incomplete lines in commitdiffJakub Narebski, Aug 23, 2006
  4. Junio C HamanoAug 24, 2006
  5. Jakub NarebskiAug 24, 2006
  6. Junio C HamanoAug 24, 2006
  7. Jakub NarebskiAug 24, 2006
  8. Junio C HamanoAug 24, 2006
  9. Jakub NarebskiAug 24, 2006
  10. Marco CostalbaAug 25, 2006
  11. Jakub NarebskiAug 25, 2006
  12. gitweb: Remove invalid comment in format_diff_lineJakub Narebski, Aug 24, 2006
  13. gitweb: Streamify patch output in git_commitdiffJakub Narebski, Aug 24, 2006
  14. gitweb: Add git_get_{following,preceding}_references functionsJakub Narebski, Aug 24, 2006
  15. gitweb: Faster return from git_get_preceding_references if possibleJakub Narebski, Aug 24, 2006
  16. gitweb: Add git_get_rev_name_tags functionJakub Narebski, Aug 24, 2006
  17. gitweb: Use git_get_name_rev_tags for commitdiff_plain X-Git-Tag: headerJakub Narebski, Aug 24, 2006
  18. gitweb: Add support for hash_parent_base parameter for blobdiffsJakub Narebski, Aug 24, 2006
  19. gitweb: Add support for hash_parent_base parameter for blobdiffsJakub Narebski, Aug 24, 2006
  20. 11/19 gitweb: Allow for pre-parsed difftree info in git_patchset_bodyJakub Narebski, Aug 25, 2006
  21. 12/19 gitweb: Parse two-line from-file/to-file diff header in git_patchset_bodyJakub Narebski, Aug 25, 2006
  22. 13/19 gitweb: Add invisible hyperlink to from-file/to-file diff headerJakub Narebski, Aug 25, 2006
  23. Linus TorvaldsAug 27, 2006
  24. 14/19 gitweb: Always display link to blobdiff_plain in git_blobdiffJakub Narebski, Aug 25, 2006
  25. 15/19 gitweb: Change here-doc back for style consistency in git_blobdiffJakub Narebski, Aug 25, 2006
  26. 16/19 gitweb: Use git-diff-tree or git-diff patch output for blobdiffJakub Narebski, Aug 25, 2006
  27. Jakub NarebskiAug 26, 2006
  28. Junio C HamanoAug 26, 2006
  29. Jakub NarebskiAug 26, 2006
  30. gitweb: Remove workaround for git-diff bug fixed in f82cd3cJakub Narebski, Aug 26, 2006
  31. 17/19 gitweb: git_blobdiff_plain is git_blobdiff('plain')Jakub Narebski, Aug 25, 2006
  32. 18/19 gitweb: Remove git_diff_print subroutineJakub Narebski, Aug 25, 2006
  33. 19/19 gitweb: Remove creating directory for temporary filesJakub Narebski, Aug 25, 2006
  34. Marco CostalbaAug 25, 2006
  35. Jakub NarebskiAug 25, 2006
  36. Junio C HamanoAug 26, 2006
  37. Marco CostalbaAug 26, 2006
  38. Junio C HamanoAug 26, 2006
  39. Marco CostalbaAug 26, 2006
  40. Marco CostalbaAug 26, 2006
  41. Shawn PearceAug 26, 2006
  42. Josef WeidendorferAug 26, 2006
  43. Jakub NarebskiAug 26, 2006
  44. Junio C HamanoAug 26, 2006
  45. Jakub NarebskiAug 26, 2006
  46. Junio C HamanoAug 27, 2006
  47. Junio C HamanoAug 27, 2006
  48. Jakub NarebskiAug 27, 2006
  49. 00/19 gitweb: Remove dependency on external diff and need for temporary filesJakub Narebski, Aug 25, 2006
  50. Linus TorvaldsAug 27, 2006
  51. David MillerAug 27, 2006
  52. Linus TorvaldsAug 27, 2006
  53. Jakub NarebskiAug 27, 2006

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.