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

Re: [PATCH 16/16] write_sha1_file: freshen existing objects

From
Junio C Hamano <gitster@pobox.com>
Date
Oct 3, 2014, 21:29 UTC
Message-ID
<xmqq61g0lu3t.fsf@gitster.dls.corp.google.com>
In-Reply-To
<20141003204110.GP16293@peff.net>
Jeff King <peff@peff.net> writes:
Show 20 quoted lines
> 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.

An old referent by a recent unreachable may be in pack. Is it expected that the same pack will have many similar old objects (in other words, is it worth trying to optimize check-and-freshen by bypassing access() and utime(), perhaps by keeping a "freshened in this process already" flag in struct packed_git)?

Could check-and-freshen-nonlocal() ever be called with freshen set to true? Should it be? In other words, should we be mucking with objects in other people's repositories with utime()?

Other than these two minor questions, I am happy with this patch.
Thanks.
Show 135 quoted lines
> 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 d017289..d263b38 100644
> --- a/sha1_file.c
> +++ b/sha1_file.c
> @@ -442,27 +442,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;
> @@ -2949,6 +2975,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];
> @@ -2961,7 +2998,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
Previous: Jeff KingNext: Jeff King
Message 50 of 58 in “make prune mtime-checking more careful”
  1. 0/16 make prune mtime-checking more carefulJeff King, Oct 3, 2014
  2. 01/16 foreach_alt_odb: propagate return value from callbackJeff King, Oct 3, 2014
  3. René ScharfeOct 3, 2014
  4. Jeff KingOct 4, 2014
  5. 02/16 isxdigit: cast input to unsigned charJeff King, Oct 3, 2014
  6. 03/16 object_array: factor out slopbuf-freeing logicJeff King, Oct 3, 2014
  7. Michael HaggertyOct 7, 2014
  8. Jeff KingOct 8, 2014
  9. Michael HaggertyOct 8, 2014
  10. Jeff KingOct 8, 2014
  11. 04/16 object_array: add a "clear" functionJeff King, Oct 3, 2014
  12. 05/16 clean up name allocation in prepare_revision_walkJeff King, Oct 3, 2014
  13. 06/16 reachable: clear pending array after walking itJeff King, Oct 3, 2014
  14. 07/16 t5304: use test_path_is_* instead of "test -f"Jeff King, Oct 3, 2014
  15. Junio C HamanoOct 3, 2014
  16. 08/16 t5304: use helper to report failure of "test foo = bar"Jeff King, Oct 3, 2014
  17. Junio C HamanoOct 3, 2014
  18. Jeff KingOct 4, 2014
  19. Michael HaggertyOct 7, 2014
  20. Junio C HamanoOct 7, 2014
  21. Jeff KingOct 7, 2014
  22. Junio C HamanoOct 7, 2014
  23. Junio C HamanoOct 7, 2014
  24. Jeff KingOct 7, 2014
  25. Junio C HamanoOct 7, 2014
  26. Michael HaggertyOct 7, 2014
  27. Jeff KingOct 8, 2014
  28. Junio C HamanoOct 8, 2014
  29. 09/16 prune: factor out loose-object directory traversalJeff King, Oct 3, 2014
  30. Junio C HamanoOct 3, 2014
  31. Jeff KingOct 4, 2014
  32. Michael HaggertyOct 7, 2014
  33. Jeff KingOct 8, 2014
  34. 10/16 count-objects: do not use xsize_t when counting object sizeJeff King, Oct 3, 2014
  35. 11/16 count-objects: use for_each_loose_file_in_objdirJeff King, Oct 3, 2014
  36. 12/16 sha1_file: add for_each iterators for loose and packed objectsJeff King, Oct 3, 2014
  37. René ScharfeOct 5, 2014
  38. Ramsay JonesOct 5, 2014
  39. 13/16 prune: keep objects reachable from recent objectsJeff King, Oct 3, 2014
  40. Junio C HamanoOct 3, 2014
  41. Jeff KingOct 4, 2014
  42. Jeff KingOct 4, 2014
  43. Junio C HamanoOct 4, 2014
  44. Michael HaggertyOct 7, 2014
  45. Jeff KingOct 8, 2014
  46. Michael HaggertyOct 8, 2014
  47. 14/16 pack-objects: refactor unpack-unreachable expiration checkJeff King, Oct 3, 2014
  48. 15/16 pack-objects: match prune logic for discarding objectsJeff King, Oct 3, 2014
  49. 16/16 write_sha1_file: freshen existing objectsJeff King, Oct 3, 2014
  50. Junio C HamanoOct 3, 2014
  51. Jeff KingOct 4, 2014
  52. René ScharfeOct 5, 2014
  53. Junio C HamanoOct 3, 2014
  54. Junio C HamanoOct 4, 2014
  55. René ScharfeOct 5, 2014
  56. Jeff KingOct 6, 2014
  57. Jeff KingOct 8, 2014
  58. Junio C HamanoOct 8, 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.