Merge branch 'jl/remote-rm-prune' into maint
authorJunio C Hamano <gitster@pobox.com>
Wed, 25 Jun 2014 18:49:01 +0000 (11:49 -0700)
committerJunio C Hamano <gitster@pobox.com>
Wed, 25 Jun 2014 18:49:01 +0000 (11:49 -0700)
"git remote rm" and "git remote prune" can involve removing many
refs at once, which is not a very efficient thing to do when very
many refs exist in the packed-refs file.

* jl/remote-rm-prune:
remote prune: optimize "dangling symref" check/warning
remote: repack packed-refs once when deleting multiple refs
remote rm: delete remote configuration as the last

builtin/remote.c
refs.c
refs.h
index b3ab4cf8f6517c715845db7e17c67cad5f50d730..9b3e368983570be72a5c4b8476f69dc39522d192 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;
 }
 
@@ -789,10 +797,6 @@ static int rm(int argc, const char **argv)
        known_remotes.to_delete = remote;
        for_each_remote(add_known_remote, &known_remotes);
 
-       strbuf_addf(&buf, "remote.%s", remote->name);
-       if (git_config_rename_section(buf.buf, NULL) < 1)
-               return error(_("Could not remove config section '%s'"), buf.buf);
-
        read_branches();
        for (i = 0; i < branch_list.nr; i++) {
                struct string_list_item *item = branch_list.items + i;
@@ -837,6 +841,12 @@ static int rm(int argc, const char **argv)
        }
        string_list_clear(&skipped, 0);
 
+       if (!result) {
+               strbuf_addf(&buf, "remote.%s", remote->name);
+               if (git_config_rename_section(buf.buf, NULL) < 1)
+                       return error(_("Could not remove config section '%s'"), buf.buf);
+       }
+
        return result;
 }
 
@@ -1303,6 +1313,8 @@ static int prune_remote(const char *remote, int dry_run)
 {
        int result = 0, i;
        struct ref_states states;
+       struct string_list delete_refs_list = STRING_LIST_INIT_NODUP;
+       const char **delete_refs;
        const char *dangling_msg = dry_run
                ? _(" %s will become dangling!")
                : _(" %s has become dangling!");
@@ -1316,11 +1328,20 @@ 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++) {
                const char *refname = states.stale.items[i].util;
 
+               string_list_insert(&delete_refs_list, refname);
+
                if (!dry_run)
                        result |= delete_ref(refname, NULL, 0);
 
@@ -1330,9 +1351,11 @@ static int prune_remote(const char *remote, int dry_run)
                else
                        printf_ln(_(" * [pruned] %s"),
                               abbrev_ref(refname, "refs/remotes/"));
-               warn_dangling_symref(stdout, dangling_msg, refname);
        }
 
+       warn_dangling_symrefs(stdout, dangling_msg, &delete_refs_list);
+       string_list_clear(&delete_refs_list, 0);
+
        free_remote_ref_states(&states);
        return result;
 }
diff --git a/refs.c b/refs.c
index 28d5eca8eaff7be48a4b89e6217886b8c96eb3b2..59fb70087a438a763cdbfb2c4e75de36d533aead 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1611,6 +1611,7 @@ int peel_ref(const char *refname, unsigned char *sha1)
 struct warn_if_dangling_data {
        FILE *fp;
        const char *refname;
+       const struct string_list *refnames;
        const char *msg_fmt;
 };
 
@@ -1625,8 +1626,12 @@ static int warn_if_dangling_symref(const char *refname, const unsigned char *sha
                return 0;
 
        resolves_to = resolve_ref_unsafe(refname, junk, 0, NULL);
-       if (!resolves_to || strcmp(resolves_to, d->refname))
+       if (!resolves_to
+           || (d->refname
+               ? strcmp(resolves_to, d->refname)
+               : !string_list_has_string(d->refnames, resolves_to))) {
                return 0;
+       }
 
        fprintf(d->fp, d->msg_fmt, refname);
        fputc('\n', d->fp);
@@ -1639,6 +1644,18 @@ void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
 
        data.fp = fp;
        data.refname = refname;
+       data.refnames = NULL;
+       data.msg_fmt = msg_fmt;
+       for_each_rawref(warn_if_dangling_symref, &data);
+}
+
+void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames)
+{
+       struct warn_if_dangling_data data;
+
+       data.fp = fp;
+       data.refname = NULL;
+       data.refnames = refnames;
        data.msg_fmt = msg_fmt;
        for_each_rawref(warn_if_dangling_symref, &data);
 }
@@ -2431,7 +2448,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..1440acc06ce59f35d23719adef18774731efe5a4 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -89,6 +89,7 @@ static inline const char *has_glob_specials(const char *pattern)
 extern int for_each_rawref(each_ref_fn, void *);
 
 extern void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname);
+extern void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list* refnames);
 
 /*
  * Lock the packed-refs file for writing.  Flags is passed to
@@ -132,6 +133,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 *);
 
 /*