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

[PATCH v2 17/25] write_sha1_file: freshen existing objects

From
Jeff King <peff@peff.net>
Date
Oct 15, 2014, 22:42 UTC
Message-ID
<20141015224222.GQ25630@peff.net>
In-Reply-To
<20141015223244.GA25368@peff.net>

When we try to write a loose object file, we first check whether that object already exists. If so, we skip the write as an optimization. However, this can interfere with prune's strategy of using mtimes to mark files in progress.

For example, if a branch contains a particular tree object and is deleted, that tree object may become unreachable, and have an old mtime. If a new operation then tries to write the same tree, this ends up as a noop; we notice we already have the object and do nothing. A prune running simultaneously with this operation will see the object as old, and may delete it.

We can solve this by "freshening" objects that we avoid writing by updating their mtime. The algorithm for doing so is essentially the same as that of has_sha1_file. Therefore we provide a new (static) interface "check_and_freshen", which finds and optionally freshens the object. It's trivial to implement freshening and simple checking by tweaking a single parameter.

Signed-off-by: Jeff King <peff@peff.net>
---
 sha1_file.c                | 51 +++++++++++++++++++++++++++++++++++++++-------
 t/t6501-freshen-objects.sh | 27 ++++++++++++++++++++++++
 2 files changed, 71 insertions(+), 7 deletions(-)
diff --git a/sha1_file.c b/sha1_file.c
index eb410a2..d7f1838 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -443,27 +443,53 @@ void prepare_alt_odb(void)
 	read_info_alternates(get_object_directory(), 0);
 }
 
-static int has_loose_object_local(const unsigned char *sha1)
+static int freshen_file(const char *fn)
 {
-	return !access(sha1_file_name(sha1), F_OK);
+	struct utimbuf t;
+	t.actime = t.modtime = time(NULL);
+	return !utime(fn, &t);
 }
 
-int has_loose_object_nonlocal(const unsigned char *sha1)
+static int check_and_freshen_file(const char *fn, int freshen)
+{
+	if (access(fn, F_OK))
+		return 0;
+	if (freshen && freshen_file(fn))
+		return 0;
+	return 1;
+}
+
+static int check_and_freshen_local(const unsigned char *sha1, int freshen)
+{
+	return check_and_freshen_file(sha1_file_name(sha1), freshen);
+}
+
+static int check_and_freshen_nonlocal(const unsigned char *sha1, int freshen)
 {
 	struct alternate_object_database *alt;
 	prepare_alt_odb();
 	for (alt = alt_odb_list; alt; alt = alt->next) {
 		fill_sha1_path(alt->name, sha1);
-		if (!access(alt->base, F_OK))
+		if (check_and_freshen_file(alt->base, freshen))
 			return 1;
 	}
 	return 0;
 }
 
+static int check_and_freshen(const unsigned char *sha1, int freshen)
+{
+	return check_and_freshen_local(sha1, freshen) ||
+	       check_and_freshen_nonlocal(sha1, freshen);
+}
+
+int has_loose_object_nonlocal(const unsigned char *sha1)
+{
+	return check_and_freshen_nonlocal(sha1, 0);
+}
+
 static int has_loose_object(const unsigned char *sha1)
 {
-	return has_loose_object_local(sha1) ||
-	       has_loose_object_nonlocal(sha1);
+	return check_and_freshen(sha1, 0);
 }
 
 static unsigned int pack_used_ctr;
@@ -2966,6 +2992,17 @@ static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
 	return move_temp_to_file(tmp_file, filename);
 }
 
