git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction
@ 2014-03-10 12:46 Michael Haggerty
  2014-03-10 12:46 ` [PATCH 01/26] t1400: Fix name and expected result of one test Michael Haggerty
                   ` (26 more replies)
  0 siblings, 27 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

I just sent an email to the list [1] describing how I want to
decouple reference-handling code from the rest of Git, and implement
pluggable reference storage backends.  This patch series is the first
movement in that direction.

update_refs() and "update-ref --stdin" implement the beginning of
transactions for git references, by allowing a group of reference
changes to be done in an all-or-nothing fashion.  The main point of
this patch series is to increase the abstraction level of the API for
dealing with reference transactions, by moving the handling of the
transaction to refs.c.  The new API for dealing with reference
transactions is

    ref_transaction *transaction = create_ref_transaction();
    queue_create_ref(transaction, refname, new_sha1, ...);
    queue_update_ref(transaction, refname, new_sha1, old_sha1, ...);
    queue_delete_ref(transaction, refname, old_sha1, ...);
    ...
    if (commit_ref_transaction(transaction, msg, ...))
        die(...);

When implementing this I found a number of minor problems in the
implementation of "git update-ref --stdin", not to mention that it
used "struct ref_update" all the way up and down its parser call
stack.  So most of the commits in this series are actually cleanups in
builtin/update-ref.c.  I also spend some time making the error
messages emitted by that command more uniform.

Then, in just a couple of commits, the ref_transaction abstraction is
introduced, update-ref is changed to use it, and update_refs() is
removed from the refs API (it was only used by this one caller).

Finally, now that refs.c owns the data structures for dealing with
transactions, it is possible to make a few simplifications.  More
changes in this neighborhood will be coming in future patches.

[1] http://article.gmane.org/gmane.comp.version-control.git/243726

Michael Haggerty (26):
  t1400: Fix name and expected result of one test
  t1400: Provide sensible input to the command
  t1400: Pass a legitimate <newvalue> to update command
  parse_arg(): Really test that argument is properly terminated
  t1400: Add some more tests involving quoted arguments
  refs.h: Rename the action_on_err constants
  update_refs(): Fix constness
  update-ref --stdin: Read the whole input at once
  parse_cmd_verify(): Copy old_sha1 instead of evaluating <oldvalue>
    twice
  update-ref.c: Extract a new function, parse_refname()
  update-ref --stdin: Improve error messages for invalid values
  update-ref --stdin: Make error messages more consistent
  update-ref --stdin: Simplify error messages for missing oldvalues
  update-ref.c: Extract a new function, parse_next_sha1()
  update-ref --stdin: Improve the error message for unexpected EOF
  update-ref --stdin: Harmonize error messages
  refs: Add a concept of a reference transaction
  update-ref --stdin: Reimplement using reference transactions
  refs: Remove API function update_refs()
  struct ref_update: Rename field "ref_name" to "refname"
  struct ref_update: Store refname as a FLEX_ARRAY.
  commit_ref_transaction(): Introduce temporary variables
  struct ref_update: Add a lock member
  struct ref_update: Add type field
  commit_ref_transaction(): Also free the ref_transaction
  commit_ref_transaction(): Work with transaction->updates in place

 builtin/checkout.c                     |   2 +-
 builtin/clone.c                        |   9 +-
 builtin/merge.c                        |   6 +-
 builtin/notes.c                        |   6 +-
 builtin/reset.c                        |   6 +-
 builtin/update-ref.c                   | 402 +++++++++++++++++++--------------
 contrib/examples/builtin-fetch--tool.c |   3 +-
 notes-cache.c                          |   2 +-
 notes-utils.c                          |   3 +-
 refs.c                                 | 184 +++++++++++----
 refs.h                                 |  93 ++++++--
 t/t1400-update-ref.sh                  |  86 ++++---
 12 files changed, 524 insertions(+), 278 deletions(-)

-- 
1.9.0

^ permalink raw reply	[flat|nested] 38+ messages in thread

* [PATCH 01/26] t1400: Fix name and expected result of one test
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 02/26] t1400: Provide sensible input to the command Michael Haggerty
                   ` (25 subsequent siblings)
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

The test

    stdin -z create ref fails with zero new value

actually passes an empty new value, not a zero new value.  So rename
the test s/zero/empty/, and change the expected error from

    fatal: create $c given zero new value

to

    fatal: create $c missing <newvalue>

Of course, this makes the test fail now, so mark it
test_expect_failure.  The failure will be fixed later in this patch
series.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 t/t1400-update-ref.sh | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 6ffd82f..fa927d2 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -827,10 +827,10 @@ test_expect_success 'stdin -z create ref fails with bad new value' '
 	test_must_fail git rev-parse --verify -q $c
 '
 
-test_expect_success 'stdin -z create ref fails with zero new value' '
+test_expect_failure 'stdin -z create ref fails with empty new value' '
 	printf $F "create $c" "" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: create $c given zero new value" err &&
+	grep "fatal: create $c missing <newvalue>" err &&
 	test_must_fail git rev-parse --verify -q $c
 '
 
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 02/26] t1400: Provide sensible input to the command
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
  2014-03-10 12:46 ` [PATCH 01/26] t1400: Fix name and expected result of one test Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 03/26] t1400: Pass a legitimate <newvalue> to update command Michael Haggerty
                   ` (24 subsequent siblings)
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

The old version was passing (among other things)

    update SP refs/heads/c NUL NUL 0{40} NUL

to "git update-ref -z --stdin" to test whether the old-value check for
c is working.  But the <newvalue> is empty, which is not allowed for
the "update" command.

So, to be sure that we are testing what we want to test, provide a
legitimate <newvalue> on the "update" line.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 t/t1400-update-ref.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index fa927d2..29391c6 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -912,7 +912,7 @@ test_expect_success 'stdin -z update refs works with identity updates' '
 
 test_expect_success 'stdin -z update refs fails with wrong old value' '
 	git update-ref $c $m &&
-	printf $F "update $a" "$m" "$m" "update $b" "$m" "$m" "update $c" "" "$Z" >stdin &&
+	printf $F "update $a" "$m" "$m" "update $b" "$m" "$m" "update $c" "$m" "$Z" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
 	grep "fatal: Cannot lock the ref '"'"'$c'"'"'" err &&
 	git rev-parse $m >expect &&
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 03/26] t1400: Pass a legitimate <newvalue> to update command
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
  2014-03-10 12:46 ` [PATCH 01/26] t1400: Fix name and expected result of one test Michael Haggerty
  2014-03-10 12:46 ` [PATCH 02/26] t1400: Provide sensible input to the command Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 17:03   ` Brad King
  2014-03-10 12:46 ` [PATCH 04/26] parse_arg(): Really test that argument is properly terminated Michael Haggerty
                   ` (23 subsequent siblings)
  26 siblings, 1 reply; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

This test is trying to test a few ways to delete references using "git
update-ref -z --stdin".  The third line passed in is

    update SP /refs/heads/c NUL NUL <sha1> NUL

, which is not a correct way to delete a reference according to the
documentation (the new value should be zeros, not empty).  Pass zeros
instead as the new value to test the code correctly.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 t/t1400-update-ref.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 29391c6..e2f1dfa 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -927,7 +927,7 @@ test_expect_success 'stdin -z update refs fails with wrong old value' '
 test_expect_success 'stdin -z delete refs works with packed and loose refs' '
 	git pack-refs --all &&
 	git update-ref $c $m~1 &&
-	printf $F "delete $a" "$m" "update $b" "$Z" "$m" "update $c" "" "$m~1" >stdin &&
+	printf $F "delete $a" "$m" "update $b" "$Z" "$m" "update $c" "$Z" "$m~1" >stdin &&
 	git update-ref -z --stdin <stdin &&
 	test_must_fail git rev-parse --verify -q $a &&
 	test_must_fail git rev-parse --verify -q $b &&
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 04/26] parse_arg(): Really test that argument is properly terminated
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (2 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 03/26] t1400: Pass a legitimate <newvalue> to update command Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 05/26] t1400: Add some more tests involving quoted arguments Michael Haggerty
                   ` (22 subsequent siblings)
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

Add a docstring to the function incorporating the comments that were
formerly within the function plus some added information.  Test that
the argument is properly terminated by either whitespace or a NUL
character, even if it is quoted, to be consistent with the non-quoted
case.  Adjust the tests to expect the new error message.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 builtin/update-ref.c  | 20 +++++++++++++++-----
 t/t1400-update-ref.sh |  4 ++--
 2 files changed, 17 insertions(+), 7 deletions(-)

diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 1292cfe..02b5f95 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -62,16 +62,26 @@ static void update_store_old_sha1(struct ref_update *update,
 	update->have_old = *oldvalue || line_termination;
 }
 
+/*
+ * Parse one whitespace- or NUL-terminated, possibly C-quoted argument
+ * and append the result to arg.  Return a pointer to the terminator.
+ * Die if there is an error in how the argument is C-quoted.  This
+ * function is only used if not -z.
+ */
 static const char *parse_arg(const char *next, struct strbuf *arg)
 {
-	/* Parse SP-terminated, possibly C-quoted argument */
-	if (*next != '"')
+	if (*next == '"') {
+		const char *orig = next;
+
+		if (unquote_c_style(arg, next, &next))
+			die("badly quoted argument: %s", orig);
+		if (*next && !isspace(*next))
+			die("unexpected character after quoted argument: %s", orig);
+	} else {
 		while (*next && !isspace(*next))
 			strbuf_addch(arg, *next++);
-	else if (unquote_c_style(arg, next, &next))
-		die("badly quoted argument: %s", next);
+	}
 
-	/* Return position after the argument */
 	return next;
 }
 
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index e2f1dfa..5836842 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -356,10 +356,10 @@ test_expect_success 'stdin fails on badly quoted input' '
 	grep "fatal: badly quoted argument: \\\"master" err
 '
 
-test_expect_success 'stdin fails on arguments not separated by space' '
+test_expect_success 'stdin fails on junk after quoted argument' '
 	echo "create \"$a\"master" >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: expected SP but got: master" err
+	grep "fatal: unexpected character after quoted argument: \\\"$a\\\"master" err
 '
 
 test_expect_success 'stdin fails create with no ref' '
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 05/26] t1400: Add some more tests involving quoted arguments
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (3 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 04/26] parse_arg(): Really test that argument is properly terminated Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 13:53   ` Johan Herland
  2014-03-10 12:46 ` [PATCH 06/26] refs.h: Rename the action_on_err constants Michael Haggerty
                   ` (21 subsequent siblings)
  26 siblings, 1 reply; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

Previously there were no good tests of C-quoted arguments.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 t/t1400-update-ref.sh | 26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 5836842..627aaaf 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -350,12 +350,18 @@ test_expect_success 'stdin fails on unknown command' '
 	grep "fatal: unknown command: unknown $a" err
 '
 
-test_expect_success 'stdin fails on badly quoted input' '
+test_expect_success 'stdin fails on unbalanced quotes' '
 	echo "create $a \"master" >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
 	grep "fatal: badly quoted argument: \\\"master" err
 '
 
+test_expect_success 'stdin fails on invalid escape' '
+	echo "create $a \"ma\zter\"" >stdin &&
+	test_must_fail git update-ref --stdin <stdin 2>err &&
+	grep "fatal: badly quoted argument: \\\"ma\\\\zter\\\"" err
+'
+
 test_expect_success 'stdin fails on junk after quoted argument' '
 	echo "create \"$a\"master" >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
@@ -458,6 +464,24 @@ test_expect_success 'stdin create ref works' '
 	test_cmp expect actual
 '
 
