Skip to content

Commit f840c4d

Browse files
committed
Add very verbose porcelain output to status
Tools interacting with Git repositories may need to know the complete state of the working directory. For efficiency, it would be good to have a single command to obtain this information. We already have a `--porcelain` mode intended for tools' consumption, and it only makes sense to enhance this mode to offer more information. Just like we do elsewhere in Git's source code, we now interpret multiple `--verbose` flags accumulatively, and show substantially more information in porcelain mode at verbosity level 2. Signed-off-by: Jeff Hostetler <[email protected]>
1 parent dd1308a commit f840c4d

File tree

5 files changed

+932
-2
lines changed

5 files changed

+932
-2
lines changed

Documentation/git-status.txt

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,9 @@ OPTIONS
4949
twice, then also show the changes in the working tree that
5050
have not yet been staged (i.e., like the output of `git diff`).
5151

52+
When given twice with `--porcelain`, additional output is enabled.
53+
See the section entitled "Very Verbose Porcelain Format" for details.
54+
5255
-u[<mode>]::
5356
--untracked-files[=<mode>]::
5457
Show untracked files.
@@ -207,6 +210,86 @@ field from the first filename). Third, filenames containing special
207210
characters are not specially formatted; no quoting or
208211
backslash-escaping is performed.
209212

213+
Very Verbose Porcelain Format
214+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
215+
216+
When --verbose is given twice along with --porcelain, additional output
217+
is provided.
218+
219+
If --branch is given, the first line shows a summary of the current
220+
operation in progress. This line begins with "### state: ", the name
221+
of the operation in progress, and then operation-specific information.
222+
Fields are separated by a single space.
223+
224+
Operation Fields Explanation
225+
------------------------------------------------------------------
226+
clean
227+
------------------------------------------------------------------
228+
merge <nr> Number unmerged
229+
------------------------------------------------------------------
230+
am [E] Present if the current patch
231+
is empty
232+
------------------------------------------------------------------
233+
rebase <nr> Number unmerged
234+
[S] Present if split commit in
235+
progress during rebase
236+
[E] Present if editing a commit
237+
during rebase
238+
[I(<done>/<total>)] Present if in an interactive
239+
rebase. Step counts are given.
240+
[<current>:<onto>] Rebase branches
241+
------------------------------------------------------------------
242+
cherrypick <sha>
243+
<nr> Number unmerged
244+
------------------------------------------------------------------
245+
revert <sha>
246+
<nr> Number unmerged
247+
------------------------------------------------------------------
248+
bisect [<branch>]
249+
------------------------------------------------------------------
250+
251+
If --branch is given, the second line shows branch tracking information.
252+
This line begins with "### track: ". Fields are separated by a single
253+
space, unless otherwise indicated.
254+
255+
Field Meaning
256+
--------------------------------------------------------
257+
<sha> | "(initial)" Current commit
258+
<branch> | "(detached)" Current branch
259+
":"<upstream> Upstream branch, if set
260+
"+"<ahead> Ahead count, if upstream present
261+
"-"<behind> Behind count, if upstream present
262+
--------------------------------------------------------
263+
264+
A series of lines are then displayed for the tracked entries.
265+
Lines have one of the following formats:
266+
267+
XYS mH mI mW shaH shaI PATH
268+
XYS mH mI mW shaH shaI score OLD_PATH\tPATH
269+
XYS m1 m2 m3 mW sha1 sha2 sha3 PATH
270+
271+
* X and Y were described in the short format section.
272+
* S is a one character summary of the submodule status with values '0'..'7'
273+
representing the sum of: 4 when submodule has a new commit, 2 when the
274+
submodule is modified, and 1 when the submodule contains untracked changes.
275+
The value is ' ' if the entry is not a submodule.
276+
* mH, mI, and mW are the 6 digit octal modes for the head, index, and worktree
277+
versions of the entry.
278+
* m1, m2, and m3 are the modes for the stage 1, 2, and 3 versions of the entry
279+
when in an unmerged state.
280+
* shaH and shaI are the 40 character hashes for the head and index version.
281+
* sha1, sha2, and sha3 are the hashes for the stage 1, 2, and 3 versions of
282+
the entry when in an unmerged state.
283+
* score is the rename percentage score.
284+
* PATH is the current path.
285+
* OLD_PATH is the source path for a staged rename.
286+
287+
A series of lines are then displayed for untracked and ignored entries.
288+
289+
XXX PATH
290+
291+
* XXX is "???" for untracked entries and "!!!" for ignored entries.
292+
210293
CONFIGURATION
211294
-------------
212295

