3 test_description
='git status --porcelain=v2
5 This test exercises porcelain V2 output for git status.'
10 test_expect_success setup
'
11 git checkout -f --orphan initial-branch &&
13 git config core.autocrlf false &&
18 echo a >dir1/file_a &&
22 test_expect_success
'before initial commit, nothing added, only untracked' '
24 # branch.oid (initial)
25 # branch.head initial-branch
34 git status --porcelain=v2 --branch --untracked-files=normal >actual &&
35 test_cmp expect actual
38 test_expect_success
'before initial commit, things added' '
39 git add file_x file_y file_z dir1 &&
40 OID_A=$(git hash-object -t blob -- dir1/file_a) &&
41 OID_B=$(git hash-object -t blob -- dir1/file_b) &&
42 OID_X=$(git hash-object -t blob -- file_x) &&
43 OID_Y=$(git hash-object -t blob -- file_y) &&
44 OID_Z=$(git hash-object -t blob -- file_z) &&
47 # branch.oid (initial)
48 # branch.head initial-branch
49 1 A. N... 000000 100644 100644 $ZERO_OID $OID_A dir1/file_a
50 1 A. N... 000000 100644 100644 $ZERO_OID $OID_B dir1/file_b
51 1 A. N... 000000 100644 100644 $ZERO_OID $OID_X file_x
52 1 A. N... 000000 100644 100644 $ZERO_OID $OID_Y file_y
53 1 A. N... 000000 100644 100644 $ZERO_OID $OID_Z file_z
58 git status --porcelain=v2 --branch --untracked-files=all >actual &&
59 test_cmp expect actual
62 test_expect_success
'before initial commit, things added (-z)' '
63 lf_to_nul >expect <<-EOF &&
64 # branch.oid (initial)
65 # branch.head initial-branch
66 1 A. N... 000000 100644 100644 $ZERO_OID $OID_A dir1/file_a
67 1 A. N... 000000 100644 100644 $ZERO_OID $OID_B dir1/file_b
68 1 A. N... 000000 100644 100644 $ZERO_OID $OID_X file_x
69 1 A. N... 000000 100644 100644 $ZERO_OID $OID_Y file_y
70 1 A. N... 000000 100644 100644 $ZERO_OID $OID_Z file_z
75 git status -z --porcelain=v2 --branch --untracked-files=all >actual &&
76 test_cmp expect actual
79 test_expect_success
'make first commit, comfirm HEAD oid and branch' '
80 git commit -m initial &&
81 H0=$(git rev-parse HEAD) &&
84 # branch.head initial-branch
89 git status --porcelain=v2 --branch --untracked-files=all >actual &&
90 test_cmp expect actual
93 test_expect_success
'after first commit, create unstaged changes' '
95 OID_X1=$(git hash-object -t blob -- file_x) &&
97 H0=$(git rev-parse HEAD) &&
101 # branch.head initial-branch
102 1 .M N... 100644 100644 100644 $OID_X $OID_X file_x
103 1 .D N... 100644 100644 000000 $OID_Z $OID_Z file_z
108 git status --porcelain=v2 --branch --untracked-files=all >actual &&
109 test_cmp expect actual
112 test_expect_success
'after first commit, stash existing changes' '
113 cat >expect <<-EOF &&
115 # branch.head initial-branch
119 test_when_finished "git stash pop && git stash pop" &&
121 git stash -- file_x &&
123 git status --porcelain=v2 --branch --show-stash --untracked-files=no >actual &&
124 test_cmp expect actual
127 test_expect_success
'after first commit but omit untracked files and branch' '
128 cat >expect <<-EOF &&
129 1 .M N... 100644 100644 100644 $OID_X $OID_X file_x
130 1 .D N... 100644 100644 000000 $OID_Z $OID_Z file_z
133 git status --porcelain=v2 --untracked-files=no >actual &&
134 test_cmp expect actual
137 test_expect_success
'after first commit, stage existing changes' '
140 H0=$(git rev-parse HEAD) &&
142 cat >expect <<-EOF &&
144 # branch.head initial-branch
145 1 M. N... 100644 100644 100644 $OID_X $OID_X1 file_x
146 1 D. N... 100644 000000 000000 $OID_Z $ZERO_OID file_z
151 git status --porcelain=v2 --branch --untracked-files=all >actual &&
152 test_cmp expect actual
155 test_expect_success
'rename causes 2 path lines' '
156 git mv file_y renamed_y &&
157 H0=$(git rev-parse HEAD) &&
159 q_to_tab >expect <<-EOF &&
161 # branch.head initial-branch
162 1 M. N... 100644 100644 100644 $OID_X $OID_X1 file_x
163 1 D. N... 100644 000000 000000 $OID_Z $ZERO_OID file_z
164 2 R. N... 100644 100644 100644 $OID_Y $OID_Y R100 renamed_yQfile_y
169 git status --porcelain=v2 --branch --untracked-files=all >actual &&
170 test_cmp expect actual
173 test_expect_success
'rename causes 2 path lines (-z)' '
174 H0=$(git rev-parse HEAD) &&
176 ## Lines use NUL path separator and line terminator, so double transform here.
177 q_to_nul <<-EOF | lf_to_nul >expect &&
179 # branch.head initial-branch
180 1 M. N... 100644 100644 100644 $OID_X $OID_X1 file_x
181 1 D. N... 100644 000000 000000 $OID_Z $ZERO_OID file_z
182 2 R. N... 100644 100644 100644 $OID_Y $OID_Y R100 renamed_yQfile_y
187 git status --porcelain=v2 --branch --untracked-files=all -z >actual &&
188 test_cmp expect actual
191 test_expect_success
'make second commit, confirm clean and new HEAD oid' '
192 git commit -m second &&
193 H1=$(git rev-parse HEAD) &&
195 cat >expect <<-EOF &&
197 # branch.head initial-branch
202 git status --porcelain=v2 --branch --untracked-files=all >actual &&
203 test_cmp expect actual
206 test_expect_success
'confirm ignored files are not printed' '
207 test_when_finished "rm -f x.ign .gitignore" &&
208 echo x.ign >.gitignore &&
209 echo "ignore me" >x.ign &&
211 cat >expect <<-EOF &&
217 git status --porcelain=v2 --untracked-files=all >actual &&
218 test_cmp expect actual
221 test_expect_success
'ignored files are printed with --ignored' '
222 test_when_finished "rm -f x.ign .gitignore" &&
223 echo x.ign >.gitignore &&
224 echo "ignore me" >x.ign &&
226 cat >expect <<-EOF &&
233 git status --porcelain=v2 --ignored --untracked-files=all >actual &&
234 test_cmp expect actual
237 test_expect_success
'create and commit permanent ignore file' '
238 cat >.gitignore <<-EOF &&
243 git add .gitignore &&
244 git commit -m ignore_trash &&
245 H1=$(git rev-parse HEAD) &&
247 cat >expect <<-EOF &&
249 # branch.head initial-branch
252 git status --porcelain=v2 --branch >actual &&
253 test_cmp expect actual
256 test_expect_success
'verify --intent-to-add output' '
257 test_when_finished "git rm -f intent1.add intent2.add" &&
259 echo test >intent2.add &&
261 git add --intent-to-add intent1.add intent2.add &&
263 cat >expect <<-EOF &&
264 1 .A N... 000000 000000 100644 $ZERO_OID $ZERO_OID intent1.add
265 1 .A N... 000000 000000 100644 $ZERO_OID $ZERO_OID intent2.add
268 git status --porcelain=v2 >actual &&
269 test_cmp expect actual
272 test_expect_success
'verify AA (add-add) conflict' '
273 test_when_finished "git reset --hard" &&
275 git branch AA_A initial-branch &&
277 echo "Branch AA_A" >conflict.txt &&
278 OID_AA_A=$(git hash-object -t blob -- conflict.txt) &&
279 git add conflict.txt &&
280 git commit -m "branch aa_a" &&
282 git branch AA_B initial-branch &&
284 echo "Branch AA_B" >conflict.txt &&
285 OID_AA_B=$(git hash-object -t blob -- conflict.txt) &&
286 git add conflict.txt &&
287 git commit -m "branch aa_b" &&
289 git branch AA_M AA_B &&
291 test_must_fail git merge AA_A &&
293 HM=$(git rev-parse HEAD) &&
295 cat >expect <<-EOF &&
298 u AA N... 000000 100644 100644 100644 $ZERO_OID $OID_AA_B $OID_AA_A conflict.txt
301 git status --porcelain=v2 --branch --untracked-files=all >actual &&
302 test_cmp expect actual
305 test_expect_success
'verify UU (edit-edit) conflict' '
306 test_when_finished "git reset --hard" &&
308 git branch UU_ANC initial-branch &&
309 git checkout UU_ANC &&
310 echo "Ancestor" >conflict.txt &&
311 OID_UU_ANC=$(git hash-object -t blob -- conflict.txt) &&
312 git add conflict.txt &&
313 git commit -m "UU_ANC" &&
315 git branch UU_A UU_ANC &&
317 echo "Branch UU_A" >conflict.txt &&
318 OID_UU_A=$(git hash-object -t blob -- conflict.txt) &&
319 git add conflict.txt &&
320 git commit -m "branch uu_a" &&
322 git branch UU_B UU_ANC &&
324 echo "Branch UU_B" >conflict.txt &&
325 OID_UU_B=$(git hash-object -t blob -- conflict.txt) &&
326 git add conflict.txt &&
327 git commit -m "branch uu_b" &&
329 git branch UU_M UU_B &&
331 test_must_fail git merge UU_A &&
333 HM=$(git rev-parse HEAD) &&
335 cat >expect <<-EOF &&
338 u UU N... 100644 100644 100644 100644 $OID_UU_ANC $OID_UU_B $OID_UU_A conflict.txt
341 git status --porcelain=v2 --branch --untracked-files=all >actual &&
342 test_cmp expect actual
345 test_expect_success
'verify upstream fields in branch header' '
346 git checkout initial-branch &&
347 test_when_finished "rm -rf sub_repo" &&
348 git clone . sub_repo &&
350 ## Confirm local initial-branch tracks remote initial-branch.
352 HUF=$(git rev-parse HEAD) &&
354 cat >expect <<-EOF &&
356 # branch.head initial-branch
357 # branch.upstream origin/initial-branch
361 git status --porcelain=v2 --branch --untracked-files=all >actual &&
362 test_cmp expect actual &&
364 ## Test ahead/behind.
365 echo xyz >file_xyz &&
369 HUF=$(git rev-parse HEAD) &&
371 cat >expect <<-EOF &&
373 # branch.head initial-branch
374 # branch.upstream origin/initial-branch
378 git status --porcelain=v2 --branch --untracked-files=all >actual &&
379 test_cmp expect actual &&
381 ## Repeat the above but without --branch.
382 git status --porcelain=v2 --untracked-files=all >actual &&
383 test_must_be_empty actual &&
385 ## Test upstream-gone case. Fake this by pointing
386 ## origin/initial-branch at a non-existing commit.
387 git update-ref -d refs/remotes/origin/initial-branch &&
389 HUF=$(git rev-parse HEAD) &&
391 cat >expect <<-EOF &&
393 # branch.head initial-branch
394 # branch.upstream origin/initial-branch
397 git status --porcelain=v2 --branch --untracked-files=all >actual &&
398 test_cmp expect actual
402 test_expect_success
'verify --[no-]ahead-behind with V2 format' '
403 git checkout initial-branch &&
404 test_when_finished "rm -rf sub_repo" &&
405 git clone . sub_repo &&
407 ## Confirm local initial-branch tracks remote initial-branch.
409 HUF=$(git rev-parse HEAD) &&
411 # Confirm --no-ahead-behind reports traditional branch.ab with 0/0 for equal branches.
412 cat >expect <<-EOF &&
414 # branch.head initial-branch
415 # branch.upstream origin/initial-branch
419 git status --no-ahead-behind --porcelain=v2 --branch --untracked-files=all >actual &&
420 test_cmp expect actual &&
422 # Confirm --ahead-behind reports traditional branch.ab with 0/0.
423 cat >expect <<-EOF &&
425 # branch.head initial-branch
426 # branch.upstream origin/initial-branch
430 git status --ahead-behind --porcelain=v2 --branch --untracked-files=all >actual &&
431 test_cmp expect actual &&
433 ## Test non-equal ahead/behind.
434 echo xyz >file_xyz &&
438 HUF=$(git rev-parse HEAD) &&
440 # Confirm --no-ahead-behind reports branch.ab with ?/? for non-equal branches.
441 cat >expect <<-EOF &&
443 # branch.head initial-branch
444 # branch.upstream origin/initial-branch
448 git status --no-ahead-behind --porcelain=v2 --branch --untracked-files=all >actual &&
449 test_cmp expect actual &&
451 # Confirm --ahead-behind reports traditional branch.ab with 1/0.
452 cat >expect <<-EOF &&
454 # branch.head initial-branch
455 # branch.upstream origin/initial-branch
459 git status --ahead-behind --porcelain=v2 --branch --untracked-files=all >actual &&
460 test_cmp expect actual &&
462 # Confirm that "status.aheadbehind" DOES NOT work on V2 format.
463 git -c status.aheadbehind=false status --porcelain=v2 --branch --untracked-files=all >actual &&
464 test_cmp expect actual &&
466 # Confirm that "status.aheadbehind" DOES NOT work on V2 format.
467 git -c status.aheadbehind=true status --porcelain=v2 --branch --untracked-files=all >actual &&
468 test_cmp expect actual
472 test_expect_success
'create and add submodule, submodule appears clean (A. S...)' '
473 git checkout initial-branch &&
474 git clone . sub_repo &&
475 git clone . super_repo &&
476 test_config_global protocol.file.allow always &&
478 git submodule add ../sub_repo sub1 &&
480 ## Confirm stage/add of clean submodule.
481 HMOD=$(git hash-object -t blob -- .gitmodules) &&
482 HSUP=$(git rev-parse HEAD) &&
485 cat >expect <<-EOF &&
487 # branch.head initial-branch
488 # branch.upstream origin/initial-branch
490 1 A. N... 000000 100644 100644 $ZERO_OID $HMOD .gitmodules
491 1 A. S... 000000 160000 160000 $ZERO_OID $HSUB sub1
494 git status --porcelain=v2 --branch --untracked-files=all >actual &&
495 test_cmp expect actual
499 test_expect_success
'untracked changes in added submodule (AM S..U)' '
501 ## create untracked file in the submodule.
503 echo "xxxx" >file_in_sub
506 HMOD=$(git hash-object -t blob -- .gitmodules) &&
507 HSUP=$(git rev-parse HEAD) &&
510 cat >expect <<-EOF &&
512 # branch.head initial-branch
513 # branch.upstream origin/initial-branch
515 1 A. N... 000000 100644 100644 $ZERO_OID $HMOD .gitmodules
516 1 AM S..U 000000 160000 160000 $ZERO_OID $HSUB sub1
519 git status --porcelain=v2 --branch --untracked-files=all >actual &&
520 test_cmp expect actual
524 test_expect_success
'staged changes in added submodule (AM S.M.)' '
526 ## stage the changes in the submodule.
531 HMOD=$(git hash-object -t blob -- .gitmodules) &&
532 HSUP=$(git rev-parse HEAD) &&
535 cat >expect <<-EOF &&
537 # branch.head initial-branch
538 # branch.upstream origin/initial-branch
540 1 A. N... 000000 100644 100644 $ZERO_OID $HMOD .gitmodules
541 1 AM S.M. 000000 160000 160000 $ZERO_OID $HSUB sub1
544 git status --porcelain=v2 --branch --untracked-files=all >actual &&
545 test_cmp expect actual
549 test_expect_success
'staged and unstaged changes in added (AM S.M.)' '
552 ## make additional unstaged changes (on the same file) in the submodule.
553 ## This does not cause us to get S.MU (because the submodule does not report
554 ## a "?" line for the unstaged changes).
555 echo "more changes" >>file_in_sub
558 HMOD=$(git hash-object -t blob -- .gitmodules) &&
559 HSUP=$(git rev-parse HEAD) &&
562 cat >expect <<-EOF &&
564 # branch.head initial-branch
565 # branch.upstream origin/initial-branch
567 1 A. N... 000000 100644 100644 $ZERO_OID $HMOD .gitmodules
568 1 AM S.M. 000000 160000 160000 $ZERO_OID $HSUB sub1
571 git status --porcelain=v2 --branch --untracked-files=all >actual &&
572 test_cmp expect actual
576 test_expect_success
'staged and untracked changes in added submodule (AM S.MU)' '
579 ## stage new changes in tracked file.
580 git add file_in_sub &&
581 ## create new untracked file.
582 echo "yyyy" >>another_file_in_sub
585 HMOD=$(git hash-object -t blob -- .gitmodules) &&
586 HSUP=$(git rev-parse HEAD) &&
589 cat >expect <<-EOF &&
591 # branch.head initial-branch
592 # branch.upstream origin/initial-branch
594 1 A. N... 000000 100644 100644 $ZERO_OID $HMOD .gitmodules
595 1 AM S.MU 000000 160000 160000 $ZERO_OID $HSUB sub1
598 git status --porcelain=v2 --branch --untracked-files=all >actual &&
599 test_cmp expect actual
603 test_expect_success
'commit within the submodule appears as new commit in super (AM SC..)' '
606 ## Make a new commit in the submodule.
607 git add file_in_sub &&
608 rm -f another_file_in_sub &&
609 git commit -m "new commit"
612 HMOD=$(git hash-object -t blob -- .gitmodules) &&
613 HSUP=$(git rev-parse HEAD) &&
616 cat >expect <<-EOF &&
618 # branch.head initial-branch
619 # branch.upstream origin/initial-branch
621 1 A. N... 000000 100644 100644 $ZERO_OID $HMOD .gitmodules
622 1 AM SC.. 000000 160000 160000 $ZERO_OID $HSUB sub1
625 git status --porcelain=v2 --branch --untracked-files=all >actual &&
626 test_cmp expect actual
630 test_expect_success
'stage submodule in super and commit' '
632 ## Stage the new submodule commit in the super.
634 ## Commit the super so that the sub no longer appears as added.
635 git commit -m "super commit" &&
637 HSUP=$(git rev-parse HEAD) &&
639 cat >expect <<-EOF &&
641 # branch.head initial-branch
642 # branch.upstream origin/initial-branch
646 git status --porcelain=v2 --branch --untracked-files=all >actual &&
647 test_cmp expect actual
651 test_expect_success
'make unstaged changes in existing submodule (.M S.M.)' '
654 echo "zzzz" >>file_in_sub
657 HSUP=$(git rev-parse HEAD) &&
658 HSUB=$(cd sub1 && git rev-parse HEAD) &&
660 cat >expect <<-EOF &&
662 # branch.head initial-branch
663 # branch.upstream origin/initial-branch
665 1 .M S.M. 160000 160000 160000 $HSUB $HSUB sub1
668 git status --porcelain=v2 --branch --untracked-files=all >actual &&
669 test_cmp expect actual