]> git.ipfire.org Git - thirdparty/git.git/commitdiff
remote: repack packed-refs once when deleting multiple refs
authorJens Lindström <jl@opera.com>
Fri, 23 May 2014 10:29:45 +0000 (12:29 +0200)
committerJunio C Hamano <gitster@pobox.com>
Tue, 27 May 2014 19:30:42 +0000 (12:30 -0700)
When 'git remote rm' or 'git remote prune' were used in a repository
with many refs, and needed to delete many remote-tracking refs, a lot
of time was spent deleting those refs since for each deleted ref,
repack_without_refs() was called to rewrite packed-refs without just
that deleted ref.

To avoid this, call repack_without_refs() first to repack without all
the refs that will be deleted, before calling delete_ref() to delete
each one completely.  The call to repack_without_ref() in delete_ref()
then becomes a no-op, since packed-refs already won't contain any of
the deleted refs.

Signed-off-by: Jens Lindström <jl@opera.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin/remote.c
refs.c
refs.h

index 84802cd0a1c8e96b66ee951460976c1cf2ebf688..d33abe6598c737ddc176544c951ca7269db03b79 100644 (file)
@@ -749,15 +749,23 @@ static int mv(int argc, const char **argv)
 
 static int remove_branches(struct string_list *branches)
 {
+       const char **branch_names;
        int i, result = 0;
+
+       branch_names = xmalloc(branches->nr * sizeof(*branch_names));
+       for (i = 0; i < branches->nr; i++)
+               branch_names[i] = branches->items[i].string;
+       result |= repack_without_refs(branch_names, branches->nr);
+       free(branch_names);
+
        for (i = 0; i < branches->nr; i++) {
                struct string_list_item *item = branches->items + i;
                const char *refname = item->string;
-               unsigned char *sha1 = item->util;
 
-               if (delete_ref(refname, sha1, 0))
+               if (delete_ref(refname, NULL, 0))
                        result |= error(_("Could not remove branch %s"), refname);
        }
+
        return result;
 }
 
@@ -1305,6 +1313,7 @@ static int prune_remote(const char *remote, int dry_run)
 {
        int result = 0, i;
        struct ref_states states;
+       const char **delete_refs;
        const char *dangling_msg = dry_run
                ? _(" %s will become dangling!")
                : _(" %s has become dangling!");
@@ -1318,6 +1327,13 @@ static int prune_remote(const char *remote, int dry_run)
                       states.remote->url_nr
                       ? states.remote->url[0]
                       : _("(no URL)"));
+
+               delete_refs = xmalloc(states.stale.nr * sizeof(*delete_refs));
+               for (i = 0; i < states.stale.nr; i++)
+                       delete_refs[i] = states.stale.items[i].util;
+               if (!dry_run)
+                       result |= repack_without_refs(delete_refs, states.stale.nr);
+               free(delete_refs);
        }
 
        for (i = 0; i < states.stale.nr; i++) {
diff --git a/refs.c b/refs.c
index 28d5eca8eaff7be48a4b89e6217886b8c96eb3b2..262c1c26cb0d82f85f59e4a4dc513921be2080f0 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -2431,7 +2431,7 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data)
        return 0;
 }
 
-static int repack_without_refs(const char **refnames, int n)
+int repack_without_refs(const char **refnames, int n)
 {
        struct ref_dir *packed;
        struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
diff --git a/refs.h b/refs.h
index 87a1a79ad659f3520a22ae14bac1d5082cf2c27b..f287c7aa65cea7b0c52a7454fef14d6f4f1d4d38 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -132,6 +132,8 @@ extern void rollback_packed_refs(void);
  */
 int pack_refs(unsigned int flags);
 
+extern int repack_without_refs(const char **refnames, int n);
+
 extern int ref_exists(const char *);
 
 /*