Créer ses
alias Git & Bash
avec autocomplétion

Comment se créer des raccourcis et des commandes manquantes, avec une autocomplétion pertinente et contextualisée ?

1. Présentation

Besoin de créer des commandes manquantes dans Git ? Besoin de raccourcis pour des actions récurrentes Git ? Vous avez sûrement déjà dû utiliser des alias Bash pour créer ce dont vous avez besoin. Mais il s’avère que Git donne aussi la possibilité de créer ses propres alias et ses commandes complexes.

Mais alors quoi choisir pour créer ces raccourcis ? Git ou Bash ?

Nous allons voir ensemble les avantages et inconvénients de ces deux approches, et nous allons découvrir comment les coupler.

2. Alias Git VS Alias Bash

2.1. Exemple d’un alias Git

$ git config --global alias.ck "checkout"   # Créer l'alias `git ck`
$ git ck develop                            # Changement de branche avec l'alias Git
La commande $ git config --global alias.x …​ enregistre automatiquement l’alias dans le fichier global ~/.gitconfig. L’alias Git sera accessible depuis n’importe quel autre terminal.
~/.gitconfig
[alias]
        ck = checkout

2.2. Exemple d’un alias Bash

$ alias ck="git checkout"   # Création de l'alias Bash `ck`
$ ck develop                # Changement de branche avec l'alias Bash
L’alias Bash sera perdu dès la fermeture du terminal en cours. Si nous souhaitons le conserver, nous devons l’enregistrer dans le fichier ~/.bashrc ou ~/.bash_aliases.

2.3. Avantages et inconvénients

Git Bash

Usage

$ git <alias>

$ <alias>

Sauvegarde

L’alias Git est enregistré automatiquement dans la configuration de Git.

Pour être conservé, l’alias Bash doit être écrit dans le fichier .bashrc ou .bash_aliases.

Surcharge

Un alias Git global peut être surchargé par l’alias Git du dépôt.

Pas de surcharge d’alias Bash en fonction du contexte.

Conflits

Les alias Git et les alias Bash n’interfèrent pas entre eux.

Autocomplétion

Les alias Git bénéficient par défaut de l’autocomplétion de Git.

Les alias Bash ne bénéficient pas par défaut de l’autocomplétion de Git.

3. Conservez une autocomplétion contextualisée

L’alias git ck (checkout) aura une autocomplétion Git activée par défaut.

Commande Autocomplétion

git <TAB><TAB>

Affichera les commandes et les alias disponibles dans Git.

git c<TAB><TAB>

Affichera uniquement les commandes et les alias commençant par c.

git ck <TAB><TAB>

Affichera la liste des branches.

git ck --<TAB><TAB>

Affichera les options disponibles pour checkout.

L’alias Bash ck (checkout) n’a, quant à lui, pas accès par défaut à l’autocomplétion de Git.

Commande Autocomplétion

<TAB><TAB>

N’affichera rien.

c <TAB><TAB>

Affichera toutes les commandes et les alias Bash commençant par c.

ck <TAB><TAB>

N’affichera pas la liste des branches dans Git.

ck --<TAB><TAB>

N’affichera rien.

3.1. Alias Bash : comment activer l’autocomplétion Git ?

Comme les alias Bash ne bénéficient pas par défaut de l’autocomplétion de Git, vous devrez spécifier explicitement quelle autocomplétion utiliser.

Par exemple, les commandes suivantes permettent d’activer l’autocomplétion git push pour notre alias Bash p :

$ alias p="git push"
$ __git_complete p _git_push

3.2. Alias Git : comment choisir une autocomplétion spécifique ?

Nous verrons par la suite que certains alias Git complexes peuvent perdre une autocomplétion correcte. Dans ce cas, vous devrez spécifier l’autocomplétion dont vous aurez besoin :

Pour Utiliser

git myalias

function _git_myalias { _git_<command>; }

git my-alias

function _git_my_alias { _git_<command>; }

git my.alias

Pas de fonction disponible

_git_<command> peut-être _git_branch, _git_commit, _git_checkout,_git_diff, _git_pull, _git_push, _git_reset, _git_status, …​

Tapez dans votre terminal _git_<TAB> pour découvrir toutes les fonctions disponibles.

Par exemple, les commandes suivantes permettent d’activer l’autocomplétion git branch pour notre alias git myalias :

$ git config --global alias.myalias "!ici une commande complexe"
$ function _git_myalias { _git_branch; }

4. Tuto : créer l’alias git rename

4.1. Étape #1 : créer l’alias git rename

