@@ -398,6 +398,17 @@ the largest projects. You probably do not need to adjust this value.
+
Common unit suffixes of 'k', 'm', or 'g' are supported.
+core.packMtimeToBumpFiles::
+ Normally we avoid writing existing object by freshening the mtime
+ of the *.pack file which contains it in order to aid some processes
+ such like prune. Use a *.bump file instead of *.pack file will
+ avoid file system cache re-sync the large packfiles on filesystems
+ like NFS, and consequently make git commands faster.
++
+The default is 'false' which means the *.pack file will be freshened by
+default. If set to 'true', the file with the '.bump' suffix will be
+created automatically, and it's mtime will be freshened instead.
+
core.deltaBaseCacheLimit::
Maximum number of bytes per thread to reserve for caching base objects
that may be referenced by multiple deltified objects. By storing the
@@ -960,6 +960,7 @@ extern const char *git_hooks_path;
extern int zlib_compression_level;
extern int core_compression_level;
extern int pack_compression_level;
+extern int pack_mtime_to_bumpfiles;
extern size_t packed_git_window_size;
extern size_t packed_git_limit;
extern size_t delta_base_cache_limit;
@@ -1431,6 +1431,11 @@ static int git_default_core_config(const char *var, const char *value, void *cb)
return 0;
}
+ if (!strcmp(var, "core.packmtimetobumpfiles")) {
+ pack_mtime_to_bumpfiles = git_config_bool(var, value);
+ return 0;
+ }
+
if (!strcmp(var, "core.deltabasecachelimit")) {
delta_base_cache_limit = git_config_ulong(var, value);
return 0;
@@ -43,6 +43,7 @@ const char *git_hooks_path;
int zlib_compression_level = Z_BEST_SPEED;
int core_compression_level;
int pack_compression_level = Z_DEFAULT_COMPRESSION;
+int pack_mtime_to_bumpfiles;
int fsync_object_files;
size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;
size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;
@@ -1994,12 +1994,38 @@ static int freshen_loose_object(const struct object_id *oid)
static int freshen_packed_object(const struct object_id *oid)
{
struct pack_entry e;
+ struct stat st;
+ struct strbuf name_buf = STRBUF_INIT;
+ const char *filename;
+
if (!find_pack_entry(the_repository, oid, &e))
return 0;
if (e.p->freshened)
return 1;
- if (!freshen_file(e.p->pack_name))
- return 0;
+
+ filename = e.p->pack_name;
+ if (!pack_mtime_to_bumpfiles) {
+ if (!freshen_file(filename))
+ return 0;
+ e.p->freshened = 1;
+ return 1;
+ }
+
+ filename = derive_pack_filename(filename, "pack", "bump", &name_buf);
+ if (lstat(filename, &st) < 0) {
+ int fd = open(filename, O_CREAT|O_EXCL|O_WRONLY, 0664);
+ if (fd < 0) {
+ // here we need to check it again because other git process may created it
+ if (lstat(filename, &st) < 0)
+ die_errno("unable to create '%s'", filename);
+ } else {
+ close(fd);
+ }
+ } else {
+ if (!freshen_file(filename))
+ return 0;
+ }
+
e.p->freshened = 1;
return 1;
}
@@ -374,7 +374,7 @@ void close_object_store(struct raw_object_store *o)
void unlink_pack_path(const char *pack_name, int force_delete)
{
- static const char *exts[] = {".pack", ".idx", ".rev", ".keep", ".bitmap", ".promisor"};
+ static const char *exts[] = {".pack", ".idx", ".rev", ".keep", ".bitmap", ".promisor", ".bump"};
int i;
struct strbuf buf = STRBUF_INIT;
size_t plen;
@@ -741,6 +741,16 @@ struct packed_git *add_packed_git(const char *path, size_t path_len, int local)
p->pack_size = st.st_size;
p->pack_local = local;
p->mtime = st.st_mtime;
+
+ if (pack_mtime_to_bumpfiles) {
+ struct strbuf name_buf = STRBUF_INIT;
+ const char *filename;
+
+ filename = derive_pack_filename(path, "idx", "bump", &name_buf);
+ if (!stat(filename, &st)) {
+ p->mtime = st.st_mtime;
+ }
+ }
if (path_len < the_hash_algo->hexsz ||
get_sha1_hex(path + path_len - the_hash_algo->hexsz, p->hash))
hashclr(p->hash);
new file mode 100755
@@ -0,0 +1,118 @@
+#!/bin/sh
+
+test_description='packfile mtime use bump files'
+. ./test-lib.sh
+
+if stat -c %Y . >/dev/null 2>&1; then
+ get_modified_time() { stat -c %Y "$1" 2>/dev/null; }
+elif stat -f %m . >/dev/null 2>&1; then
+ get_modified_time() { stat -f %m "$1" 2>/dev/null; }
+elif date -r . +%s >/dev/null 2>&1; then
+ get_modified_time() { date -r "$1" +%s 2>/dev/null; }
+else
+ echo 'get_modified_time() is unsupported' >&2
+ get_modified_time() { printf '%s' 0; }
+fi
+
+test_expect_success 'freshen existing packfile without core.packMtimeToBumpFiles' '
+ obj1=$(echo one | git hash-object -w --stdin) &&
+ obj2=$(echo two | git hash-object -w --stdin) &&
+ pack1=$(echo $obj1 | git pack-objects .git/objects/pack/pack) &&
+ pack2=$(echo $obj2 | git pack-objects .git/objects/pack/pack) &&
+ test-tool chmtime =-60 .git/objects/pack/pack-$pack1.* &&
+ test-tool chmtime =-60 .git/objects/pack/pack-$pack2.* &&
+ pack1_mtime=$(get_modified_time .git/objects/pack/pack-$pack1.pack) &&
+ pack2_mtime=$(get_modified_time .git/objects/pack/pack-$pack2.pack) &&
+ (echo one | git hash-object -w --stdin) &&
+ ! test_path_exists .git/objects/pack/pack-$pack1.bump &&
+ ! test_path_exists .git/objects/pack/pack-$pack2.bump &&
+ pack1_mtime_new=$(get_modified_time .git/objects/pack/pack-$pack1.pack) &&
+ pack2_mtime_new=$(get_modified_time .git/objects/pack/pack-$pack2.pack) &&
+ echo "$pack1_mtime : $pack1_mtime_new" &&
+ test ! "$pack1_mtime" = "$pack1_mtime_new" &&
+ test "$pack2_mtime" = "$pack2_mtime_new"
+
+'
+
+test_expect_success 'freshen existing packfile with core.packMtimeToBumpFiles' '
+
+ rm -rf .git/objects && git init &&
+ obj1=$(echo one | git hash-object -w --stdin) &&
+ obj2=$(echo two | git hash-object -w --stdin) &&
+ pack1=$(echo $obj1 | git pack-objects .git/objects/pack/pack) &&
+ pack2=$(echo $obj2 | git pack-objects .git/objects/pack/pack) &&
+ test-tool chmtime =-60 .git/objects/pack/pack-$pack1.* &&
+ test-tool chmtime =-60 .git/objects/pack/pack-$pack2.* &&
+ pack1_mtime=$(get_modified_time .git/objects/pack/pack-$pack1.pack) &&
+ pack2_mtime=$(get_modified_time .git/objects/pack/pack-$pack2.pack) &&
+ (echo one | git -c core.packMtimeToBumpFiles=true hash-object -w --stdin) &&
+ test_path_exists .git/objects/pack/pack-$pack1.bump &&
+ ! test_path_exists .git/objects/pack/pack-$pack2.bump &&
+ pack1_mtime_new=$(get_modified_time .git/objects/pack/pack-$pack1.pack) &&
+ pack2_mtime_new=$(get_modified_time .git/objects/pack/pack-$pack2.pack) &&
+ test "$pack1_mtime" = "$pack1_mtime_new" &&
+ test "$pack2_mtime" = "$pack2_mtime_new"
+
+'
+
+test_expect_success 'repack prune unreachable objects without core.packMtimeToBumpFiles' '
+
+ rm -rf .git/objects && git init &&
+ obj1=$(echo one | git hash-object -w --stdin) &&
+ obj2=$(echo two | git hash-object -w --stdin) &&
+ pack1=$(echo $obj1 | git pack-objects .git/objects/pack/pack) &&
+ pack2=$(echo $obj2 | git pack-objects .git/objects/pack/pack) &&
+ echo one | git hash-object -w --stdin &&
+ echo two | git hash-object -w --stdin &&
+ ! test_path_exists .git/objects/pack/pack-$pack1.bump &&
+ ! test_path_exists .git/objects/pack/pack-$pack2.bump &&
+ git prune-packed &&
+ git cat-file -p $obj1 &&
+ git cat-file -p $obj2 &&
+ test-tool chmtime =-86400 .git/objects/pack/pack-$pack2.pack &&
+ git repack -A -d --unpack-unreachable=1.hour.ago &&
+ git cat-file -p $obj1 &&
+ test_must_fail git cat-file -p $obj2
+
+'
+
+test_expect_success 'repack prune unreachable objects with core.packMtimeToBumpFiles and bump files' '
+
+ rm -rf .git/objects && git init &&
+ obj1=$(echo one | git hash-object -w --stdin) &&
+ obj2=$(echo two | git hash-object -w --stdin) &&
+ pack1=$(echo $obj1 | git pack-objects .git/objects/pack/pack) &&
+ pack2=$(echo $obj2 | git pack-objects .git/objects/pack/pack) &&
+ echo one | git -c core.packMtimeToBumpFiles=true hash-object -w --stdin &&
+ echo two | git -c core.packMtimeToBumpFiles=true hash-object -w --stdin &&
+ test_path_exists .git/objects/pack/pack-$pack1.bump &&
+ test_path_exists .git/objects/pack/pack-$pack2.bump &&
+ test-tool chmtime =-86400 .git/objects/pack/pack-$pack2.pack &&
+ git -c core.packMtimeToBumpFiles=true repack -A -d --unpack-unreachable=1.hour.ago &&
+ git cat-file -p $obj1 &&
+ git cat-file -p $obj2
+
+'
+
+test_expect_success 'repack prune unreachable objects with core.packMtimeToBumpFiles and old bump files' '
+
+ rm -rf .git/objects && git init &&
+ obj1=$(echo one | git hash-object -w --stdin) &&
+ obj2=$(echo two | git hash-object -w --stdin) &&
+ pack1=$(echo $obj1 | git pack-objects .git/objects/pack/pack) &&
+ pack2=$(echo $obj2 | git pack-objects .git/objects/pack/pack) &&
+ echo one | git -c core.packMtimeToBumpFiles=true hash-object -w --stdin &&
+ echo two | git -c core.packMtimeToBumpFiles=true hash-object -w --stdin &&
+ test_path_exists .git/objects/pack/pack-$pack1.bump &&
+ test_path_exists .git/objects/pack/pack-$pack2.bump &&
+ git prune-packed &&
+ git cat-file -p $obj1 &&
+ git cat-file -p $obj2 &&
+ test-tool chmtime =-86400 .git/objects/pack/pack-$pack2.bump &&
+ git -c core.packMtimeToBumpFiles=true repack -A -d --unpack-unreachable=1.hour.ago &&
+ git cat-file -p $obj1 &&
+ test_must_fail git cat-file -p $obj2
+
+'
+
+test_done