Git : cloner un projet, travailler à plusieurs et créer des branches

logo Git
Logo de git (http://​git​-scm​.com) Git Logo by Jason Long is licen­sed under the Crea­tive Com­mons Attri­bu­tion 3.0 Unpor­ted License

 Git est un logi­ciel de contrôle de ver­sions de fichiers. Il est dis­tri­bué sous licence GNU GPLv2 et est dis­po­nible sur les prin­ci­paux sys­tèmes d'exploitation.

Dans l'article pré­cé­dent, nous avions vu com­ment ins­tal­ler et confi­gu­rer git, com­ment créer un dépôt pour un pro­jet, ain­si que les prin­cipes de base de ges­tion de ver­sions.

Dans cet article, nous ver­rons com­ment clo­ner un pro­jet (par exemple pour tra­vailler à plu­sieurs ou pour faire des sau­ve­gardes), com­ment syn­chro­ni­ser les dif­fé­rentes copies tout en détec­tant et résol­vant les pro­blèmes éven­tuels, et com­ment créer des branches et indi­quer les étapes qui cor­res­pondent à des ver­sions.

Cloner un projet (par ex. pour travailler à plusieurs ou pour sauvegarder)… et synchroniser les différentes copies

git clone pour dupliquer un dépôt

La com­mande "git clone </path/to/existing/git/repository> </path/where/it/should/be/cloned>" per­met de clo­ner un dépôt exis­tant (en local ou à tra­vers un réseau si le che­min est une URL) à un autre endroit. La méta­phore du clo­nage doit être com­prise ici comme une copie exacte : après "git clone" les deux dépôts sont les images par­faites l'une de l'autre : ils pos­sèdent le même his­to­rique, et il n'y en a pas un qui est plus légi­time que l'autre (en fait, on ver­ra dans la sec­tion sur "git pull" que suite au clo­nage la branche mas­ter du clone fait réfé­rence à la branche mas­ter du clo­né). Si plu­sieurs per­sonnes tra­vaillent sur un pro­jet, elles peuvent ain­si en obte­nir cha­cune une copie.
Dans un pre­mier temps, nous allons syn­chro­ni­ser les dépôts, puis nous ver­rons dans un second temps com­ment détec­ter et gérer les conflits qui peuvent sur­ve­nir lors de modi­fi­ca­tions concur­rentes. Nous sup­po­se­rons que le pro­jet ini­tial était dans le réper­toire repoA, et qu'on le clone dans un autre réper­toire repoB situé n'importe où sauf sous l'arborescence de repoA évi­dem­ment. Pour sim­pli­fier nous pren­drons repoA et repoB au même niveau, même si en pra­tique ça n'a pas grand inté­rêt.
Au pas­sage, vous remar­que­rez que le fichier .gitignore (c'est le même fichier qu'au pre­mier article) dans repoA a bien été pris en compte et que le fichier firstFile.txt.bak ain­si que le réper­toire log ont bien été igno­rés lors du clo­nage dans repoB.

git pull pour maintenir à jour une copie locale d'un dépôt de référence

Nous allons com­men­cer par une confi­gu­ra­tion simple où repoA est le dépôt de réfé­rence d'un pro­jet où se font toutes les modi­fi­ca­tions, et vous sou­hai­tez uni­que­ment main­te­nir à jour votre copie locale repoB sans que vous y appor­tiez la moindre modi­fi­ca­tion.

Main­te­nant que le dépôt d'origine repoA a été clo­né, nous allons com­men­cer par y faire des modi­fi­ca­tions puis un nou­veau commit.

Main­te­nant, un "git status" dans repoB montre que celui-ci est à jour (par rap­port à lui-même), mais que les modi­fi­ca­tions que nous venons de faire dans repoA n'ont pas été repor­tées dans repoB.

Pour syn­chro­ni­ser son dépôt avec celui que nous avions clo­né (ou un autre), il faut faire un appel expli­cite à "git pull". Ce der­nier ne se fait pas auto­ma­ti­que­ment (heu­reu­se­ment, ima­gi­nez un peu la pagaille). Ci-des­sous, la ligne 19 lorsque l'on regarde le conte­nu du fichier firstFile.txt après le "git pull" montre que la modi­fi­ca­tion que nous avions faite dans repoA a bien été réper­cu­tée dans repoB.

Remar­quez que dans repoB, nous fai­sons sim­ple­ment "git pull" sans lui dire expli­ci­te­ment qu'il faut aller cher­cher dans repoA. En fait, le "git clone repoA repoB", git a bien indi­qué dans repoB que ce der­nier avait été géné­ré à par­tir de repoA en le dési­gnant comme "ori­gin" (com­pa­rez donc les fichiers repoA/.git/config et repoB/.git/config). Consul­tez la docu­men­ta­tion sur les dépôts dis­tants pour plus de pré­ci­sions.

Pour réca­pi­tu­ler :

  • Le dépôt de réfé­rence fait donc un cycle clas­sique :
    1. modi­fi­ca­tions,
    2. git add,
    3. git commit.
  • Le dépôt clone se main­tient à jour en fai­sant "git pull".

git push pour propager sur le dépôt d'origine les modifications que vous avez faites en local

Alors que dans la sec­tion pré­cé­dente les modi­fi­ca­tions se fai­saient tou­jours dans le même sens, nous allons main­te­nant voir que git vous per­met de pro­pa­ger sur le dépôt d'origine des modi­fi­ca­tions que vous avez faites sur son clone. Puisque les modi­fi­ca­tions se font en sens inverse de celles du "git pull", il faut évi­dem­ment uti­li­ser "git push" depuis repoB. Afin d'éviter les conflits poten­tiels lorsque plu­sieurs uti­li­sa­teurs font des modi­fi­ca­tions sur leur clone, nous allons créer une branche spé­ci­fique dans laquelle nous allons faire nos modi­fi­ca­tions, puis nous allons envoyer cette branche dans repoA avec un "git push", et enfin fusion­ner cette branche avec la branche en cours de repoA avec un "git merge". La sec­tion sui­vante revient plus en détail sur les branches.

Dans l'exemple sui­vant, nous nous pla­çons dans repoB, nous créons une nou­velle branche myBranchFromB (ligne 6) dans laquelle nous fai­sons des modi­fi­ca­tions sur le fichier firstFile.txt (lignes 8 à 10). Remar­quez que lors du "git commit" lignes 11 puis 14, ces opé­ra­tions se font sur la nou­velle branche myBranchFromB. Enfin, lors du "git push" les lignes 22 et 23 montrent que la nou­velle branche est trans­mise à repoA.

À ce point, nous avons effec­tué des modi­fi­ca­tions dans la branche myBranchFromB de repoB, envoyé cette branche dans repoA et nous l'y avons fusion­né avec la branche prin­ci­pale (appe­lée master) de repoA. Néan­moins, la branche master de repoB n'a pas encore été mise à jour. Il nous faut donc enfin faire un "git pull" depuis repoB.

Pour réca­pi­tu­ler :

  • depuis le dépôt clone
    • n'oubliez pas de com­men­cer par un "git pull" pour être cer­tain de par­tir de la der­nière ver­sion de réfé­rence
    • créez une nou­velle branche avec git checkout -b nomNouvelleBranche
    • faites un ou plu­sieurs cycles clas­siques
      • modi­fi­ca­tions,
      • git add
      • git commit
    • faites un "git push" (comme nous l'avons vu pré­cé­dem­ment, il est pos­sible de faire plu­sieurs cycles de "commit" ato­miques puis un "git push" lorsque l'on est satis­fait)
  • depuis le dépôt ori­gine
    • assu­rez-vous d'être sur la bonne branche (par exemple master) avec un "git checkout nomBonneBranche"
    • faites un "git merge nomNouvelleBranche" de la branche que vous venez de pous­ser depuis le dépôt clone
    • résol­vez les conflits éven­tuels (cf. sec­tion sui­vante)
    • faites un "git branch -d nomNouvelleBranche" pour sup­pri­mer la  branche qui est deve­nue inutile main­te­nant que vous l'avez inté­grée
  • depuis le dépôt clone
    • repas­sez dans la bonne branche (par exemple master)
    • faites un "git pull" pour récu­pé­rer la der­nière ver­sion à jour avec les modi­fi­ca­tions que vous venez de pous­ser, les modi­fi­ca­tions éven­tuelles d'autres uti­li­sa­teurs et la réso­lu­tion des conflits éven­tuels.
    • faites un "git branch -d nomNouvelleBranche" sur la nou­velle branche pour la sup­pri­mer éga­le­ment.

Détection et résolution de conflits en cas de modifications concurrentes sur deux instances

Évi­dem­ment, la situa­tion que nous venons de voir peut se révé­ler pro­blé­ma­tique si pen­dant que vous êtes occu­pé à faire vos modi­fi­ca­tions entre votre "git pull" et votre "git push" quelqu'un fait d'autres modi­fi­ca­tions sur le dépôt d'origine (ou fait un autre "git push" avant le vôtre).

Une fois de plus, git est là pour vous sau­ver la vie :

  • si les modi­fi­ca­tions concur­rentes portent sur des fichiers dif­fé­rents ou même des endroits dif­fé­rents d'un même fichier, git se débrouille pour tout inté­grer (alors qu'avec de simples copies de fichiers, les modi­fi­ca­tions du der­nier écra­se­raient les modi­fi­ca­tions anté­rieures)
  • si les modi­fi­ca­tions concur­rentes portent sur les mêmes endroits dans un fichier,
    • git vous signale qu'il y a un conflit (et vous dit où)
    • git vous pré­pare la zone en déli­mi­tant les deux modi­fi­ca­tions concur­rentes
    • git vous laisse gérer la réso­lu­tion

