Si j’étais marseillais un verre de ricard à la main, je me serai posé cette question bien plus tôt !
Mais ce n’est pas le cas. Heureusement il n’est jamais trop tard ! ;)

Par habitude, afin de mettre à jour un repository GIT distant, j’utilise git pull.
Ca fonctionne à merveille, tout le monde a ses données à jour et on se croirait presque au pays des bisounours.
Mais Gargamel vient mettre son nez là-dedans (ouais je mélange Schtroumpf et Bisounours. Honte sur moi !).

Ce matin, j’ai voulu, avant de faire la release de la version 1.0.0 de jesus (car il est prêt pour la production), mettre à jour la documentation.
Le principe est simple. J’utilise GitHub Pages. Il me faut donc juste une branche « gh-pages », dont le contenu correspond à ce qui est en ligne chez GitHub pour ma documentation.
Dès que je commit sur cette branche, la page en ligne est mise à jour.

Cependant mes deux branches master et gh-pages n’ont pas le même index. Les deux ne peuvent pas être mergées !
Ce matin, je me vois donc faire un

git pull origin gh-pages:gh-pages

En me disant « comme cela, je récupère le contenu de la branche de documentation ».
Et la je me retrouve avec une dizaine d’erreurs de conflit ! WTF comme dirait l’autre.

Et justement pour le coup, j’aurai du faire un

git fetch origin gh-pages:gh-pages

La différence entre ces deux commandes en apparence similaires est pourtant énorme.
Git fetch ne fait que récupérer les données. Git pull les récupère et fait un merge avec la branche courante …
Du coup merge mes deux branches, vu qu’elles ont pas la même base, c’est pas super cool.

Moralité : vous pouvez faire un git fetch afin de mettre à jour votre copie locale avec les derniers commits distants. La branche sur laquelle vous faites le fetch aura les derniers commits.
Mais votre branche de travail ne sera pas modifiée.
Si vous faites un git pull, non seulement la branche sur laquelle vous faites le fetch aura les derniers commits distants. Mais votre branche de travail sera également mergée avec ces derniers commits.

En y réflechissant bien, fetch est bien plus recommandable que pull, qui peut casser un repository assez facilement.

Désolé j’ai dit dans mon précédent article que je ne reparlerai pas de GIT cette semaine.
Mais je tiens à partager ma découverte du jour que je trouve particulièrement cool :)

Il peut être particulièrement utile de, par exemple, recevoir un email à chaque fois que l’un des développeurs de votre application fait un push sur le repository distant.
Ou encore vous pouvez désirer exécuter automatiquement les tests de votre application avant chaque commit et n’autoriser le commit que si tous passent.
Et automatiser ce genre de choses pourrait être particulièrement kiffant.

Du coup … haaaave you met GIT Hooks ?
Dans le répertoire .git/hooks de votre application existent plusieurs fichiers .sample. Chacun représente un hook et est exécuté au moment opportun.
Par exemple « post-receive » sera exécuté du côté du repository distant à chaque fois que quelqu’un poussera le projet :)
De même « pre-commit » sera exécuté du côté du client avant chaque commit (et vous pourrez décider de refuser le commit automatiquement).

Par exemple plaçons dans notre fichier « post-receive.sample » que nous commencons par renommer en y enlevant l’extension « .sample » afin qu’il se nomme « post-receive » (il doit également être exécutable).

/home/ruby/scripts/post-receive.rb $1 $2 $3

Le script /home/ruby/scripts/post-receive.rb sera exécuté automatiquement à chaque post receive.
Et dans ce script nous plaçons un envoi de mail :

require 'net/smtp'
msg = "Subject: A push has been made\r\n\r\nA push has been made on one of your repositories.\r\nOld commit: " + ARGV[0] + "\r\nNew commit: " + ARGV[1] + "Ref: " + ARGV[2]
Net::SMTP.start('localhost') do |smtp|
    smtp.send_message msg, 'noreply@localhost', 'you@your_email.com'
end

