Merge branch 'mh/ref-transaction'
authorJunio C Hamano <gitster@pobox.com>
Tue, 3 Jun 2014 19:06:40 +0000 (12:06 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 3 Jun 2014 19:06:41 +0000 (12:06 -0700)
Update "update-ref --stdin [-z]" and then introduce a transactional
support for (multi-)reference updates.

* mh/ref-transaction: (27 commits)
ref_transaction_commit(): work with transaction->updates in place
struct ref_update: add a type field
struct ref_update: add a lock field
ref_transaction_commit(): simplify code using temporary variables
struct ref_update: store refname as a FLEX_ARRAY
struct ref_update: rename field "ref_name" to "refname"
refs: remove API function update_refs()
update-ref --stdin: reimplement using reference transactions
refs: add a concept of a reference transaction
update-ref --stdin: harmonize error messages
update-ref --stdin: improve the error message for unexpected EOF
t1400: test one mistake at a time
update-ref --stdin -z: deprecate interpreting the empty string as zeros
update-ref.c: extract a new function, parse_next_sha1()
t1400: test that stdin -z update treats empty <newvalue> as zeros
update-ref --stdin: simplify error messages for missing oldvalues
update-ref --stdin: make error messages more consistent
update-ref --stdin: improve error messages for invalid values
update-ref.c: extract a new function, parse_refname()
parse_cmd_verify(): copy old_sha1 instead of evaluating <oldvalue> twice
...

1  2 
builtin/checkout.c
builtin/merge.c
builtin/notes.c
t/t1400-update-ref.sh
diff --combined builtin/checkout.c
index 07cf55530918e8de6ae45e1c9b7e16707c32f844,6bf23188c9e68dbfacb3f99bb8118602cb578086..ff4492162d9543598c1cdc8951f8604a4712516d
@@@ -624,7 -624,7 +624,7 @@@ static void update_refs_for_switch(cons
                /* 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);
@@@ -895,7 -895,7 +895,7 @@@ static int parse_branchname_arg(int arg
         *       between A and B, A...B names that merge base.
         *
         *   (b) If <something> is _not_ a commit, either "--" is present
 -       *       or <something> is not a path, no -t nor -b was given, and
 +       *       or <something> is not a path, no -t or -b was given, and
         *       and there is a tracking branch whose name is <something>
         *       in one and only one remote, then this is a short-hand to
         *       fork local <something> from that remote-tracking branch.
@@@ -1095,7 -1095,7 +1095,7 @@@ int cmd_checkout(int argc, const char *
                OPT_BOOL(0, "detach", &opts.force_detach, N_("detach the HEAD at named commit")),
                OPT_SET_INT('t', "track",  &opts.track, N_("set upstream info for new branch"),
                        BRANCH_TRACK_EXPLICIT),
 -              OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new branch"), N_("new unparented branch")),
 +              OPT_STRING(0, "orphan", &opts.new_orphan_branch, N_("new-branch"), N_("new unparented branch")),
                OPT_SET_INT('2', "ours", &opts.writeout_stage, N_("checkout our version for unmerged files"),
                            2),
                OPT_SET_INT('3', "theirs", &opts.writeout_stage, N_("checkout their version for unmerged files"),
diff --combined builtin/merge.c
index 66d8843301b22a6a89d9d765e9d3020942e6f672,7d1d83e8a509bc3840f36b935b099dc48c90e499..7c9d8f2432903eba41e457d4b451ab6b928703a0
@@@ -220,7 -220,7 +220,7 @@@ static struct option builtin_merge_opti
        OPT_BOOL(0, "abort", &abort_current_merge,
                N_("abort the current in-progress merge")),
        OPT_SET_INT(0, "progress", &show_progress, N_("force progress reporting"), 1),
 -      { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key id"),
 +      { OPTION_STRING, 'S', "gpg-sign", &sign_commit, N_("key-id"),
          N_("GPG sign commit"), PARSE_OPT_OPTARG, NULL, (intptr_t) "" },
        OPT_BOOL(0, "overwrite-ignore", &overwrite_ignore, N_("update ignored files (default)")),
        OPT_END()
@@@ -398,7 -398,7 +398,7 @@@ static void finish(struct commit *head_
                        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 +1222,7 @@@ int cmd_merge(int argc, const char **ar
                        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;
        }
  
        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 --combined builtin/notes.c
index 39c8573cde00bc15e673ae549fcd0f22ea67d29f,66147b673946dc8c589f9685846a8c72e02528ea..820c34135cab439e2168dd08947bd79d7ed7ae71
@@@ -717,7 -717,7 +717,7 @@@ static int merge_commit(struct notes_me
        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);
@@@ -812,11 -812,11 +812,11 @@@ static int merge(int argc, const char *
        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)",
@@@ -939,7 -939,7 +939,7 @@@ int cmd_notes(int argc, const char **ar
        int result;
        const char *override_notes_ref = NULL;
        struct option options[] = {
 -              OPT_STRING(0, "ref", &override_notes_ref, N_("notes_ref"),
 +              OPT_STRING(0, "ref", &override_notes_ref, N_("notes-ref"),
                           N_("use notes from <notes_ref>")),
                OPT_END()
        };
diff --combined t/t1400-update-ref.sh
index e130c528fe5a0115c99b1203cfe9dced046cece3,48ccc4d6355c1f1a1de2080fafc31eb38836a400..4e2459afc549d5d2985024e8c23bf5fa117d570a
@@@ -350,22 -350,28 +350,28 @@@ test_expect_success 'stdin fails on unk
        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 arguments not separated by space' '
+ 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 &&
-       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' '
        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' '
  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' '
  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' '
  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' '
@@@ -458,6 -464,24 +464,24 @@@ test_expect_success 'stdin create ref w
        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 &&
@@@ -494,21 -518,21 +518,21 @@@ test_expect_success 'stdin update ref f
  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 <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: invalid new value for ref $c: 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: zero <newvalue>" err &&
        test_must_fail git rev-parse --verify -q $c
  '
  
@@@ -532,7 -556,7 +556,7 @@@ test_expect_success 'stdin delete ref f
  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: zero <oldvalue>" err &&
        git rev-parse $m >expect &&
        git rev-parse $a >actual &&
        test_cmp expect actual
@@@ -673,7 -697,7 +697,7 @@@ test_expect_success 'stdin -z fails on 
  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' '
  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' '
  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 too few args' '
+       printf $F "update $a" "$m" >stdin &&
+       test_must_fail git update-ref -z --stdin <stdin 2>err &&
+       grep "fatal: update $a: unexpected end of input when reading <oldvalue>" err
  '
  
  test_expect_success 'stdin -z fails update with bad ref name' '
-       printf $F "update ~a" "$m" >stdin &&
+       printf $F "update ~a" "$m" "" >stdin &&
        test_must_fail git update-ref -z --stdin <stdin 2>err &&
        grep "fatal: invalid ref format: ~a" err
  '
  
+ test_expect_success 'stdin -z emits warning with empty new value' '
+       git update-ref $a $m &&
+       printf $F "update $a" "" "" >stdin &&
+       git update-ref -z --stdin <stdin 2>err &&
+       grep "warning: update $a: missing <newvalue>, treating as zero" err &&
+       test_must_fail git rev-parse --verify -q $a
+ '
  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>\\] NUL" err
+       grep "fatal: update $a: unexpected end of input when reading <oldvalue>" err
  '
  
  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' '
  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: unexpected end of input when reading <oldvalue>" err
  '
  
  test_expect_success 'stdin -z fails delete with too many arguments' '
@@@ -757,7 -795,7 +795,7 @@@ test_expect_success 'stdin -z fails ver
  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: unexpected end of input when reading <oldvalue>" err
  '
  
  test_expect_success 'stdin -z fails option with unknown name' '
@@@ -816,32 -854,21 +854,32 @@@ test_expect_success 'stdin -z update re
  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 <oldvalue>: does-not-exist" err &&
        test_must_fail git rev-parse --verify -q $c
  '
  
 +test_expect_success 'stdin -z create ref fails when ref exists' '
 +      git update-ref $c $m &&
 +      git rev-parse "$c" >expect &&
 +      printf $F "create $c" "$m~1" >stdin &&
 +      test_must_fail git update-ref -z --stdin <stdin 2>err &&
 +      grep "fatal: Cannot lock the ref '"'"'$c'"'"'" err &&
 +      git rev-parse "$c" >actual &&
 +      test_cmp expect actual
 +'
 +
  test_expect_success 'stdin -z create ref fails with bad new value' '
 +      git update-ref -d "$c" &&
        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 <newvalue>: does-not-exist" err &&
        test_must_fail git rev-parse --verify -q $c
  '
  
- test_expect_success 'stdin -z create ref fails with zero 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 given zero new value" err &&
+       grep "fatal: create $c: missing <newvalue>" err &&
        test_must_fail git rev-parse --verify -q $c
  '
  
@@@ -865,7 -892,7 +903,7 @@@ test_expect_success 'stdin -z delete re
  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: zero <oldvalue>" err &&
        git rev-parse $m >expect &&
        git rev-parse $a >actual &&
        test_cmp expect actual
@@@ -923,7 -950,7 +961,7 @@@ test_expect_success 'stdin -z update re
  
  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 &&