+test_expect_success 'stdin succeeds with quoted argument' '
+	git update-ref -d $a &&
+	echo "create $a \"$m\"" >stdin &&
+	git update-ref --stdin <stdin &&
+	git rev-parse $m >expect &&
+	git rev-parse $a >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'stdin succeeds with escaped character' '
+	git update-ref -d $a &&
+	echo "create $a \"ma\\163ter\"" >stdin &&
+	git update-ref --stdin <stdin &&
+	git rev-parse $m >expect &&
+	git rev-parse $a >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'stdin update ref creates with zero old value' '
 	echo "update $b $m $Z" >stdin &&
 	git update-ref --stdin <stdin &&
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 06/26] refs.h: Rename the action_on_err constants
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (4 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 05/26] t1400: Add some more tests involving quoted arguments Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 07/26] update_refs(): Fix constness Michael Haggerty
                   ` (20 subsequent siblings)
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

Given that these constants are only being used when updating
references, it is inappropriate to give them such generic names as
"DIE_ON_ERR".  So prefix their names with "UPDATE_REFS_".

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 builtin/checkout.c                     |  2 +-
 builtin/clone.c                        |  9 +++++----
 builtin/merge.c                        |  6 +++---
 builtin/notes.c                        |  6 +++---
 builtin/reset.c                        |  6 ++++--
 builtin/update-ref.c                   |  5 +++--
 contrib/examples/builtin-fetch--tool.c |  3 ++-
 notes-cache.c                          |  2 +-
 notes-utils.c                          |  3 ++-
 refs.c                                 | 18 +++++++++---------
 refs.h                                 |  9 +++++++--
 11 files changed, 40 insertions(+), 29 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index ada51fa..f79b222 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -624,7 +624,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
 		/* Nothing to do. */
 	} else if (opts->force_detach || !new->path) {	/* No longer on any branch. */
 		update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
-			   REF_NODEREF, DIE_ON_ERR);
+			   REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
 		if (!opts->quiet) {
 			if (old->path && advice_detached_head)
 				detach_advice(new->name);
diff --git a/builtin/clone.c b/builtin/clone.c
index 43e772c..af3b86f 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -521,7 +521,7 @@ static void write_followtags(const struct ref *refs, const char *msg)
 		if (!has_sha1_file(ref->old_sha1))
 			continue;
 		update_ref(msg, ref->name, ref->old_sha1,
-			   NULL, 0, DIE_ON_ERR);
+			   NULL, 0, UPDATE_REFS_DIE_ON_ERR);
 	}
 }
 
@@ -589,14 +589,15 @@ static void update_head(const struct ref *our, const struct ref *remote,
 		create_symref("HEAD", our->name, NULL);
 		if (!option_bare) {
 			const char *head = skip_prefix(our->name, "refs/heads/");
-			update_ref(msg, "HEAD", our->old_sha1, NULL, 0, DIE_ON_ERR);
+			update_ref(msg, "HEAD", our->old_sha1, NULL, 0,
+				   UPDATE_REFS_DIE_ON_ERR);
 			install_branch_config(0, head, option_origin, our->name);
 		}
 	} else if (our) {
 		struct commit *c = lookup_commit_reference(our->old_sha1);
 		/* --branch specifies a non-branch (i.e. tags), detach HEAD */
 		update_ref(msg, "HEAD", c->object.sha1,
-			   NULL, REF_NODEREF, DIE_ON_ERR);
+			   NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
 	} else if (remote) {
 		/*
 		 * We know remote HEAD points to a non-branch, or
@@ -604,7 +605,7 @@ static void update_head(const struct ref *our, const struct ref *remote,
 		 * Detach HEAD in all these cases.
 		 */
 		update_ref(msg, "HEAD", remote->old_sha1,
-			   NULL, REF_NODEREF, DIE_ON_ERR);
+			   NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
 	}
 }
 
diff --git a/builtin/merge.c b/builtin/merge.c
index f0cf120..a4c3b17 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -398,7 +398,7 @@ static void finish(struct commit *head_commit,
 			const char *argv_gc_auto[] = { "gc", "--auto", NULL };
 			update_ref(reflog_message.buf, "HEAD",
 				new_head, head, 0,
-				DIE_ON_ERR);
+				UPDATE_REFS_DIE_ON_ERR);
 			/*
 			 * We ignore errors in 'gc --auto', since the
 			 * user should see them.
@@ -1222,7 +1222,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 			die(_("%s - not something we can merge"), argv[0]);
 		read_empty(remote_head->object.sha1, 0);
 		update_ref("initial pull", "HEAD", remote_head->object.sha1,
-			   NULL, 0, DIE_ON_ERR);
+			   NULL, 0, UPDATE_REFS_DIE_ON_ERR);
 		goto done;
 	} else {
 		struct strbuf merge_names = STRBUF_INIT;
@@ -1339,7 +1339,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 	}
 
 	update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.sha1,
-		   NULL, 0, DIE_ON_ERR);
+		   NULL, 0, UPDATE_REFS_DIE_ON_ERR);
 
 	if (remoteheads && !common)
 		; /* No common ancestors found. We need a real merge. */
diff --git a/builtin/notes.c b/builtin/notes.c
index 2b24d05..5e11a3e 100644
--- a/builtin/notes.c
+++ b/builtin/notes.c
@@ -713,7 +713,7 @@ static int merge_commit(struct notes_merge_options *o)
 	strbuf_insert(&msg, 0, "notes: ", 7);
 	update_ref(msg.buf, o->local_ref, sha1,
 		   is_null_sha1(parent_sha1) ? NULL : parent_sha1,
-		   0, DIE_ON_ERR);
+		   0, UPDATE_REFS_DIE_ON_ERR);
 
 	free_notes(t);
 	strbuf_release(&msg);
@@ -808,11 +808,11 @@ static int merge(int argc, const char **argv, const char *prefix)
 	if (result >= 0) /* Merge resulted (trivially) in result_sha1 */
 		/* Update default notes ref with new commit */
 		update_ref(msg.buf, default_notes_ref(), result_sha1, NULL,
-			   0, DIE_ON_ERR);
+			   0, UPDATE_REFS_DIE_ON_ERR);
 	else { /* Merge has unresolved conflicts */
 		/* Update .git/NOTES_MERGE_PARTIAL with partial merge result */
 		update_ref(msg.buf, "NOTES_MERGE_PARTIAL", result_sha1, NULL,
-			   0, DIE_ON_ERR);
+			   0, UPDATE_REFS_DIE_ON_ERR);
 		/* Store ref-to-be-updated into .git/NOTES_MERGE_REF */
 		if (create_symref("NOTES_MERGE_REF", default_notes_ref(), NULL))
 			die("Failed to store link to current notes ref (%s)",
diff --git a/builtin/reset.c b/builtin/reset.c
index 4fd1c6c..15a96aa 100644
--- a/builtin/reset.c
+++ b/builtin/reset.c
@@ -252,11 +252,13 @@ static int reset_refs(const char *rev, const unsigned char *sha1)
 	if (!get_sha1("HEAD", sha1_orig)) {
 		orig = sha1_orig;
 		set_reflog_message(&msg, "updating ORIG_HEAD", NULL);
-		update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0, MSG_ON_ERR);
+		update_ref(msg.buf, "ORIG_HEAD", orig, old_orig, 0,
+			   UPDATE_REFS_MSG_ON_ERR);
 	} else if (old_orig)
 		delete_ref("ORIG_HEAD", old_orig, 0);
 	set_reflog_message(&msg, "updating HEAD", rev);
-	update_ref_status = update_ref(msg.buf, "HEAD", sha1, orig, 0, MSG_ON_ERR);
+	update_ref_status = update_ref(msg.buf, "HEAD", sha1, orig, 0,
+				       UPDATE_REFS_MSG_ON_ERR);
 	strbuf_release(&msg);
 	return update_ref_status;
 }
diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 02b5f95..f6345e5 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -282,7 +282,8 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
 		if (end_null)
 			line_termination = '\0';
 		update_refs_stdin();
-		return update_refs(msg, updates, updates_count, DIE_ON_ERR);
+		return update_refs(msg, updates, updates_count,
+				   UPDATE_REFS_DIE_ON_ERR);
 	}
 
 	if (end_null)
@@ -314,5 +315,5 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
 		return delete_ref(refname, oldval ? oldsha1 : NULL, flags);
 	else
 		return update_ref(msg, refname, sha1, oldval ? oldsha1 : NULL,
-				  flags, DIE_ON_ERR);
+				  flags, UPDATE_REFS_DIE_ON_ERR);
 }
diff --git a/contrib/examples/builtin-fetch--tool.c b/contrib/examples/builtin-fetch--tool.c
index 8bc8c75..ee19166 100644
--- a/contrib/examples/builtin-fetch--tool.c
+++ b/contrib/examples/builtin-fetch--tool.c
@@ -31,7 +31,8 @@ static int update_ref_env(const char *action,
 		rla = "(reflog update)";
 	if (snprintf(msg, sizeof(msg), "%s: %s", rla, action) >= sizeof(msg))
 		warning("reflog message too long: %.*s...", 50, msg);
-	return update_ref(msg, refname, sha1, oldval, 0, QUIET_ON_ERR);
+	return update_ref(msg, refname, sha1, oldval, 0,
+			  UPDATE_REFS_QUIET_ON_ERR);
 }
 
 static int update_local_ref(const char *name,
diff --git a/notes-cache.c b/notes-cache.c
index eabe4a0..97dfd63 100644
--- a/notes-cache.c
+++ b/notes-cache.c
@@ -62,7 +62,7 @@ int notes_cache_write(struct notes_cache *c)
 	if (commit_tree(&msg, tree_sha1, NULL, commit_sha1, NULL, NULL) < 0)
 		return -1;
 	if (update_ref("update notes cache", c->tree.ref, commit_sha1, NULL,
-		       0, QUIET_ON_ERR) < 0)
+		       0, UPDATE_REFS_QUIET_ON_ERR) < 0)
 		return -1;
 
 	return 0;
diff --git a/notes-utils.c b/notes-utils.c
index 2975dcd..e559642 100644
--- a/notes-utils.c
+++ b/notes-utils.c
@@ -48,7 +48,8 @@ void commit_notes(struct notes_tree *t, const char *msg)
 
 	create_notes_commit(t, NULL, &buf, commit_sha1);
 	strbuf_insert(&buf, 0, "notes: ", 7); /* commit message starts at index 7 */
-	update_ref(buf.buf, t->ref, commit_sha1, NULL, 0, DIE_ON_ERR);
+	update_ref(buf.buf, t->ref, commit_sha1, NULL, 0,
+		   UPDATE_REFS_DIE_ON_ERR);
 
 	strbuf_release(&buf);
 }
diff --git a/refs.c b/refs.c
index 89228e2..58faf95 100644
--- a/refs.c
+++ b/refs.c
@@ -3243,9 +3243,9 @@ static struct ref_lock *update_ref_lock(const char *refname,
 	if (!lock) {
 		const char *str = "Cannot lock the ref '%s'.";
 		switch (onerr) {
-		case MSG_ON_ERR: error(str, refname); break;
-		case DIE_ON_ERR: die(str, refname); break;
-		case QUIET_ON_ERR: break;
+		case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break;
+		case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break;
+		case UPDATE_REFS_QUIET_ON_ERR: break;
 		}
 	}
 	return lock;
@@ -3258,9 +3258,9 @@ static int update_ref_write(const char *action, const char *refname,
 	if (write_ref_sha1(lock, sha1, action) < 0) {
 		const char *str = "Cannot update the ref '%s'.";
 		switch (onerr) {
-		case MSG_ON_ERR: error(str, refname); break;
-		case DIE_ON_ERR: die(str, refname); break;
-		case QUIET_ON_ERR: break;
+		case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break;
+		case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break;
+		case UPDATE_REFS_QUIET_ON_ERR: break;
 		}
 		return 1;
 	}
@@ -3294,11 +3294,11 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n,
 			const char *str =
 				"Multiple updates for ref '%s' not allowed.";
 			switch (onerr) {
-			case MSG_ON_ERR:
+			case UPDATE_REFS_MSG_ON_ERR:
 				error(str, updates[i]->ref_name); break;
-			case DIE_ON_ERR:
+			case UPDATE_REFS_DIE_ON_ERR:
 				die(str, updates[i]->ref_name); break;
-			case QUIET_ON_ERR:
+			case UPDATE_REFS_QUIET_ON_ERR:
 				break;
 			}
 			return 1;
diff --git a/refs.h b/refs.h
index 87a1a79..a713b34 100644
--- a/refs.h
+++ b/refs.h
@@ -214,8 +214,13 @@ extern int rename_ref(const char *oldref, const char *newref, const char *logmsg
  */
 extern int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1);
 
-/** lock a ref and then write its file */
-enum action_on_err { MSG_ON_ERR, DIE_ON_ERR, QUIET_ON_ERR };
+enum action_on_err {
+	UPDATE_REFS_MSG_ON_ERR,
+	UPDATE_REFS_DIE_ON_ERR,
+	UPDATE_REFS_QUIET_ON_ERR
+};
+
+/** Lock a ref and then write its file */
 int update_ref(const char *action, const char *refname,
 		const unsigned char *sha1, const unsigned char *oldval,
 		int flags, enum action_on_err onerr);
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 07/26] update_refs(): Fix constness
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (5 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 06/26] refs.h: Rename the action_on_err constants Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 08/26] update-ref --stdin: Read the whole input at once Michael Haggerty
                   ` (19 subsequent siblings)
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

Since full const correctness is beyond the ability of C's type system,
just put the const where it doesn't do any harm.  A (struct ref_update
**) can be passed to a (struct ref_update * const *) argument, but not
to a (const struct ref_update **) argument.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 builtin/update-ref.c | 2 +-
 refs.c               | 2 +-
 refs.h               | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index f6345e5..a8a68e8 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -14,7 +14,7 @@ static const char * const git_update_ref_usage[] = {
 
 static int updates_alloc;
 static int updates_count;
-static const struct ref_update **updates;
+static struct ref_update **updates;
 
 static char line_termination = '\n';
 static int update_flags;
diff --git a/refs.c b/refs.c
index 58faf95..0963077 100644
--- a/refs.c
+++ b/refs.c
@@ -3306,7 +3306,7 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n,
 	return 0;
 }
 
