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

[PATCH v3 14/14] refs.c: allow deleting refs with a broken sha1

From
Stefan Beller <sbeller@google.com>
Date
Nov 18, 2014, 01:35 UTC
Message-ID
<1416274550-2827-15-git-send-email-sbeller@google.com>
In-Reply-To
<1416274550-2827-1-git-send-email-sbeller@google.com>
From: Ronnie Sahlberg <sahlberg@google.com>

Add back support to make it possible to delete refs that have a broken sha1.

Add new internal flags REF_ALLOW_BROKEN and RESOLVE_REF_ALLOW_BAD_SHA1 to pass intent from branch.c that we are willing to allow resolve_ref_unsafe and lock_ref_sha1_basic to allow broken refs. Since these refs can not actually be resolved to a sha1, they instead resolve to null_sha1 when these flags are used.

For example, the ref:
   echo "Broken ref" > .git/refs/heads/foo-broken-1
can now be deleted using git branch -d foo-broken-1
Signed-off-by: Ronnie Sahlberg <sahlberg@google.com>
Signed-off-by: Jonathan Nieder <jrnieder@gmail.com>
Signed-off-by: Stefan Beller <sbeller@google.com>
---
 builtin/branch.c            | 5 +++--
 cache.h                     | 7 +++++++
 refs.c                      | 6 ++++++
 refs.h                      | 6 ++++--
 t/t1402-check-ref-format.sh | 8 ++++++++
 5 files changed, 28 insertions(+), 4 deletions(-)
