prune: clean .git/shallow after pruning objects

This patch teaches "prune" to remove shallow roots that are no longer
reachable from any refs (e.g. when the relevant refs are removed).

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Nguyễn Thái Ngọc Duy 2013-12-05 20:02:54 +07:00 committed by Junio C Hamano
parent 0d7d285f0e
commit eab3296c7e
6 changed files with 71 additions and 2 deletions

View File

@ -24,6 +24,8 @@ objects unreachable from any of these head objects from the object database.
In addition, it In addition, it
prunes the unpacked objects that are also found in packs by prunes the unpacked objects that are also found in packs by
running 'git prune-packed'. running 'git prune-packed'.
It also removes entries from .git/shallow that are not reachable by
any ref.
Note that unreachable, packed objects will remain. If this is Note that unreachable, packed objects will remain. If this is
not desired, see linkgit:git-repack[1]. not desired, see linkgit:git-repack[1].

View File

@ -16,6 +16,7 @@
#include "run-command.h" #include "run-command.h"
#include "sigchain.h" #include "sigchain.h"
#include "argv-array.h" #include "argv-array.h"
#include "commit.h"
#define FAILED_RUN "failed to run %s" #define FAILED_RUN "failed to run %s"

View File

@ -170,5 +170,9 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
s = mkpathdup("%s/pack", get_object_directory()); s = mkpathdup("%s/pack", get_object_directory());
remove_temporary_files(s); remove_temporary_files(s);
free(s); free(s);
if (is_repository_shallow())
prune_shallow(show_only);
return 0; return 0;
} }

View File

@ -235,6 +235,7 @@ extern void assign_shallow_commits_to_refs(struct shallow_info *info,
uint32_t **used, uint32_t **used,
int *ref_status); int *ref_status);
extern int delayed_reachability_test(struct shallow_info *si, int c); extern int delayed_reachability_test(struct shallow_info *si, int c);
extern void prune_shallow(int show_only);
int is_descendant_of(struct commit *, struct commit_list *); int is_descendant_of(struct commit *, struct commit_list *);
int in_merge_bases(struct commit *, struct commit *); int in_merge_bases(struct commit *, struct commit *);

View File

@ -155,10 +155,14 @@ void check_shallow_file_for_update(void)
die("shallow file was changed during fetch"); die("shallow file was changed during fetch");
} }
#define SEEN_ONLY 1
#define VERBOSE 2
struct write_shallow_data { struct write_shallow_data {
struct strbuf *out; struct strbuf *out;
int use_pack_protocol; int use_pack_protocol;
int count; int count;
unsigned flags;
}; };
static int write_one_shallow(const struct commit_graft *graft, void *cb_data) static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
@ -167,6 +171,15 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
const char *hex = sha1_to_hex(graft->sha1); const char *hex = sha1_to_hex(graft->sha1);
if (graft->nr_parent != -1) if (graft->nr_parent != -1)
return 0; return 0;
if (data->flags & SEEN_ONLY) {
struct commit *c = lookup_commit(graft->sha1);
if (!c || !(c->object.flags & SEEN)) {
if (data->flags & VERBOSE)
printf("Removing %s from .git/shallow\n",
sha1_to_hex(c->object.sha1));
return 0;
}
}
data->count++; data->count++;
if (data->use_pack_protocol) if (data->use_pack_protocol)
packet_buf_write(data->out, "shallow %s", hex); packet_buf_write(data->out, "shallow %s", hex);
@ -177,14 +190,16 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
return 0; return 0;
} }
int write_shallow_commits(struct strbuf *out, int use_pack_protocol, static int write_shallow_commits_1(struct strbuf *out, int use_pack_protocol,
const struct sha1_array *extra) const struct sha1_array *extra,
unsigned flags)
{ {
struct write_shallow_data data; struct write_shallow_data data;
int i; int i;
data.out = out; data.out = out;
data.use_pack_protocol = use_pack_protocol; data.use_pack_protocol = use_pack_protocol;
data.count = 0; data.count = 0;
data.flags = flags;
for_each_commit_graft(write_one_shallow, &data); for_each_commit_graft(write_one_shallow, &data);
if (!extra) if (!extra)
return data.count; return data.count;
@ -196,6 +211,12 @@ int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
return data.count; return data.count;
} }
int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
const struct sha1_array *extra)
{
return write_shallow_commits_1(out, use_pack_protocol, extra, 0);
}
char *setup_temporary_shallow(const struct sha1_array *extra) char *setup_temporary_shallow(const struct sha1_array *extra)
{ {
struct strbuf sb = STRBUF_INIT; struct strbuf sb = STRBUF_INIT;
@ -258,6 +279,36 @@ void advertise_shallow_grafts(int fd)
for_each_commit_graft(advertise_shallow_grafts_cb, &fd); for_each_commit_graft(advertise_shallow_grafts_cb, &fd);
} }
/*
* mark_reachable_objects() should have been run prior to this and all
* reachable commits marked as "SEEN".
*/
void prune_shallow(int show_only)
{
static struct lock_file shallow_lock;
struct strbuf sb = STRBUF_INIT;
int fd;
if (show_only) {
write_shallow_commits_1(&sb, 0, NULL, SEEN_ONLY | VERBOSE);
strbuf_release(&sb);
return;
}
check_shallow_file_for_update();
fd = hold_lock_file_for_update(&shallow_lock, git_path("shallow"),
LOCK_DIE_ON_ERROR);
if (write_shallow_commits_1(&sb, 0, NULL, SEEN_ONLY)) {
if (write_in_full(fd, sb.buf, sb.len) != sb.len)
die_errno("failed to write to %s",
shallow_lock.filename);
commit_lock_file(&shallow_lock);
} else {
unlink(git_path("shallow"));
rollback_lock_file(&shallow_lock);
}
strbuf_release(&sb);
}
#define TRACE_KEY "GIT_TRACE_SHALLOW" #define TRACE_KEY "GIT_TRACE_SHALLOW"
/* /*

View File

@ -221,4 +221,14 @@ EOF
test_cmp expected actual test_cmp expected actual
' '
test_expect_success 'prune .git/shallow' '
SHA1=`echo hi|git commit-tree HEAD^{tree}` &&
echo $SHA1 >.git/shallow &&
git prune --dry-run >out &&
grep $SHA1 .git/shallow &&
grep $SHA1 out &&
git prune &&
! test -f .git/shallow
'
test_done test_done