threads / patch / 7698

patch, 7 partsgitweb: Support comparing blobs with different names

Subject: [PATCH 2/7] gitweb: Support comparing blobs with different names

## tl;dr

7 messages between Apr 16, 2007 and May 19, 2007. Diffs are folded; open one to read it.

replies: 6people: 3as markdown or json

Martin Koegler· Apr 16, 2007, 20:18 UTC · lore

Currently, blobdiff can only compare blobs with different file names, if no hb/hpb parameters are present.

This patch adds support for comparing two blobs specified by any combination of hb/f/h and hpb/fp/hp.

Signed-off-by: Martin Koegler <mkoegler@auto.tuwien.ac.at>
---
New version, as I found a bug in the expiration handling code.

I unified all blobdiff variants and added support for comparing blobs with different names.

If h/hp parameter are missing, I need to generate them with git_get_hash_by_path, as the are needed for the html header, which is generated before parsing the git-diff output.

I currently ignore all mode changed, as they are part of the tree. I don't think that displaying a mode change message justifes two call to git-ls-tree for each blob diff (Currently it only calls git-ls-tree for each missing h/hp parameter).

 gitweb/gitweb.perl |  136 ++++++++++++++++------------------------------------
 1 files changed, 41 insertions(+), 95 deletions(-)
Show changes to gitweb/gitweb.perl +41 −95
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index cbd8d03..790041c 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -3902,109 +3902,54 @@ sub git_blobdiff {
 	my $fd;
 	my @difftree;
 	my %diffinfo;
-	my $expires;
-
-	# 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_cmd(), "diff-tree", '-r', @diff_opts,
-				$hash_parent_base, $hash_base,
-				"--", (defined $file_parent ? $file_parent : ()), $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 &&
-		         $hash =~ /[0-9a-fA-F]{40}/) {
-			# try to find filename from $hash
-
-			# read filtered raw output
-			open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
-				$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");
-		}
+	my $expires = '+1d';
 
-		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'};
+	$file_parent ||= $file_name;
 
-		# non-textual hash id's can be cached
-		if ($hash_base =~ m/^[0-9a-fA-F]{40}$/ &&
-		    $hash_parent_base =~ m/^[0-9a-fA-F]{40}$/) {
-			$expires = '+1d';
-		}
+	# non-textual hash id's can be cached
+	if (defined $hash && $hash !~ m/^[0-9a-fA-F]{40}$/) {
+		$expires = undef;
+	} elsif (defined $hash_parent && $hash_parent !~ m/^[0-9a-fA-F]{40}$/) {
+		$expires = undef;
+	} elsif (defined $hash_base && $hash_base !~ m/^[0-9a-fA-F]{40}$/) {
+		$expires = undef;
+	} elsif (defined $hash_parent_base && $hash_parent_base !~ m/^[0-9a-fA-F]{40}$/) {
+		$expires = undef;
+	}
 
-		# open patch output
-		open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
-			'-p', ($format eq 'html' ? "--full-index" : ()),
-			$hash_parent_base, $hash_base,
-			"--", (defined $file_parent ? $file_parent : ()), $file_name
-			or die_error(undef, "Open git-diff-tree failed");
+	# if hash parameter is missing, read it from the commit.
+	if (defined $hash_base && defined $file_name && !defined $hash) {
+		$hash = git_get_hash_by_path($hash_base, $file_name);
 	}
 
-	# 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;
-		}
+	if (defined $hash_parent_base && defined $file_parent && !defined $hash_parent) {
+	    $hash_parent = git_get_hash_by_path($hash_parent_base, $file_parent);
+	}
+	
+	if (!defined $hash || ! defined $hash_parent) {
+		die_error('404 Not Found', "Missing one of the blob diff parameters");
+	}
 
-		# non-textual hash id's can be cached
-		if ($hash =~ m/^[0-9a-fA-F]{40}$/ &&
-		    $hash_parent =~ m/^[0-9a-fA-F]{40}$/) {
-			$expires = '+1d';
-		}
 
