Skip to content

Commit e5e9714

Browse files
committed
Merge branch 'bc/repack'
* bc/repack: Documentation/git-repack.txt: document new -A behaviour let pack-objects do the writing of unreachable objects as loose objects add a force_object_loose() function builtin-gc.c: deprecate --prune, it now really has no effect git-gc: always use -A when manually repacking repack: modify behavior of -A option to leave unreferenced objects unpacked Conflicts: builtin-pack-objects.c
2 parents 6aad47d + bbefaa1 commit e5e9714

File tree

7 files changed

+154
-34
lines changed

7 files changed

+154
-34
lines changed

Documentation/git-repack.txt

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ git-repack - Pack unpacked objects in a repository
88

99
SYNOPSIS
1010
--------
11-
'git-repack' [-a] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N]
11+
'git-repack' [-a] [-A] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N]
1212

1313
DESCRIPTION
1414
-----------
@@ -37,6 +37,18 @@ OPTIONS
3737
leaves behind, but `git fsck --full` shows as
3838
dangling.
3939

40+
-A::
41+
Same as `-a`, but any unreachable objects in a previous
42+
pack become loose, unpacked objects, instead of being
43+
left in the old pack. Unreachable objects are never
44+
intentionally added to a pack, even when repacking.
45+
When used with '-d', this option
46+
prevents unreachable objects from being immediately
47+
deleted by way of being left in the old pack and then
48+
removed. Instead, the loose unreachable objects
49+
will be pruned according to normal expiry rules
50+
with the next linkgit:git-gc[1].
51+
4052
-d::
4153
After packing, if the newly created packs make some
4254
existing packs redundant, remove the redundant packs.

builtin-gc.c

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
219219
char buf[80];
220220

221221
struct option builtin_gc_options[] = {
222-
OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects"),
222+
OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects (deprecated)"),
223223
OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"),
224224
OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"),
225225
OPT_BOOLEAN('q', "quiet", &quiet, "suppress progress reports"),
@@ -249,24 +249,14 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
249249
/*
250250
* Auto-gc should be least intrusive as possible.
251251
*/
252-
prune = 0;
253252
if (!need_to_gc())
254253
return 0;
255254
fprintf(stderr, "Auto packing your repository for optimum "
256255
"performance. You may also\n"
257256
"run \"git gc\" manually. See "
258257
"\"git help gc\" for more information.\n");
259-
} else {
260-
/*
261-
* Use safer (for shared repos) "-A" option to
262-
* repack when not pruning. Auto-gc makes its
263-
* own decision.
264-
*/
265-
if (prune)
266-
append_option(argv_repack, "-a", MAX_ADD);
267-
else
268-
append_option(argv_repack, "-A", MAX_ADD);
269-
}
258+
} else
259+
append_option(argv_repack, "-A", MAX_ADD);
270260

271261
if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))
272262
return error(FAILED_RUN, argv_pack_refs[0]);

