Skip to content

Commit ed7e5fc

Browse files
pcloudsgitster
authored andcommitted
repack: add --keep-pack option
We allow to keep existing packs by having companion .keep files. This is helpful when a pack is permanently kept. In the next patch, git-gc just wants to keep a pack temporarily, for one pack-objects run. git-gc can use --keep-pack for this use case. A note about why the pack_keep field cannot be reused and pack_keep_in_core has to be added. This is about the case when --keep-pack is specified together with either --keep-unreachable or --unpack-unreachable, but --honor-pack-keep is NOT specified. In this case, we want to exclude objects from the packs specified on command line, not from ones with .keep files. If only one bit flag is used, we have to clear pack_keep on pack files with the .keep file. But we can't make any assumption about unreachable objects in .keep packs. If "pack_keep" field is false for .keep packs, we could potentially pull lots of unreachable objects into the new pack, or unpack them loose. The safer approach is ignore all packs with either .keep file or --keep-pack. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent e9e33ab commit ed7e5fc

File tree

6 files changed

+110
-18
lines changed

6 files changed

+110
-18
lines changed

Documentation/git-pack-objects.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ SYNOPSIS
1212
'git pack-objects' [-q | --progress | --all-progress] [--all-progress-implied]
1313
[--no-reuse-delta] [--delta-base-offset] [--non-empty]
1414
[--local] [--incremental] [--window=<n>] [--depth=<n>]
15-
[--revs [--unpacked | --all]]
15+
[--revs [--unpacked | --all]] [--keep-pack=<pack-name>]
1616
[--stdout [--filter=<filter-spec>] | base-name]
1717
[--shallow] [--keep-true-parents] < object-list
1818

@@ -126,6 +126,13 @@ base-name::
126126
has a .keep file to be ignored, even if it would have
127127
otherwise been packed.
128128

129+
--keep-pack=<pack-name>::
130+
This flag causes an object already in the given pack to be
131+
ignored, even if it would have otherwise been
132+
packed. `<pack-name>` is the the pack file name without
133+
leading directory (e.g. `pack-123.pack`). The option could be
134+
specified multiple times to keep multiple packs.
135+
129136
--incremental::
130137
This flag causes an object already in a pack to be ignored
131138
even if it would have otherwise been packed.

Documentation/git-repack.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ git-repack - Pack unpacked objects in a repository
99
SYNOPSIS
1010
--------
1111
[verse]
12-
'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [--window=<n>] [--depth=<n>] [--threads=<n>]
12+
'git repack' [-a] [-A] [-d] [-f] [-F] [-l] [-n] [-q] [-b] [--window=<n>] [--depth=<n>] [--threads=<n>] [--keep-pack=<pack-name>]
1313

1414
DESCRIPTION
1515
-----------
@@ -133,6 +133,13 @@ other objects in that pack they already have locally.
133133
with `-b` or `repack.writeBitmaps`, as it ensures that the
134134
bitmapped packfile has the necessary objects.
135135

136+
--keep-pack=<pack-name>::
137+
Exclude the given pack from repacking. This is the equivalent
138+
of having `.keep` file on the pack. `<pack-name>` is the the
139+
pack file name without leading directory (e.g. `pack-123.pack`).
140+
The option could be specified multiple times to keep multiple
141+
packs.
142+
136143
--unpack-unreachable=<when>::
137144
When loosening unreachable objects, do not bother loosening any
138145
objects older than `<when>`. This can be used to optimize out

builtin/pack-objects.c

Lines changed: 50 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "list.h"
3131
#include "packfile.h"
3232
#include "object-store.h"
33+
#include "dir.h"
3334

