#!/usr/bin/env bash # # Show status of your working tree. # Copyright (c) Petr Baudis, 2005 # Copyright (c) Pavel Roskin 2005 # # The output includes the list of branches and merge status. # Current branch is marked by ">", remote branches are marked by "R". # # Then, the files in the working tree are printed out. The output has # the following format: # # # # where '' can be one of the following: # # ?:: # '' is unknown. # A:: # '' has been added. # D:: # '' has been deleted. # !:: # '' is gone from your working copy but not deleted by `cg-rm`. # M:: # '' has been touched or modified. # m:: # '' has been touched or modified, but will not be automatically # committed the next time you call `cg-commit`. This is used during a # merge to mark files which contained local changes before the merge. # # OPTIONS # ------- # If neither -g or -w is passed, both is shown; otherwise, only the # corresponding parts are shown. # # -g:: Show the GIT repository information # Show the GIT repository information. # # -n:: Do not show status flags # Do not show status flags. This is probably useful only when you filter # the flags for a single specific flag using the '-s' option. # # -s STATUS:: Limit to files matching the STATUS flags # Show only files with the given status flag, e.g. '-s D'. You can list # multiple flags ('-s MmA') to filter for all of them. # # -S:: Do not squash directories # By default, cg-status will not list full contents of untracked # directories but only their name. This option will make it show the # all untracked files inside as well. # # -w:: Show working files # Show the working tree file list. # # -x:: Disable file exclusion # Don't exclude any files from listing. # # DIRPATH:: # Path to the directory to use as the base for the working tree # file list (instead of the current directory). # # NOTES # ----- # If a file has been removed with `cg-rm` without using the `-f` option # to remove it physically from the tree it will be reported as both being # deleted and unknown. The reason for this is that the file is internally # marked as deleted and thus also untracked. After next commit it will only # be reported as being untracked. # # FILES # ----- # $GIT_DIR/info/exclude:: # If the file exists it will be used to prune which files to # show status for. The format is similar to the `dontdiff` file; # each line contains a pattern for a file or group of files # to exclude. # # $TREE_DIR/.gitignore:: # .gitignore in the working tree will be used as an exclude file. # The excludes are applied from the project root approaching the # current subdirectory. # # BUGS # ---- # One known bug is that when you `cg-add` a new file and then delete it # but do not call `cg-rm`, it will not be listed in `cg-status` output, # but from the merging point of view there will still be "local changes" # and `cg-diff` will show a diff. USAGE="cg-status [-g] [[-n] -s STATUS] [-w] [-x] [DIRPATH]" . "${COGITO_LIB}"cg-Xlib || exit 1 should_show_flag() { [[ -z "$flagfilter" || "$flagfilter" == *"$1"* ]] } gitstatus= workstatus= exclude=yes flagfilter= noflags= squashdirs=squashdirs while optparse; do if optparse -g; then gitstatus=1 elif optparse -n; then noflags=1 elif optparse -s=; then flagfilter="$OPTARG" echo "$flagfilter" | grep -qx '[a-zA-Z?!]*' \ || die "invalid -s status flag" elif optparse -S; then squashdirs=nosquashdirs elif optparse -w; then workstatus=1 elif optparse -x; then exclude=no else optfail fi done if [ ! "$gitstatus" ] && [ ! "$workstatus" ]; then gitstatus=1 workstatus=1 fi if [ "$gitstatus" ]; then mkdir -p "$_git/refs/heads" [ "$(find "$_git/refs/heads" -follow -type f)" ] \ || die "List of heads is empty." [ -s "$_git/branch-name" ] && echo "Branch (informal): $(cat "$_git/branch-name")" if [ -s "$_git/head-name" ]; then headsha1=$(cat "$_git/$(git-symbolic-ref HEAD)") echo "Seeked from head: $(cat "$_git/head-name")" echo "Seeked at commit: $headsha1" echo fi echo "Heads:" maxlen="$(find "$_git/refs/heads" ! -type d | while read l; do l=${l#$_git/refs/heads}; echo ${#l}; done | sort -nr | head -n 1)" [ "$maxlen" -gt 35 ] && maxlen=35 find "$_git/refs/heads" ! -type d | sort | while read head; do headsha1="$(cat "$head")" headname="${head#$_git/refs/heads/}" [ "$headname" = "cg-seek-point" ] && continue cf=" "; rf=" " [ "$headname" = "$_git_head" ] && cf=">" [ -s "$_git/branches/$headname" ] && rf="R" printf " %s%s%-${maxlen}s\t%s\n" "$rf" "$cf" "$headname" "$headsha1" done if [ -s "$_git/merging" ]; then tmp="$(cat "$_git/merging")" echo echo "Merging: $(cat "$_git/merging") ($(cat "$_git/merging-sym"))" echo "Merge base: $(cat "$_git/merge-base")" [ -s "$_git/squashing" ] && echo "Squash-merge." fi if [ -s "$_git/commit-ignore" ]; then echo "Files not to be committed now (contained local changes before the merge):" sed 's/^/ /' "$_git/commit-ignore" fi if [ -s "$_git/blocked" ]; then echo echo "Changes recording BLOCKED:" sed 's/^/ /' "$_git/blocked" fi if [ ! -s "$_git/$(git-symbolic-ref HEAD)" ]; then echo echo "Before initial commit." fi fi if [ "$gitstatus" ] && [ "$workstatus" ]; then echo fi if [ "$workstatus" ]; then git-update-index --refresh > /dev/null basepath="$_git_relpath" [ "${ARGS[0]}" ] && basepath="$(echo "$_git_relpath${ARGS[0]}" | normpath)" should_show_flag '?' && list_untracked_files $exclude $squashdirs | tr '\0' '\n' | if [ "$basepath" ]; then while IFS=$'' read path; do [ x"${path#$basepath}" != x"$path" ] && echo "${path#$basepath}" done else cat fi | sed 's,^,? ,' if [ ! -s "$_git/$(git-symbolic-ref HEAD)" ]; then # Initial commit should_show_flag 'A' && git-ls-files | sed 's,^,A ,' else commitignore= [ -s "$_git/commit-ignore" ] && commitignore=1 git-diff-index HEAD "$basepath" | cut -f5- -d' ' | while IFS=$'\t' read -r mode file; do if [ "$mode" = D ]; then [ "$(git-diff-files "$file")" ] && mode=! elif [ "$mode" = M ] && [ "$commitignore" ]; then fgrep -qx "$file" "$_git/commit-ignore" && mode=m fi [ "$basepath" ] && file="${file#$basepath}" should_show_flag "$mode" && echo "$mode $file" done fi fi | if [ "$noflags" ]; then sed 's/^. //' else cat fi