Pour renommer nos branches locales, nous allons créer un alias git rename, avec une autocomplétion qui affichera les branches disponibles. Notre alias sera basé sur la commande git branch (https://git-scm.com/docs/git-branch#Documentation/git-branch.txt—​m).

Création :

$ git config --global alias.rename "branch -m"

Votre nouvel alias se retrouve automatiquement dans le fichier de configuration global de Git :

~/.gitconfig
[alias]
        rename = branch -m

Usage :

$ git rename oldname newname   # Renomme la branche `oldname` en `newname`
$ git rename newname           # Renomme la branche courante en `newname`

Autocomplétion OK :
Dans ce premier cas, git rename <TAB><TAB> nous affiche par défaut les branches disponibles.

4.2. Étape #2 : créer l’alias Bash gr

Création :

$ alias gr="git branch -m"

Usage :

$ gr oldname newname   # Renomme la branche `oldname` en `newname`
$ gr newname           # Renomme la branche courante en `newname`

Autocomplétion KO :
gr <TAB><TAB> n’a pas accès par défaut à l’autocomplétion des branches Git.

Activation de l’autocomplétion

Exploitons l’astuce du chapitre précédent Alias Git : comment choisir une autocomplétion spécifique ? :

$ __git_complete gr _git_branch

Autocomplétion OK :
gr <TAB><TAB> gagne l’autocomplétion des branches Git avec les fonctions __git_complete et _git_branch.

Synthèse

$ alias gr="git branch -m"
$ __git_complete gr _git_branch

4.3. Étape #3 : créer & coupler gr et git rename

Création :

$ git config --global alias.rename "branch -m"
$ alias gr="git rename" (1)
$ __git_complete gr _git_branch
1 Nous rattachons l’alias gr à git rename au lieu de git branch -m.

Deux avantages à cette approche :

  • gr bénéficiera automatiquement de toutes les évolutions de git rename

  • Avec ce couplage, il n’est plus nécessaire de trier et de se rappeler des fonctionnalités accessibles uniquement côté Bash et celles accessibles uniquement côté Git.

4.4. Étape #4 : usage du point d’exclamation !

Durant l’exécution de la commande, nous souhaiterions afficher le message rename branch oldbranch to newbranch. L’alias git rename doit exécuter plusieurs actions : changer un nom de branche et afficher un message.

Pour construire des alias Git plus complexes, nous devons utiliser le point d’exclamation ! : Ce signe indique qu’il faut exécuter une commande à part entière.

Création :

$ git config --global alias.rename '!echo "rename branch $1 to $2"; git branch -m'
~/.gitconfig
[alias]
        rename = "!echo \"rename branch $1 to $2\"; git branch -m"

Usage :

$ git rename oldbranch newbranch
rename branch oldbranch to newbranch

Autocomplétion KO :
git rename <TAB><TAB> n’a plus accès à l’autocomplétion des branches Git.

Réactivation de l’autocomplétion

Avec l’usage du point d’exclamation !, certains alias Git complexes perdent une autocomplétion pertinente.

Pour réactiver l’autocomplétion avec les noms des branches, exploitons l’astuce vue dans le chapitre Alias Git : comment choisir une autocomplétion spécifique ?. Utilisons la méthode implicite _git_rename, rattachée automatiquement à l’alias git rename :

$ function _git_rename { _git_branch; }

Autocomplétion OK :
git rename <TAB><TAB> retrouve l’autocomplétion des branches Git.

Et pour l’alias Bash gr, remplaçons _git_branch par _git_rename :

$ __git_complete gr _git_rename

Synthèse

$ git config --global alias.rename '!echo "rename branch $1 to $2"; git branch -m'
$ function _git_rename { _git_branch; }

$ alias gr="git rename"
$ __git_complete gr _git_rename

4.5. Étape #5 : usage de !f() { …​ }; f

Il serait plus pertinent d’afficher un message après la commande git branch, et seulement si le renommage ne retourne pas d’erreur.

Par exemple, si nous renommons la branche pikachu (qui n’existe pas) en raichu, nous aurons l’erreur suivante :

$ git branch -m pikachu raichu
error: refname refs/heads/pikachu not found
fatal: Branch rename failed

Il faut aussi prendre en compte le fait que les arguments s’ajoutent tous à la fin de la commande rattachée à l’alias :

# Ainsi exécuter :
$ git rename <arg1> <arg2>

# Sera équivalent à exécuter :
$ echo "Rename branch <arg1> to <arg2>"; git branch -m <arg1> <arg2>

Si nous déplaçons echo à la fin, l’alias Git donnera :

$ git config --global alias.rename '!git branch -m $1 $2; echo "Rename branch $1 to $2"'
# Ainsi exécuter
$ git rename <arg1> <arg2>

# Sera équivalent à exécuter :
$ git branch -m <arg1> <arg2>; echo "Rename branch <arg1> to <arg2>" <arg1> <arg2> (1)
Rename branch oldbranch to newbranch oldbranch newbranch (2)
1 Les arguments <arg1> <arg2> sont nécessairement ajoutés en fin de ligne.
2 Le message sera mal structuré.

Pour maîtriser ce comportement complexe, nous devons cloisonner notre commande dans une fonction !f() { …​ }; f :

$ git config --global alias.rename '!f() { git branch -m $@ && echo "Rename branch $1 to $2"; }; f'
~/.gitconfig
[alias]
        rename = "!f() { git branch -m $@ && echo \"Rename branch $1 to $2\"; }; f"

Plusieurs astuces se cumulent ici :

  • Dans git branch -m $@, nous utilisons tous les arguments transmis avec $@.

  • Avec &&, si git branch retourne une erreur, on n’exécute pas echo.

  • Dans echo "Rename branch $1 to $2", on réutilise les arguments $1 et $2.

  • On n’oublie pas de point-virgule ; en fin de ligne dans le corps de la fonction f() { …​; }.

  • On ne met pas de point-virgule ; à la fin de la commande f() { …​; }; f, pour que f exploite les arguments transmis à l’alias git rename.

Si la branche pikachu existe, nous aurons :

$ git rename pikachu raichu
Rename branch pikachu to raichu

Si la branche pikachu n’existe pas, nous aurons une erreur sans notre message final :

$ git rename pikachu raichu
error: refname refs/heads/pikachu not found
fatal: Branch rename failed

Si la branche raichu existe déjà, nous aurons une erreur sans notre message final :

$ git rename pikachu raichu
fatal: A branch named 'raichu' already exists.

Synthèse

$ git config --global alias.rename '!f() { git branch -m $@ && echo "Rename branch $1 to $2"; }; f'
$ function _git_rename { _git_branch; }

$ alias gr="git rename"
$ __git_complete gr _git_rename

4.6. Étape #6 : affiner l’usage des paramètres

À cette étape, nous gérons les erreurs. Par contre, si nous renommons la branche courant pikachu en raichu, nous aurons un message de confirmation mal structuré :

$ git rename raichu
Rename branch raichu to

Dans ce cas nous avons besoin de deux choses :

  1. Avoir le nom de la branche courante

  2. Conditionner le comportement de l’alias git rename s’il n’y a pas d’argument $2

La commande git rev-parse --abbrev-ref HEAD donne le nom de la branche courante. Utilisons la dans l’alias git rename :

$ git config --global alias.rename '!f() { [ -z "$2" ] && o=$(git rev-parse --abbrev-ref HEAD) || o=$1 && n=${2:-$1} && git branch -m $@ && echo "Rename branch $o to $n"; }; f'
~/.gitconfig
[alias]
        rename = "!f() { [ -z \"$2\" ] && o=$(git rev-parse --abbrev-ref HEAD) || o=$1 && n=${2:-$1} && git branch -m $@ && echo \"Rename branch $o to $n\"; }; f"

Explications

$ git config --global alias.rename '!f() {
  [ -z "$2" ] && o=$(git rev-parse --abbrev-ref HEAD) (1)
  || o=$1 (2)
  && n=${2:-$1} (3)
  && git branch -m $@ (4)
  && echo "Rename branch $o to $n"; (5)
}; f'
1 Initialisation de la variable o (pour oldname) : si l’argument $2 est null, alors o sera la branche courante…​
2 sinon o sera initialisé avec l’argument $1.
3 Initialisation de la variable n (pour newname) : si l’argument $2 est renseigné, alors il sera utilisé. Dans le cas contraire, on utilisera l’argument $1.
4 Exécution de la commande branch avec tous les paramètres.
5 Affichage du message.

