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

Re: More precise tag following

From
Junio C Hamano <junkio@cox.net>
Date
Jan 30, 2007, 09:22 UTC
Message-ID
<7vodog3m3f.fsf@assigned-by-dhcp.cox.net>
In-Reply-To
<Pine.LNX.4.64.0701281143190.25027@woody.linux-foundation.org>
Linus Torvalds <torvalds@linux-foundation.org> writes:
> One thing I looked at, which *should* be easy to do inside "git-blame", is 
> to make the case where you do *not* give a head to start with, default to 
> "current working tree" instead of HEAD.

This is still very rough; the existing diff frontends are mess and making diff-cache and diff-tree behave more or less interchangeably is quite a pain. I am not proud of the new do_diff_cache() interface I had to add, which is probably totally useless for anybody other than the three calling sites this patch has.

I tested only the most trivial case that exercises the do_diff_cache() cal in find_origin() before I got too tired, and I am retiring to bed now.

-- >8 -- [PATCH] git-blame: no rev means start from the working tree file.

Warning: this changes the semantics.

This is a WIP to make "git blame" without any positive rev to start digging from the working tree copy, which is made into a fake commit whose sole parent is the HEAD.

It might make sense to give "git-blame --cached" to start digging from the index as well, which should be trivial.

The calls to do_diff_cache() in find_copy_in_parent() and find_rename() need to be vetted, as I haven't checked them yet.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 builtin-blame.c |  119 ++++++++++++++++++++++++++++++++++++++++++++----------
 cache.h         |    1 +
 diff-lib.c      |   22 ++++++++++-
 diff.h          |    1 +
 ident.c         |    8 ++--
 5 files changed, 124 insertions(+), 27 deletions(-)
