зеркало из https://github.com/microsoft/git.git
Merge branch 'jh/status-v2-porcelain'
Enhance "git status --porcelain" output by collecting more data on the state of the index and the working tree files, which may further be used to teach git-prompt (in contrib/) to make fewer calls to git. * jh/status-v2-porcelain: status: unit tests for --porcelain=v2 test-lib-functions.sh: add lf_to_nul helper git-status.txt: describe --porcelain=v2 format status: print branch info with --porcelain=v2 --branch status: print per-file porcelain v2 status data status: collect per-file data for --porcelain=v2 status: support --porcelain[=<version>] status: cleanup API to wt_status_print status: rename long-format print routines
This commit is contained in:
Коммит
00d27937bf
|
@ -32,11 +32,14 @@ OPTIONS
|
|||
--branch::
|
||||
Show the branch and tracking info even in short-format.
|
||||
|
||||
--porcelain::
|
||||
--porcelain[=<version>]::
|
||||
Give the output in an easy-to-parse format for scripts.
|
||||
This is similar to the short output, but will remain stable
|
||||
across Git versions and regardless of user configuration. See
|
||||
below for details.
|
||||
+
|
||||
The version parameter is used to specify the format version.
|
||||
This is optional and defaults to the original version 'v1' format.
|
||||
|
||||
--long::
|
||||
Give the output in the long-format. This is the default.
|
||||
|
@ -96,7 +99,7 @@ configuration variable documented in linkgit:git-config[1].
|
|||
|
||||
-z::
|
||||
Terminate entries with NUL, instead of LF. This implies
|
||||
the `--porcelain` output format if no other format is given.
|
||||
the `--porcelain=v1` output format if no other format is given.
|
||||
|
||||
--column[=<options>]::
|
||||
--no-column::
|
||||
|
@ -180,12 +183,12 @@ in which case `XY` are `!!`.
|
|||
|
||||
If -b is used the short-format status is preceded by a line
|
||||
|
||||
## branchname tracking info
|
||||
## branchname tracking info
|
||||
|
||||
Porcelain Format
|
||||
~~~~~~~~~~~~~~~~
|
||||
Porcelain Format Version 1
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
The porcelain format is similar to the short format, but is guaranteed
|
||||
Version 1 porcelain format is similar to the short format, but is guaranteed
|
||||
not to change in a backwards-incompatible way between Git versions or
|
||||
based on user configuration. This makes it ideal for parsing by scripts.
|
||||
The description of the short format above also describes the porcelain
|
||||
|
@ -207,6 +210,124 @@ field from the first filename). Third, filenames containing special
|
|||
characters are not specially formatted; no quoting or
|
||||
backslash-escaping is performed.
|
||||
|
||||
Porcelain Format Version 2
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Version 2 format adds more detailed information about the state of
|
||||
the worktree and changed items. Version 2 also defines an extensible
|
||||
set of easy to parse optional headers.
|
||||
|
||||
Header lines start with "#" and are added in response to specific
|
||||
command line arguments. Parsers should ignore headers they
|
||||
don't recognize.
|
||||
|
||||
### Branch Headers
|
||||
|
||||
If `--branch` is given, a series of header lines are printed with
|
||||
information about the current branch.
|
||||
|
||||
Line Notes
|
||||
------------------------------------------------------------
|
||||
# branch.oid <commit> | (initial) Current commit.
|
||||
# branch.head <branch> | (detached) Current branch.
|
||||
# branch.upstream <upstream_branch> If upstream is set.
|
||||
# branch.ab +<ahead> -<behind> If upstream is set and
|
||||
the commit is present.
|
||||
------------------------------------------------------------
|
||||
|
||||
### Changed Tracked Entries
|
||||
|
||||
Following the headers, a series of lines are printed for tracked
|
||||
entries. One of three different line formats may be used to describe
|
||||
an entry depending on the type of change. Tracked entries are printed
|
||||
in an undefined order; parsers should allow for a mixture of the 3
|
||||
line types in any order.
|
||||
|
||||
Ordinary changed entries have the following format:
|
||||
|
||||
1 <XY> <sub> <mH> <mI> <mW> <hH> <hI> <path>
|
||||
|
||||
Renamed or copied entries have the following format:
|
||||
|
||||
2 <XY> <sub> <mH> <mI> <mW> <hH> <hI> <X><score> <path><sep><origPath>
|
||||
|
||||
Field Meaning
|
||||
--------------------------------------------------------
|
||||
<XY> A 2 character field containing the staged and
|
||||
unstaged XY values described in the short format,
|
||||
with unchanged indicated by a "." rather than
|
||||
a space.
|
||||
<sub> A 4 character field describing the submodule state.
|
||||
"N..." when the entry is not a submodule.
|
||||
"S<c><m><u>" when the entry is a submodule.
|
||||
<c> is "C" if the commit changed; otherwise ".".
|
||||
<m> is "M" if it has tracked changes; otherwise ".".
|
||||
<u> is "U" if there are untracked changes; otherwise ".".
|
||||
<mH> The octal file mode in HEAD.
|
||||
<mI> The octal file mode in the index.
|
||||
<mW> The octal file mode in the worktree.
|
||||
<hH> The object name in HEAD.
|
||||
<hI> The object name in the index.
|
||||
<X><score> The rename or copy score (denoting the percentage
|
||||
of similarity between the source and target of the
|
||||
move or copy). For example "R100" or "C75".
|
||||
<path> The pathname. In a renamed/copied entry, this
|
||||
is the path in the index and in the working tree.
|
||||
<sep> When the `-z` option is used, the 2 pathnames are separated
|
||||
with a NUL (ASCII 0x00) byte; otherwise, a tab (ASCII 0x09)
|
||||
byte separates them.
|
||||
<origPath> The pathname in the commit at HEAD. This is only
|
||||
present in a renamed/copied entry, and tells
|
||||
where the renamed/copied contents came from.
|
||||
--------------------------------------------------------
|
||||
|
||||
Unmerged entries have the following format; the first character is
|
||||
a "u" to distinguish from ordinary changed entries.
|
||||
|
||||
u <xy> <sub> <m1> <m2> <m3> <mW> <h1> <h2> <h3> <path>
|
||||
|
||||
Field Meaning
|
||||
--------------------------------------------------------
|
||||
<XY> A 2 character field describing the conflict type
|
||||
as described in the short format.
|
||||
<sub> A 4 character field describing the submodule state
|
||||
as described above.
|
||||
<m1> The octal file mode in stage 1.
|
||||
<m2> The octal file mode in stage 2.
|
||||
<m3> The octal file mode in stage 3.
|
||||
<mW> The octal file mode in the worktree.
|
||||
<h1> The object name in stage 1.
|
||||
<h2> The object name in stage 2.
|
||||
<h3> The object name in stage 3.
|
||||
<path> The pathname.
|
||||
--------------------------------------------------------
|
||||
|
||||
### Other Items
|
||||
|
||||
Following the tracked entries (and if requested), a series of
|
||||
lines will be printed for untracked and then ignored items
|
||||
found in the worktree.
|
||||
|
||||
Untracked items have the following format:
|
||||
|
||||
? <path>
|
||||
|
||||
Ignored items have the following format:
|
||||
|
||||
! <path>
|
||||
|
||||
### Pathname Format Notes and -z
|
||||
|
||||
When the `-z` option is given, pathnames are printed as is and
|
||||
without any quoting and lines are terminated with a NUL (ASCII 0x00)
|
||||
byte.
|
||||
|
||||
Otherwise, all pathnames will be "C-quoted" if they contain any tab,
|
||||
linefeed, double quote, or backslash characters. In C-quoting, these
|
||||
characters will be replaced with the corresponding C-style escape
|
||||
sequences and the resulting pathname will be double quoted.
|
||||
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
|
||||
|
|
|
@ -142,14 +142,24 @@ static int show_ignored_in_status, have_option_m;
|
|||
static const char *only_include_assumed;
|
||||
static struct strbuf message = STRBUF_INIT;
|
||||
|
||||
static enum status_format {
|
||||
STATUS_FORMAT_NONE = 0,
|
||||
STATUS_FORMAT_LONG,
|
||||
STATUS_FORMAT_SHORT,
|
||||
STATUS_FORMAT_PORCELAIN,
|
||||
static enum wt_status_format status_format = STATUS_FORMAT_UNSPECIFIED;
|
||||
|
||||
STATUS_FORMAT_UNSPECIFIED
|
||||
} status_format = STATUS_FORMAT_UNSPECIFIED;
|
||||
static int opt_parse_porcelain(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
enum wt_status_format *value = (enum wt_status_format *)opt->value;
|
||||
if (unset)
|
||||
*value = STATUS_FORMAT_NONE;
|
||||
else if (!arg)
|
||||
*value = STATUS_FORMAT_PORCELAIN;
|
||||
else if (!strcmp(arg, "v1") || !strcmp(arg, "1"))
|
||||
*value = STATUS_FORMAT_PORCELAIN;
|
||||
else if (!strcmp(arg, "v2") || !strcmp(arg, "2"))
|
||||
*value = STATUS_FORMAT_PORCELAIN_V2;
|
||||
else
|
||||
die("unsupported porcelain version '%s'", arg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int opt_parse_m(const struct option *opt, const char *arg, int unset)
|
||||
{
|
||||
|
@ -500,24 +510,13 @@ static int run_status(FILE *fp, const char *index_file, const char *prefix, int
|
|||
s->fp = fp;
|
||||
s->nowarn = nowarn;
|
||||
s->is_initial = get_sha1(s->reference, sha1) ? 1 : 0;
|
||||
if (!s->is_initial)
|
||||
hashcpy(s->sha1_commit, sha1);
|
||||
s->status_format = status_format;
|
||||
s->ignore_submodule_arg = ignore_submodule_arg;
|
||||
|
||||
wt_status_collect(s);
|
||||
|
||||
switch (status_format) {
|
||||
case STATUS_FORMAT_SHORT:
|
||||
wt_shortstatus_print(s);
|
||||
break;
|
||||
case STATUS_FORMAT_PORCELAIN:
|
||||
wt_porcelain_print(s);
|
||||
break;
|
||||
case STATUS_FORMAT_UNSPECIFIED:
|
||||
die("BUG: finalize_deferred_config() should have been called");
|
||||
break;
|
||||
case STATUS_FORMAT_NONE:
|
||||
case STATUS_FORMAT_LONG:
|
||||
wt_status_print(s);
|
||||
break;
|
||||
}
|
||||
wt_status_print(s);
|
||||
|
||||
return s->commitable;
|
||||
}
|
||||
|
@ -1099,7 +1098,7 @@ static const char *read_commit_message(const char *name)
|
|||
* is not in effect here.
|
||||
*/
|
||||
static struct status_deferred_config {
|
||||
enum status_format status_format;
|
||||
enum wt_status_format status_format;
|
||||
int show_branch;
|
||||
} status_deferred_config = {
|
||||
STATUS_FORMAT_UNSPECIFIED,
|
||||
|
@ -1109,6 +1108,7 @@ static struct status_deferred_config {
|
|||
static void finalize_deferred_config(struct wt_status *s)
|
||||
{
|
||||
int use_deferred_config = (status_format != STATUS_FORMAT_PORCELAIN &&
|
||||
status_format != STATUS_FORMAT_PORCELAIN_V2 &&
|
||||
!s->null_termination);
|
||||
|
||||
if (s->null_termination) {
|
||||
|
@ -1336,9 +1336,9 @@ int cmd_status(int argc, const char **argv, const char *prefix)
|
|||
N_("show status concisely"), STATUS_FORMAT_SHORT),
|
||||
OPT_BOOL('b', "branch", &s.show_branch,
|
||||
N_("show branch information")),
|
||||
OPT_SET_INT(0, "porcelain", &status_format,
|
||||
N_("machine-readable output"),
|
||||
STATUS_FORMAT_PORCELAIN),
|
||||
{ OPTION_CALLBACK, 0, "porcelain", &status_format,
|
||||
N_("version"), N_("machine-readable output"),
|
||||
PARSE_OPT_OPTARG, opt_parse_porcelain },
|
||||
OPT_SET_INT(0, "long", &status_format,
|
||||
N_("show status in long format (default)"),
|
||||
STATUS_FORMAT_LONG),
|
||||
|
@ -1380,7 +1380,13 @@ int cmd_status(int argc, const char **argv, const char *prefix)
|
|||
fd = hold_locked_index(&index_lock, 0);
|
||||
|
||||
s.is_initial = get_sha1(s.reference, sha1) ? 1 : 0;
|
||||
if (!s.is_initial)
|
||||
hashcpy(s.sha1_commit, sha1);
|
||||
|
||||
s.ignore_submodule_arg = ignore_submodule_arg;
|
||||
s.status_format = status_format;
|
||||
s.verbose = verbose;
|
||||
|
||||
wt_status_collect(&s);
|
||||
|
||||
if (0 <= fd)
|
||||
|
@ -1389,23 +1395,7 @@ int cmd_status(int argc, const char **argv, const char *prefix)
|
|||
if (s.relative_paths)
|
||||
s.prefix = prefix;
|
||||
|
||||
switch (status_format) {
|
||||
case STATUS_FORMAT_SHORT:
|
||||
wt_shortstatus_print(&s);
|
||||
break;
|
||||
case STATUS_FORMAT_PORCELAIN:
|
||||
wt_porcelain_print(&s);
|
||||
break;
|
||||
case STATUS_FORMAT_UNSPECIFIED:
|
||||
die("BUG: finalize_deferred_config() should have been called");
|
||||
break;
|
||||
case STATUS_FORMAT_NONE:
|
||||
case STATUS_FORMAT_LONG:
|
||||
s.verbose = verbose;
|
||||
s.ignore_submodule_arg = ignore_submodule_arg;
|
||||
wt_status_print(&s);
|
||||
break;
|
||||
}
|
||||
wt_status_print(&s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -232,4 +232,25 @@ test_expect_success 'status --branch with detached HEAD' '
|
|||
test_i18ncmp expected actual
|
||||
'
|
||||
|
||||
## Duplicate the above test and verify --porcelain=v1 arg parsing.
|
||||
test_expect_success 'status --porcelain=v1 --branch with detached HEAD' '
|
||||
git reset --hard &&
|
||||
git checkout master^0 &&
|
||||
git status --branch --porcelain=v1 >actual &&
|
||||
cat >expected <<-EOF &&
|
||||
## HEAD (no branch)
|
||||
?? .gitconfig
|
||||
?? actual
|
||||
?? expect
|
||||
?? expected
|
||||
?? mdconflict/
|
||||
EOF
|
||||
test_i18ncmp expected actual
|
||||
'
|
||||
|
||||
## Verify parser error on invalid --porcelain argument.
|
||||
test_expect_success 'status --porcelain=bogus' '
|
||||
test_must_fail git status --porcelain=bogus
|
||||
'
|
||||
|
||||
test_done
|
||||
|
|
|
@ -0,0 +1,593 @@
|
|||
#!/bin/sh
|
||||
|
||||
test_description='git status --porcelain=v2
|
||||
|
||||
This test exercises porcelain V2 output for git status.'
|
||||
|
||||
|
||||
. ./test-lib.sh
|
||||
|
||||
|
||||
test_expect_success setup '
|
||||
test_tick &&
|
||||
git config core.autocrlf false &&
|
||||
echo x >file_x &&
|
||||
echo y >file_y &&
|
||||
echo z >file_z &&
|
||||
mkdir dir1 &&
|
||||
echo a >dir1/file_a &&
|
||||
echo b >dir1/file_b
|
||||
'
|
||||
|
||||
test_expect_success 'before initial commit, nothing added, only untracked' '
|
||||
cat >expect <<-EOF &&
|
||||
# branch.oid (initial)
|
||||
# branch.head master
|
||||
? actual
|
||||
? dir1/
|
||||
? expect
|
||||
? file_x
|
||||
? file_y
|
||||
? file_z
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch --untracked-files=normal >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'before initial commit, things added' '
|
||||
git add file_x file_y file_z dir1 &&
|
||||
OID_A=$(git hash-object -t blob -- dir1/file_a) &&
|
||||
OID_B=$(git hash-object -t blob -- dir1/file_b) &&
|
||||
OID_X=$(git hash-object -t blob -- file_x) &&
|
||||
OID_Y=$(git hash-object -t blob -- file_y) &&
|
||||
OID_Z=$(git hash-object -t blob -- file_z) &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
# branch.oid (initial)
|
||||
# branch.head master
|
||||
1 A. N... 000000 100644 100644 $_z40 $OID_A dir1/file_a
|
||||
1 A. N... 000000 100644 100644 $_z40 $OID_B dir1/file_b
|
||||
1 A. N... 000000 100644 100644 $_z40 $OID_X file_x
|
||||
1 A. N... 000000 100644 100644 $_z40 $OID_Y file_y
|
||||
1 A. N... 000000 100644 100644 $_z40 $OID_Z file_z
|
||||
? actual
|
||||
? expect
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch --untracked-files=all >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'before initial commit, things added (-z)' '
|
||||
lf_to_nul >expect <<-EOF &&
|
||||
# branch.oid (initial)
|
||||
# branch.head master
|
||||
1 A. N... 000000 100644 100644 $_z40 $OID_A dir1/file_a
|
||||
1 A. N... 000000 100644 100644 $_z40 $OID_B dir1/file_b
|
||||
1 A. N... 000000 100644 100644 $_z40 $OID_X file_x
|
||||
1 A. N... 000000 100644 100644 $_z40 $OID_Y file_y
|
||||
1 A. N... 000000 100644 100644 $_z40 $OID_Z file_z
|
||||
? actual
|
||||
? expect
|
||||
EOF
|
||||
|
||||
git status -z --porcelain=v2 --branch --untracked-files=all >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'make first commit, comfirm HEAD oid and branch' '
|
||||
git commit -m initial &&
|
||||
H0=$(git rev-parse HEAD) &&
|
||||
cat >expect <<-EOF &&
|
||||
# branch.oid $H0
|
||||
# branch.head master
|
||||
? actual
|
||||
? expect
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch --untracked-files=all >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'after first commit, create unstaged changes' '
|
||||
echo x >>file_x &&
|
||||
OID_X1=$(git hash-object -t blob -- file_x) &&
|
||||
rm file_z &&
|
||||
H0=$(git rev-parse HEAD) &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
# branch.oid $H0
|
||||
# branch.head master
|
||||
1 .M N... 100644 100644 100644 $OID_X $OID_X file_x
|
||||
1 .D N... 100644 100644 000000 $OID_Z $OID_Z file_z
|
||||
? actual
|
||||
? expect
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch --untracked-files=all >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'after first commit but omit untracked files and branch' '
|
||||
cat >expect <<-EOF &&
|
||||
1 .M N... 100644 100644 100644 $OID_X $OID_X file_x
|
||||
1 .D N... 100644 100644 000000 $OID_Z $OID_Z file_z
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --untracked-files=no >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'after first commit, stage existing changes' '
|
||||
git add file_x &&
|
||||
git rm file_z &&
|
||||
H0=$(git rev-parse HEAD) &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
# branch.oid $H0
|
||||
# branch.head master
|
||||
1 M. N... 100644 100644 100644 $OID_X $OID_X1 file_x
|
||||
1 D. N... 100644 000000 000000 $OID_Z $_z40 file_z
|
||||
? actual
|
||||
? expect
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch --untracked-files=all >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'rename causes 2 path lines' '
|
||||
git mv file_y renamed_y &&
|
||||
H0=$(git rev-parse HEAD) &&
|
||||
|
||||
q_to_tab >expect <<-EOF &&
|
||||
# branch.oid $H0
|
||||
# branch.head master
|
||||
1 M. N... 100644 100644 100644 $OID_X $OID_X1 file_x
|
||||
1 D. N... 100644 000000 000000 $OID_Z $_z40 file_z
|
||||
2 R. N... 100644 100644 100644 $OID_Y $OID_Y R100 renamed_yQfile_y
|
||||
? actual
|
||||
? expect
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch --untracked-files=all >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'rename causes 2 path lines (-z)' '
|
||||
H0=$(git rev-parse HEAD) &&
|
||||
|
||||
## Lines use NUL path separator and line terminator, so double transform here.
|
||||
q_to_nul <<-EOF | lf_to_nul >expect &&
|
||||
# branch.oid $H0
|
||||
# branch.head master
|
||||
1 M. N... 100644 100644 100644 $OID_X $OID_X1 file_x
|
||||
1 D. N... 100644 000000 000000 $OID_Z $_z40 file_z
|
||||
2 R. N... 100644 100644 100644 $OID_Y $OID_Y R100 renamed_yQfile_y
|
||||
? actual
|
||||
? expect
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch --untracked-files=all -z >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'make second commit, confirm clean and new HEAD oid' '
|
||||
git commit -m second &&
|
||||
H1=$(git rev-parse HEAD) &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
# branch.oid $H1
|
||||
# branch.head master
|
||||
? actual
|
||||
? expect
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch --untracked-files=all >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'confirm ignored files are not printed' '
|
||||
test_when_finished "rm -f x.ign .gitignore" &&
|
||||
echo x.ign >.gitignore &&
|
||||
echo "ignore me" >x.ign &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
? .gitignore
|
||||
? actual
|
||||
? expect
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --untracked-files=all >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'ignored files are printed with --ignored' '
|
||||
test_when_finished "rm -f x.ign .gitignore" &&
|
||||
echo x.ign >.gitignore &&
|
||||
echo "ignore me" >x.ign &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
? .gitignore
|
||||
? actual
|
||||
? expect
|
||||
! x.ign
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --ignored --untracked-files=all >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'create and commit permanent ignore file' '
|
||||
cat >.gitignore <<-EOF &&
|
||||
actual*
|
||||
expect*
|
||||
EOF
|
||||
|
||||
git add .gitignore &&
|
||||
git commit -m ignore_trash &&
|
||||
H1=$(git rev-parse HEAD) &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
# branch.oid $H1
|
||||
# branch.head master
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'verify --intent-to-add output' '
|
||||
test_when_finished "git rm -f intent1.add intent2.add" &&
|
||||
touch intent1.add &&
|
||||
echo test >intent2.add &&
|
||||
|
||||
git add --intent-to-add intent1.add intent2.add &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
1 AM N... 000000 100644 100644 $_z40 $EMPTY_BLOB intent1.add
|
||||
1 AM N... 000000 100644 100644 $_z40 $EMPTY_BLOB intent2.add
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'verify AA (add-add) conflict' '
|
||||
test_when_finished "git reset --hard" &&
|
||||
|
||||
git branch AA_A master &&
|
||||
git checkout AA_A &&
|
||||
echo "Branch AA_A" >conflict.txt &&
|
||||
OID_AA_A=$(git hash-object -t blob -- conflict.txt) &&
|
||||
git add conflict.txt &&
|
||||
git commit -m "branch aa_a" &&
|
||||
|
||||
git branch AA_B master &&
|
||||
git checkout AA_B &&
|
||||
echo "Branch AA_B" >conflict.txt &&
|
||||
OID_AA_B=$(git hash-object -t blob -- conflict.txt) &&
|
||||
git add conflict.txt &&
|
||||
git commit -m "branch aa_b" &&
|
||||
|
||||
git branch AA_M AA_B &&
|
||||
git checkout AA_M &&
|
||||
test_must_fail git merge AA_A &&
|
||||
|
||||
HM=$(git rev-parse HEAD) &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
# branch.oid $HM
|
||||
# branch.head AA_M
|
||||
u AA N... 000000 100644 100644 100644 $_z40 $OID_AA_B $OID_AA_A conflict.txt
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch --untracked-files=all >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'verify UU (edit-edit) conflict' '
|
||||
test_when_finished "git reset --hard" &&
|
||||
|
||||
git branch UU_ANC master &&
|
||||
git checkout UU_ANC &&
|
||||
echo "Ancestor" >conflict.txt &&
|
||||
OID_UU_ANC=$(git hash-object -t blob -- conflict.txt) &&
|
||||
git add conflict.txt &&
|
||||
git commit -m "UU_ANC" &&
|
||||
|
||||
git branch UU_A UU_ANC &&
|
||||
git checkout UU_A &&
|
||||
echo "Branch UU_A" >conflict.txt &&
|
||||
OID_UU_A=$(git hash-object -t blob -- conflict.txt) &&
|
||||
git add conflict.txt &&
|
||||
git commit -m "branch uu_a" &&
|
||||
|
||||
git branch UU_B UU_ANC &&
|
||||
git checkout UU_B &&
|
||||
echo "Branch UU_B" >conflict.txt &&
|
||||
OID_UU_B=$(git hash-object -t blob -- conflict.txt) &&
|
||||
git add conflict.txt &&
|
||||
git commit -m "branch uu_b" &&
|
||||
|
||||
git branch UU_M UU_B &&
|
||||
git checkout UU_M &&
|
||||
test_must_fail git merge UU_A &&
|
||||
|
||||
HM=$(git rev-parse HEAD) &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
# branch.oid $HM
|
||||
# branch.head UU_M
|
||||
u UU N... 100644 100644 100644 100644 $OID_UU_ANC $OID_UU_B $OID_UU_A conflict.txt
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch --untracked-files=all >actual &&
|
||||
test_cmp expect actual
|
||||
'
|
||||
|
||||
test_expect_success 'verify upstream fields in branch header' '
|
||||
git checkout master &&
|
||||
test_when_finished "rm -rf sub_repo" &&
|
||||
git clone . sub_repo &&
|
||||
(
|
||||
## Confirm local master tracks remote master.
|
||||
cd sub_repo &&
|
||||
HUF=$(git rev-parse HEAD) &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
# branch.oid $HUF
|
||||
# branch.head master
|
||||
# branch.upstream origin/master
|
||||
# branch.ab +0 -0
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch --untracked-files=all >actual &&
|
||||
test_cmp expect actual &&
|
||||
|
||||
## Test ahead/behind.
|
||||
echo xyz >file_xyz &&
|
||||
git add file_xyz &&
|
||||
git commit -m xyz &&
|
||||
|
||||
HUF=$(git rev-parse HEAD) &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
# branch.oid $HUF
|
||||
# branch.head master
|
||||
# branch.upstream origin/master
|
||||
# branch.ab +1 -0
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch --untracked-files=all >actual &&
|
||||
test_cmp expect actual &&
|
||||
|
||||
## Repeat the above but without --branch.
|
||||
cat >expect <<-EOF &&
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --untracked-files=all >actual &&
|
||||
test_cmp expect actual &&
|
||||
|
||||
## Test upstream-gone case. Fake this by pointing origin/master at
|
||||
## a non-existing commit.
|
||||
OLD=$(git rev-parse origin/master) &&
|
||||
NEW=$_z40 &&
|
||||
mv .git/packed-refs .git/old-packed-refs &&
|
||||
sed "s/$OLD/$NEW/g" <.git/old-packed-refs >.git/packed-refs &&
|
||||
|
||||
HUF=$(git rev-parse HEAD) &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
# branch.oid $HUF
|
||||
# branch.head master
|
||||
# branch.upstream origin/master
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch --untracked-files=all >actual &&
|
||||
test_cmp expect actual
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'create and add submodule, submodule appears clean (A. S...)' '
|
||||
git checkout master &&
|
||||
git clone . sub_repo &&
|
||||
git clone . super_repo &&
|
||||
( cd super_repo &&
|
||||
git submodule add ../sub_repo sub1 &&
|
||||
|
||||
## Confirm stage/add of clean submodule.
|
||||
HMOD=$(git hash-object -t blob -- .gitmodules) &&
|
||||
HSUP=$(git rev-parse HEAD) &&
|
||||
HSUB=$HSUP &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
# branch.oid $HSUP
|
||||
# branch.head master
|
||||
# branch.upstream origin/master
|
||||
# branch.ab +0 -0
|
||||
1 A. N... 000000 100644 100644 $_z40 $HMOD .gitmodules
|
||||
1 A. S... 000000 160000 160000 $_z40 $HSUB sub1
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch --untracked-files=all >actual &&
|
||||
test_cmp expect actual
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'untracked changes in added submodule (AM S..U)' '
|
||||
( cd super_repo &&
|
||||
## create untracked file in the submodule.
|
||||
( cd sub1 &&
|
||||
echo "xxxx" >file_in_sub
|
||||
) &&
|
||||
|
||||
HMOD=$(git hash-object -t blob -- .gitmodules) &&
|
||||
HSUP=$(git rev-parse HEAD) &&
|
||||
HSUB=$HSUP &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
# branch.oid $HSUP
|
||||
# branch.head master
|
||||
# branch.upstream origin/master
|
||||
# branch.ab +0 -0
|
||||
1 A. N... 000000 100644 100644 $_z40 $HMOD .gitmodules
|
||||
1 AM S..U 000000 160000 160000 $_z40 $HSUB sub1
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch --untracked-files=all >actual &&
|
||||
test_cmp expect actual
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'staged changes in added submodule (AM S.M.)' '
|
||||
( cd super_repo &&
|
||||
## stage the changes in the submodule.
|
||||
( cd sub1 &&
|
||||
git add file_in_sub
|
||||
) &&
|
||||
|
||||
HMOD=$(git hash-object -t blob -- .gitmodules) &&
|
||||
HSUP=$(git rev-parse HEAD) &&
|
||||
HSUB=$HSUP &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
# branch.oid $HSUP
|
||||
# branch.head master
|
||||
# branch.upstream origin/master
|
||||
# branch.ab +0 -0
|
||||
1 A. N... 000000 100644 100644 $_z40 $HMOD .gitmodules
|
||||
1 AM S.M. 000000 160000 160000 $_z40 $HSUB sub1
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch --untracked-files=all >actual &&
|
||||
test_cmp expect actual
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'staged and unstaged changes in added (AM S.M.)' '
|
||||
( cd super_repo &&
|
||||
( cd sub1 &&
|
||||
## make additional unstaged changes (on the same file) in the submodule.
|
||||
## This does not cause us to get S.MU (because the submodule does not report
|
||||
## a "?" line for the unstaged changes).
|
||||
echo "more changes" >>file_in_sub
|
||||
) &&
|
||||
|
||||
HMOD=$(git hash-object -t blob -- .gitmodules) &&
|
||||
HSUP=$(git rev-parse HEAD) &&
|
||||
HSUB=$HSUP &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
# branch.oid $HSUP
|
||||
# branch.head master
|
||||
# branch.upstream origin/master
|
||||
# branch.ab +0 -0
|
||||
1 A. N... 000000 100644 100644 $_z40 $HMOD .gitmodules
|
||||
1 AM S.M. 000000 160000 160000 $_z40 $HSUB sub1
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch --untracked-files=all >actual &&
|
||||
test_cmp expect actual
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'staged and untracked changes in added submodule (AM S.MU)' '
|
||||
( cd super_repo &&
|
||||
( cd sub1 &&
|
||||
## stage new changes in tracked file.
|
||||
git add file_in_sub &&
|
||||
## create new untracked file.
|
||||
echo "yyyy" >>another_file_in_sub
|
||||
) &&
|
||||
|
||||
HMOD=$(git hash-object -t blob -- .gitmodules) &&
|
||||
HSUP=$(git rev-parse HEAD) &&
|
||||
HSUB=$HSUP &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
# branch.oid $HSUP
|
||||
# branch.head master
|
||||
# branch.upstream origin/master
|
||||
# branch.ab +0 -0
|
||||
1 A. N... 000000 100644 100644 $_z40 $HMOD .gitmodules
|
||||
1 AM S.MU 000000 160000 160000 $_z40 $HSUB sub1
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch --untracked-files=all >actual &&
|
||||
test_cmp expect actual
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'commit within the submodule appears as new commit in super (AM SC..)' '
|
||||
( cd super_repo &&
|
||||
( cd sub1 &&
|
||||
## Make a new commit in the submodule.
|
||||
git add file_in_sub &&
|
||||
rm -f another_file_in_sub &&
|
||||
git commit -m "new commit"
|
||||
) &&
|
||||
|
||||
HMOD=$(git hash-object -t blob -- .gitmodules) &&
|
||||
HSUP=$(git rev-parse HEAD) &&
|
||||
HSUB=$HSUP &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
# branch.oid $HSUP
|
||||
# branch.head master
|
||||
# branch.upstream origin/master
|
||||
# branch.ab +0 -0
|
||||
1 A. N... 000000 100644 100644 $_z40 $HMOD .gitmodules
|
||||
1 AM SC.. 000000 160000 160000 $_z40 $HSUB sub1
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch --untracked-files=all >actual &&
|
||||
test_cmp expect actual
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'stage submodule in super and commit' '
|
||||
( cd super_repo &&
|
||||
## Stage the new submodule commit in the super.
|
||||
git add sub1 &&
|
||||
## Commit the super so that the sub no longer appears as added.
|
||||
git commit -m "super commit" &&
|
||||
|
||||
HSUP=$(git rev-parse HEAD) &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
# branch.oid $HSUP
|
||||
# branch.head master
|
||||
# branch.upstream origin/master
|
||||
# branch.ab +1 -0
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch --untracked-files=all >actual &&
|
||||
test_cmp expect actual
|
||||
)
|
||||
'
|
||||
|
||||
test_expect_success 'make unstaged changes in existing submodule (.M S.M.)' '
|
||||
( cd super_repo &&
|
||||
( cd sub1 &&
|
||||
echo "zzzz" >>file_in_sub
|
||||
) &&
|
||||
|
||||
HSUP=$(git rev-parse HEAD) &&
|
||||
HSUB=$(cd sub1 && git rev-parse HEAD) &&
|
||||
|
||||
cat >expect <<-EOF &&
|
||||
# branch.oid $HSUP
|
||||
# branch.head master
|
||||
# branch.upstream origin/master
|
||||
# branch.ab +1 -0
|
||||
1 .M S.M. 160000 160000 160000 $HSUB $HSUB sub1
|
||||
EOF
|
||||
|
||||
git status --porcelain=v2 --branch --untracked-files=all >actual &&
|
||||
test_cmp expect actual
|
||||
)
|
||||
'
|
||||
|
||||
test_done
|
|
@ -81,6 +81,10 @@ test_decode_color () {
|
|||
'
|
||||
}
|
||||
|
||||
lf_to_nul () {
|
||||
perl -pe 'y/\012/\000/'
|
||||
}
|
||||
|
||||
nul_to_q () {
|
||||
perl -pe 'y/\000/Q/'
|
||||
}
|
||||
|
|
570
wt-status.c
570
wt-status.c
|
@ -139,7 +139,7 @@ void wt_status_prepare(struct wt_status *s)
|
|||
s->display_comment_prefix = 0;
|
||||
}
|
||||
|
||||
static void wt_status_print_unmerged_header(struct wt_status *s)
|
||||
static void wt_longstatus_print_unmerged_header(struct wt_status *s)
|
||||
{
|
||||
int i;
|
||||
int del_mod_conflict = 0;
|
||||
|
@ -191,7 +191,7 @@ static void wt_status_print_unmerged_header(struct wt_status *s)
|
|||
status_printf_ln(s, c, "%s", "");
|
||||
}
|
||||
|
||||
static void wt_status_print_cached_header(struct wt_status *s)
|
||||
static void wt_longstatus_print_cached_header(struct wt_status *s)
|
||||
{
|
||||
const char *c = color(WT_STATUS_HEADER, s);
|
||||
|
||||
|
@ -207,9 +207,9 @@ static void wt_status_print_cached_header(struct wt_status *s)
|
|||
status_printf_ln(s, c, "%s", "");
|
||||
}
|
||||
|
||||
static void wt_status_print_dirty_header(struct wt_status *s,
|
||||
int has_deleted,
|
||||
int has_dirty_submodules)
|
||||
static void wt_longstatus_print_dirty_header(struct wt_status *s,
|
||||
int has_deleted,
|
||||
int has_dirty_submodules)
|
||||
{
|
||||
const char *c = color(WT_STATUS_HEADER, s);
|
||||
|
||||
|
@ -226,9 +226,9 @@ static void wt_status_print_dirty_header(struct wt_status *s,
|
|||
status_printf_ln(s, c, "%s", "");
|
||||
}
|
||||
|
||||
static void wt_status_print_other_header(struct wt_status *s,
|
||||
const char *what,
|
||||
const char *how)
|
||||
static void wt_longstatus_print_other_header(struct wt_status *s,
|
||||
const char *what,
|
||||
const char *how)
|
||||
{
|
||||
const char *c = color(WT_STATUS_HEADER, s);
|
||||
status_printf_ln(s, c, "%s:", what);
|
||||
|
@ -238,7 +238,7 @@ static void wt_status_print_other_header(struct wt_status *s,
|
|||
status_printf_ln(s, c, "%s", "");
|
||||
}
|
||||
|
||||
static void wt_status_print_trailer(struct wt_status *s)
|
||||
static void wt_longstatus_print_trailer(struct wt_status *s)
|
||||
{
|
||||
status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
|
||||
}
|
||||
|
@ -304,8 +304,8 @@ static int maxwidth(const char *(*label)(int), int minval, int maxval)
|
|||
return result;
|
||||
}
|
||||
|
||||
static void wt_status_print_unmerged_data(struct wt_status *s,
|
||||
struct string_list_item *it)
|
||||
static void wt_longstatus_print_unmerged_data(struct wt_status *s,
|
||||
struct string_list_item *it)
|
||||
{
|
||||
const char *c = color(WT_STATUS_UNMERGED, s);
|
||||
struct wt_status_change_data *d = it->util;
|
||||
|
@ -331,9 +331,9 @@ static void wt_status_print_unmerged_data(struct wt_status *s,
|
|||
strbuf_release(&onebuf);
|
||||
}
|
||||
|
||||
static void wt_status_print_change_data(struct wt_status *s,
|
||||
int change_type,
|
||||
struct string_list_item *it)
|
||||
static void wt_longstatus_print_change_data(struct wt_status *s,
|
||||
int change_type,
|
||||
struct string_list_item *it)
|
||||
{
|
||||
struct wt_status_change_data *d = it->util;
|
||||
const char *c = color(change_type, s);
|
||||
|
@ -378,7 +378,7 @@ static void wt_status_print_change_data(struct wt_status *s,
|
|||
status = d->worktree_status;
|
||||
break;
|
||||
default:
|
||||
die("BUG: unhandled change_type %d in wt_status_print_change_data",
|
||||
die("BUG: unhandled change_type %d in wt_longstatus_print_change_data",
|
||||
change_type);
|
||||
}
|
||||
|
||||
|
@ -434,6 +434,31 @@ static void wt_status_collect_changed_cb(struct diff_queue_struct *q,
|
|||
if (S_ISGITLINK(p->two->mode))
|
||||
d->new_submodule_commits = !!oidcmp(&p->one->oid,
|
||||
&p->two->oid);
|
||||
|
||||
switch (p->status) {
|
||||
case DIFF_STATUS_ADDED:
|
||||
die("BUG: worktree status add???");
|
||||
break;
|
||||
|
||||
case DIFF_STATUS_DELETED:
|
||||
d->mode_index = p->one->mode;
|
||||
oidcpy(&d->oid_index, &p->one->oid);
|
||||
/* mode_worktree is zero for a delete. */
|
||||
break;
|
||||
|
||||
case DIFF_STATUS_MODIFIED:
|
||||
case DIFF_STATUS_TYPE_CHANGED:
|
||||
case DIFF_STATUS_UNMERGED:
|
||||
d->mode_index = p->one->mode;
|
||||
d->mode_worktree = p->two->mode;
|
||||
oidcpy(&d->oid_index, &p->one->oid);
|
||||
break;
|
||||
|
||||
case DIFF_STATUS_UNKNOWN:
|
||||
die("BUG: worktree status unknown???");
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -479,12 +504,36 @@ static void wt_status_collect_updated_cb(struct diff_queue_struct *q,
|
|||
if (!d->index_status)
|
||||
d->index_status = p->status;
|
||||
switch (p->status) {
|
||||
case DIFF_STATUS_ADDED:
|
||||
/* Leave {mode,oid}_head zero for an add. */
|
||||
d->mode_index = p->two->mode;
|
||||
oidcpy(&d->oid_index, &p->two->oid);
|
||||
break;
|
||||
case DIFF_STATUS_DELETED:
|
||||
d->mode_head = p->one->mode;
|
||||
oidcpy(&d->oid_head, &p->one->oid);
|
||||
/* Leave {mode,oid}_index zero for a delete. */
|
||||
break;
|
||||
|
||||
case DIFF_STATUS_COPIED:
|
||||
case DIFF_STATUS_RENAMED:
|
||||
d->head_path = xstrdup(p->one->path);
|
||||
d->score = p->score * 100 / MAX_SCORE;
|
||||
/* fallthru */
|
||||
case DIFF_STATUS_MODIFIED:
|
||||
case DIFF_STATUS_TYPE_CHANGED:
|
||||
d->mode_head = p->one->mode;
|
||||
d->mode_index = p->two->mode;
|
||||
oidcpy(&d->oid_head, &p->one->oid);
|
||||
oidcpy(&d->oid_index, &p->two->oid);
|
||||
break;
|
||||
case DIFF_STATUS_UNMERGED:
|
||||
d->stagemask = unmerged_mask(p->two->path);
|
||||
/*
|
||||
* Don't bother setting {mode,oid}_{head,index} since the print
|
||||
* code will output the stage values directly and not use the
|
||||
* values in these fields.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -565,9 +614,17 @@ static void wt_status_collect_changes_initial(struct wt_status *s)
|
|||
if (ce_stage(ce)) {
|
||||
d->index_status = DIFF_STATUS_UNMERGED;
|
||||
d->stagemask |= (1 << (ce_stage(ce) - 1));
|
||||
}
|
||||
else
|
||||
/*
|
||||
* Don't bother setting {mode,oid}_{head,index} since the print
|
||||
* code will output the stage values directly and not use the
|
||||
* values in these fields.
|
||||
*/
|
||||
} else {
|
||||
d->index_status = DIFF_STATUS_ADDED;
|
||||
/* Leave {mode,oid}_head zero for adds. */
|
||||
d->mode_index = ce->ce_mode;
|
||||
hashcpy(d->oid_index.hash, ce->sha1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -627,7 +684,7 @@ void wt_status_collect(struct wt_status *s)
|
|||
wt_status_collect_untracked(s);
|
||||
}
|
||||
|
||||
static void wt_status_print_unmerged(struct wt_status *s)
|
||||
static void wt_longstatus_print_unmerged(struct wt_status *s)
|
||||
{
|
||||
int shown_header = 0;
|
||||
int i;
|
||||
|
@ -640,17 +697,17 @@ static void wt_status_print_unmerged(struct wt_status *s)
|
|||
if (!d->stagemask)
|
||||
continue;
|
||||
if (!shown_header) {
|
||||
wt_status_print_unmerged_header(s);
|
||||
wt_longstatus_print_unmerged_header(s);
|
||||
shown_header = 1;
|
||||
}
|
||||
wt_status_print_unmerged_data(s, it);
|
||||
wt_longstatus_print_unmerged_data(s, it);
|
||||
}
|
||||
if (shown_header)
|
||||
wt_status_print_trailer(s);
|
||||
wt_longstatus_print_trailer(s);
|
||||
|
||||
}
|
||||
|
||||
static void wt_status_print_updated(struct wt_status *s)
|
||||
static void wt_longstatus_print_updated(struct wt_status *s)
|
||||
{
|
||||
int shown_header = 0;
|
||||
int i;
|
||||
|
@ -664,14 +721,14 @@ static void wt_status_print_updated(struct wt_status *s)
|
|||
d->index_status == DIFF_STATUS_UNMERGED)
|
||||
continue;
|
||||
if (!shown_header) {
|
||||
wt_status_print_cached_header(s);
|
||||
wt_longstatus_print_cached_header(s);
|
||||
s->commitable = 1;
|
||||
shown_header = 1;
|
||||
}
|
||||
wt_status_print_change_data(s, WT_STATUS_UPDATED, it);
|
||||
wt_longstatus_print_change_data(s, WT_STATUS_UPDATED, it);
|
||||
}
|
||||
if (shown_header)
|
||||
wt_status_print_trailer(s);
|
||||
wt_longstatus_print_trailer(s);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -703,7 +760,7 @@ static int wt_status_check_worktree_changes(struct wt_status *s,
|
|||
return changes;
|
||||
}
|
||||
|
||||
static void wt_status_print_changed(struct wt_status *s)
|
||||
static void wt_longstatus_print_changed(struct wt_status *s)
|
||||
{
|
||||
int i, dirty_submodules;
|
||||
int worktree_changes = wt_status_check_worktree_changes(s, &dirty_submodules);
|
||||
|
@ -711,7 +768,7 @@ static void wt_status_print_changed(struct wt_status *s)
|
|||
if (!worktree_changes)
|
||||
return;
|
||||
|
||||
wt_status_print_dirty_header(s, worktree_changes < 0, dirty_submodules);
|
||||
wt_longstatus_print_dirty_header(s, worktree_changes < 0, dirty_submodules);
|
||||
|
||||
for (i = 0; i < s->change.nr; i++) {
|
||||
struct wt_status_change_data *d;
|
||||
|
@ -721,12 +778,12 @@ static void wt_status_print_changed(struct wt_status *s)
|
|||
if (!d->worktree_status ||
|
||||
d->worktree_status == DIFF_STATUS_UNMERGED)
|
||||
continue;
|
||||
wt_status_print_change_data(s, WT_STATUS_CHANGED, it);
|
||||
wt_longstatus_print_change_data(s, WT_STATUS_CHANGED, it);
|
||||
}
|
||||
wt_status_print_trailer(s);
|
||||
wt_longstatus_print_trailer(s);
|
||||
}
|
||||
|
||||
static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitted)
|
||||
static void wt_longstatus_print_submodule_summary(struct wt_status *s, int uncommitted)
|
||||
{
|
||||
struct child_process sm_summary = CHILD_PROCESS_INIT;
|
||||
struct strbuf cmd_stdout = STRBUF_INIT;
|
||||
|
@ -772,10 +829,10 @@ static void wt_status_print_submodule_summary(struct wt_status *s, int uncommitt
|
|||
strbuf_release(&summary);
|
||||
}
|
||||
|
||||
static void wt_status_print_other(struct wt_status *s,
|
||||
struct string_list *l,
|
||||
const char *what,
|
||||
const char *how)
|
||||
static void wt_longstatus_print_other(struct wt_status *s,
|
||||
struct string_list *l,
|
||||
const char *what,
|
||||
const char *how)
|
||||
{
|
||||
int i;
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
|
@ -785,7 +842,7 @@ static void wt_status_print_other(struct wt_status *s,
|
|||
if (!l->nr)
|
||||
return;
|
||||
|
||||
wt_status_print_other_header(s, what, how);
|
||||
wt_longstatus_print_other_header(s, what, how);
|
||||
|
||||
for (i = 0; i < l->nr; i++) {
|
||||
struct string_list_item *it;
|
||||
|
@ -845,7 +902,7 @@ void wt_status_add_cut_line(FILE *fp)
|
|||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
static void wt_status_print_verbose(struct wt_status *s)
|
||||
static void wt_longstatus_print_verbose(struct wt_status *s)
|
||||
{
|
||||
struct rev_info rev;
|
||||
struct setup_revision_opt opt;
|
||||
|
@ -878,7 +935,7 @@ static void wt_status_print_verbose(struct wt_status *s)
|
|||
if (s->verbose > 1 && s->commitable) {
|
||||
/* print_updated() printed a header, so do we */
|
||||
if (s->fp != stdout)
|
||||
wt_status_print_trailer(s);
|
||||
wt_longstatus_print_trailer(s);
|
||||
status_printf_ln(s, c, _("Changes to be committed:"));
|
||||
rev.diffopt.a_prefix = "c/";
|
||||
rev.diffopt.b_prefix = "i/";
|
||||
|
@ -896,7 +953,7 @@ static void wt_status_print_verbose(struct wt_status *s)
|
|||
}
|
||||
}
|
||||
|
||||
static void wt_status_print_tracking(struct wt_status *s)
|
||||
static void wt_longstatus_print_tracking(struct wt_status *s)
|
||||
{
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
const char *cp, *ep, *branch_name;
|
||||
|
@ -962,7 +1019,7 @@ static void show_merge_in_progress(struct wt_status *s,
|
|||
status_printf_ln(s, color,
|
||||
_(" (use \"git commit\" to conclude merge)"));
|
||||
}
|
||||
wt_status_print_trailer(s);
|
||||
wt_longstatus_print_trailer(s);
|
||||
}
|
||||
|
||||
static void show_am_in_progress(struct wt_status *s,
|
||||
|
@ -983,7 +1040,7 @@ static void show_am_in_progress(struct wt_status *s,
|
|||
status_printf_ln(s, color,
|
||||
_(" (use \"git am --abort\" to restore the original branch)"));
|
||||
}
|
||||
wt_status_print_trailer(s);
|
||||
wt_longstatus_print_trailer(s);
|
||||
}
|
||||
|
||||
static char *read_line_from_git_path(const char *filename)
|
||||
|
@ -1207,7 +1264,7 @@ static void show_rebase_in_progress(struct wt_status *s,
|
|||
_(" (use \"git rebase --continue\" once you are satisfied with your changes)"));
|
||||
}
|
||||
}
|
||||
wt_status_print_trailer(s);
|
||||
wt_longstatus_print_trailer(s);
|
||||
}
|
||||
|
||||
static void show_cherry_pick_in_progress(struct wt_status *s,
|
||||
|
@ -1226,7 +1283,7 @@ static void show_cherry_pick_in_progress(struct wt_status *s,
|
|||
status_printf_ln(s, color,
|
||||
_(" (use \"git cherry-pick --abort\" to cancel the cherry-pick operation)"));
|
||||
}
|
||||
wt_status_print_trailer(s);
|
||||
wt_longstatus_print_trailer(s);
|
||||
}
|
||||
|
||||
static void show_revert_in_progress(struct wt_status *s,
|
||||
|
@ -1245,7 +1302,7 @@ static void show_revert_in_progress(struct wt_status *s,
|
|||
status_printf_ln(s, color,
|
||||
_(" (use \"git revert --abort\" to cancel the revert operation)"));
|
||||
}
|
||||
wt_status_print_trailer(s);
|
||||
wt_longstatus_print_trailer(s);
|
||||
}
|
||||
|
||||
static void show_bisect_in_progress(struct wt_status *s,
|
||||
|
@ -1262,7 +1319,7 @@ static void show_bisect_in_progress(struct wt_status *s,
|
|||
if (s->hints)
|
||||
status_printf_ln(s, color,
|
||||
_(" (use \"git bisect reset\" to get back to the original branch)"));
|
||||
wt_status_print_trailer(s);
|
||||
wt_longstatus_print_trailer(s);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1432,8 +1489,8 @@ void wt_status_get_state(struct wt_status_state *state,
|
|||
wt_status_get_detached_from(state);
|
||||
}
|
||||
|
||||
static void wt_status_print_state(struct wt_status *s,
|
||||
struct wt_status_state *state)
|
||||
static void wt_longstatus_print_state(struct wt_status *s,
|
||||
struct wt_status_state *state)
|
||||
{
|
||||
const char *state_color = color(WT_STATUS_HEADER, s);
|
||||
if (state->merge_in_progress)
|
||||
|
@ -1450,7 +1507,7 @@ static void wt_status_print_state(struct wt_status *s,
|
|||
show_bisect_in_progress(s, state, state_color);
|
||||
}
|
||||
|
||||
void wt_status_print(struct wt_status *s)
|
||||
static void wt_longstatus_print(struct wt_status *s)
|
||||
{
|
||||
const char *branch_color = color(WT_STATUS_ONBRANCH, s);
|
||||
const char *branch_status_color = color(WT_STATUS_HEADER, s);
|
||||
|
@ -1487,10 +1544,10 @@ void wt_status_print(struct wt_status *s)
|
|||
status_printf_more(s, branch_status_color, "%s", on_what);
|
||||
status_printf_more(s, branch_color, "%s\n", branch_name);
|
||||
if (!s->is_initial)
|
||||
wt_status_print_tracking(s);
|
||||
wt_longstatus_print_tracking(s);
|
||||
}
|
||||
|
||||
wt_status_print_state(s, &state);
|
||||
wt_longstatus_print_state(s, &state);
|
||||
free(state.branch);
|
||||
free(state.onto);
|
||||
free(state.detached_from);
|
||||
|
@ -1501,19 +1558,19 @@ void wt_status_print(struct wt_status *s)
|
|||
status_printf_ln(s, color(WT_STATUS_HEADER, s), "%s", "");
|
||||
}
|
||||
|
||||
wt_status_print_updated(s);
|
||||
wt_status_print_unmerged(s);
|
||||
wt_status_print_changed(s);
|
||||
wt_longstatus_print_updated(s);
|
||||
wt_longstatus_print_unmerged(s);
|
||||
wt_longstatus_print_changed(s);
|
||||
if (s->submodule_summary &&
|
||||
(!s->ignore_submodule_arg ||
|
||||
strcmp(s->ignore_submodule_arg, "all"))) {
|
||||
wt_status_print_submodule_summary(s, 0); /* staged */
|
||||
wt_status_print_submodule_summary(s, 1); /* unstaged */
|
||||
wt_longstatus_print_submodule_summary(s, 0); /* staged */
|
||||
wt_longstatus_print_submodule_summary(s, 1); /* unstaged */
|
||||
}
|
||||
if (s->show_untracked_files) {
|
||||
wt_status_print_other(s, &s->untracked, _("Untracked files"), "add");
|
||||
wt_longstatus_print_other(s, &s->untracked, _("Untracked files"), "add");
|
||||
if (s->show_ignored_files)
|
||||
wt_status_print_other(s, &s->ignored, _("Ignored files"), "add -f");
|
||||
wt_longstatus_print_other(s, &s->ignored, _("Ignored files"), "add -f");
|
||||
if (advice_status_u_option && 2000 < s->untracked_in_ms) {
|
||||
status_printf_ln(s, GIT_COLOR_NORMAL, "%s", "");
|
||||
status_printf_ln(s, GIT_COLOR_NORMAL,
|
||||
|
@ -1528,7 +1585,7 @@ void wt_status_print(struct wt_status *s)
|
|||
? _(" (use -u option to show untracked files)") : "");
|
||||
|
||||
if (s->verbose)
|
||||
wt_status_print_verbose(s);
|
||||
wt_longstatus_print_verbose(s);
|
||||
if (!s->commitable) {
|
||||
if (s->amend)
|
||||
status_printf_ln(s, GIT_COLOR_NORMAL, _("No changes"));
|
||||
|
@ -1717,7 +1774,7 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
|
|||
fputc(s->null_termination ? '\0' : '\n', s->fp);
|
||||
}
|
||||
|
||||
void wt_shortstatus_print(struct wt_status *s)
|
||||
static void wt_shortstatus_print(struct wt_status *s)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -1749,7 +1806,7 @@ void wt_shortstatus_print(struct wt_status *s)
|
|||
}
|
||||
}
|
||||
|
||||
void wt_porcelain_print(struct wt_status *s)
|
||||
static void wt_porcelain_print(struct wt_status *s)
|
||||
{
|
||||
s->use_color = 0;
|
||||
s->relative_paths = 0;
|
||||
|
@ -1757,3 +1814,398 @@ void wt_porcelain_print(struct wt_status *s)
|
|||
s->no_gettext = 1;
|
||||
wt_shortstatus_print(s);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print branch information for porcelain v2 output. These lines
|
||||
* are printed when the '--branch' parameter is given.
|
||||
*
|
||||
* # branch.oid <commit><eol>
|
||||
* # branch.head <head><eol>
|
||||
* [# branch.upstream <upstream><eol>
|
||||
* [# branch.ab +<ahead> -<behind><eol>]]
|
||||
*
|
||||
* <commit> ::= the current commit hash or the the literal
|
||||
* "(initial)" to indicate an initialized repo
|
||||
* with no commits.
|
||||
*
|
||||
* <head> ::= <branch_name> the current branch name or
|
||||
* "(detached)" literal when detached head or
|
||||
* "(unknown)" when something is wrong.
|
||||
*
|
||||
* <upstream> ::= the upstream branch name, when set.
|
||||
*
|
||||
* <ahead> ::= integer ahead value, when upstream set
|
||||
* and the commit is present (not gone).
|
||||
*
|
||||
* <behind> ::= integer behind value, when upstream set
|
||||
* and commit is present.
|
||||
*
|
||||
*
|
||||
* The end-of-line is defined by the -z flag.
|
||||
*
|
||||
* <eol> ::= NUL when -z,
|
||||
* LF when NOT -z.
|
||||
*
|
||||
*/
|
||||
static void wt_porcelain_v2_print_tracking(struct wt_status *s)
|
||||
{
|
||||
struct branch *branch;
|
||||
const char *base;
|
||||
const char *branch_name;
|
||||
struct wt_status_state state;
|
||||
int ab_info, nr_ahead, nr_behind;
|
||||
char eol = s->null_termination ? '\0' : '\n';
|
||||
|
||||
memset(&state, 0, sizeof(state));
|
||||
wt_status_get_state(&state, s->branch && !strcmp(s->branch, "HEAD"));
|
||||
|
||||
fprintf(s->fp, "# branch.oid %s%c",
|
||||
(s->is_initial ? "(initial)" : sha1_to_hex(s->sha1_commit)),
|
||||
eol);
|
||||
|
||||
if (!s->branch)
|
||||
fprintf(s->fp, "# branch.head %s%c", "(unknown)", eol);
|
||||
else {
|
||||
if (!strcmp(s->branch, "HEAD")) {
|
||||
fprintf(s->fp, "# branch.head %s%c", "(detached)", eol);
|
||||
|
||||
if (state.rebase_in_progress || state.rebase_interactive_in_progress)
|
||||
branch_name = state.onto;
|
||||
else if (state.detached_from)
|
||||
branch_name = state.detached_from;
|
||||
else
|
||||
branch_name = "";
|
||||
} else {
|
||||
branch_name = NULL;
|
||||
skip_prefix(s->branch, "refs/heads/", &branch_name);
|
||||
|
||||
fprintf(s->fp, "# branch.head %s%c", branch_name, eol);
|
||||
}
|
||||
|
||||
/* Lookup stats on the upstream tracking branch, if set. */
|
||||
branch = branch_get(branch_name);
|
||||
base = NULL;
|
||||
ab_info = (stat_tracking_info(branch, &nr_ahead, &nr_behind, &base) == 0);
|
||||
if (base) {
|
||||
base = shorten_unambiguous_ref(base, 0);
|
||||
fprintf(s->fp, "# branch.upstream %s%c", base, eol);
|
||||
free((char *)base);
|
||||
|
||||
if (ab_info)
|
||||
fprintf(s->fp, "# branch.ab +%d -%d%c", nr_ahead, nr_behind, eol);
|
||||
}
|
||||
}
|
||||
|
||||
free(state.branch);
|
||||
free(state.onto);
|
||||
free(state.detached_from);
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert various submodule status values into a
|
||||
* fixed-length string of characters in the buffer provided.
|
||||
*/
|
||||
static void wt_porcelain_v2_submodule_state(
|
||||
struct wt_status_change_data *d,
|
||||
char sub[5])
|
||||
{
|
||||
if (S_ISGITLINK(d->mode_head) ||
|
||||
S_ISGITLINK(d->mode_index) ||
|
||||
S_ISGITLINK(d->mode_worktree)) {
|
||||
sub[0] = 'S';
|
||||
sub[1] = d->new_submodule_commits ? 'C' : '.';
|
||||
sub[2] = (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED) ? 'M' : '.';
|
||||
sub[3] = (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED) ? 'U' : '.';
|
||||
} else {
|
||||
sub[0] = 'N';
|
||||
sub[1] = '.';
|
||||
sub[2] = '.';
|
||||
sub[3] = '.';
|
||||
}
|
||||
sub[4] = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Fix-up changed entries before we print them.
|
||||
*/
|
||||
static void wt_porcelain_v2_fix_up_changed(
|
||||
struct string_list_item *it,
|
||||
struct wt_status *s)
|
||||
{
|
||||
struct wt_status_change_data *d = it->util;
|
||||
|
||||
if (!d->index_status) {
|
||||
/*
|
||||
* This entry is unchanged in the index (relative to the head).
|
||||
* Therefore, the collect_updated_cb was never called for this
|
||||
* entry (during the head-vs-index scan) and so the head column
|
||||
* fields were never set.
|
||||
*
|
||||
* We must have data for the index column (from the
|
||||
* index-vs-worktree scan (otherwise, this entry should not be
|
||||
* in the list of changes)).
|
||||
*
|
||||
* Copy index column fields to the head column, so that our
|
||||
* output looks complete.
|
||||
*/
|
||||
assert(d->mode_head == 0);
|
||||
d->mode_head = d->mode_index;
|
||||
oidcpy(&d->oid_head, &d->oid_index);
|
||||
}
|
||||
|
||||
if (!d->worktree_status) {
|
||||
/*
|
||||
* This entry is unchanged in the worktree (relative to the index).
|
||||
* Therefore, the collect_changed_cb was never called for this entry
|
||||
* (during the index-vs-worktree scan) and so the worktree column
|
||||
* fields were never set.
|
||||
*
|
||||
* We must have data for the index column (from the head-vs-index
|
||||
* scan).
|
||||
*
|
||||
* Copy the index column fields to the worktree column so that
|
||||
* our output looks complete.
|
||||
*
|
||||
* Note that we only have a mode field in the worktree column
|
||||
* because the scan code tries really hard to not have to compute it.
|
||||
*/
|
||||
assert(d->mode_worktree == 0);
|
||||
d->mode_worktree = d->mode_index;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Print porcelain v2 info for tracked entries with changes.
|
||||
*/
|
||||
static void wt_porcelain_v2_print_changed_entry(
|
||||
struct string_list_item *it,
|
||||
struct wt_status *s)
|
||||
{
|
||||
struct wt_status_change_data *d = it->util;
|
||||
struct strbuf buf_index = STRBUF_INIT;
|
||||
struct strbuf buf_head = STRBUF_INIT;
|
||||
const char *path_index = NULL;
|
||||
const char *path_head = NULL;
|
||||
char key[3];
|
||||
char submodule_token[5];
|
||||
char sep_char, eol_char;
|
||||
|
||||
wt_porcelain_v2_fix_up_changed(it, s);
|
||||
wt_porcelain_v2_submodule_state(d, submodule_token);
|
||||
|
||||
key[0] = d->index_status ? d->index_status : '.';
|
||||
key[1] = d->worktree_status ? d->worktree_status : '.';
|
||||
key[2] = 0;
|
||||
|
||||
if (s->null_termination) {
|
||||
/*
|
||||
* In -z mode, we DO NOT C-quote pathnames. Current path is ALWAYS first.
|
||||
* A single NUL character separates them.
|
||||
*/
|
||||
sep_char = '\0';
|
||||
eol_char = '\0';
|
||||
path_index = it->string;
|
||||
path_head = d->head_path;
|
||||
} else {
|
||||
/*
|
||||
* Path(s) are C-quoted if necessary. Current path is ALWAYS first.
|
||||
* The source path is only present when necessary.
|
||||
* A single TAB separates them (because paths can contain spaces
|
||||
* which are not escaped and C-quoting does escape TAB characters).
|
||||
*/
|
||||
sep_char = '\t';
|
||||
eol_char = '\n';
|
||||
path_index = quote_path(it->string, s->prefix, &buf_index);
|
||||
if (d->head_path)
|
||||
path_head = quote_path(d->head_path, s->prefix, &buf_head);
|
||||
}
|
||||
|
||||
if (path_head)
|
||||
fprintf(s->fp, "2 %s %s %06o %06o %06o %s %s %c%d %s%c%s%c",
|
||||
key, submodule_token,
|
||||
d->mode_head, d->mode_index, d->mode_worktree,
|
||||
oid_to_hex(&d->oid_head), oid_to_hex(&d->oid_index),
|
||||
key[0], d->score,
|
||||
path_index, sep_char, path_head, eol_char);
|
||||
else
|
||||
fprintf(s->fp, "1 %s %s %06o %06o %06o %s %s %s%c",
|
||||
key, submodule_token,
|
||||
d->mode_head, d->mode_index, d->mode_worktree,
|
||||
oid_to_hex(&d->oid_head), oid_to_hex(&d->oid_index),
|
||||
path_index, eol_char);
|
||||
|
||||
strbuf_release(&buf_index);
|
||||
strbuf_release(&buf_head);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print porcelain v2 status info for unmerged entries.
|
||||
*/
|
||||
static void wt_porcelain_v2_print_unmerged_entry(
|
||||
struct string_list_item *it,
|
||||
struct wt_status *s)
|
||||
{
|
||||
struct wt_status_change_data *d = it->util;
|
||||
const struct cache_entry *ce;
|
||||
struct strbuf buf_index = STRBUF_INIT;
|
||||
const char *path_index = NULL;
|
||||
int pos, stage, sum;
|
||||
struct {
|
||||
int mode;
|
||||
struct object_id oid;
|
||||
} stages[3];
|
||||
char *key;
|
||||
char submodule_token[5];
|
||||
char unmerged_prefix = 'u';
|
||||
char eol_char = s->null_termination ? '\0' : '\n';
|
||||
|
||||
wt_porcelain_v2_submodule_state(d, submodule_token);
|
||||
|
||||
switch (d->stagemask) {
|
||||
case 1: key = "DD"; break; /* both deleted */
|
||||
case 2: key = "AU"; break; /* added by us */
|
||||
case 3: key = "UD"; break; /* deleted by them */
|
||||
case 4: key = "UA"; break; /* added by them */
|
||||
case 5: key = "DU"; break; /* deleted by us */
|
||||
case 6: key = "AA"; break; /* both added */
|
||||
case 7: key = "UU"; break; /* both modified */
|
||||
default:
|
||||
die("BUG: unhandled unmerged status %x", d->stagemask);
|
||||
}
|
||||
|
||||
/*
|
||||
* Disregard d.aux.porcelain_v2 data that we accumulated
|
||||
* for the head and index columns during the scans and
|
||||
* replace with the actual stage data.
|
||||
*
|
||||
* Note that this is a last-one-wins for each the individual
|
||||
* stage [123] columns in the event of multiple cache entries
|
||||
* for same stage.
|
||||
*/
|
||||
memset(stages, 0, sizeof(stages));
|
||||
sum = 0;
|
||||
pos = cache_name_pos(it->string, strlen(it->string));
|
||||
assert(pos < 0);
|
||||
pos = -pos-1;
|
||||
while (pos < active_nr) {
|
||||
ce = active_cache[pos++];
|
||||
stage = ce_stage(ce);
|
||||
if (strcmp(ce->name, it->string) || !stage)
|
||||
break;
|
||||
stages[stage - 1].mode = ce->ce_mode;
|
||||
hashcpy(stages[stage - 1].oid.hash, ce->sha1);
|
||||
sum |= (1 << (stage - 1));
|
||||
}
|
||||
if (sum != d->stagemask)
|
||||
die("BUG: observed stagemask 0x%x != expected stagemask 0x%x", sum, d->stagemask);
|
||||
|
||||
if (s->null_termination)
|
||||
path_index = it->string;
|
||||
else
|
||||
path_index = quote_path(it->string, s->prefix, &buf_index);
|
||||
|
||||
fprintf(s->fp, "%c %s %s %06o %06o %06o %06o %s %s %s %s%c",
|
||||
unmerged_prefix, key, submodule_token,
|
||||
stages[0].mode, /* stage 1 */
|
||||
stages[1].mode, /* stage 2 */
|
||||
stages[2].mode, /* stage 3 */
|
||||
d->mode_worktree,
|
||||
oid_to_hex(&stages[0].oid), /* stage 1 */
|
||||
oid_to_hex(&stages[1].oid), /* stage 2 */
|
||||
oid_to_hex(&stages[2].oid), /* stage 3 */
|
||||
path_index,
|
||||
eol_char);
|
||||
|
||||
strbuf_release(&buf_index);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print porcelain V2 status info for untracked and ignored entries.
|
||||
*/
|
||||
static void wt_porcelain_v2_print_other(
|
||||
struct string_list_item *it,
|
||||
struct wt_status *s,
|
||||
char prefix)
|
||||
{
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
const char *path;
|
||||
char eol_char;
|
||||
|
||||
if (s->null_termination) {
|
||||
path = it->string;
|
||||
eol_char = '\0';
|
||||
} else {
|
||||
path = quote_path(it->string, s->prefix, &buf);
|
||||
eol_char = '\n';
|
||||
}
|
||||
|
||||
fprintf(s->fp, "%c %s%c", prefix, path, eol_char);
|
||||
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Print porcelain V2 status.
|
||||
*
|
||||
* [<v2_branch>]
|
||||
* [<v2_changed_items>]*
|
||||
* [<v2_unmerged_items>]*
|
||||
* [<v2_untracked_items>]*
|
||||
* [<v2_ignored_items>]*
|
||||
*
|
||||
*/
|
||||
static void wt_porcelain_v2_print(struct wt_status *s)
|
||||
{
|
||||
struct wt_status_change_data *d;
|
||||
struct string_list_item *it;
|
||||
int i;
|
||||
|
||||
if (s->show_branch)
|
||||
wt_porcelain_v2_print_tracking(s);
|
||||
|
||||
for (i = 0; i < s->change.nr; i++) {
|
||||
it = &(s->change.items[i]);
|
||||
d = it->util;
|
||||
if (!d->stagemask)
|
||||
wt_porcelain_v2_print_changed_entry(it, s);
|
||||
}
|
||||
|
||||
for (i = 0; i < s->change.nr; i++) {
|
||||
it = &(s->change.items[i]);
|
||||
d = it->util;
|
||||
if (d->stagemask)
|
||||
wt_porcelain_v2_print_unmerged_entry(it, s);
|
||||
}
|
||||
|
||||
for (i = 0; i < s->untracked.nr; i++) {
|
||||
it = &(s->untracked.items[i]);
|
||||
wt_porcelain_v2_print_other(it, s, '?');
|
||||
}
|
||||
|
||||
for (i = 0; i < s->ignored.nr; i++) {
|
||||
it = &(s->ignored.items[i]);
|
||||
wt_porcelain_v2_print_other(it, s, '!');
|
||||
}
|
||||
}
|
||||
|
||||
void wt_status_print(struct wt_status *s)
|
||||
{
|
||||
switch (s->status_format) {
|
||||
case STATUS_FORMAT_SHORT:
|
||||
wt_shortstatus_print(s);
|
||||
break;
|
||||
case STATUS_FORMAT_PORCELAIN:
|
||||
wt_porcelain_print(s);
|
||||
break;
|
||||
case STATUS_FORMAT_PORCELAIN_V2:
|
||||
wt_porcelain_v2_print(s);
|
||||
break;
|
||||
case STATUS_FORMAT_UNSPECIFIED:
|
||||
die("BUG: finalize_deferred_config() should have been called");
|
||||
break;
|
||||
case STATUS_FORMAT_NONE:
|
||||
case STATUS_FORMAT_LONG:
|
||||
wt_longstatus_print(s);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
19
wt-status.h
19
wt-status.h
|
@ -38,11 +38,24 @@ struct wt_status_change_data {
|
|||
int worktree_status;
|
||||
int index_status;
|
||||
int stagemask;
|
||||
int score;
|
||||
int mode_head, mode_index, mode_worktree;
|
||||
struct object_id oid_head, oid_index;
|
||||
char *head_path;
|
||||
unsigned dirty_submodule : 2;
|
||||
unsigned new_submodule_commits : 1;
|
||||
};
|
||||
|
||||
enum wt_status_format {
|
||||
STATUS_FORMAT_NONE = 0,
|
||||
STATUS_FORMAT_LONG,
|
||||
STATUS_FORMAT_SHORT,
|
||||
STATUS_FORMAT_PORCELAIN,
|
||||
STATUS_FORMAT_PORCELAIN_V2,
|
||||
|
||||
STATUS_FORMAT_UNSPECIFIED
|
||||
};
|
||||
|
||||
struct wt_status {
|
||||
int is_initial;
|
||||
char *branch;
|
||||
|
@ -66,6 +79,9 @@ struct wt_status {
|
|||
int show_branch;
|
||||
int hints;
|
||||
|
||||
enum wt_status_format status_format;
|
||||
unsigned char sha1_commit[GIT_SHA1_RAWSZ]; /* when not Initial */
|
||||
|
||||
/* These are computed during processing of the individual sections */
|
||||
int commitable;
|
||||
int workdir_dirty;
|
||||
|
@ -107,9 +123,6 @@ int wt_status_check_rebase(const struct worktree *wt,
|
|||
int wt_status_check_bisect(const struct worktree *wt,
|
||||
struct wt_status_state *state);
|
||||
|
||||
void wt_shortstatus_print(struct wt_status *s);
|
||||
void wt_porcelain_print(struct wt_status *s);
|
||||
|
||||
__attribute__((format (printf, 3, 4)))
|
||||
void status_printf_ln(struct wt_status *s, const char *color, const char *fmt, ...);
|
||||
__attribute__((format (printf, 3, 4)))
|
||||
|
|
Загрузка…
Ссылка в новой задаче