Allow multiple updates of a ref in the same transaction as long as each update has have_old and old_sha1 matches the new_sha1 of the previous update. Add a test that verifies that a valid sequence such as create ref a update ref b a update ref c b works and a test that an invalid sequence such as this still fails: update ref a c update ref b b update ref c c Signed-off-by: Ronnie Sahlberg <sahlberg@xxxxxxxxxx> --- refs.c | 23 +++++++++++++++++------ t/t1400-update-ref.sh | 23 +++++++++++++++++++++-- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/refs.c b/refs.c index 76cab6e..87cdd91 100644 --- a/refs.c +++ b/refs.c @@ -3455,12 +3455,6 @@ int transaction_update_sha1(struct ref_transaction *transaction, if (update->lock) return 0; - /* If we could not lock the ref it means we either collided with a - different command or that we tried to perform a second update to - the same ref from within the same transaction. - */ - transaction->status = REF_TRANSACTION_ERROR; - /* -1 is the update we just added. Start at -2 and find the most recent previous update for this ref. */ @@ -3472,6 +3466,23 @@ int transaction_update_sha1(struct ref_transaction *transaction, update->refname)) break; } + /* If the current update has_old==1 and old_sha1 matches the new_sha1 + * of the previous update then merge the two updates into one. + */ + if (i >= 0 && update->have_old && !hashcmp(update->old_sha1, + transaction->updates[i]->new_sha1)) { + hashcpy(transaction->updates[i]->new_sha1, update->new_sha1); + transaction->nr--; + free((char *)transaction->updates[transaction->nr]->msg); + free(transaction->updates[transaction->nr]); + return 0; + } + /* If we could not lock the ref it means we either collided with a + different command or that we tried to perform a second update to + the same ref from within the same transaction. + */ + transaction->status = REF_TRANSACTION_ERROR; + if (err) if (i >= 0) { const char *str = diff --git a/t/t1400-update-ref.sh b/t/t1400-update-ref.sh index f9b7bef..078cd4b 100755 --- a/t/t1400-update-ref.sh +++ b/t/t1400-update-ref.sh @@ -446,7 +446,7 @@ test_expect_success 'stdin fails option with unknown name' ' grep "fatal: option unknown: unknown" err ' -test_expect_success 'stdin fails with duplicate refs' ' +test_expect_success 'stdin fails with duplicate create refs' ' cat >stdin <<-EOF && create $a $m create $b $m @@ -464,6 +464,25 @@ test_expect_success 'stdin create ref works' ' test_cmp expect actual ' +test_expect_success 'stdin succeeds with correctly chained update refs' ' + cat >stdin <<-EOF && + update $a $A $m + update $a $B $A + update $a $C $B + EOF + git update-ref --stdin <stdin +' + +test_expect_success 'stdin fails with incorrectly chained update refs' ' + cat >stdin <<-EOF && + update $a $A $C + update $a $B $B + update $a $B $B + EOF + test_must_fail git update-ref --stdin <stdin && + grep "fatal: Multiple updates for ref '"'"'$a'"'"' not allowed." err +' + test_expect_success 'stdin succeeds with quoted argument' ' git update-ref -d $a && echo "create $a \"$m\"" >stdin && @@ -786,7 +805,7 @@ test_expect_success 'stdin -z fails option with unknown name' ' grep "fatal: option unknown: unknown" err ' -test_expect_success 'stdin -z fails with duplicate refs' ' +test_expect_success 'stdin -z fails with duplicate create refs' ' printf $F "create $a" "$m" "create $b" "$m" "create $a" "$m" >stdin && test_must_fail git update-ref -z --stdin <stdin 2>err && grep "fatal: Multiple updates for ref '"'"'$a'"'"' not allowed." err -- 2.0.0.rc3.506.g3739a35 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html