diff --git a/builtin-blame.c b/builtin-blame.c
index 3033e9b..a8668c0 100644
--- a/builtin-blame.c
+++ b/builtin-blame.c
@@ -333,9 +333,13 @@ static struct origin *find_origin(struct scoreboard *sb,
 	diff_tree_setup_paths(paths, &diff_opts);
 	if (diff_setup_done(&diff_opts) < 0)
 		die("diff-setup");
-	diff_tree_sha1(parent->tree->object.sha1,
-		       origin->commit->tree->object.sha1,
-		       "", &diff_opts);
+
+	if (is_null_sha1(origin->commit->object.sha1))
+		do_diff_cache(parent->tree->object.sha1, &diff_opts, 0);
+	else
+		diff_tree_sha1(parent->tree->object.sha1,
+			       origin->commit->tree->object.sha1,
+			       "", &diff_opts);
 	diffcore_std(&diff_opts);
 
 	/* It is either one entry that says "modified", or "created",
@@ -402,9 +406,13 @@ static struct origin *find_rename(struct scoreboard *sb,
 	diff_tree_setup_paths(paths, &diff_opts);
 	if (diff_setup_done(&diff_opts) < 0)
 		die("diff-setup");
-	diff_tree_sha1(parent->tree->object.sha1,
-		       origin->commit->tree->object.sha1,
-		       "", &diff_opts);
+
+	if (is_null_sha1(origin->commit->object.sha1))
+		do_diff_cache(parent->tree->object.sha1, &diff_opts, 0);
+	else
+		diff_tree_sha1(parent->tree->object.sha1,
+			       origin->commit->tree->object.sha1,
+			       "", &diff_opts);
 	diffcore_std(&diff_opts);
 
 	for (i = 0; i < diff_queued_diff.nr; i++) {
@@ -1047,9 +1055,12 @@ static int find_copy_in_parent(struct scoreboard *sb,
 	    (!porigin || strcmp(target->path, porigin->path)))
 		diff_opts.find_copies_harder = 1;
 
-	diff_tree_sha1(parent->tree->object.sha1,
-		       target->commit->tree->object.sha1,
-		       "", &diff_opts);
+	if (is_null_sha1(target->commit->object.sha1))
+		do_diff_cache(parent->tree->object.sha1, &diff_opts, 0);
+	else
+		diff_tree_sha1(parent->tree->object.sha1,
+			       target->commit->tree->object.sha1,
+			       "", &diff_opts);
 
 	if (!diff_opts.find_copies_harder)
 		diffcore_std(&diff_opts);
@@ -1910,6 +1921,64 @@ static int git_blame_config(const char *var, const char *value)
 	return git_default_config(var, value);
 }
 
+static struct commit *fake_working_tree_commit(const char *path)
+{
+	struct stat st;
+	struct commit *commit;
+	struct origin *origin;
+	unsigned char head_sha1[20];
+	char *buf;
+	const char *ident;
+	int fd;
+
+	if (lstat(path, &st) < 0)
+		die("Cannot lstat %s", path);
+	if (get_sha1("HEAD", head_sha1))
+		die("No such ref: HEAD");
+
+	commit = xcalloc(1, sizeof(*commit));
+	commit->parents = xcalloc(1, sizeof(*commit->parents));
+	commit->parents->item = lookup_commit_reference(head_sha1);
+	commit->object.parsed = 1;
+	commit->date = st.st_mtime;
+	commit->object.type = OBJ_COMMIT;
+
+	origin = make_origin(commit, path);
+	origin->file.ptr = buf = xmalloc(st.st_size+1);
+	origin->file.size = st.st_size;
+	buf[st.st_size] = 0;
+
+	switch (st.st_mode & S_IFMT) {
+	case S_IFREG:
+		fd = open(path, O_RDONLY);
+		if (fd < 0)
+			die("cannot open %s", path);
+		if (read_in_full(fd, buf, st.st_size) != st.st_size)
+			die("cannot read %s", path);
+		break;
+	case S_IFLNK:
+		if (readlink(path, buf, st.st_size+1) != st.st_size)
+			die("cannot readlink %s", path);
+		break;
+	default:
+		die("unsupported file type %s", path);
+	}
+	hash_sha1_file(buf, st.st_size, blob_type, origin->blob_sha1);
+	commit->util = origin;
+
+	commit->buffer = xmalloc(400);
+	ident = fmt_ident("Not Committed Yet", "not.committed.yet", NULL, 0);
+	sprintf(commit->buffer,
+		"tree 0000000000000000000000000000000000000000\n"
+		"parent %s\n"
+		"author %s\n"
+		"committer %s\n\n"
+		"Version of %s from the working tree",
+		sha1_to_hex(head_sha1),
+		ident, ident, path);
+	return commit;
+}
+
 int cmd_blame(int argc, const char **argv, const char *prefix)
 {
 	struct rev_info revs;
@@ -2087,7 +2156,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 	argv[unk] = NULL;
 
 	init_revisions(&revs, NULL);
-	setup_revisions(unk, argv, &revs, "HEAD");
+	setup_revisions(unk, argv, &revs, NULL);
 	memset(&sb, 0, sizeof(sb));
 
 	/*
@@ -2114,15 +2183,10 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 	if (!sb.final) {
 		/*
 		 * "--not A B -- path" without anything positive;
-		 * default to HEAD.
+		 * do not default to HEAD, but use the cache.
 		 */
-		unsigned char head_sha1[20];
-
-		final_commit_name = "HEAD";
-		if (get_sha1(final_commit_name, head_sha1))
-			die("No such ref: HEAD");
-		sb.final = lookup_commit_reference(head_sha1);
-		add_pending_object(&revs, &(sb.final->object), "HEAD");
+		sb.final = fake_working_tree_commit(path);
+		add_pending_object(&revs, &(sb.final->object), ":");
 	}
 
 	/*
@@ -2132,11 +2196,22 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 	 */
 	prepare_revision_walk(&revs);
 
-	o = get_origin(&sb, sb.final, path);
-	if (fill_blob_sha1(o))
-		die("no such path %s in %s", path, final_commit_name);
+	if (is_null_sha1(sb.final->object.sha1)) {
+		char *buf;
+		o = sb.final->util;
+		buf = xmalloc(o->file.size + 1);
+		memcpy(buf, o->file.ptr, o->file.size + 1);
+		sb.final_buf = buf;
+		sb.final_buf_size = o->file.size;
+	}
+	else {
+		o = get_origin(&sb, sb.final, path);
+		if (fill_blob_sha1(o))
+			die("no such path %s in %s", path, final_commit_name);
 
-	sb.final_buf = read_sha1_file(o->blob_sha1, type, &sb.final_buf_size);
+		sb.final_buf = read_sha1_file(o->blob_sha1, type,
+					      &sb.final_buf_size);
+	}
 	num_read_blob++;
 	lno = prepare_lines(&sb);
 