builtin/commit.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,8 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
499499
s->fp = fp;
500500
s->nowarn = nowarn;
501501
s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
502+
if (!s->is_initial)
503+
hashcpy(s->sha_commit, sha1);
502504

503505
wt_status_collect(s);
504506

@@ -1380,7 +1382,20 @@ int cmd_status(int argc, const char **argv, const char *prefix)
13801382
fd = hold_locked_index(&index_lock, 0);
13811383

13821384
s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
1385+
if (!s.is_initial)
1386+
hashcpy(s.sha_commit, sha1);
1387+
13831388
s.ignore_submodule_arg = ignore_submodule_arg;
1389+
if (verbose > 1 && status_format == STATUS_FORMAT_PORCELAIN) {
1390+
/* Capture extra data for very verbose porcelain output. */
1391+
s.verbose = verbose;
1392+
1393+
/* Force safe_crlf off to prevent normal LF/CRLF warning
1394+
* message from being printed on stderr for each new file.
1395+
*/
1396+
safe_crlf = SAFE_CRLF_FALSE;
1397+
}
1398+
13841399
wt_status_collect(&s);
13851400

13861401
if (0 <= fd)

t/t7064-wtstatus-vvp.sh

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
#!/bin/sh
2+
3+
test_description='git status --porcelain --verbose --verbose
4+
5+
This test exercises very verbose output for git status.'
6+
7+
8+
. ./test-lib.sh
9+
10+
test_expect_success setup '
11+
test_tick &&
12+
git config --local core.autocrlf false &&
13+
echo x >file_x &&
14+
echo y >file_y &&
15+
echo z >file_z &&
16+
mkdir dir1 &&
17+
echo a >dir1/file_a &&
18+
echo b >dir1/file_b
19+
'
20+
21+
22+
##################################################################
23+
## Confirm VVP output prior to initial commit.
24+
##################################################################
25+
26+
test_expect_success pre_initial_commit_0 '
27+
cat >expected <<EOF &&
28+
### state: clean
29+
### track: (initial) master
30+
??? actual
31+
??? dir1/
32+
??? expected
33+
??? file_x
34+
??? file_y
35+
??? file_z
36+
EOF
37+
git status --porcelain --branch --verbose --verbose --ignored --untracked-files=normal >actual &&
38+
test_cmp expected actual
39+
'
40+
41+
42+
test_expect_success pre_initial_commit_1 '
43+
git add file_x file_y file_z dir1 &&
44+
cat >expected <<EOF &&
45+
### state: clean
46+
### track: (initial) master
47+
A 000000 100644 100644 0000000000000000000000000000000000000000 78981922613b2afb6025042ff6bd878ac1994e85 dir1/file_a
48+
A 000000 100644 100644 0000000000000000000000000000000000000000 61780798228d17af2d34fce4cfbdf35556832472 dir1/file_b
49+
A 000000 100644 100644 0000000000000000000000000000000000000000 587be6b4c3f93f93c489c0111bba5596147a26cb file_x
50+
A 000000 100644 100644 0000000000000000000000000000000000000000 975fbec8256d3e8a3797e7a3611380f27c49f4ac file_y
51+
A 000000 100644 100644 0000000000000000000000000000000000000000 b68025345d5301abad4d9ec9166f455243a0d746 file_z
52+
??? actual
53+
??? expected
54+
EOF
55+
git status --porcelain --branch --verbose --verbose --ignored --untracked-files=all >actual &&
56+
test_cmp expected actual
57+
'
58+
59+
60+
##################################################################
61+
## Create first commit. Confirm commit sha in new track header.
62+
## Then make some changes on top of it.
63+
##################################################################
64+
65+
test_expect_success initial_commit_0 '
66+
git commit -m initial &&
67+
H0=`git rev-parse HEAD` &&
68+
cat >expected <<EOF &&
69+
### state: clean
70+
### track: $H0 master
71+
??? actual
72+
??? expected
73+
EOF
74+
git status --porcelain --branch --verbose --verbose --ignored --untracked-files=all >actual &&
75+
test_cmp expected actual
76+
'
77+
78+
79+
test_expect_success initial_commit_1 '
80+
echo x >>file_x &&
81+
rm file_z &&
82+
H0=`git rev-parse HEAD` &&
83+
cat >expected <<EOF &&
84+
### state: clean
85+
### track: $H0 master
86+
M 100644 100644 100644 587be6b4c3f93f93c489c0111bba5596147a26cb 587be6b4c3f93f93c489c0111bba5596147a26cb file_x
87+
D 100644 100644 000000 b68025345d5301abad4d9ec9166f455243a0d746 b68025345d5301abad4d9ec9166f455243a0d746 file_z
88+
??? actual
89+
??? expected
90+
EOF
91+
git status --porcelain --branch --verbose --verbose --ignored --untracked-files=all >actual &&
92+
test_cmp expected actual
93+
'
94+
95+
96+
test_expect_success initial_commit_2 '
97+
git add file_x &&
98+
git rm file_z &&
99+
H0=`git rev-parse HEAD` &&
100+
cat >expected <<EOF &&
101+
### state: clean
102+
### track: $H0 master
103+
M 100644 100644 100644 587be6b4c3f93f93c489c0111bba5596147a26cb 560e017033beac775889ae5ff81a070fe16c6e8e file_x
104+
D 100644 000000 000000 b68025345d5301abad4d9ec9166f455243a0d746 0000000000000000000000000000000000000000 file_z
105+
??? actual
106+
??? expected
107+
EOF
108+
git status --porcelain --branch --verbose --verbose --ignored --untracked-files=all >actual &&
109+
test_cmp expected actual
110+
'
111+
112+
113+
test_expect_success initial_commit_3 '
114+
git mv file_y renamed_y &&
115+
H0=`git rev-parse HEAD` &&
116+
printf "### state: clean\n" >expected &&
117+
printf "### track: $H0 master\n" >>expected &&
118+
printf "M 100644 100644 100644 587be6b4c3f93f93c489c0111bba5596147a26cb 560e017033beac775889ae5ff81a070fe16c6e8e file_x\n" >>expected &&
119+
printf "D 100644 000000 000000 b68025345d5301abad4d9ec9166f455243a0d746 0000000000000000000000000000000000000000 file_z\n" >>expected &&
120+
printf "R 100644 100644 100644 975fbec8256d3e8a3797e7a3611380f27c49f4ac 975fbec8256d3e8a3797e7a3611380f27c49f4ac 100 file_y\trenamed_y\n" >>expected &&
121+
printf "??? actual\n" >>expected &&
122+
printf "??? expected\n" >>expected &&
123+
git status --porcelain --branch --verbose --verbose --ignored --untracked-files=all >actual &&
124+
test_cmp expected actual
125+
'
126+
127+
128+
##################################################################
129+
## Create second commit.
130+
##################################################################
131+
132+
test_expect_success second_commit_0 '
133+
git commit -m second &&
134+
H1=`git rev-parse HEAD` &&
135+
cat >expected <<EOF &&
136+
### state: clean
137+
### track: $H1 master
138+
??? actual
139+
??? expected
140+
EOF
141+
git status --porcelain --branch --verbose --verbose --ignored --untracked-files=all >actual &&
142+
test_cmp expected actual
143+
'
144+
145+
146+
##################################################################
147+
## The end.
148+
##################################################################
149+
150+
test_done

0 commit comments

Comments
 (0)