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>
Nguyễn Thái Ngọc Duy committed
Dec 5, 2013 at 20:02 UTC
eab3296c7e5c99f559818357e70eeae09c24ac99
6 files changed
+71
-2
Documentation/git-prune.txt
+2
@@ -24,6 +24,8 @@ objects unreachable from any of these head objects from the object database.
24
In addition, it
25
prunes the unpacked objects that are also found in packs by
26
running 'git prune-packed'.
27
+It also removes entries from .git/shallow that are not reachable by
28
+any ref.
29
30
Note that unreachable, packed objects will remain. If this is
31
not desired, see linkgit:git-repack[1].
builtin/gc.c
+1
@@ -16,6 +16,7 @@
16
#include "run-command.h"
17
#include "sigchain.h"
18
#include "argv-array.h"
19
+#include "commit.h"
20
21
#define FAILED_RUN "failed to run %s"
22
builtin/prune.c
+4
@@ -170,5 +170,9 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
170
s = mkpathdup("%s/pack", get_object_directory());
171
remove_temporary_files(s);
172
free(s);
173
+
174
+ if (is_repository_shallow())
175
+ prune_shallow(show_only);
176
+
177
return 0;
178
}
commit.h
+1
@@ -235,6 +235,7 @@ extern void assign_shallow_commits_to_refs(struct shallow_info *info,
235
uint32_t **used,
236
int *ref_status);
237
extern int delayed_reachability_test(struct shallow_info *si, int c);
238
+extern void prune_shallow(int show_only);
239
240
int is_descendant_of(struct commit *, struct commit_list *);
241
int in_merge_bases(struct commit *, struct commit *);
shallow.c
+53
-2
@@ -155,10 +155,14 @@ void check_shallow_file_for_update(void)
155
die("shallow file was changed during fetch");
156
}
157
158
+#define SEEN_ONLY 1
159
+#define VERBOSE 2
160
+
161
struct write_shallow_data {
162
struct strbuf *out;
163
int use_pack_protocol;
164
int count;
165
+ unsigned flags;
166
};
167
168
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)
171
const char *hex = sha1_to_hex(graft->sha1);
172
if (graft->nr_parent != -1)
173
return 0;
174
+ if (data->flags & SEEN_ONLY) {
175
+ struct commit *c = lookup_commit(graft->sha1);
176
+ if (!c || !(c->object.flags & SEEN)) {
177
+ if (data->flags & VERBOSE)
178
+ printf("Removing %s from .git/shallow\n",
179
+ sha1_to_hex(c->object.sha1));
180
+ return 0;
181
+ }
182
+ }
183
data->count++;
184
if (data->use_pack_protocol)
185
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)
190
return 0;
191
}
192
180
-int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
181
- const struct sha1_array *extra)
193
+static int write_shallow_commits_1(struct strbuf *out, int use_pack_protocol,
194
+ const struct sha1_array *extra,
195
+ unsigned flags)
196
{
197
struct write_shallow_data data;
198
int i;
199
data.out = out;
200
data.use_pack_protocol = use_pack_protocol;
201
data.count = 0;
202
+ data.flags = flags;
203
for_each_commit_graft(write_one_shallow, &data);
204
if (!extra)
205
return data.count;
@@ -196,6 +211,12 @@ int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
211
return data.count;
212
}
213
214
+int write_shallow_commits(struct strbuf *out, int use_pack_protocol,
215
+ const struct sha1_array *extra)
216
+{
217
+ return write_shallow_commits_1(out, use_pack_protocol, extra, 0);
218
+}
219
+
220
char *setup_temporary_shallow(const struct sha1_array *extra)
221
{
222
struct strbuf sb = STRBUF_INIT;
@@ -258,6 +279,36 @@ void advertise_shallow_grafts(int fd)
279
for_each_commit_graft(advertise_shallow_grafts_cb, &fd);
280
}
281
282
+/*
283
+ * mark_reachable_objects() should have been run prior to this and all
284
+ * reachable commits marked as "SEEN".
285
+ */
286
+void prune_shallow(int show_only)
287
+{
288
+ static struct lock_file shallow_lock;
289
+ struct strbuf sb = STRBUF_INIT;
290
+ int fd;
291
+
292
+ if (show_only) {
293
+ write_shallow_commits_1(&sb, 0, NULL, SEEN_ONLY | VERBOSE);
294
+ strbuf_release(&sb);
295
+ return;
296
+ }
297
+ check_shallow_file_for_update();
298
+ fd = hold_lock_file_for_update(&shallow_lock, git_path("shallow"),
299
+ LOCK_DIE_ON_ERROR);
300
+ if (write_shallow_commits_1(&sb, 0, NULL, SEEN_ONLY)) {
301
+ if (write_in_full(fd, sb.buf, sb.len) != sb.len)
302
+ die_errno("failed to write to %s",
303
+ shallow_lock.filename);
304
+ commit_lock_file(&shallow_lock);
305
+ } else {
306
+ unlink(git_path("shallow"));
307
+ rollback_lock_file(&shallow_lock);
308
+ }
309
+ strbuf_release(&sb);
310
+}
311
+
312
#define TRACE_KEY "GIT_TRACE_SHALLOW"
313
314
/*
t/t5304-prune.sh
+10
@@ -221,4 +221,14 @@ EOF
221
test_cmp expected actual
222
'
223
224
+test_expect_success 'prune .git/shallow' '
225
+ SHA1=`echo hi|git commit-tree HEAD^{tree}` &&
226
+ echo $SHA1 >.git/shallow &&
227
+ git prune --dry-run >out &&
228
+ grep $SHA1 .git/shallow &&
229
+ grep $SHA1 out &&
230
+ git prune &&
231
+ ! test -f .git/shallow
232
+'
233
+
234
test_done