path: root/builtin
diff options
authorJeff King <>2012-07-28 15:03:01 (GMT)
committerJunio C Hamano <>2012-07-29 22:04:32 (GMT)
commite54501004abbd20fa8d813c1e5b82c3b50bb9361 (patch)
tree8d15f023e4b6488b6a6fd229dc27bc25de877763 /builtin
parentd0f1ea6003d97e63110fa7d50bb07f546a909b6e (diff)
diff: do not use null sha1 as a sentinel value
The diff code represents paths using the diff_filespec struct. This struct has a sha1 to represent the sha1 of the content at that path, as well as a sha1_valid member which indicates whether its sha1 field is actually useful. If sha1_valid is not true, then the filespec represents a working tree file (e.g., for the no-index case, or for when the index is not up-to-date). The diff_filespec is only used internally, though. At the interfaces to the diff subsystem, callers feed the sha1 directly, and we create a diff_filespec from it. It's at that point that we look at the sha1 and decide whether it is valid or not; callers may pass the null sha1 as a sentinel value to indicate that it is not. We should not typically see the null sha1 coming from any other source (e.g., in the index itself, or from a tree). However, a corrupt tree might have a null sha1, which would cause "diff --patch" to accidentally diff the working tree version of a file instead of treating it as a blob. This patch extends the edges of the diff interface to accept a "sha1_valid" flag whenever we accept a sha1, and to use that flag when creating a filespec. In some cases, this means passing the flag through several layers, making the code change larger than would be desirable. One alternative would be to simply die() upon seeing corrupted trees with null sha1s. However, this fix more directly addresses the problem (while bogus sha1s in a tree are probably a bad thing, it is really the sentinel confusion sending us down the wrong code path that is what makes it devastating). And it means that git is more capable of examining and debugging these corrupted trees. For example, you can still "diff --raw" such a tree to find out when the bogus entry was introduced; you just cannot do a "--patch" diff (just as you could not with any other corrupted tree, as we do not have any content to diff). Signed-off-by: Jeff King <> Signed-off-by: Junio C Hamano <>
Diffstat (limited to 'builtin')
3 files changed, 12 insertions, 7 deletions
diff --git a/builtin/blame.c b/builtin/blame.c
index 5a67c20..fac0e93 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -96,6 +96,7 @@ struct origin {
int textconv_object(const char *path,
unsigned mode,
const unsigned char *sha1,
+ int sha1_valid,
char **buf,
unsigned long *buf_size)
@@ -103,7 +104,7 @@ int textconv_object(const char *path,
struct userdiff_driver *textconv;
df = alloc_filespec(path);
- fill_filespec(df, sha1, mode);
+ fill_filespec(df, sha1, sha1_valid, mode);
textconv = get_textconv(df);
if (!textconv) {
@@ -128,7 +129,7 @@ static void fill_origin_blob(struct diff_options *opt,
- textconv_object(o->path, o->mode, o->blob_sha1, &file->ptr, &file_size))
+ textconv_object(o->path, o->mode, o->blob_sha1, 1, &file->ptr, &file_size))
file->ptr = read_sha1_file(o->blob_sha1, &type, &file_size);
@@ -2114,7 +2115,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
switch (st.st_mode & S_IFMT) {
case S_IFREG:
- textconv_object(read_from, mode, null_sha1, &buf_ptr, &buf_len))
+ textconv_object(read_from, mode, null_sha1, 0, &buf_ptr, &buf_len))
strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
die_errno("cannot open or read '%s'", read_from);
@@ -2506,7 +2507,7 @@ parse_done:
die("no such path %s in %s", path, final_commit_name);
if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
- textconv_object(path, o->mode, o->blob_sha1, (char **) &sb.final_buf,
+ textconv_object(path, o->mode, o->blob_sha1, 1, (char **) &sb.final_buf,
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index 07bd984..72205fa 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -143,7 +143,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
die("git cat-file --textconv %s: <object> must be <sha1:path>",
- if (!textconv_object(obj_context.path, obj_context.mode, sha1, &buf, &size))
+ if (!textconv_object(obj_context.path, obj_context.mode, sha1, 1, &buf, &size))
die("git cat-file --textconv: unable to run textconv on %s",
diff --git a/builtin/diff.c b/builtin/diff.c
index 387afa7..ac2b1cc 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -29,6 +29,8 @@ static void stuff_change(struct diff_options *opt,
unsigned old_mode, unsigned new_mode,
const unsigned char *old_sha1,
const unsigned char *new_sha1,
+ int old_sha1_valid,
+ int new_sha1_valid,
const char *old_name,
const char *new_name)
@@ -54,8 +56,8 @@ static void stuff_change(struct diff_options *opt,
one = alloc_filespec(old_name);
two = alloc_filespec(new_name);
- fill_filespec(one, old_sha1, old_mode);
- fill_filespec(two, new_sha1, new_mode);
+ fill_filespec(one, old_sha1, old_sha1_valid, old_mode);
+ fill_filespec(two, new_sha1, new_sha1_valid, new_mode);
diff_queue(&diff_queued_diff, one, two);
@@ -84,6 +86,7 @@ static int builtin_diff_b_f(struct rev_info *revs,
blob[0].mode, canon_mode(st.st_mode),
blob[0].sha1, null_sha1,
+ 1, 0,
path, path);
@@ -108,6 +111,7 @@ static int builtin_diff_blobs(struct rev_info *revs,
blob[0].mode, blob[1].mode,
blob[0].sha1, blob[1].sha1,
+ 1, 1,
blob[0].name, blob[1].name);