Skip to content

Commit e545010

Browse files
peffgitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent d0f1ea6 commit e545010

14 files changed

+135
-32
lines changed

builtin.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ void finish_copy_notes_for_rewrite(struct notes_rewrite_cfg *c);
4242

4343
extern int check_pager_config(const char *cmd);
4444

45-
extern int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, char **buf, unsigned long *buf_size);
45+
extern int textconv_object(const char *path, unsigned mode, const unsigned char *sha1, int sha1_valid, char **buf, unsigned long *buf_size);
4646

4747
extern int cmd_add(int argc, const char **argv, const char *prefix);
4848
extern int cmd_annotate(int argc, const char **argv, const char *prefix);

builtin/blame.c

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,14 +96,15 @@ struct origin {
9696
int textconv_object(const char *path,
9797
unsigned mode,
9898
const unsigned char *sha1,
99+
int sha1_valid,
99100
char **buf,
100101
unsigned long *buf_size)
101102
{
102103
struct diff_filespec *df;
103104
struct userdiff_driver *textconv;
104105

105106
df = alloc_filespec(path);
106-
fill_filespec(df, sha1, mode);
107+
fill_filespec(df, sha1, sha1_valid, mode);
107108
textconv = get_textconv(df);
108109
if (!textconv) {
109110
free_filespec(df);
@@ -128,7 +129,7 @@ static void fill_origin_blob(struct diff_options *opt,
128129

129130
num_read_blob++;
130131
if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
131-
textconv_object(o->path, o->mode, o->blob_sha1, &file->ptr, &file_size))
132+
textconv_object(o->path, o->mode, o->blob_sha1, 1, &file->ptr, &file_size))
132133
;
133134
else
134135
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,
21142115
switch (st.st_mode & S_IFMT) {
21152116
case S_IFREG:
21162117
if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV) &&
2117-
textconv_object(read_from, mode, null_sha1, &buf_ptr, &buf_len))
2118+
textconv_object(read_from, mode, null_sha1, 0, &buf_ptr, &buf_len))
21182119
strbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);
21192120
else if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)
21202121
die_errno("cannot open or read '%s'", read_from);
@@ -2506,7 +2507,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
25062507
die("no such path %s in %s", path, final_commit_name);
25072508

25082509
if (DIFF_OPT_TST(&sb.revs->diffopt, ALLOW_TEXTCONV) &&
2509-
textconv_object(path, o->mode, o->blob_sha1, (char **) &sb.final_buf,
2510+
textconv_object(path, o->mode, o->blob_sha1, 1, (char **) &sb.final_buf,
25102511
&sb.final_buf_size))
25112512
;
25122513
else

builtin/cat-file.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,7 @@ static int cat_one_file(int opt, const char *exp_type, const char *obj_name)
143143
die("git cat-file --textconv %s: <object> must be <sha1:path>",
144144
obj_name);
145145

146-
if (!textconv_object(obj_context.path, obj_context.mode, sha1, &buf, &size))
146+
if (!textconv_object(obj_context.path, obj_context.mode, sha1, 1, &buf, &size))
147147
die("git cat-file --textconv: unable to run textconv on %s",
148148
obj_name);
149149
break;

builtin/diff.c

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ static void stuff_change(struct diff_options *opt,
2929
unsigned old_mode, unsigned new_mode,
3030
const unsigned char *old_sha1,
3131
const unsigned char *new_sha1,
32+
int old_sha1_valid,
33+
int new_sha1_valid,
3234
const char *old_name,
3335
const char *new_name)
3436
{
@@ -54,8 +56,8 @@ static void stuff_change(struct diff_options *opt,
5456

5557
one = alloc_filespec(old_name);
5658
two = alloc_filespec(new_name);
57-
fill_filespec(one, old_sha1, old_mode);
58-
fill_filespec(two, new_sha1, new_mode);
59+
fill_filespec(one, old_sha1, old_sha1_valid, old_mode);
60+
fill_filespec(two, new_sha1, new_sha1_valid, new_mode);
5961

6062
diff_queue(&diff_queued_diff, one, two);
6163
}
@@ -84,6 +86,7 @@ static int builtin_diff_b_f(struct rev_info *revs,
8486
stuff_change(&revs->diffopt,
8587
blob[0].mode, canon_mode(st.st_mode),
8688
blob[0].sha1, null_sha1,
89+
1, 0,
8790
path, path);
8891
diffcore_std(&revs->diffopt);
8992
diff_flush(&revs->diffopt);
@@ -108,6 +111,7 @@ static int builtin_diff_blobs(struct rev_info *revs,
108111
stuff_change(&revs->diffopt,
109112
blob[0].mode, blob[1].mode,
110113
blob[0].sha1, blob[1].sha1,
114+
1, 1,
111115
blob[0].name, blob[1].name);
112116
diffcore_std(&revs->diffopt);
113117
diff_flush(&revs->diffopt);

combine-diff.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ static char *grab_blob(const unsigned char *sha1, unsigned int mode,
111111
return xcalloc(1, 1);
112112
} else if (textconv) {
113113
struct diff_filespec *df = alloc_filespec(path);
114-
fill_filespec(df, sha1, mode);
114+
fill_filespec(df, sha1, 1, mode);
115115
*size = fill_textconv(textconv, df, &blob);
116116
free_filespec(df);
117117
} else {
@@ -823,7 +823,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
823823
&result_size, NULL, NULL);
824824
} else if (textconv) {
825825
struct diff_filespec *df = alloc_filespec(elem->path);
826-
fill_filespec(df, null_sha1, st.st_mode);
826+
fill_filespec(df, null_sha1, 0, st.st_mode);
827827
result_size = fill_textconv(textconv, df, &result);
828828
free_filespec(df);
829829
} else if (0 <= (fd = open(elem->path, O_RDONLY))) {

diff-lib.c

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,8 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
206206
if (silent_on_removed)
207207
continue;
208208
diff_addremove(&revs->diffopt, '-', ce->ce_mode,
209-
ce->sha1, ce->name, 0);
209+
ce->sha1, !is_null_sha1(ce->sha1),
210+
ce->name, 0);
210211
continue;
211212
}
212213
changed = match_stat_with_submodule(&revs->diffopt, ce, &st,
@@ -220,6 +221,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
220221
newmode = ce_mode_from_stat(ce, st.st_mode);
221222
diff_change(&revs->diffopt, oldmode, newmode,
222223
ce->sha1, (changed ? null_sha1 : ce->sha1),
224+
!is_null_sha1(ce->sha1), (changed ? 0 : !is_null_sha1(ce->sha1)),
223225
ce->name, 0, dirty_submodule);
224226

225227
}
@@ -236,11 +238,12 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
236238
static void diff_index_show_file(struct rev_info *revs,
237239
const char *prefix,
238240
struct cache_entry *ce,
239-
const unsigned char *sha1, unsigned int mode,
241+
const unsigned char *sha1, int sha1_valid,
242+
unsigned int mode,
240243
unsigned dirty_submodule)
241244
{
242245
diff_addremove(&revs->diffopt, prefix[0], mode,
243-
sha1, ce->name, dirty_submodule);
246+
sha1, sha1_valid, ce->name, dirty_submodule);
244247
}
245248

246249
static int get_stat_data(struct cache_entry *ce,
@@ -295,7 +298,7 @@ static void show_new_file(struct rev_info *revs,
295298
&dirty_submodule, &revs->diffopt) < 0)
296299
return;
297300

298-
diff_index_show_file(revs, "+", new, sha1, mode, dirty_submodule);
301+
diff_index_show_file(revs, "+", new, sha1, !is_null_sha1(sha1), mode, dirty_submodule);
299302
}
300303

