¿Es posible usar la expansión de alias con los commands integrados del shell en Git?

Tengo el siguiente alias en mi .gitconfig :

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

Debería mostrar los 5 commands de git que uso más. Tomado de aquí: http://alblue.bandlem.com/2011/04/git-tip-of-week-aliases.html (Sé que el artículo es viejo)

Cuando ejecuto el command, no produce nada:

 $ git freq $ 

Pero si ejecuto la history | cut -c 8- | grep ^git | sort | uniq -c | sort -n -r | head -n 5 history | cut -c 8- | grep ^git | sort | uniq -c | sort -n -r | head -n 5 history | cut -c 8- | grep ^git | sort | uniq -c | sort -n -r | head -n 5 , se da salida:

 $ 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 

Supongo que esto se debe a que history es un command integrado de shell y si cambio el alias de freq , por ejemplo a:

 freq = !history 

Yo obtengo:

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

¿Hay alguna manera de hacerlo funcionar?

Porque

Después de leer la pregunta, también pensé que el problema se debía al hecho de que la history es un shell incorporado.

Depurando git

Los commands de Git se pueden depurar al establecer la variable de entorno GIT_TRACE en true (para get más información, consulte la sección correspondiente en Git Internals – Variables de entorno del Libro de Git de Scott Chacon).

 $ 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' 

Al verificar la fuente se muestra que el command externo es procesado por la function execv_shell_cmd que llama a sane_execvp , un front-end más amigable para el execvp a la function de biblioteca estándar execvp que – si el nombre del command no contiene una barra – busca en cada directory en la ruta para un ejecutable con el mismo nombre que el command. Como la history es un intérprete de commands incorporado, nunca se encontrará, por lo que devuelve un error. Ver

  • página de manual de execvp
  • ¿Cómo ejecuta execvp un command?
  • execvpe.c

No siendo uno para dejar las cosas ir, más tarde llevé a cabo más experimentos utilizando otros built-ins shell y esos experimentos funcionó, así que pensé que debe ser algo más:

Sin historial de proyectiles no interactivos

El manual Reading the Bash explica que Bash History Facilities solo se usa en shells interactivos mientras que las shells iniciadas por una llamada exec no son interactivas. Es por eso que ejecutar el history no imprime ningún resultado.


Soluciones

Opción 1: activar las características del historial en el shell no interactivo

Gracias a Gilles en Unix y Linux , resulta que las funciones de historial en el shell no interactivo se pueden activar configurando la variable de shell HISTFILE y luego ejecutando el set -o history como se puede ver al ejecutar:

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

Establecer el siguiente alias en .gitconfig funcionará:

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

Nota: Si está usando un shell que no sea Bash, necesitará especificar el nombre del file en el que su shell guarda su historial de commands. Si está utilizando Bash, es posible que la variable de shell HISTFILE esté configurada en algo que no sea $HOME/.bash_history .

Además, las variables de shell como HISTFILE no se deben exportar como una variable de entorno (disponible para git) y es por eso que no lo HISTFILE aquí.

Opción 2: leer del file de historial

Una solución más simple sería leer directamente desde el file de historial de shell:

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

Esto daría los mismos resultados que ejecutar el command history en un nuevo shell interactivo, ya que cuando se inicia un nuevo shell, no tiene commands en su historial que no sean los que se leyeron en el historial.

Además, no es necesario el command de cut cuando se lee directamente desde el file de historial.

Opción 3: Bash alias

La otra opción es olvidarse de usar un alias de Git y simplemente usar un alias de Bash:

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

Mantener el file de historial de shell actualizado

Pensé en agregar lo siguiente como información auxiliar útil. Tenga en count que se aplica a un shell Bash interactivo y puede funcionar o no para otros shells modernos.

A menudo ejecuto varias shells en el mismo host al mismo time y no quiero que se sobrescriban los commands de historial guardados al salir de un shell. Aquí están mis configuraciones de .bashrc que aseguran que el file del historial del shell se escriba después de cada 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=ignonetworkingups: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"