3435
static const char *pack_usage[] = {
3536
N_("git pack-objects --stdout [<options>...] [< <ref-list> | < <object-list>]"),
@@ -55,7 +56,8 @@ static int pack_loose_unreachable;
5556
static int local;
5657
static int have_non_local_packs;
5758
static int incremental;
58-
static int ignore_packed_keep;
59+
static int ignore_packed_keep_on_disk;
60+
static int ignore_packed_keep_in_core;
5961
static int allow_ofs_delta;
6062
static struct pack_idx_option pack_idx_opts;
6163
static const char *base_name;
@@ -982,13 +984,16 @@ static int want_found_object(int exclude, struct packed_git *p)
982984
* Otherwise, we signal "-1" at the end to tell the caller that we do
983985
* not know either way, and it needs to check more packs.
984986
*/
985-
if (!ignore_packed_keep &&
987+
if (!ignore_packed_keep_on_disk &&
988+
!ignore_packed_keep_in_core &&
986989
(!local || !have_non_local_packs))
987990
return 1;
988991

989992
if (local && !p->pack_local)
990993
return 0;
991-
if (ignore_packed_keep && p->pack_local && p->pack_keep)
994+
if (p->pack_local &&
995+
((ignore_packed_keep_on_disk && p->pack_keep) ||
996+
(ignore_packed_keep_in_core && p->pack_keep_in_core)))
992997
return 0;
993998

994999
/* we don't know yet; keep looking for more packs */
@@ -2675,7 +2680,7 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs)
26752680
struct object_id oid;
26762681
struct object *o;
26772682

2678-
if (!p->pack_local || p->pack_keep)
2683+
if (!p->pack_local || p->pack_keep || p->pack_keep_in_core)
26792684
continue;
26802685
if (open_pack_index(p))
26812686
die("cannot open pack index");
@@ -2738,7 +2743,8 @@ static int has_sha1_pack_kept_or_nonlocal(const struct object_id *oid)
27382743
get_packed_git(the_repository);
27392744

27402745
while (p) {
2741-
if ((!p->pack_local || p->pack_keep) &&
2746+
if ((!p->pack_local || p->pack_keep ||
2747+
p->pack_keep_in_core) &&
27422748
find_pack_entry_one(oid->hash, p)) {
27432749
last_found = p;
27442750
return 1;
@@ -2781,7 +2787,7 @@ static void loosen_unused_packed_objects(struct rev_info *revs)
27812787
struct object_id oid;
27822788

27832789
for (p = get_packed_git(the_repository); p; p = p->next) {
2784-
if (!p->pack_local || p->pack_keep)
2790+
if (!p->pack_local || p->pack_keep || p->pack_keep_in_core)
27852791
continue;
27862792

27872793
if (open_pack_index(p))
@@ -2807,7 +2813,8 @@ static int pack_options_allow_reuse(void)
28072813
{
28082814
return pack_to_stdout &&
28092815
allow_ofs_delta &&
2810-
!ignore_packed_keep &&
2816+
!ignore_packed_keep_on_disk &&
2817+
!ignore_packed_keep_in_core &&
28112818
(!local || !have_non_local_packs) &&
28122819
!incremental;
28132820
}
@@ -2916,6 +2923,32 @@ static void get_object_list(int ac, const char **av)
29162923
oid_array_clear(&recent_objects);
29172924
}
29182925

2926+
static void add_extra_kept_packs(const struct string_list *names)
2927+
{
2928+
struct packed_git *p;
2929+
2930+
if (!names->nr)
2931+
return;
2932+
2933+
for (p = get_packed_git(the_repository); p; p = p->next) {
2934+
const char *name = basename(p->pack_name);
2935+
int i;
2936+
2937+
if (!p->pack_local)
2938+
continue;
2939+
2940+
for (i = 0; i < names->nr; i++)
2941+
if (!fspathcmp(name, names->items[i].string))
2942+
break;
2943+
2944+
if (i < names->nr) {
2945+
p->pack_keep_in_core = 1;
2946+
ignore_packed_keep_in_core = 1;
2947+
continue;
2948+
}
2949+
}
2950+
}
2951+
29192952
static int option_parse_index_version(const struct option *opt,
29202953
const char *arg, int unset)
29212954
{
@@ -2955,6 +2988,7 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
29552988
struct argv_array rp = ARGV_ARRAY_INIT;
29562989
int rev_list_unpacked = 0, rev_list_all = 0, rev_list_reflog = 0;
29572990
int rev_list_index = 0;
2991+
struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
29582992
struct option pack_objects_options[] = {
29592993
OPT_SET_INT('q', "quiet", &progress,
29602994
N_("do not show progress meter"), 0),
@@ -3019,8 +3053,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
30193053
N_("create thin packs")),
30203054
OPT_BOOL(0, "shallow", &shallow,
30213055
N_("create packs suitable for shallow fetches")),
3022-
OPT_BOOL(0, "honor-pack-keep", &ignore_packed_keep,
3056+
OPT_BOOL(0, "honor-pack-keep", &ignore_packed_keep_on_disk,
30233057
N_("ignore packs that have companion .keep file")),
3058+
OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"),
3059+
N_("ignore this pack")),
30243060
OPT_INTEGER(0, "compression", &pack_compression_level,
30253061
N_("pack compression level")),
30263062
OPT_SET_INT(0, "keep-true-parents", &grafts_replace_parents,
@@ -3148,19 +3184,20 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
31483184
if (progress && all_progress_implied)
31493185
progress = 2;
31503186

3151-
if (ignore_packed_keep) {
3187+
add_extra_kept_packs(&keep_pack_list);
3188+
if (ignore_packed_keep_on_disk) {
31523189
struct packed_git *p;
31533190
for (p = get_packed_git(the_repository); p; p = p->next)
31543191
if (p->pack_local && p->pack_keep)
31553192
break;
31563193
if (!p) /* no keep-able packs found */
3157-
ignore_packed_keep = 0;
3194+
ignore_packed_keep_on_disk = 0;
31583195
}
31593196
if (local) {
31603197
/*
3161-
* unlike ignore_packed_keep above, we do not want to
3162-
* unset "local" based on looking at packs, as it
3163-
* also covers non-local objects
3198+
* unlike ignore_packed_keep_on_disk above, we do not
3199+
* want to unset "local" based on looking at packs, as
3200+
* it also covers non-local objects
31643201
*/
31653202
struct packed_git *p;
31663203
for (p = get_packed_git(the_repository); p; p = p->next) {

builtin/repack.c

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,8 @@ static void remove_pack_on_signal(int signo)
8686
* have a corresponding .keep or .promisor file. These packs are not to
8787
* be kept if we are going to pack everything into one file.
8888
*/
89-
static void get_non_kept_pack_filenames(struct string_list *fname_list)
89+
static void get_non_kept_pack_filenames(struct string_list *fname_list,
90+
const struct string_list *extra_keep)
9091
{
9192
DIR *dir;
9293
struct dirent *e;
@@ -97,6 +98,14 @@ static void get_non_kept_pack_filenames(struct string_list *fname_list)
9798

9899
while ((e = readdir(dir)) != NULL) {
99100
size_t len;
101+
int i;
102+
103+
for (i = 0; i < extra_keep->nr; i++)
104+
if (!fspathcmp(e->d_name, extra_keep->items[i].string))
105+
break;
106+
if (extra_keep->nr > 0 && i < extra_keep->nr)
107+
continue;
108+
100109
if (!strip_suffix(e->d_name, ".pack", &len))
101110
continue;
102111

@@ -148,7 +157,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
148157
struct string_list rollback = STRING_LIST_INIT_NODUP;
149158
struct string_list existing_packs = STRING_LIST_INIT_DUP;
150159
struct strbuf line = STRBUF_INIT;
151-
int ext, ret, failed;
160+
int i, ext, ret, failed;
152161
FILE *out;
153162

154163
/* variables to be filled by option parsing */
@@ -160,6 +169,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
160169
const char *depth = NULL;
161170
const char *threads = NULL;
162171
const char *max_pack_size = NULL;
172+
struct string_list keep_pack_list = STRING_LIST_INIT_NODUP;
163173
int no_reuse_delta = 0, no_reuse_object = 0;
164174
int no_update_server_info = 0;
165175
int quiet = 0;
@@ -200,6 +210,8 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
200210
N_("maximum size of each packfile")),
201211
OPT_BOOL(0, "pack-kept-objects", &pack_kept_objects,
202212
N_("repack objects in packs marked with .keep")),
213+
OPT_STRING_LIST(0, "keep-pack", &keep_pack_list, N_("name"),
214+
N_("do not repack this pack")),
203215
OPT_END()
204216
};
205217

@@ -230,6 +242,9 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
230242
argv_array_push(&cmd.args, "--keep-true-parents");
231243
if (!pack_kept_objects)
232244
argv_array_push(&cmd.args, "--honor-pack-keep");
245+
for (i = 0; i < keep_pack_list.nr; i++)
246+
argv_array_pushf(&cmd.args, "--keep-pack=%s",
247+
keep_pack_list.items[i].string);
233248
argv_array_push(&cmd.args, "--non-empty");
234249
argv_array_push(&cmd.args, "--all");
235250
argv_array_push(&cmd.args, "--reflog");
@@ -254,7 +269,7 @@ int cmd_repack(int argc, const char **argv, const char *prefix)
254269
argv_array_push(&cmd.args, "--write-bitmap-index");
255270

256271
if (pack_everything & ALL_INTO_ONE) {
257-
get_non_kept_pack_filenames(&existing_packs);
272+
get_non_kept_pack_filenames(&existing_packs, &keep_pack_list);
258273

259274
if (existing_packs.nr && delete_redundant) {
260275
if (unpack_unreachable) {

object-store.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ struct packed_git {
7171
int pack_fd;
7272
unsigned pack_local:1,
7373
pack_keep:1,
74+
pack_keep_in_core:1,
7475
freshened:1,
7576
do_not_close:1,
7677
pack_promisor:1;

t/t7700-repack.sh

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ test_description='git repack works correctly'
44

55
. ./test-lib.sh
66

7+
commit_and_pack() {
8+
test_commit "$@" >/dev/null &&
9+
SHA1=$(git pack-objects --all --unpacked --incremental .git/objects/pack/pack </dev/null) &&
10+
echo pack-${SHA1}.pack
11+
}
12+
713
test_expect_success 'objects in packs marked .keep are not repacked' '
814
echo content1 > file1 &&
915
echo content2 > file2 &&
@@ -196,5 +202,24 @@ test_expect_success 'objects made unreachable by grafts only are kept' '
196202
git cat-file -t $H1
197203
'
198204

205+
test_expect_success 'repack --keep-pack' '
206+
test_create_repo keep-pack &&
207+
(
208+
cd keep-pack &&
209+
P1=$(commit_and_pack 1) &&
210+
P2=$(commit_and_pack 2) &&
211+
P3=$(commit_and_pack 3) &&
212+
P4=$(commit_and_pack 4) &&
213+
ls .git/objects/pack/*.pack >old-counts &&
214+
test_line_count = 4 old-counts &&
215+
git repack -a -d --keep-pack $P1 --keep-pack $P4 &&
216+
ls .git/objects/pack/*.pack >new-counts &&
217+
grep -q $P1 new-counts &&
218+
grep -q $P4 new-counts &&
219+
test_line_count = 3 new-counts &&
220+
git fsck
221+
)
222+
'
223+
199224
test_done
200225

0 commit comments

Comments
 (0)