301304
static int show_modified(struct rev_info *revs,
@@ -312,7 +315,7 @@ static int show_modified(struct rev_info *revs,
312315
&dirty_submodule, &revs->diffopt) < 0) {
313316
if (report_missing)
314317
diff_index_show_file(revs, "-", old,
315-
old->sha1, old->ce_mode, 0);
318+
old->sha1, 1, old->ce_mode, 0);
316319
return -1;
317320
}
318321

@@ -347,7 +350,8 @@ static int show_modified(struct rev_info *revs,
347350
return 0;
348351

349352
diff_change(&revs->diffopt, oldmode, mode,
350-
old->sha1, sha1, old->name, 0, dirty_submodule);
353+
old->sha1, sha1, 1, !is_null_sha1(sha1),
354+
old->name, 0, dirty_submodule);
351355
return 0;
352356
}
353357

@@ -380,7 +384,7 @@ static void do_oneway_diff(struct unpack_trees_options *o,
380384
struct diff_filepair *pair;
381385
pair = diff_unmerge(&revs->diffopt, idx->name);
382386
if (tree)
383-
fill_filespec(pair->one, tree->sha1, tree->ce_mode);
387+
fill_filespec(pair->one, tree->sha1, 1, tree->ce_mode);
384388
return;
385389
}
386390

@@ -396,7 +400,7 @@ static void do_oneway_diff(struct unpack_trees_options *o,
396400
* Something removed from the tree?
397401
*/
398402
if (!idx) {
399-
diff_index_show_file(revs, "-", tree, tree->sha1, tree->ce_mode, 0);
403+
diff_index_show_file(revs, "-", tree, tree->sha1, 1, tree->ce_mode, 0);
400404
return;
401405
}
402406

diff-no-index.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,8 +141,8 @@ static int queue_diff(struct diff_options *o,
141141
name2 = "/dev/null";
142142
d1 = alloc_filespec(name1);
143143
d2 = alloc_filespec(name2);
144-
fill_filespec(d1, null_sha1, mode1);
145-
fill_filespec(d2, null_sha1, mode2);
144+
fill_filespec(d1, null_sha1, 0, mode1);
145+
fill_filespec(d2, null_sha1, 0, mode2);
146146

147147
diff_queue(&diff_queued_diff, d1, d2);
148148
return 0;

diff.c

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2443,12 +2443,12 @@ void free_filespec(struct diff_filespec *spec)
24432443
}
24442444

24452445
void fill_filespec(struct diff_filespec *spec, const unsigned char *sha1,
2446-
unsigned short mode)
2446+
int sha1_valid, unsigned short mode)
24472447
{
24482448
if (mode) {
24492449
spec->mode = canon_mode(mode);
24502450
hashcpy(spec->sha1, sha1);
2451-
spec->sha1_valid = !is_null_sha1(sha1);
2451+
spec->sha1_valid = sha1_valid;
24522452
}
24532453
}
24542454

