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

[PATCH] fsck: return non-zero status on missing ref tips

From
Jeff King <peff@peff.net>
Date
Sep 12, 2014, 03:38 UTC
Message-ID
<20140912033830.GA5507@peff.net>
In-Reply-To
<20140909220709.GA14029@peff.net>
Show 14 quoted lines
> On Tue, Sep 09, 2014 at 03:03:33PM -0700, Junio C Hamano wrote:
> 
> > Upon finding a corrupt loose object, we forgot to note the error to
> > signal it with the exit status of the entire process.
> > 
> > [jc: adjusted t1450 and added another test]
> > 
> > Signed-off-by: Junio C Hamano <gitster@pobox.com>
> > ---
> > 
> >  * I think your fix is a right one that catches all the "we can
> >    parse minimally for the purpose of 'struct object' class system,
> >    but the object is semantically broken" cases, as fsck_obj() is
> >    where such a validation should all happen.
Here's another structural case that we should catch but do not:
-- >8 --
Subject: fsck: return non-zero status on missing ref tips

Fsck tries hard to detect missing objects, and will complain (and exit non-zero) about any inter-object links that are missing. However, it will not exit non-zero for any missing ref tips, meaning that a severely broken repository may still pass "git fsck && echo ok".

The problem is that we use for_each_ref to iterate over the ref tips, which hides broken tips. It does at least print an error from the refs.c code, but fsck does not ever see the ref and cannot note the problem in its exit code. We can solve this by using for_each_rawref and noting the error ourselves.

In addition to adding tests for this case, we add tests for all types of missing-object links (all of which worked, but which we were not testing).

Signed-off-by: Jeff King <peff@peff.net>
---
Just below here we check that refs/heads/* points only to commit
objects. That's also sort-of-structural, but is pretty easy to recover
from without data loss, so I don't think it is as obvious a candidate
for a non-zero exit.
 builtin/fsck.c  |  3 ++-
 t/t1450-fsck.sh | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 58 insertions(+), 1 deletion(-)
diff --git a/builtin/fsck.c b/builtin/fsck.c
index 29de901..0928a98 100644
--- a/builtin/fsck.c
+++ b/builtin/fsck.c
@@ -489,6 +489,7 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int f
 	obj = parse_object(sha1);
 	if (!obj) {
 		error("%s: invalid sha1 pointer %s", refname, sha1_to_hex(sha1));
+		errors_found |= ERROR_REACHABLE;
 		/* We'll continue with the rest despite the error.. */
 		return 0;
 	}
@@ -505,7 +506,7 @@ static void get_default_heads(void)
 {
 	if (head_points_at && !is_null_sha1(head_sha1))
 		fsck_handle_ref("HEAD", head_sha1, 0, NULL);
-	for_each_ref(fsck_handle_ref, NULL);
+	for_each_rawref(fsck_handle_ref, NULL);
 	if (include_reflogs)
 		for_each_reflog(fsck_handle_reflog, NULL);
 
diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh
index 0de755c..b52397a 100755
--- a/t/t1450-fsck.sh
+++ b/t/t1450-fsck.sh
@@ -302,4 +302,60 @@ test_expect_success 'fsck notices ".git" in trees' '
 	)
 '
 
+# create a static test repo which is broken by omitting
+# one particular object ($1, which is looked up via rev-parse
+# in the new repository).
+create_repo_missing () {
+	rm -rf missing &&
+	git init missing &&
+	(
+		cd missing &&
+		git commit -m one --allow-empty &&
+		mkdir subdir &&
+		echo content >subdir/file &&
+		git add subdir/file &&
+		git commit -m two &&
+		unrelated=$(echo unrelated | git hash-object --stdin -w) &&
+		git tag -m foo tag $unrelated &&
+		sha1=$(git rev-parse --verify "$1") &&
+		path=$(echo $sha1 | sed 's|..|&/|') &&
+		rm .git/objects/$path
+	)
+}
+
+test_expect_success 'fsck notices missing blob' '
+	create_repo_missing HEAD:subdir/file &&
+	test_must_fail git -C missing fsck
+'
+
+test_expect_success 'fsck notices missing subtree' '
+	create_repo_missing HEAD:subdir &&
+	test_must_fail git -C missing fsck
+'
+
+test_expect_success 'fsck notices missing root tree' '
+	create_repo_missing HEAD^{tree} &&
+	test_must_fail git -C missing fsck
+'
+
+test_expect_success 'fsck notices missing parent' '
+	create_repo_missing HEAD^ &&
+	test_must_fail git -C missing fsck
+'
+
+test_expect_success 'fsck notices missing tagged object' '
+	create_repo_missing tag^{blob} &&
+	test_must_fail git -C missing fsck
+'
+
+test_expect_success 'fsck notices ref pointing to missing commit' '
+	create_repo_missing HEAD &&
+	test_must_fail git -C missing fsck
+'
+
+test_expect_success 'fsck notices ref pointing to missing tag' '
+	create_repo_missing tag &&
+	test_must_fail git -C missing fsck
+'
+
 test_done
-- 
2.1.0.373.g91ca799
Previous: Jeff KingNext: Jeff King
Message 9 of 21 in “git fsck exit code?”
  1. David TurnerAug 27, 2014
  2. Jeff KingAug 29, 2014
  3. Junio C HamanoAug 29, 2014
  4. David TurnerAug 29, 2014
  5. Jeff KingAug 29, 2014
  6. Junio C HamanoAug 29, 2014
  7. fsck: exit with non-zero status upon error from fsck_obj()Junio C Hamano, Sep 9, 2014
  8. Jeff KingSep 9, 2014
  9. fsck: return non-zero status on missing ref tipsJeff King, Sep 12, 2014
  10. Jeff KingSep 12, 2014
  11. Jeff KingSep 12, 2014
  12. Junio C HamanoSep 12, 2014
  13. Jeff KingSep 12, 2014
  14. Michael HaggertySep 15, 2014
  15. Michael HaggertySep 15, 2014
  16. Junio C HamanoSep 9, 2014
  17. Jonathan NiederSep 9, 2014
  18. Øyvind A. HolmAug 31, 2014
  19. Øyvind A. HolmSep 1, 2014
  20. David TurnerSep 1, 2014
  21. Jeff KingSep 9, 2014

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.