builtin-pack-objects.c

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ git-pack-objects [{ -q | --progress | --all-progress }] \n\
2828
[--window=N] [--window-memory=N] [--depth=N] \n\
2929
[--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\
3030
[--threads=N] [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
31-
[--stdout | base-name] [--include-tag] [--keep-unreachable] \n\
31+
[--stdout | base-name] [--include-tag] \n\
32+
[--keep-unreachable | --unpack-unreachable] \n\
3233
[<ref-list | <object-list]";
3334

3435
struct object_entry {
@@ -67,7 +68,7 @@ static uint32_t nr_objects, nr_alloc, nr_result, nr_written;
6768

6869
static int non_empty;
6970
static int reuse_delta = 1, reuse_object = 1;
70-
static int keep_unreachable, include_tag;
71+
static int keep_unreachable, unpack_unreachable, include_tag;
7172
static int local;
7273
static int incremental;
7374
static int allow_ofs_delta;
@@ -1946,6 +1947,32 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs)
19461947
free(in_pack.array);
19471948
}
19481949

1950+
static void loosen_unused_packed_objects(struct rev_info *revs)
1951+
{
1952+
struct packed_git *p;
1953+
uint32_t i;
1954+
const unsigned char *sha1;
1955+
1956+
for (p = packed_git; p; p = p->next) {
1957+
for (i = 0; i < revs->num_ignore_packed; i++) {
1958+
if (matches_pack_name(p, revs->ignore_packed[i]))
1959+
break;
1960+
}
1961+
if (revs->num_ignore_packed <= i)
1962+
continue;
1963+
1964+
if (open_pack_index(p))
1965+
die("cannot open pack index");
1966+
1967+
for (i = 0; i < p->num_objects; i++) {
1968+
sha1 = nth_packed_object_sha1(p, i);
1969+
if (!locate_object_entry(sha1))
1970+
if (force_object_loose(sha1, p->mtime))
1971+
die("unable to force loose object");
1972+
}
1973+
}
1974+
}
1975+
19491976
static void get_object_list(int ac, const char **av)
19501977
{
19511978
struct rev_info revs;
@@ -1980,6 +2007,8 @@ static void get_object_list(int ac, const char **av)
19802007

19812008
if (keep_unreachable)
19822009
add_objects_in_unpacked_packs(&revs);
2010+
if (unpack_unreachable)
2011+
loosen_unused_packed_objects(&revs);
19832012
}
19842013

19852014
static int adjust_perm(const char *path, mode_t mode)
@@ -2114,6 +2143,10 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
21142143
keep_unreachable = 1;
21152144
continue;
21162145
}
2146+
if (!strcmp("--unpack-unreachable", arg)) {
2147+
unpack_unreachable = 1;
2148+
continue;
2149+
}
21172150
if (!strcmp("--include-tag", arg)) {
21182151
include_tag = 1;
21192152
continue;
@@ -2179,6 +2212,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
21792212
if (!pack_to_stdout && thin)
21802213
die("--thin cannot be used to build an indexable pack.");
21812214

2215+
if (keep_unreachable && unpack_unreachable)
2216+
die("--keep-unreachable and --unpack-unreachable are incompatible.");
2217+
21822218
#ifdef THREADED_DELTA_SEARCH
21832219
if (!delta_search_threads) /* --threads=0 means autodetect */
21842220
delta_search_threads = online_cpus();

cache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -521,6 +521,7 @@ extern void * read_sha1_file(const unsigned char *sha1, enum object_type *type,
521521
extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
522522
extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
523523
extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
524+
extern int force_object_loose(const unsigned char *sha1, time_t mtime);
524525

525526
extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
526527

git-repack.sh

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ OPTIONS_SPEC="\
88
git-repack [options]
99
--
1010
a pack everything in a single pack
11-
A same as -a, and keep unreachable objects too
11+
A same as -a, and turn unreachable objects loose
1212
d remove redundant packs, and run git-prune-packed
1313
f pass --no-reuse-delta to git-pack-objects
1414
n do not run git-update-server-info
@@ -23,15 +23,15 @@ max-pack-size= maximum size of each packfile
2323
SUBDIRECTORY_OK='Yes'
2424
. git-sh-setup
2525

26-
no_update_info= all_into_one= remove_redundant= keep_unreachable=
26+
no_update_info= all_into_one= remove_redundant= unpack_unreachable=
2727
local= quiet= no_reuse= extra=
2828
while test $# != 0
2929
do
3030
case "$1" in
3131
-n) no_update_info=t ;;
3232
-a) all_into_one=t ;;
3333
-A) all_into_one=t
34-
keep_unreachable=--keep-unreachable ;;
34+
unpack_unreachable=--unpack-unreachable ;;
3535
-d) remove_redundant=t ;;
3636
-q) quiet=-q ;;
3737
-f) no_reuse=--no-reuse-object ;;
@@ -79,9 +79,9 @@ case ",$all_into_one," in
7979
if test -z "$args"
8080
then
8181
args='--unpacked --incremental'
82-
elif test -n "$keep_unreachable"
82+
elif test -n "$unpack_unreachable"
8383
then
84-
args="$args $keep_unreachable"
84+
args="$args $unpack_unreachable"
8585
fi
8686
;;
8787
esac

sha1_file.c

Lines changed: 47 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2102,26 +2102,16 @@ int hash_sha1_file(const void *buf, unsigned long len, const char *type,
21022102
return 0;
21032103
}
21042104

