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

[PATCH v2 8/8] gitweb: Refinement highlightning in combined diffs

From
Michał Kiedrowicz <michal.kiedrowicz@gmail.com>
Date
Mar 23, 2012, 22:56 UTC
Message-ID
<1332543417-19664-9-git-send-email-michal.kiedrowicz@gmail.com>
In-Reply-To
<1332543417-19664-1-git-send-email-michal.kiedrowicz@gmail.com>

The highlightning of combined diffs is currently disabled. This is because output from a combined diff is much harder to highlight because it's not obvious which removed and added lines should be compared.

Moreover, code that compares added and removed lines doesn't care about combined diffs. It only skips first +/- character, treating second +/- as a line content.

Let's start with a simple case: only highlight changes that come from one parent, i.e. when every removed line has a corresponding added line for the same parent. This way the highlightning cannot get wrong. For example, following diffs would be highlighted:

	- removed line for first parent
	+ added line for first parent
	  context line
	 -removed line for second parent
	 +added line for second parent
or
	- removed line for first parent
	 -removed line for second parent
	+ added line for first parent
	 +added line for second parent
but following output will not:
	- removed line for first parent
	 -removed line for second parent
	 +added line for second parent
	++added line for both parents

Further changes may introduce more intelligent approach that better handles combined diffs.

