是否可以在Git中使用shell的内置命令进行别名扩展?

我的.gitconfig中有以下别名:

freq = !history | cut -c 8- | grep ^git | sort | uniq -c | sort -n -r | head -n 5

它应该显示我最常使用的5个git命令.取自这里:http://alblue.bandlem.com/2011/04/git-tip-of-week-aliases.html(我知道这篇文章很旧)

当我运行该命令时,它不输出任何内容:

$git freq
$

但是,如果我运行历史|切-c 8- | grep ^ git |排序| uniq -c | sort -n -r |头-n 5,输出给出:

$history | cut -c 8- | grep ^git | sort | uniq -c | sort -n -r | head -n
   5 git log
   5 git status
   5 git current
   5 git co master
   4 git co other-branch

我想这是因为历史是一个shell内置命令,如果我更改了freq别名,例如至:

freq = !history

我明白了:

$git freq
error: cannot run history: No such file or directory
fatal: While expanding alias 'freq': 'history': No such file or directory
$

有没有办法让它发挥作用?

最佳答案 原因

在阅读完问题后,我还认为这个问题是由于历史是一个内置的shell.

调试git

可以通过设置GIT_TRACE环境变量来调试Git命令
为真(有关更多信息,请参阅Scott Chacon的Git Book中有关Git Internals – Environment Variables的相关章节).

$GIT_TRACE=true git freq

10:14:11.901296 git.c:554               trace: exec: 'git-freq'
10:14:11.901296 run-command.c:341       trace: run_command: 'git-freq'
10:14:11.932496 run-command.c:341       trace: run_command: 'history | tail'
10:14:11.963697 run-command.c:192       trace: exec: '/bin/sh' '-c' 'history | tail' 'history | tail'

Checking the source显示外部命令由execv_shell_cmd函数处理,该函数调用sane_execvp,这是execvp标准库函数的一个用户友好的前端 – 如果命令名称不包含斜杠 – 搜索PATH中的每个目录与命令同名的可执行文件.由于历史是一个内置的shell,它永远不会被发现所以它返回一个错误.看到

> execvp man page
> How does execvp run a command?
> execvpe.c

不是一个让事情发生的人,我后来使用其他shell内置函数进行了进一步的实验,这些实验得以实现,所以我认为它必须是别的东西:

没有非交互式shell的历史记录

阅读Bash手册解释了Bash History Facilities仅在interactive shells中使用,而exec调用启动的shell是非交互式的.这就是运行内置历史记录不打印任何输出的原因.

解决方案

选项1:打开非交互式shell中的历史记录功能

感谢Gilles on Unix and Linux,事实证明,通过设置HISTFILE shell变量然后运行set -o history,可以打开非交互式shell中的历史记录功能,如运行:

bash -c "HISTFILE=~/.bash_history; set -o history; history"

在.gitconfig中设置以下别名将起作用:

freq = "!HISTFILE=$HOME/.bash_history; set -o history; history | cut -c 8- | grep ^git | sort | uniq -c | sort -n -r | head -n 5"

注意:如果您使用的是Bash以外的shell,则需要指定shell将其命令历史记录保存到的文件的名称.如果您使用的是Bash,则HISTFILE shell变量可能设置为$HOME / .bash_history以外的其他值.

此外,不应将诸如HISTFILE之类的shell变量导出为环境变量(可用于git),这就是我在此处未使用它的原因.

选项2:从历史文件中读取

更简单的解决方案是直接从shell历史文件中读取:

freq = !grep ^git $HOME/.bash_history | sort | uniq -c | sort -n -r | head -n 5

这将产生与在新的交互式shell中运行history命令相同的结果,因为当新的shell启动时,除了从历史文件中读取的命令之外,它的历史记录中没有命令.

此外,直接从历史文件中读取时不需要cut命令.

选项3:Bash别名

另一种选择是忘记使用Git别名并简单地使用Bash别名:

alias git-freq='history | cut -c 8- | grep ^git | sort | uniq -c | sort -n -r | head -n

保持shell历史文件是最新的

我以为我会添加以下内容作为有用的辅助信息.请注意,它适用于交互式Bash shell,它可能适用于其他现代shell,也可能不适用.

我经常同时在同一主机上运行多个shell,并且我不希望在退出shell时覆盖已保存的历史命令.这是我的.bashrc设置,它确保在每个命令之后写入shell历史文件:

# Append history entries on logout - instead of over-writing the history file.
shopt -s histappend

# Don't store duplicate lines in the history.
# Ignore any commands that begin with a space.
HISTCONTROL=ignoredups:ignorespace

# Use a very large history file to store lots of commands.
# See http://mywiki.wooledge.org/BashFAQ/088
HISTFILESIZE=5000
# Keeping a large history requires system resources
HISTSIZE=3000

# Append to the history file after every command.
PROMPT_COMMAND="history -a"
点赞