Allow passing a list of refs to skip checking to name_conflict_fn.
There are some conditions where we want to allow a temporary conflict and skip
checking those refs. For example if we have a transaction that
1, guarantees that m is a packed refs and there is no loose ref for m
2, the transaction will delete m from the packed ref
3, the transaction will create conflicting m/m

For this case we want to be able to lock and create m/m since we know that the
conflict is only transient. I.e. the conflict will be automatically resolved
by the transaction when it deletes m.

Signed-off-by: Ronnie Sahlberg <sahlb...@google.com>
---
 refs.c | 43 +++++++++++++++++++++++++++++++++----------
 1 file changed, 33 insertions(+), 10 deletions(-)

diff --git a/refs.c b/refs.c
index 6b8b7fe..7569f4e 100644
--- a/refs.c
+++ b/refs.c
@@ -798,11 +798,19 @@ struct name_conflict_cb {
        const char *refname;
        const char *oldrefname;
        const char *conflicting_refname;
+       const char **skip;
+       int skipnum;
 };
 
 static int name_conflict_fn(struct ref_entry *entry, void *cb_data)
 {
        struct name_conflict_cb *data = (struct name_conflict_cb *)cb_data;
+       int i;
+       for(i = 0; i < data->skipnum; i++) {
+               if (!strcmp(entry->name, data->skip[i])) {
+                       return 0;
+               }
+       }
        if (data->oldrefname && !strcmp(data->oldrefname, entry->name))
                return 0;
        if (names_conflict(data->refname, entry->name)) {
@@ -817,15 +825,21 @@ static int name_conflict_fn(struct ref_entry *entry, void 
*cb_data)
  * conflicting with the name of an existing reference in dir.  If
  * oldrefname is non-NULL, ignore potential conflicts with oldrefname
  * (e.g., because oldrefname is scheduled for deletion in the same
- * operation).
+ * operation). skip contains a list of refs we want to skip checking for
+ * conflicts with. Refs may be skipped due to us knowing that it will
+ * be deleted later during a transaction that deletes one reference and then
+ * creates a new conflicting reference. For example a rename from m to m/m.
  */
 static int is_refname_available(const char *refname, const char *oldrefname,
-                               struct ref_dir *dir)
+                               struct ref_dir *dir,
+                               const char **skip, int skipnum)
 {
        struct name_conflict_cb data;
        data.refname = refname;
        data.oldrefname = oldrefname;
        data.conflicting_refname = NULL;
+       data.skip = skip;
+       data.skipnum = skipnum;
 
        sort_ref_dir(dir);
        if (do_for_each_entry_in_dir(dir, 0, name_conflict_fn, &data)) {
@@ -2037,7 +2051,8 @@ int dwim_log(const char *str, int len, unsigned char 
*sha1, char **log)
 
 static struct ref_lock *lock_ref_sha1_basic(const char *refname,
                                            const unsigned char *old_sha1,
-                                           int flags, int *type_p)
+                                           int flags, int *type_p,
+                                           const char **skip, int skipnum)
 {
        char *ref_file;
        const char *orig_refname = refname;
@@ -2084,7 +2099,9 @@ static struct ref_lock *lock_ref_sha1_basic(const char 
*refname,
         * name is a proper prefix of our refname.
         */
        if (missing &&
-            !is_refname_available(refname, NULL, get_packed_refs(&ref_cache))) 
{
+            !is_refname_available(refname, NULL,
+                                  get_packed_refs(&ref_cache),
+                                  skip, skipnum)) {
                last_errno = ENOTDIR;
                goto error_return;
        }
@@ -2142,7 +2159,7 @@ struct ref_lock *lock_any_ref_for_update(const char 
*refname,
                                         const unsigned char *old_sha1,
                                         int flags, int *type_p)
 {
-       return lock_ref_sha1_basic(refname, old_sha1, flags, type_p);
+       return lock_ref_sha1_basic(refname, old_sha1, flags, type_p, NULL, 0);
 }
 
 /*
@@ -2581,6 +2598,9 @@ int rename_ref(const char *oldrefname, const char 
*newrefname, const char *logms
        int log = !lstat(git_path("logs/%s", oldrefname), &loginfo);
        const char *symref = NULL;
 
+       if (!strcmp(oldrefname, newrefname))
+               return 0;
+
        if (log && S_ISLNK(loginfo.st_mode))
                return error("reflog for %s is a symlink", oldrefname);
 
@@ -2591,10 +2611,12 @@ int rename_ref(const char *oldrefname, const char 
*newrefname, const char *logms
        if (!symref)
                return error("refname %s not found", oldrefname);
 
-       if (!is_refname_available(newrefname, oldrefname, 
get_packed_refs(&ref_cache)))
+       if (!is_refname_available(newrefname, oldrefname,
+                                 get_packed_refs(&ref_cache), NULL, 0))
                return 1;
 
-       if (!is_refname_available(newrefname, oldrefname, 
get_loose_refs(&ref_cache)))
+       if (!is_refname_available(newrefname, oldrefname,
+                                 get_loose_refs(&ref_cache), NULL, 0))
                return 1;
 
        if (log && rename(git_path("logs/%s", oldrefname), 
git_path(TMP_RENAMED_LOG)))
@@ -2627,7 +2649,7 @@ int rename_ref(const char *oldrefname, const char 
*newrefname, const char *logms
 
        logmoved = log;
 
-       lock = lock_ref_sha1_basic(newrefname, NULL, 0, NULL);
+       lock = lock_ref_sha1_basic(newrefname, NULL, 0, NULL, NULL, 0);
        if (!lock) {
                error("unable to lock %s for update", newrefname);
                goto rollback;
@@ -2642,7 +2664,7 @@ int rename_ref(const char *oldrefname, const char 
*newrefname, const char *logms
        return 0;
 
  rollback:
-       lock = lock_ref_sha1_basic(oldrefname, NULL, 0, NULL);
+       lock = lock_ref_sha1_basic(oldrefname, NULL, 0, NULL, NULL, 0);
        if (!lock) {
                error("unable to lock %s for rollback", oldrefname);
                goto rollbacklog;
@@ -3501,7 +3523,8 @@ int ref_transaction_commit(struct ref_transaction 
*transaction,
                                                    update->old_sha1 :
                                                    NULL),
                                                   update->flags,
-                                                  &update->type);
+                                                  &update->type,
+                                                  delnames, delnum);
                if (!update->lock) {
                        if (err)
                                strbuf_addf(err, "Cannot lock the ref '%s'.",
-- 
2.0.0.rc3.471.g2055d11.dirty

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to