{"thread":{"id":"56297","subject":"[PATCH 0/2] packfile: freshen the mtime of packfile by bump file","startedAt":"2021-08-16T17:06:07Z","lastAt":"2021-08-16T17:06:09Z","messageCount":3,"participants":["Sun Chao via GitGitGadget"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"432817","messageId":"pull.1066.git.git.1629133561.gitgitgadget@gmail.com","threadId":"56297","inReplyTo":null,"subject":"[PATCH 0/2] packfile: freshen the mtime of packfile by bump file","fromName":"Sun Chao via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-08-16T17:05:59Z","receivedAt":"2021-08-16T17:06:07Z","isPatch":true,"sender":{"key":"16657101987@163.com","avatar":"https://avatars.githubusercontent.com/u/192864724?v=4"},"body":"packfile: freshen the mtime of packfile by bump file\n\nWe've talked about the cache reload through earlier patches(\nhttps://lore.kernel.org/git/pull.1043.git.git.1625943685565.gitgitgadget@gmail.com),\nand we stopped because no further evidence can tell NFS client will reload\nthe page caches if the file mtime changed. So our team have done these\nexperiments:\n\nStep1: prepare git servers which mount the NFS disk and a big repo\n\nWe prepared 3 vms named c1, s1 and s2, we also have a NFS server named n1.\ns1 and s2 mount the NFS disk from n1 by:\n\n    mount -t nfs -o vers=3,timeo=600,nolock,noatime,lookupcache=postive,\\\n    actimeo=3 <n1 ip addr>:/repositories /mnt/repositories\n\n\nWe setup git server services on s1 and s2, so we can clone repos from s1 by\ngit commands. Then we created a repository under /mnt/repositories, and\npushed large files to the repository, so we can find a large .pack file in\nthe repository with about 1.2 GB size.\n\nStep2: do first git clone from client after drop caches of s1\n\nFirst we drop the caches from s1 by:\n\n    sync; echo 3 > /proc/sys/vm/drop_caches\n\n\nThen we run git command in c1 to clone the huge repository we created in\nStep1, at the same time we run the two commands in s1:\n\n    tcpdump -nn host <n1 ip addr> -w 1st_command.pcap\n    nfsiostat 1 -p /mnt/repositories\n\n\ntry to get the result and check what happends.\n\nStep3: do new git clones without drop caches of s1\n\nAfter Step2, we called new git clone command in c1 to clone the huge\nrepository for serveral times, and also run the commands at the same time:\n\n    tcpdump -nn host <n1 ip addr> -w lots_of_command.pcap\n    nfsiostat 1 -p /mnt/repositories\n\n\nStep4: do new git clones with packfile mtime changed\n\nAfter Step2 and Step3, we try to touch all the \".pack\" files from s2, and we\ncall a new git clone in c1 to download the huge repository again, and run\nthe two command in s1 at the same time:\n\n    tcpdump -nn host <n1 ip addr> -w mtime_changed_command.pcap\n    nfsiostat 1 -p /mnt/repositories\n\n\nResult:\n\nWe got a about 1.4GB big pcap file during Step2 and Step4, we can find lots\nof READ request and response after open it with wireshark. And by\n'nfsiostat' command we can see the 'ops/s' and 'KB/s' of 'read' in the\noutput shows a relatively large value for a while.\n\nBut we got a 4MB pcap file in Step3, and open it with wireshark, we can only\nfind GETATTR and FSSTAT requests and response. And we the 'nfsiostat' always\nshow 0 in 'ops/s' and 'KB/s' of 'read' part in the output.\n\nWe have done Step1 to Step4 serveral times, each time the result are same.\n\nSo we can make sure the NFS client will reload the page cache if other NFS\nclient changes the mtime of the large .pack files. And for git servers which\nuse filesystem like NFS to manage large repositories, reload large files\nthat only have mtime changed result big NFS server IOPS pressure and that\nalso makes the git server slow because the IO is the bottleneck when there\nare too many client requests for the same big repositries.\n\nAnd I do think the team who manage the git servers need a configuration\nchoise which can enhance the mtime of packfile through another file which\nshould be small enough or even empty. It should be backward compatibility\nwhen it is in default value, but just as metioned by Ævar before, maybe\nsomepeople what to use it in mixed-version environment, we should warn them\nin documents, but such configuration do big help for some team who run some\nservers mount the NFS disks.\n\nSun Chao (2):\n  packfile: rename `derive_filename()` to `derive_pack_filename()`\n  packfile: freshen the mtime of packfile by bump file\n\n Documentation/config/core.txt   |  11 +++\n builtin/index-pack.c            |  19 +----\n cache.h                         |   1 +\n config.c                        |   5 ++\n environment.c                   |   1 +\n object-file.c                   |  30 +++++++-\n packfile.c                      |  25 ++++++-\n packfile.h                      |   7 ++\n t/t5326-pack-mtime-bumpfiles.sh | 118 ++++++++++++++++++++++++++++++++\n 9 files changed, 198 insertions(+), 19 deletions(-)\n create mode 100755 t/t5326-pack-mtime-bumpfiles.sh\n\n\nbase-commit: 5d213e46bb7b880238ff5ea3914e940a50ae9369\nPublished-As: https://github.com/gitgitgadget/git/releases/tag/pr-git-1066%2Fsunchao9%2Fmaster-v1\nFetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-git-1066/sunchao9/master-v1\nPull-Request: https://github.com/git/git/pull/1066\n-- \ngitgitgadget\n"},{"id":"432818","messageId":"81afc69d22c0c782eea80719557161ae19a4f72e.1629133561.git.gitgitgadget@gmail.com","threadId":"56297","inReplyTo":"pull.1066.git.git.1629133561.gitgitgadget@gmail.com","subject":"[PATCH 1/2] packfile: rename `derive_filename()` to `derive_pack_filename()`","fromName":"Sun Chao via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-08-16T17:06:00Z","receivedAt":"2021-08-16T17:06:08Z","isPatch":true,"sender":{"key":"16657101987@163.com","avatar":"https://avatars.githubusercontent.com/u/192864724?v=4"},"body":"From: Sun Chao <16657101987@163.com>\n\nIn order to allow some function get a new file name from `.pack` file\nwith a new suffix, move `derive_filename()` in `builtin/index-pack.c`\nto `packfile.c` with a new name `derive_pack_filename(), and export\nit from `packfile.h`.\n\nSigned-off-by: Sun Chao <16657101987@163.com>\n---\n builtin/index-pack.c | 19 +++----------------\n packfile.c           | 13 +++++++++++++\n packfile.h           |  7 +++++++\n 3 files changed, 23 insertions(+), 16 deletions(-)\n\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 8336466865c..3c83789ccef 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -1435,19 +1435,6 @@ static void fix_unresolved_deltas(struct hashfile *f)\n \tfree(sorted_by_pos);\n }\n \n-static const char *derive_filename(const char *pack_name, const char *strip,\n-\t\t\t\t   const char *suffix, struct strbuf *buf)\n-{\n-\tsize_t len;\n-\tif (!strip_suffix(pack_name, strip, &len) || !len ||\n-\t    pack_name[len - 1] != '.')\n-\t\tdie(_(\"packfile name '%s' does not end with '.%s'\"),\n-\t\t    pack_name, strip);\n-\tstrbuf_add(buf, pack_name, len);\n-\tstrbuf_addstr(buf, suffix);\n-\treturn buf->buf;\n-}\n-\n static void write_special_file(const char *suffix, const char *msg,\n \t\t\t       const char *pack_name, const unsigned char *hash,\n \t\t\t       const char **report)\n@@ -1458,7 +1445,7 @@ static void write_special_file(const char *suffix, const char *msg,\n \tint msg_len = strlen(msg);\n \n \tif (pack_name)\n-\t\tfilename = derive_filename(pack_name, \"pack\", suffix, &name_buf);\n+\t\tfilename = derive_pack_filename(pack_name, \"pack\", suffix, &name_buf);\n \telse\n \t\tfilename = odb_pack_name(&name_buf, hash, suffix);\n \n@@ -1853,13 +1840,13 @@ int cmd_index_pack(int argc, const char **argv, const char *prefix)\n \tif (from_stdin && hash_algo)\n \t\tdie(_(\"--object-format cannot be used with --stdin\"));\n \tif (!index_name && pack_name)\n-\t\tindex_name = derive_filename(pack_name, \"pack\", \"idx\", &index_name_buf);\n+\t\tindex_name = derive_pack_filename(pack_name, \"pack\", \"idx\", &index_name_buf);\n \n \topts.flags &= ~(WRITE_REV | WRITE_REV_VERIFY);\n \tif (rev_index) {\n \t\topts.flags |= verify ? WRITE_REV_VERIFY : WRITE_REV;\n \t\tif (index_name)\n-\t\t\trev_index_name = derive_filename(index_name,\n+\t\t\trev_index_name = derive_pack_filename(index_name,\n \t\t\t\t\t\t\t \"idx\", \"rev\",\n \t\t\t\t\t\t\t &rev_index_name_buf);\n \t}\ndiff --git a/packfile.c b/packfile.c\nindex 9ef6d982928..315c3da259a 100644\n--- a/packfile.c\n+++ b/packfile.c\n@@ -40,6 +40,19 @@ char *sha1_pack_index_name(const unsigned char *sha1)\n \treturn odb_pack_name(&buf, sha1, \"idx\");\n }\n \n+const char *derive_pack_filename(const char *pack_name, const char *strip,\n+\t\t\t\tconst char *suffix, struct strbuf *buf)\n+{\n+\tsize_t len;\n+\tif (!strip_suffix(pack_name, strip, &len) || !len ||\n+\t    pack_name[len - 1] != '.')\n+\t\tdie(_(\"packfile name '%s' does not end with '.%s'\"),\n+\t\t    pack_name, strip);\n+\tstrbuf_add(buf, pack_name, len);\n+\tstrbuf_addstr(buf, suffix);\n+\treturn buf->buf;\n+}\n+\n static unsigned int pack_used_ctr;\n static unsigned int pack_mmap_calls;\n static unsigned int peak_pack_open_windows;\ndiff --git a/packfile.h b/packfile.h\nindex 3ae117a8aef..ff702b22e6a 100644\n--- a/packfile.h\n+++ b/packfile.h\n@@ -31,6 +31,13 @@ char *sha1_pack_name(const unsigned char *sha1);\n  */\n char *sha1_pack_index_name(const unsigned char *sha1);\n \n+/*\n+ * Return the corresponding filename with given suffix from \"file_name\"\n+ * which must has \"strip\" suffix.\n+ */\n+const char *derive_pack_filename(const char *file_name, const char *strip,\n+\t\tconst char *suffix, struct strbuf *buf);\n+\n /*\n  * Return the basename of the packfile, omitting any containing directory\n  * (e.g., \"pack-1234abcd[...].pack\").\n-- \ngitgitgadget\n\n"},{"id":"432819","messageId":"7166f77615442e511159be2d7ad2b3b46f40cbd7.1629133561.git.gitgitgadget@gmail.com","threadId":"56297","inReplyTo":"pull.1066.git.git.1629133561.gitgitgadget@gmail.com","subject":"[PATCH 2/2] packfile: freshen the mtime of packfile by bump file","fromName":"Sun Chao via GitGitGadget","fromEmail":"gitgitgadget@gmail.com","sentAt":"2021-08-16T17:06:01Z","receivedAt":"2021-08-16T17:06:09Z","isPatch":true,"sender":{"key":"16657101987@163.com","avatar":"https://avatars.githubusercontent.com/u/192864724?v=4"},"body":"From: Sun Chao <16657101987@163.com>\n\nCommit 33d4221c79 (write_sha1_file: freshen existing objects,\n2014-10-15) avoid writing existing objects by freshen their\nmtime (especially the packfiles contains them) in order to\naid the correct caching, and some process like find_lru_pack\ncan make good decision. However, this is unfriendly to\nincremental backup jobs or services rely on cached file system\nwhen there are large '.pack' files exists.\n\nFor example, after packed all objects, use 'write-tree' to\ncreate same commit with the same tree and same environments\nsuch like GIT_COMMITTER_DATE and GIT_AUTHOR_DATE, we can\nnotice the '.pack' file's mtime changed. Git servers\nthat use filesystems like NFS will reload the '.pack' files\nto file system page cache, which will slow the git commands.\n\nSo if we freshen the mtime of packfile by updating a '.bump'\nfile instead, when we check the mtime of packfile, get it from\n'.bump' file also. Large git repository may contains large\n'.pack' files, but '.bump' files can be empty. This will avoid\nfile system page caches reload large files from NFS and then\nmake git commands faster.\n\nSigned-off-by: Sun Chao <16657101987@163.com>\n---\n Documentation/config/core.txt   |  11 +++\n cache.h                         |   1 +\n config.c                        |   5 ++\n environment.c                   |   1 +\n object-file.c                   |  30 +++++++-\n packfile.c                      |  12 +++-\n t/t5326-pack-mtime-bumpfiles.sh | 118 ++++++++++++++++++++++++++++++++\n 7 files changed, 175 insertions(+), 3 deletions(-)\n create mode 100755 t/t5326-pack-mtime-bumpfiles.sh\n\ndiff --git a/Documentation/config/core.txt b/Documentation/config/core.txt\nindex c04f62a54a1..963d1b54e7e 100644\n--- a/Documentation/config/core.txt\n+++ b/Documentation/config/core.txt\n@@ -398,6 +398,17 @@ the largest projects.  You probably do not need to adjust this value.\n +\n Common unit suffixes of 'k', 'm', or 'g' are supported.\n \n+core.packMtimeToBumpFiles::\n+\tNormally we avoid writing existing object by freshening the mtime\n+\tof the *.pack file which contains it in order to aid some processes\n+\tsuch like prune. Use a *.bump file instead of *.pack file will\n+\tavoid file system cache re-sync the large packfiles on filesystems\n+\tlike NFS, and consequently make git commands faster.\n++\n+The default is 'false' which means the *.pack file will be freshened by\n+default. If set to 'true', the file with the '.bump' suffix will be\n+created automatically, and it's mtime will be freshened instead.\n+\n core.deltaBaseCacheLimit::\n \tMaximum number of bytes per thread to reserve for caching base objects\n \tthat may be referenced by multiple deltified objects.  By storing the\ndiff --git a/cache.h b/cache.h\nindex bd4869beee4..a563cbacfa2 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -960,6 +960,7 @@ extern const char *git_hooks_path;\n extern int zlib_compression_level;\n extern int core_compression_level;\n extern int pack_compression_level;\n+extern int pack_mtime_to_bumpfiles;\n extern size_t packed_git_window_size;\n extern size_t packed_git_limit;\n extern size_t delta_base_cache_limit;\ndiff --git a/config.c b/config.c\nindex f33abeab851..10ccf7c5581 100644\n--- a/config.c\n+++ b/config.c\n@@ -1431,6 +1431,11 @@ static int git_default_core_config(const char *var, const char *value, void *cb)\n \t\treturn 0;\n \t}\n \n+\tif (!strcmp(var, \"core.packmtimetobumpfiles\")) {\n+\t\tpack_mtime_to_bumpfiles = git_config_bool(var, value);\n+\t\treturn 0;\n+\t}\n+\n \tif (!strcmp(var, \"core.deltabasecachelimit\")) {\n \t\tdelta_base_cache_limit = git_config_ulong(var, value);\n \t\treturn 0;\ndiff --git a/environment.c b/environment.c\nindex d6b22ede7ea..5fa26cb3758 100644\n--- a/environment.c\n+++ b/environment.c\n@@ -43,6 +43,7 @@ const char *git_hooks_path;\n int zlib_compression_level = Z_BEST_SPEED;\n int core_compression_level;\n int pack_compression_level = Z_DEFAULT_COMPRESSION;\n+int pack_mtime_to_bumpfiles;\n int fsync_object_files;\n size_t packed_git_window_size = DEFAULT_PACKED_GIT_WINDOW_SIZE;\n size_t packed_git_limit = DEFAULT_PACKED_GIT_LIMIT;\ndiff --git a/object-file.c b/object-file.c\nindex a8be8994814..434073c17f1 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -1994,12 +1994,38 @@ static int freshen_loose_object(const struct object_id *oid)\n static int freshen_packed_object(const struct object_id *oid)\n {\n \tstruct pack_entry e;\n+\tstruct stat st;\n+\tstruct strbuf name_buf = STRBUF_INIT;\n+\tconst char *filename;\n+\n \tif (!find_pack_entry(the_repository, oid, &e))\n \t\treturn 0;\n \tif (e.p->freshened)\n \t\treturn 1;\n-\tif (!freshen_file(e.p->pack_name))\n-\t\treturn 0;\n+\n+\tfilename = e.p->pack_name;\n+\tif (!pack_mtime_to_bumpfiles) {\n+\t\tif (!freshen_file(filename))\n+\t\t\treturn 0;\n+\t\te.p->freshened = 1;\n+\t\treturn 1;\n+\t}\n+\n+\tfilename = derive_pack_filename(filename, \"pack\", \"bump\", &name_buf);\n+\tif (lstat(filename, &st) < 0) {\n+\t\tint fd = open(filename, O_CREAT|O_EXCL|O_WRONLY, 0664);\n+\t\tif (fd < 0) {\n+\t\t\t// here we need to check it again because other git process may created it\n+\t\t\tif (lstat(filename, &st) < 0)\n+\t\t\t\tdie_errno(\"unable to create '%s'\", filename);\n+\t\t} else {\n+\t\t\tclose(fd);\n+\t\t}\n+\t} else {\n+\t\tif (!freshen_file(filename))\n+\t\t\treturn 0;\n+\t}\n+\n \te.p->freshened = 1;\n \treturn 1;\n }\ndiff --git a/packfile.c b/packfile.c\nindex 315c3da259a..f5cee440601 100644\n--- a/packfile.c\n+++ b/packfile.c\n@@ -374,7 +374,7 @@ void close_object_store(struct raw_object_store *o)\n \n void unlink_pack_path(const char *pack_name, int force_delete)\n {\n-\tstatic const char *exts[] = {\".pack\", \".idx\", \".rev\", \".keep\", \".bitmap\", \".promisor\"};\n+\tstatic const char *exts[] = {\".pack\", \".idx\", \".rev\", \".keep\", \".bitmap\", \".promisor\", \".bump\"};\n \tint i;\n \tstruct strbuf buf = STRBUF_INIT;\n \tsize_t plen;\n@@ -741,6 +741,16 @@ struct packed_git *add_packed_git(const char *path, size_t path_len, int local)\n \tp->pack_size = st.st_size;\n \tp->pack_local = local;\n \tp->mtime = st.st_mtime;\n+\n+\tif (pack_mtime_to_bumpfiles) {\n+\t\tstruct strbuf name_buf = STRBUF_INIT;\n+\t\tconst char *filename;\n+\n+\t\tfilename = derive_pack_filename(path, \"idx\", \"bump\", &name_buf);\n+\t\tif (!stat(filename, &st)) {\n+\t\t\tp->mtime = st.st_mtime;\n+\t\t}\n+\t}\n \tif (path_len < the_hash_algo->hexsz ||\n \t    get_sha1_hex(path + path_len - the_hash_algo->hexsz, p->hash))\n \t\thashclr(p->hash);\ndiff --git a/t/t5326-pack-mtime-bumpfiles.sh b/t/t5326-pack-mtime-bumpfiles.sh\nnew file mode 100755\nindex 00000000000..d6d9e6dc446\n--- /dev/null\n+++ b/t/t5326-pack-mtime-bumpfiles.sh\n@@ -0,0 +1,118 @@\n+#!/bin/sh\n+\n+test_description='packfile mtime use bump files'\n+. ./test-lib.sh\n+\n+if stat -c %Y . >/dev/null 2>&1; then\n+    get_modified_time() { stat -c %Y \"$1\" 2>/dev/null; }\n+elif stat -f %m . >/dev/null 2>&1; then\n+    get_modified_time() { stat -f %m \"$1\" 2>/dev/null; }\n+elif date -r . +%s >/dev/null 2>&1; then\n+    get_modified_time() { date -r \"$1\" +%s 2>/dev/null; }\n+else\n+    echo 'get_modified_time() is unsupported' >&2\n+    get_modified_time() { printf '%s' 0; }\n+fi\n+\n+test_expect_success 'freshen existing packfile without core.packMtimeToBumpFiles' '\n+\tobj1=$(echo one | git hash-object -w --stdin) &&\n+\tobj2=$(echo two | git hash-object -w --stdin) &&\n+\tpack1=$(echo $obj1 | git pack-objects .git/objects/pack/pack) &&\n+\tpack2=$(echo $obj2 | git pack-objects .git/objects/pack/pack) &&\n+\ttest-tool chmtime =-60 .git/objects/pack/pack-$pack1.* &&\n+\ttest-tool chmtime =-60 .git/objects/pack/pack-$pack2.* &&\n+\tpack1_mtime=$(get_modified_time .git/objects/pack/pack-$pack1.pack) &&\n+\tpack2_mtime=$(get_modified_time .git/objects/pack/pack-$pack2.pack) &&\n+\t(echo one | git hash-object -w --stdin) &&\n+\t! test_path_exists .git/objects/pack/pack-$pack1.bump &&\n+\t! test_path_exists .git/objects/pack/pack-$pack2.bump &&\n+\tpack1_mtime_new=$(get_modified_time .git/objects/pack/pack-$pack1.pack) &&\n+\tpack2_mtime_new=$(get_modified_time .git/objects/pack/pack-$pack2.pack) &&\n+\techo \"$pack1_mtime : $pack1_mtime_new\" &&\n+\ttest ! \"$pack1_mtime\" = \"$pack1_mtime_new\" &&\n+\ttest \"$pack2_mtime\" = \"$pack2_mtime_new\"\n+\n+'\n+\n+test_expect_success 'freshen existing packfile with core.packMtimeToBumpFiles' '\n+\n+\trm -rf .git/objects && git init &&\n+\tobj1=$(echo one | git hash-object -w --stdin) &&\n+\tobj2=$(echo two | git hash-object -w --stdin) &&\n+\tpack1=$(echo $obj1 | git pack-objects .git/objects/pack/pack) &&\n+\tpack2=$(echo $obj2 | git pack-objects .git/objects/pack/pack) &&\n+\ttest-tool chmtime =-60 .git/objects/pack/pack-$pack1.* &&\n+\ttest-tool chmtime =-60 .git/objects/pack/pack-$pack2.* &&\n+\tpack1_mtime=$(get_modified_time .git/objects/pack/pack-$pack1.pack) &&\n+\tpack2_mtime=$(get_modified_time .git/objects/pack/pack-$pack2.pack) &&\n+\t(echo one | git -c core.packMtimeToBumpFiles=true hash-object -w --stdin) &&\n+\ttest_path_exists .git/objects/pack/pack-$pack1.bump &&\n+\t! test_path_exists .git/objects/pack/pack-$pack2.bump &&\n+\tpack1_mtime_new=$(get_modified_time .git/objects/pack/pack-$pack1.pack) &&\n+\tpack2_mtime_new=$(get_modified_time .git/objects/pack/pack-$pack2.pack) &&\n+\ttest \"$pack1_mtime\" = \"$pack1_mtime_new\" &&\n+\ttest \"$pack2_mtime\" = \"$pack2_mtime_new\"\n+\n+'\n+\n+test_expect_success 'repack prune unreachable objects without core.packMtimeToBumpFiles' '\n+\n+\trm -rf .git/objects && git init &&\n+\tobj1=$(echo one | git hash-object -w --stdin) &&\n+\tobj2=$(echo two | git hash-object -w --stdin) &&\n+\tpack1=$(echo $obj1 | git pack-objects .git/objects/pack/pack) &&\n+\tpack2=$(echo $obj2 | git pack-objects .git/objects/pack/pack) &&\n+\techo one | git hash-object -w --stdin &&\n+\techo two | git hash-object -w --stdin &&\n+\t! test_path_exists .git/objects/pack/pack-$pack1.bump &&\n+\t! test_path_exists .git/objects/pack/pack-$pack2.bump &&\n+\tgit prune-packed &&\n+\tgit cat-file -p $obj1 &&\n+\tgit cat-file -p $obj2 &&\n+\ttest-tool chmtime =-86400 .git/objects/pack/pack-$pack2.pack &&\n+\tgit repack -A -d --unpack-unreachable=1.hour.ago &&\n+\tgit cat-file -p $obj1 &&\n+\ttest_must_fail git cat-file -p $obj2\n+\n+'\n+\n+test_expect_success 'repack prune unreachable objects with core.packMtimeToBumpFiles and bump files' '\n+\n+\trm -rf .git/objects && git init &&\n+\tobj1=$(echo one | git hash-object -w --stdin) &&\n+\tobj2=$(echo two | git hash-object -w --stdin) &&\n+\tpack1=$(echo $obj1 | git pack-objects .git/objects/pack/pack) &&\n+\tpack2=$(echo $obj2 | git pack-objects .git/objects/pack/pack) &&\n+\techo one | git -c core.packMtimeToBumpFiles=true hash-object -w --stdin &&\n+\techo two | git -c core.packMtimeToBumpFiles=true hash-object -w --stdin &&\n+\ttest_path_exists .git/objects/pack/pack-$pack1.bump &&\n+\ttest_path_exists .git/objects/pack/pack-$pack2.bump &&\n+\ttest-tool chmtime =-86400 .git/objects/pack/pack-$pack2.pack &&\n+\tgit -c core.packMtimeToBumpFiles=true repack -A -d --unpack-unreachable=1.hour.ago &&\n+\tgit cat-file -p $obj1 &&\n+\tgit cat-file -p $obj2\n+\n+'\n+\n+test_expect_success 'repack prune unreachable objects with core.packMtimeToBumpFiles and old bump files' '\n+\n+\trm -rf .git/objects && git init &&\n+\tobj1=$(echo one | git hash-object -w --stdin) &&\n+\tobj2=$(echo two | git hash-object -w --stdin) &&\n+\tpack1=$(echo $obj1 | git pack-objects .git/objects/pack/pack) &&\n+\tpack2=$(echo $obj2 | git pack-objects .git/objects/pack/pack) &&\n+\techo one | git -c core.packMtimeToBumpFiles=true hash-object -w --stdin &&\n+\techo two | git -c core.packMtimeToBumpFiles=true hash-object -w --stdin &&\n+\ttest_path_exists .git/objects/pack/pack-$pack1.bump &&\n+\ttest_path_exists .git/objects/pack/pack-$pack2.bump &&\n+\tgit prune-packed &&\n+\tgit cat-file -p $obj1 &&\n+\tgit cat-file -p $obj2 &&\n+\ttest-tool chmtime =-86400 .git/objects/pack/pack-$pack2.bump &&\n+\tgit -c core.packMtimeToBumpFiles=true repack -A -d --unpack-unreachable=1.hour.ago &&\n+\tgit cat-file -p $obj1 &&\n+\ttest_must_fail git cat-file -p $obj2\n+\n+'\n+\n+test_done\n-- \ngitgitgadget\n"}]}