Skip to content

Commit 1bc550e

Browse files
committed
Merge branch 'ps/update-ref-multi-transaction'
"git update-ref --stdin" learns to take multiple transactions in a single session. * ps/update-ref-multi-transaction: update-ref: disallow "start" for ongoing transactions p1400: use `git-update-ref --stdin` to test multiple transactions update-ref: allow creation of multiple transactions t1400: avoid touching refs on filesystem
2 parents e0d2568 + 8c4417f commit 1bc550e

File tree

4 files changed

+126
-50
lines changed

4 files changed

+126
-50
lines changed

Documentation/git-update-ref.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,8 @@ option::
125125
start::
126126
Start a transaction. In contrast to a non-transactional session, a
127127
transaction will automatically abort if the session ends without an
128-
explicit commit.
128+
explicit commit. This command may create a new empty transaction when
129+
the current one has been committed or aborted already.
129130

130131
prepare::
131132
Prepare to commit the transaction. This will create lock files for all

builtin/update-ref.c

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,8 @@ static void update_refs_stdin(void)
436436
switch (state) {
437437
case UPDATE_REFS_OPEN:
438438
case UPDATE_REFS_STARTED:
439+
if (state == UPDATE_REFS_STARTED && cmd->state == UPDATE_REFS_STARTED)
440+
die("cannot restart ongoing transaction");
439441
/* Do not downgrade a transaction to a non-transaction. */
440442
if (cmd->state >= state)
441443
state = cmd->state;
@@ -446,7 +448,18 @@ static void update_refs_stdin(void)
446448
state = cmd->state;
447449
break;
448450
case UPDATE_REFS_CLOSED:
449-
die("transaction is closed");
451+
if (cmd->state != UPDATE_REFS_STARTED)
452+
die("transaction is closed");
453+
454+
/*
455+
* Open a new transaction if we're currently closed and
456+
* get a "start".
457+
*/
458+
state = cmd->state;
459+
transaction = ref_transaction_begin(&err);
460+
if (!transaction)
461+
die("%s", err.buf);
462+
450463
break;
451464
}
452465

t/perf/p1400-update-ref.sh

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,14 @@ test_description="Tests performance of update-ref"
77
test_perf_fresh_repo
88

99
test_expect_success "setup" '
10-
git init --bare target-repo.git &&
1110
test_commit PRE &&
1211
test_commit POST &&
13-
printf "create refs/heads/%d PRE\n" $(test_seq 1000) >create &&
14-
printf "update refs/heads/%d POST PRE\n" $(test_seq 1000) >update &&
15-
printf "delete refs/heads/%d POST\n" $(test_seq 1000) >delete &&
16-
git update-ref --stdin <create
12+
for i in $(test_seq 5000)
13+
do
14+
printf "start\ncreate refs/heads/%d PRE\ncommit\n" $i &&
15+
printf "start\nupdate refs/heads/%d POST PRE\ncommit\n" $i &&
16+
printf "start\ndelete refs/heads/%d POST\ncommit\n" $i
17+
done >instructions
1718
'
1819

1920
test_perf "update-ref" '
@@ -26,14 +27,7 @@ test_perf "update-ref" '
2627
'
2728

2829
test_perf "update-ref --stdin" '
29-
git update-ref --stdin <update &&
30-
git update-ref --stdin <delete &&
31-
git update-ref --stdin <create
32-
'
33-
34-
test_perf "nonatomic push" '
35-
git push ./target-repo.git $(test_seq 1000) &&
36-
git push --delete ./target-repo.git $(test_seq 1000)
30+
git update-ref --stdin <instructions >/dev/null
3731
'
3832

3933
test_done

t/t1400-update-ref.sh

Lines changed: 103 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,17 @@ test_expect_success "fail to delete $m with stale ref" '
4848
test $B = "$(git show-ref -s --verify $m)"
4949
'
5050
test_expect_success "delete $m" '
51-
test_when_finished "rm -f .git/$m" &&
51+
test_when_finished "git update-ref -d $m" &&
5252
git update-ref -d $m $B &&
53-
test_path_is_missing .git/$m
53+
test_must_fail git show-ref --verify -q $m
5454
'
5555