-		# open patch output
-		open $fd, "-|", git_cmd(), "diff", @diff_opts,
-			'-p', ($format eq 'html' ? "--full-index" : ()),
-			$hash_parent, $hash, "--"
-			or die_error(undef, "Open git-diff failed");
-	} else  {
-		die_error('404 Not Found', "Missing one of the blob diff parameters")
-			unless %diffinfo;
+	# 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) {
+		$diffinfo{'status'} = '2';
+		$diffinfo{'from_file'} = $file_parent;
+		$diffinfo{'to_file'}   = $file_name;
+	} else { # no filename given
+		$diffinfo{'status'} = '2';
+		$diffinfo{'from_file'} = $hash_parent;
+		$diffinfo{'to_file'}   = $hash;
 	}
 
+	# open patch output
+	open $fd, "-|", git_cmd(), "diff", @diff_opts,
+	$hash_parent, $hash, "--"
+		or die_error(undef, "Open git-diff failed");
+
 	# header
 	if ($format eq 'html') {
 		my $formats_nav =
@@ -4028,11 +3973,12 @@ sub git_blobdiff {
 		}
 
 	} elsif ($format eq 'plain') {
+		my $patch_file_name = $file_name || $hash;
 		print $cgi->header(
 			-type => 'text/plain',
 			-charset => 'utf-8',
 			-expires => $expires,
-			-content_disposition => 'inline; filename="' . "$file_name" . '.patch"');
+			-content_disposition => 'inline; filename="' . "$patch_file_name" . '.patch"');
 
 		print "X-Git-Url: " . $cgi->self_url() . "\n\n";
 
-- 
1.5.1.1.85.gf1888
Jakub Narebski· Apr 29, 2007, 21:35 UTC · re: Martin Koegler · lore

Re: [PATCH 2/7] gitweb: Support comparing blobs with different names

On Monday, 16 April 2007, Martin Koegler wrote:
> Currently, blobdiff can only compare blobs with different file
> names, if no hb/hpb parameters are present.
> 
Not true. Since commit 5ae917acdf40444945271e5e014cda37e202504e
  "gitweb: Support comparing blobs (files) with different names"

git_blobdiff ('blobdiff' view) suports comparing arbitrary blobs (also with different file names) when no hp/hpb parameters are present (although there would be no mode change information), but supports comparing blobs with different file names in presence of hp/hpb parameters, provided that it is proper rename diff.

Since this commit you can select rename diff which is part of 'commitdiff' view as a 'blobdiff' view, correctly.

> This patch adds support for comparing two blobs specified by any
> combination of hb/f/h and hpb/fp/hp.
>

But it used to lose mode change information. This info should be I think in commit message.

Because your work on including mode changes for git-diff with
<tree-ish>:<path-to-blob> extended SHA-1 syntax (thanks a lot!)
I guess that you would want to rewrite this patch.
 
Show 10 quoted lines
> Signed-off-by: Martin Koegler <mkoegler@auto.tuwien.ac.at>
> ---
> New version, as I found a bug in the expiration handling code.
> 
> I unified all blobdiff variants and added support for comparing blobs
> with different names.
> 
> If h/hp parameter are missing, I need to generate them with
> git_get_hash_by_path, as the are needed for the html header, which is
> generated before parsing the git-diff output.

git_get_hash_by_path uses git-ls-tree but it does not catch all the info; perhaps git_get_info_by_path would be called for here.

> I currently ignore all mode changes, as they are part of the tree. I
> don't think that displaying a mode change message justifes two call to
> git-ls-tree for each blob diff (Currently it only calls git-ls-tree
> for each missing h/hp parameter).

That is not a problem now, thanks to your patches... although it would be nice to have subroutine which would get the difftree information from extended git diff header (if it is possible).

