如何别名git-pipe line指令?

时间:2016-07-23 10:04:56

标签: git

当我输入时 enter image description here 在终端,它的工作原理。

但是,我正在尝试别名此命令,它会发生错误: enter image description here

 Chois@Chois-MacPro book-example $git config --global alias.next 'checkout `git log --reverse --ancestry-path HEAD..master | head -n 1 | cut -d \  -f 2`'
 Chois@Chois-MacPro book-example $git next
error: unknown option `reverse'
usage: git checkout [<options>] <branch>
   or: git checkout [<options>] [<branch>] -- <file>...

    -q, --quiet           suppress progress reporting
    -b <branch>           create and checkout a new branch
    -B <branch>           create/reset and checkout a branch
    -l                    create reflog for new branch
    --detach              detach the HEAD at named commit
    -t, --track           set upstream info for new branch
    --orphan <new-branch>
                          new unparented branch
    -2, --ours            checkout our version for unmerged files
    -3, --theirs          checkout their version for unmerged files
    -f, --force           force checkout (throw away local modifications)
    -m, --merge           perform a 3-way merge with the new branch
    --overwrite-ignore    update ignored files (default)
    --conflict <style>    conflict style (merge or diff3)
    -p, --patch           select hunks interactively
    --ignore-skip-worktree-bits
                          do not limit pathspecs to sparse entries only
    --ignore-other-worktrees
                          do not check if another worktree is holding the given ref
    --progress            force progress reporting

需要你的帮助。

很抱歉附加图片而不是真正的命令,但我无法表达符号`本身。

2 个答案:

答案 0 :(得分:1)

如果Git别名没有感叹号!作为前缀,Git会尝试在其自身内运行别名,这要求它不使用任何特殊的shell功能。

您正在使用的shell功能是反引号扩展 1 和管道(cmd1 | cmd2)。因此,您的特定别名需要!表单。

我看到了别名背后的想法;你可以简化这个别名。但是你可能还想让它更健壮一些,这需要一个小的shell脚本(它可以嵌入到别名中,尽管这会使它更难阅读)。

next做了什么,以及它的轻微缺陷(如果你已经知道的话,请跳到最后)

rev-spec HEAD..master表示master无法从HEAD访问的提交。例如,给出一个相当复杂的图表:

       o--o--o--o        <-- brA
      /     /    \
...--o--o--o--o---o--o   <-- master
      \
       o--o--o           <-- brB
        \
         o--o--o         <-- brC

序列brA..master为您提供此子集(采用提交* - ed,not-taken commit x - ed,无聊无关提交左o):

       x--x--x--x        <-- brA
      /     /    \
...--x--x--x--*---*--*   <-- master
      \
       o--o--o           <-- brB
        \
         o--o--o         <-- brC

brB..master为您提供以下信息:

       *--*--*--*        <-- brA
      /     /    \
...--x--*--*--*---*--*   <-- master
      \
       x--x--x           <-- brB
        \
         o--o--o         <-- brC

使用--ancestry-path将所选提交的列表修剪为仅由左侧名称标识的提交的后代(它们必须已经是右手名称的祖先,因此此约束无效)。对于第一种情况brA..master,这会丢弃一个* - 提交,我将在此处标记!

       x--x--x--x        <-- brA
      /     /    \
...--x--x--x--!---*--*   <-- master
      \
       o--o--o           <-- brB
        \
         o--o--o         <-- brC

而且,确实会带你到&#34; next&#34;向master方向提交。

但是对于brB..master情况,--ancestry-path删除所有提交,并且该集合变为空,因为{{1}没有 {{1} } commit是*

的提示的后代
brB

当然,我们只使用 !--!--!--! <-- brA / / \ ...--x--!--!--!---!--! <-- master \ x--x--x <-- brB \ o--o--o <-- brC 而不是分支名称,这样所有这些都适用于分离的HEAD案例。但是也要考虑这个图表,这是一种苯环提交。我会使用HEAD标记当前的HEAD提交:

H

在这种情况下, o--o / \ ...--H o--o <-- master \ / o--o 是&#34;苯环&#34;的左边缘。然后HEAD选择所有的响铃以及最右边的HEAD..master

o

同时, *--* / \ ...--H *--* <-- master \ / *--* 根本不删除任何内容:每个--ancestry-path - ed提交实际上都是*的后代。 H别名将在半随机选择其中一个假设它选择顶部 - 然后移动到它:

next

此时 H--o / \ ...--o o--o <-- master \ / o--o 永远不会遍历环的下半部分,因为这两个提交不再是next的后代。

换句话说,轻微的缺陷是假设在HEADHEAD的尖端之间没有内部分支和合并序列。如果有这样的序列,并且我们希望访问它们,我们必须 2 一次,预先记录原始的master提交ID集,然后遍历它们(以任何顺序)我们喜欢,可能是一种反向拓扑排序,它允许我们在经过&#34;上半部分&#34;之后跳回到苯环的下半部分,在我们的特定例子中)。

简化别名

--ancestry-path做的是生成提交ID和日志消息的列表,只提取第一行(读取git log ... | head -1 | cut ...,并从中提取ID。

我们可以使用commit <id>更简单地做同样的事情。我们仍然需要git rev-list(或等效的),因为head -1--no-walk为我们提供了-n 1本身的提交ID,而不是下一次提交:

master

(我们也省略了git rev-list --reverse --ancestry-path ..master | head -1 这个词,因为那是默认的。)

如果这会产生 no 输出,我们可能不应该运行HEAD。因此:

git checkout

这是一个双行脚本。将其嵌入别名有点棘手,因为需要引用所有内容。相反,我只是使它成为一个双行shell脚本;但如果你想把它作为别名:

id=$(git rev-list --reverse --ancestry-path ..master | head -1)
test -n "$id" && git checkout "$id" || echo 'no more commits'

这两个都可以取目标的名称,而不是硬编码[alias] next = "!f() { id=$(git rev-list --reverse --ancestry-path ..master | head -1); \ test -n \"$id\" && git checkout $id || echo 'no more commits'; }; f" 。 (但我再次将所有提交ID转储到一个文件中并逐步执行该文件,然后使用&#34; next&#34;&#34; prev&#34;别名。)

1 这通常更好地写为master而不是$(command),因为带括号的形式嵌套并且更容易理解。无论哪种方式,它都需要shell。

2 可以只记录起点和终点,并使用更复杂的算法根据`command`现在和我们已知的位置选择下一个节点遍历顺序。例如,假设我们有起点和终点。我们可以将完整列表生成到文件中,然后HEAD并找到它在文件中的位置,然后跳到文件中的下一个文件并删除文件,这是我们不需要的文件,直到我们去再次前进但实际上,将列表一次性转储到文件中要容易得多,然后一次访问一个条目,而不是在每一步重新生成此文件。

答案 1 :(得分:0)

尝试使用$()语法(easier to parse

而不是使用反引号
git config --global alias.next 'checkout $(git log --reverse --ancestry-path HEAD..master |head -n 1 | cut -d \  -f 2)'