5656
test_expect_success "delete $m without oldvalue verification" '
57-
test_when_finished "rm -f .git/$m" &&
57+
test_when_finished "git update-ref -d $m" &&
5858
git update-ref $m $A &&
5959
test $A = $(git show-ref -s --verify $m) &&
6060
git update-ref -d $m &&
61-
test_path_is_missing .git/$m
61+
test_must_fail git show-ref --verify -q $m
6262
'
6363

6464
test_expect_success "fail to create $n" '
@@ -80,26 +80,26 @@ test_expect_success "fail to delete $m (by HEAD) with stale ref" '
8080
test $B = $(git show-ref -s --verify $m)
8181
'
8282
test_expect_success "delete $m (by HEAD)" '
83-
test_when_finished "rm -f .git/$m" &&
83+
test_when_finished "git update-ref -d $m" &&
8484
git update-ref -d HEAD $B &&
85-
test_path_is_missing .git/$m
85+
test_must_fail git show-ref --verify -q $m
8686
'
8787

8888
test_expect_success "deleting current branch adds message to HEAD's log" '
89-
test_when_finished "rm -f .git/$m" &&
89+
test_when_finished "git update-ref -d $m" &&
9090
git update-ref $m $A &&
9191
git symbolic-ref HEAD $m &&
9292
git update-ref -m delete-$m -d $m &&
93-
test_path_is_missing .git/$m &&
93+
test_must_fail git show-ref --verify -q $m &&
9494
grep "delete-$m$" .git/logs/HEAD
9595
'
9696

9797
test_expect_success "deleting by HEAD adds message to HEAD's log" '
98-
test_when_finished "rm -f .git/$m" &&
98+
test_when_finished "git update-ref -d $m" &&
9999
git update-ref $m $A &&
100100
git symbolic-ref HEAD $m &&
101101
git update-ref -m delete-by-head -d HEAD &&
102-
test_path_is_missing .git/$m &&
102+
test_must_fail git show-ref --verify -q $m &&
103103
grep "delete-by-head$" .git/logs/HEAD
104104
'
105105

@@ -188,31 +188,37 @@ test_expect_success "move $m (by HEAD)" '
188188
test $B = $(git show-ref -s --verify $m)
189189
'
190190
test_expect_success "delete $m (by HEAD) should remove both packed and loose $m" '
191-
test_when_finished "rm -f .git/$m" &&
191+
test_when_finished "git update-ref -d $m" &&
192192
git update-ref -d HEAD $B &&
193193
! grep "$m" .git/packed-refs &&
194-
test_path_is_missing .git/$m
194+
test_must_fail git show-ref --verify -q $m
195195
'
196196

197-
cp -f .git/HEAD .git/HEAD.orig
198197
test_expect_success 'delete symref without dereference' '
199-
test_when_finished "cp -f .git/HEAD.orig .git/HEAD" &&
200-
git update-ref --no-deref -d HEAD &&
201-
test_path_is_missing .git/HEAD
198+
test_when_finished "git update-ref -d $m" &&
199+
echo foo >foo.c &&
200+
git add foo.c &&
201+
git commit -m foo &&
202+
git symbolic-ref SYMREF $m &&
203+
git update-ref --no-deref -d SYMREF &&
204+
git show-ref --verify -q $m &&
205+
test_must_fail git show-ref --verify -q SYMREF &&
206+
test_must_fail git symbolic-ref SYMREF
202207
'
203208

204209
test_expect_success 'delete symref without dereference when the referred ref is packed' '
205-
test_when_finished "cp -f .git/HEAD.orig .git/HEAD" &&
210+
test_when_finished "git update-ref -d $m" &&
206211
echo foo >foo.c &&
207212
git add foo.c &&
208213
git commit -m foo &&
214+
git symbolic-ref SYMREF $m &&
209215
git pack-refs --all &&
210-
git update-ref --no-deref -d HEAD &&
211-
test_path_is_missing .git/HEAD
216+
git update-ref --no-deref -d SYMREF &&
217+
git show-ref --verify -q $m &&
218+
test_must_fail git show-ref --verify -q SYMREF &&
219+
test_must_fail git symbolic-ref SYMREF
212220
'
213221

