Skip to content

Commit

Permalink
commit, tag: don't set parsed bit for parse failures
Browse files Browse the repository at this point in the history
If we can't parse a commit, then parse_commit() will return an error
code. But it _also_ sets the "parsed" flag, which tells us not to bother
trying to re-parse the object. That means that subsequent parses have no
idea that the information in the struct may be bogus.  I.e., doing this:

  parse_commit(commit);
  ...
  if (parse_commit(commit) < 0)
          die("commit is broken");

will never trigger the die(). The second parse_commit() will see the
"parsed" flag and quietly return success.

There are two obvious ways to fix this:

  1. Stop setting "parsed" until we've successfully parsed.

  2. Keep a second "corrupt" flag to indicate that we saw an error (and
     when the parsed flag is set, return 0/-1 depending on the corrupt
     flag).

This patch does option 1. The obvious downside versus option 2 is that
we might continually re-parse a broken object. But in practice,
corruption like this is rare, and we typically die() or return an error
in the caller. So it's OK not to worry about optimizing for corruption.
And it's much simpler: we don't need to use an extra bit in the object
struct, and callers which check the "parsed" flag don't need to learn
about the corrupt bit, too.

There's no new test here, because this case is already covered in t5318.
Note that we do need to update the expected message there, because we
now detect the problem in the return from "parse_commit()", and not with
a separate check for a NULL tree. In fact, we can now ditch that
explicit tree check entirely, as we're covered robustly by this change
(and the previous recent change to treat a NULL tree as a parse error).

We'll also give tags the same treatment. I don't know offhand of any
cases where the problem can be triggered (it implies somebody ignoring a
parse error earlier in the process), but consistently returning an error
should cause the least surprise.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
peff authored and gitster committed Oct 28, 2019
1 parent 78d5014 commit 228c78f
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 6 deletions.
3 changes: 0 additions & 3 deletions commit-graph.c
Expand Up @@ -855,9 +855,6 @@ static void write_graph_chunk_data(struct hashfile *f, int hash_len,
die(_("unable to parse commit %s"),
oid_to_hex(&(*list)->object.oid));
tree = get_commit_tree_oid(*list);
if (!tree)
die(_("unable to get tree for %s"),
oid_to_hex(&(*list)->object.oid));
hashwrite(f, tree->hash, hash_len);

parent = (*list)->parents;
Expand Down
14 changes: 13 additions & 1 deletion commit.c
Expand Up @@ -405,7 +405,18 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b

if (item->object.parsed)
return 0;
item->object.parsed = 1;

if (item->parents) {
/*
* Presumably this is leftover from an earlier failed parse;
* clear it out in preparation for us re-parsing (we'll hit the
* same error, but that's good, since it lets our caller know
* the result cannot be trusted.
*/
free_commit_list(item->parents);
item->parents = NULL;
}

tail += size;
if (tail <= bufptr + tree_entry_len + 1 || memcmp(bufptr, "tree ", 5) ||
bufptr[tree_entry_len] != '\n')
Expand Down Expand Up @@ -462,6 +473,7 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b
if (check_graph)
load_commit_graph_info(r, item);

item->object.parsed = 1;
return 0;
}

Expand Down
2 changes: 1 addition & 1 deletion t/t5318-commit-graph.sh
Expand Up @@ -660,7 +660,7 @@ test_expect_success 'corrupt commit-graph write (missing tree)' '
git commit-tree -p "$broken" -m "good" "$tree" >good &&
test_must_fail git commit-graph write --stdin-commits \
<good 2>test_err &&
test_i18ngrep "unable to get tree for" test_err
test_i18ngrep "unable to parse commit" test_err
)
'

Expand Down
12 changes: 11 additions & 1 deletion tag.c
Expand Up @@ -141,7 +141,16 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u

if (item->object.parsed)
return 0;
item->object.parsed = 1;

if (item->tag) {
/*
* Presumably left over from a previous failed parse;
* clear it out in preparation for re-parsing (we'll probably
* hit the same error, which lets us tell our current caller
* about the problem).
*/
FREE_AND_NULL(item->tag);
}

if (size < the_hash_algo->hexsz + 24)
return -1;
Expand Down Expand Up @@ -192,6 +201,7 @@ int parse_tag_buffer(struct repository *r, struct tag *item, const void *data, u
else
item->date = 0;

item->object.parsed = 1;
return 0;
}

Expand Down

0 comments on commit 228c78f

Please sign in to comment.