Là encore, notez bien l'intérêt de ne tra­vailler sur des chan­ge­ments les plus petits pos­sibles. Il est bien plus facile de fusion­ner deux branches ayant peu de modi­fi­ca­tions que deux branches ayant for­te­ment diver­gé et com­por­tant des dif­fé­rences aux mêmes endroits.

À ce point, nous avons ajou­té une ligne dans le fichier firstFile.txt dans repoB et une autre ligne au même endroit du fichier firstFile.txt dans repoA. Nous pro­cè­dons ensuite comme à la sec­tion pré­cé­dente pour envoyer sur repoA la branche myBranchFromB de repoB.

Il ne reste plus qu'à fusion­ner la branche myBranchFromB avec la branche prin­ci­pale de repoA (et les ennuis com­mencent).

Lors du "git merge", git détecte bien le conflit (ligne 4) et nous dit dans quel fichier cela s'est pro­duit. Dans le fichier, git a ajou­té les deux por­tions qui posent pro­blème en les déli­mi­tant par des lignes de che­vrons et en les sépa­rant par une ligne de '=' (lignes 15 à 19). C'est alors à l'utilisateur d'éditer la zone à la main et de sup­pri­mer les déli­mi­ta­tions.

Enfin, il ne reste plus qu'à mettre à jour repoB.

