git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH 11/25] refs.c: ref_transaction_commit: distinguish name conflicts from other errors

From
Jonathan Nieder <jrnieder@gmail.com>
Date
Oct 15, 2014, 00:50 UTC
Message-ID
<20141015005045.GO32245@google.com>
In-Reply-To
<20141015004522.GD32245@google.com>
From: Ronnie Sahlberg <sahlberg@google.com>
Date: Fri, 16 May 2014 14:14:38 -0700

In _commit, ENOTDIR can happen in the call to lock_ref_sha1_basic, either when we lstat the new refname or if the name checking function reports that the same type of conflict happened. In both cases, it means that we can not create the new ref due to a name conflict.

Start defining specific return codes for _commit. TRANSACTION_NAME_CONFLICT refers to a failure to create a ref due to a name conflict with another ref. TRANSACTION_GENERIC_ERROR is for all other errors.

When "git fetch" is creating refs, name conflicts differ from other errors in that they are likely to be resolved by running "git remote prune <remote>". "git fetch" currently inspects errno to decide whether to give that advice. Once it switches to the transaction API, it can check for TRANSACTION_NAME_CONFLICT instead.

Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
---
 refs.c | 26 ++++++++++++++++----------
 refs.h |  9 +++++++--
 2 files changed, 23 insertions(+), 12 deletions(-)
