From: Junio C Hamano Date: Thu, 30 Jun 2011 00:03:12 +0000 (-0700) Subject: Merge branch 'jk/maint-1.7.2-status-ignored' X-Git-Tag: v1.7.7-rc0~122 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/50cebdade1eb97ed667543b56e5b9d4db2a921f8?ds=inline;hp=-c Merge branch 'jk/maint-1.7.2-status-ignored' * jk/maint-1.7.2-status-ignored: git status --ignored: tests and docs status: fix bug with missing --ignore files Conflicts: Documentation/git-status.txt t/t7508-status.sh --- 50cebdade1eb97ed667543b56e5b9d4db2a921f8 diff --combined Documentation/git-status.txt index 38cb741f18,b663e51444..8ecc99d995 --- a/Documentation/git-status.txt +++ b/Documentation/git-status.txt @@@ -27,48 -27,31 +27,51 @@@ OPTION --short:: Give the output in the short-format. +-b:: +--branch:: + Show the branch and tracking info even in short-format. + --porcelain:: - Give the output in a stable, easy-to-parse format for scripts. - Currently this is identical to --short output, but is guaranteed - not to change in the future, making it safe for scripts. + 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. -u[]:: --untracked-files[=]:: - Show untracked files (Default: 'all'). + Show untracked files. ++ +The mode parameter is optional (defaults to 'all'), and is used to +specify the handling of untracked files; when -u is not used, the +default is 'normal', i.e. show untracked files and directories. + -The mode parameter is optional, and is used to specify -the handling of untracked files. The possible options are: +The possible options are: + --- - 'no' - Show no untracked files - 'normal' - Shows untracked files and directories - 'all' - Also shows individual files in untracked directories. --- + -See linkgit:git-config[1] for configuration variable -used to change the default for when the option is not -specified. +The default can be changed using the status.showUntrackedFiles +configuration variable documented in linkgit:git-config[1]. + +--ignore-submodules[=]:: + Ignore changes to submodules when looking for changes. can be + either "none", "untracked", "dirty" or "all", which is the default. + Using "none" will consider the submodule modified when it either contains + untracked or modified files or its HEAD differs from the commit recorded + in the superproject and can be used to override any settings of the + 'ignore' option in linkgit:git-config[1] or linkgit:gitmodules[5]. When + "untracked" is used submodules are not considered dirty when they only + contain untracked content (but they are still scanned for modified + content). Using "dirty" ignores all changes to the work tree of submodules, + only changes to the commits stored in the superproject are shown (this was + the behavior before 1.7.0). Using "all" hides all changes to submodules + (and suppresses the output of submodule summaries when the config option + `status.submodulesummary` is set). + --ignored:: + Show ignored files as well. + -z:: Terminate entries with NUL, instead of LF. This implies the `--porcelain` output format if no other format is given. @@@ -79,54 -62,36 +82,55 @@@ OUTPU The output from this command is designed to be used as a commit template comment, and all the output lines are prefixed with '#'. The default, long format, is designed to be human readable, -verbose and descriptive. They are subject to change in any time. +verbose and descriptive. Its contents and format are subject to change +at any time. The paths mentioned in the output, unlike many other git commands, are made relative to the current directory if you are working in a subdirectory (this is on purpose, to help cutting and pasting). See the status.relativePaths config option below. -In short-format, the status of each path is shown as +Short Format +~~~~~~~~~~~~ + +In the short-format, the status of each path is shown as XY PATH1 -> PATH2 -where `PATH1` is the path in the `HEAD`, and ` -> PATH2` part is +where `PATH1` is the path in the `HEAD`, and the ` \-> PATH2` part is shown only when `PATH1` corresponds to a different path in the -index/worktree (i.e. renamed). - -For unmerged entries, `X` shows the status of stage #2 (i.e. ours) and `Y` -shows the status of stage #3 (i.e. theirs). - -For entries that do not have conflicts, `X` shows the status of the index, -and `Y` shows the status of the work tree. For untracked paths, `XY` are -`??`. -For ignored paths, `XY` are `!!`; they are shown only when the `--ignored` -option is in effect. +index/worktree (i.e. the file is renamed). The 'XY' is a two-letter +status code. + +The fields (including the `\->`) are separated from each other by a +single space. If a filename contains whitespace or other nonprintable +characters, that field will be quoted in the manner of a C string +literal: surrounded by ASCII double quote (34) characters, and with +interior special characters backslash-escaped. + +For paths with merge conflicts, `X` and 'Y' show the modification +states of each side of the merge. For paths that do not have merge +conflicts, `X` shows the status of the index, and `Y` shows the status +of the work tree. For untracked paths, `XY` are `??`. Other status +codes can be interpreted as follows: + +* ' ' = unmodified +* 'M' = modified +* 'A' = added +* 'D' = deleted +* 'R' = renamed +* 'C' = copied +* 'U' = updated but unmerged + - Ignored files are not listed. ++Ignored files are not listed, unless `--ignored` option is in effect, ++in which case `XY` are `!!`. X Y Meaning ------------------------------------------------- [MD] not updated M [ MD] updated in index A [ MD] added to index - D [ MD] deleted from index + D [ M] deleted from index R [ MD] renamed in index C [ MD] copied in index [MARC] index and work tree matches @@@ -142,36 -107,9 +146,37 @@@ U U unmerged, both modified ------------------------------------------------- ? ? untracked + ! ! ignored ------------------------------------------------- +If -b is used the short-format status is preceded by a line + +## branchname tracking info + +Porcelain Format +~~~~~~~~~~~~~~~~ + +The 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 +format, with a few exceptions: + +1. The user's color.status configuration is not respected; color will + always be off. + +2. The user's status.relativePaths configuration is not respected; paths + shown will always be relative to the repository root. + +There is also an alternate -z format recommended for machine parsing. In +that format, the status field is the same, but some other things +change. First, the '\->' is omitted from rename entries and the field +order is reversed (e.g 'from \-> to' becomes 'to from'). Second, a NUL +(ASCII 0) follows each filename, replacing space as a field separator +and the terminating newline (but a space still separates the status +field from the first filename). Third, filenames containing special +characters are not specially formatted; no quoting or +backslash-escaping is performed. Fourth, there is no branch line. CONFIGURATION ------------- @@@ -194,6 -132,14 +199,6 @@@ SEE ALS -------- linkgit:gitignore[5] -Author ------- -Written by Junio C Hamano . - -Documentation --------------- -Documentation by David Greaves, Junio C Hamano and the git-list . - GIT --- Part of the linkgit:git[1] suite diff --combined t/t7508-status.sh index 1fdfbd3865,4856f9eeea..905255adf0 --- a/t/t7508-status.sh +++ b/t/t7508-status.sh @@@ -7,30 -7,6 +7,30 @@@ test_description='git status . ./test-lib.sh +test_expect_success 'status -h in broken repository' ' + mkdir broken && + test_when_finished "rm -fr broken" && + ( + cd broken && + git init && + echo "[status] showuntrackedfiles = CORRUPT" >>.git/config && + test_expect_code 129 git status -h >usage 2>&1 + ) && + test_i18ngrep "[Uu]sage" broken/usage +' + +test_expect_success 'commit -h in broken repository' ' + mkdir broken && + test_when_finished "rm -fr broken" && + ( + cd broken && + git init && + echo "[status] showuntrackedfiles = CORRUPT" >>.git/config && + test_expect_code 129 git commit -h >usage 2>&1 + ) && + test_i18ngrep "[Uu]sage" broken/usage +' + test_expect_success 'setup' ' : >tracked && : >modified && @@@ -56,7 -32,9 +56,7 @@@ ' test_expect_success 'status (1)' ' - - grep "use \"git rm --cached \.\.\.\" to unstage" output - + test_i18ngrep "use \"git rm --cached \.\.\.\" to unstage" output ' cat >expect <<\EOF @@@ -66,7 -44,7 +66,7 @@@ # # new file: dir2/added # -# Changed but not updated: +# Changes not staged for commit: # (use "git add ..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # @@@ -84,32 -62,9 +84,32 @@@ EOF test_expect_success 'status (2)' ' + git status >output && + test_i18ncmp expect output +' +cat >expect <<\EOF +# On branch master +# Changes to be committed: +# new file: dir2/added +# +# Changes not staged for commit: +# modified: dir1/modified +# +# Untracked files: +# dir1/untracked +# dir2/modified +# dir2/untracked +# expect +# output +# untracked +EOF + +test_expect_success 'status (advice.statusHints false)' ' + test_when_finished "git config --unset advice.statusHints" && + git config advice.statusHints false && git status >output && - test_cmp expect output + test_i18ncmp expect output ' @@@ -124,38 -79,134 +124,159 @@@ A dir2/adde ?? untracked EOF -test_expect_success 'status -s (2)' ' +test_expect_success 'status -s' ' git status -s >output && test_cmp expect output ' + test_expect_success 'status with gitignore' ' + { + echo ".gitignore" && + echo "expect" && + echo "output" && + echo "untracked" + } >.gitignore && + + cat >expect <<-\EOF && + M dir1/modified + A dir2/added + ?? dir2/modified + EOF + git status -s >output && + test_cmp expect output && + + cat >expect <<-\EOF && + M dir1/modified + A dir2/added + ?? dir2/modified + !! .gitignore + !! dir1/untracked + !! dir2/untracked + !! expect + !! output + !! untracked + EOF + git status -s --ignored >output && + test_cmp expect output && + + cat >expect <<-\EOF && + # On branch master + # Changes to be committed: + # (use "git reset HEAD ..." to unstage) + # + # new file: dir2/added + # - # Changed but not updated: ++ # Changes not staged for commit: + # (use "git add ..." to update what will be committed) + # (use "git checkout -- ..." to discard changes in working directory) + # + # modified: dir1/modified + # + # Untracked files: + # (use "git add ..." to include in what will be committed) + # + # dir2/modified + # Ignored files: + # (use "git add -f ..." to include in what will be committed) + # + # .gitignore + # dir1/untracked + # dir2/untracked + # expect + # output + # untracked + EOF + git status --ignored >output && + test_cmp expect output + ' + + test_expect_success 'status with gitignore (nothing untracked)' ' + { + echo ".gitignore" && + echo "expect" && + echo "dir2/modified" && + echo "output" && + echo "untracked" + } >.gitignore && + + cat >expect <<-\EOF && + M dir1/modified + A dir2/added + EOF + git status -s >output && + test_cmp expect output && + + cat >expect <<-\EOF && + M dir1/modified + A dir2/added + !! .gitignore + !! dir1/untracked + !! dir2/modified + !! dir2/untracked + !! expect + !! output + !! untracked + EOF + git status -s --ignored >output && + test_cmp expect output && + + cat >expect <<-\EOF && + # On branch master + # Changes to be committed: + # (use "git reset HEAD ..." to unstage) + # + # new file: dir2/added + # - # Changed but not updated: ++ # Changes not staged for commit: + # (use "git add ..." to update what will be committed) + # (use "git checkout -- ..." to discard changes in working directory) + # + # modified: dir1/modified + # + # Ignored files: + # (use "git add -f ..." to include in what will be committed) + # + # .gitignore + # dir1/untracked + # dir2/modified + # dir2/untracked + # expect + # output + # untracked + EOF + git status --ignored >output && + test_cmp expect output + ' + + rm -f .gitignore + +cat >expect <<\EOF +## master + M dir1/modified +A dir2/added +?? dir1/untracked +?? dir2/modified +?? dir2/untracked +?? expect +?? output +?? untracked +EOF + +test_expect_success 'status -s -b' ' + + git status -s -b >output && + test_cmp expect output + +' + +test_expect_success 'setup dir3' ' + mkdir dir3 && + : >dir3/untracked1 && + : >dir3/untracked2 +' + cat >expect <..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # @@@ -172,39 -223,25 +293,39 @@@ # Untracked files not listed (use -u option to show untracked files) EOF test_expect_success 'status -uno' ' - mkdir dir3 && - : >dir3/untracked1 && - : >dir3/untracked2 && git status -uno >output && - test_cmp expect output + test_i18ncmp expect output ' test_expect_success 'status (status.showUntrackedFiles no)' ' git config status.showuntrackedfiles no + test_when_finished "git config --unset status.showuntrackedfiles" && git status >output && - test_cmp expect output + test_i18ncmp expect output +' + +cat >expect <output && + test_i18ncmp expect output ' +git config --unset advice.statusHints cat >expect << EOF M dir1/modified A dir2/added EOF test_expect_success 'status -s -uno' ' - git config --unset status.showuntrackedfiles git status -s -uno >output && test_cmp expect output ' @@@ -222,7 -259,7 +343,7 @@@ cat >expect <..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # @@@ -241,14 -278,13 +362,14 @@@ EOF test_expect_success 'status -unormal' ' git status -unormal >output && - test_cmp expect output + test_i18ncmp expect output ' test_expect_success 'status (status.showUntrackedFiles normal)' ' git config status.showuntrackedfiles normal + test_when_finished "git config --unset status.showuntrackedfiles" && git status >output && - test_cmp expect output + test_i18ncmp expect output ' cat >expect <output && test_cmp expect output ' @@@ -280,7 -317,7 +401,7 @@@ cat >expect <..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # @@@ -300,18 -337,14 +421,18 @@@ EOF test_expect_success 'status -uall' ' git status -uall >output && - test_cmp expect output + test_i18ncmp expect output ' + test_expect_success 'status (status.showUntrackedFiles all)' ' git config status.showuntrackedfiles all + test_when_finished "git config --unset status.showuntrackedfiles" && git status >output && - rm -rf dir3 && - git config --unset status.showuntrackedfiles && - test_cmp expect output + test_i18ncmp expect output +' + +test_expect_success 'teardown dir3' ' + rm -rf dir3 ' cat >expect <expect <<\EO # # new file: ../dir2/added # -# Changed but not updated: +# Changes not staged for commit: # (use "git add ..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # @@@ -362,8 -395,10 +483,8 @@@ EOF test_expect_success 'status with relative paths' ' - (cd dir1 && git status) >output && - test_cmp expect output - + test_i18ncmp expect output ' cat >expect <<\EOF @@@ -403,19 -438,18 +524,19 @@@ test_expect_success 'status --porcelai test_expect_success 'setup unique colors' ' - git config status.color.untracked blue + git config status.color.untracked blue && + git config status.color.branch green ' cat >expect <<\EOF -# On branch master +# On branch master # Changes to be committed: # (use "git reset HEAD ..." to unstage) # # new file: dir2/added # -# Changed but not updated: +# Changes not staged for commit: # (use "git add ..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # @@@ -433,17 -467,20 +554,17 @@@ EOF test_expect_success 'status with color.ui' ' - git config color.ui always && + test_when_finished "git config --unset color.ui" && git status | test_decode_color >output && - test_cmp expect output - + test_i18ncmp expect output ' test_expect_success 'status with color.status' ' - - git config --unset color.ui && git config color.status always && + test_when_finished "git config --unset color.status" && git status | test_decode_color >output && - test_cmp expect output - + test_i18ncmp expect output ' cat >expect <<\EOF @@@ -459,6 -496,7 +580,6 @@@ EO test_expect_success 'status -s with color.ui' ' - git config --unset color.status && git config color.ui always && git status -s | test_decode_color >output && test_cmp expect output @@@ -474,25 -512,6 +595,25 @@@ test_expect_success 'status -s with col ' +cat >expect <<\EOF +## master + M dir1/modified +A dir2/added +?? dir1/untracked +?? dir2/modified +?? dir2/untracked +?? expect +?? output +?? untracked +EOF + +test_expect_success 'status -s -b with color.status' ' + + git status -s -b | test_decode_color >output && + test_cmp expect output + +' + cat >expect <<\EOF M dir1/modified A dir2/added @@@ -526,13 -545,6 +647,13 @@@ test_expect_success 'status --porcelai git config --unset color.status git config --unset color.ui +test_expect_success 'status --porcelain ignores -b' ' + + git status --porcelain -b >output && + test_cmp expect output + +' + cat >expect <<\EOF # On branch master # Changes to be committed: @@@ -540,7 -552,7 +661,7 @@@ # # new file: dir2/added # -# Changed but not updated: +# Changes not staged for commit: # (use "git add ..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # @@@ -560,10 -572,9 +681,10 @@@ EO test_expect_success 'status without relative paths' ' - git config status.relativePaths false + git config status.relativePaths false && + test_when_finished "git config --unset status.relativePaths" && (cd dir1 && git status) >output && - test_cmp expect output + test_i18ncmp expect output ' @@@ -580,8 -591,6 +701,8 @@@ EO test_expect_success 'status -s without relative paths' ' + git config status.relativePaths false && + test_when_finished "git config --unset status.relativePaths" && (cd dir1 && git status -s) >output && test_cmp expect output @@@ -605,16 -614,6 +726,16 @@@ cat <expec EOF test_expect_success 'dry-run of partial commit excluding new file in index' ' git commit --dry-run dir1/modified >output && + test_i18ncmp expect output +' + +cat >expect <output && test_cmp expect output ' @@@ -636,7 -635,7 +757,7 @@@ cat >expect <..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # @@@ -654,13 -653,13 +775,13 @@@ EOF test_expect_success 'status submodule summary is disabled by default' ' git status >output && - test_cmp expect output + test_i18ncmp expect output ' # we expect the same as the previous test test_expect_success 'status --untracked-files=all does not show submodule' ' git status --untracked-files=all >output && - test_cmp expect output + test_i18ncmp expect output ' cat >expect <expect <..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # @@@ -719,7 -718,7 +840,7 @@@ EO test_expect_success 'status submodule summary' ' git config status.submodulesummary 10 && git status >output && - test_cmp expect output + test_i18ncmp expect output ' cat >expect <expect <..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # @@@ -757,13 -756,13 +878,13 @@@ # untracked no changes added to commit (use "git add" and/or "git commit -a") EOF -test_expect_success 'status submodule summary (clean submodule)' ' +test_expect_success 'status submodule summary (clean submodule): commit' ' git commit -m "commit submodule" && git config status.submodulesummary 10 && test_must_fail git commit --dry-run >output && - test_cmp expect output && + test_i18ncmp expect output && git status >output && - test_cmp expect output + test_i18ncmp expect output ' cat >expect <expect && + git status -z >output && + test_cmp expect output +' + cat >expect <..." to update what will be committed) # (use "git checkout -- ..." to discard changes in working directory) # @@@ -819,324 -811,7 +940,324 @@@ EO test_expect_success 'commit --dry-run submodule summary (--amend)' ' git config status.submodulesummary 10 && git commit --dry-run --amend >output && - test_cmp expect output + test_i18ncmp expect output +' + +test_expect_success POSIXPERM,SANITY 'status succeeds in a read-only repository' ' + ( + chmod a-w .git && + # make dir1/tracked stat-dirty + >dir1/tracked1 && mv -f dir1/tracked1 dir1/tracked && + git status -s >output && + ! grep dir1/tracked output && + # make sure "status" succeeded without writing index out + git diff-files | grep dir1/tracked + ) + status=$? + chmod 775 .git + (exit $status) +' + +(cd sm && echo > bar && git add bar && git commit -q -m 'Add bar') && git add sm +new_head=$(cd sm && git rev-parse --short=7 --verify HEAD) +touch .gitmodules + +cat > expect << EOF +# On branch master +# Changes to be committed: +# (use "git reset HEAD ..." to unstage) +# +# modified: sm +# +# Changes not staged for commit: +# (use "git add ..." to update what will be committed) +# (use "git checkout -- ..." to discard changes in working directory) +# +# modified: dir1/modified +# +# Submodule changes to be committed: +# +# * sm $head...$new_head (1): +# > Add bar +# +# Untracked files: +# (use "git add ..." to include in what will be committed) +# +# .gitmodules +# dir1/untracked +# dir2/modified +# dir2/untracked +# expect +# output +# untracked +EOF + +test_expect_success '--ignore-submodules=untracked suppresses submodules with untracked content' ' + echo modified sm/untracked && + git status --ignore-submodules=untracked >output && + test_i18ncmp expect output +' + +test_expect_success '.gitmodules ignore=untracked suppresses submodules with untracked content' ' + git config diff.ignoreSubmodules dirty && + git status >output && + test_i18ncmp expect output && + git config --add -f .gitmodules submodule.subname.ignore untracked && + git config --add -f .gitmodules submodule.subname.path sm && + git status >output && + test_i18ncmp expect output && + git config -f .gitmodules --remove-section submodule.subname && + git config --unset diff.ignoreSubmodules +' + +test_expect_success '.git/config ignore=untracked suppresses submodules with untracked content' ' + git config --add -f .gitmodules submodule.subname.ignore none && + git config --add -f .gitmodules submodule.subname.path sm && + git config --add submodule.subname.ignore untracked && + git config --add submodule.subname.path sm && + git status >output && + test_i18ncmp expect output && + git config --remove-section submodule.subname && + git config --remove-section -f .gitmodules submodule.subname +' + +test_expect_success '--ignore-submodules=dirty suppresses submodules with untracked content' ' + git status --ignore-submodules=dirty >output && + test_i18ncmp expect output +' + +test_expect_success '.gitmodules ignore=dirty suppresses submodules with untracked content' ' + git config diff.ignoreSubmodules dirty && + git status >output && + ! test -s actual && + git config --add -f .gitmodules submodule.subname.ignore dirty && + git config --add -f .gitmodules submodule.subname.path sm && + git status >output && + test_i18ncmp expect output && + git config -f .gitmodules --remove-section submodule.subname && + git config --unset diff.ignoreSubmodules +' + +test_expect_success '.git/config ignore=dirty suppresses submodules with untracked content' ' + git config --add -f .gitmodules submodule.subname.ignore none && + git config --add -f .gitmodules submodule.subname.path sm && + git config --add submodule.subname.ignore dirty && + git config --add submodule.subname.path sm && + git status >output && + test_i18ncmp expect output && + git config --remove-section submodule.subname && + git config -f .gitmodules --remove-section submodule.subname +' + +test_expect_success '--ignore-submodules=dirty suppresses submodules with modified content' ' + echo modified >sm/foo && + git status --ignore-submodules=dirty >output && + test_i18ncmp expect output +' + +test_expect_success '.gitmodules ignore=dirty suppresses submodules with modified content' ' + git config --add -f .gitmodules submodule.subname.ignore dirty && + git config --add -f .gitmodules submodule.subname.path sm && + git status >output && + test_i18ncmp expect output && + git config -f .gitmodules --remove-section submodule.subname +' + +test_expect_success '.git/config ignore=dirty suppresses submodules with modified content' ' + git config --add -f .gitmodules submodule.subname.ignore none && + git config --add -f .gitmodules submodule.subname.path sm && + git config --add submodule.subname.ignore dirty && + git config --add submodule.subname.path sm && + git status >output && + test_i18ncmp expect output && + git config --remove-section submodule.subname && + git config -f .gitmodules --remove-section submodule.subname +' + +cat > expect << EOF +# On branch master +# Changes to be committed: +# (use "git reset HEAD ..." to unstage) +# +# modified: sm +# +# Changes not staged for commit: +# (use "git add ..." to update what will be committed) +# (use "git checkout -- ..." to discard changes in working directory) +# (commit or discard the untracked or modified content in submodules) +# +# modified: dir1/modified +# modified: sm (modified content) +# +# Submodule changes to be committed: +# +# * sm $head...$new_head (1): +# > Add bar +# +# Untracked files: +# (use "git add ..." to include in what will be committed) +# +# .gitmodules +# dir1/untracked +# dir2/modified +# dir2/untracked +# expect +# output +# untracked +EOF + +test_expect_success "--ignore-submodules=untracked doesn't suppress submodules with modified content" ' + git status --ignore-submodules=untracked > output && + test_i18ncmp expect output +' + +test_expect_success ".gitmodules ignore=untracked doesn't suppress submodules with modified content" ' + git config --add -f .gitmodules submodule.subname.ignore untracked && + git config --add -f .gitmodules submodule.subname.path sm && + git status >output && + test_i18ncmp expect output && + git config -f .gitmodules --remove-section submodule.subname +' + +test_expect_success ".git/config ignore=untracked doesn't suppress submodules with modified content" ' + git config --add -f .gitmodules submodule.subname.ignore none && + git config --add -f .gitmodules submodule.subname.path sm && + git config --add submodule.subname.ignore untracked && + git config --add submodule.subname.path sm && + git status >output && + test_i18ncmp expect output && + git config --remove-section submodule.subname && + git config -f .gitmodules --remove-section submodule.subname +' + +head2=$(cd sm && git commit -q -m "2nd commit" foo && git rev-parse --short=7 --verify HEAD) + +cat > expect << EOF +# On branch master +# Changes to be committed: +# (use "git reset HEAD ..." to unstage) +# +# modified: sm +# +# Changes not staged for commit: +# (use "git add ..." to update what will be committed) +# (use "git checkout -- ..." to discard changes in working directory) +# +# modified: dir1/modified +# modified: sm (new commits) +# +# Submodule changes to be committed: +# +# * sm $head...$new_head (1): +# > Add bar +# +# Submodules changed but not updated: +# +# * sm $new_head...$head2 (1): +# > 2nd commit +# +# Untracked files: +# (use "git add ..." to include in what will be committed) +# +# .gitmodules +# dir1/untracked +# dir2/modified +# dir2/untracked +# expect +# output +# untracked +EOF + +test_expect_success "--ignore-submodules=untracked doesn't suppress submodule summary" ' + git status --ignore-submodules=untracked > output && + test_i18ncmp expect output +' + +test_expect_success ".gitmodules ignore=untracked doesn't suppress submodule summary" ' + git config --add -f .gitmodules submodule.subname.ignore untracked && + git config --add -f .gitmodules submodule.subname.path sm && + git status >output && + test_i18ncmp expect output && + git config -f .gitmodules --remove-section submodule.subname +' + +test_expect_success ".git/config ignore=untracked doesn't suppress submodule summary" ' + git config --add -f .gitmodules submodule.subname.ignore none && + git config --add -f .gitmodules submodule.subname.path sm && + git config --add submodule.subname.ignore untracked && + git config --add submodule.subname.path sm && + git status >output && + test_i18ncmp expect output && + git config --remove-section submodule.subname && + git config -f .gitmodules --remove-section submodule.subname +' + +test_expect_success "--ignore-submodules=dirty doesn't suppress submodule summary" ' + git status --ignore-submodules=dirty > output && + test_i18ncmp expect output +' +test_expect_success ".gitmodules ignore=dirty doesn't suppress submodule summary" ' + git config --add -f .gitmodules submodule.subname.ignore dirty && + git config --add -f .gitmodules submodule.subname.path sm && + git status >output && + test_i18ncmp expect output && + git config -f .gitmodules --remove-section submodule.subname +' + +test_expect_success ".git/config ignore=dirty doesn't suppress submodule summary" ' + git config --add -f .gitmodules submodule.subname.ignore none && + git config --add -f .gitmodules submodule.subname.path sm && + git config --add submodule.subname.ignore dirty && + git config --add submodule.subname.path sm && + git status >output && + test_i18ncmp expect output && + git config --remove-section submodule.subname && + git config -f .gitmodules --remove-section submodule.subname +' + +cat > expect << EOF +# On branch master +# Changes not staged for commit: +# (use "git add ..." to update what will be committed) +# (use "git checkout -- ..." to discard changes in working directory) +# +# modified: dir1/modified +# +# Untracked files: +# (use "git add ..." to include in what will be committed) +# +# .gitmodules +# dir1/untracked +# dir2/modified +# dir2/untracked +# expect +# output +# untracked +no changes added to commit (use "git add" and/or "git commit -a") +EOF + +test_expect_success "--ignore-submodules=all suppresses submodule summary" ' + git status --ignore-submodules=all > output && + test_i18ncmp expect output +' + +test_expect_failure '.gitmodules ignore=all suppresses submodule summary' ' + git config --add -f .gitmodules submodule.subname.ignore all && + git config --add -f .gitmodules submodule.subname.path sm && + git status > output && + test_cmp expect output && + git config -f .gitmodules --remove-section submodule.subname +' + +test_expect_failure '.git/config ignore=all suppresses submodule summary' ' + git config --add -f .gitmodules submodule.subname.ignore none && + git config --add -f .gitmodules submodule.subname.path sm && + git config --add submodule.subname.ignore all && + git config --add submodule.subname.path sm && + git status > output && + test_cmp expect output && + git config --remove-section submodule.subname && + git config -f .gitmodules --remove-section submodule.subname ' test_done diff --combined wt-status.c index 9f4e0ba9c1,160e841261..02377729c4 --- a/wt-status.c +++ b/wt-status.c @@@ -9,8 -9,6 +9,8 @@@ #include "quote.h" #include "run-command.h" #include "remote.h" +#include "refs.h" +#include "submodule.h" static char default_wt_status_colors[][COLOR_MAXLEN] = { GIT_COLOR_NORMAL, /* WT_STATUS_HEADER */ @@@ -19,91 -17,11 +19,91 @@@ GIT_COLOR_RED, /* WT_STATUS_UNTRACKED */ GIT_COLOR_RED, /* WT_STATUS_NOBRANCH */ GIT_COLOR_RED, /* WT_STATUS_UNMERGED */ + GIT_COLOR_GREEN, /* WT_STATUS_LOCAL_BRANCH */ + GIT_COLOR_RED, /* WT_STATUS_REMOTE_BRANCH */ + GIT_COLOR_NIL, /* WT_STATUS_ONBRANCH */ }; static const char *color(int slot, struct wt_status *s) { - return s->use_color > 0 ? s->color_palette[slot] : ""; + const char *c = s->use_color > 0 ? s->color_palette[slot] : ""; + if (slot == WT_STATUS_ONBRANCH && color_is_nil(c)) + c = s->color_palette[WT_STATUS_HEADER]; + return c; +} + +static void status_vprintf(struct wt_status *s, int at_bol, const char *color, + const char *fmt, va_list ap, const char *trail) +{ + struct strbuf sb = STRBUF_INIT; + struct strbuf linebuf = STRBUF_INIT; + const char *line, *eol; + + strbuf_vaddf(&sb, fmt, ap); + if (!sb.len) { + strbuf_addch(&sb, '#'); + if (!trail) + strbuf_addch(&sb, ' '); + color_print_strbuf(s->fp, color, &sb); + if (trail) + fprintf(s->fp, "%s", trail); + strbuf_release(&sb); + return; + } + for (line = sb.buf; *line; line = eol + 1) { + eol = strchr(line, '\n'); + + strbuf_reset(&linebuf); + if (at_bol) { + strbuf_addch(&linebuf, '#'); + if (*line != '\n' && *line != '\t') + strbuf_addch(&linebuf, ' '); + } + if (eol) + strbuf_add(&linebuf, line, eol - line); + else + strbuf_addstr(&linebuf, line); + color_print_strbuf(s->fp, color, &linebuf); + if (eol) + fprintf(s->fp, "\n"); + else + break; + at_bol = 1; + } + if (trail) + fprintf(s->fp, "%s", trail); + strbuf_release(&linebuf); + strbuf_release(&sb); +} + +void status_printf_ln(struct wt_status *s, const char *color, + const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + status_vprintf(s, 1, color, fmt, ap, "\n"); + va_end(ap); +} + +void status_printf(struct wt_status *s, const char *color, + const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + status_vprintf(s, 1, color, fmt, ap, NULL); + va_end(ap); +} + +void status_printf_more(struct wt_status *s, const char *color, + const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + status_vprintf(s, 0, color, fmt, ap, NULL); + va_end(ap); } void wt_status_prepare(struct wt_status *s) @@@ -131,33 -49,33 +131,33 @@@ static void wt_status_print_unmerged_he { const char *c = color(WT_STATUS_HEADER, s); - color_fprintf_ln(s->fp, c, "# Unmerged paths:"); + status_printf_ln(s, c, _("Unmerged paths:")); if (!advice_status_hints) return; - if (s->in_merge) + if (s->whence != FROM_COMMIT) ; else if (!s->is_initial) - color_fprintf_ln(s->fp, c, "# (use \"git reset %s ...\" to unstage)", s->reference); + status_printf_ln(s, c, _(" (use \"git reset %s ...\" to unstage)"), s->reference); else - color_fprintf_ln(s->fp, c, "# (use \"git rm --cached ...\" to unstage)"); - color_fprintf_ln(s->fp, c, "# (use \"git add/rm ...\" as appropriate to mark resolution)"); - color_fprintf_ln(s->fp, c, "#"); + status_printf_ln(s, c, _(" (use \"git rm --cached ...\" to unstage)")); + status_printf_ln(s, c, _(" (use \"git add/rm ...\" as appropriate to mark resolution)")); + status_printf_ln(s, c, ""); } static void wt_status_print_cached_header(struct wt_status *s) { const char *c = color(WT_STATUS_HEADER, s); - color_fprintf_ln(s->fp, c, "# Changes to be committed:"); + status_printf_ln(s, c, _("Changes to be committed:")); if (!advice_status_hints) return; - if (s->in_merge) + if (s->whence != FROM_COMMIT) ; /* NEEDSWORK: use "git reset --unresolve"??? */ else if (!s->is_initial) - color_fprintf_ln(s->fp, c, "# (use \"git reset %s ...\" to unstage)", s->reference); + status_printf_ln(s, c, _(" (use \"git reset %s ...\" to unstage)"), s->reference); else - color_fprintf_ln(s->fp, c, "# (use \"git rm --cached ...\" to unstage)"); - color_fprintf_ln(s->fp, c, "#"); + status_printf_ln(s, c, _(" (use \"git rm --cached ...\" to unstage)")); + status_printf_ln(s, c, ""); } static void wt_status_print_dirty_header(struct wt_status *s, @@@ -166,17 -84,17 +166,17 @@@ { const char *c = color(WT_STATUS_HEADER, s); - color_fprintf_ln(s->fp, c, "# Changed but not updated:"); + status_printf_ln(s, c, _("Changes not staged for commit:")); if (!advice_status_hints) return; if (!has_deleted) - color_fprintf_ln(s->fp, c, "# (use \"git add ...\" to update what will be committed)"); + status_printf_ln(s, c, _(" (use \"git add ...\" to update what will be committed)")); else - color_fprintf_ln(s->fp, c, "# (use \"git add/rm ...\" to update what will be committed)"); - color_fprintf_ln(s->fp, c, "# (use \"git checkout -- ...\" to discard changes in working directory)"); + status_printf_ln(s, c, _(" (use \"git add/rm ...\" to update what will be committed)")); + status_printf_ln(s, c, _(" (use \"git checkout -- ...\" to discard changes in working directory)")); if (has_dirty_submodules) - color_fprintf_ln(s->fp, c, "# (commit or discard the untracked or modified content in submodules)"); - color_fprintf_ln(s->fp, c, "#"); + status_printf_ln(s, c, _(" (commit or discard the untracked or modified content in submodules)")); + status_printf_ln(s, c, ""); } static void wt_status_print_other_header(struct wt_status *s, @@@ -184,16 -102,16 +184,16 @@@ const char *how) { const char *c = color(WT_STATUS_HEADER, s); - color_fprintf_ln(s->fp, c, "# %s files:", what); + status_printf_ln(s, c, _("%s files:"), what); if (!advice_status_hints) return; - color_fprintf_ln(s->fp, c, "# (use \"git %s ...\" to include in what will be committed)", how); - color_fprintf_ln(s->fp, c, "#"); + status_printf_ln(s, c, _(" (use \"git %s ...\" to include in what will be committed)"), how); + status_printf_ln(s, c, ""); } static void wt_status_print_trailer(struct wt_status *s) { - color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#"); + status_printf_ln(s, color(WT_STATUS_HEADER, s), ""); } #define quote_path quote_path_relative @@@ -204,20 -122,20 +204,20 @@@ static void wt_status_print_unmerged_da const char *c = color(WT_STATUS_UNMERGED, s); struct wt_status_change_data *d = it->util; struct strbuf onebuf = STRBUF_INIT; - const char *one, *how = "bug"; + const char *one, *how = _("bug"); one = quote_path(it->string, -1, &onebuf, s->prefix); - color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t"); + status_printf(s, color(WT_STATUS_HEADER, s), "\t"); switch (d->stagemask) { - case 1: how = "both deleted:"; break; - case 2: how = "added by us:"; break; - case 3: how = "deleted by them:"; break; - case 4: how = "added by them:"; break; - case 5: how = "deleted by us:"; break; - case 6: how = "both added:"; break; - case 7: how = "both modified:"; break; - } - color_fprintf(s->fp, c, "%-20s%s\n", how, one); + case 1: how = _("both deleted:"); break; + case 2: how = _("added by us:"); break; + case 3: how = _("deleted by them:"); break; + case 4: how = _("added by them:"); break; + case 5: how = _("deleted by us:"); break; + case 6: how = _("both added:"); break; + case 7: how = _("both modified:"); break; + } + status_printf_more(s, c, "%-20s%s\n", how, one); strbuf_release(&onebuf); } @@@ -245,11 -163,11 +245,11 @@@ static void wt_status_print_change_data if (d->new_submodule_commits || d->dirty_submodule) { strbuf_addstr(&extra, " ("); if (d->new_submodule_commits) - strbuf_addf(&extra, "new commits, "); + strbuf_addf(&extra, _("new commits, ")); if (d->dirty_submodule & DIRTY_SUBMODULE_MODIFIED) - strbuf_addf(&extra, "modified content, "); + strbuf_addf(&extra, _("modified content, ")); if (d->dirty_submodule & DIRTY_SUBMODULE_UNTRACKED) - strbuf_addf(&extra, "untracked content, "); + strbuf_addf(&extra, _("untracked content, ")); strbuf_setlen(&extra, extra.len - 2); strbuf_addch(&extra, ')'); } @@@ -260,40 -178,40 +260,40 @@@ one = quote_path(one_name, -1, &onebuf, s->prefix); two = quote_path(two_name, -1, &twobuf, s->prefix); - color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t"); + status_printf(s, color(WT_STATUS_HEADER, s), "\t"); switch (status) { case DIFF_STATUS_ADDED: - color_fprintf(s->fp, c, "new file: %s", one); + status_printf_more(s, c, _("new file: %s"), one); break; case DIFF_STATUS_COPIED: - color_fprintf(s->fp, c, "copied: %s -> %s", one, two); + status_printf_more(s, c, _("copied: %s -> %s"), one, two); break; case DIFF_STATUS_DELETED: - color_fprintf(s->fp, c, "deleted: %s", one); + status_printf_more(s, c, _("deleted: %s"), one); break; case DIFF_STATUS_MODIFIED: - color_fprintf(s->fp, c, "modified: %s", one); + status_printf_more(s, c, _("modified: %s"), one); break; case DIFF_STATUS_RENAMED: - color_fprintf(s->fp, c, "renamed: %s -> %s", one, two); + status_printf_more(s, c, _("renamed: %s -> %s"), one, two); break; case DIFF_STATUS_TYPE_CHANGED: - color_fprintf(s->fp, c, "typechange: %s", one); + status_printf_more(s, c, _("typechange: %s"), one); break; case DIFF_STATUS_UNKNOWN: - color_fprintf(s->fp, c, "unknown: %s", one); + status_printf_more(s, c, _("unknown: %s"), one); break; case DIFF_STATUS_UNMERGED: - color_fprintf(s->fp, c, "unmerged: %s", one); + status_printf_more(s, c, _("unmerged: %s"), one); break; default: - die("bug: unhandled diff status %c", status); + die(_("bug: unhandled diff status %c"), status); } if (extra.len) { - color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "%s", extra.buf); + status_printf_more(s, color(WT_STATUS_HEADER, s), "%s", extra.buf); strbuf_release(&extra); } - fprintf(s->fp, "\n"); + status_printf_more(s, GIT_COLOR_NORMAL, "\n"); strbuf_release(&onebuf); strbuf_release(&twobuf); } @@@ -314,7 -232,7 +314,7 @@@ static void wt_status_collect_changed_c struct wt_status_change_data *d; p = q->queue[i]; - it = string_list_insert(p->one->path, &s->change); + it = string_list_insert(&s->change, p->one->path); d = it->util; if (!d) { d = xcalloc(1, sizeof(*d)); @@@ -361,7 -279,7 +361,7 @@@ static void wt_status_collect_updated_c struct wt_status_change_data *d; p = q->queue[i]; - it = string_list_insert(p->two->path, &s->change); + it = string_list_insert(&s->change, p->two->path); d = it->util; if (!d) { d = xcalloc(1, sizeof(*d)); @@@ -391,13 -309,9 +391,13 @@@ static void wt_status_collect_changes_w DIFF_OPT_SET(&rev.diffopt, DIRTY_SUBMODULES); if (!s->show_untracked_files) DIFF_OPT_SET(&rev.diffopt, IGNORE_UNTRACKED_IN_SUBMODULES); + if (s->ignore_submodule_arg) { + DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG); + handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg); + } rev.diffopt.format_callback = wt_status_collect_changed_cb; rev.diffopt.format_callback_data = s; - rev.prune_data = s->pathspec; + init_pathspec(&rev.prune_data, s->pathspec); run_diff_files(&rev, 0); } @@@ -411,35 -325,28 +411,35 @@@ static void wt_status_collect_changes_i opt.def = s->is_initial ? EMPTY_TREE_SHA1_HEX : s->reference; setup_revisions(0, NULL, &rev, &opt); + if (s->ignore_submodule_arg) { + DIFF_OPT_SET(&rev.diffopt, OVERRIDE_SUBMODULE_CONFIG); + handle_ignore_submodules_arg(&rev.diffopt, s->ignore_submodule_arg); + } + rev.diffopt.output_format |= DIFF_FORMAT_CALLBACK; rev.diffopt.format_callback = wt_status_collect_updated_cb; rev.diffopt.format_callback_data = s; rev.diffopt.detect_rename = 1; rev.diffopt.rename_limit = 200; rev.diffopt.break_opt = 0; - rev.prune_data = s->pathspec; + init_pathspec(&rev.prune_data, s->pathspec); run_diff_index(&rev, 1); } static void wt_status_collect_changes_initial(struct wt_status *s) { + struct pathspec pathspec; int i; + init_pathspec(&pathspec, s->pathspec); for (i = 0; i < active_nr; i++) { struct string_list_item *it; struct wt_status_change_data *d; struct cache_entry *ce = active_cache[i]; - if (!ce_path_match(ce, s->pathspec)) + if (!ce_path_match(ce, &pathspec)) continue; - it = string_list_insert(ce->name, &s->change); + it = string_list_insert(&s->change, ce->name); d = it->util; if (!d) { d = xcalloc(1, sizeof(*d)); @@@ -452,7 -359,6 +452,7 @@@ else d->index_status = DIFF_STATUS_ADDED; } + free_pathspec(&pathspec); } static void wt_status_collect_untracked(struct wt_status *s) @@@ -471,9 -377,11 +471,9 @@@ fill_directory(&dir, s->pathspec); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; - if (!cache_name_is_other(ent->name, ent->len)) - continue; - if (!match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL)) - continue; - string_list_insert(ent->name, &s->untracked); + if (cache_name_is_other(ent->name, ent->len) && + match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL)) + string_list_insert(&s->untracked, ent->name); free(ent); } @@@ -483,9 -391,11 +483,9 @@@ fill_directory(&dir, s->pathspec); for (i = 0; i < dir.nr; i++) { struct dir_entry *ent = dir.entries[i]; - if (!cache_name_is_other(ent->name, ent->len)) - continue; - if (!match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL)) - continue; - string_list_insert(ent->name, &s->ignored); + if (cache_name_is_other(ent->name, ent->len) && + match_pathspec(s->pathspec, ent->name, ent->len, 0, NULL)) + string_list_insert(&s->ignored, ent->name); free(ent); } } @@@ -608,18 -518,17 +608,18 @@@ static void wt_status_print_submodule_s struct child_process sm_summary; char summary_limit[64]; char index[PATH_MAX]; - const char *env[] = { index, NULL }; - const char *argv[] = { - "submodule", - "summary", - uncommitted ? "--files" : "--cached", - "--for-status", - "--summary-limit", - summary_limit, - uncommitted ? NULL : (s->amend ? "HEAD^" : "HEAD"), - NULL - }; + const char *env[] = { NULL, NULL }; + const char *argv[8]; + + env[0] = index; + argv[0] = "submodule"; + argv[1] = "summary"; + argv[2] = uncommitted ? "--files" : "--cached"; + argv[3] = "--for-status"; + argv[4] = "--summary-limit"; + argv[5] = summary_limit; + argv[6] = uncommitted ? NULL : (s->amend ? "HEAD^" : "HEAD"); + argv[7] = NULL; sprintf(summary_limit, "%d", s->submodule_summary); snprintf(index, sizeof(index), "GIT_INDEX_FILE=%s", s->index_file); @@@ -642,7 -551,7 +642,7 @@@ static void wt_status_print_other(struc int i; struct strbuf buf = STRBUF_INIT; - if (!s->untracked.nr) + if (!l->nr) return; wt_status_print_other_header(s, what, how); @@@ -650,9 -559,9 +650,9 @@@ for (i = 0; i < l->nr; i++) { struct string_list_item *it; it = &(l->items[i]); - color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "#\t"); - color_fprintf_ln(s->fp, color(WT_STATUS_UNTRACKED, s), "%s", - quote_path(it->string, strlen(it->string), + status_printf(s, color(WT_STATUS_HEADER, s), "\t"); + status_printf_more(s, color(WT_STATUS_UNTRACKED, s), + "%s\n", quote_path(it->string, strlen(it->string), &buf, s->prefix)); } strbuf_release(&buf); @@@ -706,74 -615,61 +706,74 @@@ static void wt_status_print_tracking(st void wt_status_print(struct wt_status *s) { - const char *branch_color = color(WT_STATUS_HEADER, s); + const char *branch_color = color(WT_STATUS_ONBRANCH, s); + const char *branch_status_color = color(WT_STATUS_HEADER, s); if (s->branch) { - const char *on_what = "On branch "; + const char *on_what = _("On branch "); const char *branch_name = s->branch; if (!prefixcmp(branch_name, "refs/heads/")) branch_name += 11; else if (!strcmp(branch_name, "HEAD")) { branch_name = ""; - branch_color = color(WT_STATUS_NOBRANCH, s); - on_what = "Not currently on any branch."; + branch_status_color = color(WT_STATUS_NOBRANCH, s); + on_what = _("Not currently on any branch."); } - color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "# "); - color_fprintf_ln(s->fp, branch_color, "%s%s", on_what, branch_name); + status_printf(s, color(WT_STATUS_HEADER, 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); } if (s->is_initial) { - color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#"); - color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "# Initial commit"); - color_fprintf_ln(s->fp, color(WT_STATUS_HEADER, s), "#"); + status_printf_ln(s, color(WT_STATUS_HEADER, s), ""); + status_printf_ln(s, color(WT_STATUS_HEADER, s), _("Initial commit")); + status_printf_ln(s, color(WT_STATUS_HEADER, s), ""); } wt_status_print_updated(s); wt_status_print_unmerged(s); wt_status_print_changed(s); - if (s->submodule_summary) { + 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 */ } if (s->show_untracked_files) { - wt_status_print_other(s, &s->untracked, "Untracked", "add"); + wt_status_print_other(s, &s->untracked, _("Untracked"), "add"); if (s->show_ignored_files) - wt_status_print_other(s, &s->ignored, "Ignored", "add -f"); + wt_status_print_other(s, &s->ignored, _("Ignored"), "add -f"); } else if (s->commitable) - fprintf(s->fp, "# Untracked files not listed (use -u option to show untracked files)\n"); + status_printf_ln(s, GIT_COLOR_NORMAL, _("Untracked files not listed%s"), + advice_status_hints + ? _(" (use -u option to show untracked files)") : ""); if (s->verbose) wt_status_print_verbose(s); if (!s->commitable) { if (s->amend) - fprintf(s->fp, "# No changes\n"); + status_printf_ln(s, GIT_COLOR_NORMAL, _("No changes")); else if (s->nowarn) ; /* nothing */ else if (s->workdir_dirty) - printf("no changes added to commit (use \"git add\" and/or \"git commit -a\")\n"); + printf(_("no changes added to commit%s\n"), + advice_status_hints + ? _(" (use \"git add\" and/or \"git commit -a\")") : ""); else if (s->untracked.nr) - printf("nothing added to commit but untracked files present (use \"git add\" to track)\n"); + printf(_("nothing added to commit but untracked files present%s\n"), + advice_status_hints + ? _(" (use \"git add\" to track)") : ""); else if (s->is_initial) - printf("nothing to commit (create/copy files and use \"git add\" to track)\n"); + printf(_("nothing to commit%s\n"), advice_status_hints + ? _(" (create/copy files and use \"git add\" to track)") : ""); else if (!s->show_untracked_files) - printf("nothing to commit (use -u to show untracked files)\n"); + printf(_("nothing to commit%s\n"), advice_status_hints + ? _(" (use -u to show untracked files)") : ""); else - printf("nothing to commit (working directory clean)\n"); + printf(_("nothing to commit%s\n"), advice_status_hints + ? _(" (working directory clean)") : ""); } } @@@ -827,20 -723,10 +827,20 @@@ static void wt_shortstatus_status(int n const char *one; if (d->head_path) { one = quote_path(d->head_path, -1, &onebuf, s->prefix); + if (*one != '"' && strchr(one, ' ') != NULL) { + putchar('"'); + strbuf_addch(&onebuf, '"'); + one = onebuf.buf; + } printf("%s -> ", one); strbuf_release(&onebuf); } one = quote_path(it->string, -1, &onebuf, s->prefix); + if (*one != '"' && strchr(one, ' ') != NULL) { + putchar('"'); + strbuf_addch(&onebuf, '"'); + one = onebuf.buf; + } printf("%s\n", one); strbuf_release(&onebuf); } @@@ -855,75 -741,15 +855,75 @@@ static void wt_shortstatus_other(int nu struct strbuf onebuf = STRBUF_INIT; const char *one; one = quote_path(it->string, -1, &onebuf, s->prefix); - color_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), sign); + color_fprintf(s->fp, color(WT_STATUS_UNTRACKED, s), "%s", sign); printf(" %s\n", one); strbuf_release(&onebuf); } } -void wt_shortstatus_print(struct wt_status *s, int null_termination) +static void wt_shortstatus_print_tracking(struct wt_status *s) +{ + struct branch *branch; + const char *header_color = color(WT_STATUS_HEADER, s); + const char *branch_color_local = color(WT_STATUS_LOCAL_BRANCH, s); + const char *branch_color_remote = color(WT_STATUS_REMOTE_BRANCH, s); + + const char *base; + const char *branch_name; + int num_ours, num_theirs; + + color_fprintf(s->fp, color(WT_STATUS_HEADER, s), "## "); + + if (!s->branch) + return; + branch_name = s->branch; + + if (!prefixcmp(branch_name, "refs/heads/")) + branch_name += 11; + else if (!strcmp(branch_name, "HEAD")) { + branch_name = _("HEAD (no branch)"); + branch_color_local = color(WT_STATUS_NOBRANCH, s); + } + + branch = branch_get(s->branch + 11); + if (s->is_initial) + color_fprintf(s->fp, header_color, _("Initial commit on ")); + if (!stat_tracking_info(branch, &num_ours, &num_theirs)) { + color_fprintf_ln(s->fp, branch_color_local, + "%s", branch_name); + return; + } + + base = branch->merge[0]->dst; + base = shorten_unambiguous_ref(base, 0); + color_fprintf(s->fp, branch_color_local, "%s", branch_name); + color_fprintf(s->fp, header_color, "..."); + color_fprintf(s->fp, branch_color_remote, "%s", base); + + color_fprintf(s->fp, header_color, " ["); + if (!num_ours) { + color_fprintf(s->fp, header_color, _("behind ")); + color_fprintf(s->fp, branch_color_remote, "%d", num_theirs); + } else if (!num_theirs) { + color_fprintf(s->fp, header_color, _("ahead ")); + color_fprintf(s->fp, branch_color_local, "%d", num_ours); + } else { + color_fprintf(s->fp, header_color, _("ahead ")); + color_fprintf(s->fp, branch_color_local, "%d", num_ours); + color_fprintf(s->fp, header_color, _(", behind ")); + color_fprintf(s->fp, branch_color_remote, "%d", num_theirs); + } + + color_fprintf_ln(s->fp, header_color, "]"); +} + +void wt_shortstatus_print(struct wt_status *s, int null_termination, int show_branch) { int i; + + if (show_branch) + wt_shortstatus_print_tracking(s); + for (i = 0; i < s->change.nr; i++) { struct wt_status_change_data *d; struct string_list_item *it; @@@ -954,5 -780,5 +954,5 @@@ void wt_porcelain_print(struct wt_statu s->use_color = 0; s->relative_paths = 0; s->prefix = NULL; - wt_shortstatus_print(s, null_termination); + wt_shortstatus_print(s, null_termination, 0); }