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

[PATCH 4/7] remote.c: add command line option parser for --lockref

From
Junio C Hamano <gitster@pobox.com>
Date
Jul 9, 2013, 19:53 UTC
Message-ID
<1373399610-8588-5-git-send-email-gitster@pobox.com>
In-Reply-To
<1373399610-8588-1-git-send-email-gitster@pobox.com>

Update "git push" and "git send-pack" to parse this commnd line option.

The intended sematics is:
 * "--lockref" alone, without specifying the details, will protect
   _all_ remote refs that are going to be updated by requiring their
   current value to be the same as the remote-tracking branch we
   have for them, unless otherwise specified;
 * "--lockref=refname", without specifying the expected value, will
   protect that refname, if it is going to be updated, by requiring
   its current value to be the same as the remote-tracking branch we
   have for it;
 * "--lockref=refname:value" will protect that refname, if it is
   going to be updated, by requiring its current value to be the
   same as the specified value (which is allowed to be different
   from the remote-tracking branch we have for the refname, or we do
   not even have to have such a remote-tracking branch when this
   form is used);
 * "--lockref=refname:" (empty value) is to expect that refname does
   not exist (yet); and
 * "--no-lockref" will cancel all the previous --lockref on the
   command line.

In any of the forms, when we try to use a remote-tracking branch for the remote ref being updated, it is an error if there is no such remote-tracking branch on our end.

Because the command line options are parsed _before_ we know which remote we are pushing to, there needs further processing to the parsed data after we instantiate the transport object to:

 * expand "refname" given by the user to a full refname to be
   matched with the list of "struct ref" used in match_push_refs()
   and set_ref_status_for_push(); and
 * learning the actual local ref that is the remote-tracking branch
   for the specified remote ref.

Further, some processing need to be deferred until we find the set of remote refs and match_push_refs() returns in order to find the ones that need to be checked after explicit ones have been processed for "--lockref" (no specific details).

These post-processing will be the topic of the next patch.
Oh, of course, the option name is still not cast in stone.
Signed-off-by: Junio C Hamano <gitster@pobox.com>
---
 builtin/push.c      |  6 ++++++
 builtin/send-pack.c | 17 +++++++++++++++
 remote.c            | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 remote.h            | 22 ++++++++++++++++++++
 4 files changed, 104 insertions(+)
diff --git a/builtin/push.c b/builtin/push.c
index 342d792..31a5ba0 100644
--- a/builtin/push.c
+++ b/builtin/push.c
@@ -21,6 +21,8 @@ static const char *receivepack;
 static int verbosity;
 static int progress = -1;
 
+static struct push_cas_option cas;
+
 static const char **refspec;
 static int refspec_nr;
 static int refspec_alloc;
@@ -432,6 +434,10 @@ int cmd_push(int argc, const char **argv, const char *prefix)
 		OPT_BIT('n' , "dry-run", &flags, N_("dry run"), TRANSPORT_PUSH_DRY_RUN),
 		OPT_BIT( 0,  "porcelain", &flags, N_("machine-readable output"), TRANSPORT_PUSH_PORCELAIN),
 		OPT_BIT('f', "force", &flags, N_("force updates"), TRANSPORT_PUSH_FORCE),
+		{ OPTION_CALLBACK,
+		  0, CAS_OPT_NAME, &cas, N_("refname>:<expect"),
+		  N_("require old value of ref to be at this value"),
+		  PARSE_OPT_OPTARG, parseopt_push_cas_option },
 		{ OPTION_CALLBACK, 0, "recurse-submodules", &flags, N_("check"),
 			N_("control recursive pushing of submodules"),
 			PARSE_OPT_OPTARG, option_parse_recurse_submodules },
diff --git a/builtin/send-pack.c b/builtin/send-pack.c
index c86c556..061e2b2 100644
--- a/builtin/send-pack.c
+++ b/builtin/send-pack.c
@@ -108,6 +108,7 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
 	int flags;
 	unsigned int reject_reasons;
 	int progress = -1;