Créer des branches et des versions

Intérêt des branches

Dans la sec­tion sur "git push", nous avons eu un pre­mier aper­çu de la notion de branche. En fait, git per­met de créer autant de branches que nous en avons besoin. La docu­men­ta­tion de git com­porte des expli­ca­tions très claires sur la notion de branche qui en plus abordent des points un peu plus tech­niques comme la notion de HEAD.

Les branches sont au cœur d'organisation des pro­jets avec git. Un exemple d'usage est le mar­quage de ver­sion. Les tags, que nous ver­rons plus loin, c'est cool, mais si vous pas­sez en ver­sion majeure sui­vante (donc, bri­sure de com­pa­ti­bi­li­té), il faut aus­si pen­ser à ceux qui ne sui­vront pas l'update et res­te­ront à la ver­sion « ancienne », et qui vou­dront des cor­rec­tions de bugs et même des mises à jour. Un exemple par­lant est python : il y a deux ver­sions qui coha­bitent, et même si l'une est des­ti­née à mou­rir bien­tôt, elle est encore déve­lop­pée et enri­chie. Même si la réa­li­té est plus com­plexe, nous pou­vons ima­gi­ner qu'un repo git de Python serait com­po­sé de deux branches prin­ci­pales, « Python2 » et « Python3 », qui per­mettent au deux ver­sions de coha­bi­ter et d'évoluer indé­pen­dam­ment. Il y en aurais bien d'autres (une pour chaque fonc­tion­na­li­té en cours de déve­lop­pe­ment), mais ces deux là seraient les « mas­ter ».

Un vrai exemple du monde de la véri­té vraie : la SFML, une librai­rie gra­phique ini­tiée et main­te­nue par un fran­çais (coco­ri­co), et qui ini­tia­le­ment était au C++ ce que la SDL est au C. Les branches sont ici uti­li­sées pour les dif­fé­rents por­tage de la librai­ries sur dif­fé­rents lan­gages, et github per­met de les visua­li­ser joli­ment. Nous voyons éga­le­ment très bien toutes les branches dont le nom com­mence par « bug­fix », qui sont en fait créées uni­que­ment pour résoudre un des pro­blèmes iden­ti­fiés par le méca­nismes de ges­tion de pro­blèmes, qui ne sera pas abor­dé ici, car cela n'a rien à voir avec git (pas direc­te­ment). Nous trou­vons éga­le­ment les branches ayant pour nom « 2.3.x » par exemple, et nous pou­vons consta­ter que de nom­breuses per­sonnes crééent leur propre branche (ça s'appelle un « fork », et c'est une notion à la base du déve­lop­pe­ment de logi­ciel libre) pour déve­lop­per une fonc­tion­na­li­té ou cor­ri­ger un bug, puis effec­tuent un merge de leur branche avec la branche prin­ci­pale (via les pull requests, où une per­sonne demande à ce qu'une de ses branches soit mer­gée avec une branche d'un autre repo qui ne lui appar­tient pas).

