All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jakub Narebski <jnareb@gmail.com>
To: git@vger.kernel.org
Cc: Jakub Narebski <jnareb@gmail.com>
Subject: [PATCH 3/6] gitweb: Add combined diff support to git_patchset_body
Date: Mon,  7 May 2007 01:10:05 +0200	[thread overview]
Message-ID: <11784930121052-git-send-email-jnareb@gmail.com> (raw)
In-Reply-To: <11784930091585-git-send-email-jnareb@gmail.com>

Calling convention for combined diff similar to the one for
git_difftree_body subroutine: difftree info (first parameter) must be
result of calling git-diff-tree with -c/--cc option, and all parents
of a commit must be passed as last parameters. See also description in
  "gitweb: Add combined diff support to git_difftree_body"

This ability is not used yet.

Generating "src" file name for renames in combined diff was separated
into fill_from_file_info subroutine; git_difftree_body was modified to
use it. Currently git_difftree_body and git_patchset_body fills this
info separately.

The from-file line in two-line from-file/to-file header is not
hyperlinked: there can be more than one "from"/"src" file. This
differs from HTML output of ordinary (not combined) diff.

format_diff_line subroutine needs extra $from/$to parameters to format
combined diff patch line correctly.

Signed-off-by: Jakub Narebski <jnareb@gmail.com>
---
Currently the following extended git diff header line in combined diff

	new file mode <mode>

gets its <mode> (filetype) explained like for appropriate extended
header lines for non-combined (ordinary) diff

	old mode <mode>
	new mode <mode>
	deleted file mode <mode>
	new file mode <mode>
	index <hash>..<hash> <mode>

while similar line

	deleted file mode <mode>,<mode>

does not get <mode> explained. Perhaps we should remove this addition,
or make explanation using tooltip (and perhaps <abbr> HTML element).

 gitweb/gitweb.perl |  221 ++++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 181 insertions(+), 40 deletions(-)

diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index c6a2fef..53ae0b8 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -897,19 +897,34 @@ sub format_subject_html {
 sub format_diff_line {
 	my $line = shift;
 	my ($from, $to) = @_;
-	my $char = substr($line, 0, 1);
 	my $diff_class = "";
 
 	chomp $line;
 
-	if ($char eq '+') {
-		$diff_class = " add";
-	} elsif ($char eq "-") {
-		$diff_class = " rem";
-	} elsif ($char eq "@") {
-		$diff_class = " chunk_header";
-	} elsif ($char eq "\\") {
-		$diff_class = " incomplete";
+	if ($from && $to && ref($from->{'href'}) eq "ARRAY") {
+		# combined diff
+		my $prefix = substr($line, 0, scalar @{$from->{'href'}});
+		if ($line =~ m/^\@{3}/) {
+			$diff_class = " chunk_header";
+		} elsif ($line =~ m/^\\/) {
+			$diff_class = " incomplete";
+		} elsif ($prefix =~ tr/+/+/) {
+			$diff_class = " add";
+		} elsif ($prefix =~ tr/-/-/) {
+			$diff_class = " rem";
+		}
+	} else {
+		# assume ordinary diff
+		my $char = substr($line, 0, 1);
+		if ($char eq '+') {
+			$diff_class = " add";
+		} elsif ($char eq '-') {
+			$diff_class = " rem";
+		} elsif ($char eq '@') {
+			$diff_class = " chunk_header";
+		} elsif ($char eq "\\") {
+			$diff_class = " incomplete";
+		}
 	}
 	$line = untabify($line);
 	if ($from && $to && $line =~ m/^\@{2} /) {
@@ -930,6 +945,39 @@ sub format_diff_line {
 		$line = "<span class=\"chunk_info\">@@ $from_text $to_text @@</span>" .
 		        "<span class=\"section\">" . esc_html($section, -nbsp=>1) . "</span>";
 		return "<div class=\"diff$diff_class\">$line</div>\n";
+	} elsif ($from && $to && $line =~ m/^\@{3}/) {
+		my ($prefix, $ranges, $section) = $line =~ m/^(\@+) (.*?) \@+(.*)$/;
+		my (@from_text, @from_start, @from_nlines, $to_text, $to_start, $to_nlines);
+
+		@from_text = split(' ', $ranges);
+		for (my $i = 0; $i < @from_text; ++$i) {
+			($from_start[$i], $from_nlines[$i]) =
+				(split(',', substr($from_text[$i], 1)), 0);
+		}
+
+		$to_text   = pop @from_text;
+		$to_start  = pop @from_start;
+		$to_nlines = pop @from_nlines;
+
+		$line = "<span class=\"chunk_info\">$prefix ";
+		for (my $i = 0; $i < @from_text; ++$i) {
+			if ($from->{'href'}[$i]) {
+				$line .= $cgi->a({-href=>"$from->{'href'}[$i]#l$from_start[$i]",
+				                  -class=>"list"}, $from_text[$i]);
+			} else {
+				$line .= $from_text[$i];
+			}
+			$line .= " ";
+		}
+		if ($to->{'href'}) {
+			$line .= $cgi->a({-href=>"$to->{'href'}#l$to_start",
+			                  -class=>"list"}, $to_text);
+		} else {
+			$line .= $to_text;
+		}
+		$line .= " $prefix</span>" .
+		         "<span class=\"section\">" . esc_html($section, -nbsp=>1) . "</span>";
+		return "<div class=\"diff$diff_class\">$line</div>\n";
 	}
 	return "<div class=\"diff$diff_class\">" . esc_html($line, -nbsp=>1) . "</div>\n";
 }
@@ -2233,6 +2281,39 @@ sub git_print_tree_entry {
 ## ......................................................................
 ## functions printing large fragments of HTML
 
+sub fill_from_file_info {
+	my ($diff, @parents) = @_;
+
+	$diff->{'from_file'} = [ ];
+	$diff->{'from_file'}[$diff->{'nparents'} - 1] = undef;
+	for (my $i = 0; $i < $diff->{'nparents'}; $i++) {
+		if ($diff->{'status'}[$i] eq 'R' ||
+		    $diff->{'status'}[$i] eq 'C') {
+			$diff->{'from_file'}[$i] =
+				git_get_path_by_hash($parents[$i], $diff->{'from_id'}[$i]);
+		}
+	}
+
+	return $diff;
+}
+
+# parameters can be strings, or references to arrays of strings
+sub from_ids_eq {
+	my ($a, $b) = @_;
+
+	if (ref($a) eq "ARRAY" && ref($b) eq "ARRAY" && @$a == @$b) {
+		for (my $i = 0; $i < @$a; ++$i) {
+			return 0 unless ($a->[$i] eq $b->[$i]);
+		}
+		return 1;
+	} elsif (!ref($a) && !ref($b)) {
+		return $a eq $b;
+	} else {
+		return 0;
+	}
+}
+
+
 sub git_difftree_body {
 	my ($difftree, $hash, @parents) = @_;
 	my ($parent) = $parents[0];
@@ -2260,6 +2341,8 @@ sub git_difftree_body {
 
 		if (exists $diff{'nparents'}) { # combined diff
 
+			fill_from_file_info(\%diff, @parents);
+
 			if ($diff{'to_id'} ne ('0' x 40)) {
 				# file exists in the result (child) commit
 				print "<td>" .
@@ -2288,16 +2371,12 @@ sub git_difftree_body {
 			for (my $i = 0; $i < $diff{'nparents'}; $i++) {
 				my $hash_parent = $parents[$i];
 				my $from_hash = $diff{'from_id'}[$i];
-				my $from_path = undef;
+				my $from_path = $diff{'from_file'}[$i];
 				my $status = $diff{'status'}[$i];
 
 				$has_history ||= ($status ne 'A');
 				$not_deleted ||= ($status ne 'D');
 
-				if ($status eq 'R' || $status eq 'C') {
-					$from_path = git_get_path_by_hash($hash_parent, $from_hash);
-				}
-
 				if ($status eq 'A') {
 					print "<td  class=\"link\" align=\"right\"> | </td>\n";
 				} elsif ($status eq 'D') {
@@ -2517,7 +2596,8 @@ sub git_difftree_body {
 }
 
 sub git_patchset_body {
-	my ($fd, $difftree, $hash, $hash_parent) = @_;
+	my ($fd, $difftree, $hash, @hash_parents) = @_;
+	my ($hash_parent) = $hash_parents[0];
 
 	my $patch_idx = 0;
 	my $patch_number = 0;
@@ -2555,6 +2635,9 @@ sub git_patchset_body {
 			if ($patch_line =~ m/^index ([0-9a-fA-F]{40})..([0-9a-fA-F]{40})/) {
 				$from_id = $1;
 				$to_id   = $2;
+			} elsif ($patch_line =~ m/^index ((?:[0-9a-fA-F]{40},)+[0-9a-fA-F]{40})..([0-9a-fA-F]{40})/) {
+				$from_id = [ split(',', $1) ];
+				$to_id   = $2;
 			}
 
 			push @diff_header, $patch_line;
@@ -2564,8 +2647,8 @@ sub git_patchset_body {
 		# check if current patch belong to current raw line
 		# and parse raw git-diff line if needed
 		if (defined $diffinfo &&
-		    $diffinfo->{'from_id'} eq $from_id &&
-		    $diffinfo->{'to_id'}   eq $to_id) {
+		    from_ids_eq($diffinfo->{'from_id'}, $from_id) &&
+		    $diffinfo->{'to_id'} eq $to_id) {
 			# this is split patch
 			print "<div class=\"patch cont\">\n";
 		} else {
@@ -2579,15 +2662,34 @@ sub git_patchset_body {
 			} else {
 				$diffinfo = parse_difftree_raw_line($difftree->[$patch_idx]);
 			}
-			$from{'file'} = $diffinfo->{'from_file'} || $diffinfo->{'file'};
-			$to{'file'}   = $diffinfo->{'to_file'}   || $diffinfo->{'file'};
-			if ($diffinfo->{'status'} ne "A") { # not new (added) file
-				$from{'href'} = href(action=>"blob", hash_base=>$hash_parent,
-				                     hash=>$diffinfo->{'from_id'},
-				                     file_name=>$from{'file'});
+			if ($diffinfo->{'nparents'}) {
+				# combined diff
+				$from{'file'} = [];
+				$from{'href'} = [];
+				fill_from_file_info($diffinfo, @hash_parents)
+					unless exists $diffinfo->{'from_file'};
+				for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) {
+					$from{'file'}[$i] = $diffinfo->{'from_file'}[$i] || $diffinfo->{'to_file'};
+					if ($diffinfo->{'status'}[$i] ne "A") { # not new (added) file
+						$from{'href'}[$i] = href(action=>"blob",
+						                         hash_base=>$hash_parents[$i],
+						                         hash=>$diffinfo->{'from_id'}[$i],
+						                         file_name=>$from{'file'}[$i]);
+					} else {
+						$from{'href'}[$i] = undef;
+					}
+				}
 			} else {
-				delete $from{'href'};
+				$from{'file'} = $diffinfo->{'from_file'} || $diffinfo->{'file'};
+				if ($diffinfo->{'status'} ne "A") { # not new (added) file
+					$from{'href'} = href(action=>"blob", hash_base=>$hash_parent,
+					                     hash=>$diffinfo->{'from_id'},
+					                     file_name=>$from{'file'});
+				} else {
+					delete $from{'href'};
+				}
 			}
+			$to{'file'} = $diffinfo->{'to_file'} || $diffinfo->{'file'};
 			if ($diffinfo->{'status'} ne "D") { # not deleted file
 				$to{'href'} = href(action=>"blob", hash_base=>$hash,
 				                   hash=>$diffinfo->{'to_id'},
@@ -2602,19 +2704,34 @@ sub git_patchset_body {
 
 		# print "git diff" header
 		$patch_line = shift @diff_header;
-		$patch_line =~ s!^(diff (.*?) )"?a/.*$!$1!;
-		if ($from{'href'}) {
-			$patch_line .= $cgi->a({-href => $from{'href'}, -class => "path"},
-			                       'a/' . esc_path($from{'file'}));
-		} else { # file was added
-			$patch_line .= 'a/' . esc_path($from{'file'});
-		}
-		$patch_line .= ' ';
-		if ($to{'href'}) {
-			$patch_line .= $cgi->a({-href => $to{'href'}, -class => "path"},
-			                       'b/' . esc_path($to{'file'}));
-		} else { # file was deleted
-			$patch_line .= 'b/' . esc_path($to{'file'});
+		if ($diffinfo->{'nparents'}) {
+
+			# combined diff
+			$patch_line =~ s!^(diff (.*?) )"?.*$!$1!;
+			if ($to{'href'}) {
+				$patch_line .= $cgi->a({-href => $to{'href'}, -class => "path"},
+				                       esc_path($to{'file'}));
+			} else { # file was deleted
+				$patch_line .= esc_path($to{'file'});
+			}
+
+		} else {
+
+			$patch_line =~ s!^(diff (.*?) )"?a/.*$!$1!;
+			if ($from{'href'}) {
+				$patch_line .= $cgi->a({-href => $from{'href'}, -class => "path"},
+				                       'a/' . esc_path($from{'file'}));
+			} else { # file was added
+				$patch_line .= 'a/' . esc_path($from{'file'});
+			}
+			$patch_line .= ' ';
+			if ($to{'href'}) {
+				$patch_line .= $cgi->a({-href => $to{'href'}, -class => "path"},
+				                       'b/' . esc_path($to{'file'}));
+			} else { # file was deleted
+				$patch_line .= 'b/' . esc_path($to{'file'});
+			}
+
 		}
 		print "<div class=\"diff header\">$patch_line</div>\n";
 
@@ -2631,14 +2748,37 @@ sub git_patchset_body {
 				$patch_line .= $cgi->a({-href=>$to{'href'}, -class=>"path"},
 				                       esc_path($to{'file'}));
 			}
-			# match <mode>
+			# match single <mode>
 			if ($patch_line =~ m/\s(\d{6})$/) {
 				$patch_line .= '<span class="info"> (' .
 				               file_type_long($1) .
 				               ')</span>';
 			}
 			# match <hash>
-			if ($patch_line =~ m/^index/) {
+			if ($patch_line =~ m/^index [0-9a-fA-F]{40},[0-9a-fA-F]{40}/) {
+				# can match only for combined diff
+				$patch_line = 'index ';
+				for (my $i = 0; $i < $diffinfo->{'nparents'}; $i++) {
+					if ($from{'href'}[$i]) {
+						$patch_line .= $cgi->a({-href=>$from{'href'}[$i],
+						                        -class=>"hash"},
+						                       substr($diffinfo->{'from_id'}[$i],0,7));
+					} else {
+						$patch_line .= '0' x 7;
+					}
+					# separator
+					$patch_line .= ',' if ($i < $diffinfo->{'nparents'} - 1);
+				}
+				$patch_line .= '..';
+				if ($to{'href'}) {
+					$patch_line .= $cgi->a({-href=>$to{'href'}, -class=>"hash"},
+					                       substr($diffinfo->{'to_id'},0,7));
+				} else {
+					$patch_line .= '0' x 7;
+				}
+
+			} elsif ($patch_line =~ m/^index [0-9a-fA-F]{40}..[0-9a-fA-F]{40}/) {
+				# can match only for ordinary diff
 				my ($from_link, $to_link);
 				if ($from{'href'}) {
 					$from_link = $cgi->a({-href=>$from{'href'}, -class=>"hash"},
@@ -2674,7 +2814,8 @@ sub git_patchset_body {
 		}
 		next PATCH if ($patch_line =~ m/^diff /);
 		#assert($patch_line =~ m/^---/) if DEBUG;
-		if ($from{'href'} && $patch_line =~ m!^--- "?a/!) {
+		if (!$diffinfo->{'nparents'} && # not from-file line for combined diff
+		    $from{'href'} && $patch_line =~ m!^--- "?a/!) {
 			$patch_line = '--- a/' .
 			              $cgi->a({-href=>$from{'href'}, -class=>"path"},
 			                      esc_path($from{'file'}));
-- 
1.5.1.3

  parent reply	other threads:[~2007-05-06 23:05 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2007-05-06 23:10 [PATCH 0/6] gitweb: Add combined diff support Jakub Narebski
2007-05-06 23:10 ` [PATCH 1/6] gitweb: Add parsing of raw combined diff format to parse_difftree_raw_line Jakub Narebski
2007-05-06 23:10 ` [PATCH 2/6] gitweb: Add combined diff support to git_difftree_body Jakub Narebski
2007-05-06 23:10 ` Jakub Narebski [this message]
2007-05-06 23:10 ` [PATCH 4/6] gitweb: Make it possible to use pre-parsed info in git_difftree_body Jakub Narebski
2007-05-06 23:10 ` [PATCH 5/6] gitweb: Show combined diff for merge commits in 'commitdiff' view Jakub Narebski
2007-05-06 23:10 ` [PATCH 6/6] gitweb: Show combined diff for merge commits in 'commit' view Jakub Narebski
2007-05-06 23:10 ` [PATCH 7/6] todo: Remove "Gitweb diff on merge commits" entry Jakub Narebski
2007-05-08  1:31 ` [PATCH 0/6] gitweb: Add combined diff support Junio C Hamano
2007-05-08  1:50   ` Jakub Narebski
2007-05-16 22:05 ` [PATCH 7/6] gitweb: Empty patch for merge means trivial merge, not no differences Jakub Narebski
2007-05-16 22:08   ` Junio C Hamano

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=11784930121052-git-send-email-jnareb@gmail.com \
    --to=jnareb@gmail.com \
    --cc=git@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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.