<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Pierre Schambacher.com &#187; billet invité</title>
	<atom:link href="http://www.pierreschambacher.com/blog/tag/billet-invite/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.pierreschambacher.com</link>
	<description>Ingénieur en Informatique pour Intellicore</description>
	<lastBuildDate>Fri, 28 May 2010 06:45:08 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.9.2</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>git in a nutshell Part 2</title>
		<link>http://www.pierreschambacher.com/blog/git-in-a-nutshell-part-2/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=git-in-a-nutshell-part-2</link>
		<comments>http://www.pierreschambacher.com/blog/git-in-a-nutshell-part-2/#comments</comments>
		<pubDate>Thu, 20 May 2010 07:00:03 +0000</pubDate>
		<dc:creator>soutade</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Programmation]]></category>
		<category><![CDATA[billet invité]]></category>
		<category><![CDATA[git]]></category>

		<guid isPermaLink="false">http://www.pierreschambacher.com/?p=325</guid>
		<description><![CDATA[Comme je l&#8217;avais promis dans mon court billet de dimanche dernier, mon ami Grégory nous offre le deuxième volet du tutoriel consacré à Git.
Dans le cas où vous auriez malencontreusement loupé la première partie consacrée à l&#8217;utilisation générale de Git, ou que vous souhaitiez tout simplement le relire pour le plaisir: http://www.pierreschambacher.com/blog/git-in-a-nutshell/
Relire le premier billet ne serait [...]]]></description>
			<content:encoded><![CDATA[<p style="text-align: justify;">Comme je l&#8217;avais promis dans mon court billet de dimanche dernier, mon ami Grégory nous offre le deuxième volet du tutoriel consacré à Git.<br />
Dans le cas où vous auriez malencontreusement loupé la première partie consacrée à l&#8217;utilisation générale de Git, ou que vous souhaitiez tout simplement le relire pour le plaisir: <a href="http://www.pierreschambacher.com/blog/git-in-a-nutshell/">http://www.pierreschambacher.com/blog/git-in-a-nutshell/</a></p>
<p style="text-align: justify;">Relire le premier billet ne serait d&#8217;ailleurs pas idiot puisqu&#8217;il y a eu quelques ajouts dans le tutoriel précédents (stashs, hooks, alias).</p>
<p style="text-align: justify;"><span id="more-325"></span></p>
<h1 style="text-align: justify;">Système de version décentralisé vs centralisé ?</h1>
<p style="text-align: justify;">La puissance d&#8217;un modèle décentralisé à la git c&#8217;est justement qu&#8217;il n&#8217;est pas figé. En effet il peut aussi bien être utilisé avec un dépôt local unique, avec une multitude de dépôts locaux, ainsi qu&#8217;avec un dépôt central&#8230; ou la combinaison de tous ces modes, tandis qu&#8217;avec un dépôt central unique on ne peut changer de modèle. Si on rajoute la facilité d&#8217;utilisation et la rapidité des opérations, il n&#8217;y a plus de raisons d&#8217;aller voir ailleurs ! Bien sûr on pourra toujours critiquer les aspects de configurations à mettre en place pour un mode centralisé, mais quand on achète une Ferrari il faut aussi apprendre à la conduire.</p>
<p style="text-align: justify;">
<h1 style="text-align: justify;">Commandes de base</h1>
<p style="text-align: justify;">L&#8217;utilisation en mode centralisé est aussi simple, côté client, que l&#8217;utilisation générale de Git. Pour ainsi dire il n&#8217;y a que 4 commandes importantes :</p>
<ul>
<li>clone</li>
<li>remote</li>
<li>pull</li>
<li>push</li>
</ul>
<h2 style="text-align: justify;">clone</h2>
<blockquote>
<p style="text-align: justify;">git clone dépôt_distant [dossier]</p>
</blockquote>
<p style="text-align: justify;">Permet de récupérer un dépôt distant dans dossier. Les protocoles supportés sont : rsync, http, https, git, ssh, file. Les fichiers étant compressés et empaquetés un checkout est donc très rapide comparé à un svn qui va tout télécharger unitairement !<br />
Par défaut la référence au projet distant sera « origin » (voir remote).</p>
<p style="text-align: justify;">
<h2 style="text-align: justify;">Remote</h2>
<p style="text-align: justify;">Cette commande permet de gérer les dépôts distants :</p>
<p style="text-align: justify;">Lister les références aux dépôts distants :</p>
<blockquote>
<p style="text-align: justify;">git remote [-v]</p>
</blockquote>
<p style="text-align: justify;">Ajouter la référence nom à un dépôt distant. Dans le cas de clone, origin référencera le dépôt distant :</p>
<blockquote>
<p style="text-align: justify;">git remote add nom dépôt_distant</p>
</blockquote>
<p style="text-align: justify;">Supprimer une référence</p>
<blockquote>
<p style="text-align: justify;">git remote rm reference</p>
</blockquote>
<p style="text-align: justify;">Renommer une référence :</p>
<blockquote>
<p style="text-align: justify;">git remote rename ancienne_ref nouvelle_ref</p>
</blockquote>
<p style="text-align: justify;">Modifier une référence</p>
<blockquote>
<p style="text-align: justify;">git remote set-url reference nouvelle_adresse</p>
</blockquote>
<p style="text-align: justify;">Afficher les informations d&#8217;une référence</p>
<blockquote>
<p style="text-align: justify;">git show reference</p>
</blockquote>
<p style="text-align: justify;">
<h2 style="text-align: justify;">pull</h2>
<blockquote>
<p style="text-align: justify;">git pull [--rebase] reference branche</p>
</blockquote>
<p style="text-align: justify;">Cette commande est équivalent à un checkout de svn. On va récupérer l&#8217;état actuel de la branche distante. Le paramètre &#8216;reference&#8217; est la référence que nous avons manipulé avec la commande remote. Par défaut on utilise origin. Après le pull il y a un merge automatique, sauf avec l&#8217;option &#8211;rebase qui permet de revenir à la version exacte du serveur (équivalent à un reset –hard de la version sur le serveur).</p>
<h2 style="text-align: justify;">push</h2>
<blockquote>
<p style="text-align: justify;">git push reference branch</p>
<p style="text-align: justify;">git push &#8211;tags dépôt</p>
</blockquote>
<p style="text-align: justify;">Cette Commande est l&#8217;inverse de pull, elle permet d&#8217;envoyer ses modifications sur le serveur. C&#8217;est l&#8217;équivalent d&#8217;un checkin de svn.<br />
L&#8217;option &#8211;tags permet d&#8217;envoyer la liste de tous tags locaux. Pour n&#8217;envoyer qu&#8217;un seul tag on procède comme suit :</p>
<blockquote>
<p style="text-align: justify;">git push reference tag NomDuTag</p>
</blockquote>
<p style="text-align: justify;">
<h1 style="text-align: justify;">Cycle de vie des commandes</h1>
<p style="text-align: justify;">Un petit exemple vaut mieux qu&#8217;un long discours :</p>
<p style="text-align: justify;">Initialisation</p>
<blockquote>
<p style="text-align: justify;">git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git linux</p>
</blockquote>
<p>ou</p>
<blockquote>
<p style="text-align: justify;">git init linux<br />
cd linux<br />
git remote add origin git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6.git<br />
git pull origin master</p>
</blockquote>
<p style="text-align: justify;">Ensuite on fait des modifications comme vu dans le tutoriel précédent avec la puissance d&#8217;un gestionnaire de version décentralisé.</p>
<p style="text-align: justify;">Pour se resynchroniser avec le serveur :</p>
<blockquote>
<p style="text-align: justify;">git commit -a<br />
git pull origin master</p>
</blockquote>
<p style="text-align: justify;">Puis on pousse nos modifications sur le serveur :</p>
<blockquote>
<p style="text-align: justify;">git push origin master</p>
</blockquote>
<p>Simple non ?</p>
<h1 style="text-align: justify;">On est mal barrés !</h1>
<p style="text-align: justify;">Les dépôts distants sont généralement crées avec l&#8217;option &#8211;bare et se terminent par .git</p>
<blockquote>
<p style="text-align: justify;">git init &#8211;bare dossier.git</p>
</blockquote>
<p style="text-align: justify;">Un dépôt de type « bare » est en fait un dépôt sans espace de travail (working directory), ainsi il n&#8217;y a pas directement les fichiers sources mais uniquement le contenu du dossier .git. Cela empêche l&#8217;utilisation de certaines commandes (git add, git rm, git commit …).</p>
<p style="text-align: justify;">
<h1 style="text-align: justify;">Supprimer une référence distante</h1>
<p style="text-align: justify;">Il peut être utile de supprimer à distance une branche ou un tag. Pour se faire il suffit d&#8217;ajouter « : » avant la référence à supprimer. En théorie lors de la spécification d&#8217;une référence on devrait la noter +ref_locale:ref_distante, donc si on omet la référence locale il y aura suppression. La syntaxe de git permet de se passer du « + » et du « : » comme dans l&#8217;exemple.</p>
<p style="text-align: justify;">Supprimer un tag distant :</p>
<blockquote>
<p style="text-align: justify;">git push &#8211;tags origin :TagASupprimer</p>
</blockquote>
<p style="text-align: justify;">Supprimer une branche distante :</p>
<blockquote>
<p style="text-align: justify;">git push origin :MaBranche</p>
</blockquote>
<p style="text-align: justify;">Pour lister les références distantes, on utilise la commande :</p>
<blockquote>
<p style="text-align: justify;">git ls-remote [--heads] [--tags] reference</p>
</blockquote>
<p style="text-align: justify;">
<h1 style="text-align: justify;">Exporter un dépôt sur le réseau</h1>
<p style="text-align: justify;">Le site <a href="http://www.jedi.be/blog/2009/05/06/8-ways-to-share-your-git-repository">http://www.jedi.be/blog/2009/05/06/8-ways-to-share-your-git-repository</a> (in English, sorry) liste les différentes façons d&#8217;exporter les dépôts git.<br />
La version maison est un démon intégré à git qui écoute sur le port 9418 par défaut, il va scanner les dossiers de sa liste blanche et s&#8217;il détecte le fichier &laquo;&nbsp;git-daemon-export-ok&nbsp;&raquo; dans le .git du dépôt, alors celui-ci sera exporté.</p>
<blockquote>
<p style="text-align: justify;">git daemon [--export-all] [--base-path=path][--user-path] [--reuseaddr] [directory…]</p>
</blockquote>
<ul>&#8211;export-all exporte tous les dépôts que le fichier &laquo;&nbsp;git-daemon-export-ok&nbsp;&raquo; soit présent ou non</ul>
<ul>&#8211;base-path permet d&#8217;indiquer le répertoire de base des dépôts</ul>
<ul>&#8211;user_path permet d&#8217;utiliser la notation ~user dans l&#8217;adresse de git clone</ul>
<ul>&#8211;reuseaddr permet de pouvoir se lier au port même s&#8217;il est déjà utilisé (après un crash en général)</ul>
<p style="text-align: justify;">On peut spécifier ensuite les répertoires de la liste blanche (en plus de &#8211;base-path). On peut utiliser un démarrage manuel ou un service comme inetd.</p>
<p style="text-align: justify;">
<h1 style="text-align: justify;">Contrôle d&#8217;accès au dépôt</h1>
<p style="text-align: justify;">Et bien c&#8217;est simple, git n&#8217;intègre pas de mécanisme de contrôle d&#8217;accès aux dépôts ! C&#8217;est un gestionnaire de version, il laisse donc ce travail à d&#8217;autres couches systèmes spécialisées. Mais ceci vient aussi du fait que le mode décentralisé est celui par défaut, donc si quelqu&#8217;un a fait une modification dans le code de Linux, il va demander à Linus de faire un pull de ses modifications. Dans ce schéma il n&#8217;y a pas besoin d&#8217;intégrer des mécanismes de sécurité divers et variés.<br />
Néanmoins il existe des solutions.</p>
<ul>
<li>Pour http(s), on peut utiliser l&#8217;authentification intégrée (voir liée à un LDAP)</li>
<li>Pour un système de fichier on a toute la gestion des droits intégrée au système (un peu lourd à mettre en place)</li>
<li>Idem pour ssh, mais cela nécessite en plus de créer des comptes ssh sur le serveur</li>
</ul>
<p style="text-align: justify;">Néanmoins la solution la plus pratique est Gitosis. Avec Gitosis il n&#8217;est nécessaire de créer qu&#8217;un seul compte sur la machine « git », chaque utilisateur est authentifié par sa clé ssh (clé unique, authentification forte) et les dépôts peuvent avoir un accès lecture seule ou lecture/écriture.<br />
Pour modifier la configuration de Gitosis, on fait un pull du projet gitosis-admin, on obtient l&#8217;arborescence suivante :</p>
<ul>
<li><strong>keydir</strong> dossier qui contient les fichiers de clés ssh publiques (.pub) de tous les utilisateurs</li>
<li><strong>gitosis.conf</strong> fichier qui contient la configuration des dépôts</li>
</ul>
<p style="text-align: justify;">Ensuite modifications, commit, push comme vu plus haut.<br />
Les dépôts contrôlés par Gitosis sont situés dans /home/git/repositories, mais rien n&#8217;empêche de faire des liens symbolique pour les stocker ailleurs.<br />
Exemple de fichier de configuration :</p>
<blockquote>
<p style="text-align: justify;">[gitosis]<br />
## Autoriser gitweb à montrer tous les dépôts<br />
gitweb = no</p>
<p>## Autoriser git-daemon à publier tous les dépôts<br />
daemon = no</p>
<p>[group quux]<br />
members = jdoe wsmith @anothergroup<br />
writable = repo1 repo2<br />
readonly = repo3</p>
<p>[group anothergroup]<br />
members = alice bill</p>
<p>[repo foo]<br />
## Configuration par dépôt au lieu de la configuration globale pour gitweb (idem pour git daemon)<br />
gitweb = yes</p></blockquote>
<p style="text-align: justify;">On définit des groupes avec des membres et les dépôts auxquels ils peuvent accéder en lecture seule ou lecture/écriture. La syntaxe de contrôle est inversée par rapport à ce qu&#8217;on a l&#8217;habitude de voir : un dépôt ne définit pas ses permissions, c&#8217;est un groupe qui définit ses accès.<br />
Le seul défaut de Gitosis est que la granularité des accès se font au niveau du dépôt complet (y compris pour les tags). Pour avoir un contrôle plus fin on sera obligé de passer par des hooks (le hook update en l&#8217;occurrence) ou de se tourner vers Gitolite qui a un comportement identique avec la possibilité de définir précisément les droits d&#8217;accès.</p>
<p style="text-align: justify;">Pour accéder à un dépôt on procède de la manière suivante :</p>
<blockquote>
<p style="text-align: justify;">git clone git@serveur:repo1</p>
</blockquote>
<p><a rel="license" href="http://creativecommons.org/licenses/by-sa/2.0/fr/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by-sa/2.0/fr/88x31.png" /></a><br /><span xmlns:dc="http://purl.org/dc/elements/1.1/" href="http://purl.org/dc/dcmitype/Text" property="dc:title" rel="dc:type">Tutoriel GIT</span> by <a xmlns:cc="http://creativecommons.org/ns#" href="http://www.pierreschambacher.com/" property="cc:attributionName" rel="cc:attributionURL">Gr&#233;gory Soutad&#233;</a> est mis &#224; disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-sa/2.0/fr/">licence Creative Commons Paternit&#233;-Partage des Conditions Initiales &#224; l&#8217;Identique 2.0 France</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.pierreschambacher.com/blog/git-in-a-nutshell-part-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>git in a nutshell</title>
		<link>http://www.pierreschambacher.com/blog/git-in-a-nutshell/?utm_source=rss&amp;utm_medium=rss&amp;utm_campaign=git-in-a-nutshell</link>
		<comments>http://www.pierreschambacher.com/blog/git-in-a-nutshell/#comments</comments>
		<pubDate>Mon, 05 Oct 2009 19:45:00 +0000</pubDate>
		<dc:creator>Pierre Schambacher</dc:creator>
				<category><![CDATA[Articles]]></category>
		<category><![CDATA[Programmation]]></category>
		<category><![CDATA[billet invité]]></category>
		<category><![CDATA[git]]></category>

		<guid isPermaLink="false">http://www.pierreschambacher.com/?p=172</guid>
		<description><![CDATA[Aujourd&#8217;hui, place à un billet invité. C&#8217;est mon ami Grégory Soutadé qui propose un petit tutoriel pour apprendre à utiliser les différentes commandes de GIT, le gestionnaire de versions, pour un usage personnel. J&#8217;ai modestement aidé à la rédaction et mise en forme de ce document que vous trouverez très bientôt au format PDF.
Vous vous [...]]]></description>
			<content:encoded><![CDATA[<p>Aujourd&#8217;hui, place à un billet invité. C&#8217;est mon ami Grégory Soutadé qui propose un petit tutoriel pour apprendre à utiliser les différentes commandes de GIT, le gestionnaire de versions, pour un usage personnel. J&#8217;ai modestement aidé à la rédaction et mise en forme de ce document que vous trouverez très bientôt au format PDF.</p>
<p style="text-align: justify;">Vous vous lancez dans un petit projet perso ? Vous aurez toutes les clés en main pour que GIT soit votre gestionnaire de versions sur ce projet.</p>
<p style="text-align: justify;"><em>Cet article ne se veut pas exhaustif mais reflète le résultat d&#8217;un usage quotidien par un autodidacte. Si vous souhaitez corriger une éventuelle erreur ou partager une connaissance, n&#8217;hésitez pas à laisser un commentaire.<br />
Il est recommandé pour la bonne compréhension de cet article de connaitre SVN.<br />
L&#8217;auteur du tutoriel est un utilisateur Linux et certaines commandes peuvent poser problème sur d&#8217;autres système (notamment gitk).</em></p>
<p style="text-align: justify;"><em><span style="text-decoration: underline;">Note à nos amis sous Windows:</span> vous pouvez utiliser git sur Windows à l&#8217;aide de msysgit disponible <a title="Git pout Windows" href="http://code.google.com/p/msysgit/">ici</a>.<br />
</em></p>
<p style="text-align: justify;"><span id="more-172"></span></p>
<p style="text-align: justify;">Tout d&#8217;abord qu&#8217;est ce que git ?! Et bien avant d&#8217;être un logiciel de gestion de versions décentralisé (ou DSCM pour Distributed Source Code Management) il a été conçu comme un système de fichiers avec un système de versionning, le tout construit pour la performance. Et quoi de plus normal quand on sait qu&#8217;il était destiné à être le système de gestion de versions du noyau Linux en remplacement de BitKeeper. Git a été écrit par Junio Hamano et Linus Torvald lui même. Bref on a entre les mains un gros bébé, pourtant simple à utiliser et performant : tout est instantané !<br />
On entend par décentralisé le fait de pouvoir « commiter » et faire évoluer un bout de code en local indépendamment d&#8217;un serveur central (contrairement à svn qui nécessite un serveur central), cela offre un confort de développement très important car on ne commite que ce qui marche VRAIMENT. Bien sûr on a tout le loisir de travailler avec un serveur central, il est même possible de faire des ponts avec un serveur svn, mais l&#8217;aspect collaboratif ne sera pas abordé ici.</p>
<p style="text-align: justify;">Comme pour svn, chaque commit donne lieu à une nouvelle version (ou révision). Git a choisit non pas d&#8217;utiliser des numéros de révision, mais un identifiant unique SHA-1 pour chaque commit (donc révision). Cela évite d&#8217;avoir des conflits entre les différentes révisions de toutes les branches de tous les clones du dépôt. Il n&#8217;est pas toujours nécessaire d&#8217;indiquer le SHA-1 complet d&#8217;un objet, mais seulement les premiers octets qui le différencie des autres objets. Si j&#8217;ai deux commits :<br />
<strong>3bdc</strong>6feaa01b6b86a0f94acab32bc3ad4c207a5d<br />
<strong>3bdc</strong>4d695b90545c891392a486da9a9e07c053b<br />
je peux identifier le premier avec 3bdc6 et le second avec 3bdc4</p>
<p style="text-align: justify;">Chaque commande git peut être appelée de deux manières différentes :</p>
<blockquote>
<p style="text-align: justify;">git commande</p>
</blockquote>
<p style="text-align: justify;">Après ces petites notions théoriques passons à la pratique !!</p>
<h1 style="text-align: justify;">Par où qu&#8217;on commence ?</h1>
<p style="text-align: justify;">Et bien pour commencer il faut initialiser ou récupérer un dépôt. Avant d&#8217;initialiser un dépôt il faut renseigner quelques variables permettant de vous identifier lors des commit. Donc :</p>
<blockquote>
<p style="text-align: justify;">git config &#8211;global user.name Grégory<br />
git config &#8211;global user.email greg@wordpress.com<br />
git config &#8211;global core.editor emacs<br />
mkdir MonDepot<br />
cd MonDepot<br />
git init</p>
</blockquote>
<p style="text-align: justify;">L&#8217;avant dernière ligne est importante si vous ne savez/voulez pas utiliser vim comme éditeur de messages lors des commits. Un joli fichier ~/.gitconfig contiendra votre configuration pour les projets suivants. Si vous omettez le –global, les informations ne seront inscrites que pour le dépôt crée.</p>
<p style="text-align: justify;">Il est aussi possible de récupérer un dépôt git déjà existant depuis : ssh, système de fichier local, internet (http://, ftp://, git://) :</p>
<blockquote>
<p style="text-align: justify;">git clone soutade@monserveur:/mondepot mondepot<br />
git clone /mon/de/pot mondepot<br />
git clone http://www.kernel.org/mondepot<br />
&#8230;</p>
</blockquote>
<h1 style="text-align: justify;">Et maintenant qu&#8217;on a un beau dépôt ?!</h1>
<p style="text-align: justify;">Et bien il faut faire des opérations sur les fichiers :</p>
<h2 style="text-align: justify;">Modification</h2>
<blockquote>
<p style="text-align: justify;">emacs MonFichier.c</p>
</blockquote>
<h2 style="text-align: justify;">Ajout au dépôt</h2>
<blockquote>
<p style="text-align: justify;">git add MonFichier.c</p>
</blockquote>
<h2 style="text-align: justify;">Renomage du fichier</h2>
<blockquote>
<p style="text-align: justify;">git mv [-f] MonFichier.c MonFichier2.c</p>
</blockquote>
<p style="text-align: justify;">Si vous ne passez par cette commande, git considère que le fichier aura été supprimé et il faudra faire un git add sur le nouveau fichier.<br />
-f pour forcer le rennomage (si le fichier existe)</p>
<h2 style="text-align: justify;">Suppression</h2>
<blockquote>
<p style="text-align: justify;">git rm [-f] [--cached] MonFichier.c</p>
</blockquote>
<p style="text-align: justify;">Si vous ajoutez l&#8217;option –cached, le fichier sera uniquement supprimé du gestionnaire de versions, mais pas du système de fichier.</p>
<h1 style="text-align: justify;">Oui, mais maintenant ça compile !</h1>
<p style="text-align: justify;">Si ça compile, c&#8217;est que ça marche (&#8230;). Bref il est temps de commiter ! Mais avant j&#8217;effectue toujours deux opérations :</p>
<blockquote>
<p style="text-align: justify;">git status</p>
</blockquote>
<p style="text-align: justify;">Cela permet de voir les fichiers ajoutés/supprimés/modifiés, voir si on n&#8217;a rien oublié (un nouveau fichier par exemple). « Untracked files » correspond aux fichiers qui existent mais qui ne sont pas (encore) pris en compte dans le gestionnaire de versions.</p>
<blockquote>
<p style="text-align: justify;">git diff [--color]</p>
</blockquote>
<p style="text-align: justify;">Avec cette opération on peut visualiser les modifications (au format « patch ») effectuées sur nos fichiers, cela va nous aider à renseigner le message du commit.</p>
<p style="text-align: justify;">Cette commande peut prendre plusieurs options :</p>
<blockquote>
<p style="text-align: justify;">git diff [--color] [&lt;commit1&gt;..&lt;commit2&gt;] [--] [paths]</p>
</blockquote>
<p style="text-align: justify;">et toutes les options de diff</p>
<p>&lt;commit1&gt;..&lt;commit2&gt; est un intervalle de commits, sachant que &lt;commit&gt; peut être désigné sous la forme :</p>
<ul>
<li> le SHA-1 d&#8217;un commit</li>
<li> le tag</li>
<li> une référence depuis le commit courant</li>
</ul>
<p style="text-align: justify;">
<p style="text-align: justify;">paths correspond à n fichiers</p>
<h2 style="text-align: justify;"><em>Apparté</em> : Le commit courant</h2>
<p style="text-align: justify;">Dans git, le commit courant (ou dernier commit avant modifications) s&#8217;appelle HEAD. On peut accéder aux commits précédents en les référençant par rapport à HEAD :<br />
HEAD^ : Père du commit courant<br />
HEAD^^ : Grand père du commit courant<br />
HEAD~n : ancêtre de nième génération de HEAD (grand grand &#8230; grand parent de HEAD)</p>
<p style="text-align: justify;">
<h2 style="text-align: justify;"><em>Apparté</em> : .gitignore</h2>
<p style="text-align: justify;">.gitignore est un fichier à créer dans le dépôt git pour ignorer un certains nombre de fichiers (dans git status). Par exemple tous les fichiers « MonFichier~ » crées par emacs. A chaque ligne on intègre une expression régulière pour le ou les fichiers à ignorer (*~ pour emacs):<br />
Si la ligne fini par /, cela désigne un répertoire et tous ce qu&#8217;il y a dessous.<br />
Si la ligne commence par !, on a la négation de l&#8217;expression.<br />
Si la ligne est vide ou qu&#8217;elle commence par #, elle est ignorée.</p>
<p style="text-align: justify;">Étant donné qu&#8217;on ne va pas re créer ce fichier pour chaque dépôt, on peut en spécifier un qui sera réutilisé à chaque fois (qui peut être composé avec un .gitignore interne au dépôt). Pour cela il faut créer un fichier quelconque avec les même règles que .gitignore et inscrire la variable globale core.excludesfile :</p>
<blockquote>
<p style="text-align: justify;">git config &#8211;global core.excludesfile CheminFichier</p>
</blockquote>
<p>Attention toutefois, le chemin ne supporte pas ~ (Référence à la racine du répertoire personnel).</p>
<p style="text-align: justify;">
<p style="text-align: justify;">Et le tant attendu :</p>
<blockquote>
<p style="text-align: justify;">git commit [-a] [-m « message »] [paths]</p>
</blockquote>
<p style="text-align: justify;">L&#8217;option -a (recommandée) permet d&#8217;ajouter toutes les modifications au dépôt, sinon il faut spécifier les fichiers dans paths ou faire explicitement un git add (même sur des fichiers modifiés) pour chaque fichier à intégrer au commit.<br />
Si vous ne précisez pas le message sur la ligne de commande (-m), un éditeur va s&#8217;ouvrir (vim par défaut) pour que vous entriez le message du commit. Le message du commit est obligatoire !</p>
<p style="text-align: justify;">Si Bob s&#8217;est trompé dans son message de commit, il est toujours possible de le modifier avec :</p>
<blockquote>
<p style="text-align: justify;">git commit &#8211;amend -c &lt;commit&gt;</p>
</blockquote>
<h1 style="text-align: justify;">Un peu d&#8217;histoire</h1>
<p style="text-align: justify;">Maintenant qu&#8217;on a fait plusieurs commits et que notre projet grossi il peut être intéressant d&#8217;avoir un résumé de l&#8217;état du projet. Pour cela deux commandes :</p>
<blockquote>
<p style="text-align: justify;">git log [options] [&lt;commit1&gt;..&lt;commit2&gt;] [--] [paths]</p>
</blockquote>
<p style="text-align: justify;">Liste des commits avec leurs message influant sur les fichiers paths.</p>
<blockquote>
<p style="text-align: justify;">git show [options] &lt;commit&gt; [--] [paths]</p>
</blockquote>
<p style="text-align: justify;">Contenu de tous les fichiers modifiés ou seulement ceux dans paths.<br />
Les options sont celles de git diff.</p>
<p style="text-align: justify;">En ligne de commande la visualisation n&#8217;est pas aisée, c&#8217;est pourquoi je recommande l&#8217;utilisation de la commande gitk qui permettra d&#8217;avoir une vue graphique et synthétique du dépôt.</p>
<h1 style="text-align: justify;">Eh Bob, j&#8217;ai une idée !</h1>
<p style="text-align: justify;">Oui, mais les idées de Bob c&#8217;est pas toujours terrible, heureusement git permet de créer FACILEMENT des branches, pas besoin d&#8217;appeler la gconf et d&#8217;imputer sur le budget du projet déjà en déficit &#8230;<br />
Bref pour créer un branche :</p>
<blockquote>
<p style="text-align: justify;">git branch NomDeLaBranche [BrancheInitiale]</p>
</blockquote>
<p style="text-align: justify;">Et voilà la branche est crée à partir de HEAD ou de BrancheInitiale.</p>
<p style="text-align: justify;"><strong>ATTENTION</strong> vous êtes toujours dans la branche de départ, pour pouvoir changer de branche il faut faire un :</p>
<blockquote>
<p style="text-align: justify;">git checkout NomDeLaBranche</p>
</blockquote>
<p style="text-align: justify;">Pour récupérer uniquement un fichier d&#8217;une autre branche :</p>
<blockquote>
<p style="text-align: justify;">git checkout AutreBranche paths</p>
</blockquote>
<p>Ces deux actions peuvent être combinée en :</p>
<blockquote><p>git checkout -b NomDeLaBranche [BrancheInitiale]</p></blockquote>
<p style="text-align: justify;">Bon ben en fait Bob il a des idées toutes pourries, suppression de la branche :</p>
<blockquote>
<p style="text-align: justify;">git branch -d NomDeLaBranche</p>
</blockquote>
<p style="text-align: justify;"><strong>ATTENTION</strong> la branche sera détruite sans confirmation, toutes les modifications seront perdues et il ne sera pas possible de les retrouver !!</p>
<p style="text-align: justify;">On peut lister les branches avec :</p>
<blockquote>
<p style="text-align: justify;">git branch</p>
</blockquote>
<p style="text-align: justify;">Pour voir le contenu d&#8217;un fichier d&#8217;une autre branche :</p>
<blockquote>
<p style="text-align: justify;">git show MaBranche:MonFichier</p>
</blockquote>
<p style="text-align: justify;">Oui, mais mon idée était beaucoup plus classe que celle de Bob, je vais donc l&#8217;intégrer dans la branche principale (master) :</p>
<blockquote>
<p style="text-align: justify;">git checkout master<br />
git merge MesBranches</p>
</blockquote>
<p style="text-align: justify;">(on peut merger depuis plusieurs branches)<br />
S&#8217;il n&#8217;y a pas de conflit, l&#8217;opération est commitée (sauf option &#8211;no-commit).</p>
<p style="text-align: justify;">Ah il y a un conflit que git n&#8217;est pas capable de résoudre: il nous l&#8217;indique et il faut le résoudre  manuellement. Le fichier accusé sera de la forme :</p>
<blockquote>
<p style="text-align: justify;">&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD:test.c<br />
printf(&laquo;&nbsp;Hello world 2\n&nbsp;&raquo;) ;<br />
=======<br />
printf(&laquo;&nbsp;Hello world 1\n&nbsp;&raquo;) ;<br />
&gt;&gt;&gt;&gt;&gt;&gt;&gt; test:test.c</p>
</blockquote>
<p style="text-align: justify;">HEAD, c&#8217;est la branche actuelle ! test est ici une branche de test. Le fichier en conflit sera affiché dans git status comme unmerged.</p>
<p style="text-align: justify;">Pas très lisible ?  On peut voir séparément les différences introduites par les deux commits sur le fichier en conflit :</p>
<blockquote>
<p style="text-align: justify;">git log &#8211;merge -p test.c</p>
</blockquote>
<p style="text-align: justify;">Une autre option très pratique pour choisir entre telle et telle version quand on est fainéant et qu&#8217;on n&#8217;a pas envie de tout éditer à la main (de toutes façons c&#8217;est ma version la meilleure) :</p>
<blockquote>
<p style="text-align: justify;">git show :z:test.c</p>
</blockquote>
<p style="text-align: justify;">où z correspond à :</p>
<ul>
<li> l&#8217;ancêtre commun des deux branches</li>
<li> branche actuelle</li>
<li> branche distante</li>
</ul>
<p>Qui montre la version z du fichier test.c. On redirige ensuite la sortie dans le fichier test.c</p>
<p style="text-align: justify;">Ne pas oublier de commiter les changements introduits par le merge (en cas de conflits) !</p>
<h1 style="text-align: justify;">On a enfin une version stable !!</h1>
<p style="text-align: justify;">Il est possible de la tagger pour avoir une référence un peu plus explicite que son SHA-1.</p>
<blockquote>
<p style="text-align: justify;">git tag MonTag [&lt;commit&gt;]</p>
</blockquote>
<p style="text-align: justify;">Par défaut HEAD de la branche courante. Un commit peut avoir plusieurs tags.</p>
<p style="text-align: justify;">Ah, en fait non, Bob avait laissé un buffer overflow, supprimer un tag :</p>
<blockquote>
<p style="text-align: justify;">git tag -d MonTag</p>
</blockquote>
<p style="text-align: justify;">Ouais, en fait la v1 c&#8217;était le top. Il est possible de revenir à une version précédente (et heureusement) via trois techniques.</p>
<p style="text-align: justify;">
<h2>Je m&#8217;appelle Rambo et rien ne me fait peur</h2>
<blockquote>
<p style="text-align: justify;">git reset [--mixed | --soft | --hard] [&lt;commit&gt;]</p>
</blockquote>
<p style="text-align: justify;">Ici on revient à un commit précédent en SUPPRIMANT tous les commits suivants<br />
&#8211;mixed : les changements n&#8217;affectent que le gestionnaire de versions et pas le système de fichier<br />
&#8211;soft : On revient simplement au commit &lt;commit&gt; au niveau du gestionnaire de versions, mais les fichiers ne sont pas changés sur le disque (un prochain commit n&#8217;effacera pas le commit suivante [i.e qu'on a reseté])<br />
&#8211;hard : Le gestionnaire de versions et les fichiers sur le disque se retrouvent en position &lt;commit&gt;, les commits suivants sont perdus.</p>
<h2 style="text-align: justify;">Méthode propre</h2>
<blockquote>
<p style="text-align: justify;">git revert &lt;commit&gt;</p>
</blockquote>
<p style="text-align: justify;">Cette méthode est « propre » car elle permet de conserver les changements introduits (même si on veut s&#8217;en séparer) en introduisant un commit faisant le travail inverse de ce qui a été cassé.</p>
<h2 style="text-align: justify;">Méthode pour un ensemble restreint de fichiers</h2>
<blockquote>
<p style="text-align: justify;">git checkout &lt;commit&gt; paths</p>
</blockquote>
<p style="text-align: justify;">Cela permet de retrouver ponctuellement un fichier d&#8217;une ancienne version.</p>
<h1 style="text-align: justify;">J&#8217;te jure ça marchait et j&#8217;ai rien touché !</h1>
<p style="text-align: justify;">12 commits ont été réalisé pendant que vous étiez en vacances et maintenant plus rien ne compile (Bob ??). Bref, git offre un mécanisme très puissant pour identifier le commit qui a tout fait planter : bisect ! On va pouvoir faire une recherche semi-automatique voir même automatique du commit fautif, git se chargera tout seul de mettre l&#8217;espace de travail tel qu&#8217;il était à chaque commit (sinon il aurait fallu jouer avec reset/revert).</p>
<p style="text-align: justify;">On commence par démarrer une session de bisect en identifiant le point où ça bloque (en général HEAD) et le point où on est sûr que c&#8217;était bon:</p>
<blockquote>
<p style="text-align: justify;">git bisect start [&lt;commit-bad&gt;] [&lt;commit-good&gt;]</p>
</blockquote>
<p style="text-align: justify;">Puis on va identifier les différents commits (la recherche est dichotomique, c&#8217;est à dire qu&#8217;il prend toujours le commit intermédiaire dans l&#8217;intervalle good-bad afin de trouver au plus vite la version responsable du problème) avec :</p>
<blockquote>
<p style="text-align: justify;">git bisect bad<br />
git bisect good</p>
</blockquote>
<p style="text-align: justify;">Chaque appel de cette commande « tag » le commit avec good ou bad et passe au commit suivant jusqu&#8217;à tomber sur le fautif (ou qu&#8217;on en ais marre). Si on sait qu&#8217;un commit (ou un intervalle de commits (ie : v1..v2)) est bon par avance on peux l&#8217;ignorer avec :</p>
<blockquote>
<p style="text-align: justify;">git bisect skip [&lt;commit&gt;[..&lt;commit&gt;]]</p>
</blockquote>
<p style="text-align: justify;">good et bad ne sont que des marqueurs, il appartient ensuite au développeur d&#8217;effectuer les actions pour retrouver un environnement correct/stable/sans bugs. On peut visualiser l&#8217;état des marqueurs (dans gitk) avec :</p>
<blockquote>
<p style="text-align: justify;">git bisect visualize</p>
</blockquote>
<p style="text-align: justify;">Une fois qu&#8217;on a fini le boulot on peut revenir au point de départ (effacer les tags et retour à HEAD) avec :</p>
<blockquote>
<p style="text-align: justify;">git bisect reset</p>
</blockquote>
<p style="text-align: justify;">Il est possible d&#8217;enregistrer toutes les opérations (ou au moins les « visualiser ») avec :</p>
<blockquote>
<p style="text-align: justify;">git bisect log</p>
</blockquote>
<p style="text-align: justify;">Et on peut ensuite les rejouer avec :</p>
<blockquote>
<p style="text-align: justify;">git bisect replay &lt;fichier créée par git bisect log&gt;</p>
</blockquote>
<p style="text-align: justify;">Mais là où git est très intéressant c&#8217;est avec le mode tout automatique :</p>
<blockquote>
<p style="text-align: justify;">git bisect run cmd</p>
</blockquote>
<p style="text-align: justify;">cmd est une commande (ou un script) qui va être appliqué sur tous les commits entre bad et good et qui va les marquer selon son code de retour :</p>
<ul>
<li> 0 : good</li>
<li> 125 : skip</li>
<li> autre : bad</li>
</ul>
<p style="text-align: justify;">
<p style="text-align: justify;">Une fois qu&#8217;on a le commit fautif on peut savoir exactement qui a fait quoi dans le fichier qui a fait que et quand il l&#8217;a fait :</p>
<blockquote>
<p style="text-align: justify;">git blame &lt;paths&gt;</p>
</blockquote>
<p style="text-align: justify;">Ce qui donne quelque chose comme :</p>
<blockquote>
<p style="text-align: justify;">1692b633 (Grégory 2009-09-19  1 ) #include &lt;stdio.h&gt;<br />
1692b633 (Grégory 2009-09-19  2 )<br />
1692b633 (Grégory 2009-09-19  3 )<br />
1692b633 (Grégory 2009-09-19  4 )<br />
1692b633 (Grégory 2009-09-19  5 ) int main()<br />
1692b633 (Grégory 2009-09-19  6 ) {<br />
<span style="text-decoration: underline;">795a06c9</span> (Grégory <span style="text-decoration: underline;">2009-09-29</span> 7 )   printf( &laquo;&nbsp;Hello world\n&nbsp;&raquo; ) ;<br />
1692b633 (Grégory 2009-09-19  8 )<br />
1692b633 (Grégory 2009-09-19  9 )   return 0 ;<br />
1692b633 (Grégory 2009-09-19 10) }</p>
<p style="text-align: justify;">
<p style="text-align: justify;">
</blockquote>
<h1 style="text-align: justify;">Ça fait stash !</h1>
<p style="text-align: justify;">Vous êtes tranquillement en train de travailler sur l&#8217;ajout d&#8217;une fonctionnalité révolutionnaire quand Bob vous apprend catastrophé qu&#8217;il a trouvé un bug terrible dans le projet et qu&#8217;il faut le corriger dans la seconde. On peut bien évidement créer une branche pour reporter les modifications et merger par la suite d&#8217;une manière plus ou moins élégante, mais git introduit un concept très intéressant : le « stash ». Cela permet de mettre les modifications en cours dans une pile et de revenir à la dernière version (~ git reset &#8211;hard HEAD), faire les bonnes modifications, commiter et retrouver son espace de travail.</p>
<p style="text-align: justify;">Créer le stash (éventuellement avec un message) :</p>
<blockquote>
<p style="text-align: justify;">git stash [save [message]]</p>
</blockquote>
<p style="text-align: justify;">Voir la liste des stashs :</p>
<blockquote>
<p style="text-align: justify;">git stash list</p>
</blockquote>
<p style="text-align: justify;">Récupérer les modifications du stash et le supprimer de la liste des stashs :</p>
<blockquote>
<p style="text-align: justify;">git stash pop [stash]</p>
</blockquote>
<p style="text-align: justify;">La syntaxe des stashs est : stash@{0}</p>
<p style="text-align: justify;">Le stash le plus récent est toujours 0. Si aucun stash n&#8217;est précisé, le plus récent est celui par défaut</p>
<p style="text-align: justify;">Voir les modifications du stash :</p>
<blockquote>
<p style="text-align: justify;">git stash show [stash]</p>
</blockquote>
<p style="text-align: justify;">Appliquer les modifications d&#8217;un stash sans le supprimer de la pile :</p>
<blockquote>
<p style="text-align: justify;">git stash apply [stash]</p>
</blockquote>
<p style="text-align: justify;">Supprimer tous les stashs :</p>
<blockquote>
<p style="text-align: justify;">git stash clear</p>
</blockquote>
<p style="text-align: justify;">Supprimer un stash sans l&#8217;appliquer :</p>
<blockquote>
<p style="text-align: justify;">git stash drop [stash]</p>
<p style="text-align: justify;">
</blockquote>
<h1 style="text-align: justify;">Hooks</h1>
<p style="text-align: justify;">Les hooks sont des scripts appelés durant l&#8217;exécution de certaines tâches, ils se trouvent dans le répertoire .git/hooks. Cela permet de contrôler/valider certaines actions. Si la valeur de retour du script est différente de 0, alors l&#8217;action ne sera pas exécutée. Git propose les hooks suivants :</p>
<ul>pre/post commit</ul>
<ul>[prepare-]commit-msg : invoqué lors de la création du message dans git commit</ul>
<ul>pre-rebase : invoqué avant un git rebase</ul>
<ul>post-checkout : invoqué après un git checkout</ul>
<ul>post-merge : après un merge et/ou un pull</ul>
<ul>pre/post receive : invoqué (sur le serveur uniquement) lors d&#8217;un git push</ul>
<ul>update : invoqué (sur le serveur uniquement) lors d&#8217;un git push</ul>
<ul>post-rewrite : invoqué lorsque le message d&#8217;un commit est modifié (git commit &#8211;amend ou git rebase)</ul>
<p>Pour les différents paramètres que prennent les hooks, voir le man. Des exemples se trouvent dans le dossier (les .sample). Pour qu&#8217;un hook soit pris en compte il faut enlever l&#8217;extension .sample (attention à ne pas oublier les droits en exécution).</p>
<h1 style="text-align: justify;">Git, mon couteau Suisse</h1>
<p style="text-align: justify;">Quelques commandes supplémentaires :</p>
<p style="text-align: justify;">Exporter les sources du projet (sans le .git)</p>
<blockquote>
<p style="text-align: justify;">git archive &#8211;format=[tar|zip] &#8211;output=File &lt;commit&gt; [paths ...]</p>
</blockquote>
<p style="text-align: justify;">Appliquer un/des patch(s)</p>
<blockquote>
<p style="text-align: justify;">git apply &lt;patchs&gt;</p>
</blockquote>
<p>Supprimer les fichiers qui sont dans le dépôt, mais pas dans le gestionnaire de versions (untracked files) :</p>
<blockquote>
<p style="text-align: justify;">git clean [-d] [fichiers]</p>
</blockquote>
<p style="text-align: justify;">Si l&#8217;argument fichiers est renseigné, seuls ces fichiers là seront supprimés.<br />
-d : Supprimer aussi les dossiers</p>
<p style="text-align: justify;">Optimiser le dépôt local pour de meilleures performances (à faire de temps en temps)</p>
<blockquote>
<p style="text-align: justify;">git gc</p>
</blockquote>
<h1 style="text-align: justify;">Pour aller plus loin</h1>
<p style="text-align: justify;">La documentation de git (en pages man) est très bien faite, il y a deux solutions :</p>
<blockquote>
<p style="text-align: justify;">git help command</p>
</blockquote>
<p style="text-align: justify;">Toutes les options pour les commandes présentées n&#8217;ont pas été spécifiées ici, je vous recommande donc pour certaines tâches spécifiques d&#8217;aller voir le man.<br />
L&#8217;aspect travail collaboratif (pull, push, rebase) n&#8217;a pas été abordé, si vous êtes curieux il y a d&#8217;autres documentation qui en parlent (peut être pour une prochaine version de ce tutoriel).</p>
<p style="text-align: justify;">Pour savoir pourquoi git est le meilleur gestionnaire de versions du noyau : <a title="http://www.youtube.com/watch?v=4XpnKHJAok8" href="http://www.youtube.com/watch?v=4XpnKHJAok8">http://www.youtube.com/watch?v=4XpnKHJAok8</a></p>
<h1 style="text-align: justify;">Le Terminal c&#8217;est bien, mais bon</h1>
<p style="text-align: justify;">Vous devriez maintenant être capable d&#8217;utiliser git en ligne de commande pour vos besoins personnels. Le terminal &#8230; ça va bien un moment mais on aime aussi avoir un client graphique un peu plus convivial qui fait le boulot pour nous ! Faisons un petit tour d&#8217;horizon des solutions existantes. Malheureusement, je n&#8217;ai pas pu tester ces solutions, libre à vous donc de vous faire une opinion sur l&#8217;état d&#8217;avancement et la fiabilité de chacun, n&#8217;hésitez pas à laisser un commentaire pour partager vos retours.</p>
<p style="text-align: justify;"><a title="Client git en Qt4" href="http://sourceforge.net/projects/qgit/">QGit</a>: Client Qt4 (donc pour les utilisateur KDE principalement). Hébergé par SourceForce avec une dernière version datée du 9 Mai de cette année. De bons retours indiqués sur le site.</p>
<p style="text-align: justify;"><a title="TortoiseGit pour Windows" href="http://code.google.com/p/tortoisegit/">TortoiseGIT</a>: Client Windows se voulant le remplaçant de TortoiseSVN. Nécessite l&#8217;installation préalable de msysgit.</p>
<p style="text-align: justify;"><a title="Client Git pour Mac" href="http://gitx.frim.nl/">GitX</a>: Application pour Mac OS X.</p>
<p style="text-align: justify;"><a title="Client git pour Mac OS" href="http://github.com/Caged/gitnub/downloads">GitNub</a>: Autre application pour Mac OS X écrite en RubyCocoa.</p>
<p style="text-align: justify;"><a title="Plug-in git pour eclipse" href="http://www.eclipse.org/egit/">EGit</a>: Plug-in pour eclipse, malheureusement peu avancé. Si vous avez du temps et de la motivation, n&#8217;hésitez pas à participer.</p>
<h1>Ressources</h1>
<p style="text-align: justify;"><a title="Site officiel de git" href="http://git-scm.com/">http://git-scm.com/</a><a title="Git sur Wikipedia" href="http://fr.wikipedia.org/wiki/Git"></p>
<p>http://fr.wikipedia.org/wiki/Git</a></p>
<p><a title="Tutoriel GIT" href="http://www.kernel.org/pub/software/scm/git/docs/gittutorial.html">http://www.kernel.org/pub/software/scm/git/docs/gittutorial.html</a><br />
<a title="Tutoriaux GIT" href="http://linux.efrei.fr/doku.php/tutoriaux/git">http://linux.efrei.fr/doku.php/tutoriaux/git<br />
</a><a title="GitHub" href="http://github.com/">http://github.com/</a><a title="Tutoriaux GIT" href="http://linux.efrei.fr/doku.php/tutoriaux/git"></a><br />
<a title="Git pour futurs barbus" href="http://www.unixgarden.com/index.php/administration-systeme/git-pour-les-futurs-barbus">http://www.unixgarden.com/index.php/administration-systeme/git-pour-les-futurs-barbus</a><br />
<a title="Git, les mains dans le cambouis" href="http://www.unixgarden.com/index.php/administration-systeme/git-les-mains-dans-le-cambouis">http://www.unixgarden.com/index.php/administration-systeme/git-les-mains-dans-le-cambouis</a></p>
<p><a rel="license" href="http://creativecommons.org/licenses/by-sa/2.0/fr/"><img alt="Creative Commons License" style="border-width:0" src="http://i.creativecommons.org/l/by-sa/2.0/fr/88x31.png" /></a><br /><span xmlns:dc="http://purl.org/dc/elements/1.1/" href="http://purl.org/dc/dcmitype/Text" property="dc:title" rel="dc:type">Tutoriel GIT</span> by <a xmlns:cc="http://creativecommons.org/ns#" href="http://www.pierreschambacher.com/" property="cc:attributionName" rel="cc:attributionURL">Gr&#233;gory Soutad&#233;</a> est mis &#224; disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-sa/2.0/fr/">licence Creative Commons Paternit&#233;-Partage des Conditions Initiales &#224; l&#8217;Identique 2.0 France</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.pierreschambacher.com/blog/git-in-a-nutshell/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
	</channel>
</rss>
