Comportement par défaut du git push
Git est hautement configurable, avec des dizaines de variables possibles dans le fichier de configuration, la longueur de l'aide de l'option config donnant d'ailleurs quelques vertiges. Au lieu de faire un (très) long billet sur les différentes possibilités de configuration, je vous propose plutôt des billets courts sur un thème particulier de la configuration.
Pour ce premier billet, je souhaite aborder le comportement de Git lors d'un push. Ce qui ont suivis l'atelier Git à Bordeaux ne seront pas étonnés par ce choix : ayant totalement oublié cette option, je ne comprenais pas que ma machine ne se comporte pas comme ceux des participants à l'atelier (ma crédibilité en tant qu'expert Git en a sérieusement pâti :).
Commençons par lire le manuel sur l'option push.default :
Defines the action git push should take if no refspec is given on the command line, no refspec is configured in the remote, and no refspec is implied by any of the options given on the command line. Possible values are:
- nothing - do not push anything.
- matching - push all matching branches. All branches having the same name in both ends are considered to be matching. This is the default.
- upstream - push the current branch to its upstream branch.
- tracking - deprecated synonym for upstream.
- current - push the current branch to a branch of the same name.
Comme vous pouvez le constater, si aucun refspec n'est fourni (en ligne de
commande ou par configuration) le comportement par défaut est de pousser
toutes les branches qui existent en local et sur le serveur. Cela veut donc
dire que si vous tapez la commande git push, vous allez mettre à jour sur le
serveur toutes les branches que vous avez modifié en local, ce qui est rarement
souhaitable. Il faudra indiquer le refspec complet git push origin mybranch
pour éviter cela.
C'est pour cette raison que je configure Git avec l'option upstream :
$ git config --global push.default upstream
2 avantages immédiats :
- Il ne pousse que la branche courante.
- Il m'oblige à faire un tracking, ce qui est bien utile.
Maintenant, si vous tentez de pousser la branche test avec la commande
git push sans tracking préalable, Git vous répond :
fatal: The current branch test has no upstream branch. To push the current branch and set the remote as upstream, use
git push --set-upstream origin test
Suivez le conseil de Git en tapant la commande ci dessus pour suivre votre la branche distante test sur origin avec votre branche locale test. Vous verrez immédiatement un changement sur votre shell avec l'information u=, qui indique que vos deux branches sont synchronisées.
Rappel sur le tracking
Le tracking consiste à associer une branche locale et une branche distante
(dite upstream dans la terminologie Git), ce qui informe Git de la branche
distante à utiliser lors d'un git pull. Si vous ne comprenez pas pourquoi
vous n'êtes pas plus souvent confronté à cette notion de tracking, c'est tout
simplement que Git le fait pour vous la plupart du temps :
- Git suit la branche master lors d'un clone.
- Git suit la branche que vous venez de créer si celle ci existe déjà sur le serveur
C'est donc (la plupart du temps) dans le cas ou créer une nouvelle branche en locale sans existance préalable sur le serveur que vous devez explicitement faire cette association.
Des exemples de fichiers gitignore
Le projet gitignore, lancé par GitHub, est une collection de fichiers gitignore. Vous trouverez des exemples pour :
- des langages (Python, Ruby, Scala...)
- des frameworks (Symphony, Rails...)
- des éditeurs (Vim, Emacs, Eclipse...)
- des systèmes d'exploitation (Linux, Windows...)
Note : ces deux dernières catégories se trouvent dans le répertoire Global.
Pour plus d'informations, (re)lisez le billet sur gitignore.
Améliorer sa productivité avec un beau shell
Git est avant un outil qu'on utilise en ligne de commande. Mais par défaut, le shell ne vous indique pas grand chose de la part de Git, en fait rien du tout :). Il faut donc taper des commandes Git pour connaitre l'état de son dépôt. Au minimum :
git statuspour l'état de son répertoire de travail et de son indexgit branchpour connaitre dans quelle branche on se trouve
Et accessoirement, il faut connaitre sa position par rapport à la branche distante, l'état de votre pile stash, etc. Bref, cela fait beaucoup de commandes à taper régulièrement. Il est bien sur possible de le faire, mais cela demande une grande rigueur : il est si facile par exemple de travailler dans la mauvaise branche, ou pire de ne pas travailler dans une branche du tout (ce qu'on appelle un HEAD détaché).
Une solution simple et élégante est d'afficher en permanence ces informations. Comme cela, plus d'erreur (cela reste possible mais l'étourderie est plus difficile). Cela tombe bien, le shell sait trés bien faire ce boulot !
Vous trouverez beaucoup d'exemples sur le Net, mais ils sont souvent trés basiques : généralement, cela se résume à afficher la branche courante sans gérer correctement tous les cas (comme le fameux HEAD détaché cité plus haut). J'ai fouillé le Net de long en large, et voici les 2 projets les plus intéressants à mes yeux :
- git-prompt
- et... le fichier de complétion de Git
Et oui ! Ce dernier, livré en standard avec Git, sait aussi faire de l'affichage, et il est même trés bon ! Passage en revue de ces deux solutions.
Git Prompt
L'avantage est qu'il est, malgré son nom, compatible avec SVN et Hg.
Ses fonctionnalités :
- affichage de la branche courante
- affichage du SHA1 courant
- configuration de l'affichage du chemin (en absolu ou relatif, choix de la taille max)
- affichage du l'état du dépôt, de l'index et du répertoire de travail avec un jeu de couleur (voir plus bas)
- affichage des fichiers qui modifie le depôt (désactivable)
- affichage du statut de sortie de votre commande (il peut bipper sur une erreur)
- la configuration (activation des modules, couleurs...) se fait simplement au travers d'un fichier
- il modifie les labels (titre des fenêtres) en affichant le chemin complet
Un jeu de couleur indique l'état du dépôt ainsi que des fichiers manipulés (entre paranthèse la couleur par défaut) :
- état initial du dépôt (blanc)
- dépôt "propre" (bleu foncé)
- présence de contenu modifié mais non ajouté à l'index (rouge foncé)
- présence de contenu modifié et ajouté à l'index (vert)
- présence de fichier non tracé (bleu clair)
- en cours d'une opération (magenta)
- commit détaché (rouge clair)
Vous avez donc un visuel assez précis de l'état de votre dépôt. Voici un exemple avec la configuration par défaut :

Vous pouvez voir que :
- je suis connecté avec le login sdouche
- ma machine s'appelle fou-hi
- je suis sur le dépot git
- je suis sur la branche "master" (simplifiée en M car master est une convention, comme trunk)
- je suis sur le commit 61d8db
- le statut du dépot est vert, j'ai donc du contenu modifié et ajouté
- un fichier
test.pyqui n'est pas tracé - un fichier
new.txtqui est ajouté
Remarque : l'état du dépôt correspond bien évidemment à l'état du ou des fichiers le plus important (un dépôt ne peut être vert si un fichier est rouge par exemple).
Vous avez maintenant une vue bien plus complète de votre dépôt. Vous pouvez aisement imaginer qu'avoir ces informations en permanence est un plus indéniable. Allez sur ce lien pour voir le shell en action sur plusieurs commandes.
Pour ma part, je n'apprécie pas trop les prompt à rallonge, j'ai donc une configuration bien plus légère :
- pas d'affichage de mon login ni du nom de ma machine (il affiche si les valeurs ne sont pas celles par défaut, par ex. si je suis root)
- un prompt classique ($ pour un utilisateur et # pour root, et non le >)
- couleurs personnalisées (répertoire en magenta par ex.)
- limite de 40 caractères pour l'affichage du chemin
- pas d'affichage des fichiers et / ou répertoires impactés
Voici une copie d'écran :

Vous constatez qu'il est bien plus simple. Vous pouvez voir la couleur verte sur le M, cela signifie donc que j'ai ajouté du contenu dans mon index. J'apprécie beaucoup la visualisation de l'ensemble des fichiers impactés mais malheureusement assez peu les prompts trop long.
Pour l'installer, c'est trés simple :
- Allez sur le projet Github et récupérer les fichiers
git-prompt.shetgit-prompt.conf - Mettez le premier quelque part dans votre compte et renommez le second en
~/.git-prompt.conf - Modifiez votre fichier
~/.bashrcavec cette ligne (remplacez "/path/to" par le répertoire ou vous avez sauvegardé votre fichier) :
[[ $- == i ]] && . ~/path/to/git-prompt.sh
- Editez le fichier ~/.git-prompt.conf` pour configurer selon vos envies
Git Completion
Le fichier de complétion possède tout le nécessaire pour afficher des informations sur votre dépôt, il est même bien plus complet sur certains aspects que git-prompt. Mais ne vous attendait pas à l'utiliser pour autre chose que Git. Il faudra donc choisir selon vos besoins.
En échange, il vous offre pas mal de possibilités, il affiche :
- la branche courante (ou le SHA1 si vous êtes sur un HEAD détaché)
- le caractère % si un contenu non tracé existe
- le caractère * si un contenu tracé mais non ajouté existe
- le caractère + si un contenu ajouté existe
- le caractère $ si vous avez un stash
- le caractère # si le dépôt est dans l'état initial
- la position courante de votre branche locale par rapport à la branche distante
Pour ce dernier, vous avez plusieurs modes :
-
avec le mode auto il affiche :
-
rien s'il n'existe pas de branche upstream
- = si vous les deux branches concordent
- < pour dire vous étes en avance
- > en retard
-
<> pour indiquer une divergeance
-
avec le mode verbose, il affiche le nombre de commits :
-
rien s'il n'existe pas de branche upstream
- u= si vous les deux branches concordent
- u+count si vous étes en avance
- u-count si vous étes en avance
- u+count-u-count pour indiquer une divergeance (nombre de commits dans votre branche et dans la branche upstream)
Cela permet de savoir par exemple si vous avez oublié de pusher ou de merger. Trés pratique, voir indispensable.
Remarques :
- Il affiche aussi le décalage avec SVN (mode git-svn) s'il trouve un serveur SVN dans la configuration du dépôt
- Vous pouvez modifier la configuration du décalage avec upstream pour chaque dépôt avec la variable
bash.showUpstream
Mais ce n'est pas tout ! Il vous indique si vous êtes en cours :
- d'un rebase intéractif en affichant |REBASE-i
- d'un rebase merge en affichant |REBASE-m
- d'un merge en affichant |MERGING
- d'un bisect en affichant |BISECTING
Bref, c'est pour moi le shell le plus complet qui existe. Le seul défaut est qu'il ne gère pas la couleur, c'est à vous de l'ajouter. Vraiment dommage.
Voici ce que donne (avec une ajout de couleurs) :

Vous pouvez voir que je suis en avance d'un commit (u pour upstream) et que j'ai du contenu non tracé et du contenu ajouté mais pas commité.
Pour disposer d'une configuration équivalente, ajoutez ces lignes dans votre fichier ~/.bashrc:
GIT_PS1_SHOWDIRTYSTATE=1 GIT_PS1_SHOWUNTRACKEDFILES=1 GIT_PS1_SHOWSTASHSTATE=1 GIT_PS1_SHOWUPSTREAM="verbose"
PS1='[\[\033[1;35m\]\W$(__git_ps1 " \[\033[1;34m\](%s)")\[\033[0m\]]\$ '
Si vous souhaitez garder votre login et le nom de la machine, voici la configuration de base qui remplace la dernière ligne de la configuration précédente :
PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
Ajouter \u@\h aprés le [ à ma configuration si vous voulez associer la configuration de base avec mon jeu de couleurs.
Notes
- Les informations Git s'affichent uniquement dans le cas ou vous êtes dans un dépôt Git
- Pour en savoir plus sur les couleurs du prompt, je vous conseille ce lien
- pour utiliser Git Completion, il faut bien évidemment que ce dernier soit installé (voir à se sujet un précédent billet)
Mon shell idéal
Cela serait une combinaison des 2 :
- la possibilité de l'utiliser avec Hg, Bzr et Git
- affichage de l'état des fichiers sur la 1ere ligne avec un jeu de couleur (à la git-prompt)
- affichage sur la deuxième ligne du nom du dépôt (ce que personne ne fait), le chemin courant, l'état du dépôt (à la Git Completion)
- affichage du nombre de patchs dans ma pile de Stash
- et surtout le choix des couleurs pour chaque élement visuel. Imaginez par exemple du rouge pour afficher un décalage avec upstream, du bleu si j'ai du stash, etc
Conclusion
Avoir en permanence l'état de votre dépôt est un plus indéniable, que je vous encourage fortement à installer ! Il m'est arrivé plusieurs fois de commiter dans la mauvaise branche, ou d'oublier de pusher. Avec un beau shell, c'est fini (enfin, cela arrive beaucoup moins souvent :).
Simplifiez vous la vie avec les alias
Git est un couteau Suisse, avec beaucoup de sous commandes et d'options. Il n'est pas rare de taper des lignes assez fastidieuses comme celle ci :
$ git log --graph --abbrev-commit --date=relative
Outre le fait qu'il faut retenir par coeur beaucoup de choses (la commande log est à cet égard un bel exemple), cela devient vite trés pénible. Les alias sont la justement pour vous simplifier la vie. Par exemple, pour disposer de raccourci des commandes usuelles :
$ git config --global alias.ci commit
$ git config --global alias.co checkout
$ git config --global alias.br branch
$ git config --global alias.st status
Ou pour reprendre le premier exemple de ce billet :
$ git config --global alias.glog 'log --graph --abbrev-commit --date=relative'
Vous pouvez maintenant visualiser vos logs avec la commande git glog.
Ce qui est formidable avec les alias est qu'ils marchent si on mélange alias et commande "normale". Par exemple :
$ git glog --first-parent
Une autre possibilité intéressante est d'appeler une commande Shell. Il suffit pour cela de commencer son alias avec ! (point d'exclamation). Par exemple :
$ git config --global alias.ka "!gitk --all"
La commande git ka appelle le programme gitk avec l'option --all.
Je vous conseille d'user et d'abuser des alias pour :
- simplifier l'utilisation de commandes complexes
- disposer d'un environnement à votre main, avec des raccourcis qui vous parlent
Pour illuster le deuxieme point, je préfère taper undo que git reset --hard. C'est un choix purement personnel. A titre d'exemple, mon fichier contient 36 alias.
Note : Cerise sur le gateau, la complétion fonctionne aussi avec les alias.
Eviter de commiter des fichiers indésirables
Pour éviter d'historiser des fichiers qui ne nous intéresse pas, il est nécessaire de configurer le fichier ~./gitignore. Bien évidemment, le contenu de ce fichier dépendra fortement du contexte (éditeur utilisé, langage, os...). Personnellement, mon fichier fait 30 lignes, je n'ai donc plus me soucier quand je fais un git add ., et je n'hésite pas à ajouter une exclusion dés que le besoin s'en fait sentir.
La configuration de ce fichier est trés simple :
- 1 ligne par exclusion
- si c'est un répertoire, mettre son nom (comme
egg) - si c'est un fichier, ajouter un wildcard (comme
*.egg)
Enfin, il faut modifier sa configuration Git pour utiliser ce filtre d'exclusion sur tous les dépôts avec la ligne excludesfile=/path/to/.gitignore dans la section [core] (remplacer "/path/to" par le chemin vers votre fichier). Ce qui donne :
[core]
excludesfile=/home/sdouche/.gitignore
Disposer d'un gitignore à deux intérêts :
- éviter de commiter des fichiers indésirables
- ne pas être surchargé d'informations inutiles (présence de fichier non suivis, untracked en anglais)
Nous allons voir plus tard que c'est pénible quand nous avons un prompt qui affiche en permanence la présence de ces fameux fichiers indésirables.
Pour plus d'information sur gitignore, je vous conseille la lecture de la doc.
Note : il est bien sûr possible de disposer d'un fichier gitignore par dépôt, utile quand on veut s'assurer que les développeurs (par exemple sur un projet Libre) ne feront pas la bétise de commiter ces fichiers à cause d'une mauvaise configuration.
Taper plus vite avec la complétion
Le petit doigt de la main gauche est pour un Unixien un doigt important (bon, vous pouvez me rétorquer que tous les doigts sont importants) : c'est celui qu'on utilise pour taper sur la touche tab. Cette touche permet sous Unix de faire la complétion. Autrement dit, de compléter le nom d'une commande quand on tape le début de celle-ci. Non seulement une commande, mais aussi les sous commandes et options (par ex --exclude de la commande diff).
Comment cela marche t'il avec Git ? Voici quelques exemples :
$ git <TAB><TAB>
Le shell répond :
add bundle commit gc rebase ....
En tapant simplement git, la complétion me propose une centaine de sous commandes Git (je ne représente ci dessus que 4 commandes à titre d'exemple). Maintenant si je tape la lettre a :
$ git a<TAB><TAB>
Le shell répond :
add aliases am amend annotate apply archive
Le shell me propose les commandes qui commencent par la lettre a. Le shell complète uniquement si ce que vous tapez est sans ambiguité. Si je reprends l'exemple précédent, si j'avais tapé ad, la complétion aurait donné add.
Mais ce qui est formi-formi-formidable avec la complétion, c'est qu'elle marche aussi sur les options des sous commandes. En reprenant l'exemple de la sous commande add :
$ git add -<TAB><TAB>
Le shell répond :
--dry-run --ignore-errors --intent-to-add --interactive --patch --refresh --update
Comme vous pouvez le constater, c'est un gain de temps incroyable. Pour la petite histoire, c'est la fréquence d'utilisation de cette touche qui me donne une première idée de la familiarité d'un candidat avec Unix en entretien d'embauche :).
Note : vous constaterez peut être que toutes les sous commandes ou options ne sont pas représentées dans la complétion. C'est un choix (ou non, par exemple un oubli ?) des développeurs du fichier de complétion. Il faut en effet modifier manuellement ce fichier à chaque nouvelle version (travail un poil fastidieux, pour vous donner une idée le fichier de la version 1.7.3.2 fait 2419 lignes), il se peut donc que les développeurs fassent des choix. Ceci est aussi valide pour les commandes Shell en général.
Activer la complétion
C'est assez simple, il suffit d'ajouter le fichier de complétion Git soit dans votre profil (fichier ~/.bashrc), soit dans la configuration globale de bash_completion du système. La plupart du temps, cette configuration est automatique à l'installation de Git.
Sous Ubuntu
Le fichier de complétion est automatiquement activé sous Ubuntu car le fichier git est placé dans le répertoire /etc/bash_completion.d/ par le paquet git lui même. Le paquet bash-completion étant lui installé par défaut.
Sous Mac OS X
Il est nécessaire d'installer (avec Homebrew ou MacPorts) le paquet bash_completion si ce n'est pas deja fait, avec la commande :
$ sudo port install bash_completion
ou
$ brew install bash-completion
Puis d'ajouter cette ligne dans votre ~/.bashrc:
source ./path/to/git-completion.bash
Changez /path/to avec le répertoire qui contient le fichier (qui est dofférent selon la méthode d'installation).
Sous Windows
Comme sous Ubuntu, la complétion est activé par défaut avec msysgit.
Configuration minimale pour démarrer
La configuration minimale se résume à donner son nom et son adresse email (pour indiquer qui est le "propriétaire" du commit) :
$ git config --global user.name "Sebastien Douche"
$ git config --global user.email sdouche@gmail.com
L'option --global permet de configurer pour tous les dépôts que vous allez utiliser, en enregistrant ces paramètres dans le fichier ~/.gitconfig.
Avec l'option --system, c'est une configuration pour tous les utilisateurs de la machine, et la configuration se trouve dans le fichier /etc/gitconfig.
Sans option, vous configurez uniquement le dépôt courant, ce qui permet par exemple de spécifier votre adresse personnelle et non professionnelle.
Trois petites informations supplémentaires :
-
Sans cette configuration, Git va utiliser directement les informations contenues dans votre profil utilisateur système, ce qui n'est pas souhaité (par ex.
seb@mamachine). -
Il n'y a pas de vérification par Git. Ce dernier créé une entrée name dans la section user. Si vous tapez
userz.namer, c'est pareil pour lui. -
Vous pouvez éditer directement les fichiers, le résultat est strictement identique. D'ailleurs je vous conseille d'éditer les fichiers pour voir à quoi ils ressemblent (avant et aprés modification).