-int update_refs(const char *action, const struct ref_update **updates_orig,
+int update_refs(const char *action, struct ref_update * const *updates_orig,
 		int n, enum action_on_err onerr)
 {
 	int ret = 0, delnum = 0, i;
diff --git a/refs.h b/refs.h
index a713b34..08e60ac 100644
--- a/refs.h
+++ b/refs.h
@@ -228,7 +228,7 @@ int update_ref(const char *action, const char *refname,
 /**
  * Lock all refs and then perform all modifications.
  */
-int update_refs(const char *action, const struct ref_update **updates,
+int update_refs(const char *action, struct ref_update * const *updates,
 		int n, enum action_on_err onerr);
 
 extern int parse_hide_refs_config(const char *var, const char *value, const char *);
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 08/26] update-ref --stdin: Read the whole input at once
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (6 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 07/26] update_refs(): Fix constness Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 09/26] parse_cmd_verify(): Copy old_sha1 instead of evaluating <oldvalue> twice Michael Haggerty
                   ` (18 subsequent siblings)
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

Decouple the parsing code from the input source (the old parsing code
had to read new data even in the middle of commands).  This might also
be a tad faster, but that is inconsequential.  Add docstrings for the
parsing functions.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 builtin/update-ref.c | 170 ++++++++++++++++++++++++++++++++-------------------
 1 file changed, 108 insertions(+), 62 deletions(-)

diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index a8a68e8..5f197fe 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -85,44 +85,70 @@ static const char *parse_arg(const char *next, struct strbuf *arg)
 	return next;
 }
 
-static const char *parse_first_arg(const char *next, struct strbuf *arg)
+/*
+ * Parse the argument immediately after "command SP".  If not -z, then
+ * handle C-quoting.  Write the argument to arg.  Set *next to point
+ * at the character that terminates the argument.  Die if C-quoting is
+ * malformed.
+ */
+static void parse_first_arg(struct strbuf *input, const char **next,
+			    struct strbuf *arg)
 {
-	/* Parse argument immediately after "command SP" */
 	strbuf_reset(arg);
 	if (line_termination) {
 		/* Without -z, use the next argument */
-		next = parse_arg(next, arg);
+		*next = parse_arg(*next, arg);
 	} else {
-		/* With -z, use rest of first NUL-terminated line */
-		strbuf_addstr(arg, next);
-		next = next + arg->len;
+		/* With -z, use everything up to the next NUL */
+		strbuf_addstr(arg, *next);
+		*next += arg->len;
 	}
-	return next;
 }
 
-static const char *parse_next_arg(const char *next, struct strbuf *arg)
+/*
+ * Parse a SP/NUL separator followed by the next SP- or NUL-terminated
+ * argument, if any.  If there is an argument, write it to arg, set
+ * *next to point at the character terminating the argument, and
+ * return 0.  If there is no argument at all (not even the empty
+ * string), return a non-zero result and leave *next unchanged.
+ */
+static int parse_next_arg(struct strbuf *input, const char **next,
+			  struct strbuf *arg)
 {
-	/* Parse next SP-terminated or NUL-terminated argument, if any */
 	strbuf_reset(arg);
 	if (line_termination) {
 		/* Without -z, consume SP and use next argument */
-		if (!*next)
-			return NULL;
-		if (*next != ' ')
-			die("expected SP but got: %s", next);
-		next = parse_arg(next + 1, arg);
+		if (!**next || **next == line_termination)
+			return -1;
+		if (**next != ' ')
+			die("expected SP but got: %s", *next);
+		(*next)++;
+		*next = parse_arg(*next, arg);
 	} else {
 		/* With -z, read the next NUL-terminated line */
-		if (*next)
-			die("expected NUL but got: %s", next);
-		if (strbuf_getline(arg, stdin, '\0') == EOF)
-			return NULL;
-		next = arg->buf + arg->len;
+		if (**next)
+			die("expected NUL but got: %s", *next);
+		(*next)++;
+		if (*next == input->buf + input->len)
+			return -1;
+		strbuf_addstr(arg, *next);
+		*next += arg->len;
 	}
-	return next;
+	return 0;
 }
 
-static void parse_cmd_update(const char *next)
+
+/*
+ * The following five parse_cmd_*() functions parse the corresponding
+ * command.  In each case, next points at the character following the
+ * command name and the following space.  They each return a pointer
+ * to the character terminating the command, and die with an
+ * explanatory message if there are any parsing problems.  All of
+ * these functions handle either text or binary format input,
+ * depending on how line_termination is set.
+ */
+
+static const char *parse_cmd_update(struct strbuf *input, const char *next)
 {
 	struct strbuf ref = STRBUF_INIT;
 	struct strbuf newvalue = STRBUF_INIT;
@@ -131,26 +157,28 @@ static void parse_cmd_update(const char *next)
 
 	update = update_alloc();
 
-	if ((next = parse_first_arg(next, &ref)) != NULL && ref.buf[0])
+	parse_first_arg(input, &next, &ref);
+	if (ref.buf[0])
 		update_store_ref_name(update, ref.buf);
 	else
 		die("update line missing <ref>");
 
-	if ((next = parse_next_arg(next, &newvalue)) != NULL)
+	if (!parse_next_arg(input, &next, &newvalue))
 		update_store_new_sha1(update, newvalue.buf);
 	else
 		die("update %s missing <newvalue>", ref.buf);
 
-	if ((next = parse_next_arg(next, &oldvalue)) != NULL)
+	if (!parse_next_arg(input, &next, &oldvalue)) {
 		update_store_old_sha1(update, oldvalue.buf);
-	else if(!line_termination)
+		if (*next != line_termination)
+			die("update %s has extra input: %s", ref.buf, next);
+	} else if (!line_termination)
 		die("update %s missing [<oldvalue>] NUL", ref.buf);
 
-	if (next && *next)
-		die("update %s has extra input: %s", ref.buf, next);
+	return next;
 }
 
-static void parse_cmd_create(const char *next)
+static const char *parse_cmd_create(struct strbuf *input, const char *next)
 {
 	struct strbuf ref = STRBUF_INIT;
 	struct strbuf newvalue = STRBUF_INIT;
@@ -158,23 +186,27 @@ static void parse_cmd_create(const char *next)
 
 	update = update_alloc();
 
-	if ((next = parse_first_arg(next, &ref)) != NULL && ref.buf[0])
+	parse_first_arg(input, &next, &ref);
+	if (ref.buf[0])
 		update_store_ref_name(update, ref.buf);
 	else
 		die("create line missing <ref>");
 
-	if ((next = parse_next_arg(next, &newvalue)) != NULL)
+	if (!parse_next_arg(input, &next, &newvalue))
 		update_store_new_sha1(update, newvalue.buf);
 	else
 		die("create %s missing <newvalue>", ref.buf);
+
 	if (is_null_sha1(update->new_sha1))
 		die("create %s given zero new value", ref.buf);
 
-	if (next && *next)
+	if (*next != line_termination)
 		die("create %s has extra input: %s", ref.buf, next);
+
+	return next;
 }
 
-static void parse_cmd_delete(const char *next)
+static const char *parse_cmd_delete(struct strbuf *input, const char *next)
 {
 	struct strbuf ref = STRBUF_INIT;
 	struct strbuf oldvalue = STRBUF_INIT;
@@ -182,23 +214,26 @@ static void parse_cmd_delete(const char *next)
 
 	update = update_alloc();
 
-	if ((next = parse_first_arg(next, &ref)) != NULL && ref.buf[0])
+	parse_first_arg(input, &next, &ref);
+	if (ref.buf[0])
 		update_store_ref_name(update, ref.buf);
 	else
 		die("delete line missing <ref>");
 
-	if ((next = parse_next_arg(next, &oldvalue)) != NULL)
+	if (!parse_next_arg(input, &next, &oldvalue)) {
 		update_store_old_sha1(update, oldvalue.buf);
-	else if(!line_termination)
+		if (update->have_old && is_null_sha1(update->old_sha1))
+			die("delete %s given zero old value", ref.buf);
+	} else if (!line_termination)
 		die("delete %s missing [<oldvalue>] NUL", ref.buf);
-	if (update->have_old && is_null_sha1(update->old_sha1))
-		die("delete %s given zero old value", ref.buf);
 
-	if (next && *next)
+	if (*next != line_termination)
 		die("delete %s has extra input: %s", ref.buf, next);
+
+	return next;
 }
 
-static void parse_cmd_verify(const char *next)
+static const char *parse_cmd_verify(struct strbuf *input, const char *next)
 {
 	struct strbuf ref = STRBUF_INIT;
 	struct strbuf value = STRBUF_INIT;
@@ -206,53 +241,64 @@ static void parse_cmd_verify(const char *next)
 
 	update = update_alloc();
 
-	if ((next = parse_first_arg(next, &ref)) != NULL && ref.buf[0])
+	parse_first_arg(input, &next, &ref);
+	if (ref.buf[0])
 		update_store_ref_name(update, ref.buf);
 	else
 		die("verify line missing <ref>");
 
-	if ((next = parse_next_arg(next, &value)) != NULL) {
+	if (!parse_next_arg(input, &next, &value)) {
 		update_store_old_sha1(update, value.buf);
 		update_store_new_sha1(update, value.buf);
-	} else if(!line_termination)
+	} else if (!line_termination)
 		die("verify %s missing [<oldvalue>] NUL", ref.buf);
 
-	if (next && *next)
+	if (*next != line_termination)
 		die("verify %s has extra input: %s", ref.buf, next);
+
+	return next;
 }
 
-static void parse_cmd_option(const char *next)
+static const char *parse_cmd_option(struct strbuf *input, const char *next)
 {
-	if (!strcmp(next, "no-deref"))
+	if (!strncmp(next, "no-deref", 8) && next[8] == line_termination)
 		update_flags |= REF_NODEREF;
 	else
 		die("option unknown: %s", next);
+	return next + 8;
 }
 
 static void update_refs_stdin(void)
 {
-	struct strbuf cmd = STRBUF_INIT;
+	struct strbuf input = STRBUF_INIT;
+	const char *next;
 
+	if (strbuf_read(&input, 0, 1000) < 0)
+		die_errno("could not read from stdin");
+	next = input.buf;
 	/* Read each line dispatch its command */
-	while (strbuf_getline(&cmd, stdin, line_termination) != EOF)
-		if (!cmd.buf[0])
+	while (next < input.buf + input.len) {
+		if (*next == line_termination)
 			die("empty command in input");
-		else if (isspace(*cmd.buf))
-			die("whitespace before command: %s", cmd.buf);
-		else if (starts_with(cmd.buf, "update "))
-			parse_cmd_update(cmd.buf + 7);
-		else if (starts_with(cmd.buf, "create "))
-			parse_cmd_create(cmd.buf + 7);
-		else if (starts_with(cmd.buf, "delete "))
-			parse_cmd_delete(cmd.buf + 7);
-		else if (starts_with(cmd.buf, "verify "))
-			parse_cmd_verify(cmd.buf + 7);
-		else if (starts_with(cmd.buf, "option "))
-			parse_cmd_option(cmd.buf + 7);
+		else if (isspace(*next))
+			die("whitespace before command: %s", next);
+		else if (starts_with(next, "update "))
+			next = parse_cmd_update(&input, next + 7);
+		else if (starts_with(next, "create "))
+			next = parse_cmd_create(&input, next + 7);
+		else if (starts_with(next, "delete "))
+			next = parse_cmd_delete(&input, next + 7);
+		else if (starts_with(next, "verify "))
+			next = parse_cmd_verify(&input, next + 7);
+		else if (starts_with(next, "option "))
+			next = parse_cmd_option(&input, next + 7);
 		else
-			die("unknown command: %s", cmd.buf);
+			die("unknown command: %s", next);
+
+		next++;
+	}
 
-	strbuf_release(&cmd);
+	strbuf_release(&input);
 }
 
 int cmd_update_ref(int argc, const char **argv, const char *prefix)
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 09/26] parse_cmd_verify(): Copy old_sha1 instead of evaluating <oldvalue> twice
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (7 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 08/26] update-ref --stdin: Read the whole input at once Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 10/26] update-ref.c: Extract a new function, parse_refname() Michael Haggerty
                   ` (17 subsequent siblings)
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

Aside from avoiding work, this makes it transparently obvious that
old_sha1 and new_sha1 are identical.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 builtin/update-ref.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 5f197fe..51adf2d 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -249,7 +249,7 @@ static const char *parse_cmd_verify(struct strbuf *input, const char *next)
 
 	if (!parse_next_arg(input, &next, &value)) {
 		update_store_old_sha1(update, value.buf);
-		update_store_new_sha1(update, value.buf);
+		hashcpy(update->new_sha1, update->old_sha1);
 	} else if (!line_termination)
 		die("verify %s missing [<oldvalue>] NUL", ref.buf);
 
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 10/26] update-ref.c: Extract a new function, parse_refname()
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (8 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 09/26] parse_cmd_verify(): Copy old_sha1 instead of evaluating <oldvalue> twice Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 11/26] update-ref --stdin: Improve error messages for invalid values Michael Haggerty
                   ` (16 subsequent siblings)
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

There is no reason to obscure the fact that parse_first_arg() always
parses refnames.  Form the new function by combining parse_first_arg()
and update_store_ref_name().

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 builtin/update-ref.c | 90 ++++++++++++++++++++++++----------------------------
 1 file changed, 41 insertions(+), 49 deletions(-)

diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 51adf2d..0dc2061 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -35,14 +35,6 @@ static struct ref_update *update_alloc(void)
 	return update;
 }
 
-static void update_store_ref_name(struct ref_update *update,
-				  const char *ref_name)
-{
-	if (check_refname_format(ref_name, REFNAME_ALLOW_ONELEVEL))
-		die("invalid ref format: %s", ref_name);
-	update->ref_name = xstrdup(ref_name);
-}
-
 static void update_store_new_sha1(struct ref_update *update,
 				  const char *newvalue)
 {
@@ -86,23 +78,35 @@ static const char *parse_arg(const char *next, struct strbuf *arg)
 }
 
 /*
- * Parse the argument immediately after "command SP".  If not -z, then
- * handle C-quoting.  Write the argument to arg.  Set *next to point
- * at the character that terminates the argument.  Die if C-quoting is
- * malformed.
+ * Parse the reference name immediately after "command SP".  If not
+ * -z, then handle C-quoting.  Return a pointer to a newly allocated
+ * string containing the name of the reference, or NULL if there was
+ * an error.  Update *next to point at the character that terminates
+ * the argument.  Die if C-quoting is malformed or the reference name
+ * is invalid.
  */
-static void parse_first_arg(struct strbuf *input, const char **next,
-			    struct strbuf *arg)
+static char *parse_refname(struct strbuf *input, const char **next)
 {
-	strbuf_reset(arg);
+	struct strbuf ref = STRBUF_INIT;
+
 	if (line_termination) {
 		/* Without -z, use the next argument */
-		*next = parse_arg(*next, arg);
+		*next = parse_arg(*next, &ref);
 	} else {
 		/* With -z, use everything up to the next NUL */
-		strbuf_addstr(arg, *next);
-		*next += arg->len;
+		strbuf_addstr(&ref, *next);
+		*next += ref.len;
+	}
+
+	if (!ref.len) {
+		strbuf_release(&ref);
+		return NULL;
 	}
+
+	if (check_refname_format(ref.buf, REFNAME_ALLOW_ONELEVEL))
+		die("invalid ref format: %s", ref.buf);
+
+	return strbuf_detach(&ref, NULL);
 }
 
 /*
@@ -150,111 +154,99 @@ static int parse_next_arg(struct strbuf *input, const char **next,
 
 static const char *parse_cmd_update(struct strbuf *input, const char *next)
 {
-	struct strbuf ref = STRBUF_INIT;
 	struct strbuf newvalue = STRBUF_INIT;
 	struct strbuf oldvalue = STRBUF_INIT;
 	struct ref_update *update;
 
 	update = update_alloc();
 
-	parse_first_arg(input, &next, &ref);
-	if (ref.buf[0])
-		update_store_ref_name(update, ref.buf);
-	else
+	update->ref_name = parse_refname(input, &next);
+	if (!update->ref_name)
 		die("update line missing <ref>");
 
 	if (!parse_next_arg(input, &next, &newvalue))
 		update_store_new_sha1(update, newvalue.buf);
 	else
-		die("update %s missing <newvalue>", ref.buf);
+		die("update %s missing <newvalue>", update->ref_name);
 
 	if (!parse_next_arg(input, &next, &oldvalue)) {
 		update_store_old_sha1(update, oldvalue.buf);
 		if (*next != line_termination)
-			die("update %s has extra input: %s", ref.buf, next);
+			die("update %s has extra input: %s", update->ref_name, next);
 	} else if (!line_termination)
-		die("update %s missing [<oldvalue>] NUL", ref.buf);
+		die("update %s missing [<oldvalue>] NUL", update->ref_name);
 
 	return next;
 }
 
 static const char *parse_cmd_create(struct strbuf *input, const char *next)
 {
-	struct strbuf ref = STRBUF_INIT;
 	struct strbuf newvalue = STRBUF_INIT;
 	struct ref_update *update;
 
 	update = update_alloc();
 
-	parse_first_arg(input, &next, &ref);
-	if (ref.buf[0])
-		update_store_ref_name(update, ref.buf);
-	else
+	update->ref_name = parse_refname(input, &next);
+	if (!update->ref_name)
 		die("create line missing <ref>");
 
 	if (!parse_next_arg(input, &next, &newvalue))
 		update_store_new_sha1(update, newvalue.buf);
 	else
-		die("create %s missing <newvalue>", ref.buf);
+		die("create %s missing <newvalue>", update->ref_name);
 
 	if (is_null_sha1(update->new_sha1))
-		die("create %s given zero new value", ref.buf);
+		die("create %s given zero new value", update->ref_name);
 
 	if (*next != line_termination)
-		die("create %s has extra input: %s", ref.buf, next);
+		die("create %s has extra input: %s", update->ref_name, next);
 
 	return next;
 }
 
 static const char *parse_cmd_delete(struct strbuf *input, const char *next)
 {
-	struct strbuf ref = STRBUF_INIT;
 	struct strbuf oldvalue = STRBUF_INIT;
 	struct ref_update *update;
 
 	update = update_alloc();
 
-	parse_first_arg(input, &next, &ref);
-	if (ref.buf[0])
-		update_store_ref_name(update, ref.buf);
-	else
+	update->ref_name = parse_refname(input, &next);
+	if (!update->ref_name)
 		die("delete line missing <ref>");
 
 	if (!parse_next_arg(input, &next, &oldvalue)) {
 		update_store_old_sha1(update, oldvalue.buf);
 		if (update->have_old && is_null_sha1(update->old_sha1))
-			die("delete %s given zero old value", ref.buf);
+			die("delete %s given zero old value", update->ref_name);
 	} else if (!line_termination)
-		die("delete %s missing [<oldvalue>] NUL", ref.buf);
+		die("delete %s missing [<oldvalue>] NUL", update->ref_name);
 
 	if (*next != line_termination)
-		die("delete %s has extra input: %s", ref.buf, next);
+		die("delete %s has extra input: %s", update->ref_name, next);
 
 	return next;
 }
 
 static const char *parse_cmd_verify(struct strbuf *input, const char *next)
 {
-	struct strbuf ref = STRBUF_INIT;
 	struct strbuf value = STRBUF_INIT;
 	struct ref_update *update;
 
 	update = update_alloc();
 
-	parse_first_arg(input, &next, &ref);
-	if (ref.buf[0])
-		update_store_ref_name(update, ref.buf);
-	else
+	update->ref_name = parse_refname(input, &next);
+	if (!update->ref_name)
 		die("verify line missing <ref>");
 
 	if (!parse_next_arg(input, &next, &value)) {
 		update_store_old_sha1(update, value.buf);
 		hashcpy(update->new_sha1, update->old_sha1);
 	} else if (!line_termination)
-		die("verify %s missing [<oldvalue>] NUL", ref.buf);
+		die("verify %s missing [<oldvalue>] NUL", update->ref_name);
 
 	if (*next != line_termination)
-		die("verify %s has extra input: %s", ref.buf, next);
+		die("verify %s has extra input: %s", update->ref_name, next);
 
 	return next;
 }
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 11/26] update-ref --stdin: Improve error messages for invalid values
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (9 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 10/26] update-ref.c: Extract a new function, parse_refname() Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 12/26] update-ref --stdin: Make error messages more consistent Michael Haggerty
                   ` (15 subsequent siblings)
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

If an invalid value is passed to "update-ref --stdin" as <oldvalue> or
<newvalue>, include the command and the name of the reference at the
beginning of the error message.  Update the tests accordingly.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 builtin/update-ref.c  | 24 +++++++++++++-----------
 t/t1400-update-ref.sh |  8 ++++----
 2 files changed, 17 insertions(+), 15 deletions(-)

diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 0dc2061..13a884a 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -35,20 +35,22 @@ static struct ref_update *update_alloc(void)
 	return update;
 }
 
-static void update_store_new_sha1(struct ref_update *update,
+static void update_store_new_sha1(const char *command,
+				  struct ref_update *update,
 				  const char *newvalue)
 {
 	if (*newvalue && get_sha1(newvalue, update->new_sha1))
-		die("invalid new value for ref %s: %s",
-		    update->ref_name, newvalue);
+		die("%s %s: invalid new value: %s",
+		    command, update->ref_name, newvalue);
 }
 
-static void update_store_old_sha1(struct ref_update *update,
+static void update_store_old_sha1(const char *command,
+				  struct ref_update *update,
 				  const char *oldvalue)
 {
 	if (*oldvalue && get_sha1(oldvalue, update->old_sha1))
-		die("invalid old value for ref %s: %s",
-		    update->ref_name, oldvalue);
+		die("%s %s: invalid old value: %s",
+		    command, update->ref_name, oldvalue);
 
 	/* We have an old value if non-empty, or if empty without -z */
 	update->have_old = *oldvalue || line_termination;
@@ -165,12 +167,12 @@ static const char *parse_cmd_update(struct strbuf *input, const char *next)
 		die("update line missing <ref>");
 
 	if (!parse_next_arg(input, &next, &newvalue))
-		update_store_new_sha1(update, newvalue.buf);
+		update_store_new_sha1("update", update, newvalue.buf);
 	else
 		die("update %s missing <newvalue>", update->ref_name);
 
 	if (!parse_next_arg(input, &next, &oldvalue)) {
-		update_store_old_sha1(update, oldvalue.buf);
+		update_store_old_sha1("update", update, oldvalue.buf);
 		if (*next != line_termination)
 			die("update %s has extra input: %s", update->ref_name, next);
 	} else if (!line_termination)
@@ -191,7 +193,7 @@ static const char *parse_cmd_create(struct strbuf *input, const char *next)
 		die("create line missing <ref>");
 
 	if (!parse_next_arg(input, &next, &newvalue))
-		update_store_new_sha1(update, newvalue.buf);
+		update_store_new_sha1("create", update, newvalue.buf);
 	else
 		die("create %s missing <newvalue>", update->ref_name);
 
@@ -216,7 +218,7 @@ static const char *parse_cmd_delete(struct strbuf *input, const char *next)
 		die("delete line missing <ref>");
 
 	if (!parse_next_arg(input, &next, &oldvalue)) {
-		update_store_old_sha1(update, oldvalue.buf);
+		update_store_old_sha1("delete", update, oldvalue.buf);
 		if (update->have_old && is_null_sha1(update->old_sha1))
 			die("delete %s given zero old value", update->ref_name);
 	} else if (!line_termination)
@@ -240,7 +242,7 @@ static const char *parse_cmd_verify(struct strbuf *input, const char *next)
 		die("verify line missing <ref>");
 
 	if (!parse_next_arg(input, &next, &value)) {
-		update_store_old_sha1(update, value.buf);
+		update_store_old_sha1("verify", update, value.buf);
 		hashcpy(update->new_sha1, update->old_sha1);
 	} else if (!line_termination)
 		die("verify %s missing [<oldvalue>] NUL", update->ref_name);
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 627aaaf..c5be870 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -518,14 +518,14 @@ test_expect_success 'stdin update ref fails with wrong old value' '
 test_expect_success 'stdin update ref fails with bad old value' '
 	echo "update $c $m does-not-exist" >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: invalid old value for ref $c: does-not-exist" err &&
+	grep "fatal: update $c: invalid old value: does-not-exist" err &&
 	test_must_fail git rev-parse --verify -q $c
 '
 
 test_expect_success 'stdin create ref fails with bad new value' '
 	echo "create $c does-not-exist" >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: invalid new value for ref $c: does-not-exist" err &&
+	grep "fatal: create $c: invalid new value: does-not-exist" err &&
 	test_must_fail git rev-parse --verify -q $c
 '
 
@@ -840,14 +840,14 @@ test_expect_success 'stdin -z update ref fails with wrong old value' '
 test_expect_success 'stdin -z update ref fails with bad old value' '
 	printf $F "update $c" "$m" "does-not-exist" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: invalid old value for ref $c: does-not-exist" err &&
+	grep "fatal: update $c: invalid old value: does-not-exist" err &&
 	test_must_fail git rev-parse --verify -q $c
 '
 
 test_expect_success 'stdin -z create ref fails with bad new value' '
 	printf $F "create $c" "does-not-exist" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: invalid new value for ref $c: does-not-exist" err &&
+	grep "fatal: create $c: invalid new value: does-not-exist" err &&
 	test_must_fail git rev-parse --verify -q $c
 '
 
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 12/26] update-ref --stdin: Make error messages more consistent
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (10 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 11/26] update-ref --stdin: Improve error messages for invalid values Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 13/26] update-ref --stdin: Simplify error messages for missing oldvalues Michael Haggerty
                   ` (14 subsequent siblings)
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

The old error messages emitted for invalid input sometimes said
"<oldvalue>"/"<newvalue>" and sometimes said "old value"/"new value".
Convert them all to the former.  Update the tests accordingly.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 builtin/update-ref.c  |  8 ++++----
 t/t1400-update-ref.sh | 14 +++++++-------
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 13a884a..e4c0854 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -40,7 +40,7 @@ static void update_store_new_sha1(const char *command,
 				  const char *newvalue)
 {
 	if (*newvalue && get_sha1(newvalue, update->new_sha1))
-		die("%s %s: invalid new value: %s",
+		die("%s %s: invalid <newvalue>: %s",
 		    command, update->ref_name, newvalue);
 }
 
@@ -49,7 +49,7 @@ static void update_store_old_sha1(const char *command,
 				  const char *oldvalue)
 {
 	if (*oldvalue && get_sha1(oldvalue, update->old_sha1))
-		die("%s %s: invalid old value: %s",
+		die("%s %s: invalid <oldvalue>: %s",
 		    command, update->ref_name, oldvalue);
 
 	/* We have an old value if non-empty, or if empty without -z */
@@ -198,7 +198,7 @@ static const char *parse_cmd_create(struct strbuf *input, const char *next)
 		die("create %s missing <newvalue>", update->ref_name);
 
 	if (is_null_sha1(update->new_sha1))
-		die("create %s given zero new value", update->ref_name);
+		die("create %s given zero <newvalue>", update->ref_name);
 
 	if (*next != line_termination)
 		die("create %s has extra input: %s", update->ref_name, next);
@@ -220,7 +220,7 @@ static const char *parse_cmd_delete(struct strbuf *input, const char *next)
 	if (!parse_next_arg(input, &next, &oldvalue)) {
 		update_store_old_sha1("delete", update, oldvalue.buf);
 		if (update->have_old && is_null_sha1(update->old_sha1))
-			die("delete %s given zero old value", update->ref_name);
+			die("delete %s given zero <oldvalue>", update->ref_name);
 	} else if (!line_termination)
 		die("delete %s missing [<oldvalue>] NUL", update->ref_name);
 
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index c5be870..3045ae7 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -518,21 +518,21 @@ test_expect_success 'stdin update ref fails with wrong old value' '
 test_expect_success 'stdin update ref fails with bad old value' '
 	echo "update $c $m does-not-exist" >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: update $c: invalid old value: does-not-exist" err &&
+	grep "fatal: update $c: invalid <oldvalue>: does-not-exist" err &&
 	test_must_fail git rev-parse --verify -q $c
 '
 
 test_expect_success 'stdin create ref fails with bad new value' '
 	echo "create $c does-not-exist" >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: create $c: invalid new value: does-not-exist" err &&
+	grep "fatal: create $c: invalid <newvalue>: does-not-exist" err &&
 	test_must_fail git rev-parse --verify -q $c
 '
 
 test_expect_success 'stdin create ref fails with zero new value' '
 	echo "create $c " >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: create $c given zero new value" err &&
+	grep "fatal: create $c given zero <newvalue>" err &&
 	test_must_fail git rev-parse --verify -q $c
 '
 
@@ -556,7 +556,7 @@ test_expect_success 'stdin delete ref fails with wrong old value' '
 test_expect_success 'stdin delete ref fails with zero old value' '
 	echo "delete $a " >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: delete $a given zero old value" err &&
+	grep "fatal: delete $a given zero <oldvalue>" err &&
 	git rev-parse $m >expect &&
 	git rev-parse $a >actual &&
 	test_cmp expect actual
@@ -840,14 +840,14 @@ test_expect_success 'stdin -z update ref fails with wrong old value' '
 test_expect_success 'stdin -z update ref fails with bad old value' '
 	printf $F "update $c" "$m" "does-not-exist" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: update $c: invalid old value: does-not-exist" err &&
+	grep "fatal: update $c: invalid <oldvalue>: does-not-exist" err &&
 	test_must_fail git rev-parse --verify -q $c
 '
 
 test_expect_success 'stdin -z create ref fails with bad new value' '
 	printf $F "create $c" "does-not-exist" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: create $c: invalid new value: does-not-exist" err &&
+	grep "fatal: create $c: invalid <newvalue>: does-not-exist" err &&
 	test_must_fail git rev-parse --verify -q $c
 '
 
@@ -878,7 +878,7 @@ test_expect_success 'stdin -z delete ref fails with wrong old value' '
 test_expect_success 'stdin -z delete ref fails with zero old value' '
 	printf $F "delete $a" "$Z" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: delete $a given zero old value" err &&
+	grep "fatal: delete $a given zero <oldvalue>" err &&
 	git rev-parse $m >expect &&
 	git rev-parse $a >actual &&
 	test_cmp expect actual
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 13/26] update-ref --stdin: Simplify error messages for missing oldvalues
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (11 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 12/26] update-ref --stdin: Make error messages more consistent Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 17:08   ` Brad King
  2014-03-10 12:46 ` [PATCH 14/26] update-ref.c: Extract a new function, parse_next_sha1() Michael Haggerty
                   ` (13 subsequent siblings)
  26 siblings, 1 reply; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

Instead of, for example,

    fatal: update refs/heads/master missing [<oldvalue>] NUL

emit

    fatal: update refs/heads/master missing <oldvalue>

Update the tests accordingly.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 builtin/update-ref.c  | 6 +++---
 t/t1400-update-ref.sh | 6 +++---
 2 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index e4c0854..a9eb5fe 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -176,7 +176,7 @@ static const char *parse_cmd_update(struct strbuf *input, const char *next)
 		if (*next != line_termination)
 			die("update %s has extra input: %s", update->ref_name, next);
 	} else if (!line_termination)
