Reputation: 6959
I have the following alias in my .gitconfig
:
freq = !history | cut -c 8- | grep ^git | sort | uniq -c | sort -n -r | head -n 5
It should show the 5 git commands I use most. Taken from here: http://alblue.bandlem.com/2011/04/git-tip-of-week-aliases.html (I know the article is old)
When I run the command, it doesn't output anything:
$ git freq
$
But if I run history | cut -c 8- | grep ^git | sort | uniq -c | sort -n -r | head -n 5
, output is given:
$ 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
I guess that this is because history
is a shell builtin command, and if I change the freq
alias, e.g. to:
freq = !history
I get:
$ git freq
error: cannot run history: No such file or directory
fatal: While expanding alias 'freq': 'history': No such file or directory
$
Is there a way to make it work?
Upvotes: 3
Views: 230
Reputation: 12003
After reading the question, I also thought the problem was due to the fact that history
is a shell builtin.
Git commands can be debugged by setting the GIT_TRACE
environment variable
to true
(for more info, see the relevant section on Git Internals - Environment Variables from Scott Chacon’s Git Book).
$ 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 shows that the external command is processed by the execv_shell_cmd
function which calls sane_execvp
, a more user friendly front-end to the execvp
standard library function which – if the command name doesn’t contain a slash – searches each directory in the PATH for an executable with the same name as the command. Since history
is a shell built-in, it won’t ever be found so it returns an error. See
Not being one to let things go, I later carried out further experiments using other shell built-ins and those experiments worked out so I figured it must be something else:
Reading the Bash manual explains that Bash History Facilities are only used in interactive shells while shells started by an exec
call are non-interactive. That’s why running the history
builtin doesn’t print any output.
Thanks to Gilles on Unix and Linux, it turns out that the history features in the non-interactive shell can be turned on by setting the HISTFILE
shell variable and then running set -o history
as can be seen by running:
bash -c "HISTFILE=~/.bash_history; set -o history; history"
Setting the following alias in .gitconfig
will work:
freq = "!HISTFILE=$HOME/.bash_history; set -o history; history | cut -c 8- | grep ^git | sort | uniq -c | sort -n -r | head -n 5"
Note: If you’re using a shell other than Bash, you’ll need to specify the name of the file that your shell saves its command history to. If you are using Bash, it’s possible that the HISTFILE
shell variable is set to something other than $HOME/.bash_history
.
Also, shell variables such as HISTFILE
should not be exported as an environment variable (available to git) which is why I didn’t use it here.
A simpler solution would be to read directly from the shell history file:
freq = !grep ^git $HOME/.bash_history | sort | uniq -c | sort -n -r | head -n 5
This would give the same results as running the history
command in a new interactive shell since when a new shell starts, it has no commands in its history other than what it has read from the history file.
Also, there’s no need for the cut
command when reading directly from the history file.
The other option is to forget about using a Git alias and to simply use a Bash alias:
alias git-freq='history | cut -c 8- | grep ^git | sort | uniq -c | sort -n -r | head -n
I thought I’d add the following as useful ancillary information. Note that it applies to an interactive Bash shell and it may or may not work for other modern shells.
I often run multiple shells on the same host at the same time and I don’t want saved history commands to be over-written when exiting a shell. Here are my .bashrc
settings which ensure that the shell history file is written to after every command:
# 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"
Upvotes: 2