Ainsi à chaque push fait, nous envoyons un email à l’adresse de notre choix (you@your_email.com) signalant qu’un nouveau push a été fait.
Cela peut être une liste de diffusion par exemple.
Mais bien évidemment cela peut être autre chose qu’un email. Il est tout à fait possible, par exemple, de signaler cela sur un salon Campfire utilisé par l’équipe de développement.

Note : si vous hébergez votre repository sur GitHub, il ne vous est pas utile de créer de hook post-receive de cette manière.
Ceux-ci sont gérés via l’interface web.

Pour quelques informations supplémentaires et notamment l’exécution automatique des tests avant un commit que j’ai mentionné plus haut, je vous invite à lire Les hooks sur le Git Book (fr).

Rassurez vous ceci est le dernier article de cette série sur GIT. Je pense repartir sur un peu de rails la semaine prochaine.
On me pose souvent la question « pourquoi choisir GIT et pas SVN ? »

Voici donc de manière absolument pas triée ni objective tous les arguments qui font que je préfère GIT à SVN (et de loin).
Notez que ce n’est que mon opinion. Vous pouvez en avoir une autre c’est absolument pas génant.

  • Pouvoir être nomade

    C’est ce que je montre dans mon article précédent.
    Avec GIT, contrairement à SVN, vous pouvez continuer à committer et ainsi conserver un repository avec des commits propres et pas un gros commit une fois de temps en temps. Même si vous n’êtes pas connecté ou n’avez pas accès à votre repository distant. Vous restez productif même en déplacement.

  • Une vraie gestion des tags et branches

    Avec SVN si vous désirez créer une nouvelle branche c’est pas compliqué : vous faites un copier/coller de votre répertoire trunk, le mettez dans branch/nom_de_la_branche. Et c’est fait. Y’a plus qu’à committer. Et le jour ou vous voulez fusionner les deux branches, morflez.
    Pareil pour les tags.

    GIT gère les branches et tags de manière native.
    Pas besoin d’action manuelle (copier/coller).
    Vous faites un « git branch <nouvelle_branch> » et votre branche est créée.
    Un « git checkout <branch> » et vous changez de branche active.
    Et un « git rebase <branch> <seconde_branch> » et vos deux branches sont fusionnées !

  • GitHub

    Ouais c’est pas vraiment GIT.
    Mais je kiffe littéralement GitHub.
    Pouvoir forker un projet en un clic; pouvoir commenter des commits etc.
    Tout projet libre devrait permettre ce genre de choses sur son repository. C’est ça qui pousse les autres développeurs à créer des patchs.

Ces trois arguments me suffisent personnellement à nettement préférer GIT à SVN.
Et vous ? Quel est votre logiciel de contrôle de version préféré ?

Je viens de réaliser un screencast sur GIT et la synchronisation multi repositories.
Celui-ci explique :

  • Comment transmettre les données committées sur votre serveur de développement
  • Comment les transmettre sur un système de fichier local (par exemple une clé USB) lorsque vous êtes en hors ligne
  • Comment récupérer les derniers commits distants

Vous pouvez visualiser ce screencast ci-dessous.

Lorsque vous manipulez un repository GIT en utilisant (voir en abusant (ce qui n’est pas forcément négatif)) les branches, vous pouvez régulièrement vous poser la question « dans quelle branche suis-je ? Est-ce la bonne ? »
Et c’est toujours particulièrement lourd de devoir faire un
git branch

Afin de voir toutes les branches et de trouver l’active.
Je viens cependant de tomber sur une astuce donnée par le support github.

# git branch
parse_git_branch() {
    git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1) /'
}
PS1="\$(parse_git_branch)$PS1"

Placez ceci dans ~/.bashrc ou /etc/bashrc (pour que cela soit valide pour tous les utilisateurs de la machine).
Rechargez votre console et admirez :)

dmathieu@dmathieu-ubuntu:~$ cd /home/dmathieu/projects/refstats/
(master) dmathieu@dmathieu-ubuntu:~/projects/refstats$