+	struct push_cas_option cas = {0};
 
 	argv++;
 	for (i = 1; i < argc; i++, argv++) {
@@ -170,6 +171,22 @@ int cmd_send_pack(int argc, const char **argv, const char *prefix)
 				helper_status = 1;
 				continue;
 			}
+			if (!strcmp(arg, "--" CAS_OPT_NAME)) {
+				if (parse_push_cas_option(&cas, NULL, 0) < 0)
+					exit(1);
+				continue;
+			}
+			if (!strcmp(arg, "--no-" CAS_OPT_NAME)) {
+				if (parse_push_cas_option(&cas, NULL, 1) < 0)
+					exit(1);
+				continue;
+			}
+			if (!prefixcmp(arg, "--" CAS_OPT_NAME "=")) {
+				if (parse_push_cas_option(&cas,
+							  strchr(arg, '=') + 1, 1) < 0)
+					exit(1);
+				continue;
+			}
 			usage(send_pack_usage);
 		}
 		if (!dest) {
diff --git a/remote.c b/remote.c
index 81bc876..e9b423a 100644
--- a/remote.c
+++ b/remote.c
@@ -1938,3 +1938,62 @@ struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fet
 	string_list_clear(&ref_names, 0);
 	return stale_refs;
 }
+
+/*
+ * Lockref aka CAS
+ */
+void clear_cas_option(struct push_cas_option *cas)
+{
+	int i;
+
+	for (i = 0; i < cas->nr; i++)
+		free(cas->entry->refname);
+	free(cas->entry);
+	memset(cas, 0, sizeof(*cas));
+}
+
+static struct push_cas *add_cas_entry(struct push_cas_option *cas,
+				      const char *refname,
+				      size_t refnamelen)
+{
+	struct push_cas *entry;
+	ALLOC_GROW(cas->entry, cas->nr + 1, cas->alloc);
+	entry = &cas->entry[cas->nr++];
+	memset(entry, 0, sizeof(*entry));
+	entry->refname = xmemdupz(refname, refnamelen);
+	return entry;
+}
+
+int parseopt_push_cas_option(const struct option *opt, const char *arg, int unset)
+{
+	return parse_push_cas_option(opt->value, arg, unset);
+}
+
+int parse_push_cas_option(struct push_cas_option *cas, const char *arg, int unset)
+{
+	const char *colon;
+	struct push_cas *entry;
+
+	if (unset) {
+		/* "--no-lockref" */
+		clear_cas_option(cas);
+		return 0;
+	}
+
+	if (!arg) {
+		/* just "--lockref" */
+		cas->use_tracking_for_rest = 1;
+		return 0;
+	}
+
+	/* "--lockref=refname" or "--lockref=refname:value" */
+	colon = strchrnul(arg, ':');
+	entry = add_cas_entry(cas, arg, colon - arg);
+	if (!*colon)
+		entry->use_tracking = 1;
+	else if (!colon[1])
+		hashclr(entry->expect);
+	else if (get_sha1(colon + 1, entry->expect))
+		return error("cannot parse expected object name '%s'", colon + 1);
+	return 0;
+}
diff --git a/remote.h b/remote.h
index 7ad37e6..28eb6a3 100644
--- a/remote.h
+++ b/remote.h
@@ -1,6 +1,8 @@
 #ifndef REMOTE_H
 #define REMOTE_H
 