-		die("update %s missing [<oldvalue>] NUL", update->ref_name);
+		die("update %s missing <oldvalue>", update->ref_name);
 
 	return next;
 }
@@ -222,7 +222,7 @@ static const char *parse_cmd_delete(struct strbuf *input, const char *next)
 		if (update->have_old && is_null_sha1(update->old_sha1))
 			die("delete %s given zero <oldvalue>", update->ref_name);
 	} else if (!line_termination)
-		die("delete %s missing [<oldvalue>] NUL", update->ref_name);
+		die("delete %s missing <oldvalue>", update->ref_name);
 
 	if (*next != line_termination)
 		die("delete %s has extra input: %s", update->ref_name, next);
@@ -245,7 +245,7 @@ static const char *parse_cmd_verify(struct strbuf *input, const char *next)
 		update_store_old_sha1("verify", update, value.buf);
 		hashcpy(update->new_sha1, update->old_sha1);
 	} else if (!line_termination)
-		die("verify %s missing [<oldvalue>] NUL", update->ref_name);
+		die("verify %s missing <oldvalue>", update->ref_name);
 
 	if (*next != line_termination)
 		die("verify %s has extra input: %s", update->ref_name, next);
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 3045ae7..42fec4e 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -739,7 +739,7 @@ test_expect_success 'stdin -z fails update with no new value' '
 test_expect_success 'stdin -z fails update with no old value' '
 	printf $F "update $a" "$m" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: update $a missing \\[<oldvalue>\\] NUL" err
+	grep "fatal: update $a missing <oldvalue>" err
 '
 
 test_expect_success 'stdin -z fails update with too many arguments' '
@@ -763,7 +763,7 @@ test_expect_success 'stdin -z fails delete with bad ref name' '
 test_expect_success 'stdin -z fails delete with no old value' '
 	printf $F "delete $a" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: delete $a missing \\[<oldvalue>\\] NUL" err
+	grep "fatal: delete $a missing <oldvalue>" err
 '
 
 test_expect_success 'stdin -z fails delete with too many arguments' '
@@ -781,7 +781,7 @@ test_expect_success 'stdin -z fails verify with too many arguments' '
 test_expect_success 'stdin -z fails verify with no old value' '
 	printf $F "verify $a" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: verify $a missing \\[<oldvalue>\\] NUL" err
+	grep "fatal: verify $a missing <oldvalue>" err
 '
 
 test_expect_success 'stdin -z fails option with unknown name' '
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 14/26] update-ref.c: Extract a new function, parse_next_sha1()
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (12 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 13/26] update-ref --stdin: Simplify error messages for missing oldvalues Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 15/26] update-ref --stdin: Improve the error message for unexpected EOF Michael Haggerty
                   ` (12 subsequent siblings)
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

Replace three functions, update_store_new_sha1(),
update_store_old_sha1(), and parse_next_arg(), with a single function,
parse_next_sha1().  The new function takes care of a whole argument,
including checking whether it is there, converting it to an SHA-1, and
emitting errors on EOF or for invalid values.  The return value
indicates whether the argument was present or absent, which requires
a bit of intelligence because absent values are represented
differently depending on whether "-z" was used.

The new interface means that the calling functions, parse_cmd_*(),
don't have to interpret the result differently based on the
line_termination mode that is in effect.  It also means that
parse_cmd_create() can distinguish unambiguously between an empty new
value and a zeros new value, which fixes a failure in t1400.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 builtin/update-ref.c  | 138 +++++++++++++++++++++++++++-----------------------
 t/t1400-update-ref.sh |   2 +-
 2 files changed, 77 insertions(+), 63 deletions(-)

diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index a9eb5fe..5937291 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -35,27 +35,6 @@ static struct ref_update *update_alloc(void)
 	return update;
 }
 
-static void update_store_new_sha1(const char *command,
-				  struct ref_update *update,
-				  const char *newvalue)
-{
-	if (*newvalue && get_sha1(newvalue, update->new_sha1))
-		die("%s %s: invalid <newvalue>: %s",
-		    command, update->ref_name, newvalue);
-}
-
-static void update_store_old_sha1(const char *command,
-				  struct ref_update *update,
-				  const char *oldvalue)
-{
-	if (*oldvalue && get_sha1(oldvalue, update->old_sha1))
-		die("%s %s: invalid <oldvalue>: %s",
-		    command, update->ref_name, oldvalue);
-
-	/* We have an old value if non-empty, or if empty without -z */
-	update->have_old = *oldvalue || line_termination;
-}
-
 /*
  * Parse one whitespace- or NUL-terminated, possibly C-quoted argument
  * and append the result to arg.  Return a pointer to the terminator.
@@ -112,35 +91,74 @@ static char *parse_refname(struct strbuf *input, const char **next)
 }
 
 /*
- * Parse a SP/NUL separator followed by the next SP- or NUL-terminated
- * argument, if any.  If there is an argument, write it to arg, set
- * *next to point at the character terminating the argument, and
+ * Parse an argument separator followed by the next argument, if any.
+ * If there is an argument, convert it to a SHA-1, write it to sha1,
+ * set *next to point at the character terminating the argument, and
  * return 0.  If there is no argument at all (not even the empty
- * string), return a non-zero result and leave *next unchanged.
+ * string), return 1 and leave *next unchanged.  If the value is
+ * provided but cannot be converted to a SHA-1, die.
  */
-static int parse_next_arg(struct strbuf *input, const char **next,
-			  struct strbuf *arg)
+static int parse_next_sha1(struct strbuf *input, const char **next,
+			   unsigned char *sha1,
+			   const char *command, const char *refname, int old)
 {
-	strbuf_reset(arg);
+	struct strbuf arg = STRBUF_INIT;
+	int ret = 0;
+
+	if (*next == input->buf + input->len)
+		goto eof;
+
 	if (line_termination) {
 		/* Without -z, consume SP and use next argument */
 		if (!**next || **next == line_termination)
-			return -1;
+			return 1;
 		if (**next != ' ')
-			die("expected SP but got: %s", *next);
+			die("%s %s: expected SP but got: %s",
+			    command, refname, *next);
 		(*next)++;
-		*next = parse_arg(*next, arg);
+		*next = parse_arg(*next, &arg);
+		if (arg.len) {
+			if (get_sha1(arg.buf, sha1))
+				goto invalid;
+		} else {
+			/* Without -z, an empty value means all zeros: */
+			hashclr(sha1);
+		}
 	} else {
 		/* With -z, read the next NUL-terminated line */
 		if (**next)
-			die("expected NUL but got: %s", *next);
+			die("%s %s: expected NUL but got: %s",
+			    command, refname, *next);
 		(*next)++;
 		if (*next == input->buf + input->len)
-			return -1;
-		strbuf_addstr(arg, *next);
-		*next += arg->len;
+			goto eof;
+		strbuf_addstr(&arg, *next);
+		*next += arg.len;
+
+		if (arg.len) {
+			if (get_sha1(arg.buf, sha1))
+				goto invalid;
+		} else {
+			/* With -z, an empty value means unspecified: */
+			ret = 1;
+		}
 	}
-	return 0;
+
+	strbuf_release(&arg);
+
+	return ret;
+
+ invalid:
+	die(old ?
+	    "%s %s: invalid <oldvalue>: %s" :
+	    "%s %s: invalid <newvalue>: %s",
+	    command, refname, arg.buf);
+
+ eof:
+	die(old ?
+	    "%s %s missing <oldvalue>" :
+	    "%s %s missing <newvalue>",
+	    command, refname);
 }
 
 
@@ -156,8 +174,6 @@ static int parse_next_arg(struct strbuf *input, const char **next,
 
 static const char *parse_cmd_update(struct strbuf *input, const char *next)
 {
-	struct strbuf newvalue = STRBUF_INIT;
-	struct strbuf oldvalue = STRBUF_INIT;
 	struct ref_update *update;
 
 	update = update_alloc();
@@ -166,24 +182,21 @@ static const char *parse_cmd_update(struct strbuf *input, const char *next)
 	if (!update->ref_name)
 		die("update line missing <ref>");
 
-	if (!parse_next_arg(input, &next, &newvalue))
-		update_store_new_sha1("update", update, newvalue.buf);
-	else
+	if (parse_next_sha1(input, &next, update->new_sha1,
+			    "update", update->ref_name, 0))
 		die("update %s missing <newvalue>", update->ref_name);
 
-	if (!parse_next_arg(input, &next, &oldvalue)) {
-		update_store_old_sha1("update", update, oldvalue.buf);
-		if (*next != line_termination)
-			die("update %s has extra input: %s", update->ref_name, next);
-	} else if (!line_termination)
-		die("update %s missing <oldvalue>", update->ref_name);
+	update->have_old = !parse_next_sha1(input, &next, update->old_sha1,
+					    "update", update->ref_name, 1);
+
+	if (*next != line_termination)
+		die("update %s has extra input: %s", update->ref_name, next);
 
 	return next;
 }
 
 static const char *parse_cmd_create(struct strbuf *input, const char *next)
 {
-	struct strbuf newvalue = STRBUF_INIT;
 	struct ref_update *update;
 
 	update = update_alloc();
@@ -192,9 +205,8 @@ static const char *parse_cmd_create(struct strbuf *input, const char *next)
 	if (!update->ref_name)
 		die("create line missing <ref>");
 
-	if (!parse_next_arg(input, &next, &newvalue))
-		update_store_new_sha1("create", update, newvalue.buf);
-	else
+	if (parse_next_sha1(input, &next, update->new_sha1,
+			    "create", update->ref_name, 0))
 		die("create %s missing <newvalue>", update->ref_name);
 
 	if (is_null_sha1(update->new_sha1))
@@ -208,7 +220,6 @@ static const char *parse_cmd_create(struct strbuf *input, const char *next)
 
 static const char *parse_cmd_delete(struct strbuf *input, const char *next)
 {
-	struct strbuf oldvalue = STRBUF_INIT;
 	struct ref_update *update;
 
 	update = update_alloc();
@@ -217,12 +228,14 @@ static const char *parse_cmd_delete(struct strbuf *input, const char *next)
 	if (!update->ref_name)
 		die("delete line missing <ref>");
 
-	if (!parse_next_arg(input, &next, &oldvalue)) {
-		update_store_old_sha1("delete", update, oldvalue.buf);
-		if (update->have_old && is_null_sha1(update->old_sha1))
+	if (parse_next_sha1(input, &next, update->old_sha1,
+			    "delete", update->ref_name, 1)) {
+		update->have_old = 0;
+	} else {
+		if (is_null_sha1(update->old_sha1))
 			die("delete %s given zero <oldvalue>", update->ref_name);
-	} else if (!line_termination)
-		die("delete %s missing <oldvalue>", update->ref_name);
+		update->have_old = 1;
+	}
 
 	if (*next != line_termination)
 		die("delete %s has extra input: %s", update->ref_name, next);
@@ -232,7 +245,6 @@ static const char *parse_cmd_delete(struct strbuf *input, const char *next)
 
 static const char *parse_cmd_verify(struct strbuf *input, const char *next)
 {
-	struct strbuf value = STRBUF_INIT;
 	struct ref_update *update;
 
 	update = update_alloc();
@@ -241,11 +253,13 @@ static const char *parse_cmd_verify(struct strbuf *input, const char *next)
 	if (!update->ref_name)
 		die("verify line missing <ref>");
 
-	if (!parse_next_arg(input, &next, &value)) {
-		update_store_old_sha1("verify", update, value.buf);
+	if (parse_next_sha1(input, &next, update->old_sha1,
+			    "verify", update->ref_name, 1)) {
+		update->have_old = 0;
+	} else {
 		hashcpy(update->new_sha1, update->old_sha1);
-	} else if (!line_termination)
-		die("verify %s missing <oldvalue>", update->ref_name);
+		update->have_old = 1;
+	}
 
 	if (*next != line_termination)
 		die("verify %s has extra input: %s", update->ref_name, next);
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 42fec4e..7332753 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -851,7 +851,7 @@ test_expect_success 'stdin -z create ref fails with bad new value' '
 	test_must_fail git rev-parse --verify -q $c
 '
 
-test_expect_failure 'stdin -z create ref fails with empty new value' '
+test_expect_success 'stdin -z create ref fails with empty new value' '
 	printf $F "create $c" "" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
 	grep "fatal: create $c missing <newvalue>" err &&
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 15/26] update-ref --stdin: Improve the error message for unexpected EOF
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (13 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 14/26] update-ref.c: Extract a new function, parse_next_sha1() Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 16/26] update-ref --stdin: Harmonize error messages Michael Haggerty
                   ` (11 subsequent siblings)
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

Distinguish this error from the error that an argument is missing for
another reason.  Update the tests accordingly.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 builtin/update-ref.c  |  4 ++--
 t/t1400-update-ref.sh | 10 +++++-----
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 5937291..0a81a11 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -156,8 +156,8 @@ static int parse_next_sha1(struct strbuf *input, const char **next,
 
  eof:
 	die(old ?
-	    "%s %s missing <oldvalue>" :
-	    "%s %s missing <newvalue>",
+	    "%s %s: unexpected end of input when reading <oldvalue>" :
+	    "%s %s: unexpected end of input when reading <newvalue>",
 	    command, refname);
 }
 
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 7332753..e9a0103 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -709,7 +709,7 @@ test_expect_success 'stdin -z fails create with bad ref name' '
 test_expect_success 'stdin -z fails create with no new value' '
 	printf $F "create $a" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: create $a missing <newvalue>" err
+	grep "fatal: create $a: unexpected end of input when reading <newvalue>" err
 '
 
 test_expect_success 'stdin -z fails create with too many arguments' '
@@ -733,13 +733,13 @@ test_expect_success 'stdin -z fails update with bad ref name' '
 test_expect_success 'stdin -z fails update with no new value' '
 	printf $F "update $a" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: update $a missing <newvalue>" err
