refs.c: pass a list of names to skip to is_refname_available

Change is_refname_available to take a list of strings to exclude when checking for conflicts instead of just one single name. We can already exclude a single name for the sake of renames. This generalizes that support. ref_transaction_commit already tracks a set of refs that are being deleted in an array. This array is then used to exclude refs from being written to the packed-refs file. At some stage we will want to change this array to a struct string_list and then we can pass it to is_refname_available via the call to lock_ref_sha1_basic. That will allow us to perform transactions that perform multiple renames as long as there are no conflicts within the starting or ending state. For example, that would allow a single transaction that contains two renames that are both individually conflicting: m -> n/n n -> m/m No functional change intended yet. Signed-off-by: Ronnie Sahlberg <sahlberg@google.com> Signed-off-by: Jonathan Nieder <jrnieder@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Ronnie Sahlberg committed May 1, 2014 at 11:16 UTC 5fe7d825da8af83a9a3b9bc7f3295f836b6e3a75
1 file changed +32 -18
refs.c
+32 -18
@@ -787,13 +787,13 @@ static void prime_ref_dir(struct ref_dir *dir)
787 }
788 }
789
790 -static int entry_matches(struct ref_entry *entry, const char *refname)
790 +static int entry_matches(struct ref_entry *entry, const struct string_list *list)
791 {
792 - return refname && !strcmp(entry->name, refname);
792 + return list && string_list_has_string(list, entry->name);
793 }
794
795 struct nonmatching_ref_data {
796 - const char *skip;
796 + const struct string_list *skip;
797 struct ref_entry *found;
798 };
799
@@ -817,16 +817,19 @@ static void report_refname_conflict(struct ref_entry *entry,
817 /*
818 * Return true iff a reference named refname could be created without
819 * conflicting with the name of an existing reference in dir. If
820 - * oldrefname is non-NULL, ignore potential conflicts with oldrefname
821 - * (e.g., because oldrefname is scheduled for deletion in the same
820 + * skip is non-NULL, ignore potential conflicts with refs in skip
821 + * (e.g., because they are scheduled for deletion in the same
822 * operation).
823 *
824 * Two reference names conflict if one of them exactly matches the
825 * leading components of the other; e.g., "foo/bar" conflicts with
826 * both "foo" and with "foo/bar/baz" but not with "foo/bar" or
827 * "foo/barbados".
828 + *
829 + * skip must be sorted.
830 */
829 -static int is_refname_available(const char *refname, const char *oldrefname,
831 +static int is_refname_available(const char *refname,
832 + const struct string_list *skip,
833 struct ref_dir *dir)
834 {
835 const char *slash;
@@ -840,12 +843,12 @@ static int is_refname_available(const char *refname, const char *oldrefname,
843 * looking for a conflict with a leaf entry.
844 *
845 * If we find one, we still must make sure it is
843 - * not "oldrefname".
846 + * not in "skip".
847 */
848 pos = search_ref_dir(dir, refname, slash - refname);
849 if (pos >= 0) {
850 struct ref_entry *entry = dir->entries[pos];
848 - if (entry_matches(entry, oldrefname))
851 + if (entry_matches(entry, skip))
852 return 1;
853 report_refname_conflict(entry, refname);
854 return 0;
@@ -878,13 +881,13 @@ static int is_refname_available(const char *refname, const char *oldrefname,
881 /*
882 * We found a directory named "refname". It is a
883 * problem iff it contains any ref that is not
881 - * "oldrefname".
884 + * in "skip".
885 */
886 struct ref_entry *entry = dir->entries[pos];
887 struct ref_dir *dir = get_ref_dir(entry);
888 struct nonmatching_ref_data data;
889
887 - data.skip = oldrefname;
890 + data.skip = skip;
891 sort_ref_dir(dir);
892 if (!do_for_each_entry_in_dir(dir, 0, nonmatching_ref_fn, &data))
893 return 1;
@@ -2139,6 +2142,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
2142 */
2143 static struct ref_lock *lock_ref_sha1_basic(const char *refname,
2144 const unsigned char *old_sha1,
2145 + const struct string_list *skip,
2146 int flags, int *type_p)
2147 {
2148 char *ref_file;
@@ -2188,7 +2192,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
2192 * name is a proper prefix of our refname.
2193 */
2194 if (missing &&
2191 - !is_refname_available(refname, NULL, get_packed_refs(&ref_cache))) {
2195 + !is_refname_available(refname, skip, get_packed_refs(&ref_cache))) {
2196 last_errno = ENOTDIR;
2197 goto error_return;
2198 }
@@ -2246,7 +2250,7 @@ struct ref_lock *lock_any_ref_for_update(const char *refname,
2250 const unsigned char *old_sha1,
2251 int flags, int *type_p)
2252 {
2249 - return lock_ref_sha1_basic(refname, old_sha1, flags, type_p);
2253 + return lock_ref_sha1_basic(refname, old_sha1, NULL, flags, type_p);
2254 }
2255
2256 /*
@@ -2690,6 +2694,18 @@ static int rename_tmp_log(const char *newrefname)
2694 return 0;
2695 }
2696
2697 +static int rename_ref_available(const char *oldname, const char *newname)
2698 +{
2699 + struct string_list skip = STRING_LIST_INIT_NODUP;
2700 + int ret;
2701 +
2702 + string_list_insert(&skip, oldname);
2703 + ret = is_refname_available(newname, &skip, get_packed_refs(&ref_cache))
2704 + && is_refname_available(newname, &skip, get_loose_refs(&ref_cache));
2705 + string_list_clear(&skip, 0);
2706 + return ret;
2707 +}
2708 +
2709 int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
2710 {
2711 unsigned char sha1[20], orig_sha1[20];
@@ -2709,10 +2725,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
2725 if (!symref)
2726 return error("refname %s not found", oldrefname);
2727
2712 - if (!is_refname_available(newrefname, oldrefname, get_packed_refs(&ref_cache)))
2713 - return 1;
2714 -
2715 - if (!is_refname_available(newrefname, oldrefname, get_loose_refs(&ref_cache)))
2728 + if (!rename_ref_available(oldrefname, newrefname))
2729 return 1;
2730
2731 if (log && rename(git_path("logs/%s", oldrefname), git_path(TMP_RENAMED_LOG)))
@@ -2742,7 +2755,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
2755
2756 logmoved = log;
2757
2745 - lock = lock_ref_sha1_basic(newrefname, NULL, 0, NULL);
2758 + lock = lock_ref_sha1_basic(newrefname, NULL, NULL, 0, NULL);
2759 if (!lock) {
2760 error("unable to lock %s for update", newrefname);
2761 goto rollback;
@@ -2757,7 +2770,7 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
2770 return 0;
2771
2772 rollback:
2760 - lock = lock_ref_sha1_basic(oldrefname, NULL, 0, NULL);
2773 + lock = lock_ref_sha1_basic(oldrefname, NULL, NULL, 0, NULL);
2774 if (!lock) {
2775 error("unable to lock %s for rollback", oldrefname);
2776 goto rollbacklog;
@@ -3636,6 +3649,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
3649 (update->have_old ?
3650 update->old_sha1 :
3651 NULL),
3652 + NULL,
3653 update->flags,
3654 &update->type);
3655 if (!update->lock) {