commit-graph: return with errors during write

The write_commit_graph() method uses die() to report failure and
exit when confronted with an unexpected condition. This use of
die() in a library function is incorrect and is now replaced by
error() statements and an int return type. Return zero on success
and a negative value on failure.

Now that we use 'goto cleanup' to jump to the terminal condition
on an error, we have new paths that could lead to uninitialized
values. New initializers are added to correct for this.

The builtins 'commit-graph', 'gc', and 'commit' call these methods,
so update them to check the return value. Test that 'git commit-graph
write' returns a proper error code when hitting a failure condition
in write_commit_graph().

Signed-off-by: Derrick Stolee <dstolee@microsoft.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Derrick Stolee 2019-06-12 06:29:37 -07:00 committed by Junio C Hamano
parent c7944050af
commit e103f7276f
6 changed files with 77 additions and 39 deletions

View File

@ -141,6 +141,7 @@ static int graph_write(int argc, const char **argv)
struct string_list *pack_indexes = NULL; struct string_list *pack_indexes = NULL;
struct string_list *commit_hex = NULL; struct string_list *commit_hex = NULL;
struct string_list lines; struct string_list lines;
int result = 0;
static struct option builtin_commit_graph_write_options[] = { static struct option builtin_commit_graph_write_options[] = {
OPT_STRING(0, "object-dir", &opts.obj_dir, OPT_STRING(0, "object-dir", &opts.obj_dir,
@ -168,10 +169,8 @@ static int graph_write(int argc, const char **argv)
read_replace_refs = 0; read_replace_refs = 0;
if (opts.reachable) { if (opts.reachable)
write_commit_graph_reachable(opts.obj_dir, opts.append, 1); return write_commit_graph_reachable(opts.obj_dir, opts.append, 1);
return 0;
}
string_list_init(&lines, 0); string_list_init(&lines, 0);
if (opts.stdin_packs || opts.stdin_commits) { if (opts.stdin_packs || opts.stdin_commits) {
@ -188,14 +187,15 @@ static int graph_write(int argc, const char **argv)
UNLEAK(buf); UNLEAK(buf);
} }
write_commit_graph(opts.obj_dir, if (write_commit_graph(opts.obj_dir,
pack_indexes, pack_indexes,
commit_hex, commit_hex,
opts.append, opts.append,
1); 1))
result = 1;
UNLEAK(lines); UNLEAK(lines);
return 0; return result;
} }
int cmd_commit_graph(int argc, const char **argv, const char *prefix) int cmd_commit_graph(int argc, const char **argv, const char *prefix)

View File

@ -1669,8 +1669,9 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
"new_index file. Check that disk is not full and quota is\n" "new_index file. Check that disk is not full and quota is\n"
"not exceeded, and then \"git reset HEAD\" to recover.")); "not exceeded, and then \"git reset HEAD\" to recover."));
if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0)) if (git_env_bool(GIT_TEST_COMMIT_GRAPH, 0) &&
write_commit_graph_reachable(get_object_directory(), 0, 0); write_commit_graph_reachable(get_object_directory(), 0, 0))
return 1;
repo_rerere(the_repository, 0); repo_rerere(the_repository, 0);
run_command_v_opt(argv_gc_auto, RUN_GIT_CMD); run_command_v_opt(argv_gc_auto, RUN_GIT_CMD);

View File

