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 &&
477 git submodule add ../sub_repo sub1 &&
479 ## Confirm stage/add of clean submodule.
480 HMOD=$(git hash-object -t blob -- .gitmodules) &&
481 HSUP=$(git rev-parse HEAD) &&
484 cat >expect <<-EOF &&
486 # branch.head initial-branch
487 # branch.upstream origin/initial-branch
489 1 A. N... 000000 100644 100644 $ZERO_OID $HMOD .gitmodules
490 1 A. S... 000000 160000 160000 $ZERO_OID $HSUB sub1
493 git status --porcelain=v2 --branch --untracked-files=all >actual &&
494 test_cmp expect actual
498 test_expect_success
'untracked changes in added submodule (AM S..U)' '
500 ## create untracked file in the submodule.
502 echo "xxxx" >file_in_sub
505 HMOD=$(git hash-object -t blob -- .gitmodules) &&
506 HSUP=$(git rev-parse HEAD) &&
509 cat >expect <<-EOF &&
511 # branch.head initial-branch
512 # branch.upstream origin/initial-branch
514 1 A. N... 000000 100644 100644 $ZERO_OID $HMOD .gitmodules
515 1 AM S..U 000000 160000 160000 $ZERO_OID $HSUB sub1
518 git status --porcelain=v2 --branch --untracked-files=all >actual &&
519 test_cmp expect actual
523 test_expect_success
'staged changes in added submodule (AM S.M.)' '
525 ## stage the changes in the submodule.
530 HMOD=$(git hash-object -t blob -- .gitmodules) &&
531 HSUP=$(git rev-parse HEAD) &&
534 cat >expect <<-EOF &&
536 # branch.head initial-branch
537 # branch.upstream origin/initial-branch
539 1 A. N... 000000 100644 100644 $ZERO_OID $HMOD .gitmodules
540 1 AM S.M. 000000 160000 160000 $ZERO_OID $HSUB sub1
543 git status --porcelain=v2 --branch --untracked-files=all >actual &&
544 test_cmp expect actual
548 test_expect_success
'staged and unstaged changes in added (AM S.M.)' '
551 ## make additional unstaged changes (on the same file) in the submodule.
552 ## This does not cause us to get S.MU (because the submodule does not report
553 ## a "?" line for the unstaged changes).
554 echo "more changes" >>file_in_sub
557 HMOD=$(git hash-object -t blob -- .gitmodules) &&
558 HSUP=$(git rev-parse HEAD) &&
561 cat >expect <<-EOF &&
563 # branch.head initial-branch
564 # branch.upstream origin/initial-branch
566 1 A. N... 000000 100644 100644 $ZERO_OID $HMOD .gitmodules
567 1 AM S.M. 000000 160000 160000 $ZERO_OID $HSUB sub1
570 git status --porcelain=v2 --branch --untracked-files=all >actual &&
571 test_cmp expect actual
575 test_expect_success
'staged and untracked changes in added submodule (AM S.MU)' '
578 ## stage new changes in tracked file.
579 git add file_in_sub &&
580 ## create new untracked file.
581 echo "yyyy" >>another_file_in_sub
584 HMOD=$(git hash-object -t blob -- .gitmodules) &&
585 HSUP=$(git rev-parse HEAD) &&
588 cat >expect <<-EOF &&
590 # branch.head initial-branch
591 # branch.upstream origin/initial-branch
593 1 A. N... 000000 100644 100644 $ZERO_OID $HMOD .gitmodules
594 1 AM S.MU 000000 160000 160000 $ZERO_OID $HSUB sub1
597 git status --porcelain=v2 --branch --untracked-files=all >actual &&
598 test_cmp expect actual
602 test_expect_success
'commit within the submodule appears as new commit in super (AM SC..)' '
605 ## Make a new commit in the submodule.
606 git add file_in_sub &&
607 rm -f another_file_in_sub &&
608 git commit -m "new commit"
611 HMOD=$(git hash-object -t blob -- .gitmodules) &&
612 HSUP=$(git rev-parse HEAD) &&
615 cat >expect <<-EOF &&
617 # branch.head initial-branch
618 # branch.upstream origin/initial-branch
620 1 A. N... 000000 100644 100644 $ZERO_OID $HMOD .gitmodules
621 1 AM SC.. 000000 160000 160000 $ZERO_OID $HSUB sub1
624 git status --porcelain=v2 --branch --untracked-files=all >actual &&
625 test_cmp expect actual
629 test_expect_success
'stage submodule in super and commit' '
631 ## Stage the new submodule commit in the super.
633 ## Commit the super so that the sub no longer appears as added.
634 git commit -m "super commit" &&
636 HSUP=$(git rev-parse HEAD) &&
638 cat >expect <<-EOF &&
640 # branch.head initial-branch
641 # branch.upstream origin/initial-branch
645 git status --porcelain=v2 --branch --untracked-files=all >actual &&
646 test_cmp expect actual
650 test_expect_success
'make unstaged changes in existing submodule (.M S.M.)' '
653 echo "zzzz" >>file_in_sub
656 HSUP=$(git rev-parse HEAD) &&
657 HSUB=$(cd sub1 && git rev-parse HEAD) &&
659 cat >expect <<-EOF &&
661 # branch.head initial-branch
662 # branch.upstream origin/initial-branch
664 1 .M S.M. 160000 160000 160000 $HSUB $HSUB sub1
667 git status --porcelain=v2 --branch --untracked-files=all >actual &&
668 test_cmp expect actual