git 普通命令成功时别名失败

git alias fails when plain command succeeds

我的 ~/.gitconfig 文件中存储了以下别名

reset-master = reset $(git merge-base master $(git rev-parse --abbrev-ref HEAD))

但是当我 运行 git reset-master 它失败了

$ git reset-master
error: unknown option `abbrev-ref'
usage: git reset [--mixed | --soft | --hard | --merge | --keep] [-q] 
[<commit>]
   or: git reset [-q] [<tree-ish>] [--] <paths>...
   or: git reset --patch [<tree-ish>] [--] [<paths>...]

    -q, --quiet           be quiet, only report errors
    --mixed               reset HEAD and index
    --soft                reset only HEAD
    --hard                reset HEAD, index and working tree
    --merge               reset HEAD, index and working tree
    --keep                reset HEAD but keep local changes
    --recurse-submodules[=<reset>]
                          control recursive updating of submodules
    -p, --patch           select hunks interactively
    -N, --intent-to-add   record only the fact that removed paths will be added later

和运行宁

git reset $(git merge-base master $(git rev-parse --abbrev-ref HEAD))

工作得很好。我做错了什么?

TL;DR

您需要使用 ! 形式的 Git 别名:!git reset $(...),而不是更短的形式 !reset $(...).

多头

要理解这个问题,我们需要知道:

cmd1 $(cmd2 arg ...)

实际上由 shell 处理,由 运行ning cmd2 及其参数 first.输出——更准确地说,标准输出流——从cmd2返回到shell,它读取它并将其分解成单词,然后传递给那些单词 cmd1。因此:

wc $(seq -f f%g 1 3)

第一 运行s seq -f f%g 1 3:

$ seq -f f%g 1 3
f1
f2
f3

shell读取这些单词,将它们变成三个参数传递给wc

wc f1 f2 f3

(这个例子有点傻,因为我们知道 seq -f f%g 1 3 总是打印这三个名字,所以我们可以只用 运行 wc 这三个名字,但是适用于插图。)

Git的别名,默认是不是 运行到shell:

reset-master = reset $(git merge-base master $(git rev-parse --abbrev-ref HEAD))

表示 Git 尝试将文字字符串 $(git 作为第一个参数传递给 git reset。第二个参数是 merge-base,依此类推。其中一个参数是 --abbrev-ref,这是 git reset 首先查看的参数 - 它会扫描所有选项的参数,然后再尝试理解该行的其余部分 - 所以就是那个它抱怨。

如果我们将整个字符串提供给 shell,但是,我们会得到 shell 来扩展每个 $(...) 的出现.第一个 运行s git rev-parse --abbrev-ref HEAD,它打印当前分支的名称(如果有的话,或者 HEAD 如果我们有一个分离的 HEAD)。此输出被反馈到外部 git merge-base 命令,以找到 master 和命名分支(或再次 HEAD )之间的合并基础提交。幸运的话,git merge-base 打印一个提交哈希 ID,shell 将其替换为最终命令:

git reset <hash-id>

我们可以Git通过写来做到这一点:

reset-master = !git reset $(git merge-base master $(git rev-parse --abbrev-ref HEAD))

作为以!开头的Git别名意味着运行通过shell的剩余文本。

注意如果有没有合并基础commit,这会运行git reset,相当于git reset --mixed HEAD,会重新设置索引以匹配 HEAD 提交。如果有多个合并基础——这种情况很少见但并非不可能——Git 会随机选择一个。编写一个 shell 函数或 shell 脚本可能会很好,它使用 git merge-base --all 来验证确实存在 one 合并基础,但更简单的别名几乎适用于所有实际情况。