2105-
int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
2105+
static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
2106+
void *buf, unsigned long len, time_t mtime)
21062107
{
2107-
int size, ret;
2108+
int fd, size, ret;
21082109
unsigned char *compressed;
21092110
z_stream stream;
2110-
unsigned char sha1[20];
21112111
char *filename;
21122112
static char tmpfile[PATH_MAX];
2113-
char hdr[32];
2114-
int fd, hdrlen;
21152113

2116-
/* Normally if we have it in the pack then we do not bother writing
2117-
* it out into .git/objects/??/?{38} file.
2118-
*/
2119-
write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
21202114
filename = sha1_file_name(sha1);
2121-
if (returnsha1)
2122-
hashcpy(returnsha1, sha1);
2123-
if (has_sha1_file(sha1))
2124-
return 0;
21252115
fd = open(filename, O_RDONLY);
21262116
if (fd >= 0) {
21272117
/*
@@ -2182,9 +2172,53 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
21822172
die("unable to write sha1 file");
21832173
free(compressed);
21842174

2175+
if (mtime) {
2176+
struct utimbuf utb;
2177+
utb.actime = mtime;
2178+
utb.modtime = mtime;
2179+
if (utime(tmpfile, &utb) < 0)
2180+
warning("failed utime() on %s: %s",
2181+
tmpfile, strerror(errno));
2182+
}
2183+
21852184
return move_temp_to_file(tmpfile, filename);
21862185
}
21872186

2187+
int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
2188+
{
2189+
unsigned char sha1[20];
2190+
char hdr[32];
2191+
int hdrlen;
2192+
2193+
/* Normally if we have it in the pack then we do not bother writing
2194+
* it out into .git/objects/??/?{38} file.
2195+
*/
2196+
write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
2197+
if (returnsha1)
2198+
hashcpy(returnsha1, sha1);
2199+
if (has_sha1_file(sha1))
2200+
return 0;
2201+
return write_loose_object(sha1, hdr, hdrlen, buf, len, 0);
2202+
}
2203+
2204+
int force_object_loose(const unsigned char *sha1, time_t mtime)
2205+
{
2206+
struct stat st;
2207+
void *buf;
2208+
unsigned long len;
2209+
enum object_type type;
2210+
char hdr[32];
2211+
int hdrlen;
2212+
2213+
if (find_sha1_file(sha1, &st))
2214+
return 0;
2215+
buf = read_packed_sha1(sha1, &type, &len);
2216+
if (!buf)
2217+
return error("cannot read sha1_file for %s", sha1_to_hex(sha1));
2218+
hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1;
2219+
return write_loose_object(sha1, hdr, hdrlen, buf, len, mtime);
2220+
}
2221+
21882222
/*
21892223
* We need to unpack and recompress the object for writing
21902224
* it out to a different file.
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/bin/sh
2+
3+
test_description='git-repack works correctly'
4+
5+
. ./test-lib.sh
6+
7+
test_expect_success '-A option leaves unreachable objects unpacked' '
8+
echo content > file1 &&
9+
git add . &&
10+
git commit -m initial_commit &&
11+
# create a transient branch with unique content
12+
git checkout -b transient_branch &&
13+
echo more content >> file1 &&
14+
# record the objects created in the database for file, commit, tree
15+
fsha1=$(git hash-object file1) &&
16+
git commit -a -m more_content &&
17+
csha1=$(git rev-parse HEAD^{commit}) &&
18+
tsha1=$(git rev-parse HEAD^{tree}) &&
19+
git checkout master &&
20+
echo even more content >> file1 &&
21+
git commit -a -m even_more_content &&
22+
# delete the transient branch
23+
git branch -D transient_branch &&
24+
# pack the repo
25+
git repack -A -d -l &&
26+
# verify objects are packed in repository
27+
test 3 = $(git verify-pack -v -- .git/objects/pack/*.idx |
28+
grep -e "^$fsha1 " -e "^$csha1 " -e "^$tsha1 " |
29+
sort | uniq | wc -l) &&
30+
git show $fsha1 &&
31+
git show $csha1 &&
32+
git show $tsha1 &&
33+
# now expire the reflog
34+
sleep 1 &&
35+
git reflog expire --expire-unreachable=now --all &&
36+
# and repack
37+
git repack -A -d -l &&
38+
# verify objects are retained unpacked
39+
test 0 = $(git verify-pack -v -- .git/objects/pack/*.idx |
40+
grep -e "^$fsha1 " -e "^$csha1 " -e "^$tsha1 " |
41+
sort | uniq | wc -l) &&
42+
git show $fsha1 &&
43+
git show $csha1 &&
44+
git show $tsha1
45+
'
46+
47+
test_done

0 commit comments

Comments
 (0)