Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Warn when doing git commit -a if something is staged

Tags:

git

Sometimes from muscle memory, I run git commit -a when I have some files or parts of files carefully staged and ready to commit, causing me to lose my careful staging action.

Is there a way to make git commit -a warn if there is anything (file or patch) currently staged?

(Clearly I should just use -a less to alleviate my problem, but my question stands.)

like image 907
Sophie Alpert Avatar asked Jul 09 '11 00:07

Sophie Alpert


2 Answers

Unfortunately, Git doesn't allow aliases to override existing commands, otherwise you could easily add this functionality via an alias.

But, you can get part way there. It would require retraining yourself to type something instead of git commit—perhaps git c.

Here's how you could do it:

  1. Put the following shell code in a script file somewhere (e.g., /path/to/commit-wrapper)

    #!/bin/sh
    
    # avoid echo because some implementations treat backslashes specially
    log() { printf '%s\n' "$*"; }
    error() { log "ERROR: $*" >&2; }
    fatal() { error "$*"; exit 1; }
    
    check_for_staged() {
        git diff-index --cached --quiet HEAD || {
    
            # simply exit with an error if run non-interactively
            tty >/dev/null \
                || fatal "don't use '$1' when you have staged changes"
    
            # this script is being run interactively; prompt the user to
            # continue
            error "'$1' option used when there are staged changes"
            while true; do
                printf 'Continue anyway? [y/N] ' >&2
                read answer || { printf '\n'; answer=N; }
                [ -n "${answer}" ] || answer=N
                case ${answer} in
                    y|Y) break;;
                    n|N) echo "aborted" >&2; exit 1;;
                    *) error "Please answer 'y' or 'n'.";;
                esac
            done
        }
    }
    
    # TODO:  use 'git rev-parse --parseopt' to reliably detect '-a' (e.g.,
    # to properly handle invocations such as 'git commit -sa')
    for i in "$@"; do
        case ${i} in
            --) break;;
            -a|--all) check_for_staged "${i}"; break;;
        esac
    done
    
    git commit "$@"
    
  2. Make the file executable: chmod a+x /path/to/commit-wrapper
  3. Set up your alias: git config --global alias.c '!/path/to/commit-wrapper'
  4. Use git c instead of git commit.

If Git is ever changed to allow aliases for existing commands, change the last line to say "$(git --exec-path)"/git-commit "$@" to avoid an infinite loop.

like image 50
Richard Hansen Avatar answered Oct 04 '22 00:10

Richard Hansen


Add a pre-commit hooke in your repo, with something like below:

#!/bin/sh
echo "Make sure you haven't used the -a flag or verify git diff --cached returns nothing"
echo "Run commit again with --no-verify if ok to proceed"
exit 1

That should help you overcome your "muscle memory".

Unfortunately, pre-commit hook cannot be powerful enough to do more checks ( like if you had supplied the -a argument to the commit. ) Also, when you do -a, the pre-commit hook will see as though all the files were staged, eventhough after the execution, you will see them as unstaged. So you cannot differentiate between what you had staged previously and the files staged because of the -a.

like image 42
manojlds Avatar answered Oct 04 '22 00:10

manojlds