From: Junio C Hamano Date: Mon, 16 Jun 2014 19:17:58 +0000 (-0700) Subject: Merge branch 'jl/remote-rm-prune' X-Git-Tag: v2.1.0-rc0~118 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/474df928b11975d8c0ef532a3b61e1b86143406a?ds=inline;hp=-c Merge branch 'jl/remote-rm-prune' "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 --- 474df928b11975d8c0ef532a3b61e1b86143406a diff --combined builtin/remote.c index 9f6202167a,9b3e368983..c9102e8fe9 --- a/builtin/remote.c +++ b/builtin/remote.c @@@ -282,7 -282,7 +282,7 @@@ static int config_read_branches(const c item = string_list_insert(&branch_list, name); if (!item->util) - item->util = xcalloc(sizeof(struct branch_info), 1); + item->util = xcalloc(1, sizeof(struct branch_info)); info = item->util; if (type == REMOTE) { if (info->remote_name) @@@ -398,7 -398,7 +398,7 @@@ static int get_push_ref_states(const st item = string_list_append(&states->push, abbrev_branch(ref->peer_ref->name)); - item->util = xcalloc(sizeof(struct push_info), 1); + item->util = xcalloc(1, sizeof(struct push_info)); info = item->util; info->forced = ref->force; info->dest = xstrdup(abbrev_branch(ref->name)); @@@ -433,7 -433,7 +433,7 @@@ static int get_push_ref_states_noquery( states->push.strdup_strings = 1; if (!remote->push_refspec_nr) { item = string_list_append(&states->push, _("(matching)")); - info = item->util = xcalloc(sizeof(struct push_info), 1); + info = item->util = xcalloc(1, sizeof(struct push_info)); info->status = PUSH_STATUS_NOTQUERIED; info->dest = xstrdup(item->string); } @@@ -446,7 -446,7 +446,7 @@@ else item = string_list_append(&states->push, _("(delete)")); - info = item->util = xcalloc(sizeof(struct push_info), 1); + info = item->util = xcalloc(1, sizeof(struct push_info)); info->forced = spec->force; info->status = PUSH_STATUS_NOTQUERIED; info->dest = xstrdup(spec->dst ? spec->dst : item->string); @@@ -749,15 -749,23 +749,23 @@@ static int mv(int argc, const char **ar 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 +797,6 @@@ static int rm(int argc, const char **ar 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 +841,12 @@@ } 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 +1313,8 @@@ static int prune_remote(const char *rem { 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 +1328,20 @@@ 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 +1351,11 @@@ 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 --combined refs.c index 68982637ed,59fb70087a..ceeeec645c --- a/refs.c +++ b/refs.c @@@ -1611,6 -1611,7 +1611,7 @@@ int peel_ref(const char *refname, unsig struct warn_if_dangling_data { FILE *fp; const char *refname; + const struct string_list *refnames; const char *msg_fmt; }; @@@ -1625,8 -1626,12 +1626,12 @@@ static int warn_if_dangling_symref(cons 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 +1644,18 @@@ void warn_dangling_symref(FILE *fp, con 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); } @@@ -1999,6 -2016,7 +2016,6 @@@ int dwim_log(const char *str, int len, *log = NULL; for (p = ref_rev_parse_rules; *p; p++) { - struct stat st; unsigned char hash[20]; char path[PATH_MAX]; const char *ref, *it; @@@ -2007,9 -2025,12 +2024,9 @@@ ref = resolve_ref_unsafe(path, hash, 1, NULL); if (!ref) continue; - if (!stat(git_path("logs/%s", path), &st) && - S_ISREG(st.st_mode)) + if (reflog_exists(path)) it = path; - else if (strcmp(ref, path) && - !stat(git_path("logs/%s", ref), &st) && - S_ISREG(st.st_mode)) + else if (strcmp(ref, path) && reflog_exists(ref)) it = ref; else continue; @@@ -2427,7 -2448,7 +2444,7 @@@ static int curate_packed_ref_fn(struct 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; @@@ -3042,19 -3063,6 +3059,19 @@@ int read_ref_at(const char *refname, un return 1; } +int reflog_exists(const char *refname) +{ + struct stat st; + + return !lstat(git_path("logs/%s", refname), &st) && + S_ISREG(st.st_mode); +} + +int delete_reflog(const char *refname) +{ + return remove_path(git_path("logs/%s", refname)); +} + static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data) { unsigned char osha1[20], nsha1[20]; @@@ -3252,9 -3260,9 +3269,9 @@@ static struct ref_lock *update_ref_lock if (!lock) { const char *str = "Cannot lock the ref '%s'."; switch (onerr) { - case MSG_ON_ERR: error(str, refname); break; - case DIE_ON_ERR: die(str, refname); break; - case QUIET_ON_ERR: break; + case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break; + case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break; + case UPDATE_REFS_QUIET_ON_ERR: break; } } return lock; @@@ -3267,118 -3275,15 +3284,118 @@@ static int update_ref_write(const char if (write_ref_sha1(lock, sha1, action) < 0) { const char *str = "Cannot update the ref '%s'."; switch (onerr) { - case MSG_ON_ERR: error(str, refname); break; - case DIE_ON_ERR: die(str, refname); break; - case QUIET_ON_ERR: break; + case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break; + case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break; + case UPDATE_REFS_QUIET_ON_ERR: break; } return 1; } return 0; } +/** + * Information needed for a single ref update. Set new_sha1 to the + * new value or to zero to delete the ref. To check the old value + * while locking the ref, set have_old to 1 and set old_sha1 to the + * value or to zero to ensure the ref does not exist before update. + */ +struct ref_update { + unsigned char new_sha1[20]; + unsigned char old_sha1[20]; + int flags; /* REF_NODEREF? */ + int have_old; /* 1 if old_sha1 is valid, 0 otherwise */ + struct ref_lock *lock; + int type; + const char refname[FLEX_ARRAY]; +}; + +/* + * Data structure for holding a reference transaction, which can + * consist of checks and updates to multiple references, carried out + * as atomically as possible. This structure is opaque to callers. + */ +struct ref_transaction { + struct ref_update **updates; + size_t alloc; + size_t nr; +}; + +struct ref_transaction *ref_transaction_begin(void) +{ + return xcalloc(1, sizeof(struct ref_transaction)); +} + +static void ref_transaction_free(struct ref_transaction *transaction) +{ + int i; + + for (i = 0; i < transaction->nr; i++) + free(transaction->updates[i]); + + free(transaction->updates); + free(transaction); +} + +void ref_transaction_rollback(struct ref_transaction *transaction) +{ + ref_transaction_free(transaction); +} + +static struct ref_update *add_update(struct ref_transaction *transaction, + const char *refname) +{ + size_t len = strlen(refname); + struct ref_update *update = xcalloc(1, sizeof(*update) + len + 1); + + strcpy((char *)update->refname, refname); + ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc); + transaction->updates[transaction->nr++] = update; + return update; +} + +void ref_transaction_update(struct ref_transaction *transaction, + const char *refname, + unsigned char *new_sha1, unsigned char *old_sha1, + int flags, int have_old) +{ + struct ref_update *update = add_update(transaction, refname); + + hashcpy(update->new_sha1, new_sha1); + update->flags = flags; + update->have_old = have_old; + if (have_old) + hashcpy(update->old_sha1, old_sha1); +} + +void ref_transaction_create(struct ref_transaction *transaction, + const char *refname, + unsigned char *new_sha1, + int flags) +{ + struct ref_update *update = add_update(transaction, refname); + + assert(!is_null_sha1(new_sha1)); + hashcpy(update->new_sha1, new_sha1); + hashclr(update->old_sha1); + update->flags = flags; + update->have_old = 1; +} + +void ref_transaction_delete(struct ref_transaction *transaction, + const char *refname, + unsigned char *old_sha1, + int flags, int have_old) +{ + struct ref_update *update = add_update(transaction, refname); + + update->flags = flags; + update->have_old = have_old; + if (have_old) { + assert(!is_null_sha1(old_sha1)); + hashcpy(update->old_sha1, old_sha1); + } +} + int update_ref(const char *action, const char *refname, const unsigned char *sha1, const unsigned char *oldval, int flags, enum action_on_err onerr) @@@ -3394,7 -3299,7 +3411,7 @@@ static int ref_update_compare(const voi { const struct ref_update * const *u1 = r1; const struct ref_update * const *u2 = r2; - return strcmp((*u1)->ref_name, (*u2)->ref_name); + return strcmp((*u1)->refname, (*u2)->refname); } static int ref_update_reject_duplicates(struct ref_update **updates, int n, @@@ -3402,15 -3307,15 +3419,15 @@@ { int i; for (i = 1; i < n; i++) - if (!strcmp(updates[i - 1]->ref_name, updates[i]->ref_name)) { + if (!strcmp(updates[i - 1]->refname, updates[i]->refname)) { const char *str = "Multiple updates for ref '%s' not allowed."; switch (onerr) { - case MSG_ON_ERR: - error(str, updates[i]->ref_name); break; - case DIE_ON_ERR: - die(str, updates[i]->ref_name); break; - case QUIET_ON_ERR: + case UPDATE_REFS_MSG_ON_ERR: + error(str, updates[i]->refname); break; + case UPDATE_REFS_DIE_ON_ERR: + die(str, updates[i]->refname); break; + case UPDATE_REFS_QUIET_ON_ERR: break; } return 1; @@@ -3418,21 -3323,26 +3435,21 @@@ return 0; } -int update_refs(const char *action, const struct ref_update **updates_orig, - int n, enum action_on_err onerr) +int ref_transaction_commit(struct ref_transaction *transaction, + const char *msg, enum action_on_err onerr) { int ret = 0, delnum = 0, i; - struct ref_update **updates; - int *types; - struct ref_lock **locks; const char **delnames; + int n = transaction->nr; + struct ref_update **updates = transaction->updates; - if (!updates_orig || !n) + if (!n) return 0; /* Allocate work space */ - updates = xmalloc(sizeof(*updates) * n); - types = xmalloc(sizeof(*types) * n); - locks = xcalloc(n, sizeof(*locks)); delnames = xmalloc(sizeof(*delnames) * n); /* Copy, sort, and reject duplicate refs */ - memcpy(updates, updates_orig, sizeof(*updates) * n); qsort(updates, n, sizeof(*updates), ref_update_compare); ret = ref_update_reject_duplicates(updates, n, onerr); if (ret) @@@ -3440,44 -3350,35 +3457,44 @@@ /* Acquire all locks while verifying old values */ for (i = 0; i < n; i++) { - locks[i] = update_ref_lock(updates[i]->ref_name, - (updates[i]->have_old ? - updates[i]->old_sha1 : NULL), - updates[i]->flags, - &types[i], onerr); - if (!locks[i]) { + struct ref_update *update = updates[i]; + + update->lock = update_ref_lock(update->refname, + (update->have_old ? + update->old_sha1 : NULL), + update->flags, + &update->type, onerr); + if (!update->lock) { ret = 1; goto cleanup; } } /* Perform updates first so live commits remain referenced */ - for (i = 0; i < n; i++) - if (!is_null_sha1(updates[i]->new_sha1)) { - ret = update_ref_write(action, - updates[i]->ref_name, - updates[i]->new_sha1, - locks[i], onerr); - locks[i] = NULL; /* freed by update_ref_write */ + for (i = 0; i < n; i++) { + struct ref_update *update = updates[i]; + + if (!is_null_sha1(update->new_sha1)) { + ret = update_ref_write(msg, + update->refname, + update->new_sha1, + update->lock, onerr); + update->lock = NULL; /* freed by update_ref_write */ if (ret) goto cleanup; } + } /* Perform deletes now that updates are safely completed */ - for (i = 0; i < n; i++) - if (locks[i]) { - delnames[delnum++] = locks[i]->ref_name; - ret |= delete_ref_loose(locks[i], types[i]); + for (i = 0; i < n; i++) { + struct ref_update *update = updates[i]; + + if (update->lock) { + delnames[delnum++] = update->lock->ref_name; + ret |= delete_ref_loose(update->lock, update->type); } + } + ret |= repack_without_refs(delnames, delnum); for (i = 0; i < delnum; i++) unlink_or_warn(git_path("logs/%s", delnames[i])); @@@ -3485,10 -3386,12 +3502,10 @@@ cleanup: for (i = 0; i < n; i++) - if (locks[i]) - unlock_ref(locks[i]); - free(updates); - free(types); - free(locks); + if (updates[i]->lock) + unlock_ref(updates[i]->lock); free(delnames); + ref_transaction_free(transaction); return ret; } diff --combined refs.h index 09ff483c19,1440acc06c..4e3050d976 --- a/refs.h +++ b/refs.h @@@ -10,7 -10,19 +10,7 @@@ struct ref_lock int force_write; }; -/** - * Information needed for a single ref update. Set new_sha1 to the - * new value or to zero to delete the ref. To check the old value - * while locking the ref, set have_old to 1 and set old_sha1 to the - * value or to zero to ensure the ref does not exist before update. - */ -struct ref_update { - const char *ref_name; - unsigned char new_sha1[20]; - unsigned char old_sha1[20]; - int flags; /* REF_NODEREF? */ - int have_old; /* 1 if old_sha1 is valid, 0 otherwise */ -}; +struct ref_transaction; /* * Bit values set in the flags argument passed to each_ref_fn(): @@@ -77,6 -89,7 +77,7 @@@ static inline const char *has_glob_spec 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 @@@ -120,6 -133,8 +121,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 *); /* @@@ -154,19 -169,13 +157,19 @@@ extern void unlock_ref(struct ref_lock extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg); /** Setup reflog before using. **/ -int log_ref_setup(const char *ref_name, char *logfile, int bufsize); +int log_ref_setup(const char *refname, char *logfile, int bufsize); /** Reads log for the value of ref during at_time. **/ extern int read_ref_at(const char *refname, unsigned long at_time, int cnt, unsigned char *sha1, char **msg, unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt); +/** Check if a particular reflog exists */ +extern int reflog_exists(const char *refname); + +/** Delete a reflog */ +extern int delete_reflog(const char *refname); + /* iterate over reflog entries */ typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *); int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data); @@@ -208,80 -217,18 +211,80 @@@ extern int rename_ref(const char *oldre */ extern int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1); -/** lock a ref and then write its file */ -enum action_on_err { MSG_ON_ERR, DIE_ON_ERR, QUIET_ON_ERR }; +enum action_on_err { + UPDATE_REFS_MSG_ON_ERR, + UPDATE_REFS_DIE_ON_ERR, + UPDATE_REFS_QUIET_ON_ERR +}; + +/* + * Begin a reference transaction. The reference transaction must + * eventually be commited using ref_transaction_commit() or rolled + * back using ref_transaction_rollback(). + */ +struct ref_transaction *ref_transaction_begin(void); + +/* + * Roll back a ref_transaction and free all associated data. + */ +void ref_transaction_rollback(struct ref_transaction *transaction); + + +/* + * The following functions add a reference check or update to a + * ref_transaction. In all of them, refname is the name of the + * reference to be affected. The functions make internal copies of + * refname, so the caller retains ownership of the parameter. flags + * can be REF_NODEREF; it is passed to update_ref_lock(). + */ + + +/* + * Add a reference update to transaction. new_sha1 is the value that + * the reference should have after the update, or zeros if it should + * be deleted. If have_old is true, then old_sha1 holds the value + * that the reference should have had before the update, or zeros if + * it must not have existed beforehand. + */ +void ref_transaction_update(struct ref_transaction *transaction, + const char *refname, + unsigned char *new_sha1, unsigned char *old_sha1, + int flags, int have_old); + +/* + * Add a reference creation to transaction. new_sha1 is the value + * that the reference should have after the update; it must not be the + * null SHA-1. It is verified that the reference does not exist + * already. + */ +void ref_transaction_create(struct ref_transaction *transaction, + const char *refname, + unsigned char *new_sha1, + int flags); + +/* + * Add a reference deletion to transaction. If have_old is true, then + * old_sha1 holds the value that the reference should have had before + * the update (which must not be the null SHA-1). + */ +void ref_transaction_delete(struct ref_transaction *transaction, + const char *refname, + unsigned char *old_sha1, + int flags, int have_old); + +/* + * Commit all of the changes that have been queued in transaction, as + * atomically as possible. Return a nonzero value if there is a + * problem. The ref_transaction is freed by this function. + */ +int ref_transaction_commit(struct ref_transaction *transaction, + const char *msg, enum action_on_err onerr); + +/** Lock a ref and then write its file */ int update_ref(const char *action, const char *refname, const unsigned char *sha1, const unsigned char *oldval, int flags, enum action_on_err onerr); -/** - * Lock all refs and then perform all modifications. - */ -int update_refs(const char *action, const struct ref_update **updates, - int n, enum action_on_err onerr); - extern int parse_hide_refs_config(const char *var, const char *value, const char *); extern int ref_is_hidden(const char *);