diff --git a/refs.c b/refs.c
index a007cf3..9d9bbeb 100644
--- a/refs.c
+++ b/refs.c
@@ -3637,9 +3637,10 @@ int ref_transaction_commit(struct ref_transaction *transaction,
 
 	/* Copy, sort, and reject duplicate refs */
 	qsort(updates, n, sizeof(*updates), ref_update_compare);
-	ret = ref_update_reject_duplicates(updates, n, err);
-	if (ret)
+	if (ref_update_reject_duplicates(updates, n, err)) {
+		ret = TRANSACTION_GENERIC_ERROR;
 		goto cleanup;
+	}
 
 	/* Acquire all locks while verifying old values */
 	for (i = 0; i < n; i++) {
@@ -3653,10 +3654,12 @@ int ref_transaction_commit(struct ref_transaction *transaction,
 						   update->flags,
 						   &update->type);
 		if (!update->lock) {
+			ret = (errno == ENOTDIR)
+				? TRANSACTION_NAME_CONFLICT
+				: TRANSACTION_GENERIC_ERROR;
 			if (err)
 				strbuf_addf(err, "Cannot lock the ref '%s'.",
 					    update->refname);
-			ret = 1;
 			goto cleanup;
 		}
 	}
@@ -3666,15 +3669,16 @@ int ref_transaction_commit(struct ref_transaction *transaction,
 		struct ref_update *update = updates[i];
 
 		if (!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 */
-			if (ret) {
+			if (write_ref_sha1(update->lock, update->new_sha1,
+					   update->msg)) {
+				update->lock = NULL; /* freed by write_ref_sha1 */
 				if (err)
 					strbuf_addf(err, "Cannot update the ref '%s'.",
 						    update->refname);
+				ret = TRANSACTION_GENERIC_ERROR;
 				goto cleanup;
 			}
+			update->lock = NULL; /* freed by write_ref_sha1 */
 		}
 	}
 
@@ -3683,14 +3687,16 @@ int ref_transaction_commit(struct ref_transaction *transaction,
 		struct ref_update *update = updates[i];
 
 		if (update->lock) {
-			ret |= delete_ref_loose(update->lock, update->type,
-						err);
+			if (delete_ref_loose(update->lock, update->type, err))
+				ret = TRANSACTION_GENERIC_ERROR;
+
 			if (!(update->flags & REF_ISPRUNING))
 				delnames[delnum++] = update->lock->ref_name;
 		}
 	}
 
-	ret |= repack_without_refs(delnames, delnum, err);
+	if (repack_without_refs(delnames, delnum, err))
+		ret = TRANSACTION_GENERIC_ERROR;
 	for (i = 0; i < delnum; i++)
 		unlink_or_warn(git_path("logs/%s", delnames[i]));
 	clear_loose_ref_cache(&ref_cache);
diff --git a/refs.h b/refs.h
index 1a55cab..50b115a 100644
--- a/refs.h
+++ b/refs.h
@@ -326,9 +326,14 @@ int ref_transaction_delete(struct ref_transaction *transaction,
 
 /*
  * Commit all of the changes that have been queued in transaction, as
- * atomically as possible.  Return a nonzero value if there is a
- * problem.
+ * atomically as possible.
+ *
+ * Returns 0 for success, or one of the below error codes for errors.
  */
+/* Naming conflict (for example, the ref names A and A/B conflict). */
+#define TRANSACTION_NAME_CONFLICT -1
+/* All other errors. */
+#define TRANSACTION_GENERIC_ERROR -2
 int ref_transaction_commit(struct ref_transaction *transaction,
 			   struct strbuf *err);
 
-- 
2.1.0.rc2.206.gedb03e5
Previous: Jonathan NiederNext: Jonathan Nieder
Message 12 of 28 in “rs/ref-transaction ("Use ref transactions", part 3)”
  1. 0/25 rs/ref-transaction ("Use ref transactions", part 3)Jonathan Nieder, Oct 15, 2014
  2. 01/25 mv test: recreate mod/ directory instead of relying on stale copyJonathan Nieder, Oct 15, 2014
  3. 02/25 wrapper.c: remove/unlink_or_warn: simplify, treat ENOENT as successJonathan Nieder, Oct 15, 2014
  4. 03/25 refs.c: lock_ref_sha1_basic is used for all refsJonathan Nieder, Oct 15, 2014
  5. 04/25 wrapper.c: add a new function unlink_or_msgJonathan Nieder, Oct 15, 2014
  6. 05/25 refs.c: add an err argument to delete_ref_looseJonathan Nieder, Oct 15, 2014
  7. 06/25 refs.c: pass the ref log message to _create/delete/update instead of _commitJonathan Nieder, Oct 15, 2014
  8. 07/25 rename_ref: don't ask read_ref_full where the ref came fromJonathan Nieder, Oct 15, 2014
  9. 08/25 refs.c: refuse to lock badly named refs in lock_ref_sha1_basicJonathan Nieder, Oct 15, 2014
  10. 09/25 refs.c: call lock_ref_sha1_basic directly from commitJonathan Nieder, Oct 15, 2014
  11. 10/25 refs.c: pass a list of names to skip to is_refname_availableJonathan Nieder, Oct 15, 2014
  12. 11/25 refs.c: ref_transaction_commit: distinguish name conflicts from other errorsJonathan Nieder, Oct 15, 2014
  13. 12/25 fetch.c: change s_update_ref to use a ref transactionJonathan Nieder, Oct 15, 2014
  14. 13/25 refs.c: make write_ref_sha1 staticJonathan Nieder, Oct 15, 2014
  15. 14/25 refs.c: change resolve_ref_unsafe reading argument to be a flags fieldJonathan Nieder, Oct 15, 2014
  16. 15/25 reflog test: test interaction with detached HEADJonathan Nieder, Oct 15, 2014
  17. 16/25 branch -d: avoid repeated symref resolutionJonathan Nieder, Oct 15, 2014
  18. 17/25 branch -d: simplify by using RESOLVE_REF_READINGJonathan Nieder, Oct 15, 2014
  19. 18/25 packed-ref cache: forbid dot-components in refnamesJonathan Nieder, Oct 15, 2014
  20. 19/25 test: put tests for handling of bad ref names in one placeJonathan Nieder, Oct 15, 2014
  21. 20/25 refs.c: allow listing and deleting badly named refsJonathan Nieder, Oct 15, 2014
  22. 21/25 for-each-ref: skip and warn about broken ref namesJonathan Nieder, Oct 15, 2014
  23. 22/25 remote rm/prune: print a message when writing packed-refs failsJonathan Nieder, Oct 15, 2014
  24. 23/25 refs.c: do not permit err == NULLJonathan Nieder, Oct 15, 2014
  25. 24/25 lockfile: remove unable_to_lock_errorJonathan Nieder, Oct 15, 2014
  26. 25/25 ref_transaction_commit: bail out on failure to remove a refJonathan Nieder, Oct 15, 2014
  27. Junio C HamanoOct 15, 2014
  28. Michael HaggertyOct 16, 2014

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.