Lorsque vous êtes dans un repository GIT, la branche courante s’affichera.
Dans le cas contraire, rien ne s’affichera :)

Certaines personnes, notamment Clément (et d’autres) m’ont demandé il y a quelques semaines de cela de rédiger un article sur les commandes GIT de base pour une utilisation console.
Déjà, sachez qu’il existe de multiples interfaces graphiques pour Windows et Mac.
Il en existe également pour Linux. Mais je n’ai rien trouvé qui ne convienne à mes exigences. Donc je fais tout en ligne de commande et j’utilise gitk pour visualiser les commits faits et en cours.
Je n’aborde ici que les commandes de base, pour le développement de tous les jours. Pas de notion de branches ou de tags donc.

A chacune des commandes ci-dessous, il faut précéder l’exécutable « git ». Nous aurons donc : git <nom de commande>

  • add

    Permet d’ajouter des fichiers au prochain commit. Cette commande prends comme paramètre le nom du fichier ou du répertoire à ajouter.
    Exemples :

    git add app/models/mon_model.rb

    Ajoutera le fichier app/models/mon_model.rb à la liste des fichiers à committer.

    git add app/models

    Ajoutera tous les fichiers présents dans le dossier app/models dans la liste des fichiers à committer

    git add .

    Ajoutera tous les fichiers du projet à committer.
    Evidemment seuls les nouveaux fichiers ou ceux modifiés seront ajoutés. C’est le principe même d’un SCM ;)

  • rm

    Vous l’aurez peut-être deviné, cette commande est l’inverse de add. Elle vous permet de supprimer un fichier du repository.
    En effet un fichier supprimé physiquement n’est pas supprimé dans le repository. Il faut donc le faire via ce rm.
    Note : si vous supprimer un fichier existant avec cette méthode, il sera supprimé physiquement.

    Exemples :

    git rm app/models/mon_model.rb

    Supprimera le fichier app/models/mon_model.rb

    git rm -r app/models

    Supprimera tous les modèles du projet.
    Notez le -r qui force la récursivité car vous supprimez un répertoire.

  • commit

    Après avoir ajouté et supprimé tout plein de fichiers à notre prochain commit, il faut faire celui-ci !
    La commande commit permet donc de valider les changements faits. Tant que vous n’avez pas committé, vous pouvez revenir en arrière (même après en fait. Mais on verra pas cela dans cet article).

    Exemple :

    git commit -m "Un super message de description"
  • reset

    Comme je disais, tant que vous n’avez pas committé, vous pouvez annuler des changements particulièrement simplement avec git reset.
    Cette commande réinitialise tous les fichiers dans votre repository à leur état lors du dernier commit. Vous annulez donc tous les changements non committés et revenez sur une base propre.

    Exemple :

    git reset --hard HEAD
  • push

    Après avoir committé vos changements, il vous faut probablement pousser ceux-ci sur le repository distant afin de pouvoir partager vos modifications avec les autres développeurs du projet.
    Ou bien tout simplement de pouvoir récupérer ces changements sur une autre machine (celle de test ou de production par exemple).

    Exemples :

    git push

    Poussera la branche courante sur le repository distant nommé « origin »

    git push backup

    Poussera la branche courante sur le repository distant nommé « backup »

    git push backup V2

    Poussera la branche nommée « V2″ sur le repository distant nommé « backup ».

  • pull

    Pousser c’est bien. Mais il faut récupérer les données qui ont été poussées par vous sur une autre machine ou par un autre développeur.
    Cette commande fonctionne avec exactement les même arguments que la précédente mais pour récupérer les données de votre repository.

Voila. Avec tout cela, vous pouvez :

  • Ajouter, supprimer des fichiers et les committer
  • Annuler une modification non committée
  • Envoyer et récupérer les données sur un repository distant

Ce qui est, au final, les commandes GIT de tous les jours (dans l’utilisation que j’en fais du moins).
Peut-être ferais-je d’autres articles plus avancés au niveau de la gestion des branches ou de l’annulation d’un commit. Mais cela viendra dans un prochain article.

 
Fork me on GitHub