Nous aurons maintenant :

$ git rename raichu
Rename branch pikachu to raichu (1)
1 Le nom de la branche courante est bien récupéré à la volée et injecté dans le message.

Synthèse

$ git config --global alias.rename '!f() { [ -z "$2" ] && o=$(git rev-parse --abbrev-ref HEAD) || o=$1 && n=${2:-$1} && git branch -m $@ && echo "Rename branch $o to $n"; }; f'
$ function _git_rename { _git_branch; }

$ alias gr="git rename"
$ __git_complete gr _git_rename

4.7. Étape #7 : sauvegardez tous nos alias dans .bash_aliases

Dans le cadre du tutoriel, nous avons créé nos alias directement dans le terminal. Nous allons maintenant regrouper et écrire tous nos alias Git et Bash dans le même fichier ~/.bash_aliases.

Votre fichier ~/.bashrc va vérifier que le fichier ~/.bash_aliases existe. Si c’est bien le cas, le fichier ~/.bash_aliases sera importé. Vous devez retrouver la condition suivante :

~/.bashrc
if [ -f ~/.bash_aliases ]; then
    . ~/.bash_aliases
fi
Alias : differences entre .bash_rc, .bash_aliases et /usr/local/bin - https://askubuntu.com/questions/2528/aliases-difference-between-bash-rc-bash-aliases-and-usr-local-bin.

