Fork me on GitHub

Le commit initial vide

Nous instaurons la rubrique astuce avec le commit initial vide. Le commit initial a un statut particulier puisque c'est... le premier, l'ancêtre de tous les commits. C'est pourquoi je commence toujours un dépôt par un commit initial vide avec la commande :

$git commit --allow-empty -m "Initial commit."

C'est seulement ensuite que j'ajoute du contenu. A quoi ça sert ? Tout simplement à jouer avec tous les commits de contenu.

Examinons deux exemples.

rebase -i

Un rebase -i demande une limite basse, un commit de référence en sommes. Si mon graphe est composé des commits c1, c2, c3 et c4, je peux jouer sur c2 à c4, mais pas c1, ce dernier étant la référence. Si j'ai du contenu dans c1, je l'ai dans le baba ! Inversement, si c1 est un commit vide, alors tout mon contenu est accessible.

Travailler directement dans une branche

Une bonne pratique est de ne pas travailler dans master, mais uniquement dans des branches (de fonctionnalités, d'intégration ou de correction de bugs) et de merger ensuite (avec sans fast forward selon le workflow que vous avez choisi). Mais comme mon commit initial est obligatoirement dans master, c'est raté. Encore une fois, si ce commit ne contient pas de contenu, le problème disparait de lui-même : mon premier commit de contenu (le deuxième dans le graphe donc) sera lui, dans une branche.

Conclusion

N'oubliez pas que la puissance de Git vient dans la capacité à manipuler votre graphe dans tous les sens, et même si vous ne voyez pas l'intérêt de modifier vos premiers commits, vous en verrez l'utilité plusieurs semaines aprés. Voir des mois après :).

Lire et poster des commentaires

La différence entre Hg et Git

J'aime beaucoup ce billet, il résume parfaitement à mes yeux la différence majeure entre Hg et Git quand on vient de SVN : il est possible d'utiliser Hg (Mercurial) comme un SVN qui marche, ce n'est pas du tout le cas avec Git car très vite, on se retrouve à faire des grosses bêtises si on n'a pas compris les concepts importants.

Hg, un SVN qui marche

Ce n'est pas une attaque contre Hg, bien au contraire, c'est une choix réfléchi de se comporter comme tel :

  • C'est un outil de gestion de source.
  • Chaque commit sait dans quelle branche il se trouve.
  • Les possibilités de manipulation des branches sont assez réduites quand on n'ajoute pas d'extensions.

La volonté du fondateur de Mercurial est de toujours sacrifier les fonctionnalités au profit d'une conception claire, là ou Linus Torvalds avait en tête dés le départ un certain nombre de fonctionnalités. La vitesse étant le seul objectif qui autorise à sacrifier des fonctionnalités.

Hg est un outil puissant, qui autorise ce que SVN ne permettait pas (pour être précis, il le faisait tellement mal qu'on ne se permettait pas de le faire) : une gestion plus fine des branches. Avec Hg, créer des branches et merger ne pose plus de soucis particulier. Comme le dit l'article :

For Hg, the instructor goes through the Hg primer with the student. The student is then left to use Hg with the instructor watching. Every time the student begins to think about branching or merging, the instructor hits the student over the head with the bat. This provides a negative reinforcement for the student's SVN branching and merging habits.

Une fois assimilées la gestion des branches et la notion de distribué, on se débrouille relativement bien. Je connais d'ailleurs peu d'utilisateurs de Mercurial qui ont étudiés le DAG, ou les possiblités avancées des branches (rebase, cherry-pick...).

Git, un outil de gestion de contenu

C'est une notion importante à saisir, Git n'est pas un outil de gestion de source (VCS). Comme avec n'importe quel outil de contenu, il est possible de modifier ou de supprimer ce que l'on veut : Git permet de modifier l'historique, ce qui n'est pas le cas d'un VCS, comme CVS, SVN ou Hg. De ce postulat simple découle une autre philosophie, et donc une utilisation sensiblement différente : une distinction entre branche du DAG et branche utilisateur.

Une branche du DAG (directed Acyclic Graph ou graphe orienté acyclique) est manipulée par Git et permet de suivre les modifications du code : qui est le descendant de qui ? Avec ce graphe, il est simple pour un DVCS de savoir quoi faire lors d'une opération comme une fusion (un merge). Mais chaque branche est anonyme, elle ne porte pas de nom. Les noms de branches sont dans un espace différent, distinct, qui permet de les manipuler indifféremment du DAG. Ou inversement de manipuler le DAG sans toucher aux noms. Cet espace utilisateur est appelé référence.

Note : Je reviendrai la dessus dans un prochain billet, il est temps maintenant de transformer la conférence #gitfr en billet.

Cette distinction est source de confusion, d'erreur et de déception. Mais c'est ce qui rend Git si puissant !

C'est ce que dit la suite du billet :

At the very beginning, the instructor bashes the student repeatedly over the head with the bat until all brain cells containing any memory of SVN are destroyed. The instructor then teaches the student the Git primer.

J'aime beaucoup cette image :). Il ne faut surtout pas utiliser Git comme on utilise SVN, ni de prêt ni de loin, il faut ré-apprendre ce qu'est le contrôle de version. Je ne dit pas que cela est facile, c'est même la raison première de la création de #gitfr.

Mais ce travail est valorisé au centuple une fois Git maitrisé, et vous ouvre un champ des possibles inimaginable quand on vient de SVN...

Lire et poster des commentaires

Google Code supporte Git

Le ticket 2454 est maintenant fermé. Nommé native git support, c'était le ticket le plus demandé (starred) par les utilisateurs. Un peu plus de 2 ans après le support de Mercurial, Google Code supporte donc Git. Cela signifie que les 3 plus gros hébergeurs de code (Sourceforge, Google Code et GitHub) supportent maitenant notre DVCS préféré. Cela signifie aussi que l'avenir s'assombrie pour Mercurial, qui se retrouve (presque) dans la même situation que Bzr, avec un seul hébergeur dépendant important. Terme mal choisi pour désigner un hébergeur qui s'appuie uniquement sur une techno, en l'occurence Bitbucket (Launchpad pour Bzr).

Pourquoi ce changement alors que rien ne semblait prévu ? Je vois deux raisons, l'une technique et l'autre marketing.

L'argument technique

Comme le dit très bien Shawn Pearce, développeur Git, mainteneur des projets Gerrit et JGit (ré-implentation de Git en Java, utilisé par Gerrit et Eclipse notamment), sur la liste de diffusion, Git supporte depuis la version 1.6.6 le protocole smart http. Ce dernier gère bien mieux le protocole http sur lequel Google Code s'appuie massivement. Ce qui a ouvert la voie, il suffisait (hum) ensuite de modifier Git pour gérer l'infrastructure Google. Si cela vous intéresse, jeter un oeil sur la video Google I/O 2009 - Mercurial on BigTable qui explique cette même modification avec Mercurial. Une vidéo similaire devrait sortir pour Git (chouette !).

Chose étonnante, d'après Dave Borowitz, Google code n'utilise pas JGit mais Dulwich, codé en Python. Je suis curieux de connaitre les raisons de ce choix (Shawn avait averti que ce n'était pas JGit mais n'étant pas le responsable du projet, il ne voulait pas en dire plus).

L'argument marketing

La montée en puissance de GitHub ne fait aucun doute, cet article le montre fort bien : entre janvier et mai 2011, le nombre de commits sur GitHub représente le total des commits de Sourceforge, Google Code et CodePlex réunis. Même si ce chiffre est à prendre avec de grosses pincettes, les DVCS poussent au commit unitaire donc à en produire bien plus, c'est un chiffre intéressant (le nombre de lignes de code serait lui significatif).

Et chose intéressante, on retrouve le C++ et Java en tête, cela laisse entendre que ces communautés ont eux aussi (au moins partiellement) basculées sur GitHub (après les communautés Ruby, Javascript et Python).

Pour conclure

Redmonk dit que GitHub est le nouveau centre de gravité, les chiffres le prouvent, et je trouve cela très bien. D'un coté, les développeurs sont poussés à ne plus utiliser des hébergements 1.0 (et zut, je succombe moi aussi à cette mode débile). De l'autre, il pousse les hébergeurs à augmenter drastiquement la qualité. Et on ne va pas s'en plaindre !

Lire et poster des commentaires

gitfr sur GitHub

Par pure feinéantise intellectuelle, je n'ai pas voulu prendre mon temps pour tester diverses solutions techniques pour le blog. J'ai décidé d'aller au plus vite en utilisant les services d'un blog en ligne (tumblr en l'occurrence). Mal m'en a pris ! Malgré quelques avantages indéniables, j'ai ressenti très rapidement les limitations du système :

  • Il faut être en ligne pour écrire, ou alors écrire dans un éditeur et copier le contenu, puis le remettre en forme. De manière générale, tout se fait en ligne à travers une interface simpl(ist)e.

  • Le site tumblr est souvent en surcapacité. Les utilisateurs de Twitter savent de quoi je veux parler :).

  • Des permalinks fantaisistes (ID au lieu du titre).

  • L'incapacité de collaborer sur le site, il faut en effet créer un sous-site, le principal n'étant pas partageable (qui a eu cette idée débile ?).

  • L'outil Twitter mettait en avant l'url tumblr, et non celui du site. Je ne trouve pas ça très élégant

Et j'en passe...

Et maintenant ?

Ce blog est maintenant propulsé par Blogofile, un générateur de site statique, ce qui permet d'être hébergé sur GitHub. Gitfr utilise Git, la boucle est bouclée ! :).

Vous trouverez ce site sous 2 formes :

Remarques

Le bugtracker est à votre dispostion si vous avez des remarques, mais je suis aussi preneur de pull-request : propositions de CSS ou de fonctionnalités supplémentaires.

Vous voulez écrire ?

Blogofile permet de spécifier l'auteur des billets, donc si vous souhaitez écrire sur le blog, je suis aussi preneur, ca serait bien que #gitfr ne reste pas un projet personnel :).

Lire et poster des commentaires

Git 1.7.6 publiée

Nouvelle fournée pour Git avec cette branche 1.7.6.x. A noter que c'est la première version qui respecte le nouveau cycle de release plus court voulu par Junio.

A la lecture du changelog, voici quelques points que je remarque :

  • Le magic pathspec ":/".

  • L'option patch pour la commande git-commit.

  • La possibilité d'utiliser les expressions rationnelles pour git-grep.

  • La variable de configuration merge.ff pour spécifier s'il faut toujours ou jamais faire un commit de merge.

  • Ne pas spécifier de branche à git-rebase signifie rebaser sur upstream.

Changelog

  • Various git-svn updates.

  • Updates the way content tags are handled in gitweb. Also adds a UI to choose common timezone for displaying the dates.

  • Similar to branch names, tagnames that begin with "-" are now disallowed.

  • Clean-up of the C part of i18n (but not l10n---please wait) continues.

  • The scripting part of the codebase is getting prepared for i18n/l10n.

  • Pushing and pulling from a repository with large number of refs that point to identical commits are optimized by not listing the same commit during the common ancestor negotiation exchange with the other side.

  • Adding a file larger than core.bigfilethreshold (defaults to 1/2 Gig) using "git add" will send the contents straight to a packfile without having to hold it and its compressed representation both at the same time in memory.

  • Processes spawned by "[alias] = !process" in the configuration can inspect GIT_PREFIX environment variable to learn where in the working tree the original command was invoked.

  • A magic pathspec ":/" tells a command that limits its operation to the current directory when ran from a subdirectory to work on the entire working tree. In general, ":/path/to/file" would be relative to the root of the working tree hierarchy.

After "git reset --hard; edit Makefile; cd t/", "git add -u" would be a no-op, but "git add -u :/" would add the updated contents of the Makefile at the top level. If you want to name a path in the current subdirectory whose unusual name begins with ":/", you can name it by "./:/that/path" or by "\:/that/path".

  • "git blame" learned "--abbrev[=]" option to control the minimum number of hexdigits shown for commit object names.

  • "git blame" learned "--line-porcelain" that is less efficient but is easier to parse.

  • Aborting "git commit --interactive" discards updates to the index made during the interactive session.

  • "git commit" learned a "--patch" option to directly jump to the per-hunk selection UI of the interactive mode.

  • "git diff" and its family of commands learned --dirstat=0 to show directories that contribute less than 0.1% of changes.

  • "git diff" and its family of commands learned --dirstat=lines mode to assess damage to the directory based on number of lines in the patch output, not based on the similarity numbers.

  • "git format-patch" learned "--quiet" option to suppress the output of the names of generated files.

  • "git format-patch" quotes people's names when it has RFC822 special characters in it, e.g. "Junio C. Hamano" jch@example.com. Earlier it was up to the user to do this when using its output.

  • "git format-patch" can take an empty --subject-prefix now.

  • "git grep" learned the "-P" option to take pcre regular expressions.

  • "git log" and friends learned a new "--notes" option to replace the "--show-notes" option. Unlike "--show-notes", "--notes=" does not imply showing the default notes.

  • They also learned a log.abbrevCommit configuration variable to augment the --abbrev-commit command line option.

  • "git ls-remote" learned "--exit-code" option to consider it a different kind of error when no remote ref to be shown.

  • "git merge" learned "-" as a short-hand for "the previous branch", just like the way "git checkout -" works.

  • "git merge" uses "merge.ff" configuration variable to decide to always create a merge commit (i.e. --no-ff, aka merge.ff=no), refuse to create a merge commit (i.e. --ff-only, aka merge.ff=only). Setting merge.ff=yes (or not setting it at all) restores the default behaviour of allowing fast-forward to happen when possible.

  • p4-import (from contrib) learned a new option --preserve-user.

  • "git read-tree -m" learned "--dry-run" option that reports if a merge would fail without touching the index nor the working tree.

  • "git rebase" that does not specify on top of which branch to rebase the current branch now uses @{upstream} of the current branch.

  • "git rebase" finished either normally or with --abort did not update the reflog for HEAD to record the event to come back to where it started from.

  • "git remote add -t only-this-branch --mirror=fetch" is now allowed. Earlier a fetch-mode mirror meant mirror everything, but now it only means refs are not renamed.

  • "git rev-list --count" used with "--cherry-mark" counts the cherry-picked commits separately, producing more a useful output.

  • "git submodule update" learned "--force" option to get rid of local changes in submodules and replace them with the up-to-date version.

  • "git status" and friends ignore .gitmodules file while the file is still in a conflicted state during a merge, to avoid using information that is not final and possibly corrupt with conflict markers.

  • "git config" used to choke with an insanely long line. (merge ef/maint-strbuf-init later)

  • "git diff --quiet" did not work well with --diff-filter. (merge jk/diff-not-so-quick later)

  • "git status -z" did not default to --porcelain output format. (merge bc/maint-status-z-to-use-porcelain later)

Lire et poster des commentaires