+	grep "fatal: update $a: unexpected end of input when reading <newvalue>" err
 '
 
 test_expect_success 'stdin -z fails update with no old value' '
 	printf $F "update $a" "$m" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: update $a missing <oldvalue>" err
+	grep "fatal: update $a: unexpected end of input when reading <oldvalue>" err
 '
 
 test_expect_success 'stdin -z fails update with too many arguments' '
@@ -763,7 +763,7 @@ test_expect_success 'stdin -z fails delete with bad ref name' '
 test_expect_success 'stdin -z fails delete with no old value' '
 	printf $F "delete $a" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: delete $a missing <oldvalue>" err
+	grep "fatal: delete $a: unexpected end of input when reading <oldvalue>" err
 '
 
 test_expect_success 'stdin -z fails delete with too many arguments' '
@@ -781,7 +781,7 @@ test_expect_success 'stdin -z fails verify with too many arguments' '
 test_expect_success 'stdin -z fails verify with no old value' '
 	printf $F "verify $a" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: verify $a missing <oldvalue>" err
+	grep "fatal: verify $a: unexpected end of input when reading <oldvalue>" err
 '
 
 test_expect_success 'stdin -z fails option with unknown name' '
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 16/26] update-ref --stdin: Harmonize error messages
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (14 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 15/26] update-ref --stdin: Improve the error message for unexpected EOF Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 17/26] refs: Add a concept of a reference transaction Michael Haggerty
                   ` (10 subsequent siblings)
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

Make (most of) the error messages for invalid input have the same
format [1]:

    $COMMAND [SP $REFNAME]: $MESSAGE

Update the tests accordingly.

[1] A few error messages still have their old form, because $COMMAND
and $REFNAME aren't passed all the way down the call stack.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
Making more error messages conform to the new pattern is an exercise
left to the reader (or maybe the writer if I find time to get back to
it).

 builtin/update-ref.c  | 24 ++++++++++++------------
 t/t1400-update-ref.sh | 32 ++++++++++++++++----------------
 2 files changed, 28 insertions(+), 28 deletions(-)

diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index 0a81a11..ac41635 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -180,17 +180,17 @@ static const char *parse_cmd_update(struct strbuf *input, const char *next)
 
 	update->ref_name = parse_refname(input, &next);
 	if (!update->ref_name)
-		die("update line missing <ref>");
+		die("update: missing <ref>");
 
 	if (parse_next_sha1(input, &next, update->new_sha1,
 			    "update", update->ref_name, 0))
-		die("update %s missing <newvalue>", update->ref_name);
+		die("update %s: missing <newvalue>", update->ref_name);
 
 	update->have_old = !parse_next_sha1(input, &next, update->old_sha1,
 					    "update", update->ref_name, 1);
 
 	if (*next != line_termination)
-		die("update %s has extra input: %s", update->ref_name, next);
+		die("update %s: extra input: %s", update->ref_name, next);
 
 	return next;
 }
@@ -203,17 +203,17 @@ static const char *parse_cmd_create(struct strbuf *input, const char *next)
 
 	update->ref_name = parse_refname(input, &next);
 	if (!update->ref_name)
-		die("create line missing <ref>");
+		die("create: missing <ref>");
 
 	if (parse_next_sha1(input, &next, update->new_sha1,
 			    "create", update->ref_name, 0))
-		die("create %s missing <newvalue>", update->ref_name);
+		die("create %s: missing <newvalue>", update->ref_name);
 
 	if (is_null_sha1(update->new_sha1))
-		die("create %s given zero <newvalue>", update->ref_name);
+		die("create %s: zero <newvalue>", update->ref_name);
 
 	if (*next != line_termination)
-		die("create %s has extra input: %s", update->ref_name, next);
+		die("create %s: extra input: %s", update->ref_name, next);
 
 	return next;
 }
@@ -226,19 +226,19 @@ static const char *parse_cmd_delete(struct strbuf *input, const char *next)
 
 	update->ref_name = parse_refname(input, &next);
 	if (!update->ref_name)
-		die("delete line missing <ref>");
+		die("delete: missing <ref>");
 
 	if (parse_next_sha1(input, &next, update->old_sha1,
 			    "delete", update->ref_name, 1)) {
 		update->have_old = 0;
 	} else {
 		if (is_null_sha1(update->old_sha1))
-			die("delete %s given zero <oldvalue>", update->ref_name);
+			die("delete %s: zero <oldvalue>", update->ref_name);
 		update->have_old = 1;
 	}
 
 	if (*next != line_termination)
-		die("delete %s has extra input: %s", update->ref_name, next);
+		die("delete %s: extra input: %s", update->ref_name, next);
 
 	return next;
 }
@@ -251,7 +251,7 @@ static const char *parse_cmd_verify(struct strbuf *input, const char *next)
 
 	update->ref_name = parse_refname(input, &next);
 	if (!update->ref_name)
-		die("verify line missing <ref>");
+		die("verify: missing <ref>");
 
 	if (parse_next_sha1(input, &next, update->old_sha1,
 			    "verify", update->ref_name, 1)) {
@@ -262,7 +262,7 @@ static const char *parse_cmd_verify(struct strbuf *input, const char *next)
 	}
 
 	if (*next != line_termination)
-		die("verify %s has extra input: %s", update->ref_name, next);
+		die("verify %s: extra input: %s", update->ref_name, next);
 
 	return next;
 }
diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index e9a0103..3cc5c66 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -371,7 +371,7 @@ test_expect_success 'stdin fails on junk after quoted argument' '
 test_expect_success 'stdin fails create with no ref' '
 	echo "create " >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: create line missing <ref>" err
+	grep "fatal: create: missing <ref>" err
 '
 
 test_expect_success 'stdin fails create with bad ref name' '
@@ -383,19 +383,19 @@ test_expect_success 'stdin fails create with bad ref name' '
 test_expect_success 'stdin fails create with no new value' '
 	echo "create $a" >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: create $a missing <newvalue>" err
+	grep "fatal: create $a: missing <newvalue>" err
 '
 
 test_expect_success 'stdin fails create with too many arguments' '
 	echo "create $a $m $m" >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: create $a has extra input:  $m" err
+	grep "fatal: create $a: extra input:  $m" err
 '
 
 test_expect_success 'stdin fails update with no ref' '
 	echo "update " >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: update line missing <ref>" err
+	grep "fatal: update: missing <ref>" err
 '
 
 test_expect_success 'stdin fails update with bad ref name' '
@@ -407,19 +407,19 @@ test_expect_success 'stdin fails update with bad ref name' '
 test_expect_success 'stdin fails update with no new value' '
 	echo "update $a" >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: update $a missing <newvalue>" err
+	grep "fatal: update $a: missing <newvalue>" err
 '
 
 test_expect_success 'stdin fails update with too many arguments' '
 	echo "update $a $m $m $m" >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: update $a has extra input:  $m" err
+	grep "fatal: update $a: extra input:  $m" err
 '
 
 test_expect_success 'stdin fails delete with no ref' '
 	echo "delete " >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: delete line missing <ref>" err
+	grep "fatal: delete: missing <ref>" err
 '
 
 test_expect_success 'stdin fails delete with bad ref name' '
@@ -431,13 +431,13 @@ test_expect_success 'stdin fails delete with bad ref name' '
 test_expect_success 'stdin fails delete with too many arguments' '
 	echo "delete $a $m $m" >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: delete $a has extra input:  $m" err
+	grep "fatal: delete $a: extra input:  $m" err
 '
 
 test_expect_success 'stdin fails verify with too many arguments' '
 	echo "verify $a $m $m" >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: verify $a has extra input:  $m" err
+	grep "fatal: verify $a: extra input:  $m" err
 '
 
 test_expect_success 'stdin fails option with unknown name' '
@@ -532,7 +532,7 @@ test_expect_success 'stdin create ref fails with bad new value' '
 test_expect_success 'stdin create ref fails with zero new value' '
 	echo "create $c " >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: create $c given zero <newvalue>" err &&
+	grep "fatal: create $c: zero <newvalue>" err &&
 	test_must_fail git rev-parse --verify -q $c
 '
 
@@ -556,7 +556,7 @@ test_expect_success 'stdin delete ref fails with wrong old value' '
 test_expect_success 'stdin delete ref fails with zero old value' '
 	echo "delete $a " >stdin &&
 	test_must_fail git update-ref --stdin <stdin 2>err &&
-	grep "fatal: delete $a given zero <oldvalue>" err &&
+	grep "fatal: delete $a: zero <oldvalue>" err &&
 	git rev-parse $m >expect &&
 	git rev-parse $a >actual &&
 	test_cmp expect actual
@@ -697,7 +697,7 @@ test_expect_success 'stdin -z fails on unknown command' '
 test_expect_success 'stdin -z fails create with no ref' '
 	printf $F "create " >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: create line missing <ref>" err
+	grep "fatal: create: missing <ref>" err
 '
 
 test_expect_success 'stdin -z fails create with bad ref name' '
@@ -721,7 +721,7 @@ test_expect_success 'stdin -z fails create with too many arguments' '
 test_expect_success 'stdin -z fails update with no ref' '
 	printf $F "update " >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: update line missing <ref>" err
+	grep "fatal: update: missing <ref>" err
 '
 
 test_expect_success 'stdin -z fails update with bad ref name' '
@@ -751,7 +751,7 @@ test_expect_success 'stdin -z fails update with too many arguments' '
 test_expect_success 'stdin -z fails delete with no ref' '
 	printf $F "delete " >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: delete line missing <ref>" err
+	grep "fatal: delete: missing <ref>" err
 '
 
 test_expect_success 'stdin -z fails delete with bad ref name' '
@@ -854,7 +854,7 @@ test_expect_success 'stdin -z create ref fails with bad new value' '
 test_expect_success 'stdin -z create ref fails with empty new value' '
 	printf $F "create $c" "" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: create $c missing <newvalue>" err &&
+	grep "fatal: create $c: missing <newvalue>" err &&
 	test_must_fail git rev-parse --verify -q $c
 '
 
@@ -878,7 +878,7 @@ test_expect_success 'stdin -z delete ref fails with wrong old value' '
 test_expect_success 'stdin -z delete ref fails with zero old value' '
 	printf $F "delete $a" "$Z" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-	grep "fatal: delete $a given zero <oldvalue>" err &&
+	grep "fatal: delete $a: zero <oldvalue>" err &&
 	git rev-parse $m >expect &&
 	git rev-parse $a >actual &&
 	test_cmp expect actual
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 17/26] refs: Add a concept of a reference transaction
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (15 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 16/26] update-ref --stdin: Harmonize error messages Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 18/26] update-ref --stdin: Reimplement using reference transactions Michael Haggerty
                   ` (9 subsequent siblings)
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

Build out the API for dealing with a bunch of reference checks and
changes within a transaction.  Define an opaque ref_transaction type
that is managed entirely within refs.c.  Introduce functions for
starting a transaction, adding updates to a transaction, and
committing a transaction.

This API will soon replace update_refs().

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c | 85 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 refs.h | 63 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 148 insertions(+)

diff --git a/refs.c b/refs.c
index 0963077..54260ce 100644
--- a/refs.c
+++ b/refs.c
@@ -3267,6 +3267,85 @@ static int update_ref_write(const char *action, const char *refname,
 	return 0;
 }
 
+/*
+ * Data structure for holding a reference transaction, which can
+ * consist of checks and updates to multiple references, carried out
+ * as atomically as possible.  This structure is opaque to callers.
+ */
+struct ref_transaction {
+	struct ref_update **updates;
+	size_t alloc;
+	size_t nr;
+};
+
+struct ref_transaction *create_ref_transaction(void)
+{
+	return xcalloc(1, sizeof(struct ref_transaction));
+}
+
+void free_ref_transaction(struct ref_transaction *transaction)
+{
+	int i;
+
+	for (i = 0; i < transaction->nr; i++) {
+		struct ref_update *update = transaction->updates[i];
+
+		free((char *)update->ref_name);
+		free(update);
+	}
+
+	free(transaction->updates);
+	free(transaction);
+}
+
+static struct ref_update *add_update(struct ref_transaction *transaction,
+				     const char *refname)
+{
+	struct ref_update *update = xcalloc(1, sizeof(*update));
+
+	update->ref_name = xstrdup(refname);
+	ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
+	transaction->updates[transaction->nr++] = update;
+	return update;
+}
+
+void queue_update_ref(struct ref_transaction *transaction, const char *refname,
+		      unsigned char *new_sha1, unsigned char *old_sha1,
+		      int flags, int have_old)
+{
+	struct ref_update *update = add_update(transaction, refname);
+
+	hashcpy(update->new_sha1, new_sha1);
+	update->flags = flags;
+	update->have_old = have_old;
+	if (have_old)
+		hashcpy(update->old_sha1, old_sha1);
+}
+
+void queue_create_ref(struct ref_transaction *transaction, const char *refname,
+		      unsigned char *new_sha1,
+		      int flags)
+{
+	struct ref_update *update = add_update(transaction, refname);
+
+	hashcpy(update->new_sha1, new_sha1);
+	hashclr(update->old_sha1);
+	update->flags = flags;
+	update->have_old = 1;
+}
+
+void queue_delete_ref(struct ref_transaction *transaction, const char *refname,
+		      unsigned char *old_sha1,
+		      int flags, int have_old)
+{
+	struct ref_update *update = add_update(transaction, refname);
+
+	update->flags = flags;
+	update->have_old = have_old;
+	if (have_old)
+		hashcpy(update->old_sha1, old_sha1);
+}
+
 int update_ref(const char *action, const char *refname,
 	       const unsigned char *sha1, const unsigned char *oldval,
 	       int flags, enum action_on_err onerr)
@@ -3378,6 +3457,12 @@ cleanup:
 	return ret;
 }
 