En géné­ral, un dépôt git est com­po­sé d'au moins deux branches, sou­vent nom­mées mas­ter et dev. Mas­ter est la branche dite prin­ci­pale, où le code est stable. Autre­ment dit, lorsque vous récu­pé­rez les fichiers de la branche mas­ter d'un pro­jet, vous devriez avoir le code le plus fonc­tion­nel pos­sible. (c'est sou­vent la release qui est sto­ckée en mas­ter)

La branche dev, quant à elle, est une branche où le code est en déve­lop­pe­ment. C'est à par­tir de celle-ci que se créent des branches telles que « bugfix53 » qui va implé­men­ter la cor­rec­tion du bug numé­ro 53. Lorsque la branche dev est stable et pleine de nou­velles fonc­tion­na­li­tés, elle est mer­gée avec mas­ter, qui est alors pas­sé à la ver­sion sui­vante. (et il est pos­sible de pla­cer des tags sur les der­niers com­mits pour mon­trer où en est la ver­sion)

Quand le pro­jet devient plus gros, ou, mieux, qu'une nou­velle ver­sion impor­tante va sor­tir, c'est une bonne idée de créer un fork de mas­ter nom­mé selon la ver­sion (par exemple « 4.x », ou « 5.3.x »). Dés lors, comme la branche dev conti­nue de mer­ger avec le mas­ter, les fonc­tion­na­li­tés n'ayant pas été ajou­tées avant devront attendre la pro­chaine ver­sion du logi­ciel (et donc la pro­chaine branche) pour être uti­li­sées. Avec ce sys­tème, il est pos­sible de se pas­ser de la branche dev et créer les nou­velles fonc­tion­na­li­tés direc­te­ment en for­kant le mas­ter.

Autre exemple : sur le repo de Julia, nous obser­vons plus de 183 branches et 192 pull requests. Chaque branche est créée par un contri­bu­teur avec un nom adap­té au pro­blème auquel cette branche va répondre, et lorsque c'est ter­mi­né, la branche sera « mer­gée » à la branche prin­ci­pale.

Sou­vent, il y a un peu de dis­cus­sion pour que le code ajou­té soit par­fait, juste, en accord avec le pro­jet, n'introduise pas de conflit, ou pour avoir des mes­sages de com­mit de meilleure qua­li­té.

Cer­taines per­sonnes attendent de leurs contri­bu­teurs qu'ils suivent une ligne de conduite par­fois assez stricte. Cela peut sem­bler tyran­nique, mais c'est comme obli­ger un style de code dans un pro­jet à plu­sieurs : sans uni­for­mi­sa­tion, le code est d'autant plus dur à lire et hété­ro­gène dans sa construc­tion, alors autant se for­cer à écrire les choses de la même façon dés le début.

Le scé­na­rio sui­vant est for­te­ment ins­pi­ré de la docu­men­ta­tion et vous donne l'idée géné­rale :

  • vous faites une branche pour ajou­ter une nou­velle fonc­tion­na­li­té
  • en plein milieu on vous demande de cor­ri­ger un bug en urgence
    • vous ne le faites pas dans votre nou­velle branche car le code n'est pas encore fonc­tion­nel
    • vous ne le faites pas non plus dans la branche prin­ci­pale car celle-ci ne doit pas être uti­li­sée comme branche de déve­lop­pe­ment
      • vous créez une seconde branche depuis la branche prin­ci­pale pour cor­ri­ger le bug
      • vous fusion­nez cette seconde branche avec la branche prin­ci­pale une fois la cor­rec­tion ter­mi­née
      • vous ter­mi­nez votre ajout de fonc­tion­na­li­té sur la pre­mière branche
      • vous fusion­nez votre pre­mière branche avec la (nou­velle) branche prin­ci­pale en réglant les conflits éven­tuels

Commandes utiles pour manipuler les branches

La com­mande "git branch" per­met de lis­ter les branches exis­tantes. Elle a notam­ment deux filtres :

  • "git branch --merged" indique uni­que­ment les branches qui ont déjà été fusion­nées.
  • "git branch --no-merged" indique uni­que­ment les branches qui n'ont pas encore été fusion­nées.

 

La com­mande "git checkout nomDeLaBranche" per­met de pas­ser sur la branche nom­De­La­Branche (qui doit déjà exis­ter), et qui devient alors la branche cou­rante.

La com­mande "git branch -b nomDeLaBranche" per­met de créer une nou­velle branche. La branche ain­si créé est iden­tique à la branche cou­rante : elle contient le même his­to­rique et les même com­mits, mais une modi­fi­ca­tion sur l'une n’impactera pas l'autre. La com­mande "git checkout -b nomDeLaBranche" a exac­te­ment le même effet, mais "switche" sur celle-ci juste après : la branche cou­rante devient nom­De­La­Branche. Nous pou­vons donc en déduire l'équation sui­vante : "git branch -b nomDeLaBranche" + "git checkout nomDeLaBranche" = "git checkout -b nomDeLaBranche".

La com­mande "git branch -d nomDeLaBranche" per­met de détruire une branche. Si la branche n'a pas été tota­le­ment mer­gée, aucune action ne sera opé­rée, et git expli­que­ra la marche à suivre pour résoudre le pro­blème ; en géné­ral cela arrive quand vous essayez de détruire une branche que vous n'avez pas mer­gée. Détruire mal­gré tout la branche (avec l'option ‑D, comme l'expliquera git) sup­pri­me­ra DÉFINITIVEMENT les modi­fi­ca­tions n'ayant pas été rapa­triées sur une autre branche. Ce cas de figure arrive typi­que­ment lorsque vous déve­lop­pez une cor­rec­tion de bug sur une branche dédiée, et que le bug est cor­ri­gé par quelqu'un d'autre : votre branche est inutile, elle peut donc être détruite.

Enfin, la com­mande "git merge nomDeLaBranche" per­met de fusion­ner la branche nom­De­La­Branche avec la branche cou­rante, en détec­tant éven­tuel­le­ment les conflits. La branche cible (nom­De­La­Branche) ne sera pas modi­fiée lors de cette opé­ra­tion.

Utiliser des tags pour indiquer les versions

Nous avons vu que git log donne l'historique de tous les commit. Nous avons aus­si lour­de­ment insis­té sur les avan­tages de faire de nom­breux commit ato­miques plu­tôt qu'un gros com­mit chaque fois que vous pro­dui­sez une nou­velle ver­sion stable. On com­prend donc bien que repé­rer par­mi cette longue liste le commit qui cor­res­pond à la ver­sion 5.2 de votre pro­jet ne va pas être facile, d'autant plus que le mes­sage asso­cié à chaque com­mit décrit ce qui a chan­gé, et pas for­cé­ment le fait que ce com­mit consti­tue une étape impor­tante pour le pro­jet.

En plus du mes­sage de des­crip­tion, vous pou­vez asso­cier un tag à un commit. En fait git sup­porte deux types de tag :

  • un tag léger (light­weight tag) est sim­ple­ment une asso­cia­tion entre une chaîne de carac­tères (le tag) et un com­mit ;
  • un tag anno­té (anno­ta­ted tag) est un objet qui contient la chaîne de carac­tères ain­si que la date, l'identité de l'auteur, etc. Il peut éven­tuel­le­ment être signé.

On uti­lise prin­ci­pa­le­ment les tags légers pour la main­te­nance, les besoins tem­po­raires et les affaires internes, et les tags anno­tés pour les anno­ta­tions impor­tantes.

La com­mande git tag ren­voie la liste de tous les tags d'un pro­jet, triés par ordre alpha­bé­tique.

La com­mande git show someTagValue ren­voie les infor­ma­tions du tag et une des­crip­tion du com­mit asso­cié.

La com­mande git tag -d someTagValue détruit le tag someTagValue.

Créer un tag léger

La com­mande "git tag someTagValue" per­met de créer un tag léger. Le tag est alors asso­cié au der­nier commit.

La com­mande git tag someTagValue commitCheckSum per­met d'associer un tag léger à un commit anté­rieur. La com­mande git log (mais c'est assez ver­beux), ou mieux git log --pretty=oneline est alors bien pra­tique pour retrou­ver la valeur de check­sum de chaque com­mit en fai­sant.

