Create 'expire' and 'repack' verbs for git-multi-pack-index
cover Create 'expire' and 'repack' verbs for git-multi-pack-index
The multi-pack-index provides a fast way to find an object among a large list of pack-files. It stores a single pack-reference for each object id, so duplicate objects are ignored. Among a list of pack-files storing the same object, the most-recently modified one is used.
Create new subcommands for the multi-pack-index builtin.
* 'git multi-pack-index expire': If we have a pack-file indexed by the multi-pack-index, but all objects in that pack are duplicated in more-recently modified packs, then delete that pack (and any others like it). Delete the reference to that pack in the multi-pack-index.
* 'git multi-pack-index repack --batch-size=': Starting from the oldest pack-files covered by the multi-pack-index, find those whose "expected size" is below the batch size until we have a collection of packs whose expected sizes add up to the batch size. We compute the expected size by multiplying the number of referenced objects by the pack-size and dividing by the total number of objects in the pack. If the batch-size is zero, then select all packs. Create a new pack containing all objects that the multi-pack-index references to those packs.
This allows us to create a new pattern for repacking objects: run 'repack'. After enough time has passed that all Git commands that started before the last 'repack' are finished, run 'expire' again. This approach has some advantages over the existing "repack everything" model:
1. Incremental. We can repack a small batch of objects at a time, instead of repacking all reachable objects. We can also limit ourselves to the objects that do not appear in newer pack-files.
2. Highly Available. By adding a new pack-file (and not deleting the old pack-files) we do not interrupt concurrent Git commands, and do not suffer performance degradation. By expiring only pack-files that have no referenced objects, we know that Git commands that are doing normal object lookups* will not be interrupted.
* Note: if someone concurrently runs a Git command that uses get_all_packs(), then that command could try to read the pack-files and pack-indexes that we are deleting during an expire command. Such commands are usually related to object maintenance (i.e. fsck, gc, pack-objects) or are related to less-often-used features (i.e. fast-import, http-backend, server-info).
We are using this approach in VFS for Git to do background maintenance of the "shared object cache" which is a Git alternate directory filled with packfiles containing commits and trees. We currently download pack-files on an hourly basis to keep up-to-date with the central server. The cache servers supply packs on an hourly and daily basis, so most of the hourly packs become useless after a new daily pack is downloaded. The 'expire' command would clear out most of those packs, but many will still remain with fewer than 100 objects remaining. The 'repack' command (with a batch size of 1-3gb, probably) can condense the remaining packs in commands that run for 1-3 min at a time. Since the daily packs range from 100-250mb, we will also combine and condense those packs.
This series is the same as v6 of an earlier series [1].
Thanks, -Stolee
[1] https://public-inbox.org/git/[email protected]/T/#u
Derrick Stolee (11):
repack: refactor pack deletion for future use
Docs: rearrange subcommands for multi-pack-index
multi-pack-index: prepare for 'expire' subcommand
midx: simplify computation of pack name lengths
midx: refactor permutation logic and pack sorting
multi-pack-index: implement 'expire' subcommand
multi-pack-index: prepare 'repack' subcommand
midx: implement midx_repack()
multi-pack-index: test expire while adding packs
midx: add test that 'expire' respects .keep files t5319-multi-pack-index.sh: test batch size zero
Documentation/git-multi-pack-index.txt | 32 +- builtin/multi-pack-index.c | 14 +- builtin/repack.c | 14 +- midx.c | 440 +++++++++++++++++++------ midx.h | 2 + packfile.c | 28 ++ packfile.h | 7 + t/t5319-multi-pack-index.sh | 184 +++++++++++ 8 files changed, 602 insertions(+), 119 deletions(-)
base-commit: af96fe3392fb078cb5447bcb94f2ed8d79d0a4a8
Published-As: https://github.com/gitgitgadget/git/releases/tag/pr-261%2Fderrickstolee%2Fmidx-expire%2Fupstream-v1
Fetch-It-Via: git fetch https://github.com/gitgitgadget/git pr-261/derrickstolee/midx-expire/upstream-v1
Pull-Request: https://github.com/gitgitgadget/git/pull/261
Signature · 1 line
gitgitgadget
PATCH 1/11 repack: refactor pack deletion for future use
From: Derrick Stolee <[email protected]>
The repack builtin deletes redundant pack-files and their associated .idx, .promisor, .bitmap, and .keep files. We will want to re-use this logic in the future for other types of repack, so pull the logic into 'unlink_pack_path()' in packfile.c.
The 'ignore_keep' parameter is enabled for the use in repack, but will be important for a future caller.
--- builtin/repack.c | 14 ++------------ packfile.c | 28 ++++++++++++++++++++++++++++ packfile.h | 7 +++++++ 3 files changed, 37 insertions(+), 12 deletions(-)
builtin/repack.c +2 -12
@@ -129,19 +129,9 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list,static void remove_redundant_pack (const char *dir_name,const char *base_name) { -const char *exts[] = {".pack" ,".idx" ,".keep" ,".bitmap" ,".promisor" }; -int i;struct strbuf buf =STRBUF_INIT ; -size_t plen; - -strbuf_addf (&buf,"%s/%s" , dir_name, base_name); - plen = buf.len; - -for (i =0 ; i <ARRAY_SIZE (exts); i++) { -strbuf_setlen (&buf, plen); -strbuf_addstr (&buf, exts[i]); -unlink (buf.buf); - } +strbuf_addf (&buf,"%s/%s.pack" , dir_name, base_name); +unlink_pack_path (buf.buf,1 );strbuf_release (&buf); }
packfile.c +28 -0
@@ -352,6 +352,34 @@ void close_all_packs(struct raw_object_store *o) } } +void unlink_pack_path (const char *pack_name,int force_delete) +{ +static const char *exts[] = {".pack" ,".idx" ,".keep" ,".bitmap" ,".promisor" }; +int i; +struct strbuf buf =STRBUF_INIT ; +size_t plen; + +strbuf_addstr (&buf, pack_name); +strip_suffix_mem (buf.buf, &buf.len,".pack" ); + plen = buf.len; + +if (!force_delete) { +strbuf_addstr (&buf,".keep" ); +if (!access (buf.buf,F_OK )) { +strbuf_release (&buf); +return ; + } + } + +for (i =0 ; i <ARRAY_SIZE (exts); i++) { +strbuf_setlen (&buf, plen); +strbuf_addstr (&buf, exts[i]); +unlink (buf.buf); + } + +strbuf_release (&buf); +} +/* * The LRU pack is the one with the oldest MRU window, preferring packs * with no used windows, or the oldest mtime if it has no windows allocated.
packfile.h +7 -0
@@ -94,6 +94,13 @@ void unuse_pack(struct pack_window **);void clear_delta_base_cache (void );struct packed_git *add_packed_git (const char *path,size_t path_len,int local); +/* +* Unlink the .pack and associated extension files. +* Does not unlink if 'force_delete' is false and the pack-file is +* marked as ".keep". +*/ +extern void unlink_pack_path (const char *pack_name,int force_delete); +/* * Make sure that a pointer access into an mmap'd index file is within bounds, * and can provide at least 8 bytes of data.
Signature · 1 line
gitgitgadget
PATCH 2/11 Docs: rearrange subcommands for multi-pack-index
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
We will add new subcommands to the multi-pack-index, and that will make the documentation a bit messier. Clean up the 'verb' descriptions by renaming the concept to 'subcommand' and removing the reference to the object directory.
--- Documentation/git-multi-pack-index.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-)
Documentation/git-multi-pack-index.txt +5 -5
@@ -9,7 +9,7 @@ git-multi-pack-index - Write and verify multi-pack-indexes SYNOPSIS -------- [verse] -'git multi-pack-index' [--object-dir=<dir>] <verb> +'git multi-pack-index' [--object-dir=<dir>] <subcommand> DESCRIPTION -----------
@@ -23,13 +23,13 @@ OPTIONS `<dir>/packs/multi-pack-index` for the current MIDX file, and `<dir>/packs` for the pack-files to index. +The following subcommands are available: + write:: - When given as the verb, write a new MIDX file to - `<dir>/packs/multi-pack-index`. + Write a new MIDX file. verify:: - When given as the verb, verify the contents of the MIDX file - at `<dir>/packs/multi-pack-index`. + Verify the contents of the MIDX file. EXAMPLES
Signature · 1 line
gitgitgadget
PATCH 3/11 multi-pack-index: prepare for 'expire' subcommand
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
The multi-pack-index tracks objects in a collection of pack-files. Only one copy of each object is indexed, using the modified time of the pack-files to determine tie-breakers. It is possible to have a pack-file with no referenced objects because all objects have a duplicate in a newer pack-file.
Introduce a new 'expire' subcommand to the multi-pack-index builtin. This subcommand will delete these unused pack-files and rewrite the multi-pack-index to no longer refer to those files. More details about the specifics will follow as the method is implemented.
Add a test that verifies the 'expire' subcommand is correctly wired, but will still be valid when the verb is implemented. Specifically, create a set of packs that should all have referenced objects and should not be removed during an 'expire' operation. The packs are created carefully to ensure they have a specific order when sorted by size. This will be important in a later test.
--- Documentation/git-multi-pack-index.txt | 5 +++ builtin/multi-pack-index.c | 4 ++- midx.c | 5 +++ midx.h | 1 + t/t5319-multi-pack-index.sh | 49 ++++++++++++++++++++++++++ 5 files changed, 63 insertions(+), 1 deletion(-)
Documentation/git-multi-pack-index.txt +5 -0
@@ -31,6 +31,11 @@ write:: verify:: Verify the contents of the MIDX file. +expire:: + Delete the pack-files that are tracked by the MIDX file, but + have no objects referenced by the MIDX. Rewrite the MIDX file + afterward to remove all references to these pack-files. + EXAMPLES --------
builtin/multi-pack-index.c +3 -1
@@ -6,7 +6,7 @@#include "trace2.h" static char const *const builtin_multi_pack_index_usage[] = { -N_ ("git multi-pack-index [--object-dir=<dir>] (write|verify)" ), +N_ ("git multi-pack-index [--object-dir=<dir>] (write|verify|expire)" ),NULL };
@@ -47,6 +47,8 @@ int cmd_multi_pack_index(int argc, const char **argv,return write_midx_file (opts.object_dir);if (!strcmp (argv[0 ],"verify" ))return verify_midx_file (the_repository, opts.object_dir); +if (!strcmp (argv[0 ],"expire" )) +return expire_midx_packs (the_repository, opts.object_dir);die (_ ("unrecognized verb: %s" ), argv[0 ]); }
midx.c +5 -0
@@ -1140,3 +1140,8 @@ int verify_midx_file(struct repository *r, const char *object_dir)return verify_midx_error; } + +int expire_midx_packs (struct repository *r,const char *object_dir) +{ +return 0 ; +}
midx.h +1 -0
@@ -50,6 +50,7 @@ int prepare_multi_pack_index_one(struct repository *r, const char *object_dir, iint write_midx_file (const char *object_dir);void clear_midx_file (struct repository *r);int verify_midx_file (struct repository *r,const char *object_dir); +int expire_midx_packs (struct repository *r,const char *object_dir);void close_midx (struct multi_pack_index *m);
t/t5319-multi-pack-index.sh +49 -0
53 lines not on this page · raw message
Signature · 1 line
gitgitgadget
PATCH 4/11 midx: simplify computation of pack name lengths
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
Before writing the multi-pack-index, we compute the length of the pack-index names concatenated together. This forms the data in the pack name chunk, and we precompute it to compute chunk offsets. The value is also modified to fit alignment needs.
Previously, this computation was coupled with adding packs from the existing multi-pack-index and the remaining packs in the object dir not already covered by the multi-pack-index.
In anticipation of this becoming more complicated with the 'expire' subcommand, simplify the computation by centralizing it to a single loop before writing the file.
--- midx.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-)
midx.c +9 -9
@@ -433,7 +433,6 @@ struct pack_list {uint32_t nr;uint32_t alloc_list;uint32_t alloc_names; -size_t pack_name_concat_len;struct multi_pack_index *m; };
@@ -468,7 +467,6 @@ static void add_pack_to_midx(const char *full_path, size_t full_path_len, } packs->names[packs->nr] =xstrdup (file_name); - packs->pack_name_concat_len +=strlen (file_name) +1 ; packs->nr++; } }
@@ -812,6 +810,7 @@ int write_midx_file(const char *object_dir)uint32_t nr_entries, num_large_offsets =0 ;struct pack_midx_entry *entries =NULL ;int large_offsets_needed =0 ; +int pack_name_concat_len =0 ; midx_name =get_midx_filename (object_dir);if (safe_create_leading_directories (midx_name)) {
@@ -827,7 +826,6 @@ int write_midx_file(const char *object_dir) packs.alloc_names = packs.alloc_list; packs.list =NULL ; packs.names =NULL ; - packs.pack_name_concat_len =0 ;ALLOC_ARRAY (packs.list, packs.alloc_list);ALLOC_ARRAY (packs.names, packs.alloc_names);
@@ -838,7 +836,6 @@ int write_midx_file(const char *object_dir) packs.list[packs.nr] =NULL ; packs.names[packs.nr] =xstrdup (packs.m->pack_names[i]); - packs.pack_name_concat_len +=strlen (packs.names[packs.nr]) +1 ; packs.nr++; } }
@@ -848,10 +845,6 @@ int write_midx_file(const char *object_dir)if (packs.m && packs.nr == packs.m->num_packs) goto cleanup; -if (packs.pack_name_concat_len %MIDX_CHUNK_ALIGNMENT ) - packs.pack_name_concat_len +=MIDX_CHUNK_ALIGNMENT - - (packs.pack_name_concat_len %MIDX_CHUNK_ALIGNMENT ); -ALLOC_ARRAY (pack_perm, packs.nr);sort_packs_by_name (packs.names, packs.nr, pack_perm);
@@ -864,6 +857,13 @@ int write_midx_file(const char *object_dir) large_offsets_needed =1 ; } +for (i =0 ; i < packs.nr; i++) + pack_name_concat_len +=strlen (packs.names[i]) +1 ; + +if (pack_name_concat_len %MIDX_CHUNK_ALIGNMENT ) + pack_name_concat_len +=MIDX_CHUNK_ALIGNMENT - + (pack_name_concat_len %MIDX_CHUNK_ALIGNMENT ); +hold_lock_file_for_update (&lk, midx_name,LOCK_DIE_ON_ERROR ); f =hashfd (lk.tempfile->fd, lk.tempfile->filename.buf);FREE_AND_NULL (midx_name);
@@ -881,7 +881,7 @@ int write_midx_file(const char *object_dir) cur_chunk++; chunk_ids[cur_chunk] =MIDX_CHUNKID_OIDFANOUT ; - chunk_offsets[cur_chunk] = chunk_offsets[cur_chunk -1 ] + packs.pack_name_concat_len; + chunk_offsets[cur_chunk] = chunk_offsets[cur_chunk -1 ] + pack_name_concat_len; cur_chunk++; chunk_ids[cur_chunk] =MIDX_CHUNKID_OIDLOOKUP ;
Signature · 1 line
gitgitgadget
PATCH 5/11 midx: refactor permutation logic and pack sorting
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
In anticipation of the expire subcommand, refactor the way we sort the packfiles by name. This will greatly simplify our approach to dropping expired packs from the list.
First, create 'struct pack_info' to replace 'struct pack_pair'. This struct contains the necessary information about a pack, including its name, a pointer to its packfile struct (if not already in the multi-pack-index), and the original pack-int-id.
Second, track the pack information using an array of pack_info structs in the pack_list struct. This simplifies the logic around the multiple arrays we were tracking in that struct.
Finally, update get_sorted_entries() to not permute the pack-int-id and instead supply the permutation to write_midx_object_offsets(). This requires sorting the packs after get_sorted_entries().
--- midx.c | 156 +++++++++++++++++++++++++-------------------------------- 1 file changed, 69 insertions(+), 87 deletions(-)
midx.c +69 -87
@@ -427,12 +427,23 @@ static size_t write_midx_header(struct hashfile *f,return MIDX_HEADER_SIZE ; } +struct pack_info { +uint32_t orig_pack_int_id; +char *pack_name; +struct packed_git *p; +}; + +static int pack_info_compare (const void *_a,const void *_b) +{ +struct pack_info *a = (struct pack_info *)_a; +struct pack_info *b = (struct pack_info *)_b; +return strcmp (a->pack_name, b->pack_name); +} +struct pack_list { -struct packed_git **list; -char **names; +struct pack_info *info;uint32_t nr; -uint32_t alloc_list; -uint32_t alloc_names; +uint32_t alloc;struct multi_pack_index *m; };
@@ -445,66 +456,32 @@ static void add_pack_to_midx(const char *full_path, size_t full_path_len,if (packs->m &&midx_contains_pack (packs->m, file_name))return ; -ALLOC_GROW (packs->list, packs->nr +1 , packs->alloc_list); -ALLOC_GROW (packs->names, packs->nr +1 , packs->alloc_names); +ALLOC_GROW (packs->info, packs->nr +1 , packs->alloc); - packs->list[packs->nr] =add_packed_git (full_path, - full_path_len, -0 ); + packs->info[packs->nr].p =add_packed_git (full_path, + full_path_len, +0 ); -if (!packs->list[packs->nr]) { +if (!packs->info[packs->nr].p) {warning (_ ("failed to add packfile '%s'" ), full_path);return ; } -if (open_pack_index (packs->list[packs->nr])) { +if (open_pack_index (packs->info[packs->nr].p)) {warning (_ ("failed to open pack-index '%s'" ), full_path); -close_pack (packs->list[packs->nr]); -FREE_AND_NULL (packs->list[packs->nr]); +close_pack (packs->info[packs->nr].p); +FREE_AND_NULL (packs->info[packs->nr].p);return ; } - packs->names[packs->nr] =xstrdup (file_name); + packs->info[packs->nr].pack_name =xstrdup (file_name); + packs->info[packs->nr].orig_pack_int_id = packs->nr; packs->nr++; } } -struct pack_pair { -uint32_t pack_int_id; -char *pack_name; -}; - -static int pack_pair_compare (const void *_a,const void *_b) -{ -struct pack_pair *a = (struct pack_pair *)_a; -struct pack_pair *b = (struct pack_pair *)_b; -return strcmp (a->pack_name, b->pack_name); -} - -static void sort_packs_by_name (char **pack_names,uint32_t nr_packs,uint32_t *perm) -{ -uint32_t i; -struct pack_pair *pairs; - -ALLOC_ARRAY (pairs, nr_packs); - -for (i =0 ; i < nr_packs; i++) { - pairs[i].pack_int_id = i; - pairs[i].pack_name = pack_names[i]; - } - -QSORT (pairs, nr_packs, pack_pair_compare); - -for (i =0 ; i < nr_packs; i++) { - pack_names[i] = pairs[i].pack_name; - perm[pairs[i].pack_int_id] = i; - } - -free (pairs); -} -struct pack_midx_entry {struct object_id oid;uint32_t pack_int_id;
@@ -530,7 +507,6 @@ static int midx_oid_compare(const void *_a, const void *_b) }static int nth_midxed_pack_midx_entry (struct multi_pack_index *m, -uint32_t *pack_perm,struct pack_midx_entry *e,uint32_t pos) {
@@ -538,7 +514,7 @@ static int nth_midxed_pack_midx_entry(struct multi_pack_index *m,return 1 ;nth_midxed_object_oid (&e->oid, m, pos); - e->pack_int_id = pack_perm[nth_midxed_pack_int_id (m, pos)]; + e->pack_int_id =nth_midxed_pack_int_id (m, pos); e->offset =nth_midxed_offset (m, pos);/* consider objects in midx to be from "old" packs */
@@ -572,8 +548,7 @@ static void fill_pack_entry(uint32_t pack_int_id,* of a packfile containing the object). */ static struct pack_midx_entry *get_sorted_entries (struct multi_pack_index *m, -struct packed_git **p, -uint32_t *perm, +struct pack_info *info,uint32_t nr_packs,uint32_t *nr_objects) {
@@ -584,7 +559,7 @@ static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,uint32_t start_pack = m ? m->num_packs :0 ;for (cur_pack = start_pack; cur_pack < nr_packs; cur_pack++) - total_objects += p[cur_pack]->num_objects; + total_objects += info[cur_pack].p->num_objects;/* * As we de-duplicate by fanout value, we expect the fanout
@@ -609,7 +584,7 @@ static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,for (cur_object = start; cur_object < end; cur_object++) {ALLOC_GROW (entries_by_fanout, nr_fanout +1 , alloc_fanout); -nth_midxed_pack_midx_entry (m, perm, +nth_midxed_pack_midx_entry (m, &entries_by_fanout[nr_fanout], cur_object); nr_fanout++;
@@ -620,12 +595,12 @@ static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m,uint32_t start =0 , end;if (cur_fanout) - start =get_pack_fanout (p[cur_pack], cur_fanout -1 ); - end =get_pack_fanout (p[cur_pack], cur_fanout); + start =get_pack_fanout (info[cur_pack].p, cur_fanout -1 ); + end =get_pack_fanout (info[cur_pack].p, cur_fanout);for (cur_object = start; cur_object < end; cur_object++) {ALLOC_GROW (entries_by_fanout, nr_fanout +1 , alloc_fanout); -fill_pack_entry (perm[cur_pack], p[cur_pack], cur_object, &entries_by_fanout[nr_fanout]); +fill_pack_entry (cur_pack, info[cur_pack].p, cur_object, &entries_by_fanout[nr_fanout]); nr_fanout++; } }
@@ -654,7 +629,7 @@ static struct pack_midx_entry *get_sorted_entries(struct multi_pack_index *m, }static size_t write_midx_pack_names (struct hashfile *f, -char **pack_names, +struct pack_info *info,uint32_t num_packs) {uint32_t i;
@@ -662,14 +637,14 @@ static size_t write_midx_pack_names(struct hashfile *f,size_t written =0 ;for (i =0 ; i < num_packs; i++) { -size_t writelen =strlen (pack_names[i]) +1 ; +size_t writelen =strlen (info[i].pack_name) +1 ; -if (i &&strcmp (pack_names[i], pack_names[i -1 ]) <=0 ) +if (i &&strcmp (info[i].pack_name, info[i -1 ].pack_name) <=0 )BUG ("incorrect pack-file order: %s before %s" , - pack_names[i -1 ], - pack_names[i]); + info[i -1 ].pack_name, + info[i].pack_name); -hashwrite (f, pack_names[i], writelen); +hashwrite (f, info[i].pack_name, writelen); written += writelen; }
@@ -740,6 +715,7 @@ static size_t write_midx_oid_lookup(struct hashfile *f, unsigned char hash_len, }static size_t write_midx_object_offsets (struct hashfile *f,int large_offset_needed, +uint32_t *perm,struct pack_midx_entry *objects,uint32_t nr_objects) {struct pack_midx_entry *list = objects;
@@ -749,7 +725,7 @@ static size_t write_midx_object_offsets(struct hashfile *f, int large_offset_neefor (i =0 ; i < nr_objects; i++) {struct pack_midx_entry *obj = list++; -hashwrite_be32 (f, obj->pack_int_id); +hashwrite_be32 (f, perm[obj->pack_int_id]);if (large_offset_needed && obj->offset >>31 )hashwrite_be32 (f,MIDX_LARGE_OFFSET_NEEDED | nr_large_offset++);
@@ -822,20 +798,17 @@ int write_midx_file(const char *object_dir) packs.m =load_multi_pack_index (object_dir,1 ); packs.nr =0 ; - packs.alloc_list = packs.m ? packs.m->num_packs :16 ; - packs.alloc_names = packs.alloc_list; - packs.list =NULL ; - packs.names =NULL ; -ALLOC_ARRAY (packs.list, packs.alloc_list); -ALLOC_ARRAY (packs.names, packs.alloc_names); + packs.alloc = packs.m ? packs.m->num_packs :16 ; + packs.info =NULL ; +ALLOC_ARRAY (packs.info, packs.alloc);if (packs.m) {for (i =0 ; i < packs.m->num_packs; i++) { -ALLOC_GROW (packs.list, packs.nr +1 , packs.alloc_list); -ALLOC_GROW (packs.names, packs.nr +1 , packs.alloc_names); +ALLOC_GROW (packs.info, packs.nr +1 , packs.alloc); - packs.list[packs.nr] =NULL ; - packs.names[packs.nr] =xstrdup (packs.m->pack_names[i]); + packs.info[packs.nr].orig_pack_int_id = i; + packs.info[packs.nr].pack_name =xstrdup (packs.m->pack_names[i]); + packs.info[packs.nr].p =NULL ; packs.nr++; } }
@@ -845,10 +818,7 @@ int write_midx_file(const char *object_dir)if (packs.m && packs.nr == packs.m->num_packs) goto cleanup; -ALLOC_ARRAY (pack_perm, packs.nr); -sort_packs_by_name (packs.names, packs.nr, pack_perm); - - entries =get_sorted_entries (packs.m, packs.list, pack_perm, packs.nr, &nr_entries); + entries =get_sorted_entries (packs.m, packs.info, packs.nr, &nr_entries);for (i =0 ; i < nr_entries; i++) {if (entries[i].offset >0x7fffffff )
@@ -857,8 +827,21 @@ int write_midx_file(const char *object_dir) large_offsets_needed =1 ; } +QSORT (packs.info, packs.nr, pack_info_compare); + +/* +* pack_perm stores a permutation between pack-int-ids from the +* previous multi-pack-index to the new one we are writing: +* +* pack_perm[old_id] = new_id +*/ +ALLOC_ARRAY (pack_perm, packs.nr); +for (i =0 ; i < packs.nr; i++) { + pack_perm[packs.info[i].orig_pack_int_id] = i; + } +for (i =0 ; i < packs.nr; i++) - pack_name_concat_len +=strlen (packs.names[i]) +1 ; + pack_name_concat_len +=strlen (packs.info[i].pack_name) +1 ;if (pack_name_concat_len %MIDX_CHUNK_ALIGNMENT ) pack_name_concat_len += MIDX_CHUNK_ALIGNMENT -
@@ -929,7 +912,7 @@ int write_midx_file(const char *object_dir)switch (chunk_ids[i]) {case MIDX_CHUNKID_PACKNAMES : - written +=write_midx_pack_names (f, packs.names, packs.nr); + written +=write_midx_pack_names (f, packs.info, packs.nr);break ;case MIDX_CHUNKID_OIDFANOUT :
@@ -941,7 +924,7 @@ int write_midx_file(const char *object_dir)break ;case MIDX_CHUNKID_OBJECTOFFSETS : - written +=write_midx_object_offsets (f, large_offsets_needed, entries, nr_entries); + written +=write_midx_object_offsets (f, large_offsets_needed, pack_perm, entries, nr_entries);break ;case MIDX_CHUNKID_LARGEOFFSETS :
@@ -964,15 +947,14 @@ int write_midx_file(const char *object_dir) cleanup:for (i =0 ; i < packs.nr; i++) { -if (packs.list[i]) { -close_pack (packs.list[i]); -free (packs.list[i]); +if (packs.info[i].p) { +close_pack (packs.info[i].p); +free (packs.info[i].p); } -free (packs.names[i]); +free (packs.info[i].pack_name); } -free (packs.list); -free (packs.names); +free (packs.info);free (entries);free (pack_perm);free (midx_name);
Signature · 1 line
gitgitgadget
PATCH 6/11 multi-pack-index: implement 'expire' subcommand
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
The 'git multi-pack-index expire' subcommand looks at the existing mult-pack-index, counts the number of objects referenced in each pack-file, deletes the pack-fils with no referenced objects, and rewrites the multi-pack-index to no longer reference those packs.
Refactor the write_midx_file() method to call write_midx_internal() which now takes an existing 'struct multi_pack_index' and a list of pack-files to drop (as specified by the names of their pack- indexes). As we write the new multi-pack-index, we drop those file names from the list of known pack-files.
The expire_midx_packs() method removes the unreferenced pack-files after carefully closing the packs to avoid open handles.
Test that a new pack-file that covers the contents of two other pack-files leads to those pack-files being deleted during the expire subcommand. Be sure to read the multi-pack-index to ensure it no longer references those packs.
--- midx.c | 119 +++++++++++++++++++++++++++++++++--- t/t5319-multi-pack-index.sh | 20 ++++++ 2 files changed, 129 insertions(+), 10 deletions(-)
midx.c +109 -10
@@ -34,6 +34,8 @@#define MIDX_CHUNK_LARGE_OFFSET_WIDTH (sizeof(uint64_t))#define MIDX_LARGE_OFFSET_NEEDED 0x80000000 +#define PACK_EXPIRED UINT_MAX +static char *get_midx_filename (const char *object_dir) {return xstrfmt ("%s/pack/multi-pack-index" , object_dir);
@@ -431,6 +433,7 @@ struct pack_info {uint32_t orig_pack_int_id;char *pack_name;struct packed_git *p; + unsigned expired : 1; };static int pack_info_compare (const void *_a,const void *_b)
@@ -478,6 +481,7 @@ static void add_pack_to_midx(const char *full_path, size_t full_path_len, packs->info[packs->nr].pack_name =xstrdup (file_name); packs->info[packs->nr].orig_pack_int_id = packs->nr; + packs->info[packs->nr].expired =0 ; packs->nr++; } }
@@ -637,13 +641,17 @@ static size_t write_midx_pack_names(struct hashfile *f,size_t written =0 ;for (i =0 ; i < num_packs; i++) { -size_t writelen =strlen (info[i].pack_name) +1 ; +size_t writelen; + +if (info[i].expired) +continue ;if (i &&strcmp (info[i].pack_name, info[i -1 ].pack_name) <=0 )BUG ("incorrect pack-file order: %s before %s" , info[i -1 ].pack_name, info[i].pack_name); + writelen =strlen (info[i].pack_name) +1 ;hashwrite (f, info[i].pack_name, writelen); written += writelen; }
@@ -725,6 +733,11 @@ static size_t write_midx_object_offsets(struct hashfile *f, int large_offset_neefor (i =0 ; i < nr_objects; i++) {struct pack_midx_entry *obj = list++; +if (perm[obj->pack_int_id] ==PACK_EXPIRED ) +BUG ("object %s is in an expired pack with int-id %d" , +oid_to_hex (&obj->oid), + obj->pack_int_id); +hashwrite_be32 (f, perm[obj->pack_int_id]);if (large_offset_needed && obj->offset >>31 )
@@ -771,7 +784,8 @@ static size_t write_midx_large_offsets(struct hashfile *f, uint32_t nr_large_offreturn written; } -int write_midx_file (const char *object_dir) +static int write_midx_internal (const char *object_dir,struct multi_pack_index *m, +struct string_list *packs_to_drop) {unsigned char cur_chunk, num_chunks =0 ;char *midx_name;
@@ -787,6 +801,8 @@ int write_midx_file(const char *object_dir)struct pack_midx_entry *entries =NULL ;int large_offsets_needed =0 ;int pack_name_concat_len =0 ; +int dropped_packs =0 ; +int result =0 ; midx_name =get_midx_filename (object_dir);if (safe_create_leading_directories (midx_name)) {
@@ -795,7 +811,10 @@ int write_midx_file(const char *object_dir) midx_name); } - packs.m =load_multi_pack_index (object_dir,1 ); +if (m) + packs.m = m; +else + packs.m =load_multi_pack_index (object_dir,1 ); packs.nr =0 ; packs.alloc = packs.m ? packs.m->num_packs :16 ;
@@ -809,13 +828,14 @@ int write_midx_file(const char *object_dir) packs.info[packs.nr].orig_pack_int_id = i; packs.info[packs.nr].pack_name =xstrdup (packs.m->pack_names[i]); packs.info[packs.nr].p =NULL ; + packs.info[packs.nr].expired =0 ; packs.nr++; } }for_each_file_in_pack_dir (object_dir, add_pack_to_midx, &packs); -if (packs.m && packs.nr == packs.m->num_packs) +if (packs.m && packs.nr == packs.m->num_packs && !packs_to_drop) goto cleanup; entries =get_sorted_entries (packs.m, packs.info, packs.nr, &nr_entries);
@@ -829,6 +849,34 @@ int write_midx_file(const char *object_dir)QSORT (packs.info, packs.nr, pack_info_compare); +if (packs_to_drop && packs_to_drop->nr) { +int drop_index =0 ; +int missing_drops =0 ; + +for (i =0 ; i < packs.nr && drop_index < packs_to_drop->nr; i++) { +int cmp =strcmp (packs.info[i].pack_name, + packs_to_drop->items[drop_index].string); + +if (!cmp) { + drop_index++; + packs.info[i].expired =1 ; + }else if (cmp >0 ) { +error (_ ("did not see pack-file %s to drop" ), + packs_to_drop->items[drop_index].string); + drop_index++; + missing_drops++; + i--; + }else { + packs.info[i].expired =0 ; + } + } + +if (missing_drops) { + result =1 ; + goto cleanup; + } + } +/* * pack_perm stores a permutation between pack-int-ids from the * previous multi-pack-index to the new one we are writing:
@@ -837,11 +885,18 @@ int write_midx_file(const char *object_dir)*/ ALLOC_ARRAY (pack_perm, packs.nr);for (i =0 ; i < packs.nr; i++) { - pack_perm[packs.info[i].orig_pack_int_id] = i; +if (packs.info[i].expired) { + dropped_packs++; + pack_perm[packs.info[i].orig_pack_int_id] =PACK_EXPIRED ; + }else { + pack_perm[packs.info[i].orig_pack_int_id] = i - dropped_packs; + } } -for (i =0 ; i < packs.nr; i++) - pack_name_concat_len +=strlen (packs.info[i].pack_name) +1 ; +for (i =0 ; i < packs.nr; i++) { +if (!packs.info[i].expired) + pack_name_concat_len +=strlen (packs.info[i].pack_name) +1 ; + }if (pack_name_concat_len %MIDX_CHUNK_ALIGNMENT ) pack_name_concat_len += MIDX_CHUNK_ALIGNMENT -
@@ -857,7 +912,7 @@ int write_midx_file(const char *object_dir) cur_chunk =0 ; num_chunks = large_offsets_needed ?5 :4 ; - written =write_midx_header (f, num_chunks, packs.nr); + written =write_midx_header (f, num_chunks, packs.nr - dropped_packs); chunk_ids[cur_chunk] =MIDX_CHUNKID_PACKNAMES ; chunk_offsets[cur_chunk] = written + (num_chunks +1 ) *MIDX_CHUNKLOOKUP_WIDTH ;
@@ -958,7 +1013,12 @@ int write_midx_file(const char *object_dir)free (entries);free (pack_perm);free (midx_name); -return 0 ; +return result; +} + +int write_midx_file (const char *object_dir) +{ +return write_midx_internal (object_dir,NULL ,NULL ); }void clear_midx_file (struct repository *r)
@@ -1125,5 +1185,44 @@ int verify_midx_file(struct repository *r, const char *object_dir)int expire_midx_packs (struct repository *r,const char *object_dir) { -return 0 ; +uint32_t i, *count, result =0 ; +struct string_list packs_to_drop =STRING_LIST_INIT_DUP ; +struct multi_pack_index *m =load_multi_pack_index (object_dir,1 ); + +if (!m) +return 0 ; + + count =xcalloc (m->num_packs,sizeof (uint32_t )); +for (i =0 ; i < m->num_objects; i++) { +int pack_int_id =nth_midxed_pack_int_id (m, i); + count[pack_int_id]++; + } + +for (i =0 ; i < m->num_packs; i++) { +char *pack_name; + +if (count[i]) +continue ; + +if (prepare_midx_pack (r, m, i)) +continue ; + +if (m->packs[i]->pack_keep) +continue ; + + pack_name =xstrdup (m->packs[i]->pack_name); +close_pack (m->packs[i]); + +string_list_insert (&packs_to_drop, m->pack_names[i]); +unlink_pack_path (pack_name,0 ); +free (pack_name); + } + +free (count); + +if (packs_to_drop.nr) + result =write_midx_internal (object_dir, m, &packs_to_drop); + +string_list_clear (&packs_to_drop,0 ); +return result; }
t/t5319-multi-pack-index.sh +20 -0
@@ -412,4 +412,24 @@ test_expect_success 'expire does not remove any packs' ' ) ' +test_expect_success 'expire removes unreferenced packs' ' + ( + cd dup && + git pack-objects --revs .git/objects/pack/pack-combined <<-EOF && + refs/heads/A + ^refs/heads/C + EOF + git multi-pack-index write && + ls .git/objects/pack | grep -v -e pack-[AB] >expect && + git multi-pack-index expire && + ls .git/objects/pack >actual && + test_cmp expect actual && + ls .git/objects/pack/ | grep idx >expect-idx && + test-tool read-midx .git/objects | grep idx >actual-midx && + test_cmp expect-idx actual-midx && + git multi-pack-index verify && + git fsck + ) +' + test_done
Signature · 1 line
gitgitgadget
Re: [PATCH 06/11] multi-pack-index: implement 'expire' subcommand
Quoted 4 lines · Derrick Stolee via GitGitGadget
From: Derrick Stolee <[email protected]>
The 'git multi-pack-index expire' subcommand looks at the existing mult-pack-index, counts the number of objects referenced in each
s/mult/&i/
Quoted 1 line
pack-file, deletes the pack-fils with no referenced objects, and
s/fils/files/
Quoted 1 line
rewrites the multi-pack-index to no longer reference those packs.
As we do *not* want to expire a packfile that was created after the last run of "git multi-pack-index write", "... in each pack-file" in the above paragraph is a bit tricky. It cannot literally be "in each packfile", but a subset of all existing packfiles that are known to the midx we are looking at.
An obvious alternative is to actually update midx file (so it no longer "looks at the existing multi-pack-index", but "makes sure we have up-to-date view of the multi-pack-index that covers all packs).
And I actually suspect that you implemented that alternative here. IOW, "looks at the existing multi-pack-index" in the first paragraph is misleading.
I wondered if the error behaviour of add_pack_to_midx() that ignores a pack that cannot be added to midx for whatever reason can cause issues, but such a pack would not become part of m->packs[] so it may not have anything count[]ed from it, but it will not be eligible for expiration, either. So we'd be safe there, I think.
Quoted 4 lines
Test that a new pack-file that covers the contents of two other pack-files leads to those pack-files being deleted during the expire subcommand. Be sure to read the multi-pack-index to ensure it no longer references those packs.
I am curious to learn how we guarantee that the midx logic chooses to record the copy of an object in the new single packfile over the other copy of the same object in one of the two older and presumably smaller packfiles for all objects in the latter two packfiles, because they cannot be expired if even one object were used from there.
PATCH 7/11 multi-pack-index: prepare 'repack' subcommand
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
In an environment where the multi-pack-index is useful, it is due to many pack-files and an inability to repack the object store into a single pack-file. However, it is likely that many of these pack-files are rather small, and could be repacked into a slightly larger pack-file without too much effort. It may also be important to ensure the object store is highly available and the repack operation does not interrupt concurrent git commands.
Introduce a 'repack' subcommand to 'git multi-pack-index' that takes a '--batch-size' option. The subcommand will inspect the multi-pack-index for referenced pack-files whose size is smaller than the batch size, until collecting a list of pack-files whose sizes sum to larger than the batch size. Then, a new pack-file will be created containing the objects from those pack-files that are referenced by the multi-pack-index. The resulting pack is likely to actually be smaller than the batch size due to compression and the fact that there may be objects in the pack- files that have duplicate copies in other pack-files.
The current change introduces the command-line arguments, and we add a test that ensures we parse these options properly. Since we specify a small batch size, we will guarantee that future implementations do not change the list of pack-files.
In addition, we hard-code the modified times of the packs in the pack directory to ensure the list of packs sorted by modified time matches the order if sorted by size (ascending). This will be important in a future test.
--- Documentation/git-multi-pack-index.txt | 17 +++++++++++++++++ builtin/multi-pack-index.c | 12 ++++++++++-- midx.c | 5 +++++ midx.h | 1 + t/t5319-multi-pack-index.sh | 20 +++++++++++++++++++- 5 files changed, 52 insertions(+), 3 deletions(-)
Documentation/git-multi-pack-index.txt +17 -0
@@ -36,6 +36,23 @@ expire:: have no objects referenced by the MIDX. Rewrite the MIDX file afterward to remove all references to these pack-files. +repack:: + Create a new pack-file containing objects in small pack-files + referenced by the multi-pack-index. If the size given by the + `--batch-size=<size>` argument is zero, then create a pack + containing all objects referenced by the multi-pack-index. For + a non-zero batch size, Select the pack-files by examining packs + from oldest-to-newest, computing the "expected size" by counting + the number of objects in the pack referenced by the + multi-pack-index, then divide by the total number of objects in + the pack and multiply by the pack size. We select packs with + expected size below the batch size until the set of packs have + total expected size at least the batch size. If the total size + does not reach the batch size, then do nothing. If a new pack- + file is created, rewrite the multi-pack-index to reference the + new pack-file. A later run of 'git multi-pack-index expire' will + delete the pack-files that were part of this batch. + EXAMPLES --------
builtin/multi-pack-index.c +10 -2
@@ -6,12 +6,13 @@#include "trace2.h" static char const *const builtin_multi_pack_index_usage[] = { -N_ ("git multi-pack-index [--object-dir=<dir>] (write|verify|expire)" ), +N_ ("git multi-pack-index [--object-dir=<dir>] (write|verify|expire|repack --batch-size=<size>)" ),NULL };static struct opts_multi_pack_index {const char *object_dir; +unsigned long batch_size; } opts; int cmd_multi_pack_index(int argc, const char **argv,
@@ -20,6 +21,8 @@ int cmd_multi_pack_index(int argc, const char **argv,static struct option builtin_multi_pack_index_options[] = {OPT_FILENAME (0 ,"object-dir" , &opts.object_dir,N_ ("object directory containing set of packfile and pack-index pairs" )), +OPT_MAGNITUDE (0 ,"batch-size" , &opts.batch_size, +N_ ("during repack, collect pack-files of smaller size into a batch that is larger than this size" )),OPT_END (), };
@@ -43,6 +46,11 @@ int cmd_multi_pack_index(int argc, const char **argv,trace2_cmd_mode (argv[0 ]); +if (!strcmp (argv[0 ],"repack" )) +return midx_repack (the_repository, opts.object_dir, (size_t )opts.batch_size); +if (opts.batch_size) +die (_ ("--batch-size option is only for 'repack' subcommand" )); +if (!strcmp (argv[0 ],"write" ))return write_midx_file (opts.object_dir);if (!strcmp (argv[0 ],"verify" ))
@@ -50,5 +58,5 @@ int cmd_multi_pack_index(int argc, const char **argv,if (!strcmp (argv[0 ],"expire" ))return expire_midx_packs (the_repository, opts.object_dir); -die (_ ("unrecognized verb: %s" ), argv[0 ]); +die (_ ("unrecognized subcommand: %s" ), argv[0 ]); }
midx.c +5 -0
@@ -1226,3 +1226,8 @@ int expire_midx_packs(struct repository *r, const char *object_dir)string_list_clear (&packs_to_drop,0 );return result; } + +int midx_repack (struct repository *r,const char *object_dir,size_t batch_size) +{ +return 0 ; +}
midx.h +1 -0
@@ -51,6 +51,7 @@ int write_midx_file(const char *object_dir);void clear_midx_file (struct repository *r);int verify_midx_file (struct repository *r,const char *object_dir);int expire_midx_packs (struct repository *r,const char *object_dir); +int midx_repack (struct repository *r,const char *object_dir,size_t batch_size);void close_midx (struct multi_pack_index *m);
t/t5319-multi-pack-index.sh +19 -1
@@ -398,7 +398,8 @@ test_expect_success 'setup expire tests' ' git pack-objects --revs .git/objects/pack/pack-E <<-EOF && refs/heads/E EOF - git multi-pack-index write + git multi-pack-index write && + cp -r .git/objects/pack .git/objects/pack-backup ) '
@@ -432,4 +433,21 @@ test_expect_success 'expire removes unreferenced packs' ' ) ' +test_expect_success 'repack with minimum size does not alter existing packs' ' + ( + cd dup && + rm -rf .git/objects/pack && + mv .git/objects/pack-backup .git/objects/pack && + touch -m -t 201901010000 .git/objects/pack/pack-D* && + touch -m -t 201901010001 .git/objects/pack/pack-C* && + touch -m -t 201901010002 .git/objects/pack/pack-B* && + touch -m -t 201901010003 .git/objects/pack/pack-A* && + ls .git/objects/pack >expect && + MINSIZE=$(ls -l .git/objects/pack/*pack | awk "{print \$5;}" | sort -n | head -n 1) && + git multi-pack-index repack --batch-size=$MINSIZE && + ls .git/objects/pack >actual && + test_cmp expect actual + ) +' + test_done
Signature · 1 line
gitgitgadget
PATCH 8/11 midx: implement midx_repack()
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
To repack with a non-zero batch-size, first sort all pack-files by their modified time. Second, walk those pack-files from oldest to newest, compute their expected size, and add the packs to a list if they are smaller than the given batch-size. Stop when the total expected size is at least the batch size.
If the batch size is zero, select all packs in the multi-pack-index.
Finally, collect the objects from the multi-pack-index that are in the selected packs and send them to 'git pack-objects'. Write a new multi-pack-index that includes the new pack.
Using a batch size of zero is very similar to a standard 'git repack' command, except that we do not delete the old packs and instead rely on the new multi-pack-index to prevent new processes from reading the old packs. This does not disrupt other Git processes that are currently reading the old packs based on the old multi-pack-index.
While first designing a 'git multi-pack-index repack' operation, I started by collecting the batches based on the actual size of the objects instead of the size of the pack-files. This allows repacking a large pack-file that has very few referencd objects. However, this came at a significant cost of parsing pack-files instead of simply reading the multi-pack-index and getting the file information for the pack-files. The "expected size" version provides similar behavior, but could skip a pack-file if the average object size is much larger than the actual size of the referenced objects, or can create a large pack if the actual size of the referenced objects is larger than the expected size.
--- midx.c | 151 +++++++++++++++++++++++++++++++++++- t/t5319-multi-pack-index.sh | 28 +++++++ 2 files changed, 178 insertions(+), 1 deletion(-)
midx.c +150 -1
@@ -9,6 +9,7 @@#include "midx.h" #include "progress.h" #include "trace2.h" +#include "run-command.h" #define MIDX_SIGNATURE 0x4d494458/* "MIDX" */ #define MIDX_VERSION 1
@@ -1227,7 +1228,155 @@ int expire_midx_packs(struct repository *r, const char *object_dir)return result; } -int midx_repack (struct repository *r,const char *object_dir,size_t batch_size) +struct repack_info { +timestamp_t mtime; +uint32_t referenced_objects; +uint32_t pack_int_id; +}; + +static int compare_by_mtime (const void *a_,const void *b_) { +const struct repack_info *a, *b; + + a = (const struct repack_info *)a_; + b = (const struct repack_info *)b_; + +if (a->mtime < b->mtime) +return -1 ; +if (a->mtime > b->mtime) +return 1 ; +return 0 ; +} + +static int fill_included_packs_all (struct multi_pack_index *m, +unsigned char *include_pack) +{ +uint32_t i; + +for (i =0 ; i < m->num_packs; i++) + include_pack[i] =1 ; + +return m->num_packs <2 ; +} + +static int fill_included_packs_batch (struct repository *r, +struct multi_pack_index *m, +unsigned char *include_pack, +size_t batch_size) +{ +uint32_t i, packs_to_repack; +size_t total_size; +struct repack_info *pack_info =xcalloc (m->num_packs,sizeof (struct repack_info )); + +for (i =0 ; i < m->num_packs; i++) { + pack_info[i].pack_int_id = i; + +if (prepare_midx_pack (r, m, i)) +continue ; + + pack_info[i].mtime = m->packs[i]->mtime; + } + +for (i =0 ; batch_size && i < m->num_objects; i++) { +uint32_t pack_int_id =nth_midxed_pack_int_id (m, i); + pack_info[pack_int_id].referenced_objects++; + } + +QSORT (pack_info, m->num_packs, compare_by_mtime); + + total_size =0 ; + packs_to_repack =0 ; +for (i =0 ; total_size < batch_size && i < m->num_packs; i++) { +int pack_int_id = pack_info[i].pack_int_id; +struct packed_git *p = m->packs[pack_int_id]; +size_t expected_size; + +if (!p) +continue ; +if (open_pack_index (p) || !p->num_objects) +continue ; + + expected_size = (size_t )(p->pack_size + * pack_info[i].referenced_objects); + expected_size /= p->num_objects; + +if (expected_size >= batch_size) +continue ; + + packs_to_repack++; + total_size += expected_size; + include_pack[pack_int_id] =1 ; + } + +free (pack_info); + +if (total_size < batch_size || packs_to_repack <2 ) +return 1 ; +return 0 ; } + +int midx_repack (struct repository *r,const char *object_dir,size_t batch_size) +{ +int result =0 ; +uint32_t i; +unsigned char *include_pack; +struct child_process cmd =CHILD_PROCESS_INIT ; +struct strbuf base_name =STRBUF_INIT ; +struct multi_pack_index *m =load_multi_pack_index (object_dir,1 ); + +if (!m) +return 0 ; + + include_pack =xcalloc (m->num_packs,sizeof (unsigned char )); + +if (batch_size) { +if (fill_included_packs_batch (r, m, include_pack, batch_size)) + goto cleanup; + }else if (fill_included_packs_all (m, include_pack)) + goto cleanup; + +argv_array_push (&cmd.args,"pack-objects" ); + +strbuf_addstr (&base_name, object_dir); +strbuf_addstr (&base_name,"/pack/pack" ); +argv_array_push (&cmd.args, base_name.buf); +strbuf_release (&base_name); + + cmd.git_cmd =1 ; + cmd.in = cmd.out =-1 ; + +if (start_command (&cmd)) { +error (_ ("could not start pack-objects" )); + result =1 ; + goto cleanup; + } + +for (i =0 ; i < m->num_objects; i++) { +struct object_id oid; +uint32_t pack_int_id =nth_midxed_pack_int_id (m, i); + +if (!include_pack[pack_int_id]) +continue ; + +nth_midxed_object_oid (&oid, m, i); +xwrite (cmd.in,oid_to_hex (&oid), the_hash_algo->hexsz); +xwrite (cmd.in,"\n" ,1 ); + } +close (cmd.in); + +if (finish_command (&cmd)) { +error (_ ("could not finish pack-objects" )); + result =1 ; + goto cleanup; + } + + result =write_midx_internal (object_dir, m,NULL ); + m =NULL ; + +cleanup: +if (m) +close_midx (m); +free (include_pack); +return result; +}
t/t5319-multi-pack-index.sh +28 -0
@@ -450,4 +450,32 @@ test_expect_success 'repack with minimum size does not alter existing packs' ' ) ' +test_expect_success 'repack creates a new pack' ' + ( + cd dup && + ls .git/objects/pack/*idx >idx-list && + test_line_count = 5 idx-list && + THIRD_SMALLEST_SIZE=$(ls -l .git/objects/pack/*pack | awk "{print \$5;}" | sort -n | head -n 3 | tail -n 1) && + BATCH_SIZE=$(($THIRD_SMALLEST_SIZE + 1)) && + git multi-pack-index repack --batch-size=$BATCH_SIZE && + ls .git/objects/pack/*idx >idx-list && + test_line_count = 6 idx-list && + test-tool read-midx .git/objects | grep idx >midx-list && + test_line_count = 6 midx-list + ) +' + +test_expect_success 'expire removes repacked packs' ' + ( + cd dup && + ls -al .git/objects/pack/*pack && + ls -S .git/objects/pack/*pack | head -n 4 >expect && + git multi-pack-index expire && + ls -S .git/objects/pack/*pack >actual && + test_cmp expect actual && + test-tool read-midx .git/objects | grep idx >midx-list && + test_line_count = 4 midx-list + ) +' + test_done
Signature · 1 line
gitgitgadget
PATCH 9/11 multi-pack-index: test expire while adding packs
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
During development of the multi-pack-index expire subcommand, a version went out that improperly computed the pack order if a new pack was introduced while other packs were being removed. Part of the subtlety of the bug involved the new pack being placed before other packs that already existed in the multi-pack-index.
Add a test to t5319-multi-pack-index.sh that catches this issue. The test adds new packs that cause another pack to be expired, and creates new packs that are lexicographically sorted before and after the existing packs.
--- t/t5319-multi-pack-index.sh | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+)
t/t5319-multi-pack-index.sh +32 -0
@@ -478,4 +478,36 @@ test_expect_success 'expire removes repacked packs' ' ) ' +test_expect_success 'expire works when adding new packs' ' + ( + cd dup && + git pack-objects --revs .git/objects/pack/pack-combined <<-EOF && + refs/heads/A + ^refs/heads/B + EOF + git pack-objects --revs .git/objects/pack/pack-combined <<-EOF && + refs/heads/B + ^refs/heads/C + EOF + git pack-objects --revs .git/objects/pack/pack-combined <<-EOF && + refs/heads/C + ^refs/heads/D + EOF + git multi-pack-index write && + git pack-objects --revs .git/objects/pack/a-pack <<-EOF && + refs/heads/D + ^refs/heads/E + EOF + git multi-pack-index write && + git pack-objects --revs .git/objects/pack/z-pack <<-EOF && + refs/heads/E + EOF + git multi-pack-index expire && + ls .git/objects/pack/ | grep idx >expect && + test-tool read-midx .git/objects | grep idx >actual && + test_cmp expect actual && + git multi-pack-index verify + ) +' + test_done
Signature · 1 line
gitgitgadget
PATCH 10/11 midx: add test that 'expire' respects .keep files
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
The 'git multi-pack-index expire' subcommand may delete packs that are not needed from the perspective of the multi-pack-index. If a pack has a .keep file, then we should not delete that pack. Add a test that ensures we preserve a pack that would otherwise be expired. First, create a new pack that contains every object in the repo, then add it to the multi-pack-index. Then create a .keep file for a pack starting with "a-pack" that was added in the previous test. Finally, expire and verify that the pack remains and the other packs were expired.
--- t/t5319-multi-pack-index.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+)
t/t5319-multi-pack-index.sh +18 -0
@@ -510,4 +510,22 @@ test_expect_success 'expire works when adding new packs' ' ) ' +test_expect_success 'expire respects .keep files' ' + ( + cd dup && + git pack-objects --revs .git/objects/pack/pack-all <<-EOF && + refs/heads/A + EOF + git multi-pack-index write && + PACKA=$(ls .git/objects/pack/a-pack*\.pack | sed s/\.pack\$//) && + touch $PACKA.keep && + git multi-pack-index expire && + ls -S .git/objects/pack/a-pack* | grep $PACKA >a-pack-files && + test_line_count = 3 a-pack-files && + test-tool read-midx .git/objects | grep idx >midx-list && + test_line_count = 2 midx-list + ) +' + + test_done
Signature · 1 line
gitgitgadget
PATCH 11/11 t5319-multi-pack-index.sh: test batch size zero
In reply to Derrick Stolee via GitGitGadgetFrom: Derrick Stolee <[email protected]>
The 'git multi-pack-index repack' command can take a batch size of zero, which creates a new pack-file containing all objects in the multi-pack-index. The first 'repack' command will create one new pack-file, and an 'expire' command after that will delete the old pack-files, as they no longer contain any referenced objects in the multi-pack-index.
We must remove the .keep file that was added in the previous test in order to expire that pack-file.
Also test that a 'repack' will do nothing if there is only one pack-file.
--- t/t5319-multi-pack-index.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
t/t5319-multi-pack-index.sh +19 -0
@@ -527,5 +527,24 @@ test_expect_success 'expire respects .keep files' ' ) ' +test_expect_success 'repack --batch-size=0 repacks everything' ' + ( + cd dup && + rm .git/objects/pack/*.keep && + ls .git/objects/pack/*idx >idx-list && + test_line_count = 2 idx-list && + git multi-pack-index repack --batch-size=0 && + ls .git/objects/pack/*idx >idx-list && + test_line_count = 3 idx-list && + test-tool read-midx .git/objects | grep idx >midx-list && + test_line_count = 3 midx-list && + git multi-pack-index expire && + ls -al .git/objects/pack/*idx >idx-list && + test_line_count = 1 idx-list && + git multi-pack-index repack --batch-size=0 && + ls -al .git/objects/pack/*idx >new-idx-list && + test_cmp idx-list new-idx-list + ) +' test_done
Signature · 1 line
gitgitgadget
PATCH 1/1 t5319: don't trip over a user name with whitespace
In reply to Derrick Stolee via GitGitGadgetOn my Windows system where the POSIX commands are provided by MSYS2, I observe this output:
$ ls -l Makefile -rw-r--r-- 1 Johannes Sixt 197121 101780 Jun 30 09:33 Makefile
Notice the blank in the user name. Obviously, extracting the size of a file by counting columns won't work. But two tests in t5319 do that. Use the stat command to print just the file size.
---
Notice further that the group ID is presented numerically. The actual output used in the tests is:
-r--r--r-- 1 Johannes Sixt 197121 2239 Jan 1 01:03 .git/objects/pack/pack-A-40289997979ee9f2d7198a5b4ac9fe14e27cbe4b.pack -r--r--r-- 1 Johannes Sixt 197121 1385 Jan 1 01:02 .git/objects/pack/pack-B-926e20b0e641003b2f6bcddd6ccf53e7741c8642.pack -r--r--r-- 1 Johannes Sixt 197121 838 Jan 1 01:01 .git/objects/pack/pack-C-b33c048d7202659249bb0cea7bbc997ce448c75d.pack -r--r--r-- 1 Johannes Sixt 197121 560 Jan 1 01:00 .git/objects/pack/pack-D-49ca36507a52bbba6d1768e28666eb0f4e5f7d95.pack
-r--r--r-- 1 Johannes Sixt 197121 4587 Jun 30 16:17 .git/objects/pack/pack-E-31ea0966bb15206fbfd1b4c2f5be4b24a03050f5.pack
In both tests, this group ID was passed as --batch-size, but actually the first of the two tests does not fail.
Now I have to wonder: Why that? The test carefully attempts to extract the smallest pack size to pass as --batch-size argument. But when it passes some large number, the test still passes. Is this the intent?
That is, both a small and a large --batch-size satisfy 'does not alter existing packs', but some medium sized --batch-size satisfy the next test 'repack creates a new pack'. That is very curious.
Disclaimer: I've no clue about this multi-pack-index thing.
t/t5319-multi-pack-index.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
t/t5319-multi-pack-index.sh +2 -2
@@ -443,7 +443,7 @@ test_expect_success 'repack with minimum size does not alter existing packs' ' touch -m -t 201901010002 .git/objects/pack/pack-B* && touch -m -t 201901010003 .git/objects/pack/pack-A* && ls .git/objects/pack >expect && - MINSIZE=$(ls -l .git/objects/pack/*pack | awk "{print \$5;}" | sort -n | head -n 1) && + MINSIZE=$(stat -c %s .git/objects/pack/*pack | sort -n | head -n 1) && git multi-pack-index repack --batch-size=$MINSIZE && ls .git/objects/pack >actual && test_cmp expect actual
@@ -455,7 +455,7 @@ test_expect_success 'repack creates a new pack' ' cd dup && ls .git/objects/pack/*idx >idx-list && test_line_count = 5 idx-list && - THIRD_SMALLEST_SIZE=$(ls -l .git/objects/pack/*pack | awk "{print \$5;}" | sort -n | head -n 3 | tail -n 1) && + THIRD_SMALLEST_SIZE=$(stat -c %s .git/objects/pack/*pack | sort -n | head -n 3 | tail -n 1) && BATCH_SIZE=$(($THIRD_SMALLEST_SIZE + 1)) && git multi-pack-index repack --batch-size=$BATCH_SIZE && ls .git/objects/pack/*idx >idx-list &&
Signature · 1 line
2.21.0.285.gc38d92e052
Re: [PATCH] t5319: don't trip over a user name with whitespace
Quoted 14 lines · Johannes Sixt
On my Windows system where the POSIX commands are provided by MSYS2, I observe this output:
$ ls -l Makefile -rw-r--r-- 1 Johannes Sixt 197121 101780 Jun 30 09:33 MakefileNotice the blank in the user name. Obviously, extracting the size of a file by counting columns won't work. But two tests in t5319 do that. Use the stat command to print just the file size.
Signed-off-by Johannes Sixt <[email protected]>---
t/t5319-multi-pack-index.sh test_expect_success 'repack with minimum size does not alter existing packs' ' · line 443
Quoted 2 lines
t/t5319-multi-pack-index.sh +1
-1@@ -443,7 +443,7 @@ test_expect_success 'repack with minimum size does not alter existing packs' ' - MINSIZE=$(ls -l .git/objects/pack/*pack | awk "{print \$5;}" | sort -n | head -n 1) && + MINSIZE=$(stat -c %s .git/objects/pack/*pack | sort -n | head -n 1) &&
Unfortunately, this is not portable. While "stat -c %s" works on Linux and MSYS2, neither that option nor the format directive are recognized on BSD-like platforms (I tested Mac OS and FreeBSD), which instead need "stat -f %z".
Re: [PATCH] t5319: don't trip over a user name with whitespace
Am 30.06.19 um 21:48 schrieb Eric Sunshine:
Quoted 10 lines · Johannes Sixt
t/t5319-multi-pack-index.sh test_expect_success 'repack with minimum size does not alter existing packs' ' · line 443
Quoted 4 lines
t/t5319-multi-pack-index.sh +1
-1@@ -443,7 +443,7 @@ test_expect_success 'repack with minimum size does not alter existing packs' ' - MINSIZE=$(ls -l .git/objects/pack/*pack | awk "{print \$5;}" | sort -n | head -n 1) && + MINSIZE=$(stat -c %s .git/objects/pack/*pack | sort -n | head -n 1) &&Unfortunately, this is not portable. While "stat -c %s" works on Linux and MSYS2, neither that option nor the format directive are recognized on BSD-like platforms (I tested Mac OS and FreeBSD), which instead need "stat -f %z".
Ouch! I did notice that stat(1) is not in POSIX, but hoped that it was sufficiently portable. I need a new idea...
-- Hannes
Re: [PATCH] t5319: don't trip over a user name with whitespace
Quoted 12 lines · Johannes Sixt
Am 30.06.19 um 21:48 schrieb Eric Sunshine:
Quoted 10 lines · Johannes Sixt
t/t5319-multi-pack-index.sh test_expect_success 'repack with minimum size does not alter existing packs' ' · line 443
Quoted 4 lines
t/t5319-multi-pack-index.sh +1
-1@@ -443,7 +443,7 @@ test_expect_success 'repack with minimum size does not alter existing packs' ' - MINSIZE=$(ls -l .git/objects/pack/*pack | awk "{print \$5;}" | sort -n | head -n 1) && + MINSIZE=$(stat -c %s .git/objects/pack/*pack | sort -n | head -n 1) &&Unfortunately, this is not portable. While "stat -c %s" works on Linux and MSYS2, neither that option nor the format directive are recognized on BSD-like platforms (I tested Mac OS and FreeBSD), which instead need "stat -f %z".
Quoted 2 lines
Ouch! I did notice that stat(1) is not in POSIX, but hoped that it was sufficiently portable. I need a new idea...
If we are OK relying on rudimentary perl[1], then:
perl -le "print((stat)[7]) for @ARGV"
works. If you want it more readable, then maybe:
perl -MFile::stat -le "print stat(\$_)->size for @ARGV"
Both of those should work with even antique perl versions.
-Peff
[1] I'd also be fine with implementing a test-tool helper in C that behaves like stat(1). Or we could punt on that until somebody feels like trying to eradicate perl (because this is far from the first perl one-liner in the test suite).
Re: [PATCH] t5319: don't trip over a user name with whitespace
Am 01.07.19 um 00:25 schrieb Jeff King:
Quoted 14 lines · Johannes Sixt
Quoted 13 lines
Am 30.06.19 um 21:48 schrieb Eric Sunshine:
Quoted 10 lines · Johannes Sixt
t/t5319-multi-pack-index.sh test_expect_success 'repack with minimum size does not alter existing packs' ' · line 443
Quoted 4 lines
t/t5319-multi-pack-index.sh +1
-1@@ -443,7 +443,7 @@ test_expect_success 'repack with minimum size does not alter existing packs' ' - MINSIZE=$(ls -l .git/objects/pack/*pack | awk "{print \$5;}" | sort -n | head -n 1) && + MINSIZE=$(stat -c %s .git/objects/pack/*pack | sort -n | head -n 1) &&Unfortunately, this is not portable. While "stat -c %s" works on Linux and MSYS2, neither that option nor the format directive are recognized on BSD-like platforms (I tested Mac OS and FreeBSD), which instead need "stat -f %z".
Quoted 6 lines
Quoted 2 lines
Ouch! I did notice that stat(1) is not in POSIX, but hoped that it was sufficiently portable. I need a new idea...
If we are OK relying on rudimentary perl[1], then:
perl -le "print((stat)[7]) for @ARGV"
I'm fine with that. But then we should do the sort + head -n 1 at the same time. I can do that with a small script, but I'm sure it's possible in a one-liner...
-- Hannes
Re: [PATCH] t5319: don't trip over a user name with whitespace
Quoted 7 lines · Johannes Sixt
Quoted 3 lines
If we are OK relying on rudimentary perl[1], then:
perl -le "print((stat)[7]) for @ARGV"
I'm fine with that. But then we should do the sort + head -n 1 at the same time. I can do that with a small script, but I'm sure it's possible in a one-liner...
Something like:
perl -le 'print((sort { $a <=> $b } map { (stat)[7] } @ARGV)[0])'
but that is getting pretty unreadable. If we rely on List::Util, it's a bit nicer:
perl -MList::Util=min -e 'print min(map { (stat)[7] } @ARGV)'
but that implies perl v5.7.3. Which is from 2002, and older than our usual minimum-perl version, but we've typically been very conservative with these one-liners, since they do not respect NO_PERL.
Probably writing it out like:
perl -le '
my @sizes = map { (stat)[7] } @ARGV;
@sizes = sort { $a <=> $b } @sizes;
print $size[0];
'would be better (and makes "3rd-smallest" a trivial change).
I see Gábor suggested using "wc -c" elsewhere in the thread. That would be fine with me, too, though I think the required sed there may be getting pretty unreadable, too. :)
-Peff
Re: [PATCH] t5319: don't trip over a user name with whitespace
Quoted 3 lines · Jeff King
I see Gábor suggested using "wc -c" elsewhere in the thread. That would be fine with me, too, though I think the required sed there may be getting pretty unreadable, too. :)
It could be done even without 'sed', though at the expense of running a coupe more 'wc -c's in a loop:
t/t5319-multi-pack-index.sh +12 -2
@@ -443,7 +443,12 @@ test_expect_success 'repack with minimum size does not alter existing packs' ' touch -m -t 201901010002 .git/objects/pack/pack-B* && touch -m -t 201901010003 .git/objects/pack/pack-A* && ls .git/objects/pack >expect && - MINSIZE=$(ls -l .git/objects/pack/*pack | awk "{print \$5;}" | sort -n | head -n 1) && + MINSIZE=$( + for pack in .git/objects/pack/*pack + do + wc -c <"$pack" + done | sort -n | head -n 1 + ) && git multi-pack-index repack --batch-size=$MINSIZE && ls .git/objects/pack >actual && test_cmp expect actual
@@ -455,7 +460,12 @@ test_expect_success 'repack creates a new pack' ' cd dup && ls .git/objects/pack/*idx >idx-list && test_line_count = 5 idx-list && - THIRD_SMALLEST_SIZE=$(ls -l .git/objects/pack/*pack | awk "{print \$5;}" | sort -n | head -n 3 | tail -n 1) && + THIRD_SMALLEST_SIZE=$( + for pack in .git/objects/pack/*pack + do + wc -c <"$pack" + done | sort -n | head -n 3 | tail -n 1 + ) && BATCH_SIZE=$(($THIRD_SMALLEST_SIZE + 1)) && git multi-pack-index repack --batch-size=$BATCH_SIZE && ls .git/objects/pack/*idx >idx-list &&
Is it really better? Dunno, but at least there is no subtlety with the leading padding spaces.
Re: [PATCH] t5319: don't trip over a user name with whitespace
Sorry I'm late to the thread. Thanks for reporting this issue, Johannes.
Quoted 40 lines · SZEDER Gábor
Quoted 3 lines · Jeff King
I see Gábor suggested using "wc -c" elsewhere in the thread. That would be fine with me, too, though I think the required sed there may be getting pretty unreadable, too. :)
It could be done even without 'sed', though at the expense of running a coupe more 'wc -c's in a loop:>
t/t5319-multi-pack-index.sh +12
-2@@ -443,7 +443,12 @@ test_expect_success 'repack with minimum size does not alter existing packs' ' touch -m -t 201901010002 .git/objects/pack/pack-B* && touch -m -t 201901010003 .git/objects/pack/pack-A* && ls .git/objects/pack >expect && - MINSIZE=$(ls -l .git/objects/pack/*pack | awk "{print \$5;}" | sort -n | head -n 1) && + MINSIZE=$( + for pack in .git/objects/pack/*pack + do + wc -c <"$pack" + done | sort -n | head -n 1 + ) && git multi-pack-index repack --batch-size=$MINSIZE && ls .git/objects/pack >actual && test_cmp expect actual@@ -455,7 +460,12 @@ test_expect_success 'repack creates a new pack' ' cd dup && ls .git/objects/pack/*idx >idx-list && test_line_count = 5 idx-list && - THIRD_SMALLEST_SIZE=$(ls -l .git/objects/pack/*pack | awk "{print \$5;}" | sort -n | head -n 3 | tail -n 1) && + THIRD_SMALLEST_SIZE=$( + for pack in .git/objects/pack/*pack + do + wc -c <"$pack" + done | sort -n | head -n 3 | tail -n 1 + ) && BATCH_SIZE=$(($THIRD_SMALLEST_SIZE + 1)) && git multi-pack-index repack --batch-size=$BATCH_SIZE && ls .git/objects/pack/*idx >idx-list &&
Quoted 2 lines
Is it really better? Dunno, but at least there is no subtlety with the leading padding spaces.
Your change is certainly more straight-forward, and avoids all issues around Perl compatibility.
It does have the issue of more 'wc' processes, which is a downside. The count here is not too bad, but if we need to duplicate this pattern elsewhere we may be better off creating a stat(1) replacement in test-lib.sh.
Thanks,
-Stolee
Re: [PATCH] t5319: don't trip over a user name with whitespace
In reply to Jeff KingHi Peff,
Quoted 17 lines · Jeff King
Quoted 12 lines · Johannes Sixt
Am 30.06.19 um 21:48 schrieb Eric Sunshine:
Quoted 10 lines · Johannes Sixt
t/t5319-multi-pack-index.sh test_expect_success 'repack with minimum size does not alter existing packs' ' · line 443
Quoted 4 lines
t/t5319-multi-pack-index.sh +1
-1@@ -443,7 +443,7 @@ test_expect_success 'repack with minimum size does not alter existing packs' ' - MINSIZE=$(ls -l .git/objects/pack/*pack | awk "{print \$5;}" | sort -n | head -n 1) && + MINSIZE=$(stat -c %s .git/objects/pack/*pack | sort -n | head -n 1) &&Unfortunately, this is not portable. While "stat -c %s" works on Linux and MSYS2, neither that option nor the format directive are recognized on BSD-like platforms (I tested Mac OS and FreeBSD), which instead need "stat -f %z".
Quoted 2 lines
Ouch! I did notice that stat(1) is not in POSIX, but hoped that it was sufficiently portable. I need a new idea...
Quoted 7 lines
If we are OK relying on rudimentary perl[1], then:
perl -le "print((stat)[7]) for @ARGV"
works. If you want it more readable, then maybe:
perl -MFile::stat -le "print stat(\$_)->size for @ARGV"
Or we stop introducing new Perl calls, and use the perfectly fine test-tool path-utils file-size command:
https://github.com/git/git/blob/v2.22.0/t/helper/test-path-utils.c#L302-L312
This solves not only portability problems but also avoids yet another obstacle into making a NO_PERL test suite run really work without Perl.
Ciao,
Dscho
Re: [PATCH] t5319: don't trip over a user name with whitespace
Quoted 29 lines · Johannes Schindelin
Hi Peff,
Quoted 17 lines · Jeff King
Quoted 12 lines · Johannes Sixt
Am 30.06.19 um 21:48 schrieb Eric Sunshine:
Quoted 10 lines · Johannes Sixt
t/t5319-multi-pack-index.sh test_expect_success 'repack with minimum size does not alter existing packs' ' · line 443
Quoted 4 lines
t/t5319-multi-pack-index.sh +1
-1@@ -443,7 +443,7 @@ test_expect_success 'repack with minimum size does not alter existing packs' ' - MINSIZE=$(ls -l .git/objects/pack/*pack | awk "{print \$5;}" | sort -n | head -n 1) && + MINSIZE=$(stat -c %s .git/objects/pack/*pack | sort -n | head -n 1) &&Unfortunately, this is not portable. While "stat -c %s" works on Linux and MSYS2, neither that option nor the format directive are recognized on BSD-like platforms (I tested Mac OS and FreeBSD), which instead need "stat -f %z".
Quoted 2 lines
Ouch! I did notice that stat(1) is not in POSIX, but hoped that it was sufficiently portable. I need a new idea...
Quoted 7 lines
If we are OK relying on rudimentary perl[1], then:
perl -le "print((stat)[7]) for @ARGV"
works. If you want it more readable, then maybe:
perl -MFile::stat -le "print stat(\$_)->size for @ARGV"
Quoted 7 lines
Or we stop introducing new Perl calls, and use the perfectly fine
test-tool path-utils file-sizecommand:https://github.com/git/git/blob/v2.22.0/t/helper/test-path-utils.c#L302-L312
This solves not only portability problems but also avoids yet another obstacle into making a
NO_PERLtest suite run really work without Perl.
Thanks! This does seem like the best option. Thanks for bringing this to our attention. Here is a diff, and I'll prepare a full patch:
t/t5319-multi-pack-index.sh +2 -2
@@ -447,7 +447,7 @@ test_expect_success 'repack with minimum size does not alter existing packs' ' touch -m -t 201901010002 .git/objects/pack/pack-B* && touch -m -t 201901010003 .git/objects/pack/pack-A* && ls .git/objects/pack >expect && - MINSIZE=$(ls -l .git/objects/pack/*pack | awk "{print \$5;}" | sort -n | head -n 1) && + MINSIZE=$(test-tool path-utils file-size .git/objects/pack/*pack | sort -n | head -n 1) && git multi-pack-index repack --batch-size=$MINSIZE && ls .git/objects/pack >actual && test_cmp expect actual
@@ -459,7 +459,7 @@ test_expect_success 'repack creates a new pack' ' cd dup && ls .git/objects/pack/*idx >idx-list && test_line_count = 5 idx-list && - THIRD_SMALLEST_SIZE=$(ls -l .git/objects/pack/*pack | awk "{print \$5;}" | sort -n | head -n 3 | tail -n 1) && + THIRD_SMALLEST_SIZE=$(test-tool path-utils file-size .git/objects/pack/*pack | sort -n | head -n 3 | tail -n 1) && BATCH_SIZE=$(($THIRD_SMALLEST_SIZE + 1)) && git multi-pack-index repack --batch-size=$BATCH_SIZE && ls .git/objects/pack/*idx >idx-list &&
Thanks,
-Stolee
Re: [PATCH] t5319: don't trip over a user name with whitespace
Am 01.07.19 um 14:30 schrieb Derrick Stolee:
Quoted 10 lines · Johannes Schindelin
Quoted 7 lines
Or we stop introducing new Perl calls, and use the perfectly fine
test-tool path-utils file-sizecommand:https://github.com/git/git/blob/v2.22.0/t/helper/test-path-utils.c#L302-L312
This solves not only portability problems but also avoids yet another obstacle into making a
NO_PERLtest suite run really work without Perl.Thanks! This does seem like the best option. Thanks for bringing this to our attention. Here is a diff, and I'll prepare a full patch:
Thanks. Please also explain why the first of the two tests does not fail with a large --batch-size (unless it is obvious for people who know a bit about multi-pack-index, of course).
-- Hannes
Re: [PATCH] t5319: don't trip over a user name with whitespace
Quoted 7 lines · Johannes Sixt
Am 01.07.19 um 14:30 schrieb Derrick Stolee:
Quoted 6 lines · Johannes Schindelin
Quoted 5 lines
Or we stop introducing new Perl calls, and use the perfectly fine
test-tool path-utils file-sizecommand:https://github.com/git/git/blob/v2.22.0/t/helper/test-path-utils.c#L302-L312
Quoted 8 lines
Quoted 4 lines
Quoted 2 lines
This solves not only portability problems but also avoids yet another obstacle into making a
NO_PERLtest suite run really work without Perl.Thanks! This does seem like the best option. Thanks for bringing this to our attention. Here is a diff, and I'll prepare a full patch:
Thanks. Please also explain why the first of the two tests does not fail with a large --batch-size (unless it is obvious for people who know a bit about multi-pack-index, of course).
Sorry about missing that. Here is an explanation:
The --batch-size=X option determines how much data we want to move in a single operation. Based on this value, we select packs greedily as follows:
1. If a pack has size at most X, include it.
2. If our total pack size is greater than X, stop.
At the end of this process, if we have zero or one packs we do nothing. That is why a small size does nothing.
Also, if our total pack size is smaller than X, we do nothing. Our batch is not "full" enough to merit the work. This is why a large size does nothing.
Thanks,
-Stolee
Re: [PATCH] t5319: don't trip over a user name with whitespace
In reply to Johannes SchindelinQuoted 4 lines · Johannes Schindelin
Or we stop introducing new Perl calls, and use the perfectly fine
test-tool path-utils file-sizecommand:https://github.com/git/git/blob/v2.22.0/t/helper/test-path-utils.c#L302-L312
Ah, thanks, I missed that we had already added it. Certainly that seems like the right solution then.
-Peff
Re: [PATCH] t5319: don't trip over a user name with whitespace
In reply to Johannes SixtQuoted 12 lines · Johannes Sixt
Am 30.06.19 um 21:48 schrieb Eric Sunshine:
Quoted 10 lines · Johannes Sixt
t/t5319-multi-pack-index.sh test_expect_success 'repack with minimum size does not alter existing packs' ' · line 443
Quoted 4 lines
t/t5319-multi-pack-index.sh +1
-1@@ -443,7 +443,7 @@ test_expect_success 'repack with minimum size does not alter existing packs' ' - MINSIZE=$(ls -l .git/objects/pack/*pack | awk "{print \$5;}" | sort -n | head -n 1) && + MINSIZE=$(stat -c %s .git/objects/pack/*pack | sort -n | head -n 1) &&Unfortunately, this is not portable. While "stat -c %s" works on Linux and MSYS2, neither that option nor the format directive are recognized on BSD-like platforms (I tested Mac OS and FreeBSD), which instead need "stat -f %z".
Quoted 2 lines
Ouch! I did notice that stat(1) is not in POSIX, but hoped that it was sufficiently portable. I need a new idea...
'wc -c' perhaps? We already use it in a couple of places in the test suite to get file size.
t/t5319-multi-pack-index.sh +2 -2
@@ -443,7 +443,7 @@ test_expect_success 'repack with minimum size does not alter existing packs' ' touch -m -t 201901010002 .git/objects/pack/pack-B* && touch -m -t 201901010003 .git/objects/pack/pack-A* && ls .git/objects/pack >expect && - MINSIZE=$(ls -l .git/objects/pack/*pack | awk "{print \$5;}" | sort -n | head -n 1) && + MINSIZE=$(wc -c .git/objects/pack/*pack | sort -n | sed -n -e "s/^ *//" -e "1 s/[^ ]*$//p") && git multi-pack-index repack --batch-size=$MINSIZE && ls .git/objects/pack >actual && test_cmp expect actual
@@ -455,7 +455,7 @@ test_expect_success 'repack creates a new pack' ' cd dup && ls .git/objects/pack/*idx >idx-list && test_line_count = 5 idx-list && - THIRD_SMALLEST_SIZE=$(ls -l .git/objects/pack/*pack | awk "{print \$5;}" | sort -n | head -n 3 | tail -n 1) && + THIRD_SMALLEST_SIZE=$(wc -c .git/objects/pack/*pack | sort -n | sed -n -e "s/^ *//" -e "3 s/[^ ]*$//p") && BATCH_SIZE=$(($THIRD_SMALLEST_SIZE + 1)) && git multi-pack-index repack --batch-size=$BATCH_SIZE && ls .git/objects/pack/*idx >idx-list &&
Note that 'wc -c' prints the numbers in its output left padded, hence the 'sed "s/^ *//"' above, and once 'sed' is already up and running, it might as well take over the role of that 'head -n 3 | tail -n 1'.
Re: [PATCH] t5319: don't trip over a user name with whitespace
In reply to Johannes Sixtt/t5319-multi-pack-index.sh test_expect_success 'repack with minimum size does not alter existing packs' ' · line 446
Quoted 10 lines · Johannes Sixt
t/t5319-multi-pack-index.sh +1
-1@@ -443,7 +443,7 @@ test_expect_success 'repack with minimum size does not alter existing packs' ' touch -m -t 201901010002 .git/objects/pack/pack-B* && touch -m -t 201901010003 .git/objects/pack/pack-A* && ls .git/objects/pack >expect && - MINSIZE=$(ls -l .git/objects/pack/*pack | awk "{print \$5;}" | sort -n | head -n 1) && + MINSIZE=$(stat -c %s .git/objects/pack/*pack | sort -n | head -n 1) &&
You could also use ls -n.
Andreas.
Signature · 3 lines
Andreas Schwab, [email protected] GPG Key fingerprint = 7578 EB47 D4E5 4D69 2510 2552 DF73 E780 A9DA AEC1 "And now for something completely different."
Re: [PATCH] t5319: don't trip over a user name with whitespace
Quoted 14 lines · Andreas Schwab
t/t5319-multi-pack-index.sh test_expect_success 'repack with minimum size does not alter existing packs' ' · line 446
Quoted 10 lines · Johannes Sixt
t/t5319-multi-pack-index.sh +1
-1@@ -443,7 +443,7 @@ test_expect_success 'repack with minimum size does not alter existing packs' ' touch -m -t 201901010002 .git/objects/pack/pack-B* && touch -m -t 201901010003 .git/objects/pack/pack-A* && ls .git/objects/pack >expect && - MINSIZE=$(ls -l .git/objects/pack/*pack | awk "{print \$5;}" | sort -n | head -n 1) && + MINSIZE=$(stat -c %s .git/objects/pack/*pack | sort -n | head -n 1) &&You could also use ls -n.
Nice ;-)