diff --git a/cache.h b/cache.h
index 9873ee9..dcceea4 100644
--- a/cache.h
+++ b/cache.h
@@ -321,6 +321,7 @@ unsigned long approxidate(const char *);
 
 extern const char *git_author_info(int);
 extern const char *git_committer_info(int);
+extern const char *fmt_ident(const char *name, const char *email, const char *date_str, int);
 
 struct checkout {
 	const char *base_dir;
diff --git a/diff-lib.c b/diff-lib.c
index 2c9be60..b93f7a3 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -271,7 +271,7 @@ static int diff_cache(struct rev_info *revs,
 				break;
 			}
 			/* Show difference between old and new */
-			show_modified(revs,ac[1], ce, 1,
+			show_modified(revs, ac[1], ce, 1,
 				      cached, match_missing);
 			break;
 		case 1:
@@ -372,3 +372,23 @@ int run_diff_index(struct rev_info *revs, int cached)
 	diff_flush(&revs->diffopt);
 	return ret;
 }
+
+int do_diff_cache(const unsigned char *tree_sha1, struct diff_options *opt, int cached)
+{
+	struct tree *tree;
+	struct rev_info revs;
+
+	init_revisions(&revs, NULL);
+	revs.prune_data = opt->paths;
+	discard_cache();
+	if (read_cache() < 0)
+		die("cannot read index");
+	mark_merge_entries();
+	tree = parse_tree_indirect(tree_sha1);
+	if (!tree)
+		die("bad tree object %s", sha1_to_hex(tree_sha1));
+	if (read_tree(tree, 1, opt->paths))
+		return error("unable to read tree %s", sha1_to_hex(tree_sha1));
+	return diff_cache(&revs, active_cache, active_nr, revs.prune_data,
+			  cached, 0);
+}
diff --git a/diff.h b/diff.h
index 7a347cf..dd180b8 100644
--- a/diff.h
+++ b/diff.h
@@ -222,6 +222,7 @@ extern int run_diff_files(struct rev_info *revs, int silent_on_removed);
 
 extern int run_diff_index(struct rev_info *revs, int cached);
 
+extern int do_diff_cache(const unsigned char *, struct diff_options *, int);
 extern int diff_flush_patch_id(struct diff_options *, unsigned char *);
 
 #endif /* DIFF_H */
diff --git a/ident.c b/ident.c
index a6fc7b5..bb03bdd 100644
--- a/ident.c
+++ b/ident.c
@@ -185,8 +185,8 @@ static const char *env_hint =
 "Add --global to set your account\'s default\n"
 "\n";
 
-static const char *get_ident(const char *name, const char *email,
-			     const char *date_str, int error_on_no_name)
+const char *fmt_ident(const char *name, const char *email,
+		      const char *date_str, int error_on_no_name)
 {
 	static char buffer[1000];
 	char date[50];
@@ -233,7 +233,7 @@ static const char *get_ident(const char *name, const char *email,
 
 const char *git_author_info(int error_on_no_name)
 {
-	return get_ident(getenv("GIT_AUTHOR_NAME"),
+	return fmt_ident(getenv("GIT_AUTHOR_NAME"),
 			 getenv("GIT_AUTHOR_EMAIL"),
 			 getenv("GIT_AUTHOR_DATE"),
 			 error_on_no_name);
@@ -241,7 +241,7 @@ const char *git_author_info(int error_on_no_name)
 
 const char *git_committer_info(int error_on_no_name)
 {
-	return get_ident(getenv("GIT_COMMITTER_NAME"),
+	return fmt_ident(getenv("GIT_COMMITTER_NAME"),
 			 getenv("GIT_COMMITTER_EMAIL"),
 			 getenv("GIT_COMMITTER_DATE"),
 			 error_on_no_name);
-- 
1.5.0.rc2.77.g1732a
Previous: Jeff KingNext: Shawn O. Pearce
Message 42 of 92 in “More precise tag following”
  1. Junio C HamanoJan 26, 2007
  2. Junio C HamanoJan 26, 2007
  3. Shawn O. PearceJan 27, 2007
  4. Junio C HamanoJan 27, 2007
  5. Jeff KingJan 27, 2007
  6. Nicolas PitreJan 27, 2007
  7. Simon 'corecode' SchubertJan 27, 2007
  8. Johannes SchindelinJan 27, 2007
  9. Simon 'corecode' SchubertJan 27, 2007
  10. Jakub NarebskiJan 27, 2007
  11. Linus TorvaldsJan 27, 2007
  12. Johannes SchindelinJan 27, 2007
  13. Simon 'corecode' SchubertJan 27, 2007
  14. Johannes SchindelinJan 27, 2007
  15. Simon 'corecode' SchubertJan 27, 2007
  16. Nicolas PitreJan 27, 2007
  17. Linus TorvaldsJan 27, 2007
  18. Linus TorvaldsJan 27, 2007
  19. Junio C HamanoJan 27, 2007
  20. Linus TorvaldsJan 27, 2007
  21. Junio C HamanoJan 28, 2007
  22. git-blame --porcelain: quote filename in c-style when needed.Junio C Hamano, Jan 28, 2007
  23. git-blame --incremental: don't use pagerRené Scharfe, Jan 28, 2007
  24. Junio C HamanoJan 28, 2007
  25. Junio C HamanoJan 28, 2007
  26. René ScharfeJan 29, 2007
  27. git blame --progressJunio C Hamano, Jan 29, 2007
  28. Simon 'corecode' SchubertJan 29, 2007
  29. Alex RiesenJan 29, 2007
  30. Matthias LederhoferJan 29, 2007
  31. Junio C HamanoJan 29, 2007
  32. René ScharfeJan 29, 2007
  33. Linus TorvaldsJan 29, 2007
  34. Junio C HamanoJan 30, 2007
  35. Linus TorvaldsJan 28, 2007
  36. Junio C HamanoJan 28, 2007
  37. Linus TorvaldsJan 28, 2007
  38. Junio C HamanoJan 28, 2007
  39. document 'blame --incremental'Junio C Hamano, Jan 28, 2007
  40. Junio C HamanoJan 28, 2007
  41. Jeff KingJan 28, 2007
  42. Junio C HamanoJan 30, 2007
  43. Shawn O. PearceJan 30, 2007
  44. Linus TorvaldsJan 30, 2007
  45. Junio C HamanoJan 28, 2007
  46. Shawn O. PearceJan 29, 2007
  47. Junio C HamanoJan 29, 2007
  48. Shawn O. PearceJan 29, 2007
  49. Linus TorvaldsJan 29, 2007
  50. Simon 'corecode' SchubertJan 29, 2007
  51. Theodore TsoJan 29, 2007
  52. Linus TorvaldsJan 29, 2007
  53. Jakub NarebskiJan 29, 2007
  54. Shawn O. PearceJan 29, 2007
  55. Jakub NarebskiJan 29, 2007
  56. Shawn O. PearceFeb 9, 2007
  57. David KågedalJan 31, 2007
  58. David KågedalJan 31, 2007
  59. Peter EriksenJan 31, 2007
  60. David KågedalJan 31, 2007
  61. Peter EriksenJan 31, 2007
  62. Jakub NarebskiJan 31, 2007
  63. David KågedalJan 31, 2007
  64. Simon 'corecode' SchubertJan 27, 2007
  65. Johannes SchindelinJan 27, 2007
  66. Simon 'corecode' SchubertJan 27, 2007
  67. Johannes SchindelinJan 27, 2007
  68. Jakub NarebskiJan 27, 2007
  69. Linus TorvaldsJan 27, 2007
  70. Linus TorvaldsJan 27, 2007
  71. Jakub NarebskiJan 27, 2007
  72. Linus TorvaldsJan 27, 2007
  73. Chris LeeJan 27, 2007
  74. Theodore TsoJan 28, 2007
  75. Linus TorvaldsJan 28, 2007
  76. David LangJan 28, 2007
  77. Nicolas PitreJan 29, 2007
  78. Linus TorvaldsJan 29, 2007
  79. Nicolas PitreJan 29, 2007
  80. Chris LeeJan 29, 2007
  81. Eric WongJan 29, 2007
  82. Eric WongJan 30, 2007
  83. Eric WongJan 30, 2007
  84. Eric WongJan 30, 2007
  85. Jakub NarebskiJan 27, 2007
  86. Jeff KingJan 27, 2007
  87. Linus TorvaldsJan 27, 2007
  88. Jeff KingJan 27, 2007
  89. Theodore TsoJan 28, 2007
  90. Randal L. SchwartzJan 28, 2007
  91. Jeff KingJan 28, 2007
  92. Shawn O. PearceJan 28, 2007

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.