aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/git-repack.txt14
-rw-r--r--builtin-gc.c16
-rw-r--r--builtin-pack-objects.c40
-rw-r--r--cache.h1
-rwxr-xr-xgit-repack.sh10
-rw-r--r--sha1_file.c60
-rwxr-xr-xt/t7701-repack-unpack-unreachable.sh47
7 files changed, 154 insertions, 34 deletions
diff --git a/Documentation/git-repack.txt b/Documentation/git-repack.txt
index d14ab5154..f81a6607d 100644
--- a/Documentation/git-repack.txt
+++ b/Documentation/git-repack.txt
@@ -8,7 +8,7 @@ git-repack - Pack unpacked objects in a repository
SYNOPSIS
--------
-'git-repack' [-a] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N]
+'git-repack' [-a] [-A] [-d] [-f] [-l] [-n] [-q] [--window=N] [--depth=N]
DESCRIPTION
-----------
@@ -37,6 +37,18 @@ OPTIONS
leaves behind, but `git fsck --full` shows as
dangling.
+-A::
+ Same as `-a`, but any unreachable objects in a previous
+ pack become loose, unpacked objects, instead of being
+ left in the old pack. Unreachable objects are never
+ intentionally added to a pack, even when repacking.
+ When used with '-d', this option
+ prevents unreachable objects from being immediately
+ deleted by way of being left in the old pack and then
+ removed. Instead, the loose unreachable objects
+ will be pruned according to normal expiry rules
+ with the next linkgit:git-gc[1].
+
-d::
After packing, if the newly created packs make some
existing packs redundant, remove the redundant packs.
diff --git a/builtin-gc.c b/builtin-gc.c
index f99ebc792..48f7d95f9 100644
--- a/builtin-gc.c
+++ b/builtin-gc.c
@@ -219,7 +219,7 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
char buf[80];
struct option builtin_gc_options[] = {
- OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects"),
+ OPT_BOOLEAN(0, "prune", &prune, "prune unreferenced objects (deprecated)"),
OPT_BOOLEAN(0, "aggressive", &aggressive, "be more thorough (increased runtime)"),
OPT_BOOLEAN(0, "auto", &auto_gc, "enable auto-gc mode"),
OPT_BOOLEAN('q', "quiet", &quiet, "suppress progress reports"),
@@ -249,24 +249,14 @@ int cmd_gc(int argc, const char **argv, const char *prefix)
/*
* Auto-gc should be least intrusive as possible.
*/
- prune = 0;
if (!need_to_gc())
return 0;
fprintf(stderr, "Auto packing your repository for optimum "
"performance. You may also\n"
"run \"git gc\" manually. See "
"\"git help gc\" for more information.\n");
- } else {
- /*
- * Use safer (for shared repos) "-A" option to
- * repack when not pruning. Auto-gc makes its
- * own decision.
- */
- if (prune)
- append_option(argv_repack, "-a", MAX_ADD);
- else
- append_option(argv_repack, "-A", MAX_ADD);
- }
+ } else
+ append_option(argv_repack, "-A", MAX_ADD);
if (pack_refs && run_command_v_opt(argv_pack_refs, RUN_GIT_CMD))
return error(FAILED_RUN, argv_pack_refs[0]);
diff --git a/builtin-pack-objects.c b/builtin-pack-objects.c
index e20851e1c..f43eb6701 100644
--- a/builtin-pack-objects.c
+++ b/builtin-pack-objects.c
@@ -28,7 +28,8 @@ git-pack-objects [{ -q | --progress | --all-progress }] \n\
[--window=N] [--window-memory=N] [--depth=N] \n\
[--no-reuse-delta] [--no-reuse-object] [--delta-base-offset] \n\
[--threads=N] [--non-empty] [--revs [--unpacked | --all]*] [--reflog] \n\
- [--stdout | base-name] [--include-tag] [--keep-unreachable] \n\
+ [--stdout | base-name] [--include-tag] \n\
+ [--keep-unreachable | --unpack-unreachable] \n\
[<ref-list | <object-list]";
struct object_entry {
@@ -67,7 +68,7 @@ static uint32_t nr_objects, nr_alloc, nr_result, nr_written;
static int non_empty;
static int reuse_delta = 1, reuse_object = 1;
-static int keep_unreachable, include_tag;
+static int keep_unreachable, unpack_unreachable, include_tag;
static int local;
static int incremental;
static int allow_ofs_delta;
@@ -1946,6 +1947,32 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs)
free(in_pack.array);
}
+static void loosen_unused_packed_objects(struct rev_info *revs)
+{
+ struct packed_git *p;
+ uint32_t i;
+ const unsigned char *sha1;
+
+ for (p = packed_git; p; p = p->next) {
+ for (i = 0; i < revs->num_ignore_packed; i++) {
+ if (matches_pack_name(p, revs->ignore_packed[i]))
+ break;
+ }
+ if (revs->num_ignore_packed <= i)
+ continue;
+
+ if (open_pack_index(p))
+ die("cannot open pack index");
+
+ for (i = 0; i < p->num_objects; i++) {
+ sha1 = nth_packed_object_sha1(p, i);
+ if (!locate_object_entry(sha1))
+ if (force_object_loose(sha1, p->mtime))
+ die("unable to force loose object");
+ }
+ }
+}
+
static void get_object_list(int ac, const char **av)
{
struct rev_info revs;
@@ -1980,6 +2007,8 @@ static void get_object_list(int ac, const char **av)
if (keep_unreachable)
add_objects_in_unpacked_packs(&revs);
+ if (unpack_unreachable)
+ loosen_unused_packed_objects(&revs);
}
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)
keep_unreachable = 1;
continue;
}
+ if (!strcmp("--unpack-unreachable", arg)) {
+ unpack_unreachable = 1;
+ continue;
+ }
if (!strcmp("--include-tag", arg)) {
include_tag = 1;
continue;
@@ -2179,6 +2212,9 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
if (!pack_to_stdout && thin)
die("--thin cannot be used to build an indexable pack.");
+ if (keep_unreachable && unpack_unreachable)
+ die("--keep-unreachable and --unpack-unreachable are incompatible.");
+
#ifdef THREADED_DELTA_SEARCH
if (!delta_search_threads) /* --threads=0 means autodetect */
delta_search_threads = online_cpus();
diff --git a/cache.h b/cache.h
index 0f89f1569..8db19cc25 100644
--- a/cache.h
+++ b/cache.h
@@ -521,6 +521,7 @@ extern void * read_sha1_file(const unsigned char *sha1, enum object_type *type,
extern int hash_sha1_file(const void *buf, unsigned long len, const char *type, unsigned char *sha1);
extern int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *return_sha1);
extern int pretend_sha1_file(void *, unsigned long, enum object_type, unsigned char *);
+extern int force_object_loose(const unsigned char *sha1, time_t mtime);
extern int check_sha1_signature(const unsigned char *sha1, void *buf, unsigned long size, const char *type);
diff --git a/git-repack.sh b/git-repack.sh
index 501519ab6..10f735cff 100755
--- a/git-repack.sh
+++ b/git-repack.sh
@@ -8,7 +8,7 @@ OPTIONS_SPEC="\
git-repack [options]
--
a pack everything in a single pack
-A same as -a, and keep unreachable objects too
+A same as -a, and turn unreachable objects loose
d remove redundant packs, and run git-prune-packed
f pass --no-reuse-delta to git-pack-objects
n do not run git-update-server-info
@@ -23,7 +23,7 @@ max-pack-size= maximum size of each packfile
SUBDIRECTORY_OK='Yes'
. git-sh-setup
-no_update_info= all_into_one= remove_redundant= keep_unreachable=
+no_update_info= all_into_one= remove_redundant= unpack_unreachable=
local= quiet= no_reuse= extra=
while test $# != 0
do
@@ -31,7 +31,7 @@ do
-n) no_update_info=t ;;
-a) all_into_one=t ;;
-A) all_into_one=t
- keep_unreachable=--keep-unreachable ;;
+ unpack_unreachable=--unpack-unreachable ;;
-d) remove_redundant=t ;;
-q) quiet=-q ;;
-f) no_reuse=--no-reuse-object ;;
@@ -79,9 +79,9 @@ case ",$all_into_one," in
if test -z "$args"
then
args='--unpacked --incremental'
- elif test -n "$keep_unreachable"
+ elif test -n "$unpack_unreachable"
then
- args="$args $keep_unreachable"
+ args="$args $unpack_unreachable"
fi
;;
esac
diff --git a/sha1_file.c b/sha1_file.c
index 3516777bc..141f5a713 100644
--- a/sha1_file.c
+++ b/sha1_file.c
@@ -2102,26 +2102,16 @@ int hash_sha1_file(const void *buf, unsigned long len, const char *type,
return 0;
}
-int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
+static int write_loose_object(const unsigned char *sha1, char *hdr, int hdrlen,
+ void *buf, unsigned long len, time_t mtime)
{
- int size, ret;
+ int fd, size, ret;
unsigned char *compressed;
z_stream stream;
- unsigned char sha1[20];
char *filename;
static char tmpfile[PATH_MAX];
- char hdr[32];
- int fd, hdrlen;
- /* Normally if we have it in the pack then we do not bother writing
- * it out into .git/objects/??/?{38} file.
- */
- write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
filename = sha1_file_name(sha1);
- if (returnsha1)
- hashcpy(returnsha1, sha1);
- if (has_sha1_file(sha1))
- return 0;
fd = open(filename, O_RDONLY);
if (fd >= 0) {
/*
@@ -2182,9 +2172,53 @@ int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned cha
die("unable to write sha1 file");
free(compressed);
+ if (mtime) {
+ struct utimbuf utb;
+ utb.actime = mtime;
+ utb.modtime = mtime;
+ if (utime(tmpfile, &utb) < 0)
+ warning("failed utime() on %s: %s",
+ tmpfile, strerror(errno));
+ }
+
return move_temp_to_file(tmpfile, filename);
}
+int write_sha1_file(void *buf, unsigned long len, const char *type, unsigned char *returnsha1)
+{
+ unsigned char sha1[20];
+ char hdr[32];
+ int hdrlen;
+
+ /* Normally if we have it in the pack then we do not bother writing
+ * it out into .git/objects/??/?{38} file.
+ */
+ write_sha1_file_prepare(buf, len, type, sha1, hdr, &hdrlen);
+ if (returnsha1)
+ hashcpy(returnsha1, sha1);
+ if (has_sha1_file(sha1))
+ return 0;
+ return write_loose_object(sha1, hdr, hdrlen, buf, len, 0);
+}
+
+int force_object_loose(const unsigned char *sha1, time_t mtime)
+{
+ struct stat st;
+ void *buf;
+ unsigned long len;
+ enum object_type type;
+ char hdr[32];
+ int hdrlen;
+
+ if (find_sha1_file(sha1, &st))
+ return 0;
+ buf = read_packed_sha1(sha1, &type, &len);
+ if (!buf)
+ return error("cannot read sha1_file for %s", sha1_to_hex(sha1));
+ hdrlen = sprintf(hdr, "%s %lu", typename(type), len) + 1;
+ return write_loose_object(sha1, hdr, hdrlen, buf, len, mtime);
+}
+
/*
* We need to unpack and recompress the object for writing
* it out to a different file.
diff --git a/t/t7701-repack-unpack-unreachable.sh b/t/t7701-repack-unpack-unreachable.sh
new file mode 100755
index 000000000..6a5211f18
--- /dev/null
+++ b/t/t7701-repack-unpack-unreachable.sh
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+test_description='git-repack works correctly'
+
+. ./test-lib.sh
+
+test_expect_success '-A option leaves unreachable objects unpacked' '
+ echo content > file1 &&
+ git add . &&
+ git commit -m initial_commit &&
+ # create a transient branch with unique content
+ git checkout -b transient_branch &&
+ echo more content >> file1 &&
+ # record the objects created in the database for file, commit, tree
+ fsha1=$(git hash-object file1) &&
+ git commit -a -m more_content &&
+ csha1=$(git rev-parse HEAD^{commit}) &&
+ tsha1=$(git rev-parse HEAD^{tree}) &&
+ git checkout master &&
+ echo even more content >> file1 &&
+ git commit -a -m even_more_content &&
+ # delete the transient branch
+ git branch -D transient_branch &&
+ # pack the repo
+ git repack -A -d -l &&
+ # verify objects are packed in repository
+ test 3 = $(git verify-pack -v -- .git/objects/pack/*.idx |
+ grep -e "^$fsha1 " -e "^$csha1 " -e "^$tsha1 " |
+ sort | uniq | wc -l) &&
+ git show $fsha1 &&
+ git show $csha1 &&
+ git show $tsha1 &&
+ # now expire the reflog
+ sleep 1 &&
+ git reflog expire --expire-unreachable=now --all &&
+ # and repack
+ git repack -A -d -l &&
+ # verify objects are retained unpacked
+ test 0 = $(git verify-pack -v -- .git/objects/pack/*.idx |
+ grep -e "^$fsha1 " -e "^$csha1 " -e "^$tsha1 " |
+ sort | uniq | wc -l) &&
+ git show $fsha1 &&
+ git show $csha1 &&
+ git show $tsha1
+'
+
+test_done