@ -664,9 +664,10 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
clean_pack_garbage(); clean_pack_garbage();
} }
if (gc_write_commit_graph) if (gc_write_commit_graph &&
write_commit_graph_reachable(get_object_directory(), 0, write_commit_graph_reachable(get_object_directory(), 0,
!quiet && !daemonized); !quiet && !daemonized))
return 1;
if (auto_gc && too_many_loose_objects()) if (auto_gc && too_many_loose_objects())
warning(_("There are too many unreachable loose objects; " warning(_("There are too many unreachable loose objects; "

View File

@ -851,18 +851,21 @@ static int add_ref_to_list(const char *refname,
return 0; return 0;
} }
void write_commit_graph_reachable(const char *obj_dir, int append, int write_commit_graph_reachable(const char *obj_dir, int append,
int report_progress) int report_progress)
{ {
struct string_list list = STRING_LIST_INIT_DUP; struct string_list list = STRING_LIST_INIT_DUP;
int result;
for_each_ref(add_ref_to_list, &list); for_each_ref(add_ref_to_list, &list);
write_commit_graph(obj_dir, NULL, &list, append, report_progress); result = write_commit_graph(obj_dir, NULL, &list,
append, report_progress);
string_list_clear(&list, 0); string_list_clear(&list, 0);
return result;
} }
void write_commit_graph(const char *obj_dir, int write_commit_graph(const char *obj_dir,
struct string_list *pack_indexes, struct string_list *pack_indexes,
struct string_list *commit_hex, struct string_list *commit_hex,
int append, int report_progress) int append, int report_progress)
@ -871,7 +874,7 @@ void write_commit_graph(const char *obj_dir,
struct packed_commit_list commits; struct packed_commit_list commits;
struct hashfile *f; struct hashfile *f;
uint32_t i, count_distinct = 0; uint32_t i, count_distinct = 0;
char *graph_name; char *graph_name = NULL;
struct lock_file lk = LOCK_INIT; struct lock_file lk = LOCK_INIT;
uint32_t chunk_ids[5]; uint32_t chunk_ids[5];
uint64_t chunk_offsets[5]; uint64_t chunk_offsets[5];
@ -883,15 +886,17 @@ void write_commit_graph(const char *obj_dir,
uint64_t progress_cnt = 0; uint64_t progress_cnt = 0;
struct strbuf progress_title = STRBUF_INIT; struct strbuf progress_title = STRBUF_INIT;
unsigned long approx_nr_objects; unsigned long approx_nr_objects;
int res = 0;
if (!commit_graph_compatible(the_repository)) if (!commit_graph_compatible(the_repository))
return; return 0;
oids.nr = 0; oids.nr = 0;
approx_nr_objects = approximate_object_count(); approx_nr_objects = approximate_object_count();
oids.alloc = approx_nr_objects / 32; oids.alloc = approx_nr_objects / 32;
oids.progress = NULL; oids.progress = NULL;
oids.progress_done = 0; oids.progress_done = 0;
commits.list = NULL;
if (append) { if (append) {
prepare_commit_graph_one(the_repository, obj_dir); prepare_commit_graph_one(the_repository, obj_dir);
@ -932,10 +937,16 @@ void write_commit_graph(const char *obj_dir,
strbuf_setlen(&packname, dirlen); strbuf_setlen(&packname, dirlen);
strbuf_addstr(&packname, pack_indexes->items[i].string); strbuf_addstr(&packname, pack_indexes->items[i].string);
p = add_packed_git(packname.buf, packname.len, 1); p = add_packed_git(packname.buf, packname.len, 1);
if (!p) if (!p) {
die(_("error adding pack %s"), packname.buf); error(_("error adding pack %s"), packname.buf);
if (open_pack_index(p)) res = -1;
die(_("error opening index for %s"), packname.buf); goto cleanup;
}
if (open_pack_index(p)) {
error(_("error opening index for %s"), packname.buf);
res = -1;
goto cleanup;
}
for_each_object_in_pack(p, add_packed_commits, &oids, for_each_object_in_pack(p, add_packed_commits, &oids,
FOR_EACH_OBJECT_PACK_ORDER); FOR_EACH_OBJECT_PACK_ORDER);
close_pack(p); close_pack(p);
@ -1006,8 +1017,11 @@ void write_commit_graph(const char *obj_dir,
} }
stop_progress(&progress); stop_progress(&progress);
if (count_distinct >= GRAPH_EDGE_LAST_MASK) if (count_distinct >= GRAPH_EDGE_LAST_MASK) {
die(_("the commit graph format cannot write %d commits"), count_distinct); error(_("the commit graph format cannot write %d commits"), count_distinct);
res = -1;
goto cleanup;
}
commits.nr = 0; commits.nr = 0;
commits.alloc = count_distinct; commits.alloc = count_distinct;
@ -1039,16 +1053,21 @@ void write_commit_graph(const char *obj_dir,
num_chunks = num_extra_edges ? 4 : 3; num_chunks = num_extra_edges ? 4 : 3;
stop_progress(&progress); stop_progress(&progress);
if (commits.nr >= GRAPH_EDGE_LAST_MASK) if (commits.nr >= GRAPH_EDGE_LAST_MASK) {
die(_("too many commits to write graph")); error(_("too many commits to write graph"));
res = -1;
goto cleanup;
}
compute_generation_numbers(&commits, report_progress); compute_generation_numbers(&commits, report_progress);
graph_name = get_commit_graph_filename(obj_dir); graph_name = get_commit_graph_filename(obj_dir);
if (safe_create_leading_directories(graph_name)) { if (safe_create_leading_directories(graph_name)) {
UNLEAK(graph_name); UNLEAK(graph_name);
die_errno(_("unable to create leading directories of %s"), error(_("unable to create leading directories of %s"),
graph_name); graph_name);
res = -1;
goto cleanup;
} }
hold_lock_file_for_update(&lk, graph_name, LOCK_DIE_ON_ERROR); hold_lock_file_for_update(&lk, graph_name, LOCK_DIE_ON_ERROR);
@ -1107,9 +1126,12 @@ void write_commit_graph(const char *obj_dir,
finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC); finalize_hashfile(f, NULL, CSUM_HASH_IN_STREAM | CSUM_FSYNC);
commit_lock_file(&lk); commit_lock_file(&lk);
cleanup:
free(graph_name); free(graph_name);
free(commits.list); free(commits.list);
free(oids.list); free(oids.list);
return res;
} }
#define VERIFY_COMMIT_GRAPH_ERROR_HASH 2 #define VERIFY_COMMIT_GRAPH_ERROR_HASH 2

View File

@ -65,9 +65,15 @@ struct commit_graph *parse_commit_graph(void *graph_map, int fd,
*/ */
int generation_numbers_enabled(struct repository *r); int generation_numbers_enabled(struct repository *r);
void write_commit_graph_reachable(const char *obj_dir, int append, /*
* The write_commit_graph* methods return zero on success
* and a negative value on failure. Note that if the repository
* is not compatible with the commit-graph feature, then the
* methods will return 0 without writing a commit-graph.
*/
int write_commit_graph_reachable(const char *obj_dir, int append,
int report_progress); int report_progress);
void write_commit_graph(const char *obj_dir, int write_commit_graph(const char *obj_dir,
struct string_list *pack_indexes, struct string_list *pack_indexes,
struct string_list *commit_hex, struct string_list *commit_hex,
int append, int report_progress); int append, int report_progress);

View File

@ -23,6 +23,14 @@ test_expect_success 'write graph with no packs' '
test_path_is_file info/commit-graph test_path_is_file info/commit-graph
' '
test_expect_success 'close with correct error on bad input' '
cd "$TRASH_DIRECTORY/full" &&
echo doesnotexist >in &&
{ git commit-graph write --stdin-packs <in 2>stderr; ret=$?; } &&
test "$ret" = 1 &&
test_i18ngrep "error adding pack" stderr
'
test_expect_success 'create commits and repack' ' test_expect_success 'create commits and repack' '
cd "$TRASH_DIRECTORY/full" && cd "$TRASH_DIRECTORY/full" &&
for i in $(test_seq 3) for i in $(test_seq 3)