diff --git a/builtin/branch.c b/builtin/branch.c
index 3b79c50..04f57d4 100644
--- a/builtin/branch.c
+++ b/builtin/branch.c
@@ -238,7 +238,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
 		target = resolve_ref_unsafe(name,
 					    RESOLVE_REF_READING
 					    | RESOLVE_REF_NO_RECURSE
-					    | RESOLVE_REF_ALLOW_BAD_NAME,
+					    | RESOLVE_REF_ALLOW_BAD_NAME
+					    | RESOLVE_REF_ALLOW_BAD_SHA1,
 					    sha1, &flags);
 		if (!target) {
 			error(remote_branch
@@ -255,7 +256,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
 			continue;
 		}
 
-		if (delete_ref(name, sha1, REF_NODEREF)) {
+		if (delete_ref(name, sha1, REF_NODEREF|REF_ALLOW_BROKEN)) {
 			error(remote_branch
 			      ? _("Error deleting remote branch '%s'")
 			      : _("Error deleting branch '%s'"),
diff --git a/cache.h b/cache.h
index 99ed096..61e61af 100644
--- a/cache.h
+++ b/cache.h
@@ -1000,10 +1000,17 @@ extern int read_ref(const char *refname, unsigned char *sha1);
  * resolved. The function returns NULL for such ref names.
  * Caps and underscores refers to the special refs, such as HEAD,
  * FETCH_HEAD and friends, that all live outside of the refs/ directory.
+ *
+ * RESOLVE_REF_ALLOW_BAD_SHA1 when this flag is set and the ref contains
+ * an invalid sha1, resolve_ref_unsafe will clear the sha1 argument,
+ * set the REF_ISBROKEN flag and return the refname.
+ * This allows using resolve_ref_unsafe to check for existence of such
+ * broken refs.
  */
 #define RESOLVE_REF_READING 0x01
 #define RESOLVE_REF_NO_RECURSE 0x02
 #define RESOLVE_REF_ALLOW_BAD_NAME 0x04
+#define RESOLVE_REF_ALLOW_BAD_SHA1 0x08
 extern const char *resolve_ref_unsafe(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);
 extern char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags);
 
diff --git a/refs.c b/refs.c
index b318888..d75af6c 100644
--- a/refs.c
+++ b/refs.c
@@ -1584,6 +1584,10 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned
 			    (buffer[40] != '\0' && !isspace(buffer[40]))) {
 				if (flags)
 					*flags |= REF_ISBROKEN;
+				if (resolve_flags & RESOLVE_REF_ALLOW_BAD_SHA1) {
+					hashclr(sha1);
+					return refname;
+				}
 				errno = EINVAL;
 				return NULL;
 			}
@@ -2265,6 +2269,8 @@ static struct ref_lock *lock_ref_sha1_basic(const char *refname,
 		if (flags & REF_NODEREF)
 			resolve_flags |= RESOLVE_REF_NO_RECURSE;
 	}
+	if (flags & REF_ALLOW_BROKEN)
+		resolve_flags |= RESOLVE_REF_ALLOW_BAD_SHA1;
 
 	refname = resolve_ref_unsafe(refname, resolve_flags,
 				     lock->old_sha1, &type);
diff --git a/refs.h b/refs.h
index 721e21f..2e97f4f 100644
--- a/refs.h
+++ b/refs.h
@@ -185,11 +185,13 @@ extern int peel_ref(const char *refname, unsigned char *sha1);
  * REF_NODEREF: act on the ref directly, instead of dereferencing
  *              symbolic references.
  * REF_DELETING: tolerate broken refs
+ * REF_ALLOW_BROKEN: allow locking refs that are broken.
  *
  * Flags >= 0x100 are reserved for internal use.
  */
-#define REF_NODEREF	0x01
-#define REF_DELETING	0x02
+#define REF_NODEREF		0x01
+#define REF_DELETING		0x02
+#define REF_ALLOW_BROKEN	0x04
 
 /** Reads log for the value of ref during at_time. **/
 extern int read_ref_at(const char *refname, unsigned int flags,
diff --git a/t/t1402-check-ref-format.sh b/t/t1402-check-ref-format.sh
index e5dc62e..a0aef69 100755
--- a/t/t1402-check-ref-format.sh
+++ b/t/t1402-check-ref-format.sh
@@ -197,4 +197,12 @@ invalid_ref_normalized 'heads///foo.lock'
 invalid_ref_normalized 'foo.lock/bar'
 invalid_ref_normalized 'foo.lock///bar'
 
+test_expect_success 'git branch -d can delete ref with broken sha1' '
+	echo "012brokensha1" > .git/refs/heads/brokensha1 &&
+	test_when_finished "rm -f .git/refs/heads/brokensha1" &&
+	git branch -d brokensha1 &&
+	git branch >output &&
+	! grep -e "brokensha1" output
+'
+
 test_done
-- 
2.2.0.rc2.5.gf7b9fb2
Previous: Stefan BellerNext: Michael Haggerty
Message 15 of 31 in “ref-transactions-reflog”
  1. 00/14 ref-transactions-reflogStefan Beller, Nov 18, 2014
  2. 01/14 refs.c: make ref_transaction_create a wrapper for ref_transaction_updateStefan Beller, Nov 18, 2014
  3. 02/14 refs.c: make ref_transaction_delete a wrapper for ref_transaction_updateStefan Beller, Nov 18, 2014
  4. 03/14 refs.c: rename the transaction functionsStefan Beller, Nov 18, 2014
  5. 04/14 refs.c: add a function to append a reflog entry to a fdStefan Beller, Nov 18, 2014
  6. 05/14 refs.c: add a new update_type field to ref_updateStefan Beller, Nov 18, 2014
  7. 06/14 refs.c: add a transaction function to append a reflog entryStefan Beller, Nov 18, 2014
  8. 07/14 refs.c: add a flag to allow reflog updates to truncate the logStefan Beller, Nov 18, 2014
  9. 08/14 refs.c: only write reflog update if msg is non-NULLStefan Beller, Nov 18, 2014
  10. 09/14 refs.c: allow multiple reflog updates during a single transactionStefan Beller, Nov 18, 2014
  11. 10/14 reflog.c: use a reflog transaction when writing during expireStefan Beller, Nov 18, 2014
  12. 11/14 refs.c: rename log_ref_setup to create_reflogStefan Beller, Nov 18, 2014
  13. 12/14 refs.c: Remove unlock_ref/close_ref/commit_ref from the refs apiStefan Beller, Nov 18, 2014
  14. 13/14 refs.c: remove lock_any_ref_for_updateStefan Beller, Nov 18, 2014
  15. 14/14 refs.c: allow deleting refs with a broken sha1Stefan Beller, Nov 18, 2014
  16. Michael HaggertyNov 18, 2014
  17. Ronnie SahlbergNov 18, 2014
  18. Michael HaggertyNov 18, 2014
  19. Junio C HamanoNov 18, 2014
  20. Michael HaggertyNov 18, 2014
  21. Junio C HamanoNov 18, 2014
  22. Stefan BellerNov 19, 2014
  23. Jonathan NiederNov 20, 2014
  24. Junio C HamanoNov 20, 2014
  25. Michael HaggertyNov 20, 2014
  26. Jonathan NiederNov 20, 2014
  27. 0/4 Using transactions for the reflogStefan Beller, Nov 27, 2014
  28. 1/4 refs.c: rename the transaction functionsStefan Beller, Nov 27, 2014
  29. 2/4 refs.c: add a new update_type field to ref_updateStefan Beller, Nov 27, 2014
  30. 3/4 refs.c: add a transaction function to append a reflog entryStefan Beller, Nov 27, 2014
  31. 4/4 reflog.c: use a reflog transaction when writing during expireStefan Beller, Nov 27, 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.