我可以让git reset --hard更安全还是禁用它?

rqcrx0a6  于 2023-06-04  发布在  Git
关注(0)|答案(3)|浏览(170)

我已经养成了经常使用git reset --hard的坏习惯。
我现在发现有git reset --keep,如果我真的需要删除一些东西,我甚至可以做git stash && git stash drop
所以我想禁用硬重置,或者让他们在不可恢复地删除未提交的更改之前询问,或者至少创建备份。可以在不将git Package 在shell函数中的情况下完成此操作吗?

uurv41yg

uurv41yg1#

git本质上不支持您所要求的内容。
您可以编写一个名为git的脚本或Bash函数,并将其放在PATH中的git之前。脚本或函数将只检查“禁止的命令”,否则将运行常规的git程序(例如/usr/bin/git)。

rxztt3cl

rxztt3cl2#

Git因在执行有潜在危险的操作时不警告用户而臭名昭著。在任何情况下,对什么构成危险的看法都是不同的。基本上,当你在命令行中使用Git时,你就是在说你是一个高级用户,你知道你在做什么。没有办法让Git消除你对这个概念的误解。
相反,您可以使用GUI,例如Sourcetree。它知道reset --hard有潜在的危险,并发出警告,迫使你停下来仔细想想你在做什么。

fykwrbwg

fykwrbwg3#

这个问题询问是否有一种方法可以做到这一点,并以添加“是否有一种方法可以在没有shell脚本的情况下做到这一点”结束。我认为这意味着OP希望有一种方法来完成它,如果可能的话,最好没有 Package 器。由于没有 Package 器是不可能的,所以我在使用git时添加了一些额外的检查。它涉及到编写一个 Package 器脚本,我在shell启动文件中将其别名为git。如果我做了一些我不想再做的事情,脚本会进行检查并退出,否则它会调用command git "$@"
下面的脚本在仓库中有未提交(staged或unstaged)的更改时禁用“git reset --hard”。

function has_arg(){
    local arg=$1; shift
    for a in "$@" ; do
        if [[ "${a}" == "${arg}" ]] ; then
            return 0
        fi
    done
    return 1
}

function repo_is_clean(){
    git diff --no-ext-diff --quiet 2>/dev/null \
    && git diff --no-ext-diff --cached --quiet 2>/dev/null
}

# Copied straight from git-completion.bash to determine the git command
# with minor adaptations (using ${!c} to get the c-th argument instead
# of ${COMP_WORDS[c]} and some stuff removed).
declare i c=1 git_command
while [ $c -le ${#} ]; do
    i=${!c}
    case "$i" in
    --git-dir=*)  ;;
    --git-dir)   ((c++)) ;;
    --bare) ;;
    --help) git_command="help"; break ;;
    -c|--work-tree|--namespace) ((c++)) ;;
    -C) ((c++)) ;;
    -*) ;;
    *) git_command="$i"; break ;;
    esac
    ((c++))
done

if [[ ${git_command} == reset ]] && has_arg --hard "$@" && ! repo_is_clean ; then
    echo "PHIL: You have uncommitted changes, use stash to get rid of them"
    echo "      and git stash drop later which will allow you an extra"
    echo "      opportunity to realize if you made a mistake"
    exit 1
fi

command git "$@"

关于它如何工作的一些说明:

  • has_arg检查它的第一个参数是否出现在后面的参数列表中,因此has_arg --hard "$@"检查传递给脚本的参数是否包含--hard
  • repo_is_clean:如果没有未暂存的更改,则git diff --no-exit-diff --quiet以0存在,而--cached对暂存的更改也是如此。因此,如果函数失败(返回非零值),则存在一些未提交的更改。
  • while循环来自git-completion.bash,我找到了它用来确定git命令是什么的代码,并去掉了这个脚本不需要的东西。
  • 其中一项检查:如果命令被重置并且--hard存在,并且存储库具有未提交的更改,则打印消息并退出。
  • 如果我们到达脚本的末尾,则委托实际的git命令转发所有参数。

要使用它,请使此脚本可执行,并在shell启动文件中为其创建别名

alias git=<your-script>

打破习惯

因为这个问题提到了打破习惯,这里有一些更“激烈”的事情,我做了打破一个不同的习惯。
出于下面解释的原因,我想打破执行git commit -m的习惯,转而使用编辑器执行git commit(没有-m):

if [[ "${git_command}" == commit ]] && has_arg -m "$@"; then
    echo "PHIL: Don't use -m for commits"
    exit 1
fi

有一段时间,我仍然总是在做git commit -m "message",然后得到消息,然后在编辑器中做。我想改变这一点,所以我把简单的echo替换为下面的代码,让它变得更痛苦:

if [[ "${git_command}" == commit ]] && has_arg -m "$@"; then
    trap 'n=0; echo "sorry, you gotta wait! setting counter back to 0"' SIGINT
    echo "To help break the habit of using git commit -m, please endure"
    echo "uh, I mean 'enjoy' this 5 second uninterruptible sleep"
    for((n=1;n<=5;n++)); do
        sleep 1
        printf "\r${n}"
    done
    echo ""
    exit 1
fi

SIGINT上的trap会导致当用户在shell中按下Ctrl-C以试图摆脱5秒惩罚时运行echo -n ...
请注意,只要您编写了格式正确的提交消息,使用git commit -m就没有什么问题。但是有些人会写一个超长的提交消息,我希望能够鼓励他们总是使用编辑器,为此我必须自己做。
还有其他的原因,为什么我喜欢使用编辑器的所有时间,但这是不相关的问题,我的“惩罚”系统几乎是无论如何。

git reset的替代方法--hard

虽然git reset --hard可以用来移动分支所指向的位置,但这是你的习惯,而且你提到了git stash && git stash drop,这让我相信你用它来摆脱不需要的未提交的更改,可能意识到你摆脱了一些你想保留的更改时已经太晚了。
在这种情况下,git checkout -p命令很有用。它就像git add -p一样,以交互方式向您显示大量更改,并要求您做出决定。你按y键就可以丢弃大块

相关问题