+int commit_ref_transaction(struct ref_transaction *transaction,
+			   const char *msg, enum action_on_err onerr)
+{
+	return update_refs(msg, transaction->updates, transaction->nr, onerr);
+}
+
 char *shorten_unambiguous_ref(const char *refname, int strict)
 {
 	int i;
diff --git a/refs.h b/refs.h
index 08e60ac..2848fb7 100644
--- a/refs.h
+++ b/refs.h
@@ -24,6 +24,8 @@ struct ref_update {
 	int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
 };
 
+struct ref_transaction;
+
 /*
  * Bit values set in the flags argument passed to each_ref_fn():
  */
@@ -220,6 +222,67 @@ enum action_on_err {
 	UPDATE_REFS_QUIET_ON_ERR
 };
 
+/*
+ * Allocate and initialize a ref_transaction object.  The object must
+ * be freed by calling free_ref_transaction().
+ */
+struct ref_transaction *create_ref_transaction(void);
+
+/*
+ * Free a ref_transaction and all associated data.  This function does
+ * not commit the transaction; that must be done first (if desired) by
+ * calling commit_ref_transaction().
+ */
+void free_ref_transaction(struct ref_transaction *transaction);
+
+
+/*
+ * The following functions add a reference check or update to a
+ * ref_transaction.  In all of them, refname is the name of the
+ * reference to be affected.  The functions make internal copies of
+ * refname, so the caller retains ownership of the parameter.  flags
+ * can be REF_NODEREF; it is passed to update_ref_lock().
+ */
+
+
+/*
+ * Add a reference update to transaction.  new_sha1 is the value that
+ * the reference should have after the update, or zeros if it should
+ * be deleted.  If have_old is true, then old_sha1 holds the value
+ * that the reference should have had before the update, or zeros if
+ * it must not have existed beforehand.
+ */
+void queue_update_ref(struct ref_transaction *transaction, const char *refname,
+		      unsigned char *new_sha1, unsigned char *old_sha1,
+		      int flags, int have_old);
+
+/*
+ * Add a reference creation to transaction.  new_sha1 is the value
+ * that the reference should have after the update, or zeros if it
+ * should be deleted.  It is verified that the reference does not
+ * exist already.
+ */
+void queue_create_ref(struct ref_transaction *transaction, const char *refname,
+		      unsigned char *new_sha1,
+		      int flags);
+
+/*
+ * Add a reference deletion to transaction.  If have_old is true, then
+ * old_sha1 holds the value that the reference should have had before
+ * the update.
+ */
+void queue_delete_ref(struct ref_transaction *transaction, const char *refname,
+		      unsigned char *old_sha1,
+		      int flags, int have_old);
+
+/*
+ * Commit all of the changes that have been queued in transaction, as
+ * atomically as possible.  Return a nonzero value if there is a
+ * problem.  The transaction is unmodified by this function.
+ */
+int commit_ref_transaction(struct ref_transaction *transaction,
+			   const char *msg, enum action_on_err onerr);
+
 /** Lock a ref and then write its file */
 int update_ref(const char *action, const char *refname,
 		const unsigned char *sha1, const unsigned char *oldval,
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 18/26] update-ref --stdin: Reimplement using reference transactions
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (16 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 17/26] refs: Add a concept of a reference transaction Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 19/26] refs: Remove API function update_refs() Michael Haggerty
                   ` (8 subsequent siblings)
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

This change is mostly clerical: the parse_cmd_*() functions need to
use local variables rather than a struct ref_update to collect the
arguments needed for each update, and then call queue_*_ref() to queue
the change rather than building up the list of changes at the caller
side.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 builtin/update-ref.c | 142 +++++++++++++++++++++++++++------------------------
 1 file changed, 76 insertions(+), 66 deletions(-)

diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index ac41635..ffed061 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -12,29 +12,11 @@ static const char * const git_update_ref_usage[] = {
 	NULL
 };
 
-static int updates_alloc;
-static int updates_count;
-static struct ref_update **updates;
+static struct ref_transaction *transaction;
 
 static char line_termination = '\n';
 static int update_flags;
 
-static struct ref_update *update_alloc(void)
-{
-	struct ref_update *update;
-
-	/* Allocate and zero-init a struct ref_update */
-	update = xcalloc(1, sizeof(*update));
-	ALLOC_GROW(updates, updates_count + 1, updates_alloc);
-	updates[updates_count++] = update;
-
-	/* Store and reset accumulated options */
-	update->flags = update_flags;
-	update_flags = 0;
-
-	return update;
-}
-
 /*
  * Parse one whitespace- or NUL-terminated, possibly C-quoted argument
  * and append the result to arg.  Return a pointer to the terminator.
@@ -174,95 +156,118 @@ static int parse_next_sha1(struct strbuf *input, const char **next,
 
 static const char *parse_cmd_update(struct strbuf *input, const char *next)
 {
-	struct ref_update *update;
-
-	update = update_alloc();
+	char *refname;
+	unsigned char new_sha1[20];
+	unsigned char old_sha1[20];
+	int have_old;
 
-	update->ref_name = parse_refname(input, &next);
-	if (!update->ref_name)
+	refname = parse_refname(input, &next);
+	if (!refname)
 		die("update: missing <ref>");
 
-	if (parse_next_sha1(input, &next, update->new_sha1,
-			    "update", update->ref_name, 0))
-		die("update %s: missing <newvalue>", update->ref_name);
+	if (parse_next_sha1(input, &next, new_sha1,
+			    "update", refname, 0))
+		die("update %s: missing <newvalue>", refname);
 
-	update->have_old = !parse_next_sha1(input, &next, update->old_sha1,
-					    "update", update->ref_name, 1);
+	have_old = !parse_next_sha1(input, &next, old_sha1,
+				    "update", refname, 1);
 
 	if (*next != line_termination)
-		die("update %s: extra input: %s", update->ref_name, next);
+		die("update %s: extra input: %s", refname, next);
+
+	queue_update_ref(transaction, refname, new_sha1, old_sha1,
+			 update_flags, have_old);
+
+	update_flags = 0;
+	free(refname);
 
 	return next;
 }
 
 static const char *parse_cmd_create(struct strbuf *input, const char *next)
 {
-	struct ref_update *update;
-
-	update = update_alloc();
+	char *refname;
+	unsigned char new_sha1[20];
 
-	update->ref_name = parse_refname(input, &next);
-	if (!update->ref_name)
+	refname = parse_refname(input, &next);
+	if (!refname)
 		die("create: missing <ref>");
 
-	if (parse_next_sha1(input, &next, update->new_sha1,
-			    "create", update->ref_name, 0))
-		die("create %s: missing <newvalue>", update->ref_name);
+	if (parse_next_sha1(input, &next, new_sha1,
+			    "create", refname, 0))
+		die("create %s: missing <newvalue>", refname);
 
-	if (is_null_sha1(update->new_sha1))
-		die("create %s: zero <newvalue>", update->ref_name);
+	if (is_null_sha1(new_sha1))
+		die("create %s: zero <newvalue>", refname);
 
 	if (*next != line_termination)
-		die("create %s: extra input: %s", update->ref_name, next);
+		die("create %s: extra input: %s", refname, next);
+
+	queue_create_ref(transaction, refname, new_sha1, update_flags);
+
+	update_flags = 0;
+	free(refname);
 
 	return next;
 }
 
 static const char *parse_cmd_delete(struct strbuf *input, const char *next)
 {
-	struct ref_update *update;
+	char *refname;
+	unsigned char old_sha1[20];
+	int have_old;
 
-	update = update_alloc();
-
-	update->ref_name = parse_refname(input, &next);
-	if (!update->ref_name)
+	refname = parse_refname(input, &next);
+	if (!refname)
 		die("delete: missing <ref>");
 
-	if (parse_next_sha1(input, &next, update->old_sha1,
-			    "delete", update->ref_name, 1)) {
-		update->have_old = 0;
+	if (parse_next_sha1(input, &next, old_sha1, "delete", refname, 1)) {
+		have_old = 0;
 	} else {
-		if (is_null_sha1(update->old_sha1))
-			die("delete %s: zero <oldvalue>", update->ref_name);
-		update->have_old = 1;
+		if (is_null_sha1(old_sha1))
+			die("delete %s: zero <oldvalue>", refname);
+		have_old = 1;
 	}
 
 	if (*next != line_termination)
-		die("delete %s: extra input: %s", update->ref_name, next);
+		die("delete %s: extra input: %s", refname, next);
+
+	queue_delete_ref(transaction, refname, old_sha1, update_flags, have_old);
+
+	update_flags = 0;
+	free(refname);
 
 	return next;
 }
 
 static const char *parse_cmd_verify(struct strbuf *input, const char *next)
 {
-	struct ref_update *update;
-
-	update = update_alloc();
+	char *refname;
+	unsigned char new_sha1[20];
+	unsigned char old_sha1[20];
+	int have_old;
 
-	update->ref_name = parse_refname(input, &next);
-	if (!update->ref_name)
+	refname = parse_refname(input, &next);
+	if (!refname)
 		die("verify: missing <ref>");
 
-	if (parse_next_sha1(input, &next, update->old_sha1,
-			    "verify", update->ref_name, 1)) {
-		update->have_old = 0;
+	if (parse_next_sha1(input, &next, old_sha1,
+			    "verify", refname, 1)) {
+		hashclr(new_sha1);
+		have_old = 0;
 	} else {
-		hashcpy(update->new_sha1, update->old_sha1);
-		update->have_old = 1;
+		hashcpy(new_sha1, old_sha1);
+		have_old = 1;
 	}
 
 	if (*next != line_termination)
-		die("verify %s: extra input: %s", update->ref_name, next);
+		die("verify %s: extra input: %s", refname, next);
+
+	queue_update_ref(transaction, refname, new_sha1, old_sha1,
+			 update_flags, have_old);
+
+	update_flags = 0;
+	free(refname);
 
 	return next;
 }
@@ -331,13 +336,18 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
 		die("Refusing to perform update with empty message.");
 
 	if (read_stdin) {
+		int ret;
+		transaction = create_ref_transaction();
+
 		if (delete || no_deref || argc > 0)
 			usage_with_options(git_update_ref_usage, options);
 		if (end_null)
 			line_termination = '\0';
 		update_refs_stdin();
-		return update_refs(msg, updates, updates_count,
-				   UPDATE_REFS_DIE_ON_ERR);
+		ret = commit_ref_transaction(transaction, msg,
+					     UPDATE_REFS_DIE_ON_ERR);
+		free_ref_transaction(transaction);
+		return ret;
 	}
 
 	if (end_null)
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 19/26] refs: Remove API function update_refs()
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (17 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 18/26] update-ref --stdin: Reimplement using reference transactions Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 20/26] struct ref_update: Rename field "ref_name" to "refname" Michael Haggerty
                   ` (7 subsequent siblings)
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

This should be done via reference transactions now.  This also means
that struct ref_update can become private.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c | 31 ++++++++++++++++++++-----------
 refs.h | 20 --------------------
 2 files changed, 20 insertions(+), 31 deletions(-)

diff --git a/refs.c b/refs.c
index 54260ce..91af0a0 100644
--- a/refs.c
+++ b/refs.c
@@ -3267,6 +3267,20 @@ static int update_ref_write(const char *action, const char *refname,
 	return 0;
 }
 
+/**
+ * Information needed for a single ref update.  Set new_sha1 to the
+ * new value or to zero to delete the ref.  To check the old value
+ * while locking the ref, set have_old to 1 and set old_sha1 to the
+ * value or to zero to ensure the ref does not exist before update.
+ */
+struct ref_update {
+	const char *ref_name;
+	unsigned char new_sha1[20];
+	unsigned char old_sha1[20];
+	int flags; /* REF_NODEREF? */
+	int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
+};
+
 /*
  * Data structure for holding a reference transaction, which can
  * consist of checks and updates to multiple references, carried out
@@ -3385,16 +3399,17 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n,
 	return 0;
 }
 
-int update_refs(const char *action, struct ref_update * const *updates_orig,
-		int n, enum action_on_err onerr)
+int commit_ref_transaction(struct ref_transaction *transaction,
+			   const char *msg, enum action_on_err onerr)
 {
 	int ret = 0, delnum = 0, i;
 	struct ref_update **updates;
 	int *types;
 	struct ref_lock **locks;
 	const char **delnames;
+	int n = transaction->nr;
 
-	if (!updates_orig || !n)
+	if (!n)
 		return 0;
 
 	/* Allocate work space */