Show 11 quoted lines
> +	my $expires = '+1d';
> +	# non-textual hash id's can be cached
> +	if (defined $hash && $hash !~ m/^[0-9a-fA-F]{40}$/) {
> +		$expires = undef;
> +	} elsif (defined $hash_parent && $hash_parent !~ m/^[0-9a-fA-F]{40}$/) {
> +		$expires = undef;
> +	} elsif (defined $hash_base && $hash_base !~ m/^[0-9a-fA-F]{40}$/) {
> +		$expires = undef;
> +	} elsif (defined $hash_parent_base && $hash_parent_base !~ m/^[0-9a-fA-F]{40}$/) {
> +		$expires = undef;
> +	}

Usually gitweb _changes_ $expires to '+1d' when appropriate, instead of defaulting to '+1d' and undefining it. But this might be the matter of style consistency, and personal preferences: one complicated boolean expression or set of if ... elsif ... conditionals.

The idea is that both TO and FROM are to be immutable; this means either $hash_parent (or $hash_parent_base) being full SHA-1 and having filename defined, or if appropriate "parent" hash is not present (because "parent" hash has precedence over blob hash) it means that $hash (or $hash_parent) being full SHA-1.

[...]  
> +	if (defined $hash_parent_base && defined $file_parent && !defined $hash_parent) {
> +	    $hash_parent = git_get_hash_by_path($hash_parent_base, $file_parent);
> +	}
[...]
> +	# open patch output
> +	open $fd, "-|", git_cmd(), "diff", @diff_opts,
> +	$hash_parent, $hash, "--"
> +		or die_error(undef, "Open git-diff failed");

You would most probably use now "$hash_base:$file_name" instead of $hash if $hash_base is defined, i.e.

  defined $hash_base ? "$hash_base:$file_name" : $hash

and similarly for $hash_parent parameter now that <tree>:<path> form respects mode changes information.

-- 
Jakub Narebski
ShadeHawk on #git
Poland
Martin Koegler· Apr 30, 2007, 05:27 UTC · re: Jakub Narebski · lore

Re: [PATCH 2/7] gitweb: Support comparing blobs with different names

On Sun, Apr 29, 2007 at 11:35:49PM +0200, Jakub Narebski wrote:
Show 5 quoted lines
> On Monday, 16 April 2007, Martin Koegler wrote:
> 
> > Currently, blobdiff can only compare blobs with different file
> > names, if no hb/hpb parameters are present.
> > 

I've already posted a new version of this patch: http://www.spinics.net/lists/git/msg28812.html

Can you please look at it.
Show 13 quoted lines
> > Signed-off-by: Martin Koegler <mkoegler@auto.tuwien.ac.at>
> > ---
> > New version, as I found a bug in the expiration handling code.
> > 
> > I unified all blobdiff variants and added support for comparing blobs
> > with different names.
> > 
> > If h/hp parameter are missing, I need to generate them with
> > git_get_hash_by_path, as the are needed for the html header, which is
> > generated before parsing the git-diff output.
> 
> git_get_hash_by_path uses git-ls-tree but it does not catch all the info;
> perhaps git_get_info_by_path would be called for here.

I now only need the hash of the blob, if not passed as parameter, to generate a correct header.

Show 17 quoted lines
> [...]  
> > +	if (defined $hash_parent_base && defined $file_parent && !defined $hash_parent) {
> > +	    $hash_parent = git_get_hash_by_path($hash_parent_base, $file_parent);
> > +	}
> [...]
> > +	# open patch output
> > +	open $fd, "-|", git_cmd(), "diff", @diff_opts,
> > +	$hash_parent, $hash, "--"
> > +		or die_error(undef, "Open git-diff failed");
> 
> You would most probably use now "$hash_base:$file_name" instead of $hash
> if $hash_base is defined, i.e.
> 
>   defined $hash_base ? "$hash_base:$file_name" : $hash
> 
> and similarly for $hash_parent parameter now that <tree>:<path> form
> respects mode changes information.
Addressed in new patch.
mfg Martin Kögler
Martin Koegler· Apr 16, 2007, 20:20 UTC · lore

[PATCH 4/7] gitweb: Add treediff view

