Skip to content

Commit 8198907

Browse files
phordgitster
authored andcommitted
use delete_refs when deleting tags or branches
'git tag -d' accepts one or more tag refs to delete, but each deletion is done by calling `delete_ref` on each argv. This is very slow when removing from packed refs. Use delete_refs instead so all the removals can be done inside a single transaction with a single update. Do the same for 'git branch -d'. Since delete_refs performs all the packed-refs delete operations inside a single transaction, if any of the deletes fail then all them will be skipped. In practice, none of them should fail since we verify the hash of each one before calling delete_refs, but some network error or odd permissions problem could have different results after this change. Also, since the file-backed deletions are not performed in the same transaction, those could succeed even when the packed-refs transaction fails. After deleting branches, remove the branch config only if the branch ref was removed and was not subsequently added back in. A manual test deleting 24,000 tags took about 30 minutes using delete_ref. It takes about 5 seconds using delete_refs. Acked-by: Elijah Newren <[email protected]> Signed-off-by: Phil Hord <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 66e871b commit 8198907

File tree

2 files changed

+62
-29
lines changed

2 files changed

+62
-29
lines changed

builtin/branch.c

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,9 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
202202
int remote_branch = 0;
203203
struct strbuf bname = STRBUF_INIT;
204204
unsigned allowed_interpret;
205+
struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
206+
struct string_list_item *item;
207+
int branch_name_pos;
205208

206209
switch (kinds) {
207210
case FILTER_REFS_REMOTES:
@@ -219,6 +222,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
219222
default:
220223
die(_("cannot use -a with -d"));
221224
}
225+
branch_name_pos = strcspn(fmt, "%");
222226

223227
if (!force) {
224228
head_rev = lookup_commit_reference(the_repository, &head_oid);
@@ -265,30 +269,35 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
265269
goto next;
266270
}
267271

268-
if (delete_ref(NULL, name, is_null_oid(&oid) ? NULL : &oid,
269-
REF_NO_DEREF)) {
270-
error(remote_branch
271-
? _("Error deleting remote-tracking branch '%s'")
272-
: _("Error deleting branch '%s'"),
273-
bname.buf);
274-
ret = 1;
275-
goto next;
276-
}
277-
if (!quiet) {
278-
printf(remote_branch
279-
? _("Deleted remote-tracking branch %s (was %s).\n")
280-
: _("Deleted branch %s (was %s).\n"),
281-
bname.buf,
282-
(flags & REF_ISBROKEN) ? "broken"
283-
: (flags & REF_ISSYMREF) ? target
284-
: find_unique_abbrev(&oid, DEFAULT_ABBREV));
285-
}
286-
delete_branch_config(bname.buf);
272+
item = string_list_append(&refs_to_delete, name);
273+
item->util = xstrdup((flags & REF_ISBROKEN) ? "broken"
274+
: (flags & REF_ISSYMREF) ? target
275+
: find_unique_abbrev(&oid, DEFAULT_ABBREV));
287276

288277
next:
289278
free(target);
290279
}
291280

281+
if (delete_refs(NULL, &refs_to_delete, REF_NO_DEREF))
282+
ret = 1;
283+
284+
for_each_string_list_item(item, &refs_to_delete) {
285+
char *describe_ref = item->util;
286+
char *name = item->string;
287+
if (!ref_exists(name)) {
288+
char *refname = name + branch_name_pos;
289+
if (!quiet)
290+
printf(remote_branch
291+
? _("Deleted remote-tracking branch %s (was %s).\n")
292+
: _("Deleted branch %s (was %s).\n"),
293+
name + branch_name_pos, describe_ref);
294+
295+
delete_branch_config(refname);
296+
}
297+
free(describe_ref);
298+
}
299+
string_list_clear(&refs_to_delete, 0);
300+
292301
free(name);
293302
strbuf_release(&bname);
294303

builtin/tag.c

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -72,10 +72,10 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting,
7272
}
7373

7474
typedef int (*each_tag_name_fn)(const char *name, const char *ref,
75-
const struct object_id *oid, const void *cb_data);
75+
const struct object_id *oid, void *cb_data);
7676

7777
static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
78-
const void *cb_data)
78+
void *cb_data)
7979
{
8080
const char **p;
8181
struct strbuf ref = STRBUF_INIT;
@@ -97,18 +97,42 @@ static int for_each_tag_name(const char **argv, each_tag_name_fn fn,
9797
return had_error;
9898
}
9999

100-
static int delete_tag(const char *name, const char *ref,
101-
const struct object_id *oid, const void *cb_data)
100+
static int collect_tags(const char *name, const char *ref,
101+
const struct object_id *oid, void *cb_data)
102102
{
103-
if (delete_ref(NULL, ref, oid, 0))
104-
return 1;
105-
printf(_("Deleted tag '%s' (was %s)\n"), name,
106-
find_unique_abbrev(oid, DEFAULT_ABBREV));
103+
struct string_list *ref_list = cb_data;
104+
105+
string_list_append(ref_list, ref);
106+
ref_list->items[ref_list->nr - 1].util = oiddup(oid);
107107
return 0;
108108
}
109109

110+
static int delete_tags(const char **argv)
111+
{
112+
int result;
113+
struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
114+
struct string_list_item *item;
115+
116+
result = for_each_tag_name(argv, collect_tags, (void *)&refs_to_delete);
117+
if (delete_refs(NULL, &refs_to_delete, REF_NO_DEREF))
118+
result = 1;
119+
120+
for_each_string_list_item(item, &refs_to_delete) {
121+
const char *name = item->string;
122+
struct object_id *oid = item->util;
123+
if (!ref_exists(name))
124+
printf(_("Deleted tag '%s' (was %s)\n"),
125+
item->string + 10,
126+
find_unique_abbrev(oid, DEFAULT_ABBREV));
127+
128+
free(oid);
129+
}
130+
string_list_clear(&refs_to_delete, 0);
131+
return result;
132+
}
133+
110134
static int verify_tag(const char *name, const char *ref,
111-
const struct object_id *oid, const void *cb_data)
135+
const struct object_id *oid, void *cb_data)
112136
{
113137
int flags;
114138
const struct ref_format *format = cb_data;
@@ -512,7 +536,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
512536
if (filter.reachable_from || filter.unreachable_from)
513537
die(_("--merged and --no-merged options are only allowed in list mode"));
514538
if (cmdmode == 'd')
515-
return for_each_tag_name(argv, delete_tag, NULL);
539+
return delete_tags(argv);
516540
if (cmdmode == 'v') {
517541
if (format.format && verify_ref_format(&format))
518542
usage_with_options(git_tag_usage, options);

0 commit comments

Comments
 (0)