Si ~/.bash_aliases n’existe pas

Si vous tentez de lire le fichier inexistant ~/.bash_aliases, la commande cat vous retournera une erreur :

$ cat ~/.bash_aliases
cat: /home/<user>/.bash_aliases: No such file or directory

Dans ce cas créons le fichier vide :

$ touch ~/.bash_aliases

Si ~/.bash_aliases existe

Éditons le fichier :

$ vim ~/.bash_aliases

Écrivons les lignes suivantes :

~/.bash_aliases
# --- Alias Git ---

git config --global alias.rename '!f() { [ -z "$2" ] && o=$(git rev-parse --abbrev-ref HEAD) || o=$1 && n=${2:-$1} && git branch -m $@ && echo "Rename branch $o to $n"; }; f'
function _git_rename { _git_branch; }

# --- Alias Bash ---

# avoid error "__git_complete: command not found"
# @see https://stackoverflow.com/a/47496210/13480534
[ -f /usr/share/bash-completion/completions/git ] && . /usr/share/bash-completion/completions/git

alias gr="git rename"
__git_complete gr _git_rename

echo "Git & Bash aliases loaded!"
Dans le fichier ~/.bash_aliases, pour utiliser la fonction __git_complete, nous sommes obligés de charger auparavant le fichier /usr/share/bash-completion/completions/git. Plus d’infos sur https://stackoverflow.com/a/47496210/13480534.

Petits rappels pour Vim :

Touche Action

i

Insert mode

ESC

Command mode

:wq

Pour sauver et sortir

:q!

Pour quitter sans sauvegarder

Quand vous avez sauvegardé votre fichier ~/.bash_aliases, rechargez ~/.bashrc :

$ . ~/.bashrc
Git & Bash aliases loaded!

Voilà ! Nos alias Git et Bash sont bien chargés, et le seront à chaque ouverture de terminal.

5. Dernières astuces

5.1. Comment gérer l’erreur de frappe gti ?

Frapper gti à la place de git ! Combien de fois vous êtes-vous agacé par une perte d’autocomplétion suite à cette petite erreur de frappe ? Remédions à ce souci avec une petite astuce vue dans https://stackoverflow.com/a/24665529 :

$ alias gti="git"
$ __git_complete gti __git_main

Maintenant, gti se comporte exactement comme git 🎉

Vous n’avez plus qu’à ajouter ces commandes dans votre fichier ~/.bash_aliases :

~/.bash_aliases
# --- Alias Git ---

git config --global alias.rename '!f() { [ -z "$2" ] && o=$(git rev-parse --abbrev-ref HEAD) || o=$1 && n=${2:-$1} && git branch -m $@ && echo "Rename branch $o to $n"; }; f'
function _git_rename { _git_branch; }

# --- Alias Bash ---

# avoid error "__git_complete: command not found"
# @see https://stackoverflow.com/a/47496210/13480534
[ -f /usr/share/bash-completion/completions/git ] && . /usr/share/bash-completion/completions/git

alias gti="git"
__git_complete gti __git_main

alias gr="git rename"
__git_complete gr _git_rename

echo "Git & Bash aliases loaded!"

5.2. Supprimer des alias du fichier ~/.gitconfig

Si vous renommez vos alias Git dans le fichier ~/.bash_aliases, les anciens noms seront toujours conservés dans le fichier ~/.gitconfig. Vous devrez supprimer manuellement ces anciens alias.

Méthode #1 : modifier directement le fichier

Éditer Commande

La config globale

git config --global --edit

La config du dépôt

git config --edit

Méthode #2 : supprimer en ligne de commande

Supprimer Commande

Un alias global

git config --global --unset alias.<nom>

Toutes les références globales dupliquées

git config --global --unset-all alias.<nom>

Un alias du dépôt

git config --unset alias.<nom>

Toutes les références du dépôt dupliquées

git config --unset-all alias.<nom>

6. Pour aller plus loin

Vous pouvez retrouver mes expérimentations d’une centaine d’alias sur le Gist Git and Bash aliases defined and documented in a single .bash_aliases file, with Git auto-completion.

Git Xtended, mon outil maison open source, qui me facilite les commandes récurrentes avec Git, exploite tous les mécanismes vus dans cet article, et vous propose une pléiade d’alias Git et Bash bien documentés. Plus d’infos sur :

Bonne lecture !