Warn when doing git commit

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.)


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:

  • Put the following shell code in a script file somewhere (eg, /path/to/commit-wrapper )

    #!/bin/sh
    
    # avoid echo because some implementations treat backslashes specially
    log() { printf '%sn' "$*"; }
    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 "$@"
    
  • Make the file executable: chmod a+x /path/to/commit-wrapper
  • Set up your alias: git config --global alias.c '!/path/to/commit-wrapper'
  • 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.


    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 .


    You can use your favorite scripting language to create git2 command, that will check if you're running it with arguments commit -a . If that is the case, run /usr/bin/git status --porcelain and check if there are some changes to the index (porcelain-formatted output is easier to parse, my first guess would be to run it through grep '^[AMD]' and check whether it found something. Now, you can either print a warning and quit, or run /usr/bin/git which all original argument to continue like there was no git2 (which is also what you do if you haven't been run with commit -a).

    Here's an example in Perl (which git requires anyway):

    #!/usr/bin/env perl
    
    use strict;
    use warnings;
    
    my %aliases = map { split(/n/, $_, 2) }
       split //,
       `git config -z --get-regexp alias.`;
    
    my %commit_aliases = (( commit => 1 ),
       map { s/alias.//; $_ => 1 }
       grep $aliases{$_} =~ /^commitb/,
       keys %aliases);
    
    my ($command, @args) = @ARGV;
    
    if ($commit_aliases{$command} && $args[0] =~ /^-a|^--all/) {
       my @staged = grep /^M/, split //, `git status -z`;
       if (@staged) {
          print "There are staged changes, are you sure you want to commit all? (y/N) ";
          chomp(my $answer = <STDIN>);
          if ($answer =~ /^y/i) {
             run_command()
          }
       } else {
          run_command()
       }
    } else {
       run_command()
    }
    
    sub run_command {
       system 'git', $command, @args;
       exit $? >> 8;
    }
    

    Then, create a bash alias alias git2 git and you're all set.

    链接地址: http://www.djcxy.com/p/50882.html

    上一篇: 无法看到使用git diff进行提交的更改

    下一篇: 在做git commit时发出警告