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

Re: [PATCH 0/2] Demonstrate and partially work around a gitattributes problem

From
Jeff King <peff@peff.net>
Date
May 16, 2017, 07:54 UTC
Message-ID
<20170516075418.m3knwvdsr5ab6vzy@sigill.intra.peff.net>
In-Reply-To
<cover.1494861793.git.johannes.schindelin@gmx.de>
On Mon, May 15, 2017 at 05:23:44PM +0200, Johannes Schindelin wrote:
Show 15 quoted lines
> Granted, it is a bit of a less common use case to call
> 
> 	git diff <commit1>:<file1> <commit2>:<file2>
> 
> There are valid scenarios for such calls, of course.
> 
> And sometimes, one may want to compare even files that are stored in
> subdirectories instead of the top-level directory. Example:
> 
> 	git diff cafebabe:t/README bee7e55:t/README
> 
> Now, the surprising part is that Git tries to read a .gitattributes
> files interpreting the *entire* prefix `cafebabe:t/` as a *directory
> path*. I.e. it will try to read the *file* (not the blob)
> `cafebabe:t/.gitattributes`.
You also get a very silly-looking diff header:
  $ git diff HEAD:Makefile HEAD~100:Makefile
  diff --git a/HEAD:Makefile b/HEAD~100:Makefile
  index e35542e63..9b36068ac 100644
  --- a/HEAD:Makefile
  +++ b/HEAD~100:Makefile
  [...]

I think the problem is that diff's blob-compare should be feeding the actual path to the diff machinery, rather than the original name. True, that carries less information, but it matches the other cases. For instance:

  git diff HEAD~100:Makefile Makefile

will diff a blob and a file, and uses the file's path (so "HEAD~100" does not appear at all in the output).

Likewise, doing:
  git diff HEAD~100 HEAD -- Makefile

will show you the diff over "Makefile", without bothering to mention the commit endpoints.

So I think we should just be feeding "Makefile" to the diff code even for the 2-blob case.

Something like the patch below almost gets us there, but it suffers from the fact that the revision machinery doesn't record the path for the blobs it parses. So:

  1. It's a little inefficient, because it has to re-resolve the names
     again.
  2. It works for "git diff $commit:file1 $commit:file2", because each
     of the blobs has a name we can re-resolve. But connecting them with
     "$commit:file1..$commit:file2" doesn't work, because that name is
     given to _both_ blobs, and can't be resolved as a single sha1. I'd
     argue that this is actually a bug in the revision code.

Both would be solved if handle_revision_arg used get_sha1_with_context to record the path while resolving (struct object_entry already has a slot for it, but the revision code never sets it).

-Peff
---
diff --git a/builtin/diff.c b/builtin/diff.c
index d184aafab..e8c541f00 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -406,10 +406,30 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
 			obj->flags |= flags;
 			add_object_array(obj, name, &ent);
 		} else if (obj->type == OBJ_BLOB) {
+			struct object_context oc;
+
 			if (2 <= blobs)
 				die(_("more than two blobs given: '%s'"), name);
-			hashcpy(blob[blobs].oid.hash, obj->oid.hash);
-			blob[blobs].name = name;
+
+			/*
+			 * We already resolved this once as a blob via the
+			 * rev_info machinery, but it did not bother to collect
+			 * any path information. Let's re-resolve to get it.
+			 *
+			 * There are a few corner cases where there is no path
+			 * information (e.g., a tag pointing straight at a
+			 * blob). In that case we'll fall back to just using
+			 * the name the user gave us.
+			 */
+			if (!get_sha1_with_context(entry->name, 0, blob[blobs].oid.hash, &oc) &&
+			    !hashcmp(blob[blobs].oid.hash, obj->oid.hash) &&
+			    oc.path[0]) {
+				blob[blobs].name = xstrdup(oc.path);
+			} else {
+				blob[blobs].name = name;
+				hashcpy(blob[blobs].oid.hash, obj->oid.hash);
+			}
+
 			blob[blobs].mode = entry->mode;
 			blobs++;
 
Previous: Johannes SchindelinNext: Jeff King
Message 4 of 33 in “Demonstrate and partially work around a gitattributes problem”
  1. 0/2 Demonstrate and partially work around a gitattributes problemJohannes Schindelin, May 15, 2017
  2. 1/2 gitattributes: demonstrate that Git tries to read a bogus fileJohannes Schindelin, May 15, 2017
  3. 2/2 mingw: Suppress warning that <commit>:.gitattributes does not existJohannes Schindelin, May 15, 2017
  4. Jeff KingMay 16, 2017
  5. Jeff KingMay 16, 2017
  6. Junio C HamanoMay 17, 2017
  7. Jeff KingMay 17, 2017
  8. Johannes SchindelinMay 18, 2017
  9. Jeff KingMay 19, 2017
  10. 0/15 retain blob info for git diff HEAD:foo HEAD:barJeff King, May 19, 2017
  11. 01/15 handle_revision_arg: reset "dotdot" consistentlyJeff King, May 19, 2017
  12. Philip OakleyMay 20, 2017
  13. Jeff KingMay 23, 2017
  14. Philip OakleyMay 23, 2017
  15. 02/15 handle_revision_arg: simplify commit reference lookupsJeff King, May 19, 2017
  16. 03/15 handle_revision_arg: stop using "dotdot" as a generic pointerJeff King, May 19, 2017
  17. Junio C HamanoMay 24, 2017
  18. Jeff KingMay 24, 2017
  19. 04/15 handle_revision_arg: hoist ".." check out of range parsingJeff King, May 19, 2017
  20. 05/15 handle_revision_arg: add handle_dotdot() helperJeff King, May 19, 2017
  21. Junio C HamanoMay 24, 2017
  22. 06/15 sha1_name: consistently refer to object_context as "oc"Jeff King, May 19, 2017
  23. 07/15 get_sha1_with_context: always initialize oc->symlink_pathJeff King, May 19, 2017
  24. 08/15 get_sha1_with_context: dynamically allocate oc->pathJeff King, May 19, 2017
  25. 09/15 t4063: add tests of direct blob diffsJeff King, May 19, 2017
  26. 10/15 handle_revision_arg: record modes for "a..b" endpointsJeff King, May 19, 2017
  27. 11/15 handle_revision_arg: record paths for pending objectsJeff King, May 19, 2017
  28. 12/15 diff: pass whole pending entry in blobinfoJeff King, May 19, 2017
  29. 13/15 diff: use the word "path" instead of "name" for blobsJeff King, May 19, 2017
  30. 14/15 diff: use pending "path" if it is availableJeff King, May 19, 2017
  31. 15/15 diff: use blob path for blob/file diffsJeff King, May 19, 2017
  32. Junio C HamanoMay 24, 2017
  33. Jeff KingMay 24, 2017

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.