3 # Show status of the repository and your working tree
4 # Copyright (c) Petr Baudis, 2005
5 # Copyright (c) Pavel Roskin 2005
7 # The output includes the list of branches and merge status.
8 # Current branch is marked with ">", remote branches are marked with "R".
9 # Branches with shelved local changes (currently produced only by
10 # `cg-switch -l`) are marked with "s".
12 # Then, the files in the working tree are printed out. The output has
13 # the following format:
15 # <status flag> <file>
17 # where '<status flag>' can be one of the following:
20 # '<file>' is not known to Cogito. See the IGNORING section below.
22 # '<file>' has been added.
24 # '<file>' has been deleted.
26 # '<file>' is gone from your working copy but not deleted by `cg-rm`.
28 # '<file>' has been touched or modified.
30 # '<file>' has been touched or modified, but will not be automatically
31 # committed the next time you call `cg-commit`. This is used during a
32 # merge to mark files which contained local changes before the merge.
36 # If neither -g or -w is passed, both is shown; otherwise, only the
37 # corresponding parts are shown.
39 # -g:: Show the GIT repository information
40 # Show the GIT repository information.
42 # -n:: Do not show status flags
43 # Do not show status flags. This is probably useful only when you filter
44 # the flags for a single specific flag using the '-s' option.
46 # -s STATUS:: Limit to files matching the STATUS flags
47 # Show only files with the given status flag, e.g. '-s D'. You can list
48 # multiple flags ('-s MmA') to filter for all of them. You can prepend
49 # '^' to the STATUS argument to invert the filter - only items with flags
50 # NOT listed in the STATUS string will be printed out.
52 # -S:: Do not squash directories
53 # By default, cg-status will not list full contents of untracked
54 # directories but only their name. This option will make it show the
55 # all untracked files inside as well.
57 # -w:: Show working files
58 # Show the working tree file list.
60 # -x:: Disable file exclusion
61 # Don't exclude any files from listing.
64 # Path to the directory to use as the base for the working tree
65 # file list (instead of the current directory).
69 # If a file has been removed with `cg-rm` without using the `-f` option
70 # to remove it physically from the tree it will be reported as both being
71 # deleted and unknown. The reason for this is that the file is internally
72 # marked as deleted and thus also untracked. After next commit it will only
73 # be reported as being untracked.
77 # You can declare some files to be ignored: this means that they will
78 # not be listed as unknown in `cg-status`, `cg-clean` will not remove
79 # them (unless '-x' is passed), and `cg-init` and `cg-add -a` will
80 # not add them. However, the moment you explicitly tell Cogito about
81 # them using `cg-add`, Cogito will stop ignoring them; it will commit
82 # any modifications in them, etc.: the concept is the same as e.g. in CVS.
83 # Typically, autogenerated and backup files are marked as ignored.
85 # Which files to ignore is determined by lists of exclude patterns
86 # stored in various files. There is one pattern per line and the
87 # patterns are classic shell glob patterns (with '*' and '?' wildcards).
88 # The pattern can be prefixed by '!' to unignore matching files.
89 # If the pattern does not contain a slash, it is applied in all
90 # directories; otherwise, only to the given path in the tree; use
91 # leading slash to denote the tree root.
93 # For example, consider the following:
95 # -------------------------------------------------------------------
99 # -------------------------------------------------------------------
101 # This will ignore all hidden files except '.gitignore' in all
102 # directories and the '.list' file in project root.
104 # When collecting the ignore patterns, first the default ignore
105 # patterns are loaded from '/usr/share/cogito/default-exclude'
106 # (or a slightly different path depending on your installation prefix).
107 # Then, '.git/info/exclude' from your working copy is loaded. At last,
108 # during the actual tree traversal '.gitignore' in each visited directory
109 # is loaded for the time of its traversal.
113 # $GIT_DIR/info/exclude::
114 # The list of ignore patterns; the list itself is not version-tracked
115 # and is local to this particular clone.
118 # '.gitignore' in the working tree will be also scanned for ignore
119 # patterns. Contrary to the exclude file, it is usually version-tracked.
123 # One known bug is that when you `cg-add` a new file and then delete it
124 # but do not call `cg-rm`, it will not be listed in `cg-status` output,
125 # but from the merging point of view there will still be "local changes"
126 # and `cg-diff` will show a diff.
128 # Testsuite: Marginal (part of t9202-merge-on-dirty)
130 USAGE
="cg-status [-g] [[-n] -s STATUS] [-w] [-x] [DIRPATH]"
133 .
"${COGITO_LIB}"cg-Xlib ||
exit 1
137 [[ -z "$flagfilter" ||
"$flagfilter" == *"$1"* ]]
146 squashdirs
=squashdirs
150 elif optparse
-n; then
152 elif optparse
-s=; then
154 echo "$flagfilter" |
grep -qx '[a-zA-Z?!^]*' \
155 || die
"invalid -s status flag"
156 if [ x
"${flagfilter:0:1}" = x
"^" ]; then
157 flagfilter
="$(echo "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ?
!" | tr -d "${flagfilter:1}")"
159 elif optparse
-S; then
160 squashdirs
=nosquashdirs
161 elif optparse
-w; then
163 elif optparse
-x; then
169 if [ ! "$gitstatus" ] && [ ! "$workstatus" ]; then
173 if [ "$_git_no_wc" ]; then
178 mstatedir
="$_git/cg-merge-state"
181 if [ "$gitstatus" ]; then
182 [ -s "$_git/branch-name" ] && echo "Branch (informal): $(cat "$_git/branch-name
")"
184 if [ -s "$_git/head-name" ]; then
185 headsha1
=$
(cat "$_git/$(git-symbolic-ref HEAD)")
186 echo "Seeked from head: $(cat "$_git/head-name
")"
187 echo "Seeked at commit: $headsha1"
191 # -name * will prevent listing hidden heads
192 maxlen
="$(git-for-each-ref --format="%(refname
)" refs/heads | column_width "refs
/heads
/")"
193 maxlen
=$
((maxlen
+ 1))
194 maxloglen
=$
(tput cols
)
195 # 5 status flags + 2 times separating spaces = 7
196 maxloglen
=$
((maxloglen
- maxlen
- 7))
198 #maxsha1len=$((maxloglen - 40))
199 #[ $maxsha1len -lt 12 ] && maxsha1len=12
200 #[ $maxsha1len -gt 40 ] && maxsha1len=40
203 sha1digits
="$maxsha1len"
204 if [ $maxsha1len -lt 40 ]; then
206 maxsha1len
=$
((maxsha1len
+1))
210 maxloglen
=$
((maxloglen
- maxsha1len
))
212 [ $maxlen -gt 1 ] && echo "Heads:"
214 git-for-each-ref
--format="%(refname) %(objectname) %(subject)" refs
/heads |
215 while read head sha1 headlog
; do
216 headname
="${head#refs/heads/}"
217 headsha1
="${sha1:0:$sha1digits}"
218 [ "$headname" = "cg-seek-point" ] && continue
219 cf
=" "; rf
=" "; sf
=" ";
220 [ "$headname" = "$_git_head" ] && cf
=">"
221 [ -s "$_git/branches/$headname" ] && rf
="R"
222 # ..cg-shelve deprecated as of 2006-11-19
223 exists_ref
"refs/heads/.cg-shelve-$headname" && sf
="s"
224 exists_ref
"refs/shelves/$headname" && sf
="s"
225 columns_print
" $sf$rf$cf" - "$headname" $maxlen "$headsha1$trimmark" $
((maxsha1len
+ 1)) "$headlog" m
$maxloglen
228 if [ -s "$mstatedir/merging" ]; then
229 tmp
="$(cat "$mstatedir/merging
")"
231 echo "Merging: $(cat "$mstatedir/merging
") ($(cat "$mstatedir/merging-sym
"))"
232 echo "Merge base: $(cat "$mstatedir/merge-base
")"
233 [ -s "$mstatedir/squashing" ] && echo "Squash-merge."
235 if [ -s "$mstatedir/commit-ignore" ]; then
236 echo "Files not to be committed now (contained local changes before the merge):"
237 sed 's/^/ /' "$mstatedir/commit-ignore"
240 if [ -s "$_git/blocked" ]; then
242 echo "Changes recording BLOCKED:"
243 sed 's/^/ /' "$_git/blocked"
246 if [ ! -s "$_git/$(git-symbolic-ref HEAD)" ]; then
248 echo "Before initial commit."
251 if [ "$_git_no_wc" ]; then
253 echo "Repository without attached working copy."
259 if [ "$gitstatus" ] && [ "$workstatus" ]; then
265 if [ "$workstatus" ]; then
266 git-update-index
--refresh > /dev
/null
268 basepath
="$_git_relpath"
269 [ "${ARGS[0]}" ] && basepath
="$(echo "$_git_relpath${ARGS[0]}" | normpath)"
271 should_show_flag
'?' &&
272 list_untracked_files
$exclude $squashdirs |
tr '\0' '\n' |
273 if [ "$basepath" ]; then
274 while IFS
=$
'' read path
; do
275 [ x
"${path#$basepath}" != x
"$path" ] &&
276 echo "${path#$_git_relpath}"
283 if [ ! -s "$_git/$(git-symbolic-ref HEAD)" ]; then
285 should_show_flag
'A' && git-ls-files |
sed 's,^,A ,'
288 [ -s "$mstatedir/commit-ignore" ] && commitignore
=1
290 git-diff-index HEAD
-- "${basepath:-.}" | cut
-f5- -d' ' |
291 while IFS
=$
'\t' read -r mode
file; do
292 if [ "$mode" = D
]; then
293 [ "$(git-diff-files -- "$file")" ] && continue # "!"
294 elif [ "$mode" = M
] && [ "$commitignore" ]; then
295 fgrep
-qx "$file" "$mstatedir/commit-ignore" && mode
=m
297 should_show_flag
"$mode" && echo "$mode ${file#$_git_relpath}"
300 git-diff-files
-- "${basepath:-.}" | cut
-f5- -d' ' |
301 while IFS
=$
'\t' read -r mode
file; do
302 if [ "$mode" = D
]; then
303 should_show_flag
"!" && echo "! ${file#$_git_relpath}"
307 fi |
if [ "$noflags" ]; then