Créer un tag annoté

la com­mande git tag -a someTagValue -m "some description for the tag" per­met de créer un tag anno­té. Là encore, cela asso­cie le tag anno­té au der­nier commit, et il suf­fit de pré­ci­ser le check­sum si on veut asso­cier le tag à un commit anté­rieur.

Dans l'exemple ci-des­sous, nous asso­cions le tag anno­té v2.0 au der­nier commit, et le tag v1.0 au commit concer­nant le fichier .gitignore, juste avant la par­tie sur la réso­lu­tion de conflits. Remar­quez que le tag léger gitignore et le tag anno­té v1.0 sont asso­ciés au même com­mit, et que les com­mandes git show res­pec­tives montrent les infor­ma­tions sup­plé­men­taires asso­ciées au tag anno­té.

Les forges logicielles

Sous ce nom bar­bare débor­dant de sens com­plexes et poin­tus se cachent des sites que beau­coup de monde uti­lise par­fois sans le remar­quer. L'un des exemples les plus connus est sour­ce­forge, qui est uti­li­sée par beau­coup de logi­ciels pour y être… for­gés.

Le prin­cipe d'une forge logi­cielle est de per­mettre à un (groupe d')utilisateur(s) ou une asso­cia­tion de tra­vailler col­la­bo­ra­ti­ve­ment sur un pro­jet, avec une uni­for­mi­sa­tion des outils de ges­tion. Et dans ges­tion, il y a ges­tion de ver­sions, dont parle cet article, mais pas que. Une forge logi­cielle pro­pose en géné­ral un wiki inté­gré, un sys­tème de tickets, voire des logi­ciels de ges­tion de pro­jet pour géné­rer des dia­grammes de gantt, de l'UML,… En d'autres termes, la forge logi­cielle est le ter­rain de jeu de la science du génie logi­ciel, et en uti­li­ser les bases ne peut qu'être utile et effi­cace.

