Ce chapitre complète l’enseignement GIT en donnant des indices ou des corrections pour tous les problèmes entendus en séance de TP et en projet.
A priori, ce chapitre est plutôt là pour vous aider à résoudre des problèmes plus tard. Sa lecture avant d’avoir expérimenté (et fait des erreurs) avec git n’est pas utile.
smartgit est un logiciel disponible sur les plateformes habituelles et qui était utilisé dans un enseignement Java. Il y a quelques écrans de smartgit dans ces transparents, mais le même type de fonction existe dans tous les logiciels auxquels vous aurez accès: eclipse, intelliJ, visual code…
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
.
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”.
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
.
Voir le screencast
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:
.git
, souvent non visible dans
l’Explorer/Finder, qui contient les données privées du logiciel
GIT.git checkout
avec les modifications
en cours.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.
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.
moi
et autre
à partir
de la branche master où il y a tous les fichiers.git checkout moi
moi
, détruisez tous les fichiers de
votre collègue par git rm
.git commit -a -m "message pertinent"
git checkout autre
autre
, détruisez tous vos fichiers par
git rm
.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.
Voir le screencast
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:
git init
git commit
puis git push
sur le dépot distantgit clone
du dépot distantSi 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 ?
git init
git commit
puis
git push
sur le dépot distantVous 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:
Voir le screencast
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.
A quoi servent les branches Git ?
Que se passe-t-il sans branches ?
Avec une branche et des dossiers séparés, vous êtes complètement indépendants des autres modules, vous développez sans être perturbé.e.s par le travail des autres.
Vous pouvez même faire une branche par personne, si vous travaillez sur des parties clairement séparées de celle de votre binôme.
Quand vous êtes prêt.e.s à partager votre travail avec d’autres, vous leur dites de fusionner votre branche avec la leur. C’est le principe de la “merge request”. Les fusions dans “master” sont plutôt à faire par les intégrateurs.
Comment je peux me servir des branches Git ?
Je crée une branche à partir d’une autre
git status
)git branch toto
pour créer la branche “toto”
(je suis toujours dans la branche “tata”, “toto” et “tata” sont pour
l’instant identiques)git checkout toto
pour passer de “tata” à
“toto”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 “main” ou
“master” est importée dans le dépôt local par défaut.
git checkout tata
la première fois, git
importe tous les commits de la branche “tata” dans mon dépôt localgit fetch —all
Pour partager mon travail qui se trouve dans une branche GIT, deux possibilités
git merge source
où source est la branche
source des commits à partagergit add
sur le fichier que je viens
d’éditergit commit
quand tous les conflits sont
résolusgit rebase destination
où destination est la
branche qui va recevoir les commits partagésgit add
sur le fichier que je viens
d’éditergit commit
quand tous les conflits sont
résolusLa fusion simple est plus facile à comprendre mais crée un historique GIT plus compliqué.
Si vous avez un problème qui n’a pas de solution décrite ici, dites le à un de vos enseignants, je rajouterai une video.