@@ -3404,7 +3419,7 @@ int update_refs(const char *action, struct ref_update * const *updates_orig,
 	delnames = xmalloc(sizeof(*delnames) * n);
 
 	/* Copy, sort, and reject duplicate refs */
-	memcpy(updates, updates_orig, sizeof(*updates) * n);
+	memcpy(updates, transaction->updates, sizeof(*updates) * n);
 	qsort(updates, n, sizeof(*updates), ref_update_compare);
 	ret = ref_update_reject_duplicates(updates, n, onerr);
 	if (ret)
@@ -3426,7 +3441,7 @@ int update_refs(const char *action, struct ref_update * const *updates_orig,
 	/* Perform updates first so live commits remain referenced */
 	for (i = 0; i < n; i++)
 		if (!is_null_sha1(updates[i]->new_sha1)) {
-			ret = update_ref_write(action,
+			ret = update_ref_write(msg,
 					       updates[i]->ref_name,
 					       updates[i]->new_sha1,
 					       locks[i], onerr);
@@ -3457,12 +3472,6 @@ cleanup:
 	return ret;
 }
 
-int commit_ref_transaction(struct ref_transaction *transaction,
-			   const char *msg, enum action_on_err onerr)
-{
-	return update_refs(msg, transaction->updates, transaction->nr, onerr);
-}
-
 char *shorten_unambiguous_ref(const char *refname, int strict)
 {
 	int i;
diff --git a/refs.h b/refs.h
index 2848fb7..b1f8b74 100644
--- a/refs.h
+++ b/refs.h
@@ -10,20 +10,6 @@ struct ref_lock {
 	int force_write;
 };
 
-/**
- * Information needed for a single ref update.  Set new_sha1 to the
- * new value or to zero to delete the ref.  To check the old value
- * while locking the ref, set have_old to 1 and set old_sha1 to the
- * value or to zero to ensure the ref does not exist before update.
- */
-struct ref_update {
-	const char *ref_name;
-	unsigned char new_sha1[20];
-	unsigned char old_sha1[20];
-	int flags; /* REF_NODEREF? */
-	int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
-};
-
 struct ref_transaction;
 
 /*
@@ -288,12 +274,6 @@ int update_ref(const char *action, const char *refname,
 		const unsigned char *sha1, const unsigned char *oldval,
 		int flags, enum action_on_err onerr);
 
-/**
- * Lock all refs and then perform all modifications.
- */
-int update_refs(const char *action, struct ref_update * const *updates,
-		int n, enum action_on_err onerr);
-
 extern int parse_hide_refs_config(const char *var, const char *value, const char *);
 extern int ref_is_hidden(const char *);
 
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 20/26] struct ref_update: Rename field "ref_name" to "refname"
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (18 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 19/26] refs: Remove API function update_refs() Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 21/26] struct ref_update: Store refname as a FLEX_ARRAY Michael Haggerty
                   ` (6 subsequent siblings)
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

This is consistent with the usual nomenclature.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c | 18 +++++++++---------
 refs.h |  2 +-
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/refs.c b/refs.c
index 91af0a0..5d08cdf 100644
--- a/refs.c
+++ b/refs.c
@@ -3274,7 +3274,7 @@ static int update_ref_write(const char *action, const char *refname,
  * value or to zero to ensure the ref does not exist before update.
  */
 struct ref_update {
-	const char *ref_name;
+	const char *refname;
 	unsigned char new_sha1[20];
 	unsigned char old_sha1[20];
 	int flags; /* REF_NODEREF? */
@@ -3304,7 +3304,7 @@ void free_ref_transaction(struct ref_transaction *transaction)
 	for (i = 0; i < transaction->nr; i++) {
 		struct ref_update *update = transaction->updates[i];
 
-		free((char *)update->ref_name);
+		free((char *)update->refname);
 		free(update);
 	}
 
@@ -3317,7 +3317,7 @@ static struct ref_update *add_update(struct ref_transaction *transaction,
 {
 	struct ref_update *update = xcalloc(1, sizeof(*update));
 
-	update->ref_name = xstrdup(refname);
+	update->refname = xstrdup(refname);
 	ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
 	transaction->updates[transaction->nr++] = update;
 	return update;
@@ -3375,7 +3375,7 @@ static int ref_update_compare(const void *r1, const void *r2)
 {
 	const struct ref_update * const *u1 = r1;
 	const struct ref_update * const *u2 = r2;
-	return strcmp((*u1)->ref_name, (*u2)->ref_name);
+	return strcmp((*u1)->refname, (*u2)->refname);
 }
 
 static int ref_update_reject_duplicates(struct ref_update **updates, int n,
@@ -3383,14 +3383,14 @@ static int ref_update_reject_duplicates(struct ref_update **updates, int n,
 {
 	int i;
 	for (i = 1; i < n; i++)
-		if (!strcmp(updates[i - 1]->ref_name, updates[i]->ref_name)) {
+		if (!strcmp(updates[i - 1]->refname, updates[i]->refname)) {
 			const char *str =
 				"Multiple updates for ref '%s' not allowed.";
 			switch (onerr) {
 			case UPDATE_REFS_MSG_ON_ERR:
-				error(str, updates[i]->ref_name); break;
+				error(str, updates[i]->refname); break;
 			case UPDATE_REFS_DIE_ON_ERR:
-				die(str, updates[i]->ref_name); break;
+				die(str, updates[i]->refname); break;
 			case UPDATE_REFS_QUIET_ON_ERR:
 				break;
 			}
@@ -3427,7 +3427,7 @@ int commit_ref_transaction(struct ref_transaction *transaction,
 
 	/* Acquire all locks while verifying old values */
 	for (i = 0; i < n; i++) {
-		locks[i] = update_ref_lock(updates[i]->ref_name,
+		locks[i] = update_ref_lock(updates[i]->refname,
 					   (updates[i]->have_old ?
 					    updates[i]->old_sha1 : NULL),
 					   updates[i]->flags,
@@ -3442,7 +3442,7 @@ int commit_ref_transaction(struct ref_transaction *transaction,
 	for (i = 0; i < n; i++)
 		if (!is_null_sha1(updates[i]->new_sha1)) {
 			ret = update_ref_write(msg,
-					       updates[i]->ref_name,
+					       updates[i]->refname,
 					       updates[i]->new_sha1,
 					       locks[i], onerr);
 			locks[i] = NULL; /* freed by update_ref_write */
diff --git a/refs.h b/refs.h
index b1f8b74..cc24213 100644
--- a/refs.h
+++ b/refs.h
@@ -154,7 +154,7 @@ extern void unlock_ref(struct ref_lock *lock);
 extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
 
 /** Setup reflog before using. **/
-int log_ref_setup(const char *ref_name, char *logfile, int bufsize);
+int log_ref_setup(const char *refname, char *logfile, int bufsize);
 
 /** Reads log for the value of ref during at_time. **/
 extern int read_ref_at(const char *refname, unsigned long at_time, int cnt,
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 21/26] struct ref_update: Store refname as a FLEX_ARRAY.
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (19 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 20/26] struct ref_update: Rename field "ref_name" to "refname" Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 22/26] commit_ref_transaction(): Introduce temporary variables Michael Haggerty
                   ` (5 subsequent siblings)
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/refs.c b/refs.c
index 5d08cdf..335d0e2 100644
--- a/refs.c
+++ b/refs.c
@@ -3274,11 +3274,11 @@ static int update_ref_write(const char *action, const char *refname,
  * value or to zero to ensure the ref does not exist before update.
  */
 struct ref_update {
-	const char *refname;
 	unsigned char new_sha1[20];
 	unsigned char old_sha1[20];
 	int flags; /* REF_NODEREF? */
 	int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
+	const char refname[FLEX_ARRAY];
 };
 
 /*
@@ -3301,12 +3301,8 @@ void free_ref_transaction(struct ref_transaction *transaction)
 {
 	int i;
 
-	for (i = 0; i < transaction->nr; i++) {
-		struct ref_update *update = transaction->updates[i];
-
-		free((char *)update->refname);
-		free(update);
-	}
+	for (i = 0; i < transaction->nr; i++)
+		free(transaction->updates[i]);
 
 	free(transaction->updates);
 	free(transaction);
@@ -3315,9 +3311,10 @@ void free_ref_transaction(struct ref_transaction *transaction)
 static struct ref_update *add_update(struct ref_transaction *transaction,
 				     const char *refname)
 {
-	struct ref_update *update = xcalloc(1, sizeof(*update));
+	size_t len = strlen(refname);
+	struct ref_update *update = xcalloc(1, sizeof(*update) + len + 1);
 
-	update->refname = xstrdup(refname);
+	strcpy((char *)update->refname, refname);
 	ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
 	transaction->updates[transaction->nr++] = update;
 	return update;
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 22/26] commit_ref_transaction(): Introduce temporary variables
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (20 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 21/26] struct ref_update: Store refname as a FLEX_ARRAY Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 23/26] struct ref_update: Add a lock member Michael Haggerty
                   ` (4 subsequent siblings)
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

Use temporary variables in the for-loop blocks to simplify expressions
in the rest of the loop.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/refs.c b/refs.c
index 335d0e2..ec638e9 100644
--- a/refs.c
+++ b/refs.c
@@ -3424,10 +3424,12 @@ int commit_ref_transaction(struct ref_transaction *transaction,
 
 	/* Acquire all locks while verifying old values */
 	for (i = 0; i < n; i++) {
-		locks[i] = update_ref_lock(updates[i]->refname,
-					   (updates[i]->have_old ?
-					    updates[i]->old_sha1 : NULL),
-					   updates[i]->flags,
+		struct ref_update *update = updates[i];
+
+		locks[i] = update_ref_lock(update->refname,
+					   (update->have_old ?
+					    update->old_sha1 : NULL),
+					   update->flags,
 					   &types[i], onerr);
 		if (!locks[i]) {
 			ret = 1;
@@ -3436,23 +3438,28 @@ int commit_ref_transaction(struct ref_transaction *transaction,
 	}
 
 	/* Perform updates first so live commits remain referenced */
-	for (i = 0; i < n; i++)
-		if (!is_null_sha1(updates[i]->new_sha1)) {
+	for (i = 0; i < n; i++) {
+		struct ref_update *update = updates[i];
+
+		if (!is_null_sha1(update->new_sha1)) {
 			ret = update_ref_write(msg,
-					       updates[i]->refname,
-					       updates[i]->new_sha1,
+					       update->refname,
+					       update->new_sha1,
 					       locks[i], onerr);
 			locks[i] = NULL; /* freed by update_ref_write */
 			if (ret)
 				goto cleanup;
 		}
+	}
 
 	/* Perform deletes now that updates are safely completed */
-	for (i = 0; i < n; i++)
+	for (i = 0; i < n; i++) {
 		if (locks[i]) {
 			delnames[delnum++] = locks[i]->ref_name;
 			ret |= delete_ref_loose(locks[i], types[i]);
 		}
+	}
+
 	ret |= repack_without_refs(delnames, delnum);
 	for (i = 0; i < delnum; i++)
 		unlink_or_warn(git_path("logs/%s", delnames[i]));
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 23/26] struct ref_update: Add a lock member
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (21 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 22/26] commit_ref_transaction(): Introduce temporary variables Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 24/26] struct ref_update: Add type field Michael Haggerty
                   ` (3 subsequent siblings)
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

Now that we manage ref_update objects internally, we can use them to
hold some of the scratch space we need when actually carrying out the
updates.  Store the (struct ref_lock *) there.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c | 32 ++++++++++++++++----------------
 1 file changed, 16 insertions(+), 16 deletions(-)

diff --git a/refs.c b/refs.c
index ec638e9..73aec88 100644
--- a/refs.c
+++ b/refs.c
@@ -3278,6 +3278,7 @@ struct ref_update {
 	unsigned char old_sha1[20];
 	int flags; /* REF_NODEREF? */
 	int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
+	struct ref_lock *lock;
 	const char refname[FLEX_ARRAY];
 };
 
@@ -3402,7 +3403,6 @@ int commit_ref_transaction(struct ref_transaction *transaction,
 	int ret = 0, delnum = 0, i;
 	struct ref_update **updates;
 	int *types;
-	struct ref_lock **locks;
 	const char **delnames;
 	int n = transaction->nr;
 
@@ -3412,7 +3412,6 @@ int commit_ref_transaction(struct ref_transaction *transaction,
 	/* Allocate work space */
 	updates = xmalloc(sizeof(*updates) * n);
 	types = xmalloc(sizeof(*types) * n);
-	locks = xcalloc(n, sizeof(*locks));
 	delnames = xmalloc(sizeof(*delnames) * n);
 
 	/* Copy, sort, and reject duplicate refs */
@@ -3426,12 +3425,12 @@ int commit_ref_transaction(struct ref_transaction *transaction,
 	for (i = 0; i < n; i++) {
 		struct ref_update *update = updates[i];
 
-		locks[i] = update_ref_lock(update->refname,
-					   (update->have_old ?
-					    update->old_sha1 : NULL),
-					   update->flags,
-					   &types[i], onerr);
-		if (!locks[i]) {
+		update->lock = update_ref_lock(update->refname,
+					       (update->have_old ?
+						update->old_sha1 : NULL),
+					       update->flags,
+					       &types[i], onerr);
+		if (!update->lock) {
 			ret = 1;
 			goto cleanup;
 		}
@@ -3445,8 +3444,8 @@ int commit_ref_transaction(struct ref_transaction *transaction,
 			ret = update_ref_write(msg,
 					       update->refname,
 					       update->new_sha1,
-					       locks[i], onerr);
-			locks[i] = NULL; /* freed by update_ref_write */
+					       update->lock, onerr);
+			update->lock = NULL; /* freed by update_ref_write */
 			if (ret)
 				goto cleanup;
 		}
@@ -3454,9 +3453,11 @@ int commit_ref_transaction(struct ref_transaction *transaction,
 
 	/* Perform deletes now that updates are safely completed */
 	for (i = 0; i < n; i++) {
-		if (locks[i]) {
-			delnames[delnum++] = locks[i]->ref_name;
-			ret |= delete_ref_loose(locks[i], types[i]);
+		struct ref_update *update = updates[i];
+
+		if (update->lock) {
+			delnames[delnum++] = update->lock->ref_name;
+			ret |= delete_ref_loose(update->lock, types[i]);
 		}
 	}
 
@@ -3467,11 +3468,10 @@ int commit_ref_transaction(struct ref_transaction *transaction,
 
 cleanup:
 	for (i = 0; i < n; i++)
-		if (locks[i])
-			unlock_ref(locks[i]);
+		if (updates[i]->lock)
+			unlock_ref(updates[i]->lock);
 	free(updates);
 	free(types);
-	free(locks);
 	free(delnames);
 	return ret;
 }
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 24/26] struct ref_update: Add type field
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (22 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 23/26] struct ref_update: Add a lock member Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 25/26] commit_ref_transaction(): Also free the ref_transaction Michael Haggerty
                   ` (2 subsequent siblings)
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

This is temporary space for commit_ref_transaction()

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/refs.c b/refs.c
index 73aec88..1fd38b0 100644
--- a/refs.c
+++ b/refs.c
@@ -3279,6 +3279,7 @@ struct ref_update {
 	int flags; /* REF_NODEREF? */
 	int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
 	struct ref_lock *lock;
+	int type;
 	const char refname[FLEX_ARRAY];
 };
 
@@ -3402,7 +3403,6 @@ int commit_ref_transaction(struct ref_transaction *transaction,
 {
 	int ret = 0, delnum = 0, i;
 	struct ref_update **updates;
-	int *types;
 	const char **delnames;
 	int n = transaction->nr;
 
@@ -3411,7 +3411,6 @@ int commit_ref_transaction(struct ref_transaction *transaction,
 
 	/* Allocate work space */
 	updates = xmalloc(sizeof(*updates) * n);
-	types = xmalloc(sizeof(*types) * n);
 	delnames = xmalloc(sizeof(*delnames) * n);
 
 	/* Copy, sort, and reject duplicate refs */
@@ -3429,7 +3428,7 @@ int commit_ref_transaction(struct ref_transaction *transaction,
 					       (update->have_old ?
 						update->old_sha1 : NULL),
 					       update->flags,
-					       &types[i], onerr);
+					       &update->type, onerr);
 		if (!update->lock) {
 			ret = 1;
 			goto cleanup;
@@ -3457,7 +3456,7 @@ int commit_ref_transaction(struct ref_transaction *transaction,
 
 		if (update->lock) {
 			delnames[delnum++] = update->lock->ref_name;
-			ret |= delete_ref_loose(update->lock, types[i]);
+			ret |= delete_ref_loose(update->lock, update->type);
 		}
 	}
 
@@ -3471,7 +3470,6 @@ cleanup:
 		if (updates[i]->lock)
 			unlock_ref(updates[i]->lock);
 	free(updates);
-	free(types);
 	free(delnames);
 	return ret;
 }
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 25/26] commit_ref_transaction(): Also free the ref_transaction
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (23 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 24/26] struct ref_update: Add type field Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 12:46 ` [PATCH 26/26] commit_ref_transaction(): Work with transaction->updates in place Michael Haggerty
  2014-03-10 17:44 ` [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Brad King
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

Change commit_ref_transaction() to also free the associated data, to
absolve the caller from having to do it.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 builtin/update-ref.c |  1 -
 refs.c               |  1 +
 refs.h               | 11 ++++++-----
 3 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/builtin/update-ref.c b/builtin/update-ref.c
index ffed061..b33288c 100644
--- a/builtin/update-ref.c
+++ b/builtin/update-ref.c
@@ -346,7 +346,6 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
 		update_refs_stdin();
 		ret = commit_ref_transaction(transaction, msg,
 					     UPDATE_REFS_DIE_ON_ERR);
-		free_ref_transaction(transaction);
 		return ret;
 	}
 
diff --git a/refs.c b/refs.c
index 1fd38b0..d83fc7b 100644
--- a/refs.c
+++ b/refs.c
@@ -3471,6 +3471,7 @@ cleanup:
 			unlock_ref(updates[i]->lock);
 	free(updates);
 	free(delnames);
+	free_ref_transaction(transaction);
 	return ret;
 }
 
diff --git a/refs.h b/refs.h
index cc24213..754894b 100644
--- a/refs.h
+++ b/refs.h
@@ -210,14 +210,15 @@ enum action_on_err {
 
 /*
  * Allocate and initialize a ref_transaction object.  The object must
- * be freed by calling free_ref_transaction().
+ * be freed by calling commit_ref_transaction() or
+ * free_ref_transaction().
  */
 struct ref_transaction *create_ref_transaction(void);
 
 /*
- * Free a ref_transaction and all associated data.  This function does
- * not commit the transaction; that must be done first (if desired) by
- * calling commit_ref_transaction().
+ * Free a ref_transaction and all associated data.  This function
+ * should be called to free a ref_transaction that will not be
+ * committed.
  */
 void free_ref_transaction(struct ref_transaction *transaction);
 
@@ -264,7 +265,7 @@ void queue_delete_ref(struct ref_transaction *transaction, const char *refname,
 /*
  * Commit all of the changes that have been queued in transaction, as
  * atomically as possible.  Return a nonzero value if there is a
- * problem.  The transaction is unmodified by this function.
+ * problem.  The transaction is freed by this function.
  */
 int commit_ref_transaction(struct ref_transaction *transaction,
 			   const char *msg, enum action_on_err onerr);
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* [PATCH 26/26] commit_ref_transaction(): Work with transaction->updates in place
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (24 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 25/26] commit_ref_transaction(): Also free the ref_transaction Michael Haggerty
@ 2014-03-10 12:46 ` Michael Haggerty
  2014-03-10 17:44 ` [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Brad King
  26 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 12:46 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git,
	Michael Haggerty

Now that we free the transaction when we are done, there is no need to
make a copy of transaction->updates before working with it.

Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 refs.c | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/refs.c b/refs.c
index d83fc7b..ea33adc 100644
--- a/refs.c
+++ b/refs.c
@@ -3402,19 +3402,17 @@ int commit_ref_transaction(struct ref_transaction *transaction,
 			   const char *msg, enum action_on_err onerr)
 {
 	int ret = 0, delnum = 0, i;
-	struct ref_update **updates;
 	const char **delnames;
 	int n = transaction->nr;
+	struct ref_update **updates = transaction->updates;
 
 	if (!n)
 		return 0;
 
 	/* Allocate work space */
-	updates = xmalloc(sizeof(*updates) * n);
 	delnames = xmalloc(sizeof(*delnames) * n);
 
 	/* Copy, sort, and reject duplicate refs */
-	memcpy(updates, transaction->updates, sizeof(*updates) * n);
 	qsort(updates, n, sizeof(*updates), ref_update_compare);
 	ret = ref_update_reject_duplicates(updates, n, onerr);
 	if (ret)
@@ -3469,7 +3467,6 @@ cleanup:
 	for (i = 0; i < n; i++)
 		if (updates[i]->lock)
 			unlock_ref(updates[i]->lock);
-	free(updates);
 	free(delnames);
 	free_ref_transaction(transaction);
 	return ret;
-- 
1.9.0

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* Re: [PATCH 05/26] t1400: Add some more tests involving quoted arguments
  2014-03-10 12:46 ` [PATCH 05/26] t1400: Add some more tests involving quoted arguments Michael Haggerty
@ 2014-03-10 13:53   ` Johan Herland
  0 siblings, 0 replies; 38+ messages in thread
From: Johan Herland @ 2014-03-10 13:53 UTC (permalink / raw)
  To: Michael Haggerty
  Cc: Junio C Hamano, Brad King, Jeff King, Vicent Marti,
	Git mailing list

On Mon, Mar 10, 2014 at 1:46 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:
> Previously there were no good tests of C-quoted arguments.
>
> Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>

FWIW, the first 5 patches seem trivially correct to me. Feel free to add:

Reviewed-by: Johan Herland <johan@herland.net>

-- 
Johan Herland, <johan@herland.net>
www.herland.net

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 03/26] t1400: Pass a legitimate <newvalue> to update command
  2014-03-10 12:46 ` [PATCH 03/26] t1400: Pass a legitimate <newvalue> to update command Michael Haggerty
@ 2014-03-10 17:03   ` Brad King
  2014-03-10 21:38     ` Michael Haggerty
  0 siblings, 1 reply; 38+ messages in thread
From: Brad King @ 2014-03-10 17:03 UTC (permalink / raw)
  To: Michael Haggerty
  Cc: Junio C Hamano, Jeff King, Vicent Marti, Johan Herland, git

On 03/10/2014 08:46 AM, Michael Haggerty wrote:
> This test is trying to test a few ways to delete references using "git
> update-ref -z --stdin".  The third line passed in is
> 
>     update SP /refs/heads/c NUL NUL <sha1> NUL
> 
> , which is not a correct way to delete a reference according to the
> documentation (the new value should be zeros, not empty).  Pass zeros
> instead as the new value to test the code correctly.

In my original work on this feature, an empty <newvalue> is allowed.
Since newvalue is not optional an empty value can be treated as zero.
The relevant documentation is:

 update::
         Set <ref> to <newvalue> after verifying <oldvalue>, if given.
         Specify a zero <newvalue> to ensure the ref does not exist

 ...

 Use 40 "0" or the empty string to specify a zero value, except that
 with `-z` an empty <oldvalue> is considered missing.

The two together say that <newvalue> can be the empty string instead
of a literal zero.

-Brad

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 13/26] update-ref --stdin: Simplify error messages for missing oldvalues
  2014-03-10 12:46 ` [PATCH 13/26] update-ref --stdin: Simplify error messages for missing oldvalues Michael Haggerty
@ 2014-03-10 17:08   ` Brad King
  2014-03-10 17:12     ` Brad King
  0 siblings, 1 reply; 38+ messages in thread
From: Brad King @ 2014-03-10 17:08 UTC (permalink / raw)
  To: Michael Haggerty
  Cc: Junio C Hamano, Jeff King, Vicent Marti, Johan Herland, git

On 03/10/2014 08:46 AM, Michael Haggerty wrote:
> Instead of, for example,
> 
>     fatal: update refs/heads/master missing [<oldvalue>] NUL
> 
> emit
> 
>     fatal: update refs/heads/master missing <oldvalue>
[snip]
> -		die("update %s missing [<oldvalue>] NUL", update->ref_name);
> +		die("update %s missing <oldvalue>", update->ref_name);

The reason for the original wording is that the <oldvalue> is indeed
optional.  This can only occur at end-of-input, and it is actually the
*NUL* that is missing because an empty old value can be specified to
mean that it it intentionally missing.

-Brad

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 13/26] update-ref --stdin: Simplify error messages for missing oldvalues
  2014-03-10 17:08   ` Brad King
@ 2014-03-10 17:12     ` Brad King
  0 siblings, 0 replies; 38+ messages in thread
From: Brad King @ 2014-03-10 17:12 UTC (permalink / raw)
  To: Michael Haggerty
  Cc: Junio C Hamano, Jeff King, Vicent Marti, Johan Herland, git

On 03/10/2014 01:08 PM, Brad King wrote:
>> -		die("update %s missing [<oldvalue>] NUL", update->ref_name);
>> +		die("update %s missing <oldvalue>", update->ref_name);
> 
> The reason for the original wording is that the <oldvalue> is indeed
> optional.  This can only occur at end-of-input, and it is actually the
> *NUL* that is missing because an empty old value can be specified to
> mean that it it intentionally missing.

I see a following patch makes the wording even clearer about
unexpected end of input, so ignore my previous review.

-Brad

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction
  2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
                   ` (25 preceding siblings ...)
  2014-03-10 12:46 ` [PATCH 26/26] commit_ref_transaction(): Work with transaction->updates in place Michael Haggerty
@ 2014-03-10 17:44 ` Brad King
  2014-03-10 21:46   ` Michael Haggerty
  26 siblings, 1 reply; 38+ messages in thread
From: Brad King @ 2014-03-10 17:44 UTC (permalink / raw)
  To: Michael Haggerty
  Cc: Junio C Hamano, Jeff King, Vicent Marti, Johan Herland, git

Hi Michael,

This is excellent work.

I haven't reviewed every line of logic in detail but the changes
look correct at a high level.  The only exception is that the empty
<newvalue> is supposed to be accepted and treated as zero even in
"--stdin -z" mode.  See my response to that individual change.

On 03/10/2014 08:46 AM, Michael Haggerty wrote:
> The new API for dealing with reference transactions is
> 
>     ref_transaction *transaction = create_ref_transaction();
>     queue_create_ref(transaction, refname, new_sha1, ...);
>     queue_update_ref(transaction, refname, new_sha1, old_sha1, ...);
>     queue_delete_ref(transaction, refname, old_sha1, ...);
>     ...
>     if (commit_ref_transaction(transaction, msg, ...))
>         die(...);

The layout of this API looks good.

The name "queue" is not fully representative of the current behavior.
It implies that the order is meaningful but we currently allow at most
one update to a ref and sort them by refname.  Does your follow-up work
define behavior for multiple updates to one ref?  Can it collapse them
into a single update after checking internal consistency of the sequence?

> So most of the commits in this series are actually cleanups in
> builtin/update-ref.c.  I also spend some time making the error
> messages emitted by that command more uniform.

All good cleanups, thanks.

> Finally, now that refs.c owns the data structures for dealing with
> transactions, it is possible to make a few simplifications.

Yes, it is much nicer to keep the data structures private, especially
as it avoids the copy of the transaction made before sorting.

Thanks,
-Brad

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 03/26] t1400: Pass a legitimate <newvalue> to update command
  2014-03-10 17:03   ` Brad King
@ 2014-03-10 21:38     ` Michael Haggerty
  2014-03-11 12:49       ` Brad King
  2014-03-11 20:06       ` Junio C Hamano
  0 siblings, 2 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 21:38 UTC (permalink / raw)
  To: Brad King; +Cc: Junio C Hamano, Jeff King, Vicent Marti, Johan Herland, git

Brad,

Thanks for your feedback.

On 03/10/2014 06:03 PM, Brad King wrote:
> On 03/10/2014 08:46 AM, Michael Haggerty wrote:
>> This test is trying to test a few ways to delete references using "git
>> update-ref -z --stdin".  The third line passed in is
>>
>>     update SP /refs/heads/c NUL NUL <sha1> NUL
>>
>> , which is not a correct way to delete a reference according to the
>> documentation (the new value should be zeros, not empty).  Pass zeros
>> instead as the new value to test the code correctly.
> 
> In my original work on this feature, an empty <newvalue> is allowed.
> Since newvalue is not optional an empty value can be treated as zero.
> The relevant documentation is:
> 
>  update::
>          Set <ref> to <newvalue> after verifying <oldvalue>, if given.
>          Specify a zero <newvalue> to ensure the ref does not exist
> 
>  ...
> 
>  Use 40 "0" or the empty string to specify a zero value, except that
>  with `-z` an empty <oldvalue> is considered missing.
> 
> The two together say that <newvalue> can be the empty string instead
> of a literal zero.

OK, with your explanation and after reading the docs a couple more
times, I can see your reading.  Your rules as I now understand them:

* Without -z
  * 0{40} or the empty string represents zeros
  * No preceding SP delimiter indicates that the value is missing
    (as are any following values)

* With -z
  * For <newvalue>
    * 0{40} or the empty string represents zeros (the value is
      not allowed to be missing)
  * For <oldvalue>
    * 0{40} represents zeros
    * The empty string indicates that the value is missing

I implemented the slightly simpler rules

* Without -z
  * 0{40} or the empty string represents zeros
  * No preceding delimiter indicates that the value is missing (as
    are any following values)

* With -z
  * 0{40} represents zeros
  * The empty string indicates that the value is missing

It seems to me that "-z" input will nearly always be machine-generated,
so there is not much reason to accept the empty string as shorthand for
zeros.  So I think that my version of the rules, being simpler to
explain, is a slight improvement.  But your version is already out in
the wild, so backwards-compatibility is also a consideration, even
though it is rather a fine point in a rather unlikely usage (why use
update rather than delete to delete a reference?).

I don't know.  I'm willing to rewrite the code to go back to your rules,
or rewrite the documentation to describe my rules.

Neutral bystanders *cough*Junio*cough*, what do you prefer?

Michael

-- 
Michael Haggerty
mhagger@alum.mit.edu
http://softwareswirl.blogspot.com/

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction
  2014-03-10 17:44 ` [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Brad King
@ 2014-03-10 21:46   ` Michael Haggerty
  0 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-10 21:46 UTC (permalink / raw)
  To: Brad King; +Cc: Junio C Hamano, Jeff King, Vicent Marti, Johan Herland, git

On 03/10/2014 06:44 PM, Brad King wrote:
> [...]

Thanks for your kind words.

> On 03/10/2014 08:46 AM, Michael Haggerty wrote:
>> The new API for dealing with reference transactions is
>>
>>     ref_transaction *transaction = create_ref_transaction();
>>     queue_create_ref(transaction, refname, new_sha1, ...);
>>     queue_update_ref(transaction, refname, new_sha1, old_sha1, ...);
>>     queue_delete_ref(transaction, refname, old_sha1, ...);
>>     ...
>>     if (commit_ref_transaction(transaction, msg, ...))
>>         die(...);
> 
> The layout of this API looks good.
> 
> The name "queue" is not fully representative of the current behavior.
> It implies that the order is meaningful but we currently allow at most
> one update to a ref and sort them by refname.  Does your follow-up work
> define behavior for multiple updates to one ref?  Can it collapse them
> into a single update after checking internal consistency of the sequence?

I don't really like the word "queue" in these function names, but I
couldn't think of a better alternative.  I wanted a word that conveys
that the change is being collected for later application as opposed to
being applied immediately.  Other suggestions are welcome.

In the future I *do* want to define the behavior for multiple updates to
a single ref.  Even now, although order is not preserved when carrying
out the updates, the facts that (1) at most one update is allowed to
each reference and (2) the changes are made (approximately) atomically
together mean that the effect of committing a transaction is
(approximately) indistinguishable from the effect it would have if order
were preserved.

> [...]

Cheers,
Michael

-- 
Michael Haggerty
mhagger@alum.mit.edu
http://softwareswirl.blogspot.com/

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 03/26] t1400: Pass a legitimate <newvalue> to update command
  2014-03-10 21:38     ` Michael Haggerty
@ 2014-03-11 12:49       ` Brad King
  2014-03-11 20:06       ` Junio C Hamano
  1 sibling, 0 replies; 38+ messages in thread
From: Brad King @ 2014-03-11 12:49 UTC (permalink / raw)
  To: Michael Haggerty
  Cc: Junio C Hamano, Jeff King, Vicent Marti, Johan Herland, git

On 03/10/2014 05:38 PM, Michael Haggerty wrote:
> It seems to me that "-z" input will nearly always be machine-generated,
> so there is not much reason to accept the empty string as shorthand for
> zeros.  So I think that my version of the rules, being simpler to
> explain, is a slight improvement.

I agree.

> But your version is already out in the wild, so backwards-compatibility
> is also a consideration, even though it is rather a fine point in a
> rather unlikely usage (why use update rather than delete to delete a
> reference?).

I'm not using empty==zero with -z in any deployment.  Since the feature
is quite new, the behavior change is not silent, and it is easy to
construct input that works with both versions, I do not think we need
to worry about compatibility.

> or rewrite the documentation to describe my rules.

I prefer this approach.

Thanks,
-Brad

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 03/26] t1400: Pass a legitimate <newvalue> to update command
  2014-03-10 21:38     ` Michael Haggerty
  2014-03-11 12:49       ` Brad King
@ 2014-03-11 20:06       ` Junio C Hamano
  2014-03-11 21:41         ` Brad King
  1 sibling, 1 reply; 38+ messages in thread
From: Junio C Hamano @ 2014-03-11 20:06 UTC (permalink / raw)
  To: Michael Haggerty; +Cc: Brad King, Jeff King, Vicent Marti, Johan Herland, git

Michael Haggerty <mhagger@alum.mit.edu> writes:

> It seems to me that "-z" input will nearly always be machine-generated,
> so there is not much reason to accept the empty string as shorthand for
> zeros.  So I think that my version of the rules, being simpler to
> explain, is a slight improvement.  But your version is already out in
> the wild, so backwards-compatibility is also a consideration, even
> though it is rather a fine point in a rather unlikely usage (why use
> update rather than delete to delete a reference?).
>
> I don't know.  I'm willing to rewrite the code to go back to your rules,
> or rewrite the documentation to describe my rules.
>
> Neutral bystanders *cough*Junio*cough*, what do you prefer?

I may be misremembering things, but your first sentence quoted above
was exactly my reaction while reviewing the original change, and I
might have even raised that as an issue myself, saying something
like "consistency across values is more important than type-saving
in a machine format".

Since nobody else were raising the issue back then, however, we are
stuck with the interface.  I am not against deprecating and removing
the support for it in the longer term, though.

^ permalink raw reply	[flat|nested] 38+ messages in thread

* Re: [PATCH 03/26] t1400: Pass a legitimate <newvalue> to update command
  2014-03-11 20:06       ` Junio C Hamano
@ 2014-03-11 21:41         ` Brad King
  2014-03-20 17:01           ` Michael Haggerty
  0 siblings, 1 reply; 38+ messages in thread
From: Brad King @ 2014-03-11 21:41 UTC (permalink / raw)
  To: Michael Haggerty
  Cc: Junio C Hamano, Jeff King, Vicent Marti, Johan Herland, git

On Tue, Mar 11, 2014 at 4:06 PM, Junio C Hamano <gitster@pobox.com> wrote:
> I may be misremembering things, but your first sentence quoted above
> was exactly my reaction while reviewing the original change, and I
> might have even raised that as an issue myself, saying something
> like "consistency across values is more important than type-saving
> in a machine format".

For reference, the original design discussion of the format was here:

 http://thread.gmane.org/gmane.comp.version-control.git/233842

I do not recall this issue being raised before, but now that it has
been raised I fully agree:

 http://thread.gmane.org/gmane.comp.version-control.git/243754/focus=243862

In -z mode an empty <newvalue> should be treated as missing just as
it is for <oldvalue>.  This is obvious now in hindsight and I wish I
had realized this at the time.  Back then I went through a lot of
iterations on the format and missed this simplification in the final
version :(

Moving forward:

The "create" command rejects a zero <newvalue> so the change in
question for that command is merely the wording of the error message
and there is no compatibility issue.

The "update" command supports a zero <newvalue> so that it can
be used for all operations (create, update, delete, verify) with
the proper combination of old and new values.  The change in question
makes an empty <newvalue> an error where it was previously treated
as zero.  (BTW, Michael, I do not see a test case for the new error
in your series.  Something like the patch below should work.)

> I am not against deprecating and removing
> the support for it in the longer term, though.

As I reported in my above-linked response, I'm not depending on
the old behavior myself.  Also if one were to start seeing this
error then generated input needs only trivial changes to avoid it.
If we do want to preserve compatibility for others then perhaps an
empty <newvalue> with -z should produce:

 warning: update $ref: missing <newvalue>, treating as zero

Then after a few releases it can be switched to an error.

Thanks,
-Brad


diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh
index 3cc5c66..1e9fe7c 100755
--- a/t/t1400-update-ref.sh
+++ b/t/t1400-update-ref.sh
@@ -730,6 +730,12 @@ test_expect_success 'stdin -z fails update with bad ref name' '
 	grep "fatal: invalid ref format: ~a" err
 '

+test_expect_success 'stdin -z fails update with empty new value' '
+	printf $F "update $a" "" >stdin &&
+	test_must_fail git update-ref -z --stdin <stdin 2>err &&
+	grep "fatal: update $a: missing <newvalue>" err
+'
+
 test_expect_success 'stdin -z fails update with no new value' '
 	printf $F "update $a" >stdin &&
 	test_must_fail git update-ref -z --stdin <stdin 2>err &&
-- 
1.8.5.2

^ permalink raw reply related	[flat|nested] 38+ messages in thread

* Re: [PATCH 03/26] t1400: Pass a legitimate <newvalue> to update command
  2014-03-11 21:41         ` Brad King
@ 2014-03-20 17:01           ` Michael Haggerty
  0 siblings, 0 replies; 38+ messages in thread
From: Michael Haggerty @ 2014-03-20 17:01 UTC (permalink / raw)
  To: Brad King; +Cc: Junio C Hamano, Jeff King, Vicent Marti, Johan Herland, git

On 03/11/2014 10:41 PM, Brad King wrote:
> On Tue, Mar 11, 2014 at 4:06 PM, Junio C Hamano <gitster@pobox.com> wrote:
>> I may be misremembering things, but your first sentence quoted above
>> was exactly my reaction while reviewing the original change, and I
>> might have even raised that as an issue myself, saying something
>> like "consistency across values is more important than type-saving
>> in a machine format".
> 
> For reference, the original design discussion of the format was here:
> 
>  http://thread.gmane.org/gmane.comp.version-control.git/233842
> 
> I do not recall this issue being raised before, but now that it has
> been raised I fully agree:
> 
>  http://thread.gmane.org/gmane.comp.version-control.git/243754/focus=243862
> 
> In -z mode an empty <newvalue> should be treated as missing just as
> it is for <oldvalue>.  This is obvious now in hindsight and I wish I
> had realized this at the time.  Back then I went through a lot of
> iterations on the format and missed this simplification in the final
> version :(

It's not your fault; anybody could have reviewed your code at the time
(I most of all, because I have been so active in this area of the code).

> Moving forward:
> 
> The "create" command rejects a zero <newvalue> so the change in
> question for that command is merely the wording of the error message
> and there is no compatibility issue.
> 
> The "update" command supports a zero <newvalue> so that it can
> be used for all operations (create, update, delete, verify) with
> the proper combination of old and new values.  The change in question
> makes an empty <newvalue> an error where it was previously treated
> as zero.  (BTW, Michael, I do not see a test case for the new error
> in your series.  Something like the patch below should work.)
> 
>> I am not against deprecating and removing
>> the support for it in the longer term, though.
> 
> As I reported in my above-linked response, I'm not depending on
> the old behavior myself.  Also if one were to start seeing this
> error then generated input needs only trivial changes to avoid it.
> If we do want to preserve compatibility for others then perhaps an
> empty <newvalue> with -z should produce:
> 
>  warning: update $ref: missing <newvalue>, treating as zero

This last suggestion is what I am implementing for the re-roll (coming
shortly).  Thanks for the discussion.

Michael

-- 
Michael Haggerty
mhagger@alum.mit.edu
http://softwareswirl.blogspot.com/

^ permalink raw reply	[flat|nested] 38+ messages in thread

end of thread, other threads:[~2014-03-20 17:01 UTC | newest]

Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-03-10 12:46 [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Michael Haggerty
2014-03-10 12:46 ` [PATCH 01/26] t1400: Fix name and expected result of one test Michael Haggerty
2014-03-10 12:46 ` [PATCH 02/26] t1400: Provide sensible input to the command Michael Haggerty
2014-03-10 12:46 ` [PATCH 03/26] t1400: Pass a legitimate <newvalue> to update command Michael Haggerty
2014-03-10 17:03   ` Brad King
2014-03-10 21:38     ` Michael Haggerty
2014-03-11 12:49       ` Brad King
2014-03-11 20:06       ` Junio C Hamano
2014-03-11 21:41         ` Brad King
2014-03-20 17:01           ` Michael Haggerty
2014-03-10 12:46 ` [PATCH 04/26] parse_arg(): Really test that argument is properly terminated Michael Haggerty
2014-03-10 12:46 ` [PATCH 05/26] t1400: Add some more tests involving quoted arguments Michael Haggerty
2014-03-10 13:53   ` Johan Herland
2014-03-10 12:46 ` [PATCH 06/26] refs.h: Rename the action_on_err constants Michael Haggerty
2014-03-10 12:46 ` [PATCH 07/26] update_refs(): Fix constness Michael Haggerty
2014-03-10 12:46 ` [PATCH 08/26] update-ref --stdin: Read the whole input at once Michael Haggerty
2014-03-10 12:46 ` [PATCH 09/26] parse_cmd_verify(): Copy old_sha1 instead of evaluating <oldvalue> twice Michael Haggerty
2014-03-10 12:46 ` [PATCH 10/26] update-ref.c: Extract a new function, parse_refname() Michael Haggerty
2014-03-10 12:46 ` [PATCH 11/26] update-ref --stdin: Improve error messages for invalid values Michael Haggerty
2014-03-10 12:46 ` [PATCH 12/26] update-ref --stdin: Make error messages more consistent Michael Haggerty
2014-03-10 12:46 ` [PATCH 13/26] update-ref --stdin: Simplify error messages for missing oldvalues Michael Haggerty
2014-03-10 17:08   ` Brad King
2014-03-10 17:12     ` Brad King
2014-03-10 12:46 ` [PATCH 14/26] update-ref.c: Extract a new function, parse_next_sha1() Michael Haggerty
2014-03-10 12:46 ` [PATCH 15/26] update-ref --stdin: Improve the error message for unexpected EOF Michael Haggerty
2014-03-10 12:46 ` [PATCH 16/26] update-ref --stdin: Harmonize error messages Michael Haggerty
2014-03-10 12:46 ` [PATCH 17/26] refs: Add a concept of a reference transaction Michael Haggerty
2014-03-10 12:46 ` [PATCH 18/26] update-ref --stdin: Reimplement using reference transactions Michael Haggerty
2014-03-10 12:46 ` [PATCH 19/26] refs: Remove API function update_refs() Michael Haggerty
2014-03-10 12:46 ` [PATCH 20/26] struct ref_update: Rename field "ref_name" to "refname" Michael Haggerty
2014-03-10 12:46 ` [PATCH 21/26] struct ref_update: Store refname as a FLEX_ARRAY Michael Haggerty
2014-03-10 12:46 ` [PATCH 22/26] commit_ref_transaction(): Introduce temporary variables Michael Haggerty
2014-03-10 12:46 ` [PATCH 23/26] struct ref_update: Add a lock member Michael Haggerty
2014-03-10 12:46 ` [PATCH 24/26] struct ref_update: Add type field Michael Haggerty
2014-03-10 12:46 ` [PATCH 25/26] commit_ref_transaction(): Also free the ref_transaction Michael Haggerty
2014-03-10 12:46 ` [PATCH 26/26] commit_ref_transaction(): Work with transaction->updates in place Michael Haggerty
2014-03-10 17:44 ` [PATCH 00/26] Clean up update-refs --stdin and implement ref_transaction Brad King
2014-03-10 21:46   ` Michael Haggerty

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).