@@ -4589,6 +4589,7 @@ static int is_submodule_ignored(const char *path, struct diff_options *options)
45894589
void diff_addremove(struct diff_options *options,
45904590
int addremove, unsigned mode,
45914591
const unsigned char *sha1,
4592+
int sha1_valid,
45924593
const char *concatpath, unsigned dirty_submodule)
45934594
{
45944595
struct diff_filespec *one, *two;
@@ -4620,9 +4621,9 @@ void diff_addremove(struct diff_options *options,
46204621
two = alloc_filespec(concatpath);
46214622

46224623
if (addremove != '+')
4623-
fill_filespec(one, sha1, mode);
4624+
fill_filespec(one, sha1, sha1_valid, mode);
46244625
if (addremove != '-') {
4625-
fill_filespec(two, sha1, mode);
4626+
fill_filespec(two, sha1, sha1_valid, mode);
46264627
two->dirty_submodule = dirty_submodule;
46274628
}
46284629

@@ -4635,6 +4636,7 @@ void diff_change(struct diff_options *options,
46354636
unsigned old_mode, unsigned new_mode,
46364637
const unsigned char *old_sha1,
46374638
const unsigned char *new_sha1,
4639+
int old_sha1_valid, int new_sha1_valid,
46384640
const char *concatpath,
46394641
unsigned old_dirty_submodule, unsigned new_dirty_submodule)
46404642
{
@@ -4649,6 +4651,8 @@ void diff_change(struct diff_options *options,
46494651
const unsigned char *tmp_c;
46504652
tmp = old_mode; old_mode = new_mode; new_mode = tmp;
46514653
tmp_c = old_sha1; old_sha1 = new_sha1; new_sha1 = tmp_c;
4654+
tmp = old_sha1_valid; old_sha1_valid = new_sha1_valid;
4655+
new_sha1_valid = tmp;
46524656
tmp = old_dirty_submodule; old_dirty_submodule = new_dirty_submodule;
46534657
new_dirty_submodule = tmp;
46544658
}
@@ -4659,8 +4663,8 @@ void diff_change(struct diff_options *options,
46594663

46604664
one = alloc_filespec(concatpath);
46614665
two = alloc_filespec(concatpath);
4662-
fill_filespec(one, old_sha1, old_mode);
4663-
fill_filespec(two, new_sha1, new_mode);
4666+
fill_filespec(one, old_sha1, old_sha1_valid, old_mode);
4667+
fill_filespec(two, new_sha1, new_sha1_valid, new_mode);
46644668
one->dirty_submodule = old_dirty_submodule;
46654669
two->dirty_submodule = new_dirty_submodule;
46664670

diff.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,14 @@ typedef void (*change_fn_t)(struct diff_options *options,
1919
unsigned old_mode, unsigned new_mode,
2020
const unsigned char *old_sha1,
2121
const unsigned char *new_sha1,
22+
int old_sha1_valid, int new_sha1_valid,
2223
const char *fullpath,
2324
unsigned old_dirty_submodule, unsigned new_dirty_submodule);
2425

2526
typedef void (*add_remove_fn_t)(struct diff_options *options,
2627
int addremove, unsigned mode,
2728
const unsigned char *sha1,
29+
int sha1_valid,
2830
const char *fullpath, unsigned dirty_submodule);
2931

3032
typedef void (*diff_format_fn_t)(struct diff_queue_struct *q,
@@ -209,12 +211,15 @@ extern void diff_addremove(struct diff_options *,
209211
int addremove,
210212
unsigned mode,
211213
const unsigned char *sha1,
214+
int sha1_valid,
212215
const char *fullpath, unsigned dirty_submodule);
213216

214217
extern void diff_change(struct diff_options *,
215218
unsigned mode1, unsigned mode2,
216219
const unsigned char *sha1,
217220
const unsigned char *sha2,
221+
int sha1_valid,
222+
int sha2_valid,
218223
const char *fullpath,
219224
unsigned dirty_submodule1, unsigned dirty_submodule2);
220225

diffcore-rename.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ static struct diff_rename_dst *locate_rename_dst(struct diff_filespec *two,
4848
memmove(rename_dst + first + 1, rename_dst + first,
4949
(rename_dst_nr - first - 1) * sizeof(*rename_dst));
5050
rename_dst[first].two = alloc_filespec(two->path);
51-
fill_filespec(rename_dst[first].two, two->sha1, two->mode);
51+
fill_filespec(rename_dst[first].two, two->sha1, two->sha1_valid, two->mode);
5252
rename_dst[first].pair = NULL;
5353
return &(rename_dst[first]);
5454
}

0 commit comments

Comments
 (0)