Par exemple, dans le dépôt sour­ce­forge de Potass­co, on trouve les fichiers dans un dépôt svn (un autre ges­tion­naire de ver­sions), deux mai­ling lists, un sys­tème de tickets pour remon­ter les bugs et pro­po­ser des modi­fi­ca­tions (de futures branches, pour enri­chir de futures ver­sions !),  un wiki, …

Dans ce tuto, nous avons beau­coup vu de liens vers une des plus fameuses forges : github. Bien qu'elle ne soit en elle-même pas libre (son code source n'est pas dis­tri­bué ni sous licence libre), c'est une des pla­te­formes prin­ci­pales de l'univers du libre. Elle pro­pose, gra­tui­te­ment pour les pro­jets open-source, payant sinon, un espace de sto­ckage illi­mi­té pour les pro­jets, un unique ges­tion­naire de ver­sions (git !) avec une inter­face gra­phique dédiée à la visua­li­sa­tion des dépôts et pull requests, un sys­tème de wiki, de ges­tion de pro­blèmes,…

Il est à noter qu'au moment où ces lignes sont écrites, deux forges logi­cielles ont dis­pa­ru en peu de temps : google code et gito­rious ont été res­pec­ti­ve­ment fer­mée et rache­tée, car trop forte concur­rence ou absorp­tion par un autre pro­jet de forge. Une autre, et pas des moindres, sour­ce­forge, com­mence sérieu­se­ment à inquié­ter de par son com­por­te­ment que d'aucun diront amo­ral, au point que de nom­breux pro­jets, suite aux déboires du pro­jet The Gimp, migrent vers d'autres forges (ce der­nier lien montre au pas­sage une bonne quan­ti­té de forges, avec les ges­tion­naires de ver­sions sup­por­tés par cha­cune)

Les forges logi­cielles forment un sujet suf­fi­sam­ment vaste pour être trai­té dans un article dédié. En géné­ral, regar­der les offres et fonc­tion­na­li­tés de cha­cune d'entre elles suf­fit à se faire une idée. Par­mi les autres alter­na­tives :

  • git­lab, une solu­tion effi­cace pour l'auto-hébergement, ou comme solide alter­na­tive à github.
  • fra­ma­git, tenu par l'association Fra­ma­soft et basée sur git­lab. Le nombre de dépôts est limi­té, mais c'est une asso fran­çaise et, pour le coup, des plus libristes qui soient !
  • bit­bu­cket, qui pro­pose notam­ment une offre gra­tuite pour les petits dépôts pri­vés.
  • gogs, ver­sé dans l'auto-hébergement (mer­ci Pierre Mari­jon).
  • git­chain, un nou­veau pro­jet orien­té pure décen­tra­li­sa­tion, ce qui parait impor­tant, dans ce monde où l'on a trop ten­dance à faire confiance au « cloud ».
  • red­mine, une forge très com­plète, uti­li­sée dans le domaine du génie logi­ciel.
  • Sour­ce­Sup, la forge de Rena­ter.

Choi­sir une forge n'est pas une déci­sion pri­mor­diale : vous arri­ve­rez cer­tai­ne­ment à tra­vailler sur plu­sieurs pro­jets, tous héber­gés dans une forge dif­fé­rente. Il peut éga­le­ment arri­ver qu'un pro­jet change de forge ; d'abord parce que rien ne l'interdit, mais aus­si parce que par­fois, comme pour sour­ce­forge, la phi­lo­so­phie d'une forge est remise en ques­tion et que les créa­teurs d'un pro­jet pré­fèrent rejoindre une forge plus en adé­qua­tion avec leurs besoins. Pour pas­ser d'une forge à l'autre, quelques com­mandes suf­fisent sou­vent.

