refs: break out ref conflict checks

Create new function find_descendant_ref, to hold one of the ref conflict checks used in verify_refname_available. Multiple backends will need this function, so move it to the common code. Also move rename_ref_available to the common code, because alternate backends might need it and it has no files-backend-specific code. Signed-off-by: David Turner <dturner@twopensource.com> Signed-off-by: Junio C Hamano <gitster@pobox.com> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> Signed-off-by: Jeff King <peff@peff.net>

David Turner committed Nov 10, 2015 at 12:42 UTC 0845122c39c415fa50904ee94c6b60e4e722466b
3 files changed +67 -42
refs.c
+44
@@ -1029,3 +1029,47 @@ int ref_is_hidden(const char *refname)
1029 }
1030 return 0;
1031 }
1032 +
1033 +const char *find_descendant_ref(const char *dirname,
1034 + const struct string_list *extras,
1035 + const struct string_list *skip)
1036 +{
1037 + int pos;
1038 +
1039 + if (!extras)
1040 + return NULL;
1041 +
1042 + /*
1043 + * Look at the place where dirname would be inserted into
1044 + * extras. If there is an entry at that position that starts
1045 + * with dirname (remember, dirname includes the trailing
1046 + * slash) and is not in skip, then we have a conflict.
1047 + */
1048 + for (pos = string_list_find_insert_index(extras, dirname, 0);
1049 + pos < extras->nr; pos++) {
1050 + const char *extra_refname = extras->items[pos].string;
1051 +
1052 + if (!starts_with(extra_refname, dirname))
1053 + break;
1054 +
1055 + if (!skip || !string_list_has_string(skip, extra_refname))
1056 + return extra_refname;
1057 + }
1058 + return NULL;
1059 +}
1060 +
1061 +int rename_ref_available(const char *oldname, const char *newname)
1062 +{
1063 + struct string_list skip = STRING_LIST_INIT_NODUP;
1064 + struct strbuf err = STRBUF_INIT;
1065 + int ret;
1066 +
1067 + string_list_insert(&skip, oldname);
1068 + ret = !verify_refname_available(newname, NULL, &skip, &err);
1069 + if (!ret)
1070 + error("%s", err.buf);
1071 +
1072 + string_list_clear(&skip, 0);
1073 + strbuf_release(&err);
1074 + return ret;
1075 +}
refs/files-backend.c
+7 -42
@@ -728,6 +728,7 @@ static int verify_refname_available_dir(const char *refname,
728 struct strbuf *err)
729 {
730 const char *slash;
731 + const char *extra_refname;
732 int pos;
733 struct strbuf dirname = STRBUF_INIT;
734 int ret = -1;
@@ -833,32 +834,12 @@ static int verify_refname_available_dir(const char *refname,
834 }
835 }
836
836 - if (extras) {
837 - /*
838 - * Check for entries in extras that start with
839 - * "$refname/". We do that by looking for the place
840 - * where "$refname/" would be inserted in extras. If
841 - * there is an entry at that position that starts with
842 - * "$refname/" and is not in skip, then we have a
843 - * conflict.
844 - */
845 - for (pos = string_list_find_insert_index(extras, dirname.buf, 0);
846 - pos < extras->nr; pos++) {
847 - const char *extra_refname = extras->items[pos].string;
848 -
849 - if (!starts_with(extra_refname, dirname.buf))
850 - break;
851 -
852 - if (!skip || !string_list_has_string(skip, extra_refname)) {
853 - strbuf_addf(err, "cannot process '%s' and '%s' at the same time",
854 - refname, extra_refname);
855 - goto cleanup;
856 - }
857 - }
858 - }
859 -
860 - /* No conflicts were found */
861 - ret = 0;
837 + extra_refname = find_descendant_ref(dirname.buf, extras, skip);
838 + if (extra_refname)
839 + strbuf_addf(err, "cannot process '%s' and '%s' at the same time",
840 + refname, extra_refname);
841 + else
842 + ret = 0;
843
844 cleanup:
845 strbuf_release(&dirname);
@@ -2473,22 +2454,6 @@ int verify_refname_available(const char *newname,
2454 return 0;
2455 }
2456
2476 -static int rename_ref_available(const char *oldname, const char *newname)
2477 -{
2478 - struct string_list skip = STRING_LIST_INIT_NODUP;
2479 - struct strbuf err = STRBUF_INIT;
2480 - int ret;
2481 -
2482 - string_list_insert(&skip, oldname);
2483 - ret = !verify_refname_available(newname, NULL, &skip, &err);
2484 - if (!ret)
2485 - error("%s", err.buf);
2486 -
2487 - string_list_clear(&skip, 0);
2488 - strbuf_release(&err);
2489 - return ret;
2490 -}
2491 -
2457 static int write_ref_to_lockfile(struct ref_lock *lock,
2458 const unsigned char *sha1, struct strbuf *err);
2459 static int commit_ref_update(struct ref_lock *lock,
refs/refs-internal.h
+16
@@ -181,4 +181,20 @@ int files_log_ref_write(const char *refname, const unsigned char *old_sha1,
181 const unsigned char *new_sha1, const char *msg,
182 int flags, struct strbuf *err);
183
184 +/*
185 + * Check for entries in extras that are within the specified
186 + * directory, where dirname is a reference directory name including
187 + * the trailing slash (e.g., "refs/heads/foo/"). Ignore any
188 + * conflicting references that are found in skip. If there is a
189 + * conflicting reference, return its name.
190 + *
191 + * extras and skip must be sorted lists of reference names. Either one
192 + * can be NULL, signifying the empty list.
193 + */
194 +const char *find_descendant_ref(const char *dirname,
195 + const struct string_list *extras,
196 + const struct string_list *skip);
197 +
198 +int rename_ref_available(const char *oldname, const char *newname);
199 +
200 #endif /* REFS_REFS_INTERNAL_H */