214-
git update-ref -d $m
215-
216222
test_expect_success 'update-ref -d is not confused by self-reference' '
217223
git symbolic-ref refs/heads/self refs/heads/self &&
218224
test_when_finished "rm -f .git/refs/heads/self" &&
@@ -226,25 +232,25 @@ test_expect_success 'update-ref --no-deref -d can delete self-reference' '
226232
test_when_finished "rm -f .git/refs/heads/self" &&
227233
test_path_is_file .git/refs/heads/self &&
228234
git update-ref --no-deref -d refs/heads/self &&
229-
test_path_is_missing .git/refs/heads/self
235+
test_must_fail git show-ref --verify -q refs/heads/self
230236
'
231237

232238
test_expect_success 'update-ref --no-deref -d can delete reference to bad ref' '
233239
>.git/refs/heads/bad &&
234240
test_when_finished "rm -f .git/refs/heads/bad" &&
235241
git symbolic-ref refs/heads/ref-to-bad refs/heads/bad &&
236-
test_when_finished "rm -f .git/refs/heads/ref-to-bad" &&
242+
test_when_finished "git update-ref -d refs/heads/ref-to-bad" &&
237243
test_path_is_file .git/refs/heads/ref-to-bad &&
238244
git update-ref --no-deref -d refs/heads/ref-to-bad &&
239-
test_path_is_missing .git/refs/heads/ref-to-bad
245+
test_must_fail git show-ref --verify -q refs/heads/ref-to-bad
240246
'
241247

242248
test_expect_success '(not) create HEAD with old sha1' '
243249
test_must_fail git update-ref HEAD $A $B
244250
'
245251
test_expect_success "(not) prior created .git/$m" '
246-
test_when_finished "rm -f .git/$m" &&
247-
test_path_is_missing .git/$m
252+
test_when_finished "git update-ref -d $m" &&
253+
test_must_fail git show-ref --verify -q $m
248254
'
249255

250256
test_expect_success 'create HEAD' '
@@ -254,7 +260,7 @@ test_expect_success '(not) change HEAD with wrong SHA1' '
254260
test_must_fail git update-ref HEAD $B $Z
255261
'
256262
test_expect_success "(not) changed .git/$m" '
257-
test_when_finished "rm -f .git/$m" &&
263+
test_when_finished "git update-ref -d $m" &&
258264
! test $B = $(git show-ref -s --verify $m)
259265
'
260266

@@ -284,8 +290,8 @@ test_expect_success 'empty directory removal' '
284290
test_path_is_file .git/refs/heads/d1/d2/r1 &&
285291
test_path_is_file .git/logs/refs/heads/d1/d2/r1 &&
286292
git branch -d d1/d2/r1 &&
287-
test_path_is_missing .git/refs/heads/d1/d2 &&
288-
test_path_is_missing .git/logs/refs/heads/d1/d2 &&
293+
test_must_fail git show-ref --verify -q refs/heads/d1/d2 &&
294+
test_must_fail git show-ref --verify -q logs/refs/heads/d1/d2 &&
289295
test_path_is_file .git/refs/heads/d1/r2 &&
290296
test_path_is_file .git/logs/refs/heads/d1/r2
291297
'
@@ -298,8 +304,8 @@ test_expect_success 'symref empty directory removal' '
298304
test_path_is_file .git/refs/heads/e1/e2/r1 &&
299305
test_path_is_file .git/logs/refs/heads/e1/e2/r1 &&
300306
git update-ref -d HEAD &&
301-
test_path_is_missing .git/refs/heads/e1/e2 &&
302-
test_path_is_missing .git/logs/refs/heads/e1/e2 &&
307+
test_must_fail git show-ref --verify -q refs/heads/e1/e2 &&
308+
test_must_fail git show-ref --verify -q logs/refs/heads/e1/e2 &&
303309
test_path_is_file .git/refs/heads/e1/r2 &&
304310
test_path_is_file .git/logs/refs/heads/e1/r2 &&
305311
test_path_is_file .git/logs/HEAD
@@ -1388,7 +1394,8 @@ test_expect_success 'handle per-worktree refs in refs/bisect' '
13881394
git rev-parse refs/bisect/something >../worktree-head &&
13891395
git for-each-ref | grep refs/bisect/something
13901396
) &&
1391-
test_path_is_missing .git/refs/bisect &&
1397+
git show-ref >actual &&
1398+
! grep 'refs/bisect' actual &&
13921399
test_must_fail git rev-parse refs/bisect/something &&
13931400
git update-ref refs/bisect/something HEAD &&
13941401
git rev-parse refs/bisect/something >main-head &&
@@ -1500,7 +1507,7 @@ test_expect_success 'transaction can handle abort' '
15001507
git update-ref --stdin <stdin >actual &&
15011508
printf "%s: ok\n" start abort >expect &&
15021509
test_cmp expect actual &&
1503-
test_path_is_missing .git/$b
1510+
test_must_fail git show-ref --verify -q $b
15041511
'
15051512