git_treediff supports comparing different trees. A tree can be specified either as hash or as base hash and filename. --- New version, as I found a bug in the expiration handling code.

I removed the parent links for now, as they require more discussion.

This patch uses empty fp/f for the root directory and does not propagte f to fp. Patch 6 adds this.

 gitweb/gitweb.perl |  116 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 116 insertions(+), 0 deletions(-)
Show changes to gitweb/gitweb.perl +116 −0
diff --git a/gitweb/gitweb.perl b/gitweb/gitweb.perl
index 1551d95..c2dc82d 100755
--- a/gitweb/gitweb.perl
+++ b/gitweb/gitweb.perl
@@ -450,6 +450,8 @@ my %actions = (
 	"tag" => \&git_tag,
 	"tags" => \&git_tags,
 	"tree" => \&git_tree,
+	"treediff" => \&git_treediff,
+	"treediff_plain" => \&git_treediff_plain,
 	"snapshot" => \&git_snapshot,
 	"object" => \&git_object,
 	# those below don't need $project
@@ -4174,6 +4176,120 @@ sub git_commitdiff_plain {
 	git_commitdiff('plain');
 }
 
+sub git_treediff {
+	my $format = shift || 'html';
+	my $expires = '+1d';
+
+	# non-textual hash id's can be cached
+	if (defined $hash && $hash !~ m/^[0-9a-fA-F]{40}$/) {
+		$expires = undef;
+	} elsif (defined $hash_parent && $hash_parent !~ m/^[0-9a-fA-F]{40}$/) {
+		$expires = undef;
+	} elsif (defined $hash_base && $hash_base !~ m/^[0-9a-fA-F]{40}$/) {
+		$expires = undef;
+	} elsif (defined $hash_parent_base && $hash_parent_base !~ m/^[0-9a-fA-F]{40}$/) {
+		$expires = undef;
+	}
+
+	# we need to prepare $formats_nav before any parameter munging
+	my $formats_nav;
+	if ($format eq 'html') {
+		$formats_nav =
+			$cgi->a({-href => href(action=>"treediff_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)},
+					"raw");
+	}
+
+	if (!defined $hash) {
+		if (!defined $hash_base) {
+			die_error(undef,'tree parameter missing');
+		}
+		$hash = $hash_base;
+		$hash .= ":".$file_name if (defined $file_name);
+	}
+	
+	if (!defined $hash_parent) {
+		if (!defined $hash_parent_base) {
+			die_error(undef,'tree parameter missing');
+		}
+		$hash_parent = $hash_parent_base;
+		$hash_parent .= ":".$file_parent if (defined $file_parent);
+	}
+
+	# read treediff
+	my $fd;
+	my @difftree;
+	if ($format eq 'html') {
+		open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
+		"--no-commit-id", "--patch-with-raw", "--full-index",
+		$hash_parent, $hash, "--"
+			or die_error(undef, "Open git-diff-tree failed");
+		
+		while (my $line = <$fd>) {
+			chomp $line;
+			# empty line ends raw part of diff-tree output
+			last unless $line;
+			push @difftree, $line;
+		}
+		
+	} elsif ($format eq 'plain') {
+		open $fd, "-|", git_cmd(), "diff-tree", '-r', @diff_opts,
+		'-p', $hash_parent, $hash, "--"
+			or die_error(undef, "Open git-diff-tree failed");
+		
+	} else {
+		die_error(undef, "Unknown treediff format");
+	}
+
+	# write header
+	if ($format eq 'html') {
+		git_header_html(undef, $expires);
+		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);
+		} else {
+			print "<div class=\"page_nav\"><br/>$formats_nav<br/></div>\n";
+			print "<div class=\"title\">$hash vs $hash_parent</div>\n";
+		}
+		print "<div class=\"page_body\">\n";
+		
+	} elsif ($format eq 'plain') {
+		my $filename = basename($project) . "-$hash-$hash_parent.patch";
+		
+		print $cgi->header(
+						   -type => 'text/plain',
+						   -charset => 'utf-8',
+						   -expires => $expires,
+						   -content_disposition => 'inline; filename="' . "$filename" . '"');
+		
+		print "X-Git-Url: " . $cgi->self_url() . "\n\n";
+		print "---\n\n";
+	}
+	
+	# write patch
+	if ($format eq 'html') {
+		git_difftree_body(\@difftree, $hash_base, $hash_parent_base, $file_name, $file_parent);
+		print "<br/>\n";
+
+		git_patchset_body($fd, \@difftree, $hash_base, $hash_parent_base, $file_name, $file_parent);
+		close $fd;
+		print "</div>\n"; # class="page_body"
+		git_footer_html();
+
+	} elsif ($format eq 'plain') {
+		local $/ = undef;
+		print <$fd>;
+		close $fd
+			or print "Reading git-diff-tree failed\n";
+	}
+}
+
+sub git_treediff_plain {
+	git_treediff('plain');
+}
+
 sub git_history {
 	if (!defined $hash_base) {
 		$hash_base = git_get_head_hash($project);
-- 
1.5.1.1.85.gf1888
Petr Baudis· May 18, 2007, 08:49 UTC · lore

Re: [PATCH 5/7] gitweb: Prototyp for selecting diffs in JavaScript

  Hi,
On Sun, Apr 15, 2007 at 10:46:08PM CEST, Martin Koegler wrote:
> ---
> This patch is only to test the other patches. I'm working on reimplementing it in perl.
  I think this is something that I'd quite like to testdrive at
repo.or.cz, but I'm admittelly somewhat lost in all the patches,
resends, new versions, something got applied, etc. - if it's not too big
a hassle for you, would you please care to resend the latest series?
  Thanks,
-- 
				Petr "Pasky" Baudis
Stuff: http://pasky.or.cz/
Ever try. Ever fail. No matter. // Try again. Fail again. Fail better.
		-- Samuel Beckett
Martin Koegler· May 19, 2007, 07:57 UTC · re: Petr Baudis · lore

Re: [PATCH 5/7] gitweb: Prototyp for selecting diffs in JavaScript

On Fri, May 18, 2007 at 10:49:35AM +0200, Petr Baudis wrote:
Show 8 quoted lines
> On Sun, Apr 15, 2007 at 10:46:08PM CEST, Martin Koegler wrote:
> > ---
> > This patch is only to test the other patches. I'm working on reimplementing it in perl.
> 
>   I think this is something that I'd quite like to testdrive at
> repo.or.cz, but I'm admittelly somewhat lost in all the patches,
> resends, new versions, something got applied, etc. - if it's not too big
> a hassle for you, would you please care to resend the latest series?
Againgst what version/repository do you want the patch?

For the new version, I intend to move the JavaScript file into the head tag of the html page. I could integrate your incremental blame link rewrite as well. If I'm doing this, we could remove the blamelink class hack as well as the fixBlameLinks JavaScript function.

mfg Martin Kögler
Petr Baudis· May 19, 2007, 08:27 UTC · re: Martin Koegler · lore

Re: [PATCH 5/7] gitweb: Prototyp for selecting diffs in JavaScript

On Sat, May 19, 2007 at 09:57:01AM CEST, Martin Koegler wrote:
> Againgst what version/repository do you want the patch?

Whatever is comfortable for you :-) - next would be ideal for me, but can be something else as well.

> For the new version, I intend to move the JavaScript file into the
> head tag of the html page. I could integrate your incremental blame
> link rewrite as well. If I'm doing this, we could remove the blamelink
> class hack as well as the fixBlameLinks JavaScript function.

It's not clear at all yet whether incremental blame is viable idea because I suspect Firefox might be just too horribly slow at working with DOM for it to make sense for any files of non-trivial size. So I wouldn't rely on this going in just yet. :-(

-- 
				Petr "Pasky" Baudis
Stuff: http://pasky.or.cz/
Ever try. Ever fail. No matter. // Try again. Fail again. Fail better.
		-- Samuel Beckett

← back to recent threads