+#include "parse-options.h"
+
 enum {
 	REMOTE_CONFIG,
 	REMOTE_REMOTES,
@@ -230,4 +232,24 @@ struct ref *guess_remote_head(const struct ref *head,
 /* Return refs which no longer exist on remote */
 struct ref *get_stale_heads(struct refspec *refs, int ref_count, struct ref *fetch_map);
 
+/*
+ * Lockref aka CAS
+ */
+#define CAS_OPT_NAME "lockref"
+
+struct push_cas_option {
+	unsigned use_tracking_for_rest:1;
+	struct push_cas {
+		unsigned char expect[20];
+		unsigned use_tracking:1;
+		char *refname;
+	} *entry;
+	int nr;
+	int alloc;
+};
+
+extern int parseopt_push_cas_option(const struct option *, const char *arg, int unset);
+extern int parse_push_cas_option(struct push_cas_option *, const char *arg, int unset);
+extern void clear_cas_option(struct push_cas_option *);
+
 #endif
-- 
1.8.3.2-875-g76c723c
Previous: Junio C HamanoNext: John Keeping
Message 20 of 62 in “[RFD] Making "git push [--force/--delete]" safer?”
  1. Junio C HamanoJul 2, 2013
  2. Johan HerlandJul 2, 2013
  3. Johan HerlandJul 3, 2013
  4. Junio C HamanoJul 3, 2013
  5. Johan HerlandJul 3, 2013
  6. Jonathan del StrotherJul 3, 2013
  7. Johan HerlandJul 3, 2013
  8. Michael HaggertyJul 3, 2013
  9. Johannes SixtJul 3, 2013
  10. Junio C HamanoJul 3, 2013
  11. Johannes SixtJul 4, 2013
  12. Junio C HamanoJul 4, 2013
  13. Junio C HamanoJul 3, 2013
  14. Junio C HamanoJul 3, 2013
  15. Junio C HamanoJul 3, 2013
  16. 0/7 safer "push --force" with compare-and-swapJunio C Hamano, Jul 9, 2013
  17. 1/7 cache.h: move remote/connect API out of itJunio C Hamano, Jul 9, 2013
  18. 2/7 builtin/push.c: use OPT_BOOL, not OPT_BOOLEANJunio C Hamano, Jul 9, 2013
  19. 3/7 push: beginning of compare-and-swap "force/delete safety"Junio C Hamano, Jul 9, 2013
  20. 4/7 remote.c: add command line option parser for --lockrefJunio C Hamano, Jul 9, 2013
  21. John KeepingJul 16, 2013
  22. Junio C HamanoJul 17, 2013
  23. Junio C HamanoJul 17, 2013
  24. 5/7 push --lockref: implement logic to populate old_sha1_expect[]Junio C Hamano, Jul 9, 2013
  25. 6/7 t5533: test "push --lockref"Junio C Hamano, Jul 9, 2013
  26. 7/7 push: document --lockrefJunio C Hamano, Jul 9, 2013
  27. Aaron SchrabJul 9, 2013
  28. Junio C HamanoJul 9, 2013
  29. Johannes SixtJul 9, 2013
  30. Junio C HamanoJul 9, 2013
  31. Johannes SixtJul 9, 2013
  32. Junio C HamanoJul 9, 2013
  33. Junio C HamanoJul 9, 2013
  34. Johannes SixtJul 11, 2013
  35. Junio C HamanoJul 11, 2013
  36. Junio C HamanoJul 11, 2013
  37. Johannes SixtJul 12, 2013
  38. Junio C HamanoJul 12, 2013
  39. Johannes SixtJul 12, 2013
  40. Junio C HamanoJul 12, 2013
  41. Johannes SixtJul 13, 2013
  42. Junio C HamanoJul 13, 2013
  43. Junio C HamanoJul 13, 2013
  44. Johannes SixtJul 13, 2013
  45. John KeepingJul 14, 2013
  46. Johannes SixtJul 13, 2013
  47. Junio C HamanoJul 14, 2013
  48. Johannes SixtJul 14, 2013
  49. Jonathan NiederJul 14, 2013
  50. Jonathan NiederJul 14, 2013
  51. Johannes SixtJul 14, 2013
  52. Jonathan NiederJul 14, 2013
  53. Junio C HamanoJul 15, 2013
  54. Jonathan NiederJul 15, 2013
  55. Junio C HamanoJul 15, 2013
  56. Johannes SixtJul 15, 2013
  57. Junio C HamanoJul 15, 2013
  58. Default expectation of --lockrefJunio C Hamano, Jul 15, 2013
  59. Johannes SixtJul 15, 2013
  60. Marc BranchaudJul 9, 2013
  61. Michael HaggertyJul 9, 2013
  62. Junio C HamanoJul 9, 2013

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.