15061513
test_expect_success 'transaction aborts by default' '
@@ -1511,7 +1518,7 @@ test_expect_success 'transaction aborts by default' '
15111518
git update-ref --stdin <stdin >actual &&
15121519
printf "%s: ok\n" start >expect &&
15131520
test_cmp expect actual &&
1514-
test_path_is_missing .git/$b
1521+
test_must_fail git show-ref --verify -q $b
15151522
'
15161523

15171524
test_expect_success 'transaction with prepare aborts by default' '
@@ -1523,7 +1530,68 @@ test_expect_success 'transaction with prepare aborts by default' '
15231530
git update-ref --stdin <stdin >actual &&
15241531
printf "%s: ok\n" start prepare >expect &&
15251532
test_cmp expect actual &&
1526-
test_path_is_missing .git/$b
1533+
test_must_fail git show-ref --verify -q $b
1534+
'
1535+
1536+
test_expect_success 'transaction can commit multiple times' '
1537+
cat >stdin <<-EOF &&
1538+
start
1539+
create refs/heads/branch-1 $A
1540+
commit
1541+
start
1542+
create refs/heads/branch-2 $B
1543+
commit
1544+
EOF
1545+
git update-ref --stdin <stdin >actual &&
1546+
printf "%s: ok\n" start commit start commit >expect &&
1547+
test_cmp expect actual &&
1548+
echo "$A" >expect &&
1549+
git rev-parse refs/heads/branch-1 >actual &&
1550+
test_cmp expect actual &&
1551+
echo "$B" >expect &&
1552+
git rev-parse refs/heads/branch-2 >actual &&
1553+
test_cmp expect actual
1554+
'
1555+
1556+
test_expect_success 'transaction can create and delete' '
1557+
cat >stdin <<-EOF &&
1558+
start
1559+
create refs/heads/create-and-delete $A
1560+
commit
1561+
start
1562+
delete refs/heads/create-and-delete $A
1563+
commit
1564+
EOF
1565+
git update-ref --stdin <stdin >actual &&
1566+
printf "%s: ok\n" start commit start commit >expect &&
1567+
test_must_fail git show-ref --verify refs/heads/create-and-delete
1568+
'
1569+
1570+
test_expect_success 'transaction can commit after abort' '
1571+
cat >stdin <<-EOF &&
1572+
start
1573+
create refs/heads/abort $A
1574+
abort
1575+
start
1576+
create refs/heads/abort $A
1577+
commit
1578+
EOF
1579+
git update-ref --stdin <stdin >actual &&
1580+
printf "%s: ok\n" start abort start commit >expect &&
1581+
echo "$A" >expect &&
1582+
git rev-parse refs/heads/abort >actual &&
1583+
test_cmp expect actual
1584+
'
1585+
1586+
test_expect_success 'transaction cannot restart ongoing transaction' '
1587+
cat >stdin <<-EOF &&
1588+
start
1589+
create refs/heads/restart $A
1590+
start
1591+
commit
1592+
EOF
1593+
test_must_fail git update-ref --stdin <stdin >actual &&
1594+
test_must_fail git show-ref --verify refs/heads/restart
15271595
'
15281596

15291597
test_done

0 commit comments

Comments
 (0)