Sachez qu'avec tout ce que vous savez de git, il ne vous est pas dif­fi­cile de créer un compte sur git­lab ou fra­ma­git, et de vous lan­cer ! Les seules com­mandes qui chan­ge­ront seront les pre­miers appels à git clone, git push et git pull. Il fau­dra don­ner des URL ou des argu­ments par­ti­cu­liers, qu'en géné­ral la forge vous donne direc­te­ment pour vous faire gagner du temps.

Aujourd'hui, il est rare qu'un pro­jet n'ait pas une visi­bi­li­té sur une ou plu­sieurs forges logi­cielles. Les implé­men­ta­tions de lan­gages, par exemple, sont très visibles par ce biais, ain­si que le noyau linux ou galaxy.

Au niveau de l'enseignement en infor­ma­tique, cer­tains pro­fes­seurs observent les sta­tis­tiques offertes par la forge pour juger rapi­de­ment du par­tage des tâches et de la régu­la­ri­té de tra­vail des membres d'une équipe ; par exemple, github se prête assez bien à ce jeu car four­nis­sant une grande quan­ti­té de gra­phiques colo­rés et rigo­los.

Tuner son git

C'est comme le per­mis de conduire : main­te­nant que nous avons fait plein de théo­rie et de bla bla sur les bonnes pra­tiques, nous allons ren­trer dans le dur du sujet : le tuning ! (rien à voir avec Turing)

Les jantes

Je suis cer­tain que beau­coup d'entre vous trouvent extrê­me­ment long les com­mandes de git, telles que git com­mit, avec 10 touches à frap­per sans comp­ter « enter », ou git pull, qui donne l'impression de ne fonc­tion­ner qu'en hiver. Eh bien, c'est comme le bashrc pour rem­pla­cer « ls » par « l » et « cd ..» par « c. » : nous allons faire du tuning !

À ce stade de l'article, vous devriez avoir un fichier ~/.gitconfig. Sinon, créez-le et deman­dez-vous si vous n'avez pas sau­té la pre­mière par­tie. C'est ce fichier que nous allons modi­fier sau­va­ge­ment :

Ça c'est du tuning ! Les trois pre­mières lignes devraient déjà exis­ter, et d'autres aus­si éven­tuel­le­ment (cer­taines ont été reti­rées dans l'exemple, car ce fichier contient géné­ra­le­ment des valeurs pri­vées pour authen­ti­fi­ca­tion auprès de forges). Si elle n'existe pas déjà, créez la sec­tion [alias] et mettez‑y des alias cools et mus­clés pour vous sim­pli­fier la vie. Des plus utiles au moins utiles (d'après l'auteur) :

    • ceux qui font éco­no­mi­ser deux touches : « a », « df », « ps », « pl », voire 4 pour « ci », « br » et « co »
    • ceux qui évitent d'oublier des carac­tères éloi­gnés sur le cla­vier : « cim », « cia », « ap », « dfs »
    • ceux qui per­mettent d'être effi­cace avec les stashs : « s*» (voir les liens plus bas)
    • ceux qui font évi­ter des options zar­bi : « lol », « who »
    • ceux qu'on n'utilise jamais : « yolo », « who », « diff­stat » (trop longue à taper…)

Voi­là ! Libre à vous de reco­pier ces lignes, d'en enle­ver, d'en ajou­ter,… Bref, de les adap­ter à votre usage. Toutes ne sont pas utiles, mais elles donnent des idées et des pistes pour ceux qui veulent boos­ter un peu leur pro­duc­ti­vi­té.

En prime, voi­là quelques alias à mettre dans son bashrc/​zshrc, qui se couplent assez bien avec le git­con­fig :

 

La carrosserie

Moulte che­mins mènent à l'épanouissement de git dans votre outils de tra­vail : si par chance vous n'utilisez pas d'interface gra­phique autre que celle de votre (ému­la­teur de) ter­mi­nal, il existe de nom­breux modules, packages, layouts, hack propres et moins propres qui per­mettent à git. d'être un poil plus visuel, colo­ré ou rigo­lo.

De plus, de nom­breux édi­teurs de textes et IDE pro­posent des faci­li­tés d'usage de git. Entre autres :

  • vim pro­pose trois modules d'intérêt (il y en a beau­coup d'autres) qui per­mettent de jouer avec git sim­ple­ment : le puis­sant fugi­tive, l'utile extra­dite et l'informatif git­gut­ter,
  • bash et zsh peuvent être tunés avec oh my git.
  • python pos­sède un module pour inté­ra­gir avec git : pygit.

Le mot de la fin

Tuner son git, c'est comme tuner son bashrc : c'est gagner beau­coup de temps en s'amusant, et c'est néces­saire pour faire de son envi­ron­ne­ment de tra­vail un lieu pro­duc­tif et simple. Cela méri­te­rait un article à part entière, mais ici nous nous conten­te­rons de ce simple énon­cé : que ce soit avec les alias ou les ges­tion­naires de ver­sions, le prin­cipe est le même : faites bos­ser l'ordinateur, sur­tout pas vous !

Soyez fai­néants, et vous vous sim­pli­fie­rez la vie.

Du git dans les Internets

  • des com­mandes utiles pour git dans la vie de tous les jours : via
  • tig, une inter­face en mode texte pour git
  • le prin­cipe de git stash expli­qué avec péda­go­gie et en fran­çais : via
  • un petit billet simple et fran­çais sur la contri­bu­tion (fork, pull request) : via
  • une expli­ca­tion par bit­bu­cket des contri­bu­tions [EN] : via
  • une ques­tion de fond par fra­ma­soft sur l'usage des forges comme github, pro­blé­ma­tique à laquelle répond en par­tie git­chain : via
  • com­ment contri­buer sur github (ça s'applique à toutes les autres forges) : via
  • il existe aus­si pas mal de tuto vidéo, essen­tiel­le­ment en anglais : via
  • sta­cko­ver­flow pro­pose un tag git, et un tas de réponses à des ques­tions allant du simple au com­plexe : via (ou ici pour les ges­tion­naires de ver­sions en géné­ral, ou pour une vision plus abs­traite)
  • un article court mais intense sur les bases : via
  • il existe des cen­taines de cheat­sheet sur inter­net, cer­taines inter­ac­tives : via via via (mer­ci hed­jour)
  • plus d'info sur sour­ce­forge et ses nou­velles manies [EN] : via (réponse de sour­ce­forge)
  • confi­gu­rer git pour uti­li­ser un édi­teur par­ti­cu­lier : via
  • si vous vou­lez jouer avec git : via ou décou­vrir un autre usage des branches : via (mer­ci Yoann M.)

Conclusion

Nous avons vu com­ment clo­ner un pro­jet, faire des modi­fi­ca­tions sur l'un ou l'autre des clones (voire sur les deux) et pro­pa­ger ces modi­fi­ca­tions en tenant compte de la nature décen­tra­li­sée de git. Cela illustre trois des inté­rêts majeurs des ges­tion­naires de ver­sions : per­mettre à plu­sieurs per­sonnes de par­ta­ger une base com­mune, la modi­fier selon leurs besoins tout en en fai­sant pro­fi­ter les autres, et enfin de détec­ter les conflits qui peuvent sur­ve­nir lors de modi­fi­ca­tions concur­rentes.

Enfin, nous avons rapi­de­ment abor­dé l'aspect com­mu­nau­taire que per­mettent les ges­tion­naires de ver­sions. Même si il y avait emphase sur git, c'est la même chose pour l'ensemble des ges­tion­naires de ver­sions : puisque le par­tage de code est simple et struc­tu­ré, il devient aisé de contri­buer à un pro­jet sans bou­ger de son pc. Et avec le prin­cipe des forks et des pull requests, vous avez les bases pour vous lan­cer dans la contri­bu­tion effré­née et dans les tuto­riaux plus avan­cés : il est bien sûr pos­sible de faire des choses beau­coup plus raf­fi­nées. Ces deux articles pré­sentent les prin­cipes de base per­met­tant de se sor­tir de la plu­part des situa­tions pas trop tor­dues. Nous sommes convain­cus qu'une uti­li­sa­tion même limi­tée d'un ges­tion­naire de ver­sions vaut mieux que pas de ges­tion­naire de ver­sions du tout.

Remerciements

Mer­ci à Estel, nal­lias, hed­jour et Yoann M pour les com­men­taires et dis­cus­sions lors de l'édition de cet article.

À propos de cet article

Cet article et le pré­cé­dent ont étés adap­tés à par­tir d'un cours don­né par Lucas Bour­neuf.

Il a été rédi­gé par Lucas Bour­neuf et Oli­vier Dame­ron.



Pour continuer la lecture :


Commentaires

5 réponses à “Git : cloner un projet, travailler à plusieurs et créer des branches”

  1. Avatar de Pierre

    Je m'insurge devant la poli­tique pro-vim de ce billet il n'a été fait nul­le­ment men­tion d'emacs et des mer­veilleux module pour jouer avec git dans emacs. Tout est là http://​emacs​wi​ki​.org/​e​m​a​c​s​/​Git ça va du plus simple et user friend­ly au plus com­plexe.

    1. Avatar de Olivier
      Olivier

      C'est parce que c'est un article sérieux, Môs­sieur, et quitte à expli­quer autant ne pas par­ler des édi­teurs en plas­tique

    2. Avatar de lucas

      Ce n'est pas si pro-vim que ça : on aborde nano dans la par­tie pré­cé­dente… (c'est l'éditeur par défaut de git !)

Laisser un commentaire