+static int freshen_loose_object(const unsigned char *sha1)
+{
+	return check_and_freshen(sha1, 1);
+}
+
+static int freshen_packed_object(const unsigned char *sha1)
+{
+	struct pack_entry e;
+	return find_pack_entry(sha1, &e) && freshen_file(e.p->pack_name);
+}
+
 int write_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
 {
 	unsigned char sha1[20];
@@ -2978,7 +3015,7 @@ int write_sha1_file(const void *buf, unsigned long len, const char *type, unsign
 	write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
 	if (returnsha1)
 		hashcpy(returnsha1, sha1);
-	if (has_sha1_file(sha1))
+	if (freshen_loose_object(sha1) || freshen_packed_object(sha1))
 		return 0;
 	return write_loose_object(sha1, hdr, hdrlen, buf, len, 0);
 }
diff --git a/t/t6501-freshen-objects.sh b/t/t6501-freshen-objects.sh
index e25c47d..157f3f9 100755
--- a/t/t6501-freshen-objects.sh
+++ b/t/t6501-freshen-objects.sh
@@ -100,6 +100,33 @@ for repack in '' true; do
 	test_expect_success "repository passes fsck ($title)" '
 		git fsck
 	'
+
+	test_expect_success "abandon objects again ($title)" '
+		git reset --hard HEAD^ &&
+		find .git/objects -type f |
+		xargs test-chmtime -v -86400
+	'
+
+	test_expect_success "start writing new commit with same tree ($title)" '
+		tree=$(
+			GIT_INDEX_FILE=index.tmp &&
+			export GIT_INDEX_FILE &&
+			git read-tree HEAD &&
+			add abandon &&
+			add unrelated &&
+			git write-tree
+		)
+	'
+
+	test_expect_success "simultaneous gc ($title)" '
+		git gc --prune=12.hours.ago
+	'
+
+	# tree should have been refreshed by write-tree
+	test_expect_success "finish writing out commit ($title)" '
+		commit=$(echo foo | git commit-tree -p HEAD $tree) &&
+		git update-ref HEAD $commit
+	'
 done
 
 test_done
-- 
2.1.2.596.g7379948
Previous: Jeff KingNext: Jeff King
Message 24 of 48 in “prune-safety”
  1. 0/25 prune-safetyJeff King, Oct 15, 2014
  2. 01/25 foreach_alt_odb: propagate return value from callbackJeff King, Oct 15, 2014
  3. 02/25 isxdigit: cast input to unsigned charJeff King, Oct 15, 2014
  4. Junio C HamanoOct 16, 2014
  5. 03/25 object_array: factor out slopbuf-freeing logicJeff King, Oct 15, 2014
  6. Junio C HamanoOct 16, 2014
  7. git-bundle rev handling and de-dupingJeff King, Oct 17, 2014
  8. Philip OakleyOct 17, 2014
  9. Junio C HamanoOct 17, 2014
  10. 04/25 object_array: add a "clear" functionJeff King, Oct 15, 2014
  11. 05/25 clean up name allocation in prepare_revision_walkJeff King, Oct 15, 2014
  12. 06/25 reachable: use traverse_commit_list instead of custom walkJeff King, Oct 15, 2014
  13. Junio C HamanoOct 16, 2014
  14. 07/25 reachable: reuse revision.c "add all reflogs" codeJeff King, Oct 15, 2014
  15. 08/25 prune: factor out loose-object directory traversalJeff King, Oct 15, 2014
  16. 09/25 reachable: mark index blobs as SEENJeff King, Oct 15, 2014
  17. 10/25 prune-packed: use for_each_loose_file_in_objdirJeff King, Oct 15, 2014
  18. 11/25 count-objects: do not use xsize_t when counting object sizeJeff King, Oct 15, 2014
  19. 12/25 count-objects: use for_each_loose_file_in_objdirJeff King, Oct 15, 2014
  20. 13/25 sha1_file: add for_each iterators for loose and packed objectsJeff King, Oct 15, 2014
  21. 14/25 prune: keep objects reachable from recent objectsJeff King, Oct 15, 2014
  22. 15/25 pack-objects: refactor unpack-unreachable expiration checkJeff King, Oct 15, 2014
  23. 16/25 pack-objects: match prune logic for discarding objectsJeff King, Oct 15, 2014
  24. 17/25 write_sha1_file: freshen existing objectsJeff King, Oct 15, 2014
  25. 18/25 make add_object_array_with_context interface more saneJeff King, Oct 15, 2014
  26. 19/25 traverse_commit_list: support pending blobs/trees with pathsJeff King, Oct 15, 2014
  27. 20/25 rev-list: document --reflog optionJeff King, Oct 15, 2014
  28. 21/25 rev-list: add --index-objects optionJeff King, Oct 15, 2014
  29. Junio C HamanoOct 16, 2014
  30. Jeff KingOct 17, 2014
  31. Jeff KingOct 17, 2014
  32. 22/26 rev-list: add --indexed-objects optionJeff King, Oct 17, 2014
  33. 23/26 reachable: use revision machinery's --indexed-objects codeJeff King, Oct 17, 2014
  34. 24/26 pack-objects: use argv_arrayJeff King, Oct 17, 2014
  35. 25/26 repack: pack objects mentioned by the indexJeff King, Oct 17, 2014
  36. 26/26 pack-objects: double-check options before discarding objectsJeff King, Oct 17, 2014
  37. 22/25 reachable: use revision machinery's --index-objects codeJeff King, Oct 15, 2014
  38. 23/25 pack-objects: use argv_arrayJeff King, Oct 15, 2014
  39. 24/25 repack: pack objects mentioned by the indexJeff King, Oct 15, 2014
  40. 25/25 pack-objects: double-check options before discarding objectsJeff King, Oct 15, 2014
  41. Junio C HamanoOct 16, 2014
  42. Junio C HamanoOct 16, 2014
  43. Jeff KingOct 16, 2014
  44. Jeff KingOct 16, 2014
  45. Junio C HamanoOct 16, 2014
  46. Jeff KingOct 17, 2014
  47. Jeff KingOct 18, 2014
  48. Jeff KingOct 17, 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.