Signed-off-by: Michał Kiedrowicz <michal.kiedrowicz@gmail.com>
---
 gitweb/gitweb.perl |   57 +++++++++++++++++++++++++++++++++++++++------------
 1 files changed, 43 insertions(+), 14 deletions(-)
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 872ba12..c056e83 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -5057,12 +5057,12 @@ sub print_inline_diff_lines {
 # Format removed and added line, mark changed part and HTML-format them.
 # Impementation is based on contrib/diff-highlight
 sub format_rem_add_line {
-	my ($rem, $add) = @_;
+	my ($rem, $add, $num_parents) = @_;
 	my @rem = split(//, $rem);
 	my @add = split(//, $add);
 	my ($esc_rem, $esc_add);
-	# Ignore +/- character, thus $prefix_len is set to 1.
-	my ($prefix_len, $suffix_len) = (1, 0);
+	# Ignore leading +/- characters for each parent.
+	my ($prefix_len, $suffix_len) = ($num_parents, 0);
 	my ($prefix_is_space, $suffix_is_space) = (1, 1);
 
 	while ($prefix_len < @rem && $prefix_len < @add) {
@@ -5084,7 +5084,7 @@ sub format_rem_add_line {
 	# part that isn't whitespace.  If lines are completely different, don't
 	# mark them because that would make output unreadable, especially if
 	# diff consists of multiple lines.
-	if (($prefix_len == 1 && $suffix_len == 0) ||
+	if (($prefix_len == $num_parents && $suffix_len == 0) ||
 	    ($prefix_is_space && $suffix_is_space)) {
 		$esc_rem = esc_html($rem, -nbsp=>1);
 		$esc_add = esc_html($add, -nbsp=>1);
@@ -5099,15 +5099,43 @@ sub format_rem_add_line {
 
 # HTML-format diff context, removed and added lines.
 sub format_ctx_rem_add_lines {
-	my ($ctx, $rem, $add, $is_combined) = @_;
+	my ($ctx, $rem, $add, $num_parents) = @_;
 	my (@new_ctx, @new_rem, @new_add);
+	my $can_highlight = 0;
+	my $is_combined = ($num_parents > 1);
 
 	# Highlight if every removed line has a corresponding added line.
-	# Combined diffs are not supported ATM.
-	if (!$is_combined && @$add > 0 && @$add == @$rem) {
+	if (@$add > 0 && @$add == @$rem) {
+		$can_highlight = 1;
+
+		# Highlight lines in combined diff only if the chunk contains
+		# diff between the same version, e.g.
+		#
+		#    - a
+		#   -  b
+		#    + c
+		#   +  d
+		#
+		# Otherwise the highlightling would be confusing.
+		if ($is_combined) {
+			for (my $i = 0; $i < @$add; $i++) {
+				my $prefix_rem = substr($rem->[$i], 0, $num_parents);
+				my $prefix_add = substr($add->[$i], 0, $num_parents);
+
+				$prefix_rem =~ s/-/+/g;
+
+				if ($prefix_rem ne $prefix_add) {
+					$can_highlight = 0;
+					last;
+				}
+			}
+		}
+	}
+
+	if ($can_highlight) {
 		for (my $i = 0; $i < @$add; $i++) {
 			my ($line_rem, $line_add) = format_rem_add_line(
-			        $rem->[$i], $add->[$i]);
+			        $rem->[$i], $add->[$i], $num_parents);
 			push @new_rem, $line_rem;
 			push @new_add, $line_add;
 		}
@@ -5123,10 +5151,11 @@ sub format_ctx_rem_add_lines {
 
 # Print context lines and then rem/add lines.
 sub print_diff_lines {
-	my ($ctx, $rem, $add, $diff_style, $is_combined) = @_;
+	my ($ctx, $rem, $add, $diff_style, $num_parents) = @_;
+	my $is_combined = $num_parents > 1;
 
 	($ctx, $rem, $add) = format_ctx_rem_add_lines($ctx, $rem, $add,
-		$is_combined);
+		$num_parents);
 
 	if ($diff_style eq 'sidebyside' && !$is_combined) {
 		print_sidebyside_diff_lines($ctx, $rem, $add);
@@ -5137,7 +5166,7 @@ sub print_diff_lines {
 }
 
 sub print_diff_chunk {
-	my ($diff_style, $is_combined, $from, $to, @chunk) = @_;
+	my ($diff_style, $num_parents, $from, $to, @chunk) = @_;
 	my (@ctx, @rem, @add);
 
 	# The class of the previous line. 
@@ -5174,7 +5203,7 @@ sub print_diff_chunk {
 		if (((@rem || @add) && $class eq 'ctx') || !$class ||
 		    (@rem && @add && $class ne $prev_class)) {
 			print_diff_lines(\@ctx, \@rem, \@add, $diff_style,
-					$is_combined);
+					$num_parents);
 			@ctx = @rem = @add = ();
 		}
 
@@ -5317,7 +5346,7 @@ sub git_patchset_body {
 			my $class = diff_line_class($patch_line, \%from, \%to);
 
 			if ($class eq 'chunk_header') {
-				print_diff_chunk($diff_style, $is_combined, \%from, \%to, @chunk);
+				print_diff_chunk($diff_style, scalar @hash_parents, \%from, \%to, @chunk);
 				@chunk = ( [ $class, $patch_line ] );
 			} else {
 				push @chunk, [ $class, $patch_line ];
@@ -5326,7 +5355,7 @@ sub git_patchset_body {
 
 	} continue {
 		if (@chunk) {
-			print_diff_chunk($diff_style, $is_combined, \%from, \%to, @chunk);
+			print_diff_chunk($diff_style, scalar @hash_parents, \%from, \%to, @chunk);
 			@chunk = ();
 		}
 		print "</div>\n"; # class="patch"
-- 
1.7.8.4
Previous: Michał KiedrowiczNext: Jakub Narebski
Message 28 of 30 in “gitweb: Highlight interesting parts of diff”
  1. 0/8 gitweb: Highlight interesting parts of diffMichał Kiedrowicz, Mar 23, 2012
  2. 1/8 gitweb: esc_html_hl_regions(): Don't create empty <span> elementsMichał Kiedrowicz, Mar 23, 2012
  3. Jakub NarebskiMar 24, 2012
  4. Michał KiedrowiczMar 24, 2012
  5. gitweb: Use descriptive names in esc_html_hl_regions()Michał Kiedrowicz, Mar 29, 2012
  6. 2/8 gitweb: Pass esc_html_hl_regions() options to esc_html()Michał Kiedrowicz, Mar 23, 2012
  7. Jakub NarebskiMar 24, 2012
  8. Michał KiedrowiczMar 24, 2012
  9. 3/8 gitweb: Extract print_sidebyside_diff_lines()Michał Kiedrowicz, Mar 23, 2012
  10. Jakub NarebskiMar 28, 2012
  11. Michał KiedrowiczMar 29, 2012
  12. Jakub NarebskiMar 30, 2012
  13. 4/8 gitweb: Use print_diff_chunk() for both side-by-side and inline diffsMichał Kiedrowicz, Mar 23, 2012
  14. Jakub NarebskiMar 28, 2012
  15. Michał KiedrowiczMar 29, 2012
  16. Jakub NarebskiMar 30, 2012
  17. Michal KiedrowiczMar 30, 2012
  18. 5/8 gitweb: Move HTML-formatting diff line back to process_diff_line()Michał Kiedrowicz, Mar 23, 2012
  19. Jakub NarebskiMar 29, 2012
  20. Jakub NarebskiMar 29, 2012
  21. Michał KiedrowiczMar 29, 2012
  22. 6/8 gitweb: Push formatting diff lines to print_diff_chunk()Michał Kiedrowicz, Mar 23, 2012
  23. Jakub NarebskiMar 29, 2012
  24. Michał KiedrowiczMar 29, 2012
  25. 7/8 gitweb: Highlight interesting parts of diffMichał Kiedrowicz, Mar 23, 2012
  26. Jakub NarebskiMar 29, 2012
  27. Michał KiedrowiczMar 29, 2012
  28. 8/8 gitweb: Refinement highlightning in combined diffsMichał Kiedrowicz, Mar 23, 2012
  29. Jakub NarebskiMar 29, 2012
  30. Michal KiedrowiczMar 30, 2012

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.