When we are updating more than one single ref, i.e. not a commit, then write the updated refs directly to the packed refs file instead of writing them as loose refs. Change clone to use a transaction instead of using the pacekd refs api. Signed-off-by: Ronnie Sahlberg <sahlberg@xxxxxxxxxx> --- builtin/clone.c | 16 +++++++++---- refs.c | 74 +++++++++++++++++++++++++++++++++++++++++---------------- 2 files changed, 66 insertions(+), 24 deletions(-) diff --git a/builtin/clone.c b/builtin/clone.c index b12989d..000a236 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -497,17 +497,25 @@ static struct ref *wanted_peer_refs(const struct ref *refs, static void write_remote_refs(const struct ref *local_refs) { const struct ref *r; + struct ref_transaction *transaction; + struct strbuf err = STRBUF_INIT; - lock_packed_refs(LOCK_DIE_ON_ERROR); + transaction = transaction_begin(&err); + if (!transaction) + die("%s", err.buf); for (r = local_refs; r; r = r->next) { if (!r->peer_ref) continue; - add_packed_ref(r->peer_ref->name, r->old_sha1); + if (transaction_update_sha1(transaction, r->peer_ref->name, + r->old_sha1, NULL, 0, 0, NULL, + &err)) + die("%s", err.buf); } - if (commit_packed_refs()) - die_errno("unable to overwrite old ref-pack file"); + if (transaction_commit(transaction, &err)) + die("%s", err.buf); + transaction_free(transaction); } static void write_followtags(const struct ref *refs, const char *msg) diff --git a/refs.c b/refs.c index 4281011..6aa64dd 100644 --- a/refs.c +++ b/refs.c @@ -2481,31 +2481,18 @@ static int repack_without_refs(const char **refnames, int n, struct strbuf *err) struct ref_dir *packed; struct string_list refs_to_delete = STRING_LIST_INIT_DUP; struct string_list_item *ref_to_delete; - int i, ret, removed = 0; + int i, ret; /* Look for a packed ref */ for (i = 0; i < n; i++) if (get_packed_ref(refnames[i])) break; - /* Avoid processing if we have nothing to do */ - if (i == n) - return 0; /* no refname exists in packed refs */ - packed = get_packed_refs(&ref_cache); /* Remove refnames from the cache */ for (i = 0; i < n; i++) - if (remove_entry(packed, refnames[i]) != -1) - removed = 1; - if (!removed) { - /* - * All packed entries disappeared while we were - * acquiring the lock. - */ - rollback_packed_refs(); - return 0; - } + remove_entry(packed, refnames[i]); /* Remove any other accumulated cruft */ do_for_each_entry_in_dir(packed, 0, curate_packed_ref_fn, &refs_to_delete); @@ -3614,6 +3601,7 @@ int transaction_commit(struct ref_transaction *transaction, struct strbuf *err) { int ret = 0, delnum = 0, i, df_conflict = 0, need_repack = 0; + int num_updates = 0; const char **delnames; int n = transaction->nr; struct packed_ref_cache *packed_ref_cache; @@ -3647,18 +3635,38 @@ int transaction_commit(struct ref_transaction *transaction, goto cleanup; } - /* any loose refs are to be deleted are first copied to packed refs */ + /* count how many refs we are updating (not deleting) */ for (i = 0; i < n; i++) { struct ref_update *update = updates[i]; unsigned char sha1[20]; if (update->update_type != UPDATE_SHA1) continue; - if (!is_null_sha1(update->new_sha1)) + if (is_null_sha1(update->new_sha1)) + continue; + + num_updates++; + } + + /* + * Always copy loose refs that are to be deleted to the packed refs. + * If we are updating multiple refs then copy all non symref refs + * to the packed refs too. + */ + for (i = 0; i < n; i++) { + struct ref_update *update = updates[i]; + unsigned char sha1[20]; + int flag; + + if (update->update_type != UPDATE_SHA1) + continue; + if (num_updates < 2 && !is_null_sha1(update->new_sha1)) continue; if (get_packed_ref(update->refname)) continue; - if (!resolve_ref_unsafe(update->refname, sha1, 1, NULL)) + if (!resolve_ref_unsafe(update->refname, sha1, 1, &flag)) + continue; + if (flag & REF_ISSYMREF) continue; add_packed_ref(update->refname, sha1); @@ -3682,11 +3690,14 @@ int transaction_commit(struct ref_transaction *transaction, goto cleanup; } } + need_repack = 0; /* * At this stage any refs that are to be deleted have been moved to the * packed refs file anf the packed refs file is deleted. We can now * safely delete these loose refs. + * If we are updating multiple normal refs, then those will also be in + * the packed refs file so we can delete them too. */ /* Unlink any loose refs scheduled for deletion */ @@ -3724,7 +3735,10 @@ int transaction_commit(struct ref_transaction *transaction, update->lock = NULL; } - /* Acquire all ref locks for updates while verifying old values */ + /* + * Acquire all ref locks for updates while verifying old values. + * If we are multi-updating then update them in packed refs. + */ for (i = 0; i < n; i++) { struct ref_update *update = updates[i]; @@ -3748,6 +3762,22 @@ int transaction_commit(struct ref_transaction *transaction, ret = -1; goto cleanup; } + if (num_updates < 2 || update->type & REF_ISSYMREF) + continue; + + if (delete_ref_loose(update->lock, update->type, err)) { + ret = -1; + goto cleanup; + } + unlock_ref(update->lock); + update->lock = NULL; + + packed = get_packed_refs(&ref_cache);; + remove_entry(packed, update->refname); + add_packed_ref(update->refname, update->new_sha1); + need_repack = 1; + + try_remove_empty_parents((char *)update->refname); } /* delete reflog for all deleted refs */ @@ -3795,7 +3825,7 @@ int transaction_commit(struct ref_transaction *transaction, if (update->update_type != UPDATE_SHA1) continue; - if (!is_null_sha1(update->new_sha1)) { + if (update->lock && !is_null_sha1(update->new_sha1)) { ret = write_ref_sha1(update->lock, update->new_sha1, update->msg); update->lock = NULL; /* freed by write_ref_sha1 */ @@ -3860,6 +3890,10 @@ int transaction_commit(struct ref_transaction *transaction, } } + if (need_repack) { + packed = get_packed_refs(&ref_cache); + sort_ref_dir(packed); + } if (repack_without_refs(delnames, delnum, err)) ret = -1; clear_loose_ref_cache(&ref_cache); -- 2.0.0.583.g402232d -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html