Travaux pratiques : une calculatrice Java sous Git

Sommaire

Ces travaux pratiques vous font pratiquer Git et GitLab à travers un mini-projet : une calculatrice en Java, qui évolue au fil des exercices. Vous n’avez pas besoin de compiler le code Java : l’objectif est de manipuler Git, pas d’apprendre Java.

Avant de commencer

Objectifs pédagogiques

  • Maîtriser les commandes Git essentielles (init, add, commit, push, pull).
  • Gérer des branches et résoudre des conflits de fusion.
  • Travailler en équipe avec GitLab : merge requests, tickets, jalons, wiki.
  • Utiliser des techniques avancées : rebase, stash, cherry-pick, revert.

Matériel et organisation

  • Git installé (voir Premiers pas) et un terminal Unix : sous Windows, Git Bash.
  • Un compte gratuit sur gitlab.com, avec une clé SSH configurée.
  • Un éditeur de texte (Visual Studio Code, Notepad++, IntelliJ…).
  • Environ 6 heures en tout : 2 heures de lecture des pages de cours et 3 h 30 d’exercices, hors pauses.

Le TP est organisé en quatre blocs progressifs :

BlocThématiqueExercices
Bloc 1Fondamentaux Git1 et 2
Bloc 2Branches et collaboration3, 4 et 5
Bloc 3Techniques avancées6 et 7
Bloc 4Outils GitLab8

Bloc 1 : Fondamentaux Git

Durée indicative des exercices : 45 min. Cours associé : Introduction, Premiers pas, Gérer un dépôt Git (dépôt local, historique, tags) et Travailler avec GitLab (clés SSH, dépôt distant).

Exercice 1 : Premiers pas avec Git (30 min)

Objectifs

  • Initialiser un dépôt Git local
  • Créer une première classe Java simple
  • Effectuer votre premier commit
  • Créer un dépôt distant sur GitLab et pusher le code

Commandes à maîtriser

git init : Initialise un nouveau dépôt Git dans le répertoire courant

git add : Ajoute des fichiers à l’index (zone de staging)

git commit : Enregistre les modifications de l’index dans l’historique

git status : Affiche l’état actuel du dépôt

git remote : Gère les connexions aux dépôts distants

git push : Envoie les commits locaux vers un dépôt distant

git tag : Crée une étiquette pour marquer une version spécifique

À faire

Étape 1 — Initialiser le projet

Créez un nouveau répertoire pour votre projet :

mkdir calculator-project
cd calculator-project
git init

Étape 2 — Créer la structure du projet

Créez la structure de dossiers Java :

mkdir -p src

Étape 3 — Créer la première classe Calculator.java

Créez le fichier src/Calculator.java avec le code suivant :

public class Calculator {
    public int add(int a, int b) {
        return a + b;
    }
    
    public int subtract(int a, int b) {
        return a - b;
    }
}

Étape 4 — Créer un fichier .gitignore

Créez un fichier .gitignore pour ignorer les fichiers compilés :

*.class
*.jar
out/
target/

Étape 5 — Créer un README

Créez un fichier README.md :

# Projet Calculatrice

Mini-projet Java pour apprendre Git.

## Fonctionnalités
- Addition
- Soustraction

Étape 6 — Premier commit

Ajoutez tous les fichiers à l’index et faites votre premier commit :

git add .
git status  # Vérifiez que tous les fichiers sont bien indexés
git commit -m "Initial commit: Calculator avec add et subtract"

Étape 7 — Créer un tag v1.0

Marquez cette première version :

git tag -a v1.0 -m "Version 1.0 - Operations de base"
git tag  # Listez les tags pour vérifier

Étape 8 — Créer le dépôt sur GitLab

  • a. Connectez-vous à gitlab.com

  • b. Cliquez sur ‘New project’ > ‘Create blank project’

  • c. Nom : calculator-project

  • d. Visibilité : Privé ou Public

  • e. Ne cochez PAS ‘Initialize repository with a README’

  • f. Cliquez sur ‘Create project’

Étape 9 — Lier et pusher vers GitLab

