GIT Cheat Sheet

Ce document complète l’enseignement GIT d’INF103 en donnant des indices ou des corrections pour tous les problèmes entendus en séance de TP et en PACT.

Les screencasts sont

GIT ET LES FICHIERS

GIT gère des fichiers et leur évolution au cours du temps. Quand vous modifiez un fichier, GIT retient l’information suivante:

Tout fichier peut être mis dans GIT. Mais tous les types de fichier ne font pas sens. Par exemple, un fichier compilé à partir d’un fichier source n’a pas sa place dans GIT si le fichier source y est déjà car il peut être recréé à partir du fichier source. Donc pour vos projets Java, ne mettez pas vos fichiers .class dans GIT. Dans vos projets en C, ne mettez pas les .o dans GIT.

Pour un fichier texte, GIT va vous indiquer les différences entre deux versions, les lignes ajoutées, retirées, modifiées. Pour un fichier binaire, GIT va juste dire que le fichier a changé, et les différences n’ont pas de sens. En ce sens, il n’est pas souvent pertinent de mettre un fichier binaire dans GIT.

GIT vous permet de consigner dans un fichier la liste des fichiers dont l’inclusion dans GIT ne fait pas sens. Ce fichier s’appelle .gitignore. Il peut y avoir un fichier .gitignore dans tous les dossiers, mais l’important est celui du dossier principal.

Pour ignorer un fichier, mettez son nom dans une ligne du .gitignore. Par exemple, sous Mac, il y a un fichier qui s’appelle .DS_Store, qui sert à Mac OSX.

Dans .gitignore, ajoutez la ligne avec .DS_Store

Pour ignorer un dossier complet, même chose, ajouter le nom du dossier suivi de /

Pour ignorer tous les fichiers .class, ajoutez une ligne avec *.class

Un fichier ignoré n’est pas stocké dans le dépot. S’il est modifié ou détruit, GIT l’ignore. Les fichiers ignorés ne sont donc pas présents dans le résultat d’un git clone.

FICHIERS A IGNORER

Il est parfois difficile au début de savoir s’il faut ignorer un fichier ou pas. Je vais traiter le cas d’Eclipse, Intellij, node.js et d’Android Studio

Pour Android Studio, ne faites rien: Android Studio crée automatiquement un fichier .gitignore qui est très bien. Utilisez le tel quel.

Pour IntelliJ, il faut ignorer les dossiers .idea/ et out/.

Si vous utilisez node.js, alors il faut ignorer le dossier node_modules/.

Pour Eclipse, la question qui se pose est: dois-je mettre dans le dépot GIT les fichiers de description du projet, à savoir le .project et le .classpath. Ces deux fichiers dépendent de la configuration de votre machine et pourront perturber vos collaborateurs, surtout si vous avez un mélange de machines Windows, Mac et Linux dans le projet. Ignorez ces deux fichiers.

Cela veut dire que quand vous récupérerez la première fois un dossier où il y a un projet eclipse, eclipse ne le verra pas comme un projet et il faudra que vous l’importiez par “Importer depuis des sources existants”.

CORRIGER LES IGNORES

Comment faire si vous avez ajouté des fichiers dans un dépot, et vous découvrez qu’il aurait fallu les ignorer.

Etape 1: ajoutez le nom des fichiers à ignorer dans .gitignore

Etape 2: faites git rm --cached sur les fichiers

Etape 3: faites git commit et vérifiez

Dans le screencast, je traite un exemple avec un projet eclipse dont tout a été commit sans .gitignore.

DOSSIERS ET DEPOTS

Avec GIT, tout tourne autour des dépots.

Un dépot est un dossier contenant un ensemble de dossiers et de fichiers qui ont une cohérence pour vous.

Faire git init dans un dossier déclare ce dossier comme le dossier principal de votre dépot. Il faut faire git init dans le dossier eclipse qui contient les fichiers .project et .classpath, pour votre projet Java.

Il ne faut pas faire git init dans le dossier workspace ou eclipse-workspace, ni dans votre dossier racine, ni dans le dossier src de votre projet Java.

Le dossier principal de votre dépot GIT contient deux choses:

Si vous avez le choix, vous pouvez faire autant de dépots séparés que vous voulez. Mais si le nombre de dépot est imposé, par exemple si votre entreprise doit payer le service GIT au nombre de dépots que vous créez, ou si dans PACT, nous vous donnons accès à un seul dépot pour nous faciliter le travail, alors vous pouvez facilement travailler dans un seul dépot avec plusieurs sous-projets.

Créez un dossier principal qui sera le support du dépot.

Créez un dossier par sous-projet et une branche ou plus par sous-projet.

Voilà: il n’y aura pas d’interférence entre les sous-projets si chacun travaille dans sa branche et dans son dossier.

RECUPERER DES ERREURS DE DOSSIERS

Vous avez créé votre dépot dans le dossier du fichier .project, mais votre collègue a créé son dépot dans un autre dossier, soit le dossier src, soit son dossier racine ou workspace.

Vous avez tou.te.s les deux poussé dans un dépot distant commun, et vous découvrez le problème en essayant de faire un merge. Vous vous attendez à un merge très difficile, or il ne se passe rien. Vous retrouvez après le merge vos deux projets dans des dossiers différents

Que faire ?

Etape 1: revenir à un état avec vos fichiers dans une branche et les fichiers de l’autre dans une autre branche.

Soit vous revenez en arrière parce que cet état existe déjà dans votre dépot, car vous aviez déjà des branches séparées.

Soit vous n’aviez pas encore de branches, vous avez tout fait sous master, et il faut reconstituer deux branches séparées.

  1. Créez deux branches moi et autre à partir de la branche master où il y a tous les fichiers.
  2. git checkout moi
  3. Vous voyez tous les fichiers
  4. Dans la branche moi, détruisez tous les fichiers de votre collègue par git rm.
  5. Il peut rester des fichiers qui sont ignorés et des dossiers vides que git ne gère pas, à supprimer manuellement.
  6. Vous ne voyez plus que vos fichiers
  7. git commit -a -m "message pertinent"
  8. git checkout autre
  9. Vous voyez de nouveau tous les fichiers
  10. Dans la branche autre, détruisez tous vos fichiers par git rm.
  11. Il peut rester des fichiers qui sont ignorés et des dossiers vides que git ne gère pas, à supprimer manuellement.
  12. Vous ne voyez plus que les fichiers de votre collègue.
  13. git commit -a -m "message pertinent"

Vous êtes maintenant revenu.e.s à l’état où nous pouvons faire le déplacement des fichiers dans une des deux branches.

Le but est donc de déplacer les fichiers d’une branche pour que les deux branches aient des fichiers avec les mêmes noms dans les mêmes dossiers.

Choisissez celle des deux branches dont vous voulez garder la structure de dossier, par exemple c’est la branche moi qui a les bons dossiers.

Mettez vous dans la branche autre, par git checkout autre. Faites git mv sur tous les fichiers et dossiers jusqu’à obtenir la même structure que dans la branche moi. Faites git commit quand la structure est la même.

C’est fini. Vous pouvez maintenant faire un merge pertinent entre les deux branches.

UNRELATED HISTORIES

Vous voulez faire un merge avec la branche de votre binome, et git vous envoie un message contenant ‘unrelated histories’ et refuse de faire le merge.

Que se passe-t-il ?

Dans le TP, nous vous avons dit de faire comme suit:

Si vous avez suivi ce processus, alors vos deux historiques GIT sont liées par le partage du premier commit de A, récupéré par B lors du git clone

Qu’avez-vous vraiment fait ?

Vous avez donc créé deux commits non liés entre eux et poussé deux points de départ de branche sur le dépot distant. Dans la fenetre log de smartgit, vous voyez deux lignes séparées.

Si vous n’avez pas créé de branche avant de pousser, alors le second qui a poussé a dû créer une autre branche que master pour pousser. Dans l’image, ma second branche est master2. J’ai essayé de faire un merge et le message est “refusing to merge unrelated histories”.

Vous pouvez forcer git à faire un merge. Commencez par vérifier que les structures de dossier sont les mêmes des deux cotés, et que les fichiers ont le même nom s’ils doivent être fusionnés.

Puis faites la commande:

git merge master2 --allow-unrelated-histories

Comme vous pouvez imaginer, cette option a un gros potentiel de confusion si vous l’utilisez n’importe comment. Donc faites bien les vérifications.

Voici le résultat dans smargit, vue log:

DOSSIER GIT DANS DOSSIER GIT

Vous avez fait git init dans un dossier puis git init ou git clone dans un sous-dossier et vous ne savez plus quoi faire.

Mettez vous dans le dossier qui est dans l’autre dossier. Faites git status. S’il y a des modifications, voyez si elles doivent être sauvagardées.

Si le dossier interne contient des modifications et est un clone, alors il a un dépot distant. Commencez par déplacer le dossier interne en dehors du dossier externe. Puis les modifications peuvent être commit et push.

Si le dossier interne contient des modifications et n’a pas de dépot distant (remote), alors il faut sauver ces modifications d’une manière autre (copie du dossier, zip…).

Puis vous supprimez le dossier interne ou vous le déplacez en dehors de l’autre dossier.

Si vous aviez ajouté le dossier interne dans le GIT du dossier externe, alors il faut commit la suppression du dossier interne dans le GIT du dossier externe.

LES BRANCHES GIT: POURQUOI

A quoi servent les branches Git ?

SE SERVIR DES BRANCHES GIT

Comment je peux me servir des branches Git ?

Je crée une branche à partir d’une autre

  1. Je suis dans une branche “tata” (à vérifier par “git status”)
  2. Je fais “git branch toto” pour créer la branche “toto” (je suis toujours dans la branche “tata”, “toto” et “tata” sont pour l’instant identiques)
  3. Je fais “git checkout toto” pour passer de “tata” à “toto”
  4. Si je fais maintenant des “git commit”, alors ils sont ajoutés dans la branche “toto” qui change par rapport à “tata”

Je fais “git branch -v” pour savoir quelles sont les branches existantes en local

Je fais “git checkout tata” pour passer d’une branche à la branche “tata”

Quand je fais “git clone”, seule la branche “master” est importée dans le dépôt local par défaut.

  1. Quand je fais “git checkout tata” la première fois, git importe tous les commits de la branche tata dans mon dépôt local
  2. Si je veux importer dans mon dépôt local toutes les branches qui existent sur le dépot distant, je fais “git fetch —all”

PARTAGER DES BRANCHES GIT

Pour partager mon travail qui se trouve dans une branche GIT, deux possibilités

  1. Je propose une fusion simple
    1. Je me mets dans la branche de destination
    2. Je fais “git merge source” où source est la branche source des commits à partager
    3. Je résous les éventuels conflits
      1. J’édite le fichier pour supprimer les indications de conflits
      2. Je fais “git add” sur le fichier que je viens d’éditer
      3. Je fais “git commit” quand tous les conflits sont résolus
  2. Je propose une fusion de type “rebase”
    1. Je me mets dans la branche source des commits à partager
    2. Je fais “git rebase destination” où destination est la branche qui va recevoir les commits partagés
    3. Je résous les éventuels conflits
      1. J’édite le fichier pour supprimer les indications de conflits
      2. Je fais “git add” sur le fichier que je viens d’éditer
      3. Je fais “git commit” quand tous les conflits sont résolus

La fusion simple est plus facile à comprendre mais crée un historique GIT plus compliqué.

AUTRES

Si vous avez un problème qui n’a pas de solution décrite ici, merci de venir me voir en 5D30 ou de m’envoyer un email avec une description du problème et si c’est pertinent, je rajouterai une video.