GitLab affiche les commandes à exécuter. Utilisez celles-ci :

git remote add origin git@gitlab.com:<votre-identifiant>/calculator-project.git
git branch -m main  # Renomme la branche master en main (inutile si init.defaultBranch est déjà configuré)
git push -u origin main --tags

Exercice 2 : Historique et navigation (15 min)

Objectifs

  • Consulter l’historique des commits de différentes manières
  • Utiliser git blame pour tracer l’origine d’une ligne de code
  • Comprendre l’importance des messages de commit

Commandes à maîtriser

git log : Affiche l’historique complet des commits

git log --oneline : Version condensée (1 ligne par commit)

git log --graph : Affiche un graphique ASCII de l’historique

git log --stat : Montre les statistiques de chaque commit

git blame <fichier> : Affiche l’auteur de chaque ligne d’un fichier

git last-modified : Affiche le dernier commit ayant modifié chaque fichier (Git ≥ 2.52)

À faire

Étape 1 — Ajouter une méthode multiply

Modifiez src/Calculator.java pour ajouter la multiplication :

public int multiply(int a, int b) {
    return a * b;
}
git add src/Calculator.java
git commit -m "feat: ajout de la multiplication"

Étape 2 — Mettre à jour le README

Ajoutez la ligne ‘- Multiplication’ dans la section Fonctionnalités :

git add README.md
git commit -m "docs: mise à jour README avec multiplication"

Étape 3 — Explorer l’historique

Testez différentes commandes pour visualiser l’historique :

git log

→ Affiche tous les détails (auteur, date, message complet)

git log --oneline

→ Format compact, idéal pour un aperçu rapide

git log --oneline --graph

→ Affiche la structure des branches (utile plus tard)

git log --stat

→ Montre quels fichiers ont été modifiés et combien de lignes

Étape 4 — Utiliser git blame

Découvrez qui a écrit chaque ligne du fichier Calculator.java :

git blame src/Calculator.java

Chaque ligne est annotée avec : le hash du commit, l’auteur, la date, et le contenu de la ligne.

Étape 5 — Utiliser git last-modified (si votre Git est en version 2.52 ou plus)

Identifiez en une seule commande le dernier commit ayant modifié chaque fichier du projet :

git last-modified

Étape 6 — Filtrer l’historique

Expérimentez avec des filtres :

git log -n 2  # Affiche seulement les 2 derniers commits
git log --author="Votre Nom"  # Vos commits uniquement
git log -- src/Calculator.java  # Commits ayant modifié ce fichier

Étape 7 — Pusher les modifications

git push origin main

Bloc 2 : Branches et collaboration

Durée indicative des exercices : 1 h 15. Cours associé : Gérer un dépôt Git (branches, fusion, conflits) et Travailler avec GitLab (branches protégées, merge requests).

Exercice 3 : Branches sans conflit (15 min)

Objectifs

  • Créer et utiliser des branches pour isoler des développements
  • Fusionner une branche sans conflit (fast-forward)
  • Comprendre l’intérêt du développement en branches parallèles

Commandes à maîtriser

git branch <nom> : Crée une nouvelle branche

git branch : Liste toutes les branches (* = branche active)

git switch <nom> : Change de branche active

git merge <branche> : Fusionne une branche dans la branche courante

git branch -d <nom> : Supprime une branche fusionnée

À faire

Étape 1 — Créer une branche pour la division

git branch feature/division
git branch  # Vérifiez que la branche est créée
git switch feature/division

Étape 2 — Implémenter la division dans la branche

Créez un nouveau fichier src/AdvancedCalculator.java :

public class AdvancedCalculator extends Calculator {
    public double divide(int a, int b) {
        if (b == 0) {
            throw new IllegalArgumentException("Division par zéro");
        }
        return (double) a / b;
    }
}
git add src/AdvancedCalculator.java
git commit -m "feat: ajout division avec gestion erreur"

Étape 3 — Mettre à jour le README dans la branche

Ajoutez ‘- Division (avec protection division par zéro)’ dans README.md :

git add README.md
git commit -m "docs: ajout division dans README"

Étape 4 — Fusionner la branche dans main

git switch main
git merge feature/division

→ Git va faire un ‘fast-forward’ car main n’a pas changé depuis la création de la branche.

Étape 5 — Vérifier et nettoyer

git log --oneline --graph  # Visualisez l'historique
git branch -d feature/division  # Supprimez la branche devenue inutile
git push origin main

Exercice 4 : Gestion des conflits (30 min)

Objectifs

  • Provoquer intentionnellement un conflit de fusion
  • Comprendre les marqueurs de conflit de Git
  • Résoudre manuellement un conflit
  • Finaliser une fusion après résolution

Comprendre les conflits

Un conflit survient quand deux branches modifient les mêmes lignes d’un fichier. Git affiche alors des marqueurs pour indiquer les versions en conflit :

<<<<<<< HEAD
Votre version (branche courante)
=======
Version de la branche à fusionner
>>>>>>> nom-de-la-branche

À faire

Étape 1 — Créer deux branches qui vont entrer en conflit

git branch feature/formatting-style1
git branch feature/formatting-style2

Étape 2 — Modifier Calculator.java dans la première branche

git switch feature/formatting-style1

Modifiez la méthode add() avec ce style :

public int add(int a, int b)
{
    return a + b;  // Style 1: accolade seule
}
git add src/Calculator.java
git commit -m "refactor: style1 pour add()"

Étape 3 — Modifier la même méthode dans la seconde branche

git switch feature/formatting-style2

Modifiez la méthode add() avec ce style différent :

public int add(int a, int b) {  // Style 2: accolade sur la ligne
    return a + b;
}
git add src/Calculator.java
git commit -m "refactor: style2 pour add()"

Étape 4 — Fusionner la première branche sans problème

git switch main
git merge feature/formatting-style1  # Fusion OK

Étape 5 — Tenter de fusionner la seconde branche → CONFLIT !

git merge feature/formatting-style2

→ Git vous avertit du conflit. Le fichier Calculator.java contient maintenant les marqueurs de conflit.

Étape 6 — Examiner le conflit

git status  # Montre les fichiers en conflit

Ouvrez src/Calculator.java. Vous verrez :

<<<<<<< HEAD
public int add(int a, int b)
{
    return a + b;  // Style 1
}
=======
public int add(int a, int b) {  // Style 2
    return a + b;
}
>>>>>>> feature/formatting-style2

Étape 7 — Résoudre le conflit

Choisissez un style (ou créez-en un troisième !). Par exemple, gardons style 2 :

public int add(int a, int b) {
    return a + b;
}

Étape 8 — Finaliser la fusion

git add src/Calculator.java  # Marque le conflit comme résolu
git commit  # Vim s'ouvre pour le message de merge
  • 1. Appuyez sur ‘i’ pour éditer si nécessaire

  • 2. Appuyez sur ‘Esc’ puis tapez ‘:wq’ et Entrée

Étape 9 — Nettoyer et pusher

git branch -d feature/formatting-style1
git branch -d feature/formatting-style2
git push origin main

Exercice 5 : Merge Requests sur GitLab (30 min)

Objectifs

  • Protéger la branche principale
  • Créer une merge request sur GitLab
  • Faire une revue de code et laisser des commentaires
  • Valider et fusionner via l’interface web

Concept : Merge Request

Une Merge Request (MR) est une demande pour fusionner une branche dans une autre. Elle permet de faire une revue de code avant la fusion, ce qui améliore la qualité et favorise le partage de connaissances dans l’équipe.

À faire

Étape 1 — Protéger la branche main sur GitLab

  • a. Allez dans votre projet GitLab

  • b. Settings > Repository > Protected branches

  • c. Branch : main

  • d. Allowed to merge : Maintainers

  • e. Allowed to push : No one

  • f. Cliquez sur ‘Protect’

Étape 2 — Créer une branche feature/power

git switch -c feature/power  # Crée et active la branche

Étape 3 — Implémenter la fonction puissance

Ajoutez dans src/AdvancedCalculator.java :

public double power(int base, int exponent) {
    return Math.pow(base, exponent);
}
git add src/AdvancedCalculator.java
git commit -m "feat: ajout fonction puissance"

Étape 4 — Pusher la branche

git push -u origin feature/power

Étape 5 — Créer une Merge Request sur GitLab

  • a. GitLab affiche un bouton ‘Create merge request’ après le push

  • b. Ou : Code > Merge requests > New merge request

  • c. Source : feature/power, Target : main

  • d. Titre : ‘feat: Ajout fonction puissance’

  • e. Description : Décrivez ce que fait la fonction

  • f. Cochez ‘Delete source branch when merge request is accepted’

  • g. Cliquez sur ‘Create merge request’

Étape 6 — Faire une revue de code

  • a. Onglet ‘Changes’ : voir les modifications ligne par ligne

  • b. Cliquez sur une ligne pour laisser un commentaire

  • c. Exemple : ‘Penser à gérer les exposants négatifs ?’

  • d. Onglet ‘Overview’ : ajouter un commentaire général

Étape 7 — Approuver et fusionner

  • a. Bouton ‘Approve’ (si vous avez les droits)

  • b. Bouton ‘Merge’ (plusieurs options possibles)

  • c. Choisissez ‘Merge commit’ (conserve l’historique complet)

  • d. Confirmez

Étape 8 — Mettre à jour votre dépôt local

git switch main
git pull --prune # Récupérer les dernières modifications et mettre à jour les références
git branch -d feature/power 

Bloc 3 : Techniques avancées

Durée indicative des exercices : 1 h 15. Cours associé : Gérer un dépôt Git (stash, cherry-pick, rebase, revert) et Flux de travail usuels.

Exercice 6 : Manipulation de l’historique (45 min)

Objectifs

  • Mettre de côté des modifications avec stash
  • Linéariser l’historique avec rebase
  • Récupérer un commit spécifique avec cherry-pick
  • Annuler un commit avec revert

Commandes à maîtriser

git stash : Met de côté les modifications en cours

git stash pop : Réapplique les dernières modifications mises de côté

git rebase <branche> : Réécrit l’historique en appliquant les commits au sommet d’une autre branche

git cherry-pick <commit> : Applique un commit spécifique sur la branche courante

git revert <commit> : Crée un nouveau commit qui annule les modifications d’un commit

À faire

Partie A : git stash

Étape 1 — Commencer une modification

Modifiez src/Calculator.java en ajoutant un commentaire :

// TODO: Ajouter validation des entrées
public int add(int a, int b) {

Étape 2 — Mettre de côté avec stash

Vous devez travailler sur quelque chose d’urgent :

git stash -m "WIP: validation des entrées"
git status  # Le fichier redevient propre

Étape 3 — Faire une correction urgente

Ajoutez une méthode dans AdvancedCalculator.java :

public int modulo(int a, int b) {
    return a % b;
}
git add src/AdvancedCalculator.java
git commit -m "feat: ajout modulo"

Étape 4 — Récupérer le travail en cours

git stash list  # Voir les stash disponibles
git stash pop  # Réapplique les modifications
Partie B : git rebase

Étape 1 — Créer une branche avec plusieurs commits

git switch -c feature/square-root

Ajoutez dans AdvancedCalculator.java :

public double squareRoot(int n) {
    return Math.sqrt(n);
}
git add src/AdvancedCalculator.java
git commit -m "feat: ajout racine carrée"

Mettez à jour le README :

git add README.md
git commit -m "docs: ajout racine carrée"

Étape 2 — Simuler un avancement de main

git switch main

Ajoutez un commentaire au début de Calculator.java :

// Calculatrice de base - Version 1.5
git add src/Calculator.java
git commit -m "docs: mise à jour version"

Étape 3 — Rebaser la branche feature

git switch feature/square-root
git rebase main

→ Les commits de feature/square-root sont maintenant au-dessus de main

git log --oneline --graph  # Visualisez l'historique linéaire

Étape 4 — Fusionner la branche

git switch main
git merge feature/square-root  # Fast-forward grâce au rebase
Partie C : git cherry-pick

Étape 1 — Créer une branche expérimentale avec un commit intéressant

git switch -c feature/absolute  # Crée et active la branche

Ajoutez une méthode absolute dans src/Calculator.java, puis commitez :

public int absolute(int n) {
    return Math.abs(n);
}
git add src/Calculator.java
git commit -m "feat: ajout absolute() pour valeur absolue"

Étape 2 — Ajouter d’autres commits non désirés dans la branche expérimentale

echo "// Code expérimental à ne pas intégrer" >> src/Calculator.java
git add src/Calculator.java
git commit -m "WIP: code expérimental instable"

Étape 3 — Récupérer uniquement le commit de la méthode absolute avec cherry-pick

git log --oneline  # Repérez l'identifiant du commit « ajout absolute() »
git switch main
git cherry-pick <identifiant-commit-absolute>

→ Le commit avec la méthode absolute est maintenant dans main, sans avoir récupéré le code expérimental instable.

Partie D : git revert

Étape 1 — Créer un commit à annuler

Ajoutez une méthode buguée dans src/Calculator.java, puis commitez :

public int buggyMethod(int a) {
    return a / 0;  // Bug volontaire !
}
git add src/Calculator.java
git commit -m "feat: méthode expérimentale"

Étape 2 — Annuler le commit avec revert

git revert HEAD  # Annule le dernier commit

→ Un nouveau commit est créé qui supprime la méthode buguée.

git log --oneline  # Les deux commits sont visibles dans l'historique

Exercice 7 : Workflow d’équipe réel (30 min)

Objectifs

  • Simuler un développement en équipe sur le même projet
  • Gérer les synchronisations avec pull –rebase
  • Lier des commits à des tickets GitLab
  • Pratiquer le workflow complet : ticket → branche → MR → merge

Configuration

Cet exercice nécessite de travailler en binôme. Si vous êtes seul, simulez deux développeurs en utilisant deux clones du dépôt dans des dossiers différents.

À faire

Étape 1 — Créer deux tickets sur GitLab

  • a. Ticket #1 : ‘Ajouter validation min/max pour les opérations’

  • b. Ticket #2 : ‘Ajouter méthode negate()’

  • c. Assignez chaque ticket à un développeur différent

Étape 2 — Dev A : Implémenter le ticket #1

git switch -c feature/1-validation

Créez src/Validator.java :

public class Validator {
    public static boolean isInRange(int value, int min, int max) {
        return value >= min && value <= max;
    }
}
git add src/Validator.java
git commit -m "feat: validation min/max (closes #1)"
git push -u origin feature/1-validation

Étape 3 — Dev B : Implémenter le ticket #2 en parallèle

git switch -c feature/2-negate

Ajoutez dans AdvancedCalculator.java :

public int negate(int n) {
    return -1 * n;
}
git add src/AdvancedCalculator.java
git commit -m "feat: negate (closes #2)"
git push -u origin feature/2-negate

Étape 4 — Dev A : Créer une MR et la faire valider

  • a. Créez une MR depuis feature/1-validation vers main

  • b. Dev B fait la revue et approuve

  • c. Dev A merge la MR

Étape 5 — Dev B : Synchroniser avec main avant de créer sa MR

git switch feature/2-negate
git fetch origin  # Récupère les dernières modifications
git rebase origin/main  # Rebase sur le main à jour

→ Les commits de Dev B sont maintenant au-dessus de ceux de Dev A

Étape 6 — Dev B : Créer sa MR et la faire valider

git push --force-with-lease origin feature/2-negate
  • a. Créez une MR depuis feature/2-negate vers main

  • b. Dev A fait la revue et approuve

  • c. Dev B merge la MR

Étape 7 — Les deux devs : Synchroniser en local

git switch main
git pull --rebase origin main

Étape 8 — Vérifier la fermeture automatique des tickets

Sur GitLab, les tickets #1 et #2 doivent être automatiquement fermés car les messages de commit contiennent le mot-clé closes suivi du numéro de ticket. Une simple mention (#1) crée un lien vers le ticket, mais seuls les mots-clés closes, fixes ou resolves déclenchent la fermeture automatique.

Bonnes pratiques :

  • Toujours faire git fetch + git rebase origin/main avant de créer une MR
  • Référencer les tickets dans les commits (#X) et les fermer avec closes #X
  • Utiliser –force-with-lease au lieu de –force (empêche d’écraser par inadvertance le code d’un autre développeur)
  • Faire des commits atomiques et des messages descriptifs

Bloc 4 : Outils GitLab

Durée indicative des exercices : 15 min. Cours associé : Travailler avec GitLab (rôles, tickets, jalons, wiki) et Introduction à GitLab CI.

Exercice 8 : Gestion de projet complète (15 min)

Objectifs

  • Créer des jalons (milestones) pour structurer le projet
  • Gérer les permissions et les rôles d’équipe
  • Documenter le projet avec le Wiki
  • Avoir une vue d’ensemble des fonctionnalités GitLab

À faire

Étape 1 — Créer un jalon ‘Version 2.0’

  • a. Plan > Milestones > New milestone

  • b. Titre : Version 2.0

  • c. Date d’échéance : 2 semaines à partir d’aujourd’hui

  • d. Description : Calculatrice scientifique complète

Étape 2 — Créer des tickets pour Version 2.0

Créez 3 tickets et associez-les au jalon :

  • a. ‘Ajouter fonction logarithme’ - Label: feature

  • b. ‘Ajouter fonction exponentielle’ - Label: feature

  • c. ‘Créer tests unitaires’ - Label: testing

Étape 3 — Configurer les permissions

Si vous travaillez en binôme :

  • a. Manage > Members

  • b. Invitez votre binôme avec le rôle ‘Developer’

  • c. Expliquez les différences entre Guest, Reporter, Developer, Maintainer

Étape 4 — Créer la documentation dans le Wiki

  • a. Plan > Wiki > Create your first page

  • b. Titre : Documentation API

  • c. Contenu (exemple) :

# API Calculatrice

## Calculator
- `add(int a, int b)` : Addition de deux nombres
- `subtract(int a, int b)` : Soustraction
- `multiply(int a, int b)` : Multiplication

## AdvancedCalculator
- `divide(int a, int b)` : Division (gère division par zéro)
- `power(int base, int exp)` : Puissance
- `squareRoot(int n)` : Racine carrée
- `absolute(int n)` : Valeur absolue
- `negate(int n)` : Opposé

Étape 5 — Explorer les autres fonctionnalités GitLab

  • a. Analyze > Repository analytics : graphiques d’activité

  • b. Plan > Boards : visualisation Kanban des tickets

  • c. Settings > General > Badges : badges de statut pour README

Pour aller plus loin

Défis supplémentaires

Si vous avez terminé tous les exercices, ces idées prolongent le TP :

  • le rebase interactif (git rebase -i) pour réordonner, fusionner ou reformuler des commits ;
  • git bisect pour retrouver le commit qui a introduit un bug ;
  • la commande expérimentale git history, disponible depuis Git 2.54 (voir Quoi de neuf côté Git ?) ;
  • une première chaîne d’intégration continue avec GitLab CI (voir Introduction à GitLab CI).

En cas de blocage

ProblèmeSolution
Git non installéInstaller Git depuis git-scm.com
Connexion HTTPS bloquéeUtiliser SSH, en le configurant pendant l’exercice 1 si besoin
Fusion en conflit qui n’aboutit pasgit merge --abort, puis recommencer calmement

Ce que vous avez appris

BlocCompétences acquises
Fondamentauxinit, add, commit, push, pull, status, log, blame, tag
Branchesbranch, switch, merge, résolution de conflits, merge requests
Avancéstash, rebase, cherry-pick, revert, workflow d’équipe
GitLabTickets, jalons, permissions, wiki, merge requests, revue de code

Bonnes pratiques à retenir

  • Committer souvent, avec des messages clairs et descriptifs.
  • Une branche = une fonctionnalité.
  • Toujours pull avant de push.
  • Protéger la branche main et passer par des merge requests.
  • Faire des revues de code constructives.
  • Utiliser un .gitignore dès le début du projet.
  • Lier les commits aux tickets (#X dans les messages).

Ressources