# [COURS] EXERCICES CORRIGÉS GIT & GITHUB - ULTRA DÉTAILLÉS

## [LIVRE] INTRODUCTION

Ce document contient **10 exercices pratiques corrigés** sur Git et GitHub, allant du niveau débutant au niveau expert. Chaque exercice simule un **projet d'entreprise réel** avec **5 développeurs** qui collaborent sur une application web complète.

**Équipe de développement :**
- [PERSONNE][CODE] **Alice** - Lead Developer (Responsable technique)
- [PERSONNE][CODE] **Bob** - Backend Developer (API et base de données)
- [PERSONNE][CODE] **Charlie** - Frontend Developer (Interface utilisateur)
- [PERSONNE][CODE] **Diana** - DevOps Engineer (Infrastructure et déploiement)
- [PERSONNE][CODE] **Emma** - QA Engineer (Tests et qualité)

**Projet fil rouge : "TaskMaster Pro"**
Application de gestion de tâches collaborative avec :
- Backend API (Node.js/Express)
- Frontend (React)
- Base de données (PostgreSQL)
- Infrastructure (Docker, CI/CD)

**Niveau de progression :**
- [VERT] Exercices 1-2 : Débutant (Bases Git)
- [JAUNE] Exercices 3-5 : Intermédiaire (Collaboration)
- [ROUGE] Exercices 6-10 : Avancé/Expert (Workflows professionnels)

**Chaque exercice contient :**
- [OK] Énoncé détaillé avec contexte professionnel
- [OK] Objectifs pédagogiques précis
- [OK] Solution complète étape par étape
- [OK] Explications ligne par ligne avec Pourquoi/Comment/Quand
- [OK] Erreurs courantes et solutions
- [OK] Tests de validation
- [OK] Points clés à retenir
- [OK] Pour aller plus loin

**Prérequis :**
- Un ordinateur avec terminal/ligne de commande
- Éditeur de texte (VS Code recommandé)
- Compte GitHub créé
- Motivation et curiosité [RAPIDE]

**Temps total estimé : 20-30 heures** (répartition sur plusieurs jours recommandée)

Bon courage ! [FORCE]

---

---

# [VERT] EXERCICE 1 : PREMIERS PAS AVEC GIT

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu rejoins l'équipe de développement de **TaskMaster Pro** en tant que **Alice**, Lead Developer. Ton premier jour, tu dois :
1. Initialiser le projet Git
2. Créer la structure de base du projet
3. Faire tes premiers commits
4. Comprendre l'historique Git

### Objectifs

- Installer et configurer Git correctement
- Comprendre les 3 zones de Git (Working Directory, Staging Area, Repository)
- Créer des commits atomiques et bien nommés
- Naviguer dans l'historique
- Comprendre les commandes `git status`, `git log`, `git diff`

### Livrables

- Dépôt Git local initialisé
- Structure de projet créée
- Minimum 5 commits bien documentés
- Fichier `.gitignore` configuré

### Contraintes

- Messages de commit en anglais (convention professionnelle)
- Commits atomiques (1 commit = 1 changement logique)
- Temps estimé : 2-3 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :

- [OK] Installer et configurer Git
- [OK] Comprendre le modèle de versioning de Git
- [OK] Créer des commits de qualité professionnelle
- [OK] Utiliser `.gitignore` efficacement
- [OK] Naviguer dans l'historique Git
- [OK] Comprendre la différence entre Working Directory, Staging Area et Repository
- [OK] Utiliser `git status`, `git add`, `git commit`, `git log`, `git diff`

---

## [DOCS] PRÉREQUIS

- Terminal/Ligne de commande
- Éditeur de texte
- Aucune connaissance Git requise

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Installation de Git

**Sur Ubuntu/Debian :**

```bash
sudo apt update
sudo apt install git -y
```

**Sur macOS :**

```bash
# Avec Homebrew
brew install git

# Ou installer Xcode Command Line Tools
xcode-select --install
```

**Sur Windows :**

1. Télécharger Git depuis https://git-scm.com/download/win
2. Exécuter l'installateur
3. Choisir les options par défaut (Git Bash recommandé)

---

**Vérifier l'installation :**

```bash
git --version
```

**Résultat attendu :**

```
git version 2.34.1
```

**[OK] Git installé !**

---

### ÉTAPE 2 : Configuration initiale de Git

**Pourquoi configurer Git ?**

Git a besoin de savoir **qui** fait les commits. Ces informations apparaissent dans l'historique et sont **publiques** sur GitHub.

---

**Configuration de l'identité (OBLIGATOIRE) :**

```bash
git config --global user.name "Alice Johnson"
git config --global user.email "alice@taskmaster.com"
```

**Explication :**

**`git config`** = Commande de configuration

**`--global`** = Configuration globale (pour tous les projets)
- Sans `--global`, la config s'applique seulement au dépôt actuel
- `--system` = Pour tous les utilisateurs du système
- `--local` = Pour le dépôt actuel uniquement

**`user.name`** = Ton nom complet
- Apparaîtra dans chaque commit
- Peut contenir des espaces
- Convention : Prénom Nom

**`user.email`** = Ton email professionnel
- **DOIT correspondre à ton email GitHub** (pour les contributions)
- GitHub lie les commits à ton compte via cet email
- Utilise l'email "noreply" de GitHub si tu veux rester privé

---

**Configuration de l'éditeur par défaut :**

```bash
# VS Code
git config --global core.editor "code --wait"

# Vim
git config --global core.editor "vim"

# Nano
git config --global core.editor "nano"
```

**Explication :**

Git ouvre un éditeur pour :
- Messages de commit longs
- Résolution de conflits
- Rebase interactif

`--wait` = Git attend que l'éditeur se ferme avant de continuer

---

**Configuration des couleurs (lisibilité) :**

```bash
git config --global color.ui auto
```

**Colorie les sorties Git (diff, status, branch, etc.)**

---

**Configuration des fins de ligne (IMPORTANT pour collaboration multi-OS) :**

```bash
# Sur Linux/macOS
git config --global core.autocrlf input

# Sur Windows
git config --global core.autocrlf true
```

**Explication :**

**Problème :** Windows utilise `CRLF` (\r\n), Linux/macOS utilisent `LF` (\n)

**`autocrlf input`** (Linux/macOS) :
- Convertit CRLF -> LF lors du commit
- Ne modifie rien au checkout
- Garantit que le dépôt contient toujours LF

**`autocrlf true`** (Windows) :
- Convertit LF -> CRLF au checkout
- Convertit CRLF -> LF au commit
- Tu travailles avec CRLF localement, mais LF dans le dépôt

**Pourquoi c'est important ?**
Sans ça, chaque fichier apparaît modifié lors du passage Windows <-> Linux

---

**Vérifier la configuration :**

```bash
git config --list
```

**Résultat :**

```
user.name=Alice Johnson
user.email=alice@taskmaster.com
core.editor=code --wait
color.ui=auto
core.autocrlf=input
```

---

**Voir la config d'un paramètre spécifique :**

```bash
git config user.name
```

**Résultat :**

```
Alice Johnson
```

---

### ÉTAPE 3 : Initialiser le dépôt Git

**Créer le dossier du projet :**

```bash
mkdir taskmaster-pro
cd taskmaster-pro
```

---

**Initialiser Git :**

```bash
git init
```

**Résultat :**

```
Initialized empty Git repository in /home/alice/taskmaster-pro/.git/
```

**Explication :**

**`git init`** crée un sous-dossier `.git/` contenant toute la base de données Git :
- Historique des commits
- Configuration locale
- Branches
- Hooks
- etc.

**[ATTENTION] Le dossier `.git/` est caché (commence par `.`)**

**Voir le dossier `.git/` :**

```bash
ls -la
```

**Résultat :**

```
drwxr-xr-x  3 alice alice 4096 Jan  3 10:00 .
drwxr-xr-x 20 alice alice 4096 Jan  3 10:00 ..
drwxr-xr-x  7 alice alice 4096 Jan  3 10:00 .git
```

**[OK] Dépôt Git initialisé !**

---

**Vérifier le statut :**

```bash
git status
```

**Résultat :**

```
On branch main

No commits yet

nothing to commit (create/copy files and use "git add" to track)
```

**Explication :**

**`On branch main`** = Tu es sur la branche principale (par défaut)
- Anciennement "master", maintenant "main" (inclusivité)
- Configurable avec `git config --global init.defaultBranch main`

**`No commits yet`** = Aucun commit créé

**`nothing to commit`** = Aucun fichier à commiter

---

### ÉTAPE 4 : Créer la structure du projet

```bash
# Créer les dossiers
mkdir -p backend/{src,tests,config}
mkdir -p frontend/{src,public,tests}
mkdir -p database/{migrations,seeds}
mkdir -p docs
```

**Explication de `-p` :**
- Crée les dossiers parents si nécessaires
- `backend/{src,tests}` = `backend/src` + `backend/tests`

---

**Créer le README :**

```bash
cat > README.md << 'EOF'
# TaskMaster Pro

Enterprise-grade task management application.

## Team

- Alice Johnson - Lead Developer
- Bob Smith - Backend Developer
- Charlie Brown - Frontend Developer  
- Diana Prince - DevOps Engineer
- Emma Watson - QA Engineer

## Tech Stack

- **Backend**: Node.js + Express
- **Frontend**: React + TypeScript
- **Database**: PostgreSQL
- **Infrastructure**: Docker + Kubernetes

## Getting Started

```bash
# Install dependencies
npm install

# Start development server
npm run dev
```

## License

MIT
EOF
```

**Explication de `<< 'EOF'` :**
- Heredoc (document embarqué)
- Crée un fichier avec du contenu multi-lignes
- `'EOF'` = Les variables ne sont PAS interprétées

---

**Vérifier le statut :**

```bash
git status
```

**Résultat :**

```
On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        README.md
        backend/
        database/
        docs/
        frontend/

nothing added to commit but untracked files present (use "git add" to track)
```

**Explication :**

**`Untracked files`** = Fichiers/dossiers que Git ne suit pas encore
- Git les voit mais ne les versionnera pas sans `git add`

**`nothing added to commit`** = Rien dans la Staging Area

---

### ÉTAPE 5 : Comprendre les 3 zones de Git

**Git a 3 zones principales :**

```
┌─────────────────────────────────────────────────────────┐
│                  WORKING DIRECTORY                      │
│  (Tes fichiers tels que tu les vois)                  │
│                                                         │
│  README.md                                              │
│  backend/                                               │
│  frontend/                                              │
└─────────────────────────────────────────────────────────┘
                         │
                         │ git add
                         [BLACK_DOWN-POINTING_TRIANGLE]
┌─────────────────────────────────────────────────────────┐
│                   STAGING AREA                          │
│  (Index - Fichiers préparés pour le commit)           │
│                                                         │
│  README.md (staged)                                     │
└─────────────────────────────────────────────────────────┘
                         │
                         │ git commit
                         [BLACK_DOWN-POINTING_TRIANGLE]
┌─────────────────────────────────────────────────────────┐
│                   REPOSITORY (.git/)                    │
│  (Historique permanent des commits)                    │
│                                                         │
│  Commit 1: "Initial commit"                             │
│  Commit 2: "Add backend structure"                      │
└─────────────────────────────────────────────────────────┘
```

**Working Directory** = Ton espace de travail
- Fichiers modifiables
- État actuel des fichiers

**Staging Area** (Index) = Zone de préparation
- Fichiers sélectionnés pour le prochain commit
- Permet de choisir QUOI commiter

**Repository** = Base de données Git
- Stocke l'historique permanent
- Inaccessible directement (via commandes Git)

**Workflow typique :**

```bash
# 1. Modifier un fichier
echo "test" > file.txt

# 2. Ajouter à la Staging Area
git add file.txt

# 3. Commiter dans le Repository
git commit -m "Add test file"
```

---

### ÉTAPE 6 : Premier commit

**Ajouter le README à la Staging Area :**

```bash
git add README.md
```

**Vérifier le statut :**

```bash
git status
```

**Résultat :**

```
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   README.md

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        backend/
        database/
        docs/
        frontend/
```

**Explication :**

**`Changes to be committed`** = Fichiers dans la Staging Area
- Prêts à être commités
- En vert (avec couleurs activées)

**`new file: README.md`** = Nouveau fichier ajouté

**Pour retirer de la Staging Area :**

```bash
git rm --cached README.md
```

**Mais ne le fais PAS maintenant !**

---

**Créer le premier commit :**

```bash
git commit -m "docs: initialize project with README"
```

**Résultat :**

```
[main (root-commit) a1b2c3d] docs: initialize project with README
 1 file changed, 25 insertions(+)
 create mode 100644 README.md
```

**Explication :**

**`git commit`** = Enregistre les changements dans le Repository

**`-m "message"`** = Message de commit
- Décrit POURQUOI ce changement
- **Obligatoire**
- Convention : Présent de l'impératif ("Add" pas "Added")

**`[main (root-commit) a1b2c3d]`** :
- `main` = Branche actuelle
- `root-commit` = Premier commit (racine)
- `a1b2c3d` = Hash court du commit (identifiant unique)

**`1 file changed, 25 insertions(+)`** :
- 1 fichier modifié
- 25 lignes ajoutées

**`create mode 100644 README.md`** :
- `100644` = Permissions (fichier normal)
- `100755` = Fichier exécutable
- `120000` = Lien symbolique

---

**Vérifier l'historique :**

```bash
git log
```

**Résultat :**

```
commit a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0 (HEAD -> main)
Author: Alice Johnson <alice@taskmaster.com>
Date:   Sat Jan 3 10:15:00 2026 +0000

    docs: initialize project with README
```

**Explication :**

**`commit a1b2c3d...`** = Hash SHA-1 complet du commit
- 40 caractères hexadécimaux
- Identifiant **unique** et **immuable**
- Calculé à partir du contenu + metadata

**`(HEAD -> main)`** :
- `HEAD` = Pointeur vers le commit actuel
- `->` = HEAD pointe vers la branche `main`
- `main` = Nom de la branche

**`Author`** = Auteur du commit (user.name + user.email)

**`Date`** = Date et heure du commit

**`docs: initialize project with README`** = Message de commit

---

**Voir un log condensé :**

```bash
git log --oneline
```

**Résultat :**

```
a1b2c3d (HEAD -> main) docs: initialize project with README
```

**Format : `<hash court> (<branches>) <message>`**

---

### ÉTAPE 7 : Convention de messages de commit

**Format professionnel (Conventional Commits) :**

```
<type>(<scope>): <subject>

<body> (optionnel)

<footer> (optionnel)
```

**Types courants :**

| Type | Usage | Exemple |
|------|-------|---------|
| `feat` | Nouvelle fonctionnalité | `feat(auth): add login endpoint` |
| `fix` | Correction de bug | `fix(api): resolve null pointer exception` |
| `docs` | Documentation | `docs(readme): update installation steps` |
| `style` | Formatage (pas de changement de code) | `style(css): fix indentation` |
| `refactor` | Refactoring | `refactor(db): optimize query performance` |
| `test` | Ajout/modification de tests | `test(auth): add unit tests for login` |
| `chore` | Tâches diverses | `chore(deps): update dependencies` |
| `perf` | Performance | `perf(api): improve response time` |
| `ci` | CI/CD | `ci(github): add automated tests` |

**Exemples :**

```bash
# [OK] BON
git commit -m "feat(backend): add user authentication"
git commit -m "fix(frontend): resolve button click issue"
git commit -m "docs(api): document authentication endpoints"

# [X] MAUVAIS
git commit -m "changes"
git commit -m "fix bug"
git commit -m "WIP"
git commit -m "asdfghjkl"
```

---

### ÉTAPE 8 : Créer la structure backend

```bash
# Créer package.json
cat > backend/package.json << 'EOF'
{
  "name": "taskmaster-backend",
  "version": "1.0.0",
  "description": "TaskMaster Pro Backend API",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js",
    "dev": "nodemon src/index.js",
    "test": "jest"
  },
  "keywords": ["api", "tasks", "express"],
  "author": "TaskMaster Team",
  "license": "MIT"
}
EOF
```

---

**Créer le fichier principal :**

```bash
cat > backend/src/index.js << 'EOF'
// TaskMaster Pro - Backend API
// Author: Alice Johnson

const PORT = process.env.PORT || 3000;

console.log(`TaskMaster Backend API`);
console.log(`Starting server on port ${PORT}...`);

// TODO: Initialize Express server
// TODO: Connect to database
// TODO: Load routes
EOF
```

---

**Ajouter et commiter :**

```bash
git add backend/
git commit -m "feat(backend): initialize backend structure

- Add package.json with npm scripts
- Create entry point index.js
- Define project metadata"
```

**Explication du message multi-lignes :**

**Ligne 1** : `<type>(<scope>): <subject>` (max 50 caractères)
- Résumé court et précis

**Ligne 2** : Vide (séparation)

**Lignes 3+** : `<body>` (détails)
- Liste des changements
- Pourquoi ce changement
- Contexte additionnel

**Pour un message multi-lignes, utilise :**

```bash
git commit
```

Sans `-m`, Git ouvre l'éditeur configuré.

---

**Vérifier le log :**

```bash
git log --oneline
```

**Résultat :**

```
b2c3d4e (HEAD -> main) feat(backend): initialize backend structure
a1b2c3d docs: initialize project with README
```

**2 commits !**

---

### ÉTAPE 9 : Créer le .gitignore

**Pourquoi `.gitignore` ?**

Git doit **ignorer** certains fichiers :
- Fichiers générés (node_modules, dist, build)
- Fichiers système (.DS_Store, Thumbs.db)
- Fichiers secrets (.env, credentials)
- Fichiers IDE (.vscode, .idea)
- Logs et caches

**Créer `.gitignore` :**

```bash
cat > .gitignore << 'EOF'
# ═══════════════════════════════════════════════════════════════
# TASKMASTER PRO - GITIGNORE
# ═══════════════════════════════════════════════════════════════

# ───────────────────────────────────────────────────────────────
# NODE.JS / NPM
# ───────────────────────────────────────────────────────────────

node_modules/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
yarn.lock

# ───────────────────────────────────────────────────────────────
# BUILD / DIST
# ───────────────────────────────────────────────────────────────

dist/
build/
out/
*.log
*.pid
*.seed
*.pid.lock

# ───────────────────────────────────────────────────────────────
# ENVIRONNEMENT / SECRETS
# ───────────────────────────────────────────────────────────────

.env
.env.local
.env.*.local
*.pem
*.key
credentials.json

# ───────────────────────────────────────────────────────────────
# IDE / ÉDITEURS
# ───────────────────────────────────────────────────────────────

.vscode/
.idea/
*.swp
*.swo
*~
.DS_Store

# ───────────────────────────────────────────────────────────────
# TESTS / COVERAGE
# ───────────────────────────────────────────────────────────────

coverage/
.nyc_output/
*.test.local

# ───────────────────────────────────────────────────────────────
# SYSTÈME D'EXPLOITATION
# ───────────────────────────────────────────────────────────────

Thumbs.db
Desktop.ini
$RECYCLE.BIN/

# ───────────────────────────────────────────────────────────────
# BASE DE DONNÉES LOCALE
# ───────────────────────────────────────────────────────────────

*.sqlite
*.db
*.db-journal

# ═══════════════════════════════════════════════════════════════
EOF
```

---

**Tester `.gitignore` :**

```bash
# Créer un dossier node_modules (simulé)
mkdir backend/node_modules
echo "fake package" > backend/node_modules/fake.js

# Créer un fichier .env
echo "SECRET_KEY=abc123" > backend/.env

# Vérifier le statut
git status
```

**Résultat :**

```
On branch main
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        .gitignore

nothing added to commit but untracked files present (use "git add" to track)
```

**[OK] `node_modules/` et `.env` sont ignorés !**

---

**Commiter `.gitignore` :**

```bash
git add .gitignore
git commit -m "chore: add comprehensive .gitignore

- Ignore node_modules and build artifacts
- Exclude environment files and secrets
- Filter IDE and OS-specific files"
```

---

### ÉTAPE 10 : Utiliser `git diff`

**Modifier un fichier :**

```bash
echo "\n## Architecture\n\n- Microservices architecture\n- RESTful API design" >> README.md
```

---

**Voir les différences NON stagées :**

```bash
git diff
```

**Résultat :**

```diff
diff --git a/README.md b/README.md
index a1b2c3d..e4f5g6h 100644
--- a/README.md
+++ b/README.md
@@ -22,3 +22,7 @@ npm run dev
 ## License
 
 MIT
+
+## Architecture
+
+- Microservices architecture
+- RESTful API design
```

**Explication :**

**`diff --git a/README.md b/README.md`** :
- Compare l'ancienne version (a/) avec la nouvelle (b/)

**`index a1b2c3d..e4f5g6h`** :
- Hash de l'ancienne version
- Hash de la nouvelle version

**`--- a/README.md`** = Ancienne version
**`+++ b/README.md`** = Nouvelle version

**`@@ -22,3 +22,7 @@`** :
- `-22,3` = Ligne 22, 3 lignes dans l'ancienne version
- `+22,7` = Ligne 22, 7 lignes dans la nouvelle version

**Lignes commençant par `-`** = Supprimées (en rouge)
**Lignes commençant par `+`** = Ajoutées (en vert)
**Lignes sans préfixe** = Inchangées (contexte)

---

**Ajouter à la Staging Area :**

```bash
git add README.md
```

---

**Voir les différences STAGÉES :**

```bash
git diff --staged
```

**Ou :**

```bash
git diff --cached
```

**Même sortie que `git diff` mais pour les fichiers stagés.**

---

**Commiter :**

```bash
git commit -m "docs(readme): add architecture section"
```

---

### ÉTAPE 11 : Naviguer dans l'historique

**Voir l'historique complet :**

```bash
git log
```

**Trop verbeux ? Utilise des options :**

```bash
# Format court
git log --oneline

# Avec graphe (branches)
git log --oneline --graph

# Avec statistiques
git log --stat

# Derniers N commits
git log -n 3

# Depuis une date
git log --since="2 days ago"

# Par auteur
git log --author="Alice"

# Par message
git log --grep="backend"
```

---

**Voir un commit spécifique :**

```bash
git show a1b2c3d
```

**Affiche :**
- Metadata (auteur, date)
- Message complet
- Diff des changements

---

**Voir les fichiers modifiés dans un commit :**

```bash
git show --name-only a1b2c3d
```

---

### [OK] TESTS DE VALIDATION

**1. Configuration correcte**

```bash
git config user.name
git config user.email
```

- [ ] Nom et email affichés correctement

---

**2. Dépôt initialisé**

```bash
ls -la | grep .git
```

- [ ] Dossier `.git/` présent

---

**3. Commits créés**

```bash
git log --oneline
```

- [ ] Au moins 4 commits visibles
- [ ] Messages de commit suivent la convention

---

**4. `.gitignore` fonctionne**

```bash
mkdir test_ignore
touch test_ignore/file.log
git status
```

- [ ] `test_ignore/` n'apparaît pas (si `.log` est ignoré)

---

**5. Zones Git comprises**

```bash
# Créer un fichier
echo "test" > test.txt

# Vérifier qu'il est untracked
git status | grep "Untracked"

# Ajouter
git add test.txt

# Vérifier qu'il est staged
git status | grep "Changes to be committed"

# Supprimer (nettoyage)
git rm --cached test.txt
rm test.txt
```

- [ ] Toutes les étapes passent

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : "Please tell me who you are"

**Symptôme :**

```
*** Please tell me who you are.

Run

  git config --global user.email "you@example.com"
  git config --global user.name "Your Name"
```

**Cause :** Configuration user.name/user.email manquante

**Solution :**

```bash
git config --global user.name "Ton Nom"
git config --global user.email "ton@email.com"
```

---

#### Erreur 2 : "Nothing to commit"

**Symptôme :**

```
nothing to commit, working tree clean
```

**Cause :** Aucun fichier modifié ou staging area vide

**Solution :**

```bash
# Vérifier le statut
git status

# Si des fichiers sont modifiés mais pas ajoutés
git add .

# Ensuite commiter
git commit -m "message"
```

---

#### Erreur 3 : "Aborting commit due to empty commit message"

**Symptôme :**

```
Aborting commit due to empty commit message.
```

**Cause :** Message de commit vide

**Solution :**

```bash
# Toujours fournir un message
git commit -m "feat: add new feature"
```

---

#### Erreur 4 : ".gitignore ne fonctionne pas"

**Symptôme :**

Fichiers ignorés apparaissent toujours dans `git status`

**Cause :** Fichiers déjà trackés par Git avant l'ajout au `.gitignore`

**Solution :**

```bash
# Supprimer du cache Git (pas du disque)
git rm --cached -r node_modules/

# Commiter la suppression
git commit -m "chore: remove node_modules from git"

# Maintenant .gitignore fonctionne
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Configuration Git**
- `user.name` et `user.email` obligatoires
- `--global` pour tous les projets
- `core.editor` pour l'éditeur par défaut

**2. Les 3 zones**
- Working Directory = Fichiers actuels
- Staging Area = Préparation commit
- Repository = Historique permanent

**3. Workflow de base**
```
Modifier -> git add -> git commit
```

**4. Messages de commit**
- Format : `<type>(<scope>): <subject>`
- Présent de l'impératif
- Descriptif et clair

**5. `.gitignore`**
- Ignorer node_modules, .env, IDE, logs
- Créer AVANT le premier commit si possible
- Utiliser des templates (github.com/github/gitignore)

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Alias Git (raccourcis)**

```bash
git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.lg "log --oneline --graph --all"
```

**Utilisation :**

```bash
git st      # au lieu de git status
git lg      # log graphique coloré
```

---

**2. Templates de commit**

```bash
git config --global commit.template ~/.gitmessage
```

**Créer `~/.gitmessage` :**

```
# <type>(<scope>): <subject> (max 50 char)
# |<----  Using a Maximum Of 50 Characters  ---->|

# Explain why this change is being made
# |<----   Try To Limit Each Line to a Maximum Of 72 Characters   ---->|

# Provide links or keys to any relevant tickets, articles or other resources
# Example: Github issue #23

# --- COMMIT END ---
# Type can be 
#    feat     (new feature)
#    fix      (bug fix)
#    refactor (refactoring code)
#    style    (formatting, missing semi colons, etc; no code change)
#    docs     (changes to documentation)
#    test     (adding or refactoring tests; no production code change)
#    chore    (updating grunt tasks etc; no production code change)
```

---

**3. Git Hooks (automation)**

```bash
# Hook pre-commit (s'exécute avant chaque commit)
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/sh
echo "Running pre-commit checks..."

# Vérifier qu'il n'y a pas de console.log
if git diff --cached | grep "console.log"; then
    echo "Error: Found console.log in staged files"
    exit 1
fi

echo "Pre-commit checks passed [OK]"
EOF

chmod +x .git/hooks/pre-commit
```

---

**4. Git Attributes (contrôle fin)**

```bash
cat > .gitattributes << 'EOF'
# Auto detect text files and perform LF normalization
* text=auto

# Documents
*.md text
*.txt text

# Scripts
*.sh text eol=lf
*.bat text eol=crlf

# Binary files
*.png binary
*.jpg binary
*.pdf binary
EOF
```

---

**5. Amender le dernier commit**

```bash
# Oublié un fichier ?
git add forgotten-file.txt
git commit --amend --no-edit

# Modifier le message ?
git commit --amend -m "New message"
```

**[ATTENTION] Ne jamais amender un commit déjà pushé !**

---

## [COURS] CONCLUSION DE L'EXERCICE 1

**[OK] Félicitations ! Tu maîtrises les bases de Git !**

**Ce que tu as appris :**
- Installer et configurer Git
- Initialiser un dépôt
- Créer des commits de qualité
- Utiliser `.gitignore`
- Naviguer dans l'historique
- Comprendre le modèle de Git

**Compétences acquises :**
- [OK] Configuration Git (niveau professionnel)
- [OK] Commits atomiques
- [OK] Convention Conventional Commits
- [OK] Utilisation de .gitignore
- [OK] Navigation dans l'historique

**Temps moyen de réalisation :** 2-3 heures

**Prochaine étape :** Exercice 2 - Branches et fusion ! [ARBRE]

---

(Le document continuerait avec les 9 autres exercices dans le même niveau de détail...)

J'ai créé le premier exercice avec le même niveau de détail exhaustif que le document Apache. Veux-tu que je continue avec les exercices 2 à 10 ? Cela représentera un document de 200-300 pages au total. Dis-moi si tu veux :

1. [OK] Continuer avec tous les 9 exercices restants (document complet)
2. Me concentrer sur certains exercices spécifiques
3. Ajuster le niveau de détail

# [VERT] EXERCICE 2 : BRANCHES ET FUSION

## [LISTE] ÉNONCÉ

### Contexte professionnel

Le projet TaskMaster Pro avance. **Alice** (Lead Developer) organise une réunion avec l'équipe pour définir comment travailler en parallèle sans se marcher sur les pieds.

**Décision :** Utiliser des branches Git pour :
- Isoler le développement de nouvelles fonctionnalités
- Permettre le travail en parallèle
- Faciliter la revue de code
- Protéger la branche principale

**Aujourd'hui, tu vas :**
1. Créer et gérer des branches
2. Comprendre les différents types de merge
3. Utiliser `git stash` pour sauvegarder du travail temporaire
4. Mettre en place un workflow de branches simple

### Objectifs

- Maîtriser la création et navigation entre branches
- Comprendre fast-forward vs 3-way merge
- Fusionner des branches proprement
- Utiliser `git stash` pour gérer le travail en cours
- Supprimer les branches obsolètes

### Livrables

- Au moins 3 branches créées
- Plusieurs merges effectués (fast-forward et 3-way)
- Utilisation de `git stash`
- Arbre de branches propre

### Contraintes

- Nommage des branches selon convention
- Branches de fonctionnalités éphémères (à supprimer après merge)
- Messages de merge descriptifs
- Temps estimé : 2-3 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :

- [OK] Créer et supprimer des branches
- [OK] Naviguer entre les branches
- [OK] Comprendre HEAD et les pointeurs de branches
- [OK] Fusionner des branches (merge)
- [OK] Différencier fast-forward et 3-way merge
- [OK] Utiliser `git stash` pour le travail temporaire
- [OK] Visualiser l'arbre de branches
- [OK] Nettoyer les branches obsolètes

---

## [DOCS] PRÉREQUIS

- Exercice 1 terminé (dépôt Git initialisé avec commits)
- Compréhension des commits
- Terminal/ligne de commande

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Comprendre les branches

**Qu'est-ce qu'une branche ?**

Une branche est un **pointeur mobile** vers un commit. Par défaut, tu es sur la branche `main`.

**Visualisation :**

```
main ──[BLACK_CIRCLE]──[BLACK_CIRCLE]──[BLACK_CIRCLE]
       │  │  │
       │  │  └─ Commit 3 (HEAD)
       │  └──── Commit 2
       └─────── Commit 1
```

**Quand tu crées une branche :**

```
       feature
          v
main ──[BLACK_CIRCLE]──[BLACK_CIRCLE]──[BLACK_CIRCLE]
       │  │  │
       │  │  └─ Commit 3 (HEAD)
       │  └──── Commit 2
       └─────── Commit 1
```

**Les deux branches pointent vers le même commit !**

**Après un commit sur `feature` :**

```
             feature
                v
       [BLACK_CIRCLE]──────[BLACK_CIRCLE] (nouveau commit)
      ╱
main [BLACK_CIRCLE]──[BLACK_CIRCLE]──[BLACK_CIRCLE]
```

**`main` n'a pas bougé, `feature` a avancé.**

---

**Pourquoi utiliser des branches ?**

| Situation | Sans branches | Avec branches |
|-----------|---------------|---------------|
| Nouvelle fonctionnalité | Modifier `main` directement -> risqué | Créer `feature/login` -> isolé |
| Bug urgent | Bloquer le développement | Créer `hotfix/critical-bug` |
| Expérimentation | Commits "WIP" dans `main` | `experiment/new-tech` -> supprimable |
| Travail d'équipe | Conflits permanents | Chacun sa branche |

---

### ÉTAPE 2 : Lister et créer des branches

**Lister les branches :**

```bash
git branch
```

**Résultat :**

```
* main
```

**`*` indique la branche actuelle (où est HEAD)**

---

**Lister toutes les branches (locales + distantes) :**

```bash
git branch -a
```

**Pour l'instant, seulement `main`.**

---

**Créer une branche :**

```bash
git branch feature/backend-api
```

**Explication :**

**`git branch <nom>`** = Crée une branche
- Ne bascule PAS vers cette branche
- Juste création du pointeur

**Nommage conventionnel :**

| Type | Format | Exemple |
|------|--------|---------|
| Fonctionnalité | `feature/<nom>` | `feature/user-auth` |
| Correction | `fix/<nom>` | `fix/login-bug` |
| Hotfix | `hotfix/<nom>` | `hotfix/security-patch` |
| Release | `release/<version>` | `release/v1.2.0` |
| Expérimentation | `experiment/<nom>` | `experiment/graphql` |

**Conventions :**
- Tout en minuscules
- Mots séparés par `-` (pas `_` ni espaces)
- Descriptif et court

---

**Vérifier que la branche existe :**

```bash
git branch
```

**Résultat :**

```
  feature/backend-api
* main
```

**`feature/backend-api` créée mais on est toujours sur `main`.**

---

**Créer ET basculer vers une branche :**

```bash
git checkout -b feature/frontend-ui
```

**Ou (Git 2.23+) :**

```bash
git switch -c feature/frontend-ui
```

**Explication :**

**`git checkout -b <nom>`** :
- `-b` = Create new branch
- Crée la branche ET bascule dessus

**`git switch -c <nom>`** :
- Commande moderne (plus claire)
- `-c` = Create
- Recommandé pour Git 2.23+

**Différence `checkout` vs `switch` :**

| Commande | Usage | Ancien/Nouveau |
|----------|-------|----------------|
| `checkout` | Basculer branches + restaurer fichiers | Ancien (confus) |
| `switch` | Basculer branches SEULEMENT | Nouveau (clair) |
| `restore` | Restaurer fichiers SEULEMENT | Nouveau (clair) |

---

**Vérifier la branche actuelle :**

```bash
git branch
```

**Résultat :**

```
  feature/backend-api
* feature/frontend-ui
  main
```

**`*` est sur `feature/frontend-ui` -> branche actuelle**

---

### ÉTAPE 3 : Travailler sur une branche

**Créer un fichier frontend :**

```bash
mkdir -p frontend/src/components
cat > frontend/src/App.jsx << 'EOF'
import React from 'react';

function App() {
  return (
    <div className="app">
      <h1>TaskMaster Pro</h1>
      <p>Task management for teams</p>
    </div>
  );
}

export default App;
EOF
```

---

**Ajouter et commiter :**

```bash
git add frontend/
git commit -m "feat(frontend): add initial React App component"
```

---

**Vérifier l'historique :**

```bash
git log --oneline --graph --all
```

**Résultat :**

```
* e5f6g7h (HEAD -> feature/frontend-ui) feat(frontend): add initial React App component
* d4e5f6g (main, feature/backend-api) chore: add comprehensive .gitignore
* c3d4e5f docs(readme): add architecture section
* b2c3d4e feat(backend): initialize backend structure
* a1b2c3d docs: initialize project with README
```

**Explication :**

**`(HEAD -> feature/frontend-ui)`** :
- `HEAD` pointe vers `feature/frontend-ui`
- `feature/frontend-ui` pointe vers le commit `e5f6g7h`

**`(main, feature/backend-api)`** :
- `main` et `feature/backend-api` pointent tous deux vers `d4e5f6g`
- Ils n'ont pas bougé depuis leur création

**Visualisation :**

```
feature/frontend-ui (HEAD)
         v
         [BLACK_CIRCLE] feat(frontend): add React App
        ╱
main ──[BLACK_CIRCLE]────[BLACK_CIRCLE] feature/backend-api
       │    │
       ...  └─ chore: add .gitignore
```

---

### ÉTAPE 4 : Fast-Forward Merge

**Retour sur `main` :**

```bash
git switch main
```

**Ou :**

```bash
git checkout main
```

---

**Vérifier qu'on est bien sur `main` :**

```bash
git branch
```

**Résultat :**

```
  feature/backend-api
  feature/frontend-ui
* main
```

---

**Fusionner `feature/frontend-ui` dans `main` :**

```bash
git merge feature/frontend-ui
```

**Résultat :**

```
Updating d4e5f6g..e5f6g7h
Fast-forward
 frontend/src/App.jsx | 12 ++++++++++++
 1 file changed, 12 insertions(+)
 create mode 100644 frontend/src/App.jsx
```

**Explication :**

**`Fast-forward`** = Avance rapide

**Quand est-ce possible ?**
- Quand la branche cible (`main`) n'a **aucun nouveau commit**
- Depuis la création de la branche source (`feature/frontend-ui`)

**Que fait Git ?**
- Déplace simplement le pointeur `main` vers le même commit que `feature/frontend-ui`
- **Aucun commit de merge créé**

**Avant le merge :**

```
feature/frontend-ui
         v
         [BLACK_CIRCLE] Commit F
        ╱
main ──[BLACK_CIRCLE]
```

**Après le merge (fast-forward) :**

```
feature/frontend-ui
         v
         [BLACK_CIRCLE] Commit F
         ^
       main
```

**`main` a simplement "avancé" jusqu'à Commit F.**

---

**Vérifier l'historique :**

```bash
git log --oneline --graph --all
```

**Résultat :**

```
* e5f6g7h (HEAD -> main, feature/frontend-ui) feat(frontend): add initial React App component
* d4e5f6g (feature/backend-api) chore: add comprehensive .gitignore
...
```

**`main` et `feature/frontend-ui` pointent vers le même commit.**

---

**Supprimer la branche `feature/frontend-ui` (plus utile) :**

```bash
git branch -d feature/frontend-ui
```

**Résultat :**

```
Deleted branch feature/frontend-ui (was e5f6g7h).
```

**Explication :**

**`-d`** = Delete (suppression sûre)
- Refuse de supprimer si la branche n'est pas fusionnée
- Protection contre la perte de commits

**`-D`** = Force delete
- Supprime même si non fusionnée
- **Dangereux** : risque de perdre du travail

---

### ÉTAPE 5 : 3-Way Merge (Merge avec commit)

**Situation :** On a 2 branches qui ont divergé.

**Créer une branche pour Bob (backend) :**

```bash
git switch -c feature/api-endpoints
```

---

**Ajouter des endpoints :**

```bash
cat > backend/src/routes.js << 'EOF'
// API Routes
// Author: Bob Smith

const express = require('express');
const router = express.Router();

// Tasks endpoints
router.get('/api/tasks', (req, res) => {
  res.json({ tasks: [] });
});

router.post('/api/tasks', (req, res) => {
  res.status(201).json({ message: 'Task created' });
});

module.exports = router;
EOF
```

---

**Commiter :**

```bash
git add backend/src/routes.js
git commit -m "feat(backend): add tasks API endpoints

- GET /api/tasks - List all tasks
- POST /api/tasks - Create new task"
```

---

**Maintenant, Charlie travaille sur le frontend (en parallèle) :**

**Retour sur `main` :**

```bash
git switch main
```

---

**Créer une branche pour Charlie :**

```bash
git switch -c feature/task-list-ui
```

---

**Ajouter un composant :**

```bash
cat > frontend/src/components/TaskList.jsx << 'EOF'
import React from 'react';

function TaskList({ tasks }) {
  return (
    <div className="task-list">
      <h2>My Tasks</h2>
      {tasks.length === 0 ? (
        <p>No tasks yet. Create one!</p>
      ) : (
        <ul>
          {tasks.map(task => (
            <li key={task.id}>{task.title}</li>
          ))}
        </ul>
      )}
    </div>
  );
}

export default TaskList;
EOF
```

---

**Commiter :**

```bash
git add frontend/src/components/TaskList.jsx
git commit -m "feat(frontend): add TaskList component

Displays list of tasks with empty state"
```

---

**Visualiser l'état actuel :**

```bash
git log --oneline --graph --all
```

**Résultat :**

```
* g7h8i9j (HEAD -> feature/task-list-ui) feat(frontend): add TaskList component
| * f6g7h8i (feature/api-endpoints) feat(backend): add tasks API endpoints
|/  
* e5f6g7h (main) feat(frontend): add initial React App component
* d4e5f6g (feature/backend-api) chore: add comprehensive .gitignore
...
```

**Visualisation :**

```
feature/task-list-ui (Charlie)
         [BLACK_CIRCLE] TaskList component
        ╱
main ──[BLACK_CIRCLE]
        ╲
         [BLACK_CIRCLE] API endpoints
      feature/api-endpoints (Bob)
```

**Les branches ont DIVERGÉ depuis `main`.**

---

**Fusionner la branche de Bob dans `main` :**

```bash
git switch main
git merge feature/api-endpoints
```

**Résultat :**

```
Updating e5f6g7h..f6g7h8i
Fast-forward
 backend/src/routes.js | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
 create mode 100644 backend/src/routes.js
```

**Fast-forward car `main` n'avait pas bougé.**

---

**Maintenant fusionner la branche de Charlie :**

```bash
git merge feature/task-list-ui
```

**Résultat :**

```
Merge made by the 'ort' strategy.
 frontend/src/components/TaskList.jsx | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)
 create mode 100644 frontend/src/components/TaskList.jsx
```

**Explication :**

**`Merge made by the 'ort' strategy`** = 3-way merge
- `ort` = Algorithme de merge de Git (Ostensibly Recursive's Twin)
- Anciennement `recursive`

**Pourquoi 3-way merge ?**

`main` a avancé (merge de Bob) **pendant** que Charlie travaillait sur `feature/task-list-ui`.

**Git doit réconcilier 3 commits :**
1. **Base commune** = Commit où les branches ont divergé
2. **Commit de `main`** = Endpoint API
3. **Commit de `feature/task-list-ui`** = TaskList

**Git crée un NOUVEAU commit de merge :**

```
         [BLACK_CIRCLE] TaskList (Charlie)
        ╱ ╲
main ──[BLACK_CIRCLE]───[BLACK_CIRCLE]─── (commit de merge)
        ╲ ╱
         [BLACK_CIRCLE] API endpoints (Bob)
```

---

**Un éditeur s'ouvre avec le message de merge par défaut :**

```
Merge branch 'feature/task-list-ui'

# Please enter a commit message to explain why this merge is necessary,
# especially if it merges an updated upstream into a topic branch.
#
# Lines starting with '#' will be ignored, and an empty message
# aborts the commit.
```

**Tu peux :**
- Garder le message par défaut
- Le personnaliser

**Message recommandé :**

```
Merge branch 'feature/task-list-ui' into main

Add TaskList component to display tasks in frontend
```

**Sauvegarder et fermer l'éditeur.**

---

**Vérifier l'historique :**

```bash
git log --oneline --graph --all
```

**Résultat :**

```
*   h8i9j0k (HEAD -> main) Merge branch 'feature/task-list-ui' into main
|\  
| * g7h8i9j (feature/task-list-ui) feat(frontend): add TaskList component
* | f6g7h8i (feature/api-endpoints) feat(backend): add tasks API endpoints
|/  
* e5f6g7h feat(frontend): add initial React App component
...
```

**Le graphe montre clairement le merge !**

**`*` = Commit**
**`|` = Ligne de branche**
**`\` `/` = Divergence/Convergence**

---

**Supprimer les branches fusionnées :**

```bash
git branch -d feature/api-endpoints
git branch -d feature/task-list-ui
```

---

### ÉTAPE 6 : Désactiver Fast-Forward (toujours créer un commit de merge)

**Pourquoi désactiver fast-forward ?**

En entreprise, on veut souvent un **historique explicite** montrant quand/quoi a été fusionné.

**Fast-forward :**
- Historique linéaire (propre)
- Mais perd l'information de branche

**3-way merge :**
- Historique non-linéaire (graphe)
- Mais préserve l'historique de branche

---

**Créer une branche :**

```bash
git switch -c feature/database-config
```

---

**Ajouter un fichier :**

```bash
cat > backend/config/database.js << 'EOF'
// Database Configuration
// Author: Bob Smith

module.exports = {
  development: {
    host: 'localhost',
    port: 5432,
    database: 'taskmaster_dev',
    username: 'postgres',
    password: 'dev_password'
  },
  production: {
    host: process.env.DB_HOST,
    port: process.env.DB_PORT,
    database: process.env.DB_NAME,
    username: process.env.DB_USER,
    password: process.env.DB_PASSWORD
  }
};
EOF
```

---

**Commiter :**

```bash
git add backend/config/
git commit -m "feat(backend): add database configuration

- Development and production configs
- Use environment variables in production"
```

---

**Fusionner SANS fast-forward :**

```bash
git switch main
git merge --no-ff feature/database-config -m "Merge branch 'feature/database-config'"
```

**Explication :**

**`--no-ff`** = No Fast-Forward
- Force la création d'un commit de merge
- Même si fast-forward est possible

**`-m "message"`** = Message de merge
- Évite l'ouverture de l'éditeur

---

**Résultat :**

```
Merge made by the 'ort' strategy.
 backend/config/database.js | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)
 create mode 100644 backend/config/database.js
```

**Un commit de merge a été créé même si fast-forward était possible.**

---

**Historique :**

```bash
git log --oneline --graph
```

**Résultat :**

```
*   i9j0k1l (HEAD -> main) Merge branch 'feature/database-config'
|\  
| * j0k1l2m (feature/database-config) feat(backend): add database configuration
|/  
*   h8i9j0k Merge branch 'feature/task-list-ui' into main
...
```

**Le commit de merge est visible !**

---

**Configurer `--no-ff` par défaut :**

```bash
git config --global merge.ff false
```

**Maintenant `git merge` créera toujours un commit de merge.**

**Pour forcer fast-forward :**

```bash
git merge --ff-only <branche>
```

---

### ÉTAPE 7 : Git Stash (sauvegarder le travail temporaire)

**Situation :** Tu travailles sur une fonctionnalité mais un bug urgent arrive.

**Créer une branche :**

```bash
git switch -c feature/user-profile
```

---

**Commencer à travailler :**

```bash
cat > frontend/src/components/UserProfile.jsx << 'EOF'
import React from 'react';

function UserProfile({ user }) {
  // TODO: Complete implementation
  return (
    <div className="user-profile">
      <h2>{user.name}</h2>
EOF
```

**Fichier incomplet (volontairement).**

---

**Vérifier le statut :**

```bash
git status
```

**Résultat :**

```
On branch feature/user-profile
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        frontend/src/components/UserProfile.jsx

nothing added to commit but untracked files present (use "git add" to track)
```

---

**PROBLÈME : Bug urgent ! Tu dois basculer sur `main`.**

**Mais tu ne peux pas commiter (le code est incomplet).**

**Solution : `git stash`**

```bash
# Ajouter le fichier au staging (obligatoire pour stash)
git add frontend/src/components/UserProfile.jsx

# Sauvegarder dans le stash
git stash
```

**Résultat :**

```
Saved working directory and index state WIP on feature/user-profile: i9j0k1l Merge branch 'feature/database-config'
```

**Explication :**

**`git stash`** = Sauvegarde temporaire
- Met de côté les changements (working directory + staging area)
- Retourne à un working directory propre
- Les changements sont dans une "pile" (stack)

**WIP** = Work In Progress

---

**Vérifier le statut :**

```bash
git status
```

**Résultat :**

```
On branch feature/user-profile
nothing to commit, working tree clean
```

**[OK] Working directory propre !**

**Le fichier `UserProfile.jsx` a disparu :**

```bash
ls frontend/src/components/
```

**Résultat :**

```
TaskList.jsx
```

**`UserProfile.jsx` n'est plus là (sauvegardé dans le stash).**

---

**Maintenant tu peux basculer vers `main` :**

```bash
git switch main
```

---

**Corriger le bug urgent :**

```bash
git switch -c hotfix/critical-bug

echo "// Emergency fix" >> backend/src/index.js

git add backend/src/index.js
git commit -m "fix(backend): resolve critical production bug"

git switch main
git merge --no-ff hotfix/critical-bug -m "Merge hotfix/critical-bug"
git branch -d hotfix/critical-bug
```

---

**Bug corrigé ! Retour au travail sur le profil utilisateur :**

```bash
git switch feature/user-profile
```

---

**Restaurer le travail sauvegardé :**

```bash
git stash pop
```

**Résultat :**

```
On branch feature/user-profile
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   frontend/src/components/UserProfile.jsx

Dropped refs/stash@{0} (a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0)
```

**Explication :**

**`git stash pop`** :
- **Applique** le stash le plus récent
- **Supprime** ce stash de la pile

**Alternative : `git stash apply`** :
- Applique le stash
- **Garde** le stash dans la pile (pour réutilisation)

---

**Le fichier est de retour :**

```bash
cat frontend/src/components/UserProfile.jsx
```

**Résultat :**

```javascript
import React from 'react';

function UserProfile({ user }) {
  // TODO: Complete implementation
  return (
    <div className="user-profile">
      <h2>{user.name}</h2>
```

---

**Terminer le composant :**

```bash
cat >> frontend/src/components/UserProfile.jsx << 'EOF'
      <p>{user.email}</p>
      <p>Member since: {user.joinDate}</p>
    </div>
  );
}

export default UserProfile;
EOF
```

---

**Commiter :**

```bash
git add frontend/src/components/UserProfile.jsx
git commit -m "feat(frontend): add UserProfile component

Displays user name, email and join date"
```

---

### ÉTAPE 8 : Gérer plusieurs stash

**Créer plusieurs stash :**

```bash
# Stash 1
echo "console.log('test');" > test1.js
git add test1.js
git stash save "Work on test feature 1"

# Stash 2
echo "console.log('test2');" > test2.js
git add test2.js
git stash save "Work on test feature 2"

# Stash 3
echo "console.log('test3');" > test3.js
git add test3.js
git stash save "Work on test feature 3"
```

---

**Lister les stash :**

```bash
git stash list
```

**Résultat :**

```
stash@{0}: On feature/user-profile: Work on test feature 3
stash@{1}: On feature/user-profile: Work on test feature 2
stash@{2}: On feature/user-profile: Work on test feature 1
```

**Format : `stash@{N}` où N=0 est le plus récent.**

---

**Voir le contenu d'un stash :**

```bash
git stash show stash@{1}
```

**Ou avec diff :**

```bash
git stash show -p stash@{1}
```

---

**Appliquer un stash spécifique :**

```bash
git stash apply stash@{1}
```

**Applique le stash 1 (pas le plus récent).**

---

**Supprimer un stash spécifique :**

```bash
git stash drop stash@{0}
```

---

**Supprimer tous les stash :**

```bash
git stash clear
```

**[ATTENTION] Irréversible !**

---

**Nettoyer (pour cet exercice) :**

```bash
git stash clear
rm -f test*.js
git status  # Doit être propre
```

---

### ÉTAPE 9 : Renommer et supprimer des branches

**Renommer la branche actuelle :**

```bash
git branch -m feature/user-profile feature/profile-component
```

**Explication :**

**`-m`** = Move (renommer)
- Renomme la branche actuelle
- Si tu n'es pas sur la branche : `git branch -m old-name new-name`

---

**Supprimer une branche locale :**

```bash
git branch -d feature/database-config
```

**Si la branche n'est pas fusionnée, Git refuse :**

```
error: The branch 'feature/database-config' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature/database-config'.
```

**Forcer la suppression :**

```bash
git branch -D feature/database-config
```

**[ATTENTION] Perte définitive des commits non fusionnés !**

---

### ÉTAPE 10 : Visualisation avancée de l'arbre

**Graphe coloré avec détails :**

```bash
git log --graph --oneline --all --decorate
```

**Alias recommandé :**

```bash
git config --global alias.tree "log --graph --oneline --all --decorate --color"
```

**Utilisation :**

```bash
git tree
```

---

**Avec dates :**

```bash
git log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --all
```

**Alias :**

```bash
git config --global alias.lg "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --all"
```

**Utilisation :**

```bash
git lg
```

**Résultat :**

```
* k1l2m3n - (HEAD -> feature/profile-component) feat(frontend): add UserProfile component (2 minutes ago) <Alice Johnson>
*   j0k1l2m - (main) Merge hotfix/critical-bug (5 minutes ago) <Alice Johnson>
|\  
| * i9j0k1l - fix(backend): resolve critical production bug (6 minutes ago) <Alice Johnson>
|/  
...
```

---

**Outil externe : `gitk` (interface graphique) :**

```bash
gitk --all
```

**Ou `git gui` :**

```bash
git gui
```

---

### [OK] TESTS DE VALIDATION

**1. Création et navigation entre branches**

```bash
# Créer une branche
git branch test-branch

# Lister
git branch | grep test-branch

# Basculer
git switch test-branch

# Vérifier
git branch | grep "* test-branch"

# Retour
git switch main

# Supprimer
git branch -d test-branch
```

- [ ] Toutes les commandes passent

---

**2. Fast-forward merge**

```bash
# Créer branche
git switch -c ff-test

# Ajouter commit
echo "test" > ff-test.txt
git add ff-test.txt
git commit -m "test: ff merge"

# Merger
git switch main
git merge ff-test

# Vérifier (doit afficher "Fast-forward")
git log --oneline | head -1 | grep "test: ff merge"
```

- [ ] Fast-forward réussi

---

**3. 3-way merge**

```bash
# Créer 2 branches divergentes
git switch -c branch-a
echo "A" > file-a.txt
git add file-a.txt
git commit -m "Add file A"

git switch main
git switch -c branch-b
echo "B" > file-b.txt
git add file-b.txt
git commit -m "Add file B"

# Merger branch-a dans main
git switch main
git merge branch-a

# Merger branch-b (3-way)
git merge branch-b
```

- [ ] Commit de merge créé

---

**4. Stash**

```bash
# Modifier un fichier
echo "changes" >> README.md

# Sauvegarder
git stash

# Vérifier que les changements ont disparu
git status | grep "nothing to commit"

# Restaurer
git stash pop

# Vérifier que les changements sont revenus
git diff README.md
```

- [ ] Stash fonctionne

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : "Please commit your changes or stash them before you switch branches"

**Symptôme :**

```
error: Your local changes to the following files would be overwritten by checkout:
        README.md
Please commit your changes or stash them before you switch branches.
Aborting
```

**Cause :** Changements non commités qui entreraient en conflit

**Solutions :**

```bash
# Option 1 : Commiter
git add .
git commit -m "WIP: save current work"

# Option 2 : Stash
git stash

# Option 3 : Forcer ([ATTENTION] perd les changements)
git checkout -f autre-branche
```

---

#### Erreur 2 : "Cannot delete branch - not fully merged"

**Symptôme :**

```
error: The branch 'feature-x' is not fully merged.
```

**Cause :** La branche contient des commits non présents dans la branche actuelle

**Solutions :**

```bash
# Vérifier si vraiment fusionnée
git branch --merged

# Si sûr de vouloir supprimer
git branch -D feature-x
```

---

#### Erreur 3 : Conflit lors d'un merge

**Symptôme :**

```
Auto-merging file.txt
CONFLICT (content): Merge conflict in file.txt
Automatic merge failed; fix conflicts and then commit the result.
```

**Solution (détaillée dans Exercice 4) :**

```bash
# 1. Identifier les conflits
git status

# 2. Éditer les fichiers en conflit
# Chercher les marqueurs <<<<<<<, =======, >>>>>>>

# 3. Résoudre manuellement

# 4. Marquer comme résolu
git add file.txt

# 5. Terminer le merge
git commit
```

---

#### Erreur 4 : Branche inexistante

**Symptôme :**

```
error: pathspec 'non-existent' did not match any file(s) known to git
```

**Cause :** Branche n'existe pas

**Vérifier les branches :**

```bash
git branch -a
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Branches**
- Pointeurs légers vers des commits
- Permettent le travail en parallèle
- Nommage conventionnel : `type/description`

**2. Fast-Forward vs 3-Way Merge**
- **Fast-forward** : Historique linéaire, pas de commit de merge
- **3-way** : Historique non-linéaire, commit de merge créé
- `--no-ff` : Force 3-way même si FF possible

**3. Git Stash**
- Sauvegarde temporaire du travail en cours
- `git stash` : Sauvegarder
- `git stash pop` : Restaurer et supprimer
- `git stash apply` : Restaurer et garder

**4. Workflow branches**
```
Créer branche -> Travailler -> Commiter -> Merger -> Supprimer branche
```

**5. Commandes essentielles**
```bash
git branch          # Lister
git branch <nom>    # Créer
git switch <nom>    # Basculer
git merge <nom>     # Fusionner
git branch -d <nom> # Supprimer
```

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Stratégies de merge**

```bash
# Stratégie récursive (par défaut)
git merge -s recursive feature

# Stratégie ours (garder tout de notre côté)
git merge -s ours feature

# Stratégie theirs (via recursive)
git merge -X theirs feature
```

---

**2. Merge sans commit**

```bash
git merge --no-commit feature
# Inspecter les changements
git status
# Si OK, commiter
git commit
# Sinon, annuler
git merge --abort
```

---

**3. Squash merge (fusionner en 1 commit)**

```bash
git merge --squash feature
git commit -m "feat: add feature X (squashed)"
```

**Résultat :** Tous les commits de `feature` sont fusionnés en 1 seul commit.

**Quand l'utiliser ?**
- Historique de branche chaotique (commits WIP)
- Veux un historique principal propre

---

**4. Rerere (Reuse Recorded Resolution)**

**Active la mémorisation des résolutions de conflits :**

```bash
git config --global rerere.enabled true
```

**Si tu résous un conflit, Git se souviendra de ta résolution.**

**La prochaine fois que le même conflit arrive, Git le résout automatiquement !**

---

**5. Branches orphelines**

**Créer une branche sans historique :**

```bash
git checkout --orphan gh-pages
git rm -rf .
echo "<h1>GitHub Pages</h1>" > index.html
git add index.html
git commit -m "Initial GitHub Pages commit"
```

**Usage :** GitHub Pages, documentation distincte

---

## [COURS] CONCLUSION DE L'EXERCICE 2

**[OK] Félicitations ! Tu maîtrises les branches et les merges !**

**Ce que tu as appris :**
- Créer et gérer des branches
- Fast-forward vs 3-way merge
- Git stash pour le travail temporaire
- Visualiser l'arbre de commits
- Conventions de nommage professionnelles

**Compétences acquises :**
- [OK] Workflow de branches (niveau professionnel)
- [OK] Merge strategies
- [OK] Git stash (sauvegardes temporaires)
- [OK] Navigation dans l'historique
- [OK] Visualisation graphique

**Temps moyen de réalisation :** 2-3 heures

**Prochaine étape :** Exercice 3 - Collaboration GitHub (remote, push, pull, PRs) ! [WEB]

---

(Le document continuerait avec les exercices 3 à 10. Veux-tu que je continue immédiatement avec l'Exercice 3, ou préfères-tu que je génère tout d'un coup dans un fichier complet ?)

# [JAUNE] EXERCICE 3 : COLLABORATION GITHUB

## [LISTE] ÉNONCÉ

### Contexte professionnel

L'équipe TaskMaster Pro a besoin de **centraliser le code** et **collaborer efficacement**. **Alice** (Lead Developer) décide d'utiliser GitHub comme plateforme de collaboration.

**Aujourd'hui, l'équipe va :**
1. Créer un dépôt GitHub pour TaskMaster Pro
2. Connecter les dépôts locaux au dépôt distant (remote)
3. Pousser le code vers GitHub (push)
4. Cloner le projet sur différentes machines
5. Synchroniser le travail entre développeurs (pull/fetch)
6. Découvrir les Pull Requests

**Équipe active :**
- [PERSONNE][CODE] **Alice** - Initialise le dépôt GitHub et configure l'équipe
- [PERSONNE][CODE] **Bob** - Clone le projet et ajoute des fonctionnalités backend
- [PERSONNE][CODE] **Charlie** - Travaille sur le frontend en parallèle
- [PERSONNE][CODE] **Diana** - Ajoute la configuration Docker
- [PERSONNE][CODE] **Emma** - Contribue aux tests

### Objectifs

- Créer et configurer un dépôt GitHub
- Comprendre les remotes (origin, upstream)
- Maîtriser push, pull, fetch
- Cloner un dépôt existant
- Gérer les collaborateurs
- Créer une première Pull Request

### Livrables

- Dépôt GitHub public ou privé créé
- Code local poussé vers GitHub
- Au moins 3 développeurs ont cloné le projet
- Plusieurs push/pull effectués
- Une Pull Request créée et mergée

### Contraintes

- Repository GitHub : `taskmaster-pro`
- Branches protégées (main ne peut pas être pushée directement)
- Tous les développeurs doivent utiliser des branches
- Messages de commit professionnels
- Temps estimé : 3-4 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :

- [OK] Créer un dépôt GitHub
- [OK] Configurer les remotes (origin, upstream)
- [OK] Pousser du code vers GitHub (git push)
- [OK] Cloner un dépôt distant (git clone)
- [OK] Synchroniser avec le dépôt distant (git pull, git fetch)
- [OK] Gérer les collaborateurs
- [OK] Créer et merger des Pull Requests
- [OK] Comprendre la différence entre fork et clone
- [OK] Résoudre les erreurs de push courantes

---

## [DOCS] PRÉREQUIS

- Exercices 1 et 2 terminés (dépôt local avec historique)
- Compte GitHub créé (github.com)
- SSH ou HTTPS configuré pour GitHub
- Compréhension des branches

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Créer un compte GitHub (si pas déjà fait)

**Aller sur :** https://github.com

**Créer un compte :**
1. Cliquer sur "Sign up"
2. Entrer email, mot de passe, username
3. Vérifier l'email
4. Choisir le plan gratuit

**[OK] Compte créé !**

---

### ÉTAPE 2 : Configurer l'authentification SSH

**Pourquoi SSH plutôt que HTTPS ?**

| Méthode | Avantages | Inconvénients |
|---------|-----------|---------------|
| **SSH** | [OK] Pas de mot de passe à chaque push<br>[OK] Plus sécurisé<br>[OK] Recommandé en entreprise | [X] Configuration initiale |
| **HTTPS** | [OK] Simple à configurer<br>[OK] Fonctionne partout | [X] Demande credentials à chaque push<br>[X] Personal Access Token requis |

**On va utiliser SSH.**

---

**Vérifier si une clé SSH existe déjà :**

```bash
ls -la ~/.ssh
```

**Si tu vois `id_rsa` ou `id_ed25519`, tu as déjà une clé.**

**Sinon, générer une nouvelle clé :**

```bash
ssh-keygen -t ed25519 -C "alice@taskmaster.com"
```

**Explication :**

**`ssh-keygen`** = Générateur de clés SSH

**`-t ed25519`** = Type de clé
- `ed25519` = Algorithme moderne, rapide, sécurisé (recommandé)
- Alternative : `rsa` (ancien, compatible)

**`-C "email"`** = Commentaire
- Identifie la clé
- Généralement ton email

---

**Résultat :**

```
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/alice/.ssh/id_ed25519):
```

**Appuie sur Entrée (accepte le chemin par défaut).**

---

```
Enter passphrase (empty for no passphrase):
```

**Tu peux :**
- **Entrer une passphrase** (mot de passe pour la clé) -> Plus sécurisé
- **Laisser vide** (pas de passphrase) -> Plus pratique

**Pour cet exercice, laisse vide (Entrée).**

---

**Résultat :**

```
Your identification has been saved in /home/alice/.ssh/id_ed25519
Your public key has been saved in /home/alice/.ssh/id_ed25519.pub
The key fingerprint is:
SHA256:abcd1234efgh5678ijkl9012mnop3456qrst7890uvwx alice@taskmaster.com
```

**[OK] Clé SSH créée !**

---

**Démarrer l'agent SSH :**

```bash
eval "$(ssh-agent -s)"
```

**Résultat :**

```
Agent pid 12345
```

---

**Ajouter la clé à l'agent :**

```bash
ssh-add ~/.ssh/id_ed25519
```

**Résultat :**

```
Identity added: /home/alice/.ssh/id_ed25519 (alice@taskmaster.com)
```

---

**Copier la clé publique :**

```bash
cat ~/.ssh/id_ed25519.pub
```

**Résultat (exemple) :**

```
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAbCdEfGhIjKlMnOpQrStUvWxYz alice@taskmaster.com
```

**Sélectionne et copie toute cette ligne.**

---

**Ajouter la clé à GitHub :**

1. Aller sur https://github.com/settings/keys
2. Cliquer "New SSH key"
3. **Title** : "Laptop Alice" (ou nom descriptif)
4. **Key type** : Authentication key
5. **Key** : Coller la clé publique
6. Cliquer "Add SSH key"

**[OK] Clé SSH ajoutée à GitHub !**

---

**Tester la connexion :**

```bash
ssh -T git@github.com
```

**Première fois, demande confirmation :**

```
The authenticity of host 'github.com (140.82.121.4)' can't be established.
ED25519 key fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
```

**Taper `yes` et Entrée.**

---

**Résultat :**

```
Hi alice! You've successfully authenticated, but GitHub does not provide shell access.
```

**[OK] SSH configuré et fonctionnel !**

---

### ÉTAPE 3 : Créer un dépôt GitHub

**Aller sur GitHub :**

1. Cliquer le `+` en haut à droite
2. "New repository"

**Remplir le formulaire :**

**Repository name** : `taskmaster-pro`

**Description** : `Enterprise-grade task management application`

**Visibility** :
- **Public** : Visible par tous (open source)
- **Private** : Visible seulement par toi et les collaborateurs

**Choisis Public pour cet exercice.**

**Initialize repository :**
- [X] **NE PAS** cocher "Add a README file"
- [X] **NE PAS** ajouter .gitignore
- [X] **NE PAS** choisir de license

**Pourquoi ?**
On a déjà un dépôt local avec historique. Si on initialise le dépôt GitHub, il aura un commit initial qui va créer un conflit.

**Cliquer "Create repository".**

---

**GitHub affiche les instructions :**

```
Quick setup — if you've done this kind of thing before

SSH: git@github.com:alice/taskmaster-pro.git
HTTPS: https://github.com/alice/taskmaster-pro.git

…or create a new repository on the command line

…or push an existing repository from the command line
git remote add origin git@github.com:alice/taskmaster-pro.git
git branch -M main
git push -u origin main
```

**On va suivre "push an existing repository".**

---

### ÉTAPE 4 : Connecter le dépôt local à GitHub

**Dans ton terminal (dépôt local taskmaster-pro) :**

**Ajouter le remote "origin" :**

```bash
git remote add origin git@github.com:alice/taskmaster-pro.git
```

**[ATTENTION] Remplace `alice` par ton username GitHub !**

**Explication :**

**`git remote add`** = Ajoute un dépôt distant

**`origin`** = Nom conventionnel du remote principal
- C'est une convention, pas une obligation
- Tu pourrais l'appeler "github" ou autre
- Mais **toujours** utiliser "origin" en pratique

**`git@github.com:alice/taskmaster-pro.git`** = URL SSH du dépôt
- Format SSH : `git@github.com:user/repo.git`
- Format HTTPS : `https://github.com/user/repo.git`

**Qu'est-ce qu'un "remote" ?**

Un **remote** est un alias vers un dépôt distant (sur GitHub, GitLab, serveur interne, etc.).

**Analogie :**
- Remote = Contact dans ton téléphone
- URL = Numéro de téléphone
- "origin" = "Maman" (nom facile à retenir)

---

**Vérifier les remotes :**

```bash
git remote -v
```

**Résultat :**

```
origin  git@github.com:alice/taskmaster-pro.git (fetch)
origin  git@github.com:alice/taskmaster-pro.git (push)
```

**Explication :**

**`-v`** = Verbose (affiche les URL)

**`(fetch)`** = URL pour récupérer (pull/fetch)
**`(push)`** = URL pour pousser (push)

**Généralement identiques, mais peuvent être différentes dans certains cas (fork, mirrors).**

---

**Renommer la branche en `main` (si elle s'appelle `master`) :**

```bash
git branch -M main
```

**Explication :**

**`-M`** = Move (force)
- Renomme la branche actuelle
- Même si `main` existe déjà (écrase)

**Pourquoi `main` au lieu de `master` ?**
- Inclusivité (éviter terminologie esclavagiste)
- GitHub utilise `main` par défaut depuis 2020
- Convention moderne

---

### ÉTAPE 5 : Pousser le code vers GitHub (premier push)

```bash
git push -u origin main
```

**Résultat :**

```
Enumerating objects: 42, done.
Counting objects: 100% (42/42), done.
Delta compression using up to 8 threads
Compressing objects: 100% (35/35), done.
Writing objects: 100% (42/42), 8.24 KiB | 2.06 MiB/s, done.
Total 42 (delta 12), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (12/12), done.
To github.com:alice/taskmaster-pro.git
 * [new branch]      main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.
```

**Explication détaillée :**

**`git push`** = Pousser les commits vers le remote

**`-u origin main`** :
- `-u` = `--set-upstream` (configure le tracking)
- `origin` = Remote cible
- `main` = Branche locale à pousser

**Que fait `-u` (--set-upstream) ?**

Configure la branche locale pour "suivre" (track) la branche distante.

**Avant `-u` :**
- `git push` -> Erreur "no upstream branch"
- Tu dois spécifier : `git push origin main`

**Après `-u` :**
- `git push` -> Pousse automatiquement vers `origin/main`
- Plus besoin de spécifier remote/branche

**À faire seulement au PREMIER push d'une branche.**

---

**`Enumerating objects: 42`** :
Git compte tous les objets à envoyer (commits, arbres, blobs)

**`Delta compression`** :
Git compresse les données pour réduire la taille du transfert

**`Writing objects: 100% (42/42)`** :
Envoi des objets vers GitHub

**`[new branch] main -> main`** :
- Branche `main` locale créée sur le remote
- `main` (local) -> `main` (distant)

**`Branch 'main' set up to track remote branch 'main'`** :
Le tracking est configuré (grâce à `-u`)

---

**Vérifier sur GitHub :**

Aller sur https://github.com/alice/taskmaster-pro

**Tu devrais voir :**
- Tous tes fichiers
- L'historique des commits
- Le README affiché

**[BRAVO] Code poussé sur GitHub ! [BRAVO]**

---

### ÉTAPE 6 : Comprendre origin/main (remote tracking branch)

**Lister toutes les branches (locales + distantes) :**

```bash
git branch -a
```

**Résultat :**

```
  feature/profile-component
* main
  remotes/origin/main
```

**Explication :**

**Branches locales :**
- `feature/profile-component`
- `main`

**Branches distantes (remote tracking branches) :**
- `remotes/origin/main`

**`remotes/origin/main`** est une **copie locale** de la branche `main` sur GitHub.

**Pourquoi "copie locale" ?**

Git garde une copie locale de l'état du dépôt distant pour pouvoir travailler offline.

**`origin/main` est mis à jour seulement lors de :**
- `git fetch` (récupère les changements)
- `git pull` (fetch + merge)
- `git push` (après avoir poussé)

---

**Voir les branches trackées :**

```bash
git branch -vv
```

**Résultat :**

```
  feature/profile-component k1l2m3n feat(frontend): add UserProfile component
* main                      k1l2m3n [origin/main] Merge hotfix/critical-bug
```

**`[origin/main]`** indique que `main` locale track `origin/main`.

---

### ÉTAPE 7 : Bob clone le projet

**Simulation : Bob est sur une autre machine.**

**Créer un nouveau dossier (simule une autre machine) :**

```bash
cd ~
mkdir bob-workspace
cd bob-workspace
```

---

**Cloner le dépôt :**

```bash
git clone git@github.com:alice/taskmaster-pro.git
```

**[ATTENTION] Remplace `alice` par ton username !**

**Résultat :**

```
Cloning into 'taskmaster-pro'...
remote: Enumerating objects: 42, done.
remote: Counting objects: 100% (42/42), done.
remote: Compressing objects: 100% (23/23), done.
remote: Total 42 (delta 12), reused 42 (delta 12), pack-reused 0
Receiving objects: 100% (42/42), 8.24 KiB | 4.12 MiB/s, done.
Resolving deltas: 100% (12/12), done.
```

**Explication :**

**`git clone <url>`** :
- Télécharge le dépôt complet
- Crée un dossier avec le nom du repo
- Configure automatiquement `origin`
- Checkout la branche par défaut (`main`)

---

**Entrer dans le dossier :**

```bash
cd taskmaster-pro
```

---

**Vérifier les remotes :**

```bash
git remote -v
```

**Résultat :**

```
origin  git@github.com:alice/taskmaster-pro.git (fetch)
origin  git@github.com:alice/taskmaster-pro.git (push)
```

**`origin` configuré automatiquement !**

---

**Vérifier l'historique :**

```bash
git log --oneline
```

**Même historique que le dépôt d'Alice.**

---

**Configurer l'identité de Bob :**

```bash
git config user.name "Bob Smith"
git config user.email "bob@taskmaster.com"
```

**[ATTENTION] Sans `--global` -> Configuration locale à ce dépôt seulement.**

---

### ÉTAPE 8 : Bob ajoute une fonctionnalité

**Créer une branche :**

```bash
git switch -c feature/task-api
```

---

**Ajouter une route API :**

```bash
cat > backend/src/api/tasks.js << 'EOF'
// Tasks API
// Author: Bob Smith

const express = require('express');
const router = express.Router();

// GET /api/tasks - Get all tasks
router.get('/', async (req, res) => {
  try {
    // TODO: Fetch from database
    const tasks = [
      { id: 1, title: 'Setup project', status: 'done' },
      { id: 2, title: 'Create API', status: 'in-progress' }
    ];
    res.json({ tasks });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

// POST /api/tasks - Create a new task
router.post('/', async (req, res) => {
  try {
    const { title, description } = req.body;
    
    // TODO: Save to database
    const newTask = {
      id: Date.now(),
      title,
      description,
      status: 'todo',
      createdAt: new Date()
    };
    
    res.status(201).json({ task: newTask });
  } catch (error) {
    res.status(500).json({ error: error.message });
  }
});

module.exports = router;
EOF
```

---

**Commiter :**

```bash
git add backend/src/api/
git commit -m "feat(api): add tasks endpoints

- GET /api/tasks - List all tasks
- POST /api/tasks - Create new task
- Includes error handling"
```

---

**Pousser la branche vers GitHub :**

```bash
git push -u origin feature/task-api
```

**Résultat :**

```
To github.com:alice/taskmaster-pro.git
 * [new branch]      feature/task-api -> feature/task-api
Branch 'feature/task-api' set up to track remote branch 'feature/task-api' from 'origin'.
```

**[OK] Branche poussée sur GitHub !**

---

**Vérifier sur GitHub :**

https://github.com/alice/taskmaster-pro/branches

**Tu devrais voir :**
- `main`
- `feature/task-api`

---

### ÉTAPE 9 : Alice récupère les changements de Bob

**Retour à l'espace de travail d'Alice :**

```bash
cd ~/taskmaster-pro
```

---

**Récupérer les changements depuis GitHub :**

```bash
git fetch origin
```

**Résultat :**

```
From github.com:alice/taskmaster-pro
 * [new branch]      feature/task-api -> origin/feature/task-api
```

**Explication :**

**`git fetch`** :
- Télécharge les changements du remote
- Met à jour les remote tracking branches (`origin/...`)
- **NE modifie PAS** les branches locales

**Différence fetch vs pull :**

| Commande | Action | Sécurité |
|----------|--------|----------|
| `git fetch` | Télécharge SEULEMENT | [OK] Sûr (ne touche pas tes fichiers) |
| `git pull` | Télécharge + Merge | [ATTENTION] Peut créer des conflits |

**`git pull` = `git fetch` + `git merge`**

---

**Voir les branches distantes :**

```bash
git branch -r
```

**Résultat :**

```
  origin/feature/task-api
  origin/main
```

---

**Voir la branche de Bob sans la checkout :**

```bash
git log origin/feature/task-api --oneline
```

**Résultat :**

```
m3n4o5p feat(api): add tasks endpoints
k1l2m3n Merge hotfix/critical-bug
...
```

---

**Créer une branche locale à partir de la branche distante :**

```bash
git switch feature/task-api
```

**Git est intelligent ! Il détecte automatiquement :**

```
Branch 'feature/task-api' set up to track remote branch 'feature/task-api' from 'origin'.
Switched to a new branch 'feature/task-api'
```

**Équivalent à :**

```bash
git switch -c feature/task-api origin/feature/task-api
```

---

**Vérifier le fichier de Bob :**

```bash
cat backend/src/api/tasks.js
```

**Le code de Bob est là !**

---

### ÉTAPE 10 : Charlie travaille en parallèle

**Simulation : Charlie clone le projet (nouvelle machine) :**

```bash
cd ~
mkdir charlie-workspace
cd charlie-workspace

git clone git@github.com:alice/taskmaster-pro.git
cd taskmaster-pro

git config user.name "Charlie Brown"
git config user.email "charlie@taskmaster.com"
```

---

**Créer une branche :**

```bash
git switch -c feature/task-form
```

---

**Ajouter un composant :**

```bash
cat > frontend/src/components/TaskForm.jsx << 'EOF'
import React, { useState } from 'react';

function TaskForm({ onSubmit }) {
  const [title, setTitle] = useState('');
  const [description, setDescription] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    
    if (!title.trim()) {
      alert('Title is required');
      return;
    }

    onSubmit({ title, description });
    setTitle('');
    setDescription('');
  };

  return (
    <form onSubmit={handleSubmit} className="task-form">
      <h2>Create New Task</h2>
      
      <div className="form-group">
        <label htmlFor="title">Title *</label>
        <input
          id="title"
          type="text"
          value={title}
          onChange={(e) => setTitle(e.target.value)}
          placeholder="Enter task title"
          required
        />
      </div>

      <div className="form-group">
        <label htmlFor="description">Description</label>
        <textarea
          id="description"
          value={description}
          onChange={(e) => setDescription(e.target.value)}
          placeholder="Enter task description (optional)"
          rows="4"
        />
      </div>

      <button type="submit">Create Task</button>
    </form>
  );
}

export default TaskForm;
EOF
```

---

**Commiter :**

```bash
git add frontend/src/components/TaskForm.jsx
git commit -m "feat(frontend): add TaskForm component

- Controlled form with title and description
- Client-side validation
- Clears form after submission"
```

---

**Pousser :**

```bash
git push -u origin feature/task-form
```

---

### ÉTAPE 11 : Comprendre git pull

**Alice veut récupérer les changements de Charlie.**

**Retour à l'espace d'Alice :**

```bash
cd ~/taskmaster-pro
git switch main
```

---

**Méthode 1 : fetch + merge (manuel) :**

```bash
# 1. Récupérer les changements
git fetch origin

# 2. Voir ce qui a changé
git log origin/main..main  # Commits dans main mais pas dans origin/main
git log main..origin/main  # Commits dans origin/main mais pas dans main

# 3. Merger
git merge origin/main
```

---

**Méthode 2 : pull (automatique) :**

```bash
git pull origin main
```

**`git pull` = `git fetch` + `git merge`**

---

**Résultat (si pas de nouveaux commits sur `main`) :**

```
Already up to date.
```

**Normal, Charlie a poussé sur `feature/task-form`, pas sur `main`.**

---

### ÉTAPE 12 : Créer une Pull Request (PR)

**Qu'est-ce qu'une Pull Request ?**

Une **Pull Request** (PR) est une **demande de fusion** de code.

**Workflow :**
```
1. Développeur crée une branche
2. Développeur pousse la branche sur GitHub
3. Développeur ouvre une Pull Request
4. Équipe revoit le code (code review)
5. Approbation -> Merge dans main
```

**Pourquoi utiliser des PRs ?**
- [OK] Revue de code (qualité)
- [OK] Discussion sur les changements
- [OK] Tests automatiques (CI/CD)
- [OK] Traçabilité (qui a approuvé quoi)
- [OK] Protection de la branche principale

---

**Bob ouvre une PR pour `feature/task-api` :**

1. Aller sur https://github.com/alice/taskmaster-pro
2. Cliquer sur "Pull requests"
3. Cliquer "New pull request"
4. **Base** : `main` (branche cible)
5. **Compare** : `feature/task-api` (branche source)
6. Cliquer "Create pull request"

**Remplir le formulaire :**

**Title** : `feat(api): add tasks endpoints`

**Description** :

```markdown
## Description

Add REST API endpoints for tasks management:
- `GET /api/tasks` - List all tasks
- `POST /api/tasks` - Create new task

## Changes

- Created `backend/src/api/tasks.js`
- Implemented error handling
- Added TODO comments for database integration

## Testing

Manual testing:
```bash
curl http://localhost:3000/api/tasks
```

## Checklist

- [x] Code follows style guidelines
- [x] Self-review completed
- [ ] Database integration (future work)
- [ ] Unit tests (future work)
```

**Cliquer "Create pull request".**

---

**GitHub affiche :**
- Les commits de la branche
- Les fichiers modifiés (diff)
- Possibilité de commenter
- Bouton "Merge pull request" (si autorisé)

---

**Alice revoit la PR :**

1. Aller dans l'onglet "Files changed"
2. Voir le diff du code
3. Ajouter des commentaires (hover sur une ligne -> `+`)

**Exemple de commentaire :**

```
Line 12: Consider adding input validation for task title length.
```

**Bob peut répondre :**

```
Good point! I'll add that in the next commit.
```

---

**Bob met à jour sa branche (suite aux commentaires) :**

```bash
cd ~/bob-workspace/taskmaster-pro
git switch feature/task-api

# Modifier le code
nano backend/src/api/tasks.js

# Commiter
git add backend/src/api/tasks.js
git commit -m "fix(api): add title length validation"

# Pousser
git push
```

**La PR est automatiquement mise à jour avec le nouveau commit !**

---

**Alice approuve la PR :**

1. Onglet "Files changed"
2. Cliquer "Review changes"
3. Sélectionner "Approve"
4. Cliquer "Submit review"

---

**Alice merge la PR :**

1. Retour à l'onglet "Conversation"
2. Cliquer "Merge pull request"
3. Choisir le type de merge :
   - **Create a merge commit** (par défaut) -> 3-way merge
   - **Squash and merge** -> Tous les commits en 1
   - **Rebase and merge** -> Rebase puis fast-forward
4. Cliquer "Confirm merge"

**Résultat :**

```
Pull request successfully merged and closed
```

---

**Supprimer la branche (optionnel mais recommandé) :**

GitHub propose : "Delete branch"

Cliquer "Delete branch"

**La branche est supprimée sur GitHub (pas localement).**

---

### ÉTAPE 13 : Synchroniser après un merge

**Alice met à jour son dépôt local :**

```bash
cd ~/taskmaster-pro
git switch main
git pull origin main
```

**Résultat :**

```
From github.com:alice/taskmaster-pro
   k1l2m3n..n4o5p6q  main       -> origin/main
Updating k1l2m3n..n4o5p6q
Fast-forward
 backend/src/api/tasks.js | 42 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)
 create mode 100644 backend/src/api/tasks.js
```

**Le code de Bob est maintenant dans `main` local !**

---

**Supprimer la branche locale (elle a été mergée) :**

```bash
git branch -d feature/task-api
```

**Résultat :**

```
Deleted branch feature/task-api (was m3n4o5p).
```

---

**Bob synchronise aussi :**

```bash
cd ~/bob-workspace/taskmaster-pro
git switch main
git pull origin main
git branch -d feature/task-api
```

---

**Charlie synchronise :**

```bash
cd ~/charlie-workspace/taskmaster-pro
git switch main
git pull origin main
```

**Maintenant tout le monde a le code de Bob dans `main` !**

---

### ÉTAPE 14 : Gérer les collaborateurs

**Ajouter Bob, Charlie, Diana et Emma comme collaborateurs :**

1. Aller sur https://github.com/alice/taskmaster-pro/settings/access
2. Cliquer "Invite a collaborator"
3. Entrer le username GitHub de Bob
4. Cliquer "Add to this repository"

**Répéter pour Charlie, Diana, Emma.**

---

**Rôles disponibles :**

| Rôle | Permissions |
|------|-------------|
| **Read** | Cloner, voir le code |
| **Triage** | Gérer issues/PRs (pas de push) |
| **Write** | Push, merge PRs |
| **Maintain** | Gérer settings (sans supprimer) |
| **Admin** | Tout (y compris supprimer) |

**Pour cet exercice, donne "Write" à tout le monde.**

---

### ÉTAPE 15 : Protéger la branche main

**Pourquoi protéger `main` ?**

- [X] Empêche les push directs (force les PRs)
- [OK] Oblige la revue de code
- [OK] Exige des tests passants
- [OK] Prévient la suppression accidentelle

---

**Configurer :**

1. Aller sur https://github.com/alice/taskmaster-pro/settings/branches
2. Cliquer "Add branch protection rule"
3. **Branch name pattern** : `main`
4. Activer :
   - [OK] **Require a pull request before merging**
   - [OK] **Require approvals** : 1
   - [OK] **Dismiss stale pull request approvals when new commits are pushed**
   - [OK] **Require status checks to pass before merging** (si CI/CD)
   - [OK] **Include administrators** (même les admins doivent respecter)
5. Cliquer "Create"

---

**Tester la protection :**

```bash
cd ~/taskmaster-pro
git switch main

echo "test" >> README.md
git add README.md
git commit -m "test: direct push to main"

git push origin main
```

**Résultat :**

```
remote: error: GH006: Protected branch update failed for refs/heads/main.
remote: error: Required approvals not met (0 of 1 required)
To github.com:alice/taskmaster-pro.git
 ! [remote rejected] main -> main (protected branch hook declined)
error: failed to push some refs to 'github.com:alice/taskmaster-pro.git'
```

**[X] Push direct bloqué ! [OK] Protection fonctionne !**

---

**Annuler le commit (pas pushé) :**

```bash
git reset --soft HEAD~1
git restore --staged README.md
git restore README.md
```

---

### [OK] TESTS DE VALIDATION

**1. Dépôt GitHub créé et accessible**

- [ ] Aller sur https://github.com/ton-username/taskmaster-pro
- [ ] Le dépôt existe et contient le code

---

**2. Clone fonctionne**

```bash
cd /tmp
git clone git@github.com:ton-username/taskmaster-pro.git test-clone
cd test-clone
git log --oneline
rm -rf /tmp/test-clone
```

- [ ] Clone réussi
- [ ] Historique présent

---

**3. Push/Pull fonctionnent**

```bash
# Créer une branche
git switch -c test-push
echo "test" > test.txt
git add test.txt
git commit -m "test: push/pull"

# Push
git push -u origin test-push

# Pull (sur une autre machine simulée)
git fetch origin
git switch test-push

# Nettoyer
git switch main
git push origin --delete test-push
git branch -d test-push
```

- [ ] Push réussi
- [ ] Branche visible sur GitHub
- [ ] Pull réussi

---

**4. Pull Request créée**

- [ ] Au moins 1 PR créée
- [ ] PR contient commits et diff
- [ ] PR mergée avec succès

---

**5. Protection de branche active**

- [ ] Essayer de push direct sur `main` -> Bloqué
- [ ] Forcer à passer par PR

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : "Permission denied (publickey)"

**Symptôme :**

```
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.
```

**Cause :** Clé SSH non configurée ou non ajoutée à GitHub

**Solutions :**

```bash
# Vérifier que la clé existe
ls ~/.ssh/id_ed25519

# Tester la connexion
ssh -T git@github.com

# Si erreur, regénérer la clé (voir ÉTAPE 2)
ssh-keygen -t ed25519 -C "ton@email.com"

# Ajouter à GitHub (voir ÉTAPE 2)
cat ~/.ssh/id_ed25519.pub
```

---

#### Erreur 2 : "Failed to push some refs"

**Symptôme :**

```
! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'github.com:user/repo.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
```

**Cause :** Le remote a des commits que tu n'as pas localement

**Solution :**

```bash
# Récupérer les changements
git pull origin main

# Résoudre les conflits si nécessaire

# Re-pousser
git push origin main
```

---

#### Erreur 3 : "fatal: refusing to merge unrelated histories"

**Symptôme :**

```
fatal: refusing to merge unrelated histories
```

**Cause :** Dépôt local et remote ont des historiques différents (souvent quand le dépôt GitHub a été initialisé avec README)

**Solution :**

```bash
git pull origin main --allow-unrelated-histories

# Résoudre les conflits (README probablement)

git push origin main
```

---

#### Erreur 4 : "GH006: Protected branch update failed"

**Symptôme :**

```
remote: error: GH006: Protected branch update failed for refs/heads/main.
```

**Cause :** Tentative de push direct sur une branche protégée

**Solution :**

```bash
# Créer une branche
git switch -c feature/my-changes

# Push la branche
git push -u origin feature/my-changes

# Créer une PR sur GitHub
```

---

#### Erreur 5 : "fatal: remote origin already exists"

**Symptôme :**

```
fatal: remote origin already exists.
```

**Cause :** Remote "origin" déjà configuré

**Solutions :**

```bash
# Voir les remotes
git remote -v

# Option 1 : Supprimer et recréer
git remote remove origin
git remote add origin git@github.com:user/repo.git

# Option 2 : Modifier l'URL
git remote set-url origin git@github.com:user/repo.git
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Remote**
- `origin` = Nom conventionnel du dépôt distant principal
- Configuré automatiquement avec `git clone`
- Ajouté manuellement avec `git remote add`

**2. Push vs Pull vs Fetch**

| Commande | Action | Modifie fichiers locaux ? |
|----------|--------|---------------------------|
| `git push` | Envoie commits -> GitHub | Non |
| `git fetch` | Récupère commits <- GitHub | Non |
| `git pull` | Fetch + Merge | Oui |

**3. Pull Request**
- Demande de fusion de code
- Permet la revue par l'équipe
- Workflow moderne en entreprise

**4. Protection de branche**
- Force l'utilisation de PRs
- Empêche push direct sur `main`
- Garantit la qualité du code

**5. Commandes essentielles**
```bash
git remote add origin <url>    # Ajouter remote
git push -u origin main        # Premier push
git clone <url>                # Cloner
git pull origin main           # Synchroniser
git fetch origin               # Récupérer sans merger
```

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Plusieurs remotes**

```bash
# Ajouter un second remote (fork, backup, etc.)
git remote add upstream git@github.com:original/repo.git

# Voir tous les remotes
git remote -v

# Fetch depuis upstream
git fetch upstream

# Merger upstream/main dans main
git merge upstream/main
```

---

**2. Changer l'URL d'un remote**

```bash
# Passer de HTTPS à SSH
git remote set-url origin git@github.com:user/repo.git

# Vérifier
git remote -v
```

---

**3. Forker un projet**

**Fork** = Copie d'un dépôt sur ton compte GitHub

**Workflow fork :**

```
1. Fork le dépôt sur GitHub (bouton "Fork")
2. Clone TON fork
   git clone git@github.com:ton-user/repo.git
3. Ajoute l'original comme upstream
   git remote add upstream git@github.com:original-user/repo.git
4. Travaille sur une branche
5. Push vers TON fork
6. Ouvre une PR vers l'original
```

**Usage :** Contribuer à des projets open source

---

**4. Pull Request templates**

**Créer `.github/pull_request_template.md` :**

```markdown
## Description

<!-- Describe your changes in detail -->

## Type of change

- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update

## Checklist

- [ ] My code follows the style guidelines
- [ ] I have performed a self-review
- [ ] I have commented my code where needed
- [ ] I have updated the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests
- [ ] All tests pass locally
```

**Commiter et pusher.**

**Maintenant chaque PR aura ce template pré-rempli !**

---

**5. Draft Pull Requests**

```
1. Créer une PR
2. Cliquer "Create draft pull request"
3. Travaille sur la branche
4. Quand prêt : "Ready for review"
```

**Usage :** Montrer ton travail en cours sans demander de review

---

**6. GitHub CLI (gh)**

```bash
# Installer
sudo apt install gh

# Authentifier
gh auth login

# Créer une PR depuis le terminal
gh pr create --title "feat: add new feature" --body "Description"

# Lister les PRs
gh pr list

# Merger une PR
gh pr merge 42
```

---

**7. Git LFS (Large File Storage)**

**Pour les gros fichiers (images, vidéos, binaries) :**

```bash
# Installer
git lfs install

# Tracker les fichiers .psd (Photoshop)
git lfs track "*.psd"

# Commiter .gitattributes
git add .gitattributes
git commit -m "chore: setup Git LFS"

# Les fichiers .psd seront stockés dans Git LFS
git add design.psd
git commit -m "feat: add design mockup"
git push
```

---

## [COURS] CONCLUSION DE L'EXERCICE 3

**[OK] Félicitations ! Tu maîtrises la collaboration avec GitHub !**

**Ce que tu as appris :**
- Créer et configurer un dépôt GitHub
- Configurer SSH pour GitHub
- Push/Pull/Fetch/Clone
- Remote tracking branches
- Pull Requests (création, review, merge)
- Protection de branches
- Gestion d'équipe

**Compétences acquises :**
- [OK] Collaboration GitHub (niveau professionnel)
- [OK] Workflow Pull Request
- [OK] Remote repositories
- [OK] SSH authentication
- [OK] Branch protection

**Temps moyen de réalisation :** 3-4 heures

**Prochaine étape :** Exercice 4 - Gestion des conflits ! [HOT]

---

# [JAUNE] EXERCICE 4 : GESTION DES CONFLITS

## [LISTE] ÉNONCÉ

### Contexte professionnel

L'équipe TaskMaster Pro grandit. Plusieurs développeurs modifient les mêmes fichiers simultanément, ce qui crée des **conflits Git**.

**Aujourd'hui, tu vas apprendre à :**
1. Comprendre pourquoi les conflits arrivent
2. Identifier les conflits
3. Résoudre les conflits manuellement
4. Utiliser des outils de merge (mergetool)
5. Prévenir les conflits
6. Gérer les conflits dans les Pull Requests

**Situations conflictuelles :**
- [PERSONNE][CODE] **Alice** et [PERSONNE][CODE] **Bob** modifient le même fichier API
- [PERSONNE][CODE] **Charlie** et [PERSONNE][CODE] **Diana** changent le même composant React
- [PERSONNE][CODE] **Emma** refactorise du code déjà modifié par **Bob**

### Objectifs

- Comprendre les types de conflits
- Résoudre des conflits de merge
- Résoudre des conflits de rebase
- Utiliser `git mergetool`
- Stratégies de prévention
- Gestion des conflits en équipe

### Livrables

- Au moins 3 conflits résolus manuellement
- 1 conflit résolu avec mergetool
- Documentation des stratégies de prévention
- Workflow d'équipe pour gérer les conflits

### Contraintes

- Ne jamais accepter aveuglément les changements (understand first)
- Tester après résolution
- Commiter avec un message descriptif
- Temps estimé : 2-3 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :

- [OK] Identifier les types de conflits
- [OK] Lire et comprendre les marqueurs de conflit
- [OK] Résoudre des conflits manuellement
- [OK] Utiliser des outils de merge
- [OK] Résoudre des conflits lors d'un rebase
- [OK] Annuler un merge en cours
- [OK] Prévenir les conflits
- [OK] Communiquer avec l'équipe sur les conflits

---

## [DOCS] PRÉREQUIS

- Exercices 1, 2 et 3 terminés
- Compréhension des branches et merges
- Dépôt GitHub configuré avec collaborateurs

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Comprendre les conflits

**Qu'est-ce qu'un conflit Git ?**

Un conflit arrive quand Git **ne peut pas fusionner automatiquement** des changements.

**Exemple simple :**

```
Fichier original (main) :
    console.log("Hello");

Alice modifie :
    console.log("Hello World");

Bob modifie :
    console.log("Bonjour");

Git ne sait pas quelle version garder ! -> CONFLIT
```

---

**Quand les conflits arrivent-ils ?**

| Situation | Conflit ? |
|-----------|-----------|
| 2 développeurs modifient des **fichiers différents** | [X] Non |
| 2 développeurs modifient des **lignes différentes** du même fichier | [X] Non (généralement) |
| 2 développeurs modifient la **même ligne** | [OK] OUI |
| 2 développeurs modifient des **lignes proches** | [ATTENTION] Parfois |

---

**Types de conflits :**

**1. Conflit de contenu** (le plus courant)
- Même fichier, même ligne modifiée différemment

**2. Conflit de suppression**
- Alice modifie un fichier
- Bob supprime ce fichier

**3. Conflit de renommage**
- Alice renomme `file.js` -> `module.js`
- Bob renomme `file.js` -> `component.js`

**4. Conflit de fichier vs dossier**
- Alice crée un fichier `utils`
- Bob crée un dossier `utils/`

---

### ÉTAPE 2 : Créer un conflit simple (simulation)

**Situation :** Alice et Bob modifient le même fichier.

**Alice travaille sur main :**

```bash
cd ~/taskmaster-pro
git switch main
git pull origin main  # S'assurer d'être à jour
```

---

**Alice modifie le README :**

```bash
cat >> README.md << 'EOF'

## Contributors

- Alice Johnson - Lead Developer & Project Manager
- Bob Smith - Backend Developer
- Charlie Brown - Frontend Developer
EOF
```

---

**Alice commite et push :**

```bash
git add README.md
git commit -m "docs(readme): add contributors section"
git push origin main
```

---

**Pendant ce temps, Bob travaille sur SA version de main (AVANT le push d'Alice) :**

```bash
cd ~/bob-workspace/taskmaster-pro
git switch main

# Bob n'a PAS encore fait git pull
# Il travaille sur une version ANCIENNE de main
```

---

**Bob modifie le README (même zone) :**

```bash
cat >> README.md << 'EOF'

## Team Members

Team lead: Alice Johnson
Backend: Bob Smith
Frontend: Charlie Brown
DevOps: Diana Prince
QA: Emma Watson
EOF
```

---

**Bob commite :**

```bash
git add README.md
git commit -m "docs(readme): add team members list"
```

---

**Bob tente de push :**

```bash
git push origin main
```

**Résultat :**

```
To github.com:alice/taskmaster-pro.git
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'github.com:alice/taskmaster-pro.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
```

**[X] Push rejeté ! Le remote a changé (Alice a pushé).**

---

### ÉTAPE 3 : Identifier le conflit

**Bob fait un pull :**

```bash
git pull origin main
```

**Résultat :**

```
From github.com:alice/taskmaster-pro
 * branch            main       -> FETCH_HEAD
Auto-merging README.md
CONFLICT (content): Merge conflict in README.md
Automatic merge failed; fix conflicts and then commit the result.
```

**Explication :**

**`Auto-merging README.md`** :
Git tente de fusionner automatiquement

**`CONFLICT (content): Merge conflict in README.md`** :
[X] Échec ! Conflit de contenu dans README.md

**`Automatic merge failed`** :
Git s'arrête et attend que tu résolves manuellement

---

**Vérifier le statut :**

```bash
git status
```

**Résultat :**

```
On branch main
Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.
  (use "git pull" to merge the remote branch into yours)

You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
        both modified:   README.md

no changes added to commit (use "git add" and/or "git commit -a")
```

**Explication :**

**`have diverged, and have 1 and 1 different commits`** :
- Branche locale a 1 commit (Bob)
- Branche distante a 1 commit (Alice)
- Ils ont divergé

**`You have unmerged paths`** :
Il y a des conflits non résolus

**`both modified: README.md`** :
README.md modifié des 2 côtés -> conflit

---

### ÉTAPE 4 : Lire les marqueurs de conflit

**Ouvrir le fichier en conflit :**

```bash
cat README.md
```

**Contenu (fin du fichier) :**

```markdown
## License

MIT

<<<<<<< HEAD
## Team Members

Team lead: Alice Johnson
Backend: Bob Smith
Frontend: Charlie Brown
DevOps: Diana Prince
QA: Emma Watson
=======
## Contributors

- Alice Johnson - Lead Developer & Project Manager
- Bob Smith - Backend Developer
- Charlie Brown - Frontend Developer
>>>>>>> a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0
```

**Explication des marqueurs :**

**`<<<<<<< HEAD`** :
Début du conflit
Version actuelle (locale, Bob)

**`=======`** :
Séparateur
Divise "notre version" et "leur version"

**`>>>>>>> a1b2c3d...`** :
Fin du conflit
Version distante (Alice)
Le hash est le commit d'Alice

---

**Décomposition :**

```
<<<<<<< HEAD
[Version de Bob]
=======
[Version d'Alice]
>>>>>>> hash-commit-alice
```

**Git dit :**
"Je ne sais pas quoi garder. À toi de décider !"

---

### ÉTAPE 5 : Résoudre le conflit manuellement

**Stratégies de résolution :**

**1. Garder seulement MA version (HEAD)**

```markdown
## Team Members

Team lead: Alice Johnson
Backend: Bob Smith
Frontend: Charlie Brown
DevOps: Diana Prince
QA: Emma Watson
```

---

**2. Garder seulement LEUR version (incoming)**

```markdown
## Contributors

- Alice Johnson - Lead Developer & Project Manager
- Bob Smith - Backend Developer
- Charlie Brown - Frontend Developer
```

---

**3. Garder LES DEUX (merge manuel)**

```markdown
## Contributors

- Alice Johnson - Lead Developer & Project Manager
- Bob Smith - Backend Developer
- Charlie Brown - Frontend Developer

## Team Structure

Team lead: Alice Johnson
Backend: Bob Smith
Frontend: Charlie Brown
DevOps: Diana Prince
QA: Emma Watson
```

---

**4. Réécrire complètement**

```markdown
## Team

| Role | Name |
|------|------|
| Lead Developer | Alice Johnson |
| Backend Developer | Bob Smith |
| Frontend Developer | Charlie Brown |
| DevOps Engineer | Diana Prince |
| QA Engineer | Emma Watson |
```

---

**Pour cet exercice, on va fusionner les deux versions :**

**Éditer README.md :**

```bash
nano README.md
```

**Supprimer les marqueurs et combiner :**

```markdown
## License

MIT

## Team

### Leadership
- Alice Johnson - Lead Developer & Project Manager

### Development Team
- Bob Smith - Backend Developer
- Charlie Brown - Frontend Developer
- Diana Prince - DevOps Engineer
- Emma Watson - QA Engineer
```

**Sauvegarder et fermer.**

---

### ÉTAPE 6 : Marquer le conflit comme résolu

**Ajouter le fichier résolu à la staging area :**

```bash
git add README.md
```

**Cela indique à Git : "Conflit résolu dans ce fichier"**

---

**Vérifier le statut :**

```bash
git status
```

**Résultat :**

```
On branch main
All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:
        modified:   README.md
```

**`All conflicts fixed`** :
Tous les conflits résolus

**`you are still merging`** :
Le merge n'est pas terminé, il faut commiter

---

**Terminer le merge :**

```bash
git commit
```

**Un éditeur s'ouvre avec un message par défaut :**

```
Merge branch 'main' of github.com:alice/taskmaster-pro into main

# Conflicts:
#       README.md
#
# It looks like you may be committing a merge.
# If this is not correct, please run
#       git update-ref -d MERGE_HEAD
# and try again.
```

**Tu peux :**
- Garder le message par défaut
- Le personnaliser

**Message amélioré :**

```
Merge branch 'main' of github.com:alice/taskmaster-pro into main

Resolve conflict in README.md:
- Combined Contributors and Team Members sections
- Organized team by roles
- Added table format for clarity
```

**Sauvegarder et fermer.**

---

**Résultat :**

```
[main p6q7r8s] Merge branch 'main' of github.com:alice/taskmaster-pro into main
```

**[OK] Conflit résolu et merge terminé !**

---

**Pousser :**

```bash
git push origin main
```

**Résultat :**

```
Enumerating objects: 10, done.
...
To github.com:alice/taskmaster-pro.git
   o5p6q7r..p6q7r8s  main -> main
```

**[OK] Code pushé avec résolution de conflit !**

---

### ÉTAPE 7 : Annuler un merge en cours

**Si tu veux abandonner le merge (AVANT le commit final) :**

```bash
git merge --abort
```

**Résultat :**
- Annule le merge
- Retourne à l'état AVANT `git pull`
- Tous les fichiers en conflit sont restaurés

---

**Quand l'utiliser ?**
- Conflit trop complexe
- Besoin de consulter l'équipe
- Réalisation d'une erreur (mauvaise branche)

---

### ÉTAPE 8 : Résoudre un conflit avec mergetool

**Installer un mergetool (meld recommandé) :**

```bash
sudo apt install meld
```

**Autres outils populaires :**
- `vimdiff` (intégré à Vim)
- `kdiff3`
- `p4merge`
- VS Code (intégré)

---

**Configurer Git pour utiliser meld :**

```bash
git config --global merge.tool meld
git config --global mergetool.meld.path /usr/bin/meld
```

---

**Créer un nouveau conflit (simulation) :**

**Alice modifie le backend :**

```bash
cd ~/taskmaster-pro
git switch main
git pull origin main

cat > backend/src/config.js << 'EOF'
module.exports = {
  port: 3000,
  database: {
    host: 'localhost',
    port: 5432,
    name: 'taskmaster'
  }
};
EOF

git add backend/src/config.js
git commit -m "feat(backend): add server configuration"
git push origin main
```

---

**Bob modifie le même fichier (version ancienne) :**

```bash
cd ~/bob-workspace/taskmaster-pro
git switch main
# Pas de pull -> version ancienne

cat > backend/src/config.js << 'EOF'
module.exports = {
  serverPort: 4000,
  db: {
    hostname: '127.0.0.1',
    dbPort: 5432,
    dbName: 'taskmaster_db'
  }
};
EOF

git add backend/src/config.js
git commit -m "feat(backend): configure server settings"
git pull origin main
```

**CONFLIT ! (attendu)**

---

**Lancer mergetool :**

```bash
git mergetool
```

**Résultat :**

```
Merging:
backend/src/config.js

Normal merge conflict for 'backend/src/config.js':
  {local}: modified file
  {remote}: modified file
Hit return to start merge resolution tool (meld):
```

**Appuyer sur Entrée.**

---

**Interface meld s'ouvre avec 3 panneaux :**

```
┌─────────────┬─────────────┬─────────────┐
│   LOCAL     │   MERGED    │   REMOTE    │
│  (Ta version)│ (Résultat) │(Leur version)│
├─────────────┼─────────────┼─────────────┤
│ port: 4000  │             │ port: 3000  │
│             │   ???       │             │
│ serverPort  │             │ database    │
└─────────────┴─────────────┴─────────────┘
```

---

**Utilisation de meld :**

1. **Panneau gauche (LOCAL)** : Ta version (Bob)
2. **Panneau centre (MERGED)** : Résultat final (éditable)
3. **Panneau droite (REMOTE)** : Leur version (Alice)

**Actions :**
- Flèches : Copier un changement vers le centre
- Édition directe dans le panneau central
- Sauvegarder (`Ctrl+S`) et fermer

---

**Exemple de résolution (panneau central) :**

```javascript
module.exports = {
  port: 3000,  // Garder version Alice
  database: {
    host: 'localhost',
    port: 5432,
    name: 'taskmaster'
  }
};
```

**Sauvegarder et fermer meld.**

---

**Git demande :**

```
Was the merge successful? [y/n]
```

**Taper `y` et Entrée.**

---

**Vérifier le statut :**

```bash
git status
```

**Résultat :**

```
On branch main
All conflicts fixed but you are still merging.

Changes to be committed:
        modified:   backend/src/config.js

Untracked files:
        backend/src/config.js.orig
```

**`config.js.orig`** = Backup du fichier avant merge (créé par mergetool)

---

**Supprimer les fichiers .orig (optionnel) :**

```bash
find . -name "*.orig" -delete
```

**Ou configurer Git pour ne pas les créer :**

```bash
git config --global mergetool.keepBackup false
```

---

**Terminer le merge :**

```bash
git commit -m "Merge: resolve config.js conflict

Keep Alice's configuration structure (port, database)
Standardize property naming"
```

---

**Pousser :**

```bash
git push origin main
```

---

### ÉTAPE 9 : Conflit lors d'un rebase

**Différence merge vs rebase :**

**Merge :**
```
      C (Bob)
     ╱ ╲
A───B───D (merge commit)
     ╲ ╱
      E (Alice)
```

**Rebase :**
```
A───B───E (Alice)───C' (Bob rebasé)
```

---

**Créer une branche et un conflit rebase :**

**Charlie crée une branche :**

```bash
cd ~/charlie-workspace/taskmaster-pro
git switch main
git pull origin main

git switch -c feature/ui-improvements
```

---

**Charlie modifie un composant :**

```bash
cat > frontend/src/components/Header.jsx << 'EOF'
import React from 'react';

function Header() {
  return (
    <header className="app-header">
      <h1>TaskMaster Pro</h1>
      <nav>
        <a href="/">Home</a>
        <a href="/tasks">Tasks</a>
      </nav>
    </header>
  );
}

export default Header;
EOF

git add frontend/src/components/Header.jsx
git commit -m "feat(ui): add Header component with navigation"
```

---

**Pendant ce temps, Diana modifie le même fichier sur main :**

```bash
cd ~
mkdir diana-workspace
cd diana-workspace
git clone git@github.com:alice/taskmaster-pro.git
cd taskmaster-pro

git config user.name "Diana Prince"
git config user.email "diana@taskmaster.com"

git switch main
```

---

**Diana modifie Header.jsx (créé en même temps) :**

```bash
cat > frontend/src/components/Header.jsx << 'EOF'
import React from 'react';
import './Header.css';

function Header({ user }) {
  return (
    <header className="main-header">
      <div className="logo">TaskMaster</div>
      <div className="user-info">
        {user ? user.name : 'Guest'}
      </div>
    </header>
  );
}

export default Header;
EOF

git add frontend/src/components/Header.jsx
git commit -m "feat(ui): create Header with user info"
git push origin main
```

---

**Charlie veut rebaser sa branche sur main (pour garder historique linéaire) :**

```bash
cd ~/charlie-workspace/taskmaster-pro

git fetch origin
git rebase origin/main
```

**Résultat :**

```
First, rewinding head to replay your work on top of it...
Applying: feat(ui): add Header component with navigation
Using index info to reconstruct a base tree...
M       frontend/src/components/Header.jsx
Falling back to patching base and 3-way merge...
Auto-merging frontend/src/components/Header.jsx
CONFLICT (add/add): Merge conflict in frontend/src/components/Header.jsx
error: could not apply a1b2c3d... feat(ui): add Header component with navigation
hint: Resolve all conflicts manually, mark them as resolved with
hint: "git add/rm <conflicted_files>", then run "git rebase --continue".
hint: You can instead skip this commit: run "git rebase --skip".
hint: To abort and get back to the state before "git rebase", run "git rebase --abort".
Could not apply a1b2c3d... feat(ui): add Header component with navigation
```

**`CONFLICT (add/add)`** :
Fichier ajouté des 2 côtés différemment

---

**Résoudre le conflit :**

```bash
cat frontend/src/components/Header.jsx
```

**Contenu :**

```jsx
import React from 'react';
<<<<<<< HEAD
import './Header.css';

function Header({ user }) {
  return (
    <header className="main-header">
      <div className="logo">TaskMaster</div>
      <div className="user-info">
        {user ? user.name : 'Guest'}
      </div>
=======

function Header() {
  return (
    <header className="app-header">
      <h1>TaskMaster Pro</h1>
      <nav>
        <a href="/">Home</a>
        <a href="/tasks">Tasks</a>
      </nav>
>>>>>>> a1b2c3d (feat(ui): add Header component with navigation)
    </header>
  );
}

export default Header;
```

---

**Éditer manuellement :**

```bash
nano frontend/src/components/Header.jsx
```

**Version fusionnée :**

```jsx
import React from 'react';
import './Header.css';

function Header({ user }) {
  return (
    <header className="main-header">
      <div className="logo">
        <h1>TaskMaster Pro</h1>
      </div>
      
      <nav className="main-nav">
        <a href="/">Home</a>
        <a href="/tasks">Tasks</a>
      </nav>
      
      <div className="user-info">
        {user ? user.name : 'Guest'}
      </div>
    </header>
  );
}

export default Header;
```

**Sauvegarder.**

---

**Marquer comme résolu :**

```bash
git add frontend/src/components/Header.jsx
```

---

**Continuer le rebase :**

```bash
git rebase --continue
```

**Résultat :**

```
[detached HEAD r8s9t0u] feat(ui): add Header component with navigation
 1 file changed, 24 insertions(+)
 create mode 100644 frontend/src/components/Header.jsx
Successfully rebased and updated refs/heads/feature/ui-improvements.
```

**[OK] Rebase terminé avec succès !**

---

**Si besoin d'annuler le rebase (avant `--continue`) :**

```bash
git rebase --abort
```

---

**Pousser la branche (force requis car historique réécrit) :**

```bash
git push -u origin feature/ui-improvements --force
```

**[ATTENTION] `--force` dangereux ! Utilise seulement sur TES branches (pas partagées).**

**Alternative plus sûre :**

```bash
git push -u origin feature/ui-improvements --force-with-lease
```

**`--force-with-lease`** :
- Force le push SEULEMENT si personne n'a pushé entre-temps
- Plus sûr que `--force`

---

### ÉTAPE 10 : Stratégies de prévention des conflits

**1. Communiquer avec l'équipe**

**Mauvais :**
- Silence -> Plusieurs personnes modifient le même fichier

**Bon :**
- Slack/Discord : "Je travaille sur Header.jsx aujourd'hui"
- Assignation GitHub : Assigner les fichiers aux développeurs

---

**2. Pull fréquemment**

```bash
# Tous les matins
git pull origin main

# Avant de commencer une nouvelle tâche
git pull origin main

# Avant de créer une PR
git pull origin main
```

**Plus tu pull souvent, moins les conflits sont complexes.**

---

**3. Branches courtes et focalisées**

**Mauvais :**
```
feature/mega-refactor
- 50 fichiers modifiés
- Travail pendant 2 semaines
- Mergé en une fois -> CONFLITS MASSIFS
```

**Bon :**
```
feature/refactor-auth (5 fichiers, 2 jours)
feature/refactor-api (3 fichiers, 1 jour)
feature/refactor-ui (8 fichiers, 3 jours)
```

---

**4. Merger/Rebaser main dans ta branche régulièrement**

```bash
# Sur ta branche feature
git fetch origin
git merge origin/main

# Ou
git rebase origin/main
```

**Bénéfice :**
- Conflits résolus au fur et à mesure
- Pas de surprise au moment du merge final

---

**5. Utiliser des fichiers modulaires**

**Mauvais :**
```
config.js  (5000 lignes, tout dedans)
-> Tout le monde modifie ce fichier
```

**Bon :**
```
config/
  database.js
  server.js
  auth.js
  email.js
-> Chacun modifie son fichier
```

---

**6. Convention de nommage et organisation**

**Exemple :**
```
components/
  auth/
    Login.jsx        (assigné à Alice)
    Register.jsx     (assigné à Bob)
  tasks/
    TaskList.jsx     (assigné à Charlie)
    TaskForm.jsx     (assigné à Charlie)
```

**Chaque développeur a ses fichiers -> Moins de conflits.**

---

**7. Code review rapide**

**Mauvais :**
- PR ouverte pendant 1 semaine
- Main avance beaucoup
- Merge final = CONFLITS

**Bon :**
- PR reviewée en 24h
- Mergée rapidement
- Moins de divergence

---

### [OK] TESTS DE VALIDATION

**1. Résolution de conflit manuel**

```bash
# Créer un conflit volontairement
echo "version 1" > test-conflict.txt
git add test-conflict.txt
git commit -m "test: version 1"

git switch -c branch-a
echo "version A" > test-conflict.txt
git commit -am "test: version A"

git switch main
echo "version B" > test-conflict.txt
git commit -am "test: version B"

git merge branch-a
# Résoudre le conflit
nano test-conflict.txt  # Éditer
git add test-conflict.txt
git commit

# Nettoyer
git branch -d branch-a
rm test-conflict.txt
git commit -am "clean: remove test file"
```

- [ ] Conflit identifié
- [ ] Marqueurs compris
- [ ] Résolution réussie

---

**2. Utilisation de mergetool**

```bash
# Configurer
git config --global merge.tool meld

# Lors d'un conflit
git mergetool

# Résoudre avec interface graphique
```

- [ ] Mergetool configuré
- [ ] Interface ouverte
- [ ] Conflit résolu

---

**3. Annulation de merge**

```bash
# Créer un conflit
# ... (comme au-dessus)

# Annuler avant commit
git merge --abort

# Vérifier
git status  # Doit être clean
```

- [ ] Merge annulé avec succès

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : "You have unstaged changes"

**Symptôme :**

```
error: Your local changes to the following files would be overwritten by merge:
        file.txt
Please commit your changes or stash them before you merge.
```

**Cause :** Changements non commités avant merge/pull

**Solution :**

```bash
# Option 1 : Commiter
git add .
git commit -m "WIP: save work"

# Option 2 : Stash
git stash
git pull
git stash pop
```

---

#### Erreur 2 : Marqueurs de conflit dans le commit

**Symptôme :**

Code deployé contient `<<<<<<< HEAD`

**Cause :** Oublié de supprimer les marqueurs lors de la résolution

**Prévention :**

```bash
# Chercher les marqueurs avant commit
git diff --check

# Ou
grep -r "<<<<<<" .
grep -r ">>>>>>>" .
```

---

#### Erreur 3 : Mauvaise version choisie

**Symptôme :**

Après résolution, code cassé ou fonctionnalité perdue

**Solution :**

```bash
# Annuler le merge
git reset --hard HEAD~1

# Recommencer
git merge feature-branch
# Résoudre plus attentivement
```

---

#### Erreur 4 : Conflit dans .gitignore ou package.json

**Solution :**

**`.gitignore`** :
- Généralement garder LES DEUX
- Fusionner les lignes

**`package.json`** :
- Attention aux versions
- Tester après résolution : `npm install`

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Marqueurs de conflit**
```
<<<<<<< HEAD
[Ta version]
=======
[Leur version]
>>>>>>> commit-hash
```

**2. Workflow de résolution**
```
1. Identifier conflit (git status)
2. Ouvrir fichier
3. Comprendre les 2 versions
4. Choisir/Fusionner
5. Supprimer marqueurs
6. git add
7. git commit
```

**3. Outils**
- `git mergetool` : Interface graphique
- `git merge --abort` : Annuler
- `git diff --check` : Vérifier marqueurs

**4. Prévention**
- Pull souvent
- Branches courtes
- Communication d'équipe
- Code review rapide

**5. Différence merge vs rebase**
- **Merge** : Commit de merge (historique non-linéaire)
- **Rebase** : Réapplique commits (historique linéaire)
- Conflits possibles dans les 2 cas

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Stratégies de merge avancées**

```bash
# Toujours choisir NOTRE version
git merge -X ours feature-branch

# Toujours choisir LEUR version
git merge -X theirs feature-branch

# Utile pour fichiers générés (lockfiles, etc.)
```

---

**2. Rerere (Reuse Recorded Resolution)**

```bash
# Activer
git config --global rerere.enabled true

# Git mémorise tes résolutions de conflits
# Si même conflit réapparaît -> résolu automatiquement !
```

---

**3. Merge drivers personnalisés**

**Exemple : Toujours garder la version la plus récente de package-lock.json**

```bash
# .gitattributes
package-lock.json merge=npm-merge-driver

# .git/config
[merge "npm-merge-driver"]
    name = Automatically merge package-lock.json
    driver = npx npm-merge-driver merge %A %O %B %P
```

---

**4. Diff3 (montrer la version commune)**

```bash
git config --global merge.conflictstyle diff3
```

**Marqueurs deviennent :**

```
<<<<<<< HEAD
[Ta version]
||||||| merged common ancestors
[Version originale commune]
=======
[Leur version]
>>>>>>> branch
```

**Plus d'informations pour résoudre intelligemment.**

---

**5. Résolution par chunks**

**Si fichier énorme avec multiples conflits :**

```bash
# Résoudre fichier par fichier
git checkout --ours file.txt   # Garder notre version
git checkout --theirs file.txt # Garder leur version
git checkout -m file.txt       # Ré-afficher les marqueurs si erreur
```

---

## [COURS] CONCLUSION DE L'EXERCICE 4

**[OK] Félicitations ! Tu maîtrises la gestion des conflits Git !**

**Ce que tu as appris :**
- Identifier et comprendre les conflits
- Lire les marqueurs de conflit
- Résoudre manuellement
- Utiliser mergetool
- Conflits lors d'un rebase
- Stratégies de prévention

**Compétences acquises :**
- [OK] Résolution de conflits (niveau professionnel)
- [OK] Merge vs Rebase conflicts
- [OK] Outils de merge (meld, vimdiff)
- [OK] Communication d'équipe
- [OK] Prévention proactive

**Temps moyen de réalisation :** 2-3 heures

**Prochaine étape :** Exercice 5 - Pull Requests et Code Review professionnelles ! [EYES]

---

(Veux-tu que je continue avec l'Exercice 5, ou préfères-tu une pause ? Il reste encore 6 exercices à créer pour compléter les 10.)

# [JAUNE] EXERCICE 5 : PULL REQUESTS ET CODE REVIEW

## [LISTE] ÉNONCÉ

### Contexte professionnel

L'équipe TaskMaster Pro adopte un workflow professionnel de développement. **Alice** (Lead Developer) met en place un processus strict de **Code Review** via les Pull Requests pour garantir la qualité du code.

**Nouvelles règles d'équipe :**
1. [X] **AUCUN push direct sur `main`** (branche protégée)
2. [OK] Toutes les modifications passent par **Pull Request**
3. [OK] **Au moins 1 approbation** requise avant merge
4. [OK] **Tests automatiques** doivent passer (CI/CD)
5. [OK] **Template de PR** obligatoire
6. [OK] Code review constructif et bienveillant

**Aujourd'hui, l'équipe va :**
- Créer des Pull Requests professionnelles
- Faire des code reviews de qualité
- Configurer la protection de branches
- Mettre en place des templates
- Utiliser GitHub Actions (CI basique)
- Gérer les reviews et approbations

**Équipe active :**
- [PERSONNE][CODE] **Alice** - Configure le workflow et review
- [PERSONNE][CODE] **Bob** - Crée une PR pour l'authentification
- [PERSONNE][CODE] **Charlie** - Crée une PR pour le dashboard
- [PERSONNE][CODE] **Diana** - Review et suggère des améliorations
- [PERSONNE][CODE] **Emma** - Ajoute des tests automatiques

### Objectifs

- Créer des Pull Requests de qualité
- Rédiger des descriptions claires
- Faire des code reviews constructives
- Utiliser les suggestions GitHub
- Configurer les branch protection rules
- Mettre en place un workflow CI/CD basique
- Gérer les conversations de review

### Livrables

- Au moins 3 Pull Requests créées
- Chaque PR avec description complète
- 5+ commentaires de review constructifs
- 1 PR avec changements demandés
- Template de PR configuré
- GitHub Actions workflow (tests basiques)
- Documentation du processus

### Contraintes

- PR description suit le template
- Code review bienveillant et constructif
- Tous les commentaires doivent être résolus
- Tests automatiques passent avant merge
- Squash merge pour historique propre
- Temps estimé : 3-4 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :

- [OK] Créer des Pull Requests professionnelles
- [OK] Rédiger des descriptions de PR efficaces
- [OK] Faire des code reviews de qualité
- [OK] Utiliser les suggestions inline
- [OK] Gérer les demandes de changements
- [OK] Configurer la protection de branches
- [OK] Mettre en place GitHub Actions
- [OK] Résoudre les conversations de review
- [OK] Choisir la stratégie de merge appropriée

---

## [DOCS] PRÉREQUIS

- Exercices 1 à 4 terminés
- Dépôt GitHub avec collaborateurs
- Compréhension des branches et merges
- Familiarité avec les conflits

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Configurer la protection de la branche main

**Pourquoi protéger `main` ?**

| Sans protection | Avec protection |
|-----------------|-----------------|
| [X] Push direct possible | [OK] Force l'utilisation de PRs |
| [X] Pas de review | [OK] Review obligatoire |
| [X] Code non testé | [OK] Tests automatiques requis |
| [X] Historique sale | [OK] Historique propre |
| [X] Bugs en production | [OK] Qualité garantie |

---

**Aller sur GitHub :**

1. Repository **taskmaster-pro**
2. **Settings** -> **Branches**
3. **Add branch protection rule**

---

**Configuration (Branch name pattern) :**

```
main
```

---

**Activer les règles suivantes :**

**[x] Require a pull request before merging**
- Empêche les push directs
- Force à créer une PR

**Sous-options :**
- [x] **Require approvals** : `1`
  - Au moins 1 personne doit approuver
  - En entreprise : souvent 2+
  
- [x] **Dismiss stale pull request approvals when new commits are pushed**
  - Si la PR change après approbation -> approbation annulée
  - Force une nouvelle review
  
- [x] **Require review from Code Owners** (optionnel)
  - Si tu as un fichier `CODEOWNERS`
  - Les propriétaires de code doivent approuver

---

**[x] Require status checks to pass before merging**
- Tests automatiques doivent passer
- On va configurer ça après

**Pour l'instant, ne pas activer (on n'a pas encore de CI).**

---

**[x] Require conversation resolution before merging**
- Tous les commentaires de review doivent être "résolus"
- Empêche de merger avec des discussions en cours

---

**[x] Require linear history** (optionnel)
- Force squash ou rebase merge
- Interdit merge commits
- Historique plus propre

**Pour cet exercice, l'activer.**

---

**[x] Include administrators**
- Les règles s'appliquent AUSSI aux admins
- Même Alice doit suivre le processus

---

**[ ] Allow force pushes** : **NON** (dangereux)

**[ ] Allow deletions** : **NON** (protège contre suppression accidentelle)

---

**Cliquer "Create" en bas.**

---

**[OK] Branche `main` protégée !**

**Vérifier :**

```bash
cd ~/taskmaster-pro
git switch main
echo "test" >> README.md
git commit -am "test: direct push"
git push origin main
```

**Résultat :**

```
remote: error: GH006: Protected branch update failed for refs/heads/main.
remote: error: Required status checks are expected but none have been reported.
To github.com:alice/taskmaster-pro.git
 ! [remote rejected] main -> main (protected branch hook declined)
error: failed to push some refs to 'github.com:alice/taskmaster-pro.git'
```

**[X] Push bloqué ! [OK] Protection fonctionne !**

---

**Annuler le commit :**

```bash
git reset --hard HEAD~1
```

---

### ÉTAPE 2 : Créer un template de Pull Request

**Pourquoi un template ?**

- [OK] Structure cohérente pour toutes les PRs
- [OK] Force la documentation
- [OK] Checklist intégrée
- [OK] Gain de temps (pré-rempli)
- [OK] Meilleure communication

---

**Créer le dossier `.github` :**

```bash
cd ~/taskmaster-pro
mkdir -p .github
```

---

**Créer le template :**

```bash
cat > .github/pull_request_template.md << 'EOF'
## [LISTE] Description

<!-- Décris les changements apportés par cette PR -->

## [OBJECTIF] Type de changement

<!-- Coche la/les case(s) appropriée(s) -->

- [ ] [BUG] Bug fix (correction d'un bug)
- [ ] * New feature (nouvelle fonctionnalité)
- [ ] [IMPACT] Breaking change (changement cassant la compatibilité)
- [ ] [NOTE] Documentation (mise à jour de la documentation)
- [ ] [DESIGN] Style (formatage, point-virgules manquants, etc.)
- [ ] [BLACK_UNIVERSAL_RECYCLING_SYMBOL] Refactoring (ni bug fix ni nouvelle fonctionnalité)
- [ ] [RAPIDE] Performance (amélioration de la performance)
- [ ] [OK] Tests (ajout ou correction de tests)
- [ ] [OUTIL] Chore (mise à jour de dépendances, configuration, etc.)

## [LIEN] Issue liée

<!-- Référence l'issue GitHub si applicable -->

Closes #<!-- numéro de l'issue -->

## [CAMERA_WITH_FLASH] Screenshots (si applicable)

<!-- Ajoute des captures d'écran pour les changements visuels -->

## [TEST] Comment tester

<!-- Explique comment tester les changements -->

```bash
# Commandes pour tester
npm install
npm test
npm run dev
```

## [OK] Checklist

<!-- Assure-toi d'avoir fait toutes ces vérifications -->

- [ ] Mon code suit les conventions de style du projet
- [ ] J'ai effectué une auto-review de mon code
- [ ] J'ai commenté mon code là où c'est nécessaire
- [ ] J'ai mis à jour la documentation
- [ ] Mes changements ne génèrent pas de nouveaux warnings
- [ ] J'ai ajouté des tests qui prouvent que ma correction fonctionne
- [ ] Les tests unitaires passent localement
- [ ] Toutes les dépendances sont à jour

## [DOCS] Contexte additionnel

<!-- Ajoute tout contexte utile pour les reviewers -->

## [SYNC] Migrations nécessaires

<!-- Si des migrations de base de données ou autres sont nécessaires -->

- [ ] N/A
- [ ] Migrations de base de données requises
- [ ] Variables d'environnement à ajouter
- [ ] Commandes de déploiement spécifiques

## [MERCI] Reviewers

<!-- Mentionne les personnes dont tu souhaites la review -->

@alice @bob @charlie @diana @emma

---

<!-- 
Merci de prendre le temps de review cette PR ! 
N'hésite pas à poser des questions ou suggérer des améliorations.
-->
EOF
```

---

**Commiter et pousser :**

```bash
git add .github/
git commit -m "chore: add pull request template

- Structured PR description
- Type of change checklist
- Testing instructions
- Comprehensive checklist
- Migration tracking"

git push origin main
```

**[ATTENTION] Ça va échouer à cause de la protection !**

**Créer une branche :**

```bash
git switch -c chore/add-pr-template
git push -u origin chore/add-pr-template
```

---

**Créer la première PR avec le nouveau template :**

1. Aller sur GitHub -> **Pull requests** -> **New pull request**
2. **Base** : `main`
3. **Compare** : `chore/add-pr-template`
4. **Create pull request**

**Le template apparaît automatiquement ! [BRAVO]**

---

**Remplir le template :**

```markdown
## [LISTE] Description

Add a comprehensive pull request template to standardize PR submissions across the team.

This template includes:
- Type of change selection
- Issue linking
- Testing instructions
- Comprehensive checklist
- Migration tracking

## [OBJECTIF] Type de changement

- [x] [OUTIL] Chore (mise à jour de dépendances, configuration, etc.)

## [LIEN] Issue liée

N/A - Infrastructure improvement

## [TEST] Comment tester

The template will automatically appear when creating new PRs.

## [OK] Checklist

- [x] Mon code suit les conventions de style du projet
- [x] J'ai effectué une auto-review de mon code
- [x] J'ai commenté mon code là où c'est nécessaire
- [x] J'ai mis à jour la documentation
- [x] Mes changements ne génèrent pas de nouveaux warnings
- [x] J'ai ajouté des tests qui prouvent que ma correction fonctionne
- [x] Les tests unitaires passent localement
- [x] Toutes les dépendances sont à jour

## [DOCS] Contexte additionnel

This follows GitHub best practices for team collaboration.

## [SYNC] Migrations nécessaires

- [x] N/A

## [MERCI] Reviewers

@bob @charlie
```

---

**Cliquer "Create pull request".**

---

**Alice approuve et merge :**

1. **Files changed** -> Review changes
2. **Approve**
3. Submit review
4. **Squash and merge** (pour historique propre)
5. Confirm squash and merge
6. Delete branch

---

**[OK] Template de PR configuré !**

**Toutes les futures PRs auront ce template pré-rempli.**

---

### ÉTAPE 3 : Bob crée une PR pour l'authentification

**Bob crée une nouvelle fonctionnalité :**

```bash
cd ~/bob-workspace/taskmaster-pro
git switch main
git pull origin main

git switch -c feature/user-authentication
```

---

**Créer un module d'authentification :**

```bash
cat > backend/src/auth/auth.service.js << 'EOF'
/**
 * Authentication Service
 * Handles user authentication and JWT token generation
 * 
 * @author Bob Smith
 */

const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');

class AuthService {
  /**
   * Generate JWT token for user
   * @param {Object} user - User object
   * @returns {string} JWT token
   */
  generateToken(user) {
    const payload = {
      id: user.id,
      email: user.email,
      role: user.role
    };

    return jwt.sign(payload, process.env.JWT_SECRET, {
      expiresIn: '24h'
    });
  }

  /**
   * Hash user password
   * @param {string} password - Plain text password
   * @returns {Promise<string>} Hashed password
   */
  async hashPassword(password) {
    const salt = await bcrypt.genSalt(10);
    return bcrypt.hash(password, salt);
  }

  /**
   * Compare password with hash
   * @param {string} password - Plain text password
   * @param {string} hash - Hashed password
   * @returns {Promise<boolean>} Match result
   */
  async comparePassword(password, hash) {
    return bcrypt.compare(password, hash);
  }

  /**
   * Validate JWT token
   * @param {string} token - JWT token
   * @returns {Object|null} Decoded payload or null
   */
  validateToken(token) {
    try {
      return jwt.verify(token, process.env.JWT_SECRET);
    } catch (error) {
      return null;
    }
  }
}

module.exports = new AuthService();
EOF
```

---

**Créer le contrôleur :**

```bash
cat > backend/src/auth/auth.controller.js << 'EOF'
/**
 * Authentication Controller
 * Handles HTTP requests for authentication
 * 
 * @author Bob Smith
 */

const authService = require('./auth.service');

class AuthController {
  /**
   * User login
   * POST /api/auth/login
   */
  async login(req, res) {
    try {
      const { email, password } = req.body;

      // TODO: Fetch user from database
      // For now, mock user
      const user = {
        id: 1,
        email: email,
        role: 'user'
      };

      // TODO: Verify password
      const isValid = await authService.comparePassword(
        password,
        '$2a$10$...' // Mock hash
      );

      if (!isValid) {
        return res.status(401).json({
          error: 'Invalid credentials'
        });
      }

      const token = authService.generateToken(user);

      res.json({
        success: true,
        token: token,
        user: {
          id: user.id,
          email: user.email,
          role: user.role
        }
      });
    } catch (error) {
      res.status(500).json({
        error: error.message
      });
    }
  }

  /**
   * User registration
   * POST /api/auth/register
   */
  async register(req, res) {
    try {
      const { email, password, name } = req.body;

      // Validate input
      if (!email || !password || !name) {
        return res.status(400).json({
          error: 'Missing required fields'
        });
      }

      // Hash password
      const hashedPassword = await authService.hashPassword(password);

      // TODO: Save user to database
      const user = {
        id: Date.now(),
        email: email,
        name: name,
        role: 'user'
      };

      const token = authService.generateToken(user);

      res.status(201).json({
        success: true,
        token: token,
        user: user
      });
    } catch (error) {
      res.status(500).json({
        error: error.message
      });
    }
  }
}

module.exports = new AuthController();
EOF
```

---

**Créer le middleware :**

```bash
cat > backend/src/auth/auth.middleware.js << 'EOF'
/**
 * Authentication Middleware
 * Protects routes with JWT authentication
 * 
 * @author Bob Smith
 */

const authService = require('./auth.service');

/**
 * Verify JWT token in Authorization header
 */
const authenticate = (req, res, next) => {
  try {
    // Get token from header
    const authHeader = req.headers.authorization;

    if (!authHeader || !authHeader.startsWith('Bearer ')) {
      return res.status(401).json({
        error: 'No token provided'
      });
    }

    const token = authHeader.substring(7); // Remove "Bearer "

    // Validate token
    const decoded = authService.validateToken(token);

    if (!decoded) {
      return res.status(401).json({
        error: 'Invalid token'
      });
    }

    // Attach user to request
    req.user = decoded;
    next();
  } catch (error) {
    res.status(500).json({
      error: error.message
    });
  }
};

/**
 * Check if user has required role
 * @param {string|string[]} roles - Required role(s)
 */
const authorize = (roles) => {
  return (req, res, next) => {
    const userRole = req.user?.role;

    const allowedRoles = Array.isArray(roles) ? roles : [roles];

    if (!allowedRoles.includes(userRole)) {
      return res.status(403).json({
        error: 'Insufficient permissions'
      });
    }

    next();
  };
};

module.exports = {
  authenticate,
  authorize
};
EOF
```

---

**Commiter :**

```bash
git add backend/src/auth/
git commit -m "feat(auth): implement JWT authentication system

- Add AuthService with token generation and password hashing
- Create AuthController for login and registration
- Implement authentication and authorization middleware
- Add comprehensive JSDoc documentation

TODO: Integrate with database
TODO: Add unit tests"
```

---

**Pousser :**

```bash
git push -u origin feature/user-authentication
```

---

**Créer la Pull Request sur GitHub :**

1. **New pull request**
2. **Compare** : `feature/user-authentication`
3. **Create pull request**

---

**Remplir le template :**

```markdown
## [LISTE] Description

Implement a complete JWT-based authentication system for the TaskMaster Pro backend.

### Features Added:
- **AuthService**: Token generation, password hashing, token validation
- **AuthController**: Login and registration endpoints
- **Auth Middleware**: Route protection and role-based authorization
- Comprehensive error handling
- JSDoc documentation for all methods

### Security Measures:
- JWT tokens with configurable expiration
- Bcrypt password hashing with salt rounds
- Role-based access control
- Bearer token authentication

## [OBJECTIF] Type de changement

- [x] * New feature (nouvelle fonctionnalité)

## [LIEN] Issue liée

Closes #12 (assuming issue exists)

## [CAMERA_WITH_FLASH] Screenshots (si applicable)

N/A - Backend API

## [TEST] Comment tester

```bash
# Install dependencies
npm install jsonwebtoken bcryptjs

# Start server
npm run dev

# Test registration
curl -X POST http://localhost:3000/api/auth/register \
  -H "Content-Type: application/json" \
  -d '{
    "email": "test@example.com",
    "password": "securePassword123",
    "name": "Test User"
  }'

# Test login
curl -X POST http://localhost:3000/api/auth/login \
  -H "Content-Type: application/json" \
  -d '{
    "email": "test@example.com",
    "password": "securePassword123"
  }'

# Test protected route (with token)
curl -X GET http://localhost:3000/api/protected \
  -H "Authorization: Bearer YOUR_TOKEN_HERE"
```

## [OK] Checklist

- [x] Mon code suit les conventions de style du projet
- [x] J'ai effectué une auto-review de mon code
- [x] J'ai commenté mon code là où c'est nécessaire
- [x] J'ai mis à jour la documentation
- [x] Mes changements ne génèrent pas de nouveaux warnings
- [ ] J'ai ajouté des tests qui prouvent que ma correction fonctionne (TODO)
- [ ] Les tests unitaires passent localement (TODO: write tests)
- [x] Toutes les dépendances sont à jour

## [DOCS] Contexte additionnel

This is the foundation of our authentication system. Future PRs will add:
- Database integration (User model)
- Email verification
- Password reset functionality
- Refresh tokens
- Rate limiting for login attempts

## [SYNC] Migrations nécessaires

- [ ] N/A
- [x] Variables d'environnement à ajouter
  - `JWT_SECRET` - Secret key for JWT signing (must be strong random string)
  
```bash
# Add to .env file
JWT_SECRET=your-super-secret-jwt-key-change-this-in-production
```

## [MERCI] Reviewers

@alice @diana

Looking forward to your feedback, especially on security aspects!
```

---

**Créer la PR.**

---

### ÉTAPE 4 : Alice fait une code review professionnelle

**Pourquoi le code review est important ?**

| Sans review | Avec review |
|-------------|-------------|
| [X] Bugs passent en production | [OK] Bugs détectés tôt |
| [X] Code de mauvaise qualité | [OK] Standards maintenus |
| [X] Pas de partage de connaissance | [OK] Équipe apprend |
| [X] Dépendance à une personne | [OK] Connaissance distribuée |
| [X] Décisions techniques non challengées | [OK] Meilleures solutions |

---

**Alice accède à la PR de Bob :**

1. **Pull requests** -> Cliquer sur la PR de Bob
2. **Files changed**

---

**Commentaire général (conversation) :**

Alice clique sur **Review changes** -> **Comment** :

```markdown
Great work on the authentication system, Bob! [BIEN]

The overall structure is solid and well-documented. I have a few suggestions to improve security and error handling.

Please address the inline comments below.
```

---

**Commentaires inline (sur des lignes spécifiques) :**

**Ligne 15 de `auth.service.js` :**

```javascript
expiresIn: '24h'
```

**Alice clique sur le `+` à côté de cette ligne et commente :**

```markdown
**[IDEE] Suggestion de sécurité**

24 heures est un peu long pour un token JWT. Pour une meilleure sécurité, je recommande:
- Access token: 15 minutes
- Refresh token: 7 jours

Qu'en penses-tu ?

**Option 1:** Token court + refresh token
**Option 2:** Token de 1 heure avec renouvellement automatique

Référence: [OWASP JWT Cheat Sheet](https://cheatsecure.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html)
```

---

**Ligne 27 de `auth.service.js` :**

```javascript
const salt = await bcrypt.genSalt(10);
```

**Alice commente :**

```markdown
[OK] Bon choix d'utiliser 10 rounds de salt.

**Note:** En 2024, OWASP recommande 12 rounds pour une meilleure sécurité, mais 10 est un bon compromis performance/sécurité.

Si on veut rendre ça configurable:

```suggestion
const salt = await bcrypt.genSalt(parseInt(process.env.BCRYPT_ROUNDS) || 10);
```

Qu'en penses-tu ?
```

**Note : ````suggestion` crée une suggestion que Bob peut accepter d'un clic !**

---

**Ligne 52 de `auth.controller.js` :**

```javascript
if (!isValid) {
  return res.status(401).json({
    error: 'Invalid credentials'
  });
}
```

**Alice commente :**

```markdown
[SECURISE] **Bonne pratique sécurité**

C'est bien d'utiliser un message générique "Invalid credentials" plutôt que "Wrong password" ou "User not found".

Cela empêche l'énumération d'emails (attaquant ne peut pas savoir si l'email existe).

[OK] Approuvé !
```

---

**Ligne 89 de `auth.controller.js` :**

```javascript
if (!email || !password || !name) {
  return res.status(400).json({
    error: 'Missing required fields'
  });
}
```

**Alice commente :**

```markdown
[NOTE] **Validation d'input**

La validation basique est là, mais on pourrait améliorer:

1. **Format email**: Vérifier que c'est un email valide
2. **Force du password**: Minimum 8 caractères, complexité
3. **Sanitization**: Prévenir XSS

Je suggère d'utiliser une librairie comme `joi` ou `express-validator`.

Exemple avec express-validator:

```javascript
const { body, validationResult } = require('express-validator');

// Middleware de validation
const validateRegistration = [
  body('email').isEmail().normalizeEmail(),
  body('password').isLength({ min: 8 }).matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/),
  body('name').trim().isLength({ min: 2, max: 50 })
];

// Dans la route
router.post('/register', validateRegistration, authController.register);
```

Qu'en penses-tu ? On peut faire ça dans cette PR ou créer une PR séparée.
```

---

**Ligne 14 de `auth.middleware.js` :**

```javascript
if (!authHeader || !authHeader.startsWith('Bearer ')) {
```

**Alice commente :**

```markdown
[OK] Bonne vérification du format Bearer.

**Question:** Que se passe-t-il si quelqu'un envoie:
```
Authorization: bearer token  (minuscule)
```

Le code actuel rejettera. Est-ce intentionnel ou devrait-on être case-insensitive ?

```suggestion
if (!authHeader || !authHeader.toLowerCase().startsWith('bearer ')) {
```

Réflexion ?
```

---

**Ligne 58 de `auth.middleware.js` :**

```javascript
if (!allowedRoles.includes(userRole)) {
```

**Alice commente :**

```markdown
[ATTENTION] **Potentiel bug**

Si `req.user` est undefined (cas impossible normalement, mais soyons prudents), cela va crasher.

```suggestion
if (!userRole || !allowedRoles.includes(userRole)) {
```

Défense en profondeur [SECURITE]
```

---

**Alice termine sa review :**

**Review changes** -> **Request changes** :

```markdown
## Summary

Excellent foundation for our authentication system! [BRAVO]

Code is well-structured and documented. I've identified a few areas for improvement:

### Must Fix (Blocking)
1. Input validation for registration (email format, password strength)
2. Null check in authorize middleware

### Should Consider (Non-blocking)
1. Shorter token expiration + refresh token
2. Configurable bcrypt rounds
3. Case-insensitive Bearer check

### Approved [OK]
- Password hashing implementation
- Generic error messages (security)
- JSDoc documentation

Please address the "Must Fix" items, then I'll re-review.

Great work overall! [CLAPPING_HANDS_SIGN]
```

**Cliquer "Submit review".**

---

### ÉTAPE 5 : Bob répond et modifie

**Bob voit les commentaires sur GitHub.**

**Bob répond aux commentaires :**

**Sur le commentaire des 24h :**

```markdown
@alice Good point! I'll implement refresh tokens in a follow-up PR to keep this one focused. 

For now, I'll reduce to 1 hour. Does that sound good?
```

---

**Sur la validation :**

```markdown
@alice Absolutely agree! I'll add express-validator in this PR since it's a security concern.

Adding now...
```

---

**Bob apporte les modifications :**

```bash
cd ~/bob-workspace/taskmaster-pro
git switch feature/user-authentication
```

---

**1. Réduire l'expiration du token :**

```bash
nano backend/src/auth/auth.service.js
```

**Modifier :**

```javascript
return jwt.sign(payload, process.env.JWT_SECRET, {
  expiresIn: '1h'  // Réduit de 24h à 1h
});
```

---

**2. Rendre bcrypt configurable :**

```javascript
async hashPassword(password) {
  const rounds = parseInt(process.env.BCRYPT_ROUNDS) || 10;
  const salt = await bcrypt.genSalt(rounds);
  return bcrypt.hash(password, salt);
}
```

---

**3. Ajouter la validation :**

```bash
cat > backend/src/auth/auth.validator.js << 'EOF'
/**
 * Authentication Validators
 * Input validation using express-validator
 * 
 * @author Bob Smith
 */

const { body } = require('express-validator');

const validateRegistration = [
  body('email')
    .isEmail()
    .withMessage('Invalid email format')
    .normalizeEmail(),
  
  body('password')
    .isLength({ min: 8 })
    .withMessage('Password must be at least 8 characters')
    .matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)/)
    .withMessage('Password must contain uppercase, lowercase, and number'),
  
  body('name')
    .trim()
    .isLength({ min: 2, max: 50 })
    .withMessage('Name must be between 2 and 50 characters')
];

const validateLogin = [
  body('email')
    .isEmail()
    .withMessage('Invalid email format')
    .normalizeEmail(),
  
  body('password')
    .notEmpty()
    .withMessage('Password is required')
];

module.exports = {
  validateRegistration,
  validateLogin
};
EOF
```

---

**4. Corriger le middleware :**

```bash
nano backend/src/auth/auth.middleware.js
```

**Modifier la vérification Bearer (case-insensitive) :**

```javascript
if (!authHeader || !authHeader.toLowerCase().startsWith('bearer ')) {
  // ...
}
```

**Corriger authorize :**

```javascript
const authorize = (roles) => {
  return (req, res, next) => {
    const userRole = req.user?.role;

    if (!userRole) {
      return res.status(401).json({
        error: 'User not authenticated'
      });
    }

    const allowedRoles = Array.isArray(roles) ? roles : [roles];

    if (!allowedRoles.includes(userRole)) {
      return res.status(403).json({
        error: 'Insufficient permissions'
      });
    }

    next();
  };
};
```

---

**Commiter les changements :**

```bash
git add backend/src/auth/
git commit -m "fix(auth): address code review feedback

- Reduce token expiration from 24h to 1h
- Make bcrypt rounds configurable via env
- Add comprehensive input validation with express-validator
- Fix case-insensitive Bearer token check
- Add null check in authorize middleware

Addresses feedback from @alice in PR review"
```

---

**Pousser :**

```bash
git push origin feature/user-authentication
```

---

**La PR est automatiquement mise à jour !**

---

**Bob répond sur GitHub :**

**Dans la conversation de la PR :**

```markdown
@alice I've addressed all the feedback:

[OK] Token expiration reduced to 1h
[OK] Bcrypt rounds now configurable
[OK] Added express-validator for input validation
[OK] Fixed case-insensitive Bearer check
[OK] Added null check in authorize

Refresh token implementation will be in a separate PR (#TBD).

Ready for re-review! [RAPIDE]
```

---

### ÉTAPE 6 : Alice re-review et approuve

**Alice voit le nouveau commit dans la PR.**

**Alice vérifie les changements :**

1. **Files changed**
2. Voir le nouveau commit
3. Vérifier que les commentaires sont adressés

---

**Alice résout les conversations :**

Pour chaque commentaire adressé, Alice clique **Resolve conversation**.

---

**Alice approuve :**

**Review changes** -> **Approve** :

```markdown
Perfect! [BRAVO]

All concerns addressed. Code looks great now.

The input validation is comprehensive and the configurable bcrypt rounds is a nice touch.

Looking forward to the refresh token PR!

Approved for merge. [OK]
```

**Submit review.**

---

**Alice merge la PR :**

1. **Squash and merge** (pour historique propre)
2. **Confirm squash and merge**

**Message de commit (personnalisé) :**

```
feat(auth): implement JWT authentication system (#23)

Complete JWT-based authentication with:
- Token generation and validation
- Password hashing with bcrypt
- Login and registration endpoints
- Authentication and authorization middleware
- Comprehensive input validation
- Configurable security parameters

Co-authored-by: Bob Smith <bob@taskmaster.com>
```

3. **Delete branch** `feature/user-authentication`

---

**[OK] PR mergée avec succès !**

---

### ÉTAPE 7 : Charlie crée une PR avec suggestions GitHub

**Charlie travaille sur le dashboard :**

```bash
cd ~/charlie-workspace/taskmaster-pro
git switch main
git pull origin main

git switch -c feature/dashboard-ui
```

---

**Créer un composant Dashboard :**

```bash
cat > frontend/src/pages/Dashboard.jsx << 'EOF'
import React, { useState, useEffect } from 'react';
import './Dashboard.css';

function Dashboard() {
  const [tasks, setTasks] = useState([]);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetchTasks();
  }, []);

  const fetchTasks = async () => {
    try {
      const response = await fetch('/api/tasks', {
        headers: {
          'Authorization': `Bearer ${localStorage.getItem('token')}`
        }
      });
      const data = await response.json();
      setTasks(data.tasks);
      setLoading(false);
    } catch (error) {
      console.error('Failed to fetch tasks:', error);
      setLoading(false);
    }
  };

  if (loading) {
    return <div className="loading">Loading...</div>;
  }

  return (
    <div className="dashboard">
      <header className="dashboard-header">
        <h1>My Dashboard</h1>
        <button onClick={() => window.location.href = '/tasks/new'}>
          New Task
        </button>
      </header>

      <div className="dashboard-stats">
        <div className="stat-card">
          <h3>Total Tasks</h3>
          <p className="stat-number">{tasks.length}</p>
        </div>
        <div className="stat-card">
          <h3>Completed</h3>
          <p className="stat-number">
            {tasks.filter(t => t.status === 'done').length}
          </p>
        </div>
        <div className="stat-card">
          <h3>In Progress</h3>
          <p className="stat-number">
            {tasks.filter(t => t.status === 'in-progress').length}
          </p>
        </div>
      </div>

      <div className="task-list">
        <h2>Recent Tasks</h2>
        {tasks.length === 0 ? (
          <p>No tasks yet. Create your first task!</p>
        ) : (
          <ul>
            {tasks.map(task => (
              <li key={task.id} className={`task-item ${task.status}`}>
                <h3>{task.title}</h3>
                <p>{task.description}</p>
                <span className="task-status">{task.status}</span>
              </li>
            ))}
          </ul>
        )}
      </div>
    </div>
  );
}

export default Dashboard;
EOF
```

---

**Créer le CSS :**

```bash
cat > frontend/src/pages/Dashboard.css << 'EOF'
.dashboard {
  padding: 2rem;
  max-width: 1200px;
  margin: 0 auto;
}

.dashboard-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 2rem;
}

.dashboard-header button {
  background: #667eea;
  color: white;
  border: none;
  padding: 0.75rem 1.5rem;
  border-radius: 4px;
  cursor: pointer;
}

.dashboard-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 1.5rem;
  margin-bottom: 3rem;
}

.stat-card {
  background: white;
  padding: 1.5rem;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.stat-card h3 {
  margin: 0 0 0.5rem 0;
  color: #666;
  font-size: 0.9rem;
  text-transform: uppercase;
}

.stat-number {
  font-size: 2.5rem;
  font-weight: bold;
  color: #667eea;
  margin: 0;
}

.task-list {
  background: white;
  padding: 2rem;
  border-radius: 8px;
  box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

.task-list ul {
  list-style: none;
  padding: 0;
}

.task-item {
  padding: 1rem;
  border-left: 4px solid #ddd;
  margin-bottom: 1rem;
}

.task-item.done {
  border-left-color: #4caf50;
}

.task-item.in-progress {
  border-left-color: #ff9800;
}

.task-status {
  display: inline-block;
  padding: 0.25rem 0.75rem;
  border-radius: 12px;
  font-size: 0.8rem;
  background: #eee;
  color: #666;
}

.loading {
  text-align: center;
  padding: 3rem;
  font-size: 1.2rem;
  color: #666;
}
EOF
```

---

**Commiter et pousher :**

```bash
git add frontend/src/pages/
git commit -m "feat(ui): add dashboard page with task statistics"
git push -u origin feature/dashboard-ui
```

---

**Créer la PR sur GitHub.**

---

**Diana fait la review et utilise les suggestions :**

**Diana commente sur plusieurs lignes :**

**Ligne 23 de `Dashboard.jsx` :**

```javascript
console.error('Failed to fetch tasks:', error);
```

**Diana utilise la fonctionnalité "suggestion" :**

Cliquer sur le `+` -> **Insert a suggestion**

**GitHub génère automatiquement :**

````markdown
```suggestion
      console.error('Failed to fetch tasks:', error);
      // TODO: Show user-friendly error message
      alert('Failed to load tasks. Please try again.');
```
````

**Diana ajoute un commentaire :**

```markdown
**[BUG] User Experience**

Les erreurs devraient être affichées à l'utilisateur, pas juste dans la console.

J'ai ajouté une suggestion avec un `alert` basique, mais idéalement on devrait utiliser un toast/notification system.
```

---

**Ligne 35 de `Dashboard.jsx` :**

```javascript
<button onClick={() => window.location.href = '/tasks/new'}>
```

**Diana suggère :**

````markdown
```suggestion
        <button onClick={() => navigate('/tasks/new')}>
```

**[ATTENTION] React Router**

Utiliser `window.location.href` force un rechargement complet de la page.

On devrait utiliser React Router pour la navigation SPA :

```javascript
import { useNavigate } from 'react-router-dom';

function Dashboard() {
  const navigate = useNavigate();
  // ...
```

Qu'en penses-tu ?
````

---

**Charlie voit les suggestions et peut les accepter d'un clic :**

1. Voir le commentaire avec suggestion
2. Cliquer **Commit suggestion**
3. GitHub crée automatiquement un commit avec la modification !

---

**Charlie accepte la première suggestion (error handling) :**

**Commit suggestion** -> Message:

```
Apply suggestion from code review

Add user-friendly error message

Co-authored-by: Diana Prince <diana@taskmaster.com>
```

**Commit !**

---

**Pour la deuxième suggestion (React Router), Charlie répond :**

```markdown
@diana Good catch! But we haven't set up React Router yet in this project.

I'll create a separate issue to set up routing properly, and we can update this in a follow-up PR.

For now, I'll leave the `window.location.href` and add a TODO comment. Sound good?
```

---

**Charlie ajoute le TODO :**

```bash
nano frontend/src/pages/Dashboard.jsx
```

```javascript
{/* TODO: Replace with React Router navigate once routing is set up (see issue #XX) */}
<button onClick={() => window.location.href = '/tasks/new'}>
  New Task
</button>
```

**Commit et push.**

---

**Diana répond :**

```markdown
@charlie Perfect! That works for me. 

Created issue #25 for React Router setup.

Otherwise, dashboard looks great! Approving. [OK]
```

**Approve** et **Submit review**.

---

**Charlie merge la PR.**

---

### ÉTAPE 8 : Mettre en place GitHub Actions (CI basique)

**Pourquoi CI/CD ?**

| Sans CI | Avec CI |
|---------|---------|
| [X] Tests manuels | [OK] Tests automatiques |
| [X] Oublis de tests | [OK] Tests obligatoires |
| [X] Code cassé mergé | [OK] Détection avant merge |
| [X] Incohérences | [OK] Standards appliqués |

---

**Emma crée un workflow GitHub Actions :**

```bash
cd ~/taskmaster-pro
git switch main
git pull origin main

git switch -c ci/setup-github-actions
```

---

**Créer le dossier workflows :**

```bash
mkdir -p .github/workflows
```

---

**Créer le workflow de CI :**

```bash
cat > .github/workflows/ci.yml << 'EOF'
name: CI

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

jobs:
  lint:
    name: Lint Code
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
        working-directory: ./backend
      
      - name: Run ESLint
        run: npm run lint || echo "Lint check - add ESLint config in future"
        working-directory: ./backend

  test-backend:
    name: Backend Tests
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
      
      - name: Install dependencies
        run: npm ci
        working-directory: ./backend
      
      - name: Run tests
        run: npm test || echo "Tests - add Jest config in future"
        working-directory: ./backend
      
      - name: Generate coverage
        run: npm run test:coverage || echo "Coverage - configure later"
        working-directory: ./backend

  test-frontend:
    name: Frontend Tests
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
      
      - name: Install dependencies
        run: npm ci || echo "Frontend deps - will add package.json"
        working-directory: ./frontend
      
      - name: Run tests
        run: npm test || echo "Frontend tests - will add later"
        working-directory: ./frontend
      
      - name: Build
        run: npm run build || echo "Build - will configure later"
        working-directory: ./frontend

  security:
    name: Security Audit
    runs-on: ubuntu-latest
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18'
      
      - name: Run security audit
        run: |
          cd backend && npm audit --audit-level=moderate || echo "Audit - will fix vulnerabilities"
          cd ../frontend && npm audit --audit-level=moderate || echo "Audit - will configure"
EOF
```

---

**Explication du workflow :**

**`on: push/pull_request`** :
- Se déclenche sur push vers `main`
- Se déclenche sur toute PR vers `main`

**`jobs:`** :
- `lint` : Vérifie le style de code
- `test-backend` : Tests backend
- `test-frontend` : Tests frontend
- `security` : Audit de sécurité

**`runs-on: ubuntu-latest`** :
- Machine virtuelle Ubuntu

**`steps:`** :
- `actions/checkout@v3` : Clone le repo
- `actions/setup-node@v3` : Installe Node.js
- `npm ci` : Installe les dépendances (plus rapide que `npm install`)
- `npm test` : Lance les tests

---

**Créer package.json backend (si manquant) :**

```bash
cat > backend/package.json << 'EOF'
{
  "name": "taskmaster-backend",
  "version": "1.0.0",
  "description": "TaskMaster Pro Backend API",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js",
    "dev": "nodemon src/index.js",
    "test": "jest",
    "test:coverage": "jest --coverage",
    "lint": "eslint src/"
  },
  "keywords": ["api", "tasks", "express"],
  "author": "TaskMaster Team",
  "license": "MIT",
  "dependencies": {
    "express": "^4.18.2",
    "jsonwebtoken": "^9.0.2",
    "bcryptjs": "^2.4.3",
    "express-validator": "^7.0.1"
  },
  "devDependencies": {
    "jest": "^29.7.0",
    "nodemon": "^3.0.1",
    "eslint": "^8.54.0"
  }
}
EOF
```

---

**Commiter :**

```bash
git add .github/workflows/ci.yml backend/package.json
git commit -m "ci: setup GitHub Actions workflow

- Add CI pipeline with 4 jobs: lint, test-backend, test-frontend, security
- Configure automatic testing on push and PR
- Add backend package.json with test scripts
- Include security audit with npm audit

This will run on all PRs to ensure code quality before merge."
```

---

**Pousser et créer PR :**

```bash
git push -u origin ci/setup-github-actions
```

**Créer la PR sur GitHub.**

---

**GitHub Actions se lance automatiquement ! [BRAVO]**

**Sur la PR, tu verras :**

```
[OK] lint / Lint Code (pull_request)
[OK] test-backend / Backend Tests (pull_request)
[ATTENTION] test-frontend / Frontend Tests (pull_request) - Expected to fail
[OK] security / Security Audit (pull_request)
```

---

**Maintenant, activer le status check sur la branche protégée :**

1. **Settings** -> **Branches** -> **main** -> **Edit**
2. **[x] Require status checks to pass before merging**
3. Chercher et sélectionner :
   - `lint`
   - `test-backend`
   - `security`
4. **Save changes**

---

**[OK] Maintenant les PRs ne peuvent être mergées que si les tests passent !**

---

### ÉTAPE 9 : Workflow complet de PR avec CI

**Créons une dernière PR pour voir tout le workflow :**

**Bob ajoute des tests :**

```bash
cd ~/bob-workspace/taskmaster-pro
git switch main
git pull origin main

git switch -c test/auth-service-tests
```

---

**Créer les tests :**

```bash
cat > backend/src/auth/auth.service.test.js << 'EOF'
/**
 * AuthService Tests
 * @author Bob Smith
 */

const authService = require('./auth.service');
const jwt = require('jsonwebtoken');

describe('AuthService', () => {
  beforeAll(() => {
    process.env.JWT_SECRET = 'test-secret-key';
  });

  describe('generateToken', () => {
    it('should generate a valid JWT token', () => {
      const user = {
        id: 1,
        email: 'test@example.com',
        role: 'user'
      };

      const token = authService.generateToken(user);

      expect(token).toBeDefined();
      expect(typeof token).toBe('string');
      
      // Verify token
      const decoded = jwt.verify(token, process.env.JWT_SECRET);
      expect(decoded.id).toBe(user.id);
      expect(decoded.email).toBe(user.email);
      expect(decoded.role).toBe(user.role);
    });

    it('should include expiration time', () => {
      const user = { id: 1, email: 'test@example.com', role: 'user' };
      const token = authService.generateToken(user);
      const decoded = jwt.verify(token, process.env.JWT_SECRET);

      expect(decoded.exp).toBeDefined();
      expect(decoded.exp).toBeGreaterThan(Date.now() / 1000);
    });
  });

  describe('hashPassword', () => {
    it('should hash a password', async () => {
      const password = 'mySecurePassword123';
      const hashed = await authService.hashPassword(password);

      expect(hashed).toBeDefined();
      expect(hashed).not.toBe(password);
      expect(hashed.length).toBeGreaterThan(20);
    });

    it('should generate different hashes for same password', async () => {
      const password = 'samePassword';
      const hash1 = await authService.hashPassword(password);
      const hash2 = await authService.hashPassword(password);

      expect(hash1).not.toBe(hash2); // Different salts
    });
  });

  describe('comparePassword', () => {
    it('should return true for correct password', async () => {
      const password = 'correctPassword';
      const hashed = await authService.hashPassword(password);
      const isMatch = await authService.comparePassword(password, hashed);

      expect(isMatch).toBe(true);
    });

    it('should return false for incorrect password', async () => {
      const hashed = await authService.hashPassword('correctPassword');
      const isMatch = await authService.comparePassword('wrongPassword', hashed);

      expect(isMatch).toBe(false);
    });
  });

  describe('validateToken', () => {
    it('should validate a valid token', () => {
      const user = { id: 1, email: 'test@example.com', role: 'user' };
      const token = authService.generateToken(user);
      const decoded = authService.validateToken(token);

      expect(decoded).not.toBeNull();
      expect(decoded.id).toBe(user.id);
    });

    it('should return null for invalid token', () => {
      const decoded = authService.validateToken('invalid.token.here');

      expect(decoded).toBeNull();
    });

    it('should return null for expired token', () => {
      // Create expired token
      const expiredToken = jwt.sign(
        { id: 1 },
        process.env.JWT_SECRET,
        { expiresIn: '-1h' } // Already expired
      );

      const decoded = authService.validateToken(expiredToken);

      expect(decoded).toBeNull();
    });
  });
});
EOF
```

---

**Configurer Jest :**

```bash
cat > backend/jest.config.js << 'EOF'
module.exports = {
  testEnvironment: 'node',
  coverageDirectory: 'coverage',
  collectCoverageFrom: [
    'src/**/*.js',
    '!src/**/*.test.js'
  ],
  testMatch: [
    '**/*.test.js'
  ]
};
EOF
```

---

**Commiter et pousher :**

```bash
git add backend/src/auth/auth.service.test.js backend/jest.config.js
git commit -m "test(auth): add comprehensive AuthService unit tests

- Test token generation and validation
- Test password hashing and comparison
- Test edge cases (expired tokens, invalid inputs)
- Configure Jest
- Achieve 100% coverage for AuthService

All 10 tests passing [OK]"
```

```bash
git push -u origin test/auth-service-tests
```

---

**Créer la PR.**

**GitHub Actions se lance automatiquement et cette fois les tests passent vraiment ! [OK]**

---

**Alice review et approuve rapidement (tests verts) :**

```markdown
Excellent test coverage! [BRAVO]

All tests passing, good edge case coverage.

Approved for merge! [OK]
```

---

**Merge !**

---

### [OK] TESTS DE VALIDATION

**1. Protection de branche active**

- [ ] Tentative de push direct sur main échoue
- [ ] Force l'utilisation de PRs

---

**2. Template de PR fonctionne**

- [ ] Nouvelle PR pré-remplie avec le template
- [ ] Sections présentes

---

**3. Code review effectué**

- [ ] Au moins 5 commentaires constructifs
- [ ] Utilisation de suggestions
- [ ] Approbation requise

---

**4. GitHub Actions configuré**

- [ ] Workflow CI créé
- [ ] Tests lancés automatiquement sur PR
- [ ] Status checks visibles

---

**5. Workflow complet**

- [ ] Branche -> Commit -> Push -> PR -> Review -> CI -> Merge
- [ ] Historique propre (squash merge)

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : "Required reviews not met"

**Symptôme :**

Impossible de merger, même si approuvé.

**Cause :** Settings mal configurés

**Solution :**

Vérifier **Settings** -> **Branches** -> **Number of required approvals**

---

#### Erreur 2 : Tests GitHub Actions échouent

**Symptôme :**

[X] Red checks sur la PR

**Diagnostic :**

Cliquer sur **Details** pour voir les logs d'erreur.

**Solutions courantes :**

- Dépendances manquantes : Vérifier `package.json`
- Tests vraiment cassés : Corriger le code
- Configuration incorrecte : Vérifier `ci.yml`

---

#### Erreur 3 : "This branch is out-of-date with the base branch"

**Symptôme :**

PR mergeable mais "Update branch" suggéré.

**Solution :**

```bash
git switch feature-branch
git fetch origin
git merge origin/main
# Résoudre conflits si nécessaire
git push
```

**Ou sur GitHub : Bouton "Update branch"**

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Pull Request parfaite**
```markdown
- Description claire
- Type de changement
- Instructions de test
- Checklist complète
- Screenshots si UI
- Liens vers issues
```

**2. Code Review de qualité**
- Constructif, pas destructif
- Suggestions concrètes
- Explications (pourquoi)
- Bienveillant
- Équilibre compliments/critiques

**3. Protection de branches**
- Force les PRs
- Exige reviews
- Nécessite tests verts
- Résolution de conversations

**4. GitHub Actions**
- Tests automatiques
- Lint/Format
- Security audit
- Build verification

**5. Stratégies de merge**

| Méthode | Historique | Usage |
|---------|------------|-------|
| **Merge commit** | Non-linéaire | Préserve historique complet |
| **Squash** | Linéaire, propre | * Recommandé (1 commit par feature) |
| **Rebase** | Linéaire | Features longues |

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. CODEOWNERS**

```bash
cat > .github/CODEOWNERS << 'EOF'
# Global owners
* @alice

# Backend owned by Bob
/backend/ @bob @alice

# Frontend owned by Charlie
/frontend/ @charlie @alice

# DevOps owned by Diana
/.github/ @diana @alice
/docker/ @diana @alice

# Tests must be reviewed by Emma
**/*.test.js @emma @alice
EOF
```

**Impact :** Assignation automatique des reviewers selon les fichiers modifiés.

---

**2. Issue Templates**

```bash
cat > .github/ISSUE_TEMPLATE/bug_report.md << 'EOF'
---
name: Bug Report
about: Report a bug
title: '[BUG] '
labels: bug
assignees: ''
---

## [BUG] Bug Description

<!-- Clear description of the bug -->

## [SYNC] Steps to Reproduce

1. 
2. 
3. 

## [OK] Expected Behavior

<!-- What should happen -->

## [X] Actual Behavior

<!-- What actually happens -->

## [CAMERA_WITH_FLASH] Screenshots

<!-- If applicable -->

## [MONDE] Environment

- OS: 
- Browser: 
- Version: 
EOF
```

---

**3. Automated PR labeling**

```yaml
# .github/workflows/label.yml
name: Label PRs

on:
  pull_request:
    types: [opened, edited]

jobs:
  label:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/labeler@v4
```

---

**4. Dependabot**

```yaml
# .github/dependabot.yml
version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/backend"
    schedule:
      interval: "weekly"
    open-pull-requests-limit: 5
```

**Crée automatiquement des PRs pour mettre à jour les dépendances !**

---

**5. PR size labeling**

Ajouter automatiquement des labels `size/XS`, `size/L` selon les lignes modifiées.

---

## [COURS] CONCLUSION DE L'EXERCICE 5

**[OK] Félicitations ! Tu maîtrises les Pull Requests professionnelles !**

**Ce que tu as appris :**
- Créer des PRs de qualité
- Faire des code reviews constructives
- Utiliser suggestions GitHub
- Protéger les branches
- GitHub Actions (CI/CD basique)
- Templates de PR
- Workflow d'équipe complet

**Compétences acquises :**
- [OK] Pull Requests (niveau professionnel)
- [OK] Code Review (niveau expert)
- [OK] Branch Protection
- [OK] GitHub Actions (CI basique)
- [OK] Collaboration d'équipe
- [OK] Quality gates

**Temps moyen de réalisation :** 3-4 heures

**Prochaine étape :** Exercice 6 - Git Flow (Stratégies de branching, releases, hotfixes) ! [WATER_WAVE]

---

(Continue avec Exercice 6 ? Il reste 5 exercices pour compléter les 10.)

# [ROUGE] EXERCICE 6 : GIT FLOW WORKFLOW

## [LISTE] ÉNONCÉ

### Contexte professionnel

TaskMaster Pro est prêt pour sa **première release en production** ! [RAPIDE]

**Alice** (Lead Developer) décide d'adopter **Git Flow**, un modèle de branching robuste pour gérer :
- Les développements en cours (develop)
- Les versions stables (main)
- Les releases (release/*)
- Les hotfixes urgents (hotfix/*)
- Le versioning sémantique (v1.0.0, v1.1.0, etc.)

**Situation actuelle :**
- Code en développement continu
- Besoin de versions stables en production
- Corrections urgentes parfois nécessaires
- Multiples features en parallèle
- Releases planifiées tous les 2 sprints

**Aujourd'hui, l'équipe va :**
1. Mettre en place Git Flow
2. Créer la branche `develop`
3. Gérer des branches `feature/*`
4. Préparer une release `release/1.0.0`
5. Déployer en production (tag v1.0.0)
6. Gérer un hotfix urgent `hotfix/1.0.1`
7. Automatiser le changelog et versioning

### Objectifs

- Comprendre et appliquer Git Flow
- Créer et gérer develop, feature, release, hotfix
- Implémenter semantic versioning
- Générer des tags et releases GitHub
- Automatiser le changelog
- Workflow de release complet

### Livrables

- Branches `main` et `develop` configurées
- Au moins 3 features développées
- 1 release complète (v1.0.0)
- 1 hotfix (v1.0.1)
- Tags Git créés
- Changelog généré automatiquement
- Documentation du workflow

### Contraintes

- Git Flow strict (pas de raccourcis)
- Semantic versioning respecté
- Tags annotés pour chaque release
- Changelog à jour
- main = production SEULEMENT
- develop = intégration
- Temps estimé : 4-5 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :

- [OK] Comprendre Git Flow en profondeur
- [OK] Créer et gérer les 5 types de branches
- [OK] Préparer des releases professionnelles
- [OK] Appliquer semantic versioning (SemVer)
- [OK] Créer des tags Git annotés
- [OK] Publier des releases GitHub
- [OK] Gérer des hotfixes urgents
- [OK] Générer des changelogs automatiques
- [OK] Automatiser le versioning

---

## [DOCS] PRÉREQUIS

- Exercices 1 à 5 terminés
- Dépôt GitHub configuré
- Compréhension des branches et merges
- Familiarité avec les PRs

---

## [GUIDE] THÉORIE : Git Flow expliqué

### Qu'est-ce que Git Flow ?

**Git Flow** est un modèle de branching créé par **Vincent Driessen** en 2010.

**Philosophie :**
- Branches longues : `main` et `develop`
- Branches éphémères : `feature/*`, `release/*`, `hotfix/*`
- Séparation claire développement/production
- Processus de release structuré

---

### Les 5 types de branches

```
main (production) ────[BLACK_CIRCLE]────────────[BLACK_CIRCLE]────────────[BLACK_CIRCLE]───────
                       ^            ^            ^
                    release      release      hotfix
                       ^            ^            ^
develop ─────[BLACK_CIRCLE]────[BLACK_CIRCLE]────[BLACK_CIRCLE]────[BLACK_CIRCLE]───────[BLACK_CIRCLE]────[BLACK_CIRCLE]───────[BLACK_CIRCLE]────[BLACK_CIRCLE]──
            ╱    ╱          ╲      ╱
     feature  feature     feature
```

---

#### 1⃣ **main** (anciennement master)

**Rôle :** Code en production

**Règles :**
- [OK] Code 100% stable et testé
- [OK] Chaque commit = une version déployée
- [OK] Protégée (aucun commit direct)
- [OK] Taggée (v1.0.0, v1.1.0, etc.)

**Source des merges :**
- `release/*` -> `main` (nouvelles versions)
- `hotfix/*` -> `main` (corrections urgentes)

---

#### 2⃣ **develop**

**Rôle :** Intégration du développement

**Règles :**
- [OK] Code fonctionnel mais pas forcément stable
- [OK] Toutes les features fusionnées ici
- [OK] Base pour les nouvelles features
- [OK] Tests d'intégration

**Source des merges :**
- `feature/*` -> `develop` (nouvelles fonctionnalités)
- `release/*` -> `develop` (après release, pour sync)
- `hotfix/*` -> `develop` (après hotfix, pour sync)

---

#### 3⃣ **feature/\*** (éphémères)

**Rôle :** Développement de nouvelles fonctionnalités

**Nommage :** `feature/nom-de-la-feature`

**Exemples :**
- `feature/user-authentication`
- `feature/task-comments`
- `feature/email-notifications`

**Cycle de vie :**
```
1. Créée depuis develop
2. Développement
3. Merge vers develop (via PR)
4. Supprimée
```

**Règles :**
- [OK] Une feature = une branche
- [OK] Peut durer plusieurs jours/semaines
- [OK] Mergée SEULEMENT dans develop
- [OK] Jamais mergée directement dans main

---

#### 4⃣ **release/\*** (éphémères)

**Rôle :** Préparation d'une nouvelle version

**Nommage :** `release/X.Y.Z`

**Exemples :**
- `release/1.0.0`
- `release/2.3.0`

**Cycle de vie :**
```
1. Créée depuis develop (quand prêt pour release)
2. Bug fixes mineurs uniquement
3. Mise à jour de version, changelog
4. Merge vers main (production)
5. Merge vers develop (synchronisation)
6. Taggée (v1.0.0)
7. Supprimée
```

**Règles :**
- [OK] Corrections mineures seulement
- [OK] Pas de nouvelles features
- [OK] Freeze du code pour stabilisation
- [OK] Tests finaux
- [OK] Documentation mise à jour

---

#### 5⃣ **hotfix/\*** (éphémères)

**Rôle :** Correction urgente en production

**Nommage :** `hotfix/X.Y.Z`

**Exemples :**
- `hotfix/1.0.1` (correction du bug en v1.0.0)
- `hotfix/2.3.1`

**Cycle de vie :**
```
1. Créée depuis main (code en production)
2. Fix rapide du bug critique
3. Merge vers main (déploiement urgent)
4. Merge vers develop (synchronisation)
5. Taggée (v1.0.1)
6. Supprimée
```

**Règles :**
- [OK] Correction d'un seul bug
- [OK] Rapide (quelques heures max)
- [OK] Incrémente la version PATCH (1.0.0 -> 1.0.1)
- [OK] Bypass le processus de release normal

---

### Semantic Versioning (SemVer)

**Format :** `MAJOR.MINOR.PATCH`

**Exemple :** `v2.3.1`

| Position | Nom | Quand incrémenter | Exemple |
|----------|-----|-------------------|---------|
| **MAJOR** | Version majeure | Breaking changes (incompatibilité) | 1.0.0 -> 2.0.0 |
| **MINOR** | Version mineure | Nouvelles features (compatible) | 1.0.0 -> 1.1.0 |
| **PATCH** | Patch | Bug fixes (compatible) | 1.0.0 -> 1.0.1 |

**Exemples :**
- `1.0.0` -> Première release stable
- `1.1.0` -> Ajout de nouvelles features
- `1.1.1` -> Correction de bugs
- `2.0.0` -> Breaking change (API change, etc.)

**Pré-releases :**
- `1.0.0-alpha.1` -> Version alpha
- `1.0.0-beta.2` -> Version beta
- `1.0.0-rc.1` -> Release Candidate

---

### Workflow complet

```
Feature Development:
develop ─────[BLACK_CIRCLE]───────┐
              ╲       ╲
         feature/A  feature/B
              v       v
develop ─────[BLACK_CIRCLE]───────[BLACK_CIRCLE]───────

Release Preparation:
develop ─────[BLACK_CIRCLE]───────────────[BLACK_CIRCLE]───────
              ╲               ╱
           release/1.0.0 ────[BLACK_CIRCLE]
              v
main ────────[BLACK_CIRCLE]────────────────────
           v1.0.0

Hotfix:
main ────[BLACK_CIRCLE]───────┐──────[BLACK_CIRCLE]──────
       v1.0.0     ╲    v1.0.1
                hotfix/1.0.1
                   v
develop ──────────[BLACK_CIRCLE]──────────────
```

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Installer Git Flow (outil CLI)

**Git Flow** a un outil CLI qui facilite la gestion des branches.

**Installation :**

**Ubuntu/Debian :**
```bash
sudo apt-get install git-flow
```

**macOS :**
```bash
brew install git-flow
```

**Vérifier :**
```bash
git flow version
```

**Résultat :**
```
1.12.3 (AVH Edition)
```

---

**Alternative : Git Flow sans l'outil**

Si tu ne veux pas installer l'outil, tu peux faire tout manuellement avec `git branch` et `git merge`. On montrera les deux méthodes.

---

### ÉTAPE 2 : Initialiser Git Flow

```bash
cd ~/taskmaster-pro
git switch main
git pull origin main
```

---

**Initialiser Git Flow :**

```bash
git flow init
```

**Réponses aux questions :**

```
Which branch should be used for bringing forth production releases?
   - main
Branch name for production releases: [main] 
[BLACK_RIGHT-POINTING_TRIANGLE] main

Which branch should be used for integration of the "next release"?
   - develop
Branch name for "next release" development: [develop] 
[BLACK_RIGHT-POINTING_TRIANGLE] develop

How to name your supporting branch prefixes?
Feature branches? [feature/] 
[BLACK_RIGHT-POINTING_TRIANGLE] feature/

Bugfix branches? [bugfix/] 
[BLACK_RIGHT-POINTING_TRIANGLE] bugfix/

Release branches? [release/] 
[BLACK_RIGHT-POINTING_TRIANGLE] release/

Hotfix branches? [hotfix/] 
[BLACK_RIGHT-POINTING_TRIANGLE] hotfix/

Support branches? [support/] 
[BLACK_RIGHT-POINTING_TRIANGLE] support/

Version tag prefix? [] 
[BLACK_RIGHT-POINTING_TRIANGLE] v
```

---

**Résultat :**

```
Summary of actions:
- A new branch 'develop' was created, based on 'main'
- You are now on branch 'develop'
```

---

**Ce qui s'est passé :**

1. Branche `develop` créée depuis `main`
2. Basculement automatique vers `develop`
3. Configuration locale de Git Flow

---

**Vérifier :**

```bash
git branch
```

**Résultat :**

```
* develop
  main
```

---

**Pousser `develop` vers GitHub :**

```bash
git push -u origin develop
```

---

**Protéger la branche `develop` sur GitHub :**

1. **Settings** -> **Branches**
2. **Add branch protection rule**
3. **Branch name pattern** : `develop`
4. Activer :
   - [x] Require a pull request before merging
   - [x] Require approvals: 1
   - [x] Include administrators
5. **Create**

---

### ÉTAPE 3 : Développer des features

**Bob commence une nouvelle feature :**

```bash
cd ~/bob-workspace/taskmaster-pro
git switch main
git pull origin main
git switch develop  # ou git pull origin develop si existe
git pull origin develop
```

---

**Créer une branche feature avec Git Flow :**

```bash
git flow feature start task-priority
```

**Résultat :**

```
Switched to a new branch 'feature/task-priority'

Summary of actions:
- A new branch 'feature/task-priority' was created, based on 'develop'
- You are now on branch 'feature/task-priority'

Now, start committing on your feature. When done, use:
     git flow feature finish task-priority
```

---

**Équivalent manuel (sans git flow) :**

```bash
git switch develop
git switch -c feature/task-priority
```

---

**Bob développe la fonctionnalité :**

```bash
cat > backend/src/models/Task.js << 'EOF'
/**
 * Task Model
 * @author Bob Smith
 */

class Task {
  constructor(data) {
    this.id = data.id || null;
    this.title = data.title;
    this.description = data.description || '';
    this.status = data.status || 'todo';
    this.priority = data.priority || 'medium'; // NEW
    this.assignee = data.assignee || null;
    this.createdAt = data.createdAt || new Date();
    this.updatedAt = data.updatedAt || new Date();
  }

  /**
   * Validate priority value
   * @param {string} priority
   * @returns {boolean}
   */
  static isValidPriority(priority) {
    const validPriorities = ['low', 'medium', 'high', 'urgent'];
    return validPriorities.includes(priority);
  }

  /**
   * Get priority level (numeric)
   * @returns {number}
   */
  getPriorityLevel() {
    const levels = {
      low: 1,
      medium: 2,
      high: 3,
      urgent: 4
    };
    return levels[this.priority] || 2;
  }

  /**
   * Update task priority
   * @param {string} newPriority
   */
  setPriority(newPriority) {
    if (!Task.isValidPriority(newPriority)) {
      throw new Error(`Invalid priority: ${newPriority}`);
    }
    this.priority = newPriority;
    this.updatedAt = new Date();
  }

  /**
   * Convert to JSON
   */
  toJSON() {
    return {
      id: this.id,
      title: this.title,
      description: this.description,
      status: this.status,
      priority: this.priority,
      assignee: this.assignee,
      createdAt: this.createdAt,
      updatedAt: this.updatedAt
    };
  }
}

module.exports = Task;
EOF
```

---

**Mettre à jour le contrôleur :**

```bash
cat > backend/src/controllers/tasks.controller.js << 'EOF'
/**
 * Tasks Controller
 * @author Bob Smith
 */

const Task = require('../models/Task');

class TasksController {
  /**
   * Get all tasks, optionally filtered by priority
   * GET /api/tasks?priority=high
   */
  async getTasks(req, res) {
    try {
      const { priority } = req.query;

      // TODO: Fetch from database
      let tasks = [
        new Task({ id: 1, title: 'Setup project', priority: 'high' }),
        new Task({ id: 2, title: 'Write tests', priority: 'medium' }),
        new Task({ id: 3, title: 'Update docs', priority: 'low' })
      ];

      // Filter by priority if provided
      if (priority) {
        if (!Task.isValidPriority(priority)) {
          return res.status(400).json({
            error: `Invalid priority: ${priority}`
          });
        }
        tasks = tasks.filter(t => t.priority === priority);
      }

      // Sort by priority (urgent first)
      tasks.sort((a, b) => b.getPriorityLevel() - a.getPriorityLevel());

      res.json({ tasks: tasks.map(t => t.toJSON()) });
    } catch (error) {
      res.status(500).json({ error: error.message });
    }
  }

  /**
   * Create task with priority
   * POST /api/tasks
   */
  async createTask(req, res) {
    try {
      const { title, description, priority } = req.body;

      if (!title) {
        return res.status(400).json({ error: 'Title is required' });
      }

      if (priority && !Task.isValidPriority(priority)) {
        return res.status(400).json({ 
          error: `Invalid priority. Must be: low, medium, high, urgent` 
        });
      }

      const task = new Task({
        id: Date.now(),
        title,
        description,
        priority: priority || 'medium'
      });

      // TODO: Save to database

      res.status(201).json({ task: task.toJSON() });
    } catch (error) {
      res.status(500).json({ error: error.message });
    }
  }

  /**
   * Update task priority
   * PATCH /api/tasks/:id/priority
   */
  async updateTaskPriority(req, res) {
    try {
      const { id } = req.params;
      const { priority } = req.body;

      if (!priority) {
        return res.status(400).json({ error: 'Priority is required' });
      }

      // TODO: Fetch task from database
      const task = new Task({ 
        id: parseInt(id), 
        title: 'Sample Task',
        priority: 'medium'
      });

      task.setPriority(priority);

      // TODO: Update in database

      res.json({ task: task.toJSON() });
    } catch (error) {
      if (error.message.startsWith('Invalid priority')) {
        return res.status(400).json({ error: error.message });
      }
      res.status(500).json({ error: error.message });
    }
  }
}

module.exports = new TasksController();
EOF
```

---

**Commiter :**

```bash
git add backend/src/models/ backend/src/controllers/
git commit -m "feat(tasks): add priority management

- Add priority field to Task model (low, medium, high, urgent)
- Implement priority validation
- Add priority-based sorting
- Create endpoint to filter tasks by priority
- Add endpoint to update task priority

Closes #28"
```

---

**Pousser la branche :**

```bash
git push -u origin feature/task-priority
```

---

**Créer une Pull Request vers `develop` (PAS `main` !) :**

1. GitHub -> **New pull request**
2. **Base** : `develop` [ATTENTION] (important !)
3. **Compare** : `feature/task-priority`
4. **Create pull request**

---

**Alice review et approve.**

**Merger dans `develop` :**

**Squash and merge** -> Confirm

---

**Bob finit la feature localement avec Git Flow :**

```bash
git switch develop
git pull origin develop

git flow feature finish task-priority
```

**Résultat :**

```
Switched to branch 'develop'
Already up to date.
Deleted branch feature/task-priority (was a1b2c3d).

Summary of actions:
- The feature branch 'feature/task-priority' was merged into 'develop'
- Feature branch 'feature/task-priority' has been locally deleted
- You are now on branch 'develop'
```

---

**Supprimer la branche distante :**

```bash
git push origin --delete feature/task-priority
```

---

### ÉTAPE 4 : Autres features (Charlie et Diana)

**Charlie développe les filtres de tâches :**

```bash
cd ~/charlie-workspace/taskmaster-pro
git switch develop
git pull origin develop

git flow feature start task-filters
```

---

**Créer le composant de filtres :**

```bash
cat > frontend/src/components/TaskFilters.jsx << 'EOF'
import React, { useState } from 'react';
import './TaskFilters.css';

function TaskFilters({ onFilterChange }) {
  const [filters, setFilters] = useState({
    status: 'all',
    priority: 'all',
    assignee: 'all'
  });

  const handleChange = (filterType, value) => {
    const newFilters = {
      ...filters,
      [filterType]: value
    };
    setFilters(newFilters);
    onFilterChange(newFilters);
  };

  const resetFilters = () => {
    const defaultFilters = {
      status: 'all',
      priority: 'all',
      assignee: 'all'
    };
    setFilters(defaultFilters);
    onFilterChange(defaultFilters);
  };

  return (
    <div className="task-filters">
      <h3>Filters</h3>
      
      <div className="filter-group">
        <label htmlFor="status-filter">Status</label>
        <select
          id="status-filter"
          value={filters.status}
          onChange={(e) => handleChange('status', e.target.value)}
        >
          <option value="all">All Statuses</option>
          <option value="todo">To Do</option>
          <option value="in-progress">In Progress</option>
          <option value="done">Done</option>
        </select>
      </div>

      <div className="filter-group">
        <label htmlFor="priority-filter">Priority</label>
        <select
          id="priority-filter"
          value={filters.priority}
          onChange={(e) => handleChange('priority', e.target.value)}
        >
          <option value="all">All Priorities</option>
          <option value="urgent">Urgent</option>
          <option value="high">High</option>
          <option value="medium">Medium</option>
          <option value="low">Low</option>
        </select>
      </div>

      <div className="filter-group">
        <label htmlFor="assignee-filter">Assignee</label>
        <select
          id="assignee-filter"
          value={filters.assignee}
          onChange={(e) => handleChange('assignee', e.target.value)}
        >
          <option value="all">All Members</option>
          <option value="alice">Alice</option>
          <option value="bob">Bob</option>
          <option value="charlie">Charlie</option>
          <option value="diana">Diana</option>
          <option value="emma">Emma</option>
        </select>
      </div>

      <button onClick={resetFilters} className="reset-button">
        Reset Filters
      </button>
    </div>
  );
}

export default TaskFilters;
EOF
```

---

**Commiter, pousser, PR vers `develop`, merger.**

---

**Diana ajoute la configuration Docker :**

```bash
cd ~/diana-workspace/taskmaster-pro
git switch develop
git pull origin develop

git flow feature start docker-setup
```

---

**Créer le Dockerfile backend :**

```bash
cat > backend/Dockerfile << 'EOF'
FROM node:18-alpine

WORKDIR /app

# Install dependencies
COPY package*.json ./
RUN npm ci --only=production

# Copy source code
COPY src/ ./src/

# Expose port
EXPOSE 3000

# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD node -e "require('http').get('http://localhost:3000/health', (r) => process.exit(r.statusCode === 200 ? 0 : 1))"

# Start server
CMD ["node", "src/index.js"]
EOF
```

---

**Créer le Dockerfile frontend :**

```bash
cat > frontend/Dockerfile << 'EOF'
# Build stage
FROM node:18-alpine AS builder

WORKDIR /app

COPY package*.json ./
RUN npm ci

COPY . .
RUN npm run build

# Production stage
FROM nginx:alpine

COPY --from=builder /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]
EOF
```

---

**Créer docker-compose.yml :**

```bash
cat > docker-compose.yml << 'EOF'
version: '3.8'

services:
  backend:
    build:
      context: ./backend
      dockerfile: Dockerfile
    container_name: taskmaster-backend
    ports:
      - "3000:3000"
    environment:
      - NODE_ENV=production
      - JWT_SECRET=${JWT_SECRET}
      - DB_HOST=database
      - DB_PORT=5432
      - DB_NAME=taskmaster
      - DB_USER=taskmaster_user
      - DB_PASSWORD=${DB_PASSWORD}
    depends_on:
      - database
    networks:
      - taskmaster-network
    restart: unless-stopped

  frontend:
    build:
      context: ./frontend
      dockerfile: Dockerfile
    container_name: taskmaster-frontend
    ports:
      - "80:80"
    depends_on:
      - backend
    networks:
      - taskmaster-network
    restart: unless-stopped

  database:
    image: postgres:15-alpine
    container_name: taskmaster-db
    environment:
      - POSTGRES_DB=taskmaster
      - POSTGRES_USER=taskmaster_user
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    volumes:
      - postgres-data:/var/lib/postgresql/data
    networks:
      - taskmaster-network
    restart: unless-stopped

networks:
  taskmaster-network:
    driver: bridge

volumes:
  postgres-data:
EOF
```

---

**Commiter, pousher, PR, merger dans `develop`.**

---

### ÉTAPE 5 : Préparer une release

**Situation : Développement terminé, prêt pour la v1.0.0**

**Features dans `develop` :**
- [OK] Authentification JWT
- [OK] Gestion de priorités
- [OK] Filtres de tâches
- [OK] Configuration Docker
- [OK] Tests automatiques

**Alice décide de sortir la version 1.0.0.**

---

**Créer une branche release :**

```bash
cd ~/taskmaster-pro
git switch develop
git pull origin develop

git flow release start 1.0.0
```

**Résultat :**

```
Switched to a new branch 'release/1.0.0'

Summary of actions:
- A new branch 'release/1.0.0' was created, based on 'develop'
- You are now on branch 'release/1.0.0'

Follow-up actions:
- Bump the version number now!
- Start committing last-minute fixes in preparing your release
- When done, run:

     git flow release finish '1.0.0'
```

---

**Équivalent manuel :**

```bash
git switch develop
git switch -c release/1.0.0
```

---

**Mettre à jour le numéro de version :**

```bash
cat > VERSION << 'EOF'
1.0.0
EOF

git add VERSION
git commit -m "chore(release): bump version to 1.0.0"
```

---

**Mettre à jour package.json :**

```bash
cd backend
npm version 1.0.0 --no-git-tag-version
cd ..

git add backend/package.json
git commit -m "chore(backend): update version to 1.0.0"
```

---

**Générer le CHANGELOG :**

```bash
cat > CHANGELOG.md << 'EOF'
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.0] - 2024-01-03

### Added
- JWT-based authentication system with login and registration
- Task priority management (low, medium, high, urgent)
- Task filtering by status, priority, and assignee
- Docker containerization for backend, frontend, and database
- Comprehensive API documentation
- Unit tests for authentication service
- GitHub Actions CI/CD pipeline
- Pull request templates

### Security
- Password hashing with bcrypt (configurable rounds)
- JWT token validation
- Input validation with express-validator
- Role-based access control middleware

### Documentation
- README with setup instructions
- API endpoint documentation
- Contributing guidelines
- Code of Conduct

[1.0.0]: https://github.com/alice/taskmaster-pro/releases/tag/v1.0.0
EOF
```

---

**Commiter :**

```bash
git add CHANGELOG.md
git commit -m "docs: add changelog for v1.0.0"
```

---

**Corrections mineures de dernière minute (si nécessaire) :**

Par exemple, corriger une typo dans le README :

```bash
nano README.md
# Corriger quelques fautes

git commit -am "docs: fix typos in README"
```

---

**Pousser la branche release :**

```bash
git push -u origin release/1.0.0
```

---

**Créer une PR vers `main` :**

1. **Base** : `main`
2. **Compare** : `release/1.0.0`
3. **Create pull request**

**Description :**

```markdown
## [RAPIDE] Release v1.0.0

This PR prepares the first stable release of TaskMaster Pro.

### [PACKAGE] What's included

- JWT authentication system
- Task priority management
- Advanced task filtering
- Docker containerization
- CI/CD pipeline
- Comprehensive documentation

### [OK] Pre-release Checklist

- [x] Version bumped to 1.0.0
- [x] CHANGELOG updated
- [x] All tests passing
- [x] Documentation up to date
- [x] No known critical bugs
- [x] Ready for production deployment

### [NOTE] Release Notes

See [CHANGELOG.md](CHANGELOG.md) for full details.

### [OBJECTIF] Post-merge Actions

After merge:
1. Create GitHub Release with tag v1.0.0
2. Deploy to production
3. Merge release back to develop
4. Announce release to team
```

---

**Alice et Diana review la release.**

**Approve et merge vers `main`.**

---

**Terminer la release avec Git Flow :**

```bash
git switch main
git pull origin main

git flow release finish 1.0.0
```

**Git Flow demande :**

1. **Message du tag :**

```
Release v1.0.0

TaskMaster Pro - First Stable Release

Features:
- JWT Authentication
- Task Priority Management
- Advanced Filtering
- Docker Support
- CI/CD Pipeline

See CHANGELOG.md for full details.
```

**Sauvegarder et fermer l'éditeur.**

2. **Message du merge vers develop :**

```
Merge tag 'v1.0.0' into develop

Incorporate release 1.0.0 changes back to develop
```

**Sauvegarder.**

---

**Résultat :**

```
Switched to branch 'main'
Merge made by the 'ort' strategy.
 ... (files merged)
Switched to branch 'develop'
Merge made by the 'ort' strategy.
 ... (files merged)
Deleted branch release/1.0.0 (was a1b2c3d).

Summary of actions:
- Release branch 'release/1.0.0' has been merged into 'main'
- The release was tagged 'v1.0.0'
- Release tag 'v1.0.0' has been back-merged into 'develop'
- Release branch 'release/1.0.0' has been locally deleted
- You are now on branch 'develop'
```

---

**Pousser tout :**

```bash
git push origin main
git push origin develop
git push origin --tags
```

**`--tags` = Pousse tous les tags (dont v1.0.0)**

---

### ÉTAPE 6 : Créer une GitHub Release

**Aller sur GitHub :**

1. **Releases** (dans la sidebar)
2. **Draft a new release**

---

**Configuration :**

**Tag** : `v1.0.0` (sélectionner le tag existant)

**Release title** : `v1.0.0 - TaskMaster Pro First Stable Release`

**Description** :

```markdown
# [BRAVO] TaskMaster Pro v1.0.0

We're excited to announce the first stable release of TaskMaster Pro!

## * Highlights

- **[SECURISE] Authentication**: Secure JWT-based authentication system
- **[GRAPHIQUE] Priority Management**: Organize tasks by priority (urgent, high, medium, low)
- **[RECHERCHE] Advanced Filtering**: Filter tasks by status, priority, and assignee
- **[DOCKER] Docker Ready**: Full containerization for easy deployment
- **[OK] CI/CD**: Automated testing and deployment pipeline

## [PACKAGE] Installation

### Using Docker (Recommended)

```bash
git clone https://github.com/alice/taskmaster-pro.git
cd taskmaster-pro
cp .env.example .env
docker-compose up -d
```

Access at: http://localhost

### Manual Installation

See [Installation Guide](https://github.com/alice/taskmaster-pro#installation) in README.

## [NOTE] Full Changelog

### Added
- JWT-based authentication with login/registration
- Task model with priority field
- Priority validation and sorting
- Task filtering API endpoints
- Frontend filter components
- Docker configuration (backend, frontend, database)
- GitHub Actions CI pipeline
- Comprehensive test suite

### Security
- Bcrypt password hashing
- JWT token validation
- Input sanitization with express-validator
- Role-based authorization middleware

### Documentation
- Complete README
- API documentation
- Contributing guidelines
- Changelog

## [BUG] Known Issues

None critical. See [Issues](https://github.com/alice/taskmaster-pro/issues) for minor enhancements.

## [MERCI] Contributors

- @alice - Lead Developer
- @bob - Backend Engineer
- @charlie - Frontend Engineer
- @diana - DevOps Engineer
- @emma - QA Engineer

## [FICHIER] License

MIT License - See [LICENSE](LICENSE) file

---

**Full Changelog**: https://github.com/alice/taskmaster-pro/blob/main/CHANGELOG.md
```

---

**Assets (optionnel) :**

Ajouter des binaires, archives, ou autres fichiers à télécharger.

---

**Cocher :**

- [ ] Set as a pre-release (pour beta/rc)
- [x] Set as the latest release

---

**Publish release** [RAPIDE]

---

**[OK] Release v1.0.0 publiée sur GitHub !**

---

### ÉTAPE 7 : Gérer un hotfix urgent

**Situation : Bug critique découvert en production !**

**Un utilisateur ne peut pas se connecter avec certains emails contenant des caractères spéciaux.**

**Alice crée un hotfix :**

```bash
cd ~/taskmaster-pro
git switch main
git pull origin main

git flow hotfix start 1.0.1
```

**Résultat :**

```
Switched to a new branch 'hotfix/1.0.1'

Summary of actions:
- A new branch 'hotfix/1.0.1' was created, based on 'main'
- You are now on branch 'hotfix/1.0.1'

Follow-up actions:
- Bump the version number now!
- Start committing your hot fixes
- When done, run:

     git flow hotfix finish '1.0.1'
```

---

**Équivalent manuel :**

```bash
git switch main
git switch -c hotfix/1.0.1
```

---

**Corriger le bug :**

```bash
nano backend/src/auth/auth.validator.js
```

**Modifier la validation d'email :**

```javascript
body('email')
  .isEmail()
  .withMessage('Invalid email format')
  .normalizeEmail({
    gmail_remove_dots: false,  // FIX: Don't remove dots
    gmail_remove_subaddress: false  // FIX: Keep + addressing
  }),
```

---

**Commiter :**

```bash
git add backend/src/auth/auth.validator.js
git commit -m "fix(auth): preserve special characters in email normalization

Bug: Users with + or . in email addresses could not login
Fix: Disable aggressive email normalization
Impact: Critical - affects user authentication

Fixes #42"
```

---

**Mettre à jour la version :**

```bash
echo "1.0.1" > VERSION

cd backend
npm version 1.0.1 --no-git-tag-version
cd ..

git add VERSION backend/package.json
git commit -m "chore: bump version to 1.0.1"
```

---

**Mettre à jour le CHANGELOG :**

```bash
cat >> CHANGELOG.md << 'EOF'

## [1.0.1] - 2024-01-04

### Fixed
- Email normalization now preserves special characters (+ and .)
- Users with Gmail addresses using + or . can now login successfully

### Security
- No security changes in this patch

[1.0.1]: https://github.com/alice/taskmaster-pro/compare/v1.0.0...v1.0.1
EOF
```

```bash
git add CHANGELOG.md
git commit -m "docs: update changelog for v1.0.1"
```

---

**Pousser le hotfix :**

```bash
git push -u origin hotfix/1.0.1
```

---

**Créer une PR URGENTE vers `main` :**

**Label : `priority: urgent`, `type: hotfix`**

**Description :**

```markdown
## [ALERTE] HOTFIX v1.0.1 - Critical Login Bug

### [BUG] Bug Description

Users with email addresses containing special characters (+ or .) cannot login.

**Affected users:** Estimated 15-20% of user base

**Severity:** **CRITICAL** - Blocks authentication

### [OUTIL] Fix

Disabled aggressive email normalization in express-validator.

**Changed:**
- `gmail_remove_dots: false`
- `gmail_remove_subaddress: false`

### [OK] Testing

- [x] Manual testing with problematic emails
- [x] Unit tests updated and passing
- [x] No regression in normal email login

### [GRAPHIQUE] Impact

- **Users affected:** Fixed immediately upon deployment
- **Breaking changes:** None
- **Database migration:** Not required

### [RAPIDE] Deployment

Ready for **immediate** deployment to production.

### [NOTE] Changelog

Version bumped: 1.0.0 -> 1.0.1 (PATCH)

Fixes #42
```

---

**Review ultra-rapide (15 minutes max pour un hotfix) :**

Alice review, approve, merge vers `main`.

---

**Terminer le hotfix :**

```bash
git switch main
git pull origin main

git flow hotfix finish 1.0.1
```

**Messages de tag et merge (comme pour release).**

---

**Résultat :**

```
Summary of actions:
- Hotfix branch 'hotfix/1.0.1' has been merged into 'main'
- The hotfix was tagged 'v1.0.1'
- Hotfix tag 'v1.0.1' has been back-merged into 'develop'
- Hotfix branch 'hotfix/1.0.1' has been locally deleted
- You are now on branch 'develop'
```

---

**Pousser :**

```bash
git push origin main
git push origin develop
git push origin --tags
```

---

**Créer une GitHub Release pour v1.0.1 (hotfix) :**

Même processus, mais description plus courte :

```markdown
# [HOT] Hotfix v1.0.1

Critical bug fix for authentication issues.

## [BUG] Fixed

- Email normalization now preserves special characters
- Users with + or . in email can now login successfully

## [RAPIDE] Deployment

This is a critical hotfix and should be deployed **immediately**.

**Full Changelog**: v1.0.0...v1.0.1
```

---

**[OK] Hotfix déployé !**

---

### ÉTAPE 8 : Automatiser le versioning et changelog

**Installer des outils d'automatisation :**

**1. Standard Version (bumps version + génère changelog)**

```bash
cd ~/taskmaster-pro
git switch develop
git pull origin develop

cd backend
npm install --save-dev standard-version
```

---

**Configurer :**

```bash
cat > backend/.versionrc.json << 'EOF'
{
  "types": [
    {"type": "feat", "section": "Features"},
    {"type": "fix", "section": "Bug Fixes"},
    {"type": "perf", "section": "Performance Improvements"},
    {"type": "revert", "section": "Reverts"},
    {"type": "docs", "section": "Documentation", "hidden": false},
    {"type": "style", "section": "Styles", "hidden": true},
    {"type": "chore", "section": "Miscellaneous Chores", "hidden": true},
    {"type": "refactor", "section": "Code Refactoring", "hidden": false},
    {"type": "test", "section": "Tests", "hidden": true},
    {"type": "build", "section": "Build System", "hidden": true},
    {"type": "ci", "section": "Continuous Integration", "hidden": true}
  ],
  "commitUrlFormat": "https://github.com/alice/taskmaster-pro/commit/{{hash}}",
  "compareUrlFormat": "https://github.com/alice/taskmaster-pro/compare/{{previousTag}}...{{currentTag}}",
  "issueUrlFormat": "https://github.com/alice/taskmaster-pro/issues/{{id}}",
  "userUrlFormat": "https://github.com/{{user}}",
  "releaseCommitMessageFormat": "chore(release): {{currentTag}}",
  "issuePrefixes": ["#"]
}
EOF
```

---

**Ajouter script dans package.json :**

```json
{
  "scripts": {
    "release": "standard-version",
    "release:minor": "standard-version --release-as minor",
    "release:major": "standard-version --release-as major",
    "release:patch": "standard-version --release-as patch"
  }
}
```

---

**Utilisation :**

```bash
# Automatiquement détermine le type (patch/minor/major)
npm run release

# Force une version spécifique
npm run release:minor  # 1.0.1 -> 1.1.0
npm run release:major  # 1.0.1 -> 2.0.0
npm run release:patch  # 1.0.1 -> 1.0.2
```

---

**Ce que ça fait :**

1. Analyse les commits depuis le dernier tag
2. Détermine le nouveau numéro de version (SemVer)
3. Génère/met à jour CHANGELOG.md
4. Bumpe la version dans package.json
5. Crée un commit "chore(release): vX.Y.Z"
6. Crée un tag vX.Y.Z

---

**2. Commitlint + Husky (force conventional commits)**

```bash
npm install --save-dev @commitlint/cli @commitlint/config-conventional husky
```

---

**Configurer commitlint :**

```bash
cat > backend/commitlint.config.js << 'EOF'
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
      2,
      'always',
      [
        'feat',
        'fix',
        'docs',
        'style',
        'refactor',
        'perf',
        'test',
        'build',
        'ci',
        'chore',
        'revert'
      ]
    ],
    'subject-case': [2, 'never', ['upper-case']],
    'header-max-length': [2, 'always', 100]
  }
};
EOF
```

---

**Installer Husky hooks :**

```bash
npx husky install

npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
```

---

**Maintenant les commits non-conventionnels sont rejetés ! [OK]**

**Exemple :**

```bash
git commit -m "fixed stuff"
```

**Erreur :**

```
⧗   input: fixed stuff
[X]   subject may not be empty [subject-empty]
[X]   type may not be empty [type-empty]
```

**Correct :**

```bash
git commit -m "fix: correct email validation"
```

**[OK] Accepté !**

---

### ÉTAPE 9 : GitHub Release automatique avec GitHub Actions

**Créer un workflow pour publier automatiquement les releases :**

```bash
cat > .github/workflows/release.yml << 'EOF'
name: Release

on:
  push:
    tags:
      - 'v*.*.*'

jobs:
  create-release:
    name: Create GitHub Release
    runs-on: ubuntu-latest
    
    permissions:
      contents: write
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
        with:
          fetch-depth: 0
      
      - name: Extract version from tag
        id: get_version
        run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
      
      - name: Extract changelog for this version
        id: changelog
        run: |
          # Extract changelog section for this version
          VERSION=${{ steps.get_version.outputs.VERSION }}
          CHANGELOG=$(sed -n "/## \[${VERSION}\]/,/## \[/p" CHANGELOG.md | sed '$d')
          echo "CHANGELOG<<EOF" >> $GITHUB_OUTPUT
          echo "$CHANGELOG" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT
      
      - name: Create Release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: Release v${{ steps.get_version.outputs.VERSION }}
          body: |
            ${{ steps.changelog.outputs.CHANGELOG }}
            
            **Full Changelog**: https://github.com/${{ github.repository }}/blob/main/CHANGELOG.md
          draft: false
          prerelease: false
  
  build-and-publish:
    name: Build and Publish Docker Images
    runs-on: ubuntu-latest
    needs: create-release
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      
      - name: Log in to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKER_USERNAME }}
          password: ${{ secrets.DOCKER_PASSWORD }}
      
      - name: Extract version
        id: get_version
        run: echo "VERSION=${GITHUB_REF#refs/tags/v}" >> $GITHUB_OUTPUT
      
      - name: Build and push backend image
        uses: docker/build-push-action@v4
        with:
          context: ./backend
          push: true
          tags: |
            alice/taskmaster-backend:latest
            alice/taskmaster-backend:${{ steps.get_version.outputs.VERSION }}
      
      - name: Build and push frontend image
        uses: docker/build-push-action@v4
        with:
          context: ./frontend
          push: true
          tags: |
            alice/taskmaster-frontend:latest
            alice/taskmaster-frontend:${{ steps.get_version.outputs.VERSION }}
EOF
```

---

**Maintenant quand tu push un tag :**

```bash
git tag -a v1.1.0 -m "Release v1.1.0"
git push origin v1.1.0
```

**GitHub Actions :**
1. Crée automatiquement la GitHub Release
2. Extrait le changelog
3. Build les images Docker
4. Publie sur Docker Hub

---

### [OK] TESTS DE VALIDATION

**1. Branches Git Flow créées**

```bash
git branch -a
```

- [ ] `main` existe
- [ ] `develop` existe
- [ ] Branches feature créées et mergées
- [ ] Branch release créée

---

**2. Tags créés**

```bash
git tag
```

- [ ] `v1.0.0` existe
- [ ] `v1.0.1` existe
- [ ] Tags annotés (pas légers)

---

**3. Workflow complet**

- [ ] Feature développée sur branche feature/*
- [ ] Mergée dans develop
- [ ] Release créée depuis develop
- [ ] Mergée dans main ET develop
- [ ] Tag créé
- [ ] GitHub Release publiée

---

**4. Hotfix**

- [ ] Créé depuis main
- [ ] Corrige un bug critique
- [ ] Mergé dans main ET develop
- [ ] Version PATCH incrémentée

---

**5. Changelog**

- [ ] CHANGELOG.md existe
- [ ] Versions documentées
- [ ] Format cohérent

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : "git flow not found"

**Solution :**

```bash
# Installer git-flow
sudo apt-get install git-flow

# Ou faire manuellement avec git branch
```

---

#### Erreur 2 : Tag déjà existe

**Symptôme :**

```
fatal: tag 'v1.0.0' already exists
```

**Solution :**

```bash
# Supprimer le tag local
git tag -d v1.0.0

# Supprimer le tag distant
git push origin --delete v1.0.0

# Recréer
git tag -a v1.0.0 -m "Release v1.0.0"
git push origin v1.0.0
```

---

#### Erreur 3 : Merge conflicts lors de release finish

**Solution :**

```bash
# Résoudre les conflits
git status
nano <fichier-en-conflit>

# Marquer comme résolu
git add <fichier>

# Continuer le merge
git commit

# Continuer git flow
git flow release finish 1.0.0
```

---

#### Erreur 4 : Oubli de merger release dans develop

**Symptôme :**

`develop` n'a pas les changements de la release

**Solution :**

```bash
git switch develop
git merge main
git push origin develop
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Les 5 types de branches**

| Branche | Depuis | Vers | Durée | Usage |
|---------|--------|------|-------|-------|
| `main` | - | - | ∞ | Production |
| `develop` | `main` | - | ∞ | Intégration |
| `feature/*` | `develop` | `develop` | Jours/semaines | Nouvelles features |
| `release/*` | `develop` | `main` + `develop` | Jours | Préparation release |
| `hotfix/*` | `main` | `main` + `develop` | Heures | Bugs critiques |

---

**2. Semantic Versioning**

```
MAJOR.MINOR.PATCH

1.0.0 -> 1.1.0 (nouvelle feature)
1.1.0 -> 1.1.1 (bug fix)
1.1.1 -> 2.0.0 (breaking change)
```

---

**3. Tags Git**

```bash
# Tag léger (pas recommandé)
git tag v1.0.0

# Tag annoté (recommandé)
git tag -a v1.0.0 -m "Release 1.0.0"

# Pousser les tags
git push origin --tags
```

---

**4. Workflow release**

```
1. develop prêt -> git flow release start X.Y.Z
2. Bump version, update changelog
3. Corrections mineures seulement
4. git flow release finish X.Y.Z
5. Push main, develop, tags
6. Créer GitHub Release
7. Déployer
```

---

**5. Workflow hotfix**

```
1. Bug critique en prod -> git flow hotfix start X.Y.Z
2. Fix rapide
3. git flow hotfix finish X.Y.Z
4. Push main, develop, tags
5. Déploiement immédiat
```

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. GitVersion (versioning automatique)**

Calcule automatiquement la version selon les branches et commits.

```yaml
# .github/workflows/version.yml
- name: Install GitVersion
  uses: gittools/actions/gitversion/setup@v0.9.7
  
- name: Determine Version
  uses: gittools/actions/gitversion/execute@v0.9.7
```

---

**2. Conventional Changelog (génération automatique)**

```bash
npm install -g conventional-changelog-cli

conventional-changelog -p angular -i CHANGELOG.md -s
```

---

**3. Release Drafter (draft automatique de releases)**

```yaml
# .github/release-drafter.yml
name-template: 'v$RESOLVED_VERSION'
tag-template: 'v$RESOLVED_VERSION'
categories:
  - title: '[RAPIDE] Features'
    labels:
      - 'feature'
  - title: '[BUG] Bug Fixes'
    labels:
      - 'fix'
```

---

**4. Trunk-Based Development (alternative à Git Flow)**

Pour les équipes qui déploient très souvent (CI/CD avancé) :

```
main ────[BLACK_CIRCLE]────[BLACK_CIRCLE]────[BLACK_CIRCLE]────[BLACK_CIRCLE]─── (toujours déployable)
          ╲    ╲    ╲
         short-lived feature branches (< 1 jour)
```

**Différences avec Git Flow :**
- Pas de branche develop
- Branches feature ultra-courtes
- Feature flags pour features non terminées
- Déploiement continu

---

**5. GitHub Flow (simplifié)**

Plus simple que Git Flow :

```
main ────[BLACK_CIRCLE]────[BLACK_CIRCLE]────────[BLACK_CIRCLE]────
          ╲          ╱
         feature ───[BLACK_CIRCLE]
```

**Règles :**
- 1 branche : `main` (toujours déployable)
- Features sur branches éphémères
- PR vers main
- Merge = déploiement immédiat

**Usage :** Petites équipes, déploiements fréquents

---

## [COURS] CONCLUSION DE L'EXERCICE 6

**[OK] Félicitations ! Tu maîtrises Git Flow !**

**Ce que tu as appris :**
- Modèle Git Flow complet
- Gestion des 5 types de branches
- Semantic Versioning
- Préparation de releases
- Gestion de hotfixes
- Tags et GitHub Releases
- Changelog automatique
- Versioning automatisé

**Compétences acquises :**
- [OK] Git Flow (niveau expert)
- [OK] Release Management
- [OK] Semantic Versioning
- [OK] Hotfix workflows
- [OK] Automation (changelog, versioning)
- [OK] Production readiness

**Temps moyen de réalisation :** 4-5 heures

**Prochaine étape :** Exercice 7 - Opérations Git avancées (rebase interactif, cherry-pick, reflog) ! [RAPIDE]

---

(Il reste 4 exercices : 7, 8, 9, 10. Veux-tu que je continue ?)

# [ROUGE] EXERCICE 7 : OPÉRATIONS GIT AVANCÉES

## [LISTE] ÉNONCÉ

### Contexte professionnel

L'équipe TaskMaster Pro maîtrise maintenant Git Flow et les workflows de base. **Alice** veut que l'équipe apprenne les **opérations Git avancées** pour :
- Nettoyer l'historique avant les PRs
- Récupérer du travail "perdu"
- Débuguer efficacement
- Réécrire l'historique proprement
- Gérer des situations complexes

**Situations réelles rencontrées :**
- [PERSONNE][CODE] **Bob** a fait 15 commits "WIP" qu'il veut squasher en 1
- [PERSONNE][CODE] **Charlie** a accidentellement supprimé une branche importante
- [PERSONNE][CODE] **Diana** veut récupérer un commit spécifique d'une autre branche
- [PERSONNE][CODE] **Emma** cherche qui a introduit un bug (et quand)
- [PERSONNE][CODE] **Alice** veut réécrire l'historique pour le rendre professionnel

**Aujourd'hui, tu vas maîtriser :**
1. **Interactive rebase** - Réécrire l'historique
2. **Cherry-pick** - Copier des commits spécifiques
3. **Reflog** - Récupérer le travail "perdu"
4. **Bisect** - Trouver le commit qui a introduit un bug
5. **Reset vs Revert** - Annuler des changements
6. **Blame** - Trouver qui a modifié quoi
7. **Stash avancé** - Techniques avancées de stashing

### Objectifs

- Maîtriser le rebase interactif (squash, reword, edit, drop)
- Utiliser cherry-pick efficacement
- Récupérer des commits avec reflog
- Debugger avec git bisect
- Comprendre reset (soft, mixed, hard)
- Analyser l'historique avec blame
- Techniques avancées de manipulation d'historique

### Livrables

- Historique nettoyé avec rebase interactif
- Commits récupérés via reflog
- Bug trouvé avec bisect
- Cherry-pick réussi
- Documentation des commandes avancées

### Contraintes

- [ATTENTION] **JAMAIS** réécrire l'historique public (déjà pushé et partagé)
- Toujours vérifier l'état avec `git status` et `git log`
- Faire des backups avant opérations destructives
- Comprendre ce qu'on fait (pas de copier-coller aveugle)
- Temps estimé : 4-5 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :

- [OK] Réécrire l'historique avec rebase interactif
- [OK] Squasher, reword, edit, drop des commits
- [OK] Cherry-pick des commits entre branches
- [OK] Utiliser reflog pour récupérer le travail perdu
- [OK] Debugger avec git bisect
- [OK] Différencier reset --soft, --mixed, --hard
- [OK] Revert des commits proprement
- [OK] Analyser l'historique avec git blame
- [OK] Manipuler l'historique en toute sécurité

---

## [DOCS] PRÉREQUIS

- Exercices 1 à 6 terminés
- Compréhension solide des commits et branches
- Familiarité avec Git Flow
- Terminal/ligne de commande

---

## [ATTENTION] AVERTISSEMENT IMPORTANT

**Les opérations suivantes réécrivent l'historique :**
- `git rebase -i`
- `git commit --amend`
- `git reset --hard`
- `git filter-branch`

**[ATTENTION] RÈGLE D'OR : Ne JAMAIS réécrire l'historique public !**

**Historique privé** (OK pour réécrire) :
- [OK] Commits sur ta branche locale non pushée
- [OK] Commits sur ta branche feature avant la PR
- [OK] Travail en cours jamais partagé

**Historique public** (NE PAS réécrire) :
- [X] Commits sur `main` ou `develop`
- [X] Commits déjà pushés et partagés avec l'équipe
- [X] Commits dans des PRs mergées

**Pourquoi ?**
- Réécrire l'historique change les hash de commits
- Les autres développeurs auront des conflits
- Peut casser les dépôts de toute l'équipe
- Perte de traçabilité

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Interactive Rebase - Nettoyer l'historique

**Situation :** Bob a développé une feature avec beaucoup de commits "WIP".

**Bob crée une branche et fait plusieurs commits :**

```bash
cd ~/bob-workspace/taskmaster-pro
git switch develop
git pull origin develop

git switch -c feature/task-comments
```

---

**Bob fait plusieurs commits (simulation de travail réel) :**

```bash
# Commit 1
cat > backend/src/models/Comment.js << 'EOF'
class Comment {
  constructor(data) {
    this.id = data.id;
    this.taskId = data.taskId;
    this.content = data.content;
  }
}
module.exports = Comment;
EOF

git add backend/src/models/Comment.js
git commit -m "WIP: start comment model"
```

---

```bash
# Commit 2
cat >> backend/src/models/Comment.js << 'EOF'

  getAuthor() {
    // TODO
  }
EOF

git commit -am "wip"
```

---

```bash
# Commit 3
nano backend/src/models/Comment.js
# Ajouter userId, createdAt

git commit -am "add fields"
```

---

```bash
# Commit 4
cat > backend/src/controllers/comments.controller.js << 'EOF'
const Comment = require('../models/Comment');

class CommentsController {
  async getComments(req, res) {
    // TODO
  }
}
module.exports = new CommentsController();
EOF

git add backend/src/controllers/comments.controller.js
git commit -m "add controller"
```

---

```bash
# Commit 5
nano backend/src/controllers/comments.controller.js
# Implémenter getComments

git commit -am "implement get"
```

---

```bash
# Commit 6
nano backend/src/controllers/comments.controller.js
# Ajouter createComment

git commit -am "fix typo"
```

---

```bash
# Commit 7
cat >> backend/src/controllers/comments.controller.js << 'EOF'
// Final implementation
EOF

git commit -am "feat(comments): implement comments system"
```

---

**Vérifier l'historique :**

```bash
git log --oneline
```

**Résultat :**

```
a7b8c9d (HEAD -> feature/task-comments) feat(comments): implement comments system
a6b7c8d fix typo
a5b6c7d implement get
a4b5c6d add controller
a3b4c5d add fields
a2b3c4d wip
a1b2c3d WIP: start comment model
b0c1d2e (origin/develop, develop) Previous commit from develop
```

**[!] C'est le chaos ! 7 commits pour une seule feature !**

**Ce qu'on veut :**
```
1 commit propre : "feat(comments): implement task comments system"
```

---

**Lancer le rebase interactif :**

```bash
git rebase -i develop
```

**Ou (si on veut les 7 derniers commits) :**

```bash
git rebase -i HEAD~7
```

---

**Un éditeur s'ouvre avec :**

```
pick a1b2c3d WIP: start comment model
pick a2b3c4d wip
pick a3b4c5d add fields
pick a4b5c6d add controller
pick a5b6c7d implement get
pick a6b7c8d fix typo
pick a7b8c9d feat(comments): implement comments system

# Rebase b0c1d2e..a7b8c9d onto b0c1d2e (7 commands)
#
# Commands:
# p, pick <commit> = use commit
# r, reword <commit> = use commit, but edit the commit message
# e, edit <commit> = use commit, but stop for amending
# s, squash <commit> = use commit, but meld into previous commit
# f, fixup <commit> = like "squash", but discard this commit's log message
# x, exec <command> = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop <commit> = remove commit
# l, label <label> = label current HEAD with a name
# t, reset <label> = reset HEAD to a label
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
# .       create a merge commit using the original merge commit's
# .       message (or the oneline, if no original merge commit was
# .       specified). Use -c <commit> to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
```

---

**Explication des commandes :**

| Commande | Abréviation | Action |
|----------|-------------|--------|
| **pick** | `p` | Garder le commit tel quel |
| **reword** | `r` | Garder le commit mais changer le message |
| **edit** | `e` | Garder le commit mais s'arrêter pour le modifier |
| **squash** | `s` | Fusionner avec le commit précédent, garder les 2 messages |
| **fixup** | `f` | Fusionner avec le commit précédent, garder seulement le message précédent |
| **drop** | `d` | Supprimer le commit |
| **exec** | `x` | Exécuter une commande shell |

---

**Stratégie de Bob :**

1. Garder le premier commit
2. Squasher tous les suivants dans le premier
3. Réécrire le message final

**Modifier le fichier :**

```
pick a1b2c3d WIP: start comment model
squash a2b3c4d wip
squash a3b4c5d add fields
squash a4b5c6d add controller
squash a5b6c7d implement get
squash a6b7c8d fix typo
squash a7b8c9d feat(comments): implement comments system

# ...
```

**Ou plus court (fixup pour ignorer les messages) :**

```
pick a1b2c3d WIP: start comment model
fixup a2b3c4d wip
fixup a3b4c5d add fields
fixup a4b5c6d add controller
fixup a5b6c7d implement get
fixup a6b7c8d fix typo
fixup a7b8c9d feat(comments): implement comments system
```

**Sauvegarder et fermer l'éditeur.**

---

**Un nouvel éditeur s'ouvre (si on a utilisé `squash`) :**

```
# This is a combination of 7 commits.
# This is the 1st commit message:

WIP: start comment model

# This is the commit message #2:

wip

# This is the commit message #3:

add fields

# ... etc
```

**Supprimer tout et écrire un message propre :**

```
feat(comments): implement task comments system

Add complete commenting functionality:
- Comment model with validation
- Comments controller with CRUD operations
- Support for nested replies
- Author tracking and timestamps

Closes #35
```

**Sauvegarder et fermer.**

---

**Résultat :**

```
[detached HEAD c9d0e1f] feat(comments): implement task comments system
 Date: Wed Jan 3 14:30:00 2024 +0100
 2 files changed, 87 insertions(+)
 create mode 100644 backend/src/models/Comment.js
 create mode 100644 backend/src/controllers/comments.controller.js
Successfully rebased and updated refs/heads/feature/task-comments.
```

---

**Vérifier l'historique :**

```bash
git log --oneline
```

**Résultat :**

```
c9d0e1f (HEAD -> feature/task-comments) feat(comments): implement task comments system
b0c1d2e (origin/develop, develop) Previous commit from develop
```

**[OK] 7 commits -> 1 commit propre !**

---

**[ATTENTION] IMPORTANT : Force push requis (historique réécrit) :**

```bash
# Si la branche n'a JAMAIS été pushée
git push -u origin feature/task-comments

# Si la branche a déjà été pushée (réécrite)
git push --force-with-lease origin feature/task-comments
```

**`--force-with-lease`** :
- Plus sûr que `--force`
- Vérifie que personne d'autre n'a pushé entre-temps
- Refuse si la branche distante a changé

---

### ÉTAPE 2 : Autres utilisations du rebase interactif

#### 2.1 Reword - Changer un message de commit

**Situation :** Charlie a fait une typo dans un message.

```bash
cd ~/charlie-workspace/taskmaster-pro
git switch develop
git pull origin develop

git switch -c feature/dashboard-widgets
```

---

**Charlie fait des commits :**

```bash
echo "widget1" > widget1.js
git add widget1.js
git commit -m "feat: add stats widjet"  # Typo: widjet au lieu de widget

echo "widget2" > widget2.js
git add widget2.js
git commit -m "feat: add chart widget"
```

---

**Corriger la typo dans le premier commit :**

```bash
git rebase -i HEAD~2
```

**Dans l'éditeur :**

```
reword a1b2c3d feat: add stats widjet
pick a2b3c4d feat: add chart widget
```

**Sauvegarder.**

---

**Nouvel éditeur pour le message :**

```
feat: add stats widjet
```

**Corriger :**

```
feat: add stats widget
```

**Sauvegarder.**

---

**[OK] Message corrigé sans toucher au contenu !**

---

#### 2.2 Edit - Modifier le contenu d'un commit

**Situation :** Diana a oublié un fichier dans un commit.

```bash
git rebase -i HEAD~3
```

**Dans l'éditeur :**

```
edit a1b2c3d feat: add config
pick a2b3c4d feat: add middleware
pick a3b4c5d feat: add routes
```

**Sauvegarder.**

---

**Git s'arrête au commit marqué `edit` :**

```
Stopped at a1b2c3d...  feat: add config
You can amend the commit now, with

  git commit --amend

Once you are satisfied with your changes, run

  git rebase --continue
```

---

**Ajouter le fichier oublié :**

```bash
echo "missing file" > config/database.yml
git add config/database.yml

git commit --amend --no-edit
```

**`--no-edit`** = Ne pas changer le message

---

**Continuer le rebase :**

```bash
git rebase --continue
```

**[OK] Fichier ajouté au commit !**

---

#### 2.3 Drop - Supprimer un commit

**Situation :** Emma a commité des logs de debug.

```bash
git rebase -i HEAD~4
```

**Dans l'éditeur :**

```
pick a1b2c3d feat: add feature
drop a2b3c4d debug: add console logs
pick a3b4c5d fix: correct bug
pick a4b5c6d docs: update readme
```

**Ou simplement supprimer la ligne :**

```
pick a1b2c3d feat: add feature
# ligne supprimée
pick a3b4c5d fix: correct bug
pick a4b5c6d docs: update readme
```

**[OK] Commit de debug supprimé de l'historique !**

---

#### 2.4 Réorganiser les commits

**On peut changer l'ordre des lignes :**

```
pick a3b4c5d fix: correct bug
pick a1b2c3d feat: add feature
pick a4b5c6d docs: update readme
```

**Les commits seront appliqués dans ce nouvel ordre.**

---

### ÉTAPE 3 : Cherry-pick - Copier un commit spécifique

**Qu'est-ce que cherry-pick ?**

**Cherry-pick** = "Cueillir une cerise"
- Copie un commit d'une branche vers une autre
- Crée un nouveau commit (hash différent)
- Utile pour appliquer un fix spécifique

**Analogie :**
Tu as un panier de fruits (branche A) et tu veux prendre SEULEMENT la pomme rouge (commit X) pour la mettre dans ton autre panier (branche B), sans prendre tout le panier.

---

**Visualisation :**

```
Branch A:  [BLACK_CIRCLE]──[BLACK_CIRCLE]──[BLACK_CIRCLE]──[BLACK_CIRCLE]  (commit X ici)
              v cherry-pick
Branch B:  [BLACK_CIRCLE]──[BLACK_CIRCLE]──X'──[BLACK_CIRCLE] (copie de X)
```

---

**Situation :** Diana a fait un fix important sur une branche, mais Alice veut seulement ce fix sur une autre branche.

**Diana travaille sur une branche :**

```bash
cd ~/diana-workspace/taskmaster-pro
git switch develop
git pull origin develop

git switch -c feature/notification-system
```

---

**Diana fait plusieurs commits :**

```bash
# Commit 1: Notification model
cat > backend/src/models/Notification.js << 'EOF'
class Notification {
  constructor(data) {
    this.id = data.id;
    this.message = data.message;
    this.userId = data.userId;
    this.read = false;
  }
}
module.exports = Notification;
EOF

git add backend/src/models/Notification.js
git commit -m "feat(notifications): add notification model"

# Récupérer le hash
COMMIT_MODEL=$(git rev-parse HEAD)
echo "Hash du commit model: $COMMIT_MODEL"
```

---

```bash
# Commit 2: Critical security fix (qui devrait être partout)
cat > backend/src/middleware/rateLimit.js << 'EOF'
const rateLimit = require('express-rate-limit');

// CRITICAL FIX: Prevent brute force attacks
const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // limit each IP to 5 requests per windowMs
  message: 'Too many login attempts, please try again later'
});

module.exports = { loginLimiter };
EOF

git add backend/src/middleware/rateLimit.js
git commit -m "fix(security): add rate limiting to prevent brute force

CRITICAL: This prevents attackers from trying unlimited passwords.
Should be applied to all branches immediately.

CVE-2024-XXXX"

# Récupérer le hash
COMMIT_SECURITY=$(git rev-parse HEAD)
echo "Hash du commit security: $COMMIT_SECURITY"
```

---

```bash
# Commit 3: Notification controller
cat > backend/src/controllers/notifications.controller.js << 'EOF'
class NotificationsController {
  async getNotifications(req, res) {
    // Implementation
  }
}
module.exports = new NotificationsController();
EOF

git add backend/src/controllers/notifications.controller.js
git commit -m "feat(notifications): add notifications controller"
```

---

**Historique de la branche :**

```bash
git log --oneline
```

```
d3e4f5g (HEAD -> feature/notification-system) feat(notifications): add notifications controller
c2d3e4f fix(security): add rate limiting to prevent brute force
b1c2d3e feat(notifications): add notification model
a0b1c2d (develop) Previous develop commit
```

---

**Alice veut SEULEMENT le fix de sécurité sur une branche de hotfix :**

```bash
cd ~/taskmaster-pro
git switch main
git pull origin main

git switch -c hotfix/rate-limit-security
```

---

**Cherry-pick le commit de sécurité :**

```bash
# Utiliser le hash du commit de sécurité
git cherry-pick c2d3e4f
```

**Résultat :**

```
[hotfix/rate-limit-security e4f5g6h] fix(security): add rate limiting to prevent brute force
 Date: Wed Jan 3 15:00:00 2024 +0100
 1 file changed, 12 insertions(+)
 create mode 100644 backend/src/middleware/rateLimit.js
```

---

**Vérifier :**

```bash
git log --oneline
```

```
e4f5g6h (HEAD -> hotfix/rate-limit-security) fix(security): add rate limiting to prevent brute force
z9y8x7w (main) Previous main commit
```

**[OK] Le commit de sécurité est copié (nouveau hash: e4f5g6h) !**

---

**Le fichier rateLimit.js est bien là :**

```bash
ls backend/src/middleware/
```

```
auth.middleware.js
rateLimit.js
```

---

**Push et merge via PR :**

```bash
git push -u origin hotfix/rate-limit-security
```

**Créer PR -> Review -> Merge -> Déploiement immédiat**

---

**Cherry-pick multiple commits :**

```bash
# Cherry-pick plusieurs commits d'un coup
git cherry-pick abc123 def456 ghi789

# Cherry-pick une plage de commits
git cherry-pick abc123..ghi789
```

---

**Gérer les conflits lors du cherry-pick :**

Si le commit ne s'applique pas proprement :

```
Auto-merging file.txt
CONFLICT (content): Merge conflict in file.txt
error: could not apply c2d3e4f... fix(security): add rate limiting
```

**Résoudre :**

```bash
# 1. Résoudre les conflits manuellement
nano file.txt

# 2. Marquer comme résolu
git add file.txt

# 3. Continuer le cherry-pick
git cherry-pick --continue
```

**Abandonner :**

```bash
git cherry-pick --abort
```

---

### ÉTAPE 4 : Git Reflog - Récupérer le travail "perdu"

**Qu'est-ce que reflog ?**

**Reflog** (Reference Log) = Journal de TOUS les mouvements de HEAD

**HEAD** = Pointeur vers le commit actuel

**Git enregistre dans reflog :**
- Chaque commit
- Chaque checkout
- Chaque reset
- Chaque rebase
- Chaque merge
- Etc.

**Même les commits "supprimés" sont dans reflog !**

---

**Situation catastrophe : Charlie supprime accidentellement une branche**

**Charlie travaille sur une feature importante :**

```bash
cd ~/charlie-workspace/taskmaster-pro
git switch develop
git pull origin develop

git switch -c feature/important-work
```

---

```bash
# Beaucoup de travail...
echo "important code" > important-feature.js
git add important-feature.js
git commit -m "feat: add critical feature"

echo "more work" >> important-feature.js
git commit -am "feat: improve feature"

echo "final touches" >> important-feature.js
git commit -am "feat: finalize feature"
```

---

**Charlie vérifie son travail :**

```bash
git log --oneline
```

```
f5g6h7i (HEAD -> feature/important-work) feat: finalize feature
e4f5g6h feat: improve feature
d3e4f5g feat: add critical feature
c2d3e4f (develop) Previous commit
```

**3 commits de travail important ! [OK]**

---

**Charlie bascule vers develop et... HORREUR ! Supprime la branche par erreur :**

```bash
git switch develop

# Charlie pense que c'était une vieille branche inutile
git branch -D feature/important-work
```

**Résultat :**

```
Deleted branch feature/important-work (was f5g6h7i).
```

**[!] PANIC ! Tout le travail est perdu !**

---

**Vérifier :**

```bash
git branch
```

```
* develop
```

**La branche a vraiment disparu ! [!]**

---

**MAIS... Git a tout gardé dans reflog ! [BRAVO]**

**Voir le reflog :**

```bash
git reflog
```

**Résultat :**

```
c2d3e4f (HEAD -> develop) HEAD@{0}: checkout: moving from feature/important-work to develop
f5g6h7i HEAD@{1}: commit: feat: finalize feature
e4f5g6h HEAD@{2}: commit: feat: improve feature
d3e4f5g HEAD@{3}: commit: feat: add critical feature
c2d3e4f (HEAD -> develop) HEAD@{4}: checkout: moving from develop to feature/important-work
...
```

**Explication :**

**`HEAD@{0}`** = Position actuelle (develop)
**`HEAD@{1}`** = Position juste avant (le dernier commit de la branche supprimée !)
**`HEAD@{2}`** = Avant-avant
**Etc.**

**Git garde l'historique de tous les mouvements ! [BRAVO]**

---

**Récupérer la branche :**

**Méthode 1 : Recréer la branche à partir du hash**

```bash
git branch feature/important-work f5g6h7i
```

**Ou :**

```bash
git branch feature/important-work HEAD@{1}
```

---

**Vérifier :**

```bash
git switch feature/important-work
git log --oneline
```

```
f5g6h7i (HEAD -> feature/important-work) feat: finalize feature
e4f5g6h feat: improve feature
d3e4f5g feat: add critical feature
c2d3e4f (develop) Previous commit
```

**[OK] TOUT est récupéré ! [BRAVO]**

---

**Méthode 2 : Checkout direct**

```bash
git checkout -b feature/important-work-recovered HEAD@{1}
```

---

**Autres usages de reflog :**

**Récupérer après un reset --hard :**

```bash
# Erreur : reset trop loin
git reset --hard HEAD~10

# Oh non ! Je voulais seulement HEAD~1 !

# Voir reflog
git reflog

# Revenir en arrière
git reset --hard HEAD@{1}
```

---

**Récupérer après un rebase raté :**

```bash
# Rebase qui a mal tourné
git rebase -i develop
# ... chaos ...

# Annuler complètement le rebase
git reflog
git reset --hard HEAD@{1}  # Avant le rebase
```

---

**[ALARM_CLOCK] Durée de conservation du reflog :**

- **90 jours** par défaut pour les références accessibles
- **30 jours** pour les références orphelines

**Configuration :**

```bash
# Voir la config
git config gc.reflogExpire
git config gc.reflogExpireUnreachable

# Modifier (garder 180 jours)
git config gc.reflogExpire 180.days.ago
```

---

### ÉTAPE 5 : Git Bisect - Trouver le bug

**Qu'est-ce que bisect ?**

**Bisect** = Recherche binaire pour trouver le commit qui a introduit un bug

**Principe :**
1. Marquer un commit "bon" (sans bug)
2. Marquer un commit "mauvais" (avec bug)
3. Git teste le commit au milieu
4. Tu dis si ce commit est bon ou mauvais
5. Git réduit de moitié à chaque fois
6. Trouve le commit coupable en O(log n)

**Exemple :**
```
Commits: 1 2 3 4 5 6 7 8 9 10

1 = bon [OK]
10 = mauvais [X]

Git teste 5 -> mauvais [X]
Git teste 3 -> bon [OK]
Git teste 4 -> mauvais [X]
-> Le bug est dans le commit 4 !
```

**10 commits -> 4 tests au lieu de 10 !**

---

**Situation : Emma découvre un bug en production mais ne sait pas quand il est apparu**

**Le bug :** La fonction de calcul de priorité retourne `undefined` au lieu d'un nombre.

**Emma sait :**
- [OK] Le commit `v1.0.0` (il y a 2 semaines) fonctionnait
- [X] Le commit actuel (`HEAD`) ne fonctionne pas

---

**Créer un scénario (simulation) :**

**Sur develop, faire plusieurs commits dont un introduit le bug :**

```bash
cd ~/taskmaster-pro
git switch develop
git pull origin develop
```

---

**Commit 1 : Bon**

```bash
cat > backend/src/utils/priority.js << 'EOF'
function calculatePriority(task) {
  const levels = { low: 1, medium: 2, high: 3, urgent: 4 };
  return levels[task.priority] || 2;
}
module.exports = { calculatePriority };
EOF

git add backend/src/utils/priority.js
git commit -m "feat(utils): add priority calculator"
```

---

**Commit 2 : Bon**

```bash
cat >> backend/src/utils/priority.js << 'EOF'

function getPriorityLabel(level) {
  const labels = { 1: 'low', 2: 'medium', 3: 'high', 4: 'urgent' };
  return labels[level] || 'medium';
}
module.exports = { calculatePriority, getPriorityLabel };
EOF

git commit -am "feat(utils): add priority label function"
```

---

**Commit 3 : BUG INTRODUIT ! [BUG]**

```bash
nano backend/src/utils/priority.js
```

**Modifier calculatePriority (introduire le bug) :**

```javascript
function calculatePriority(task) {
  const levels = { low: 1, medium: 2, high: 3, urgent: 4 };
  // BUG: typo dans "priority"
  return levels[task.priorityy] || 2;  // priorityy au lieu de priority
}
```

```bash
git commit -am "refactor(utils): optimize priority calculation"
```

---

**Commit 4 : Bon (mais le bug est toujours là)**

```bash
echo "// Add logging" >> backend/src/utils/priority.js
git commit -am "chore(utils): add logging"
```

---

**Commit 5 : Bon**

```bash
echo "// Add validation" >> backend/src/utils/priority.js
git commit -am "feat(utils): add validation"
```

---

**Marquer les commits pour la démonstration :**

```bash
git tag good-commit HEAD~4  # Commit 1
git tag bad-commit HEAD     # Commit 5
```

---

**Emma lance bisect :**

```bash
git bisect start
```

---

**Marquer le mauvais commit (actuel) :**

```bash
git bisect bad
```

**Ou :**

```bash
git bisect bad HEAD
```

---

**Marquer le bon commit (il y a 4 commits) :**

```bash
git bisect good HEAD~4
```

**Ou utiliser le hash/tag :**

```bash
git bisect good good-commit
```

---

**Résultat :**

```
Bisecting: 1 revision left to test after this (roughly 1 step)
[commit-hash] refactor(utils): optimize priority calculation
```

**Git a checké le commit du milieu !**

---

**Emma teste si ce commit a le bug :**

**Créer un script de test :**

```bash
cat > test-priority.js << 'EOF'
const { calculatePriority } = require('./backend/src/utils/priority');

const task = { priority: 'high' };
const result = calculatePriority(task);

if (result === 3) {
  console.log('[OK] GOOD: Priority calculation works');
  process.exit(0);
} else {
  console.log('[X] BAD: Priority calculation broken, got:', result);
  process.exit(1);
}
EOF
```

---

**Exécuter le test :**

```bash
node test-priority.js
```

**Résultat :**

```
[X] BAD: Priority calculation broken, got: 2
```

**Ce commit a le bug ! [X]**

---

**Marquer comme mauvais :**

```bash
git bisect bad
```

---

**Git checke un autre commit :**

```
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[commit-hash] feat(utils): add priority label function
```

---

**Tester :**

```bash
node test-priority.js
```

```
[OK] GOOD: Priority calculation works
```

**Ce commit est bon ! [OK]**

---

**Marquer comme bon :**

```bash
git bisect good
```

---

**Résultat final :**

```
abc123def456 is the first bad commit
commit abc123def456
Author: Bob Smith <bob@taskmaster.com>
Date:   Wed Jan 3 16:00:00 2024 +0100

    refactor(utils): optimize priority calculation

backend/src/utils/priority.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
```

**[BRAVO] Git a trouvé le commit coupable ! [BRAVO]**

---

**Voir le diff du commit problématique :**

```bash
git show abc123def456
```

**On voit la typo : `task.priorityy` au lieu de `task.priority`**

---

**Terminer bisect :**

```bash
git bisect reset
```

**Retourne à la branche/commit d'origine.**

---

**Corriger le bug :**

```bash
nano backend/src/utils/priority.js
# Corriger: priorityy -> priority

git commit -am "fix(utils): correct typo in priority calculation

Bug introduced in commit abc123def456
Found using git bisect"
```

---

**Bisect automatisé :**

Si tu as un script de test automatique :

```bash
git bisect start
git bisect bad HEAD
git bisect good v1.0.0

# Git exécute le script automatiquement à chaque étape
git bisect run node test-priority.js
```

**Git teste automatiquement tous les commits et trouve le coupable !**

---

### ÉTAPE 6 : Git Reset - Annuler des commits

**Git reset** a 3 modes : `--soft`, `--mixed`, `--hard`

**Comprendre les 3 zones :**

```
Working Directory -> (git add) -> Staging Area -> (git commit) -> Repository
      ^                              ^                            ^
   Fichiers              Index/Stage                      Commits (.git)
```

---

**Les 3 modes de reset :**

| Mode | Repository | Staging Area | Working Directory |
|------|------------|--------------|-------------------|
| `--soft` | [OK] Réinitialise | [X] Garde | [X] Garde |
| `--mixed` (défaut) | [OK] Réinitialise | [OK] Réinitialise | [X] Garde |
| `--hard` | [OK] Réinitialise | [OK] Réinitialise | [OK] Réinitialise |

---

#### 6.1 Reset --soft

**Usage :** Annuler des commits mais garder les changements staged

**Situation :** Bob veut fusionner 3 commits en 1 (alternative au rebase).

```bash
cd ~/bob-workspace/taskmaster-pro
git switch -c feature/test-reset

# 3 commits
echo "file1" > file1.txt
git add file1.txt
git commit -m "commit 1"

echo "file2" > file2.txt
git add file2.txt
git commit -m "commit 2"

echo "file3" > file3.txt
git add file3.txt
git commit -m "commit 3"
```

---

**Historique :**

```bash
git log --oneline
```

```
c3c3c3c (HEAD -> feature/test-reset) commit 3
b2b2b2b commit 2
a1a1a1a commit 1
z0z0z0z (develop) Previous commit
```

---

**Annuler les 3 commits MAIS garder les fichiers :**

```bash
git reset --soft HEAD~3
```

---

**Vérifier :**

```bash
git status
```

```
On branch feature/test-reset
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   file1.txt
        new file:   file2.txt
        new file:   file3.txt
```

**[OK] Les 3 fichiers sont STAGED (prêts à être commités) !**

**[OK] Les fichiers existent dans le working directory !**

---

**Historique :**

```bash
git log --oneline
```

```
z0z0z0z (HEAD -> feature/test-reset, develop) Previous commit
```

**[OK] Les 3 commits ont disparu !**

---

**Créer UN seul commit avec tout :**

```bash
git commit -m "feat: add files 1, 2, and 3"
```

**[OK] 3 commits -> 1 commit propre !**

---

#### 6.2 Reset --mixed (défaut)

**Usage :** Annuler des commits ET unstage les fichiers

```bash
git reset HEAD~1
# Équivalent à:
git reset --mixed HEAD~1
```

---

**Situation :**

```bash
echo "test" > test.txt
git add test.txt
git commit -m "add test"

git reset --mixed HEAD~1
```

---

**Vérifier :**

```bash
git status
```

```
On branch feature/test-reset
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        test.txt
```

**[OK] Commit annulé**
**[OK] Fichier UNSTAGED (pas dans staging area)**
**[OK] Fichier existe dans working directory**

---

**Usage typique :** "Oops, j'ai commité trop tôt, je veux continuer à travailler"

---

#### 6.3 Reset --hard [ATTENTION]

**[ATTENTION] DANGEREUX : Supprime TOUT !**

**Usage :** Annuler des commits ET supprimer les changements

```bash
git reset --hard HEAD~1
```

**[ATTENTION] PERTE DÉFINITIVE des changements dans le working directory !**

---

**Situation :**

```bash
echo "important work" > important.txt
git add important.txt
git commit -m "important commit"

# OOPS ! Mauvaise commande !
git reset --hard HEAD~1
```

---

**Vérifier :**

```bash
git status
```

```
On branch feature/test-reset
nothing to commit, working tree clean
```

**[X] Commit annulé**
**[X] Fichier SUPPRIMÉ du working directory**

```bash
ls important.txt
```

```
ls: cannot access 'important.txt': No such file exists
```

**[!] Fichier perdu ! (sauf reflog)**

---

**Récupérer avec reflog :**

```bash
git reflog

# Trouver le commit "important commit"
git reset --hard abc123  # Hash du commit
```

**[OK] Récupéré !**

---

**Cas d'usage légitime de --hard :**

**Nettoyer complètement le working directory :**

```bash
# Annuler tous les changements non commités
git reset --hard HEAD

# Revenir à l'état exact d'un commit
git reset --hard origin/main
```

---

### ÉTAPE 7 : Git Revert - Annuler sans réécrire l'historique

**Différence Reset vs Revert :**

| | Reset | Revert |
|---|---|---|
| **Historique** | Réécrit (supprime commits) | Conserve (ajoute un commit) |
| **Sécurité** | Dangereux si déjà pushé | Sûr toujours |
| **Usage** | Historique local | Historique public |

---

**Reset :**
```
Before: A -> B -> C -> D (HEAD)
Reset:  A -> B (HEAD)   [C et D supprimés]
```

**Revert :**
```
Before: A -> B -> C -> D (HEAD)
Revert: A -> B -> C -> D -> D' (HEAD)
                         ^
                   Annule les changements de D
```

---

**Situation :** Emma a mergé un commit dans `main` qui introduit un bug en production.

**[ATTENTION] On ne peut PAS utiliser reset (historique public) !**

---

**Emma identifie le commit problématique :**

```bash
git log --oneline
```

```
f6g7h8i (HEAD -> main) feat: add new dashboard
e5f6g7h fix: correct calculation
d4e5f6g feat: add notification  <- Ce commit a un bug !
c3d4e5f Previous commit
```

---

**Revert le commit :**

```bash
git revert d4e5f6g
```

---

**Un éditeur s'ouvre avec le message :**

```
Revert "feat: add notification"

This reverts commit d4e5f6g.
```

**Tu peux ajouter plus de détails :**

```
Revert "feat: add notification"

This reverts commit d4e5f6g.

Reason: Notification system causing crashes in production
Issue: #123
Rolling back until fix is ready
```

**Sauvegarder.**

---

**Résultat :**

```
[main g7h8i9j] Revert "feat: add notification"
 1 file changed, 10 deletions(-)
```

---

**Historique :**

```bash
git log --oneline
```

```
g7h8i9j (HEAD -> main) Revert "feat: add notification"
f6g7h8i feat: add new dashboard
e5f6g7h fix: correct calculation
d4e5f6g feat: add notification
c3d4e5f Previous commit
```

**[OK] Nouveau commit qui ANNULE les changements de d4e5f6g !**

**[OK] Historique préservé (traçabilité) !**

---

**Push vers production :**

```bash
git push origin main
```

**[OK] Déploiement immédiat du rollback !**

---

**Revert multiple commits :**

```bash
# Revert les 3 derniers commits
git revert HEAD~2..HEAD

# Ou individuellement
git revert abc123
git revert def456
git revert ghi789
```

---

**Revert d'un merge commit :**

```bash
# Merge commits ont 2 parents
git revert -m 1 abc123

# -m 1 = garder le parent 1 (généralement main)
# -m 2 = garder le parent 2 (branche mergée)
```

---

### ÉTAPE 8 : Git Blame - Trouver qui a modifié quoi

**Git blame** = Voir qui a modifié chaque ligne d'un fichier (et quand)

**Usage :**
- Trouver l'auteur d'un bug
- Comprendre pourquoi une ligne existe
- Contacter la personne qui connaît le contexte

---

**Situation :** Diana veut savoir qui a écrit une fonction spécifique.

```bash
git blame backend/src/auth/auth.service.js
```

**Résultat :**

```
a1b2c3d4 (Bob Smith    2024-01-02 14:30:15 +0100  1) /**
a1b2c3d4 (Bob Smith    2024-01-02 14:30:15 +0100  2)  * Authentication Service
a1b2c3d4 (Bob Smith    2024-01-02 14:30:15 +0100  3)  */
a1b2c3d4 (Bob Smith    2024-01-02 14:30:15 +0100  4) 
a1b2c3d4 (Bob Smith    2024-01-02 14:30:15 +0100  5) const jwt = require('jsonwebtoken');
b2c3d4e5 (Alice Johnson 2024-01-03 10:15:22 +0100  6) const bcrypt = require('bcryptjs');
a1b2c3d4 (Bob Smith    2024-01-02 14:30:15 +0100  7) 
a1b2c3d4 (Bob Smith    2024-01-02 14:30:15 +0100  8) class AuthService {
c3d4e5f6 (Bob Smith    2024-01-02 16:45:30 +0100  9)   generateToken(user) {
c3d4e5f6 (Bob Smith    2024-01-02 16:45:30 +0100 10)     const payload = {
...
```

**Format :**

```
[commit-hash] ([auteur] [date] [ligne]) [code]
```

---

**Blame sur une plage de lignes :**

```bash
# Lignes 10 à 20
git blame -L 10,20 file.js

# À partir de la ligne 50
git blame -L 50, file.js
```

---

**Voir le commit complet :**

```bash
git show a1b2c3d4
```

---

**Blame avec -w (ignorer les whitespaces) :**

```bash
git blame -w file.js
```

**Utile si quelqu'un a reformaté le fichier.**

---

**Blame avec -C (détecter les copies) :**

```bash
git blame -C file.js
```

**Détecte si le code vient d'un autre fichier (copié-collé).**

---

**Alternative : git gui blame (interface graphique) :**

```bash
git gui blame file.js
```

**Ouvre une interface graphique interactive.**

---

**[ATTENTION] Utiliser blame avec respect :**

Blame n'est PAS pour :
- [X] Blâmer quelqu'un pour un bug
- [X] Critiquer le code publiquement
- [X] Compter les lignes de code par personne

Blame est pour :
- [OK] Comprendre le contexte d'une ligne
- [OK] Contacter l'auteur pour des questions
- [OK] Trouver le commit qui a introduit un changement

---

### ÉTAPE 9 : Stash avancé

**Rappel : git stash sauvegarde temporairement le travail en cours.**

**Commandes de base :**
```bash
git stash        # Sauvegarder
git stash pop    # Restaurer et supprimer
git stash apply  # Restaurer sans supprimer
git stash list   # Lister
git stash drop   # Supprimer
```

---

#### 9.1 Stash avec message

```bash
git stash save "WIP: working on notification feature"
```

**Ou (Git 2.16+) :**

```bash
git stash push -m "WIP: notification feature"
```

---

**Liste :**

```bash
git stash list
```

```
stash@{0}: On feature/notifications: WIP: notification feature
stash@{1}: WIP on develop: a1b2c3d some commit
```

**Plus facile à identifier ! [OK]**

---

#### 9.2 Stash partiel (fichiers spécifiques)

**Stash seulement certains fichiers :**

```bash
git stash push -m "Stash only backend" backend/
```

**Ou :**

```bash
git stash push file1.js file2.js
```

---

#### 9.3 Stash interactif

```bash
git stash push --patch
```

**Git demande pour CHAQUE changement :**

```
Stash this hunk [y,n,q,a,d,e,?]?
```

**Options :**
- `y` = Yes, stash this hunk
- `n` = No, don't stash
- `q` = Quit (stash le reste)
- `a` = Stash all remaining hunks
- `d` = Don't stash remaining hunks
- `e` = Edit manually
- `?` = Help

**Utile pour stash seulement une partie des modifications !**

---

#### 9.4 Stash avec fichiers non trackés

**Par défaut, git stash ignore les fichiers non trackés.**

**Inclure les untracked files :**

```bash
git stash -u
# ou
git stash --include-untracked
```

---

**Inclure TOUT (même .gitignore files) :**

```bash
git stash -a
# ou
git stash --all
```

---

#### 9.5 Créer une branche depuis un stash

**Situation :** Tu as stashé du travail, mais tu veux le continuer sur une nouvelle branche.

```bash
git stash
git stash branch new-feature-branch
```

**Résultat :**
1. Crée `new-feature-branch`
2. Checkout cette branche
3. Applique le stash
4. Supprime le stash

**Équivalent à :**

```bash
git stash
git switch -c new-feature-branch
git stash pop
```

---

#### 9.6 Voir le contenu d'un stash

```bash
# Voir les fichiers modifiés
git stash show

# Voir le diff complet
git stash show -p

# Pour un stash spécifique
git stash show -p stash@{2}
```

---

#### 9.7 Appliquer un stash sur une autre branche

```bash
# Sur branch A
git stash

# Changer de branche
git switch branch-B

# Appliquer le stash de A
git stash pop
```

**[OK] Tu peux déplacer le travail entre branches !**

---

### [OK] TESTS DE VALIDATION

**1. Rebase interactif**

```bash
# Créer 3 commits
git switch -c test-rebase
echo "1" > test.txt && git add test.txt && git commit -m "commit 1"
echo "2" >> test.txt && git commit -am "commit 2"
echo "3" >> test.txt && git commit -am "commit 3"

# Squasher en 1
git rebase -i HEAD~3

# Vérifier
git log --oneline
```

- [ ] 3 commits squashés en 1

---

**2. Cherry-pick**

```bash
# Créer branche A
git switch -c branch-a
echo "feature A" > feature-a.txt
git add feature-a.txt
git commit -m "feat: add feature A"

COMMIT_HASH=$(git rev-parse HEAD)

# Créer branche B
git switch develop
git switch -c branch-b

# Cherry-pick de A
git cherry-pick $COMMIT_HASH

# Vérifier
ls feature-a.txt
```

- [ ] Commit copié avec succès

---

**3. Reflog**

```bash
# Créer et supprimer une branche
git switch -c temp-branch
echo "temp" > temp.txt
git add temp.txt
git commit -m "temp work"

git switch develop
git branch -D temp-branch

# Récupérer avec reflog
git reflog
git branch temp-branch-recovered HEAD@{1}

# Vérifier
git switch temp-branch-recovered
ls temp.txt
```

- [ ] Branche récupérée

---

**4. Bisect**

```bash
# Créer un scénario avec bug
git switch -c test-bisect

# 5 commits, le 3ème a un bug
for i in {1..5}; do
  if [ $i -eq 3 ]; then
    echo "buggy code" > code.txt
  else
    echo "good code $i" > code.txt
  fi
  git add code.txt
  git commit -m "commit $i"
done

# Lancer bisect
git bisect start
git bisect bad HEAD
git bisect good HEAD~4

# Tester et marquer
# ...

git bisect reset
```

- [ ] Bug trouvé avec bisect

---

**5. Reset modes**

```bash
# Test --soft
echo "test" > test.txt
git add test.txt
git commit -m "test"
git reset --soft HEAD~1
git status  # Fichier doit être staged

# Test --mixed
git commit -m "test"
git reset --mixed HEAD~1
git status  # Fichier doit être unstaged

# Test --hard
git add test.txt
git commit -m "test"
git reset --hard HEAD~1
ls test.txt 2>/dev/null && echo "File exists" || echo "File deleted"
```

- [ ] Les 3 modes fonctionnent correctement

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : "Cannot rebase: You have unstaged changes"

**Symptôme :**

```
error: cannot rebase: You have unstaged changes.
error: Please commit or stash them.
```

**Solution :**

```bash
git stash
git rebase -i HEAD~3
git stash pop
```

---

#### Erreur 2 : Conflit lors d'un rebase interactif

**Symptôme :**

```
CONFLICT (content): Merge conflict in file.txt
error: could not apply abc123... commit message
Resolve all conflicts manually, mark them as resolved with
"git add/rm <conflicted_files>", then run "git rebase --continue".
```

**Solution :**

```bash
# 1. Résoudre les conflits
nano file.txt

# 2. Marquer comme résolu
git add file.txt

# 3. Continuer le rebase
git rebase --continue

# Ou abandonner
git rebase --abort
```

---

#### Erreur 3 : "Detached HEAD" après bisect

**Symptôme :**

```
You are in 'detached HEAD' state.
```

**Solution :**

```bash
# Terminer bisect proprement
git bisect reset

# Ou revenir à une branche
git switch main
```

---

#### Erreur 4 : Force push rejeté après rebase

**Symptôme :**

```
! [rejected]        feature-branch -> feature-branch (non-fast-forward)
```

**Solution :**

```bash
# Vérifier que personne d'autre ne travaille sur la branche
git push --force-with-lease origin feature-branch
```

**[ATTENTION] Jamais sur main/develop !**

---

#### Erreur 5 : Reflog vide

**Symptôme :**

```
fatal: your current branch 'main' does not have any commits yet
```

**Cause :** Nouveau dépôt sans commits

**Solution :**

Faire au moins un commit avant d'utiliser reflog.

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Rebase interactif**

```bash
git rebase -i HEAD~N

pick    # Garder
reword  # Changer message
edit    # Modifier contenu
squash  # Fusionner (garder messages)
fixup   # Fusionner (ignorer message)
drop    # Supprimer
```

---

**2. Cherry-pick**

```bash
# Copier un commit
git cherry-pick abc123

# Copier plusieurs
git cherry-pick abc123 def456

# Plage
git cherry-pick abc123..def456
```

---

**3. Reflog**

```bash
# Voir l'historique de HEAD
git reflog

# Récupérer un commit
git reset --hard HEAD@{N}

# Récupérer une branche
git branch recovered-branch HEAD@{N}
```

---

**4. Bisect**

```bash
git bisect start
git bisect bad HEAD
git bisect good v1.0.0

# Tester, puis:
git bisect good  # ou bad

git bisect reset
```

---

**5. Reset**

| Mode | Repository | Staging | Working Dir |
|------|-----------|---------|-------------|
| `--soft` | Reset | Keep | Keep |
| `--mixed` | Reset | Reset | Keep |
| `--hard` | Reset | Reset | **RESET** [ATTENTION] |

---

**6. Revert**

```bash
# Annuler un commit (historique public)
git revert abc123

# Annuler un merge
git revert -m 1 abc123
```

---

**7. Règle d'or**

**[X] Ne JAMAIS réécrire l'historique public !**

Public = déjà pushé et partagé avec l'équipe

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Filter-branch (réécrire tout l'historique)**

**[ATTENTION] TRÈS DANGEREUX !**

```bash
# Supprimer un fichier de TOUT l'historique
git filter-branch --tree-filter 'rm -f passwords.txt' HEAD
```

**Alternative moderne (plus rapide) :**

```bash
# Utiliser git-filter-repo
pip install git-filter-repo

git filter-repo --path passwords.txt --invert-paths
```

---

**2. Rerere (Reuse Recorded Resolution)**

**Mémorise les résolutions de conflits :**

```bash
git config --global rerere.enabled true
```

**Si le même conflit réapparaît, Git le résout automatiquement ! [BRAVO]**

---

**3. Worktree (plusieurs working directories)**

**Travailler sur plusieurs branches simultanément :**

```bash
# Créer un worktree
git worktree add ../taskmaster-hotfix hotfix/urgent

# Maintenant tu as:
# ~/taskmaster-pro/ (develop)
# ~/taskmaster-hotfix/ (hotfix/urgent)

# Travailler dans les deux en même temps !

# Supprimer
git worktree remove ../taskmaster-hotfix
```

---

**4. Rebase --onto (rebase avancé)**

**Rebaser une branche sur une autre cible :**

```bash
# Situation:
# main: A - B - C
# feature1: A - B - D - E
# feature2: A - B - D - E - F - G

# Rebaser feature2 (F-G) directement sur main
git rebase --onto main feature1 feature2

# Résultat:
# main: A - B - C - F' - G'
```

---

**5. Interactive staging (git add -p)**

**Ajouter seulement certaines parties d'un fichier :**

```bash
git add -p file.txt
```

**Git demande pour chaque "hunk" :**

```
Stage this hunk [y,n,q,a,d,s,e,?]?
```

**Utile pour créer des commits atomiques !**

---

**6. Git notes (annotations de commits)**

**Ajouter des notes aux commits sans les modifier :**

```bash
# Ajouter une note
git notes add -m "This commit caused issues in prod" abc123

# Voir les notes
git log --show-notes

# Push les notes
git push origin refs/notes/*
```

---

**7. Git hooks personnalisés**

**Automatiser des tâches :**

```bash
# .git/hooks/pre-commit
#!/bin/sh
# Empêcher de commiter des TODOs

if git diff --cached | grep -i "TODO"; then
    echo "Error: Commit contains TODO"
    exit 1
fi
```

```bash
chmod +x .git/hooks/pre-commit
```

---

**8. Git attributes pour merge drivers**

**Définir comment merger certains fichiers :**

```bash
# .gitattributes
*.json merge=union
```

**Merge union = combine les deux versions sans conflit**

---

## [COURS] CONCLUSION DE L'EXERCICE 7

**[OK] Félicitations ! Tu maîtrises les opérations Git avancées !**

**Ce que tu as appris :**
- Rebase interactif (squash, reword, edit, drop)
- Cherry-pick pour copier des commits
- Reflog pour récupérer le travail "perdu"
- Bisect pour trouver les bugs
- Reset (soft, mixed, hard)
- Revert pour annuler sans réécrire
- Blame pour analyser l'historique
- Stash avancé

**Compétences acquises :**
- [OK] Manipulation d'historique (niveau expert)
- [OK] Récupération de données
- [OK] Debugging avancé
- [OK] Opérations Git complexes
- [OK] Sécurité et bonnes pratiques

**Temps moyen de réalisation :** 4-5 heures

**Prochaine étape :** Exercice 8 - Git Hooks et Automation ! [HOOK]

---

(Il reste 3 exercices : 8, 9, 10. Continue ?)

# [ROUGE] EXERCICE 8 : GIT HOOKS ET AUTOMATION

## [LISTE] ÉNONCÉ

### Contexte professionnel

L'équipe TaskMaster Pro veut **automatiser la qualité du code** et **prévenir les erreurs** avant qu'elles n'arrivent dans le dépôt. **Alice** décide de mettre en place des **Git Hooks** pour :
- Vérifier le code avant chaque commit
- Forcer les messages de commit conventionnels
- Exécuter les tests avant chaque push
- Empêcher les secrets d'être commités
- Automatiser le formatage du code
- Valider les PRs automatiquement

**Problèmes actuels :**
- [PERSONNE][CODE] **Bob** a commité un fichier `.env` avec des secrets
- [PERSONNE][CODE] **Charlie** oublie souvent de linter son code
- [PERSONNE][CODE] **Diana** push sans lancer les tests
- [PERSONNE][CODE] **Emma** écrit des messages de commit non conventionnels
- [BUG] Code non formaté arrive régulièrement dans `develop`

**Aujourd'hui, l'équipe va :**
1. Comprendre les Git Hooks (client et server)
2. Installer et configurer Husky
3. Mettre en place lint-staged (lint seulement les fichiers modifiés)
4. Configurer commitlint (messages conventionnels)
5. Créer des hooks personnalisés
6. Automatiser les tests avant push
7. Prévenir les commits de secrets
8. Formater automatiquement le code

### Objectifs

- Maîtriser les Git Hooks (types, cycle de vie)
- Installer et configurer Husky
- Mettre en place lint-staged + ESLint/Prettier
- Forcer les conventional commits avec commitlint
- Créer des hooks personnalisés
- Automatiser la qualité du code
- Prévenir les erreurs communes

### Livrables

- Husky configuré dans le projet
- Au moins 5 hooks actifs (pre-commit, commit-msg, pre-push, etc.)
- ESLint + Prettier configurés
- Commitlint fonctionnel
- Hook personnalisé pour détecter les secrets
- Documentation du système de hooks
- Tests d'intégration des hooks

### Contraintes

- Hooks doivent être partagés avec toute l'équipe (via Git)
- Performances : hooks rapides (< 5 secondes)
- Pas de faux positifs agaçants
- Possibilité de bypass en cas d'urgence (avec --no-verify)
- Compatible Windows, macOS, Linux
- Temps estimé : 4-5 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :

- [OK] Comprendre les Git Hooks (client-side et server-side)
- [OK] Configurer Husky pour gérer les hooks
- [OK] Mettre en place lint-staged
- [OK] Forcer les conventional commits
- [OK] Créer des hooks personnalisés
- [OK] Automatiser le linting et formatting
- [OK] Détecter les secrets avant commit
- [OK] Exécuter les tests avant push
- [OK] Gérer les hooks dans une équipe

---

## [DOCS] PRÉREQUIS

- Exercices 1 à 7 terminés
- Node.js et npm installés
- Compréhension de JavaScript/Shell scripting
- Familiarité avec ESLint/Prettier (bonus)

---

## [GUIDE] THÉORIE : Git Hooks expliqués

### Qu'est-ce qu'un Git Hook ?

**Git Hook** = Script exécuté automatiquement à certains moments du workflow Git

**Analogie :**
Comme les hooks (crochets) dans une application web qui s'exécutent avant/après certaines actions, Git peut exécuter des scripts avant/après commit, push, merge, etc.

---

### Types de hooks

**2 catégories :**

**1. Client-side hooks** (sur ta machine)
- Déclenchés par des opérations locales
- Exemples : commit, merge, checkout, push

**2. Server-side hooks** (sur le serveur Git)
- Déclenchés sur le serveur (GitHub, GitLab, etc.)
- Exemples : réception de push, mise à jour de branches

**On va se concentrer sur les client-side hooks.**

---

### Client-side hooks principaux

| Hook | Quand | Usage typique |
|------|-------|---------------|
| **pre-commit** | Avant le commit | Linting, tests, formatage |
| **prepare-commit-msg** | Avant l'éditeur de message | Générer template de message |
| **commit-msg** | Après écriture du message | Valider le format du message |
| **post-commit** | Après le commit | Notifications, analytics |
| **pre-push** | Avant le push | Tests complets, build |
| **post-merge** | Après un merge | Installer dépendances, migrations |
| **post-checkout** | Après checkout | Nettoyer fichiers générés |

---

### Cycle de vie d'un commit

```
git commit
    v
pre-commit (vérifications avant commit)
    v
prepare-commit-msg (préparer le message)
    v
[Éditeur de message s'ouvre]
    v
commit-msg (valider le message)
    v
[Commit créé]
    v
post-commit (actions après commit)
```

---

### Où sont les hooks ?

**Hooks natifs Git :**

```
.git/hooks/
├── pre-commit.sample
├── commit-msg.sample
├── pre-push.sample
└── ...
```

**[ATTENTION] Problème :** Le dossier `.git/` n'est PAS versionné !

**Les hooks ne sont pas partagés avec l'équipe !**

---

### Solution : Husky

**Husky** = Outil qui permet de versionner et partager les hooks Git

**Avantages :**
- [OK] Hooks versionnés dans le dépôt
- [OK] Partagés automatiquement avec l'équipe
- [OK] Facile à configurer
- [OK] Compatible avec npm/yarn
- [OK] Configuration simple

**Comment ça marche :**
1. Husky crée un dossier `.husky/` (versionné)
2. Scripts dans `.husky/` sont exécutés comme hooks
3. Lors du `npm install`, Husky configure `.git/hooks/` automatiquement

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Installer Husky

**Alice configure Husky pour le projet :**

```bash
cd ~/taskmaster-pro
git switch develop
git pull origin develop
```

---

**Initialiser npm si pas déjà fait :**

```bash
# Vérifier si package.json existe à la racine
ls package.json

# Si non, créer
npm init -y
```

---

**Installer Husky :**

```bash
npm install --save-dev husky
```

---

**Initialiser Husky (Git hooks) :**

```bash
npx husky init
```

**Ou (ancienne méthode pour Husky v7+) :**

```bash
npx husky install
```

---

**Résultat :**

```
.husky/
└── pre-commit (créé automatiquement)

package.json (modifié)
```

---

**Vérifier package.json :**

```json
{
  "scripts": {
    "prepare": "husky install"
  },
  "devDependencies": {
    "husky": "^8.0.0"
  }
}
```

**`prepare`** = Script npm exécuté automatiquement après `npm install`

**Quand un développeur clone le projet :**
```bash
git clone ...
cd taskmaster-pro
npm install  # <- Husky s'installe automatiquement !
```

---

**Vérifier que Husky fonctionne :**

```bash
cat .husky/pre-commit
```

**Contenu par défaut :**

```bash
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm test
```

---

**Tester :**

```bash
echo "test" > test.txt
git add test.txt
git commit -m "test: husky"
```

**Résultat :**

```
npm test
... (tests s'exécutent)
```

**[OK] Husky fonctionne !**

---

**Commiter la configuration Husky :**

```bash
git add .husky/ package.json package-lock.json
git commit -m "chore: setup Husky for Git hooks

- Install Husky v8
- Add prepare script for automatic installation
- Initialize pre-commit hook"
```

---

### ÉTAPE 2 : Pre-commit hook - ESLint et Prettier

**Objectif :** Vérifier et formater le code AVANT chaque commit.

**Installer ESLint et Prettier :**

```bash
cd backend

npm install --save-dev \
  eslint \
  prettier \
  eslint-config-prettier \
  eslint-plugin-prettier
```

---

**Configurer ESLint :**

```bash
cat > backend/.eslintrc.js << 'EOF'
module.exports = {
  env: {
    node: true,
    es2021: true,
    jest: true,
  },
  extends: [
    'eslint:recommended',
    'plugin:prettier/recommended'
  ],
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
  },
  rules: {
    // Erreurs
    'no-console': 'warn',
    'no-unused-vars': ['error', { 
      argsIgnorePattern: '^_',
      varsIgnorePattern: '^_' 
    }],
    
    // Code quality
    'prefer-const': 'error',
    'no-var': 'error',
    'object-shorthand': 'error',
    'prefer-template': 'error',
    
    // Prettier (formatting)
    'prettier/prettier': 'error',
  },
};
EOF
```

---

**Configurer Prettier :**

```bash
cat > backend/.prettierrc << 'EOF'
{
  "semi": true,
  "trailingComma": "es5",
  "singleQuote": true,
  "printWidth": 80,
  "tabWidth": 2,
  "arrowParens": "always"
}
EOF
```

---

**Ajouter scripts dans package.json :**

```bash
cat > backend/package.json << 'EOF'
{
  "name": "taskmaster-backend",
  "version": "1.0.1",
  "scripts": {
    "start": "node src/index.js",
    "dev": "nodemon src/index.js",
    "test": "jest",
    "lint": "eslint src/",
    "lint:fix": "eslint src/ --fix",
    "format": "prettier --write \"src/**/*.js\"",
    "format:check": "prettier --check \"src/**/*.js\""
  },
  "devDependencies": {
    "eslint": "^8.54.0",
    "prettier": "^3.1.0",
    "eslint-config-prettier": "^9.0.0",
    "eslint-plugin-prettier": "^5.0.1",
    "jest": "^29.7.0",
    "nodemon": "^3.0.1"
  }
}
EOF
```

---

**Installer lint-staged :**

**lint-staged** = Exécute le linter SEULEMENT sur les fichiers staged (modifiés et ajoutés à Git)

**Pourquoi ?**
- [OK] Plus rapide (ne lint pas tout le projet)
- [OK] Ne touche que les fichiers que tu commites
- [OK] Pas de conflits avec le code existant

```bash
cd ~/taskmaster-pro
npm install --save-dev lint-staged
```

---

**Configurer lint-staged dans package.json (racine) :**

```json
{
  "lint-staged": {
    "backend/src/**/*.js": [
      "eslint --fix",
      "prettier --write"
    ],
    "frontend/src/**/*.{js,jsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "*.{json,md,yml,yaml}": [
      "prettier --write"
    ]
  }
}
```

---

**Modifier le hook pre-commit :**

```bash
cat > .husky/pre-commit << 'EOF'
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

# Run lint-staged on staged files
npx lint-staged
EOF
```

---

**Rendre le script exécutable :**

```bash
chmod +x .husky/pre-commit
```

---

**Tester :**

**Créer un fichier avec du code mal formaté :**

```bash
cat > backend/src/test-linting.js << 'EOF'
const   x=1;
var y = 2
const z = 3

function test( ) {
console.log("test")
}

module.exports={test}
EOF
```

---

**Ajouter et commiter :**

```bash
git add backend/src/test-linting.js
git commit -m "test: linting"
```

---

**Résultat :**

```
[OK] Preparing lint-staged...
[ATTENTION] Running tasks for staged files...
  [ATTENTION] backend/src/**/*.js — 3 files
    [X] eslint --fix [FAILED]
    [BLACK_MEDIUM_SQUARE] prettier --write
v Skipped because of errors from tasks.
[OK] Reverting to original state because of errors...
[OK] Cleaning up temporary files...

[X] eslint --fix:

/home/alice/taskmaster-pro/backend/src/test-linting.js
  1:7   error  'x' is assigned a value but never used  no-unused-vars
  2:1   error  Unexpected var, use let or const instead  no-var
  2:5   error  'y' is assigned a value but never used  no-unused-vars
  3:7   error  'z' is assigned a value but never used  no-unused-vars
  6:1   error  Unexpected console statement  no-console

[X] 5 problems (5 errors, 0 warnings)
  1 error and 0 warnings potentially fixable with the `--fix` option.

husky - pre-commit hook exited with code 1 (error)
```

**[X] Commit bloqué ! Le code ne respecte pas les règles !**

---

**Corriger le code :**

```bash
cat > backend/src/test-linting.js << 'EOF'
const test = () => {
  // Using console for demo purposes
  // eslint-disable-next-line no-console
  console.log('test');
};

module.exports = { test };
EOF
```

---

**Re-essayer :**

```bash
git add backend/src/test-linting.js
git commit -m "test: linting"
```

---

**Résultat :**

```
[OK] Preparing lint-staged...
[OK] Running tasks for staged files...
[OK] Applying modifications from tasks...
[OK] Cleaning up temporary files...
[develop abc123] test: linting
 1 file changed, 7 insertions(+)
```

**[OK] Commit accepté ! Code formaté automatiquement !**

---

**Vérifier que le code a été formaté :**

```bash
cat backend/src/test-linting.js
```

**Code propre et formaté par Prettier ! ***

---

### ÉTAPE 3 : Commit-msg hook - Commitlint

**Objectif :** Forcer les messages de commit conventionnels.

**Installer commitlint :**

```bash
npm install --save-dev \
  @commitlint/cli \
  @commitlint/config-conventional
```

---

**Configurer commitlint :**

```bash
cat > commitlint.config.js << 'EOF'
module.exports = {
  extends: ['@commitlint/config-conventional'],
  rules: {
    'type-enum': [
      2,
      'always',
      [
        'feat',     // Nouvelle fonctionnalité
        'fix',      // Correction de bug
        'docs',     // Documentation
        'style',    // Formatage (pas de changement de code)
        'refactor', // Refactoring
        'perf',     // Amélioration de performance
        'test',     // Ajout/modification de tests
        'build',    // Changements du système de build
        'ci',       // Changements de CI/CD
        'chore',    // Tâches de maintenance
        'revert',   // Revert d'un commit
      ],
    ],
    'type-case': [2, 'always', 'lower-case'],
    'type-empty': [2, 'never'],
    'scope-case': [2, 'always', 'lower-case'],
    'subject-empty': [2, 'never'],
    'subject-full-stop': [2, 'never', '.'],
    'header-max-length': [2, 'always', 100],
    'body-leading-blank': [2, 'always'],
    'footer-leading-blank': [2, 'always'],
  },
};
EOF
```

---

**Créer le hook commit-msg :**

```bash
cat > .husky/commit-msg << 'EOF'
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

# Validate commit message format
npx --no -- commitlint --edit $1
EOF
```

```bash
chmod +x .husky/commit-msg
```

---

**Tester avec un mauvais message :**

```bash
echo "test" > test.txt
git add test.txt
git commit -m "added some stuff"
```

---

**Résultat :**

```
⧗   input: added some stuff
[X]   subject may not be empty [subject-empty]
[X]   type may not be empty [type-empty]

[X]   found 2 problems, 0 warnings
ⓘ   Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint

husky - commit-msg hook exited with code 1 (error)
```

**[X] Message rejeté !**

---

**Tester avec un bon message :**

```bash
git commit -m "feat: add new feature"
```

**[OK] Accepté !**

---

**Tester avec scope :**

```bash
git commit -m "feat(auth): add JWT authentication"
```

**[OK] Accepté !**

---

**Tester avec body et footer :**

```bash
git commit -m "fix(api): correct null pointer exception

The API was crashing when user object was null.
Now we check for null before accessing properties.

Closes #42"
```

**[OK] Accepté !**

---

**Messages invalides (exemples) :**

```bash
git commit -m "Fixed stuff"           # [X] Pas de type
git commit -m "feat Added feature"    # [X] Pas de ":"
git commit -m "FEAT: new feature"     # [X] Uppercase
git commit -m "feat: add feature."    # [X] Point final
git commit -m "feat:"                 # [X] Subject vide
```

---

### ÉTAPE 4 : Pre-push hook - Tests automatiques

**Objectif :** Exécuter les tests avant chaque push.

**Créer le hook pre-push :**

```bash
cat > .husky/pre-push << 'EOF'
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

echo "[TEST] Running tests before push..."

# Backend tests
cd backend
npm test -- --passWithNoTests
BACKEND_EXIT=$?

if [ $BACKEND_EXIT -ne 0 ]; then
  echo "[X] Backend tests failed. Push aborted."
  exit 1
fi

echo "[OK] All tests passed!"
EOF
```

```bash
chmod +x .husky/pre-push
```

---

**Créer des tests simples :**

```bash
cat > backend/src/utils/math.js << 'EOF'
function add(a, b) {
  return a + b;
}

function subtract(a, b) {
  return a - b;
}

module.exports = { add, subtract };
EOF
```

---

```bash
cat > backend/src/utils/math.test.js << 'EOF'
const { add, subtract } = require('./math');

describe('Math utils', () => {
  describe('add', () => {
    it('should add two numbers', () => {
      expect(add(2, 3)).toBe(5);
      expect(add(-1, 1)).toBe(0);
      expect(add(0, 0)).toBe(0);
    });
  });

  describe('subtract', () => {
    it('should subtract two numbers', () => {
      expect(subtract(5, 3)).toBe(2);
      expect(subtract(0, 5)).toBe(-5);
    });
  });
});
EOF
```

---

**Configurer Jest (si pas déjà fait) :**

```bash
cat > backend/jest.config.js << 'EOF'
module.exports = {
  testEnvironment: 'node',
  coverageDirectory: 'coverage',
  collectCoverageFrom: [
    'src/**/*.js',
    '!src/**/*.test.js',
  ],
  testMatch: [
    '**/*.test.js',
  ],
};
EOF
```

---

**Tester localement :**

```bash
cd backend
npm test
```

**Résultat :**

```
 PASS  src/utils/math.test.js
  Math utils
    add
      [OK] should add two numbers (2 ms)
    subtract
      [OK] should subtract two numbers (1 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
```

---

**Commiter et essayer de push :**

```bash
cd ~/taskmaster-pro
git add backend/src/utils/
git commit -m "test: add math utils tests"
git push origin develop
```

---

**Résultat :**

```
[TEST] Running tests before push...

> taskmaster-backend@1.0.1 test
> jest --passWithNoTests

 PASS  src/utils/math.test.js
  [OK] Math utils (10 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total

[OK] All tests passed!

Enumerating objects: 15, done.
...
To github.com:alice/taskmaster-pro.git
   abc123..def456  develop -> develop
```

**[OK] Tests passés, push autorisé !**

---

**Si un test échoue :**

```bash
cat > backend/src/utils/math.js << 'EOF'
function add(a, b) {
  return a - b;  // BUG volontaire
}

function subtract(a, b) {
  return a - b;
}

module.exports = { add, subtract };
EOF

git commit -am "bug: introduce bug in add function"
git push origin develop
```

---

**Résultat :**

```
[TEST] Running tests before push...

 FAIL  src/utils/math.test.js
  Math utils
    add
      [X] should add two numbers (4 ms)

  [BLACK_CIRCLE] Math utils › add › should add two numbers

    expect(received).toBe(expected) // Object.is equality

    Expected: 5
    Received: -1

[X] Backend tests failed. Push aborted.
error: failed to push some refs to 'github.com:alice/taskmaster-pro.git'
```

**[X] Push bloqué ! Tests échouent !**

---

**Corriger et re-push :**

```bash
# Corriger le bug
cat > backend/src/utils/math.js << 'EOF'
function add(a, b) {
  return a + b;  // Corrigé
}

function subtract(a, b) {
  return a - b;
}

module.exports = { add, subtract };
EOF

git commit -am "fix: correct add function"
git push origin develop
```

**[OK] Tests passent, push réussit !**

---

### ÉTAPE 5 : Hook personnalisé - Détecter les secrets

**Objectif :** Empêcher de commiter des secrets (API keys, passwords, tokens).

**Créer un script de détection :**

```bash
cat > scripts/check-secrets.sh << 'EOF'
#!/bin/bash

# Patterns de secrets à détecter
PATTERNS=(
  "password\s*=\s*['\"][^'\"]+['\"]"
  "api[_-]?key\s*=\s*['\"][^'\"]+['\"]"
  "secret\s*=\s*['\"][^'\"]+['\"]"
  "token\s*=\s*['\"][^'\"]+['\"]"
  "PRIVATE[_-]KEY"
  "[a-zA-Z0-9_-]*_SECRET[a-zA-Z0-9_-]*\s*=\s*['\"][^'\"]+['\"]"
  "sk_live_[a-zA-Z0-9]{24,}"  # Stripe secret key
  "sk_test_[a-zA-Z0-9]{24,}"  # Stripe test key
  "ghp_[a-zA-Z0-9]{36,}"      # GitHub Personal Access Token
  "glpat-[a-zA-Z0-9_-]{20,}"  # GitLab Personal Access Token
)

# Couleurs
RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

echo "[RECHERCHE] Checking for secrets in staged files..."

# Obtenir les fichiers staged
FILES=$(git diff --cached --name-only --diff-filter=ACM)

if [ -z "$FILES" ]; then
  echo "No files to check."
  exit 0
fi

FOUND_SECRET=0

# Vérifier chaque fichier
for FILE in $FILES; do
  # Ignorer les fichiers binaires et certains types
  if [[ $FILE == *.lock ]] || \
     [[ $FILE == *.jpg ]] || \
     [[ $FILE == *.png ]] || \
     [[ $FILE == *.gif ]] || \
     [[ $FILE == *.pdf ]]; then
    continue
  fi
  
  # Vérifier chaque pattern
  for PATTERN in "${PATTERNS[@]}"; do
    if git diff --cached $FILE | grep -iE "$PATTERN" > /dev/null; then
      echo -e "${RED}[X] Potential secret found in $FILE${NC}"
      echo -e "${YELLOW}   Pattern: $PATTERN${NC}"
      git diff --cached $FILE | grep -iE "$PATTERN" --color=always
      FOUND_SECRET=1
    fi
  done
done

if [ $FOUND_SECRET -eq 1 ]; then
  echo ""
  echo -e "${RED}================================================${NC}"
  echo -e "${RED}[ALERTE] COMMIT BLOCKED: Secrets detected!${NC}"
  echo -e "${RED}================================================${NC}"
  echo ""
  echo "Please remove secrets before committing."
  echo "Options:"
  echo "  1. Move secrets to .env file (and add .env to .gitignore)"
  echo "  2. Use environment variables"
  echo "  3. Use a secrets management service"
  echo ""
  echo "To bypass this check (NOT RECOMMENDED):"
  echo "  git commit --no-verify"
  echo ""
  exit 1
fi

echo "[OK] No secrets detected."
exit 0
EOF
```

```bash
chmod +x scripts/check-secrets.sh
```

---

**Ajouter au pre-commit hook :**

```bash
cat > .husky/pre-commit << 'EOF'
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

# Check for secrets
./scripts/check-secrets.sh

# Run lint-staged on staged files
npx lint-staged
EOF
```

---

**Tester en essayant de commiter un secret :**

```bash
cat > backend/config/test.js << 'EOF'
module.exports = {
  database: {
    password: "super_secret_password_123",
    apiKey: "sk_live_51A234567890abcdefgh"
  }
};
EOF

git add backend/config/test.js
git commit -m "feat: add config"
```

---

**Résultat :**

```
[RECHERCHE] Checking for secrets in staged files...
[X] Potential secret found in backend/config/test.js
   Pattern: password\s*=\s*['\"][^'\"]+['\"]
+    password: "super_secret_password_123",

[X] Potential secret found in backend/config/test.js
   Pattern: sk_live_[a-zA-Z0-9]{24,}
+    apiKey: "sk_live_51A234567890abcdefgh"

================================================
[ALERTE] COMMIT BLOCKED: Secrets detected!
================================================

Please remove secrets before committing.
Options:
  1. Move secrets to .env file (and add .env to .gitignore)
  2. Use environment variables
  3. Use a secrets management service

To bypass this check (NOT RECOMMENDED):
  git commit --no-verify

husky - pre-commit hook exited with code 1 (error)
```

**[X] Commit bloqué ! Secrets détectés !**

---

**Corriger en utilisant des variables d'environnement :**

```bash
cat > backend/config/test.js << 'EOF'
module.exports = {
  database: {
    password: process.env.DB_PASSWORD,
    apiKey: process.env.STRIPE_API_KEY
  }
};
EOF

# Créer .env.example (sans secrets)
cat > backend/.env.example << 'EOF'
DB_PASSWORD=your_password_here
STRIPE_API_KEY=your_stripe_key_here
EOF

# S'assurer que .env est dans .gitignore
echo ".env" >> backend/.gitignore

git add backend/config/test.js backend/.env.example backend/.gitignore
git commit -m "feat: add config with environment variables"
```

---

**Résultat :**

```
[RECHERCHE] Checking for secrets in staged files...
[OK] No secrets detected.

[OK] Preparing lint-staged...
[OK] Running tasks for staged files...
[OK] Applying modifications from tasks...
[OK] Cleaning up temporary files...

[develop xyz789] feat: add config with environment variables
 3 files changed, 12 insertions(+)
```

**[OK] Commit autorisé ! Pas de secrets !**

---

### ÉTAPE 6 : Post-merge hook - Installer les dépendances automatiquement

**Objectif :** Après un merge/pull, installer automatiquement les nouvelles dépendances.

```bash
cat > .husky/post-merge << 'EOF'
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

# Colors
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

echo "[SYNC] Post-merge hook running..."

# Vérifier si package-lock.json a changé
CHANGED_FILES="$(git diff-tree -r --name-only --no-commit-id ORIG_HEAD HEAD)"

# Backend dependencies
if echo "$CHANGED_FILES" | grep --quiet "backend/package-lock.json"; then
  echo -e "${YELLOW}[PACKAGE] Backend dependencies changed, installing...${NC}"
  cd backend && npm ci
  echo -e "${GREEN}[OK] Backend dependencies updated${NC}"
fi

# Root dependencies (Husky, etc.)
if echo "$CHANGED_FILES" | grep --quiet "^package-lock.json"; then
  echo -e "${YELLOW}[PACKAGE] Root dependencies changed, installing...${NC}"
  npm ci
  echo -e "${GREEN}[OK] Root dependencies updated${NC}"
fi

echo "[OK] Post-merge complete"
EOF
```

```bash
chmod +x .husky/post-merge
```

---

**Tester :**

Quand tu fais `git pull` ou `git merge` et que `package-lock.json` a changé, npm s'installe automatiquement ! [BRAVO]

---

### ÉTAPE 7 : Prepare-commit-msg hook - Template de commit

**Objectif :** Générer automatiquement un template de message basé sur la branche.

```bash
cat > .husky/prepare-commit-msg << 'EOF'
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

COMMIT_MSG_FILE=$1
COMMIT_SOURCE=$2

# Ne rien faire si c'est un merge, amend, ou message fourni
if [ "$COMMIT_SOURCE" = "merge" ] || \
   [ "$COMMIT_SOURCE" = "squash" ] || \
   [ "$COMMIT_SOURCE" = "message" ]; then
  exit 0
fi

# Obtenir le nom de la branche
BRANCH_NAME=$(git symbolic-ref --short HEAD 2>/dev/null)

# Extraire le type et scope de la branche
# Exemples: feature/auth-system, fix/login-bug, hotfix/1.0.1
if echo "$BRANCH_NAME" | grep -qE "^(feature|fix|hotfix|chore|docs)/"; then
  TYPE=$(echo "$BRANCH_NAME" | cut -d'/' -f1)
  SCOPE=$(echo "$BRANCH_NAME" | cut -d'/' -f2 | sed 's/-/ /g')
  
  # Mapper les types de branches aux types de commits conventionnels
  case "$TYPE" in
    feature)
      COMMIT_TYPE="feat"
      ;;
    fix|hotfix)
      COMMIT_TYPE="fix"
      ;;
    chore)
      COMMIT_TYPE="chore"
      ;;
    docs)
      COMMIT_TYPE="docs"
      ;;
    *)
      COMMIT_TYPE="$TYPE"
      ;;
  esac
  
  # Générer le template
  TEMPLATE="$COMMIT_TYPE: 

# Please enter the commit message for your changes.
# Branch: $BRANCH_NAME
# Type: $COMMIT_TYPE
# Scope: $SCOPE
#
# Conventional Commit Format:
# <type>(<scope>): <subject>
#
# <body> (optional)
#
# <footer> (optional)
#
# Types: feat, fix, docs, style, refactor, perf, test, chore
"
  
  # Écrire le template dans le fichier de message
  echo "$TEMPLATE" > "$COMMIT_MSG_FILE"
fi
EOF
```

```bash
chmod +x .husky/prepare-commit-msg
```

---

**Tester :**

```bash
git switch -c feature/user-notifications

echo "test" > test.txt
git add test.txt
git commit
```

**L'éditeur s'ouvre avec :**

```
feat: 

# Please enter the commit message for your changes.
# Branch: feature/user-notifications
# Type: feat
# Scope: user notifications
#
# Conventional Commit Format:
# <type>(<scope>): <subject>
#
# <body> (optional)
#
# <footer> (optional)
#
# Types: feat, fix, docs, style, refactor, perf, test, chore
```

**[OK] Template pré-rempli basé sur la branche !**

---

### ÉTAPE 8 : Bypass des hooks (situations d'urgence)

**Parfois, tu DOIS bypass les hooks :**

**Situations légitimes :**
- [ALERTE] Hotfix ultra-urgent en production
- [OUTIL] Correction temporaire (WIP commit)
- [BUG] Le hook lui-même a un bug

---

**Bypass un hook spécifique :**

```bash
# Bypass pre-commit et commit-msg
git commit --no-verify -m "hotfix: critical production bug"

# Alias
git commit -n -m "hotfix: critical production bug"
```

**`-n` = `--no-verify`**

---

**Bypass pre-push :**

```bash
git push --no-verify
```

---

**[ATTENTION] À utiliser avec EXTRÊME prudence !**

**Documenter toujours POURQUOI tu bypasses :**

```bash
git commit --no-verify -m "hotfix: fix production crash

Bypassing hooks due to critical production incident.
Issue: #999
Severity: P0 (Complete service outage)

TODO: Clean up this commit after incident resolved"
```

---

### ÉTAPE 9 : Configuration partagée de l'équipe

**Créer un README pour les hooks :**

```bash
cat > HOOKS.md << 'EOF'
# Git Hooks Configuration

## Overview

This project uses [Husky](https://typicode.github.io/husky/) to manage Git hooks and ensure code quality.

## Active Hooks

### pre-commit
- **Purpose**: Verify code quality before commit
- **Actions**:
  - Check for secrets (API keys, passwords)
  - Run ESLint on staged JavaScript files
  - Run Prettier to format code
  - Only processes staged files (via lint-staged)
- **Bypass**: `git commit --no-verify` (emergency only)

### commit-msg
- **Purpose**: Enforce conventional commit messages
- **Format**: `<type>(<scope>): <subject>`
- **Valid types**: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`, `ci`, `build`, `revert`
- **Examples**:
  - [OK] `feat(auth): add JWT authentication`
  - [OK] `fix(api): correct null pointer exception`
  - [X] `Added new feature` (missing type)
  - [X] `FEAT: new feature` (uppercase)
- **Bypass**: `git commit --no-verify`

### pre-push
- **Purpose**: Run tests before pushing
- **Actions**:
  - Run all backend tests with Jest
  - Ensure tests pass before code reaches remote
- **Duration**: ~5-30 seconds depending on test suite
- **Bypass**: `git push --no-verify` (NOT recommended)

### post-merge
- **Purpose**: Automatic dependency installation
- **Actions**:
  - Detects changes in `package-lock.json`
  - Runs `npm ci` automatically
  - Keeps dependencies in sync
- **Note**: Runs after `git pull` or `git merge`

### prepare-commit-msg
- **Purpose**: Generate commit message template
- **Actions**:
  - Extracts type and scope from branch name
  - Pre-fills commit message template
  - Provides helpful comments
- **Example**: Branch `feature/user-auth` -> Template starts with `feat:`

## Setup for New Developers

### First Time Setup

```bash
# 1. Clone the repository
git clone <repository-url>
cd taskmaster-pro

# 2. Install dependencies (this installs Husky automatically)
npm install

# 3. Verify Husky is installed
ls -la .husky/

# Expected output:
# .husky/
# ├── pre-commit
# ├── commit-msg
# ├── pre-push
# ├── post-merge
# └── prepare-commit-msg
```

### Backend Setup

```bash
cd backend
npm install
```

## Troubleshooting

### Hooks not running

```bash
# Reinstall Husky
npx husky install

# Verify .git/hooks/ links to .husky/
ls -la .git/hooks/
```

### "Permission denied" error

```bash
# Make hooks executable
chmod +x .husky/*
```

### ESLint errors on commit

```bash
# Fix automatically
npm run lint:fix

# Or manually fix and re-commit
```

### Tests failing on push

```bash
# Run tests locally
cd backend
npm test

# Fix failing tests
# Then commit and push again
```

### Bypassing Hooks (Emergency Only)

```bash
# Bypass all hooks
git commit --no-verify -m "emergency: critical fix"
git push --no-verify

# [ATTENTION] Only use in true emergencies!
# Document why you bypassed in the commit message
```

## Updating Hooks

Hooks are version controlled and update automatically when you pull changes.

```bash
# Pull latest changes (includes hook updates)
git pull

# Hooks are automatically updated
```

## Configuration Files

- **Husky**: `.husky/` directory
- **ESLint**: `backend/.eslintrc.js`
- **Prettier**: `backend/.prettierrc`
- **Commitlint**: `commitlint.config.js`
- **Lint-staged**: `package.json` (lint-staged section)
- **Jest**: `backend/jest.config.js`

## Additional Tools

### Manual Commands

```bash
# Lint all files
npm run lint

# Fix linting issues
npm run lint:fix

# Format all files
npm run format

# Check formatting
npm run format:check

# Run tests
cd backend && npm test

# Run tests with coverage
cd backend && npm run test:coverage
```

### CI/CD Integration

Hooks run locally. GitHub Actions runs the same checks on the server for additional security.

## Best Practices

1. [OK] **Commit often**: Small, focused commits
2. [OK] **Write clear messages**: Follow conventional commits
3. [OK] **Fix lint errors**: Don't bypass hooks
4. [OK] **Run tests locally**: Before pushing
5. [X] **Don't bypass**: Unless absolutely necessary
6. [X] **Don't commit secrets**: Use environment variables

## Support

If you encounter issues with hooks:
1. Check this documentation
2. Ask in #dev-support Slack channel
3. Contact @alice (DevOps Lead)

---

**Last updated**: January 2024
EOF
```

---

**Commiter toute la configuration :**

```bash
git add .husky/ scripts/ package.json package-lock.json \
  commitlint.config.js backend/.eslintrc.js backend/.prettierrc \
  backend/jest.config.js HOOKS.md

git commit -m "chore: complete Git hooks setup

- Add pre-commit hook (lint-staged, secret detection)
- Add commit-msg hook (commitlint)
- Add pre-push hook (automated tests)
- Add post-merge hook (dependency installation)
- Add prepare-commit-msg hook (message template)
- Configure ESLint and Prettier
- Add comprehensive documentation

All hooks are now active and will run for the entire team."

git push origin develop
```

---

### ÉTAPE 10 : Tester que tout fonctionne pour un nouveau développeur

**Simuler un nouveau développeur qui clone le projet :**

```bash
cd /tmp
git clone git@github.com:alice/taskmaster-pro.git taskmaster-test
cd taskmaster-test
```

---

**Installer les dépendances :**

```bash
npm install
```

**Résultat :**

```
> taskmaster-pro@1.0.0 prepare
> husky install

husky - Git hooks installed
```

**[OK] Husky s'installe automatiquement !**

---

**Vérifier les hooks :**

```bash
ls -la .husky/
```

```
total 24
drwxr-xr-x  7 user user  224 Jan  3 18:00 .
drwxr-xr-x 10 user user  320 Jan  3 18:00 ..
-rwxr-xr-x  1 user user  185 Jan  3 18:00 commit-msg
-rwxr-xr-x  1 user user  237 Jan  3 18:00 post-merge
-rwxr-xr-x  1 user user  201 Jan  3 18:00 pre-commit
-rwxr-xr-x  1 user user  723 Jan  3 18:00 prepare-commit-msg
-rwxr-xr-x  1 user user  286 Jan  3 18:00 pre-push
```

**[OK] Tous les hooks sont là !**

---

**Tester un commit :**

```bash
git switch -c test/hooks-verification

echo "test" > test.txt
git add test.txt
git commit -m "bad message"
```

**Résultat :**

```
⧗   input: bad message
[X]   subject may not be empty [subject-empty]
[X]   type may not be empty [type-empty]
```

**[OK] Hook commit-msg fonctionne !**

---

```bash
git commit -m "test: verify hooks work correctly"
```

**[OK] Commit réussit !**

---

**[OK] Setup complet fonctionne pour toute l'équipe !**

---

### [OK] TESTS DE VALIDATION

**1. Husky installé**

```bash
ls .husky/
npm run prepare
```

- [ ] Dossier `.husky/` existe
- [ ] Script `prepare` dans package.json

---

**2. Pre-commit hook actif**

```bash
# Code mal formaté
echo "const x=1" > test.js
git add test.js
git commit -m "test: formatting"
```

- [ ] ESLint détecte les erreurs
- [ ] Prettier formate automatiquement

---

**3. Commit-msg hook actif**

```bash
git commit -m "bad message"
```

- [ ] Message rejeté
- [ ] Erreur de commitlint affichée

---

**4. Pre-push hook actif**

```bash
# Créer un test qui échoue
# Essayer de push
git push origin develop
```

- [ ] Tests exécutés
- [ ] Push bloqué si tests échouent

---

**5. Secret detection**

```bash
echo 'const apiKey = "sk_live_123456789"' > secret.js
git add secret.js
git commit -m "test: secret"
```

- [ ] Secret détecté
- [ ] Commit bloqué

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : "husky - command not found"

**Symptôme :**

```
.husky/pre-commit: line 2: husky: command not found
```

**Cause :** Husky pas installé ou mal configuré

**Solution :**

```bash
npm install
npx husky install
```

---

#### Erreur 2 : "Permission denied"

**Symptôme :**

```
.husky/pre-commit: Permission denied
```

**Solution :**

```bash
chmod +x .husky/*
```

---

#### Erreur 3 : Hooks ne s'exécutent pas

**Diagnostic :**

```bash
# Vérifier que .git/hooks pointe vers .husky
ls -la .git/hooks/

# Devrait montrer des liens symboliques vers .husky
```

**Solution :**

```bash
npx husky install
```

---

#### Erreur 4 : ESLint erreurs sur fichiers existants

**Symptôme :**

Lint-staged vérifie des fichiers non modifiés

**Solution :**

Lint-staged devrait seulement checker les fichiers staged. Vérifier la config dans `package.json`.

---

#### Erreur 5 : Tests trop lents (pre-push)

**Symptôme :**

Pre-push prend 5+ minutes

**Solution :**

```bash
# Option 1: Run only unit tests on push
npm test -- --testPathIgnorePatterns=integration

# Option 2: Use --changedSince
npm test -- --changedSince=origin/develop

# Option 3: Parallelize tests
npm test -- --maxWorkers=4
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Git Hooks**

| Hook | Quand | Usage |
|------|-------|-------|
| `pre-commit` | Avant commit | Lint, format, secrets |
| `commit-msg` | Après message | Valider format |
| `pre-push` | Avant push | Tests |
| `post-merge` | Après merge | Install deps |

---

**2. Husky Setup**

```bash
# Installation
npm install --save-dev husky
npx husky install

# Créer un hook
npx husky add .husky/pre-commit "npm test"

# Auto-install pour l'équipe
# package.json:
"scripts": {
  "prepare": "husky install"
}
```

---

**3. Lint-staged**

```json
{
  "lint-staged": {
    "*.js": ["eslint --fix", "prettier --write"]
  }
}
```

**Avantages :**
- [OK] Rapide (seulement fichiers modifiés)
- [OK] Pas de conflits avec code existant

---

**4. Commitlint**

```bash
# Format
<type>(<scope>): <subject>

# Exemples
feat(auth): add login
fix(api): correct bug
docs: update README
```

---

**5. Bypass Hooks**

```bash
git commit --no-verify
git push --no-verify
```

**[ATTENTION] Urgences seulement !**

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Danger.js (Code review automation)**

```bash
npm install --save-dev danger

# dangerfile.js
import { danger, warn, fail } from 'danger';

// Check PR size
const bigPRThreshold = 500;
if (danger.github.pr.additions + danger.github.pr.deletions > bigPRThreshold) {
  warn(':exclamation: Big PR - consider breaking into smaller PRs');
}

// Require tests
const hasTests = danger.git.modified_files.some(f => f.includes('.test.'));
if (!hasTests) {
  warn('This PR does not include tests');
}
```

---

**2. Lefthook (Alternative à Husky, plus rapide)**

```bash
# Install
npm install --save-dev lefthook

# lefthook.yml
pre-commit:
  parallel: true
  commands:
    lint:
      glob: "*.js"
      run: npx eslint {staged_files}
    secrets:
      run: ./scripts/check-secrets.sh
```

---

**3. Git hooks côté serveur**

**Sur GitHub : Webhooks + Actions**

```yaml
# .github/workflows/hooks.yml
name: Server Hooks

on: [push, pull_request]

jobs:
  lint:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - run: npm ci
      - run: npm run lint
```

---

**4. Pre-commit framework (Python)**

```bash
# .pre-commit-config.yaml
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v4.4.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
      - id: check-added-large-files
```

---

**5. Detect secrets avec git-secrets**

```bash
# AWS git-secrets
git secrets --install
git secrets --register-aws

# Scan repo
git secrets --scan
```

---

**6. Hooks de performance**

```bash
# Mesurer le temps des hooks
cat > .husky/pre-commit << 'EOF'
#!/bin/sh
START=$(date +%s)

# ... hooks ...

END=$(date +%s)
DIFF=$(( $END - $START ))
echo "[OK] Pre-commit completed in ${DIFF}s"
EOF
```

---

**7. Hooks conditionnels**

```bash
# N'exécuter les tests que si certains fichiers changent
cat > .husky/pre-push << 'EOF'
#!/bin/sh

CHANGED_FILES=$(git diff --name-only @{u}..HEAD)

if echo "$CHANGED_FILES" | grep -q "backend/src/"; then
  echo "Backend files changed, running tests..."
  cd backend && npm test
else
  echo "No backend changes, skipping tests"
fi
EOF
```

---

## [COURS] CONCLUSION DE L'EXERCICE 8

**[OK] Félicitations ! Tu maîtrises les Git Hooks et l'automation !**

**Ce que tu as appris :**
- Git Hooks (types, cycle de vie)
- Husky pour versionner les hooks
- Lint-staged pour optimiser le linting
- Commitlint pour forcer les conventions
- Secret detection automatique
- Tests automatiques avant push
- Hooks personnalisés
- Configuration d'équipe

**Compétences acquises :**
- [OK] Git Hooks (niveau expert)
- [OK] Automation de la qualité
- [OK] ESLint + Prettier
- [OK] Conventional Commits
- [OK] Security (secret detection)
- [OK] Team collaboration

**Temps moyen de réalisation :** 4-5 heures

**Prochaine étape :** Exercice 9 - Monorepo Management (Workspaces, Lerna, Nx) ! [PACKAGE]

---

(Il reste 2 exercices : 9 et 10. Continue ?)

# [ROUGE] EXERCICE 9 : MONOREPO MANAGEMENT

## [LISTE] ÉNONCÉ

### Contexte professionnel

TaskMaster Pro grandit rapidement ! L'équipe développe maintenant :
- [MOBILE] **Application mobile** (React Native)
- [WEB] **Application web** (React)
- [ECRAN] **Application desktop** (Electron)
- [PLUGIN] **Backend API** (Node.js/Express)
- [DOCS] **Bibliothèque UI partagée** (components communs)
- [OUTILS] **Utilitaires partagés** (functions communes)
- [GUIDE] **Documentation** (Storybook)

**Problèmes actuels avec des repos séparés :**
- [X] Code dupliqué entre projets
- [X] Versions de dépendances incohérentes
- [X] Difficile de tester les changements cross-project
- [X] PRs dispersées entre 7 repositories
- [X] Refactoring complexe (touche plusieurs repos)
- [X] CI/CD configuration dupliquée
- [X] Onboarding difficile pour nouveaux devs

**Solution : MONOREPO** [OBJECTIF]

**Alice** décide de migrer vers un **monorepo** pour :
- [OK] Centraliser tout le code
- [OK] Partager le code facilement
- [OK] Atomic commits (changes cross-project)
- [OK] Simplifier le versioning
- [OK] Améliorer la collaboration
- [OK] Unified tooling (ESLint, TypeScript, etc.)

**Aujourd'hui, l'équipe va :**
1. Comprendre les monorepos en profondeur
2. Restructurer le projet en monorepo
3. Configurer npm/yarn workspaces
4. Implémenter Nx pour l'orchestration
5. Créer des packages partagés
6. Gérer les dépendances inter-packages
7. Optimiser les builds (caching, parallelization)
8. Mettre en place le versioning
9. Adapter le CI/CD

### Objectifs

- Maîtriser les concepts de monorepo
- Configurer npm/yarn workspaces
- Utiliser Nx pour l'orchestration
- Créer des packages internes
- Gérer les dépendances partagées
- Optimiser les performances (caching)
- Adapter Git et CI/CD

### Livrables

- Structure monorepo complète
- Workspaces configurés
- Nx installé et configuré
- 3+ packages partagés créés
- Build parallelisé et optimisé
- Documentation architecture
- CI/CD adapté au monorepo

### Contraintes

- Tous les projets dans un seul repo
- Builds indépendants par package
- Dépendances partagées versionnées
- Performance : builds rapides (caching)
- Temps estimé : 5-6 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :

- [OK] Comprendre les monorepos (avantages/inconvénients)
- [OK] Structurer un monorepo professionnellement
- [OK] Configurer npm/yarn workspaces
- [OK] Utiliser Nx pour orchestration et caching
- [OK] Créer et gérer des packages internes
- [OK] Gérer les dépendances inter-packages
- [OK] Optimiser les builds (cache, parallelization)
- [OK] Versionner les packages (independent vs fixed)
- [OK] Adapter le CI/CD pour monorepos
- [OK] Utiliser git strategies (sparse-checkout, etc.)

---

## [DOCS] PRÉREQUIS

- Exercices 1 à 8 terminés
- Node.js 18+ et npm 7+
- Compréhension de npm packages
- Familiarité avec JavaScript/TypeScript

---

## [GUIDE] THÉORIE : Monorepos expliqués

### Qu'est-ce qu'un Monorepo ?

**Monorepo** = Un seul repository contenant plusieurs projets/packages

**Contraire : Polyrepo** (multi-repo) = Un repository par projet

---

**Analogie :**

**Polyrepo** = Maison avec plusieurs maisons séparées
```
[DOSSIER] taskmaster-backend/
[DOSSIER] taskmaster-frontend/
[DOSSIER] taskmaster-mobile/
[DOSSIER] taskmaster-shared/
```

**Monorepo** = Immeuble avec plusieurs appartements
```
[DOSSIER] taskmaster/
  [DOSSIER] apps/
    [DOSSIER] backend/
    [DOSSIER] frontend/
    [DOSSIER] mobile/
  [DOSSIER] packages/
    [DOSSIER] shared/
    [DOSSIER] ui-components/
```

---

### Exemples réels de Monorepos

**Google** : ~2 milliards de lignes de code dans 1 monorepo
**Facebook** : Tout React, React Native, Jest, etc.
**Microsoft** : Windows est dans un monorepo
**Twitter** : Twitter.com, mobile apps, services
**Airbnb** : Web, mobile, backend
**Uber** : Toutes les applications

**Open Source :**
- **Babel** : tous les plugins
- **Jest** : tous les packages
- **React** : React, ReactDOM, React Native
- **Angular** : framework + CLI + docs
- **Vue 3** : core, compiler, reactivity, etc.

---

### Avantages des Monorepos

| Avantage | Explication | Exemple |
|----------|-------------|---------|
| **Code sharing** | Partage facile entre projets | Component Button utilisé partout |
| **Atomic changes** | Un commit = changement cross-project | Refactor API -> update tous les consumers |
| **Unified versioning** | Toutes les apps à la même version | Pas de "backend v2 incompatible avec frontend v1" |
| **Simplified dependencies** | Une seule version de React partout | Pas de conflits de versions |
| **Single source of truth** | Un seul endroit pour chercher | Pas besoin de chercher dans 10 repos |
| **Easier refactoring** | Refactor + tests dans même PR | Renommer une fonction -> tous les usages mis à jour |
| **Unified tooling** | ESLint, TypeScript, etc. partagés | Configuration une seule fois |
| **Better collaboration** | Tout le monde voit tout le code | Reviews cross-team faciles |
| **Easier onboarding** | Clone 1 repo, pas 10 | `git clone` et c'est tout |

---

### Inconvénients des Monorepos

| Inconvénient | Explication | Mitigation |
|--------------|-------------|------------|
| **Repository size** | Repo peut devenir énorme | Git LFS, shallow clone |
| **Clone time** | Long à cloner | Sparse checkout |
| **Build time** | Tout rebuilder prend du temps | Caching, affected builds only |
| **Git performance** | Git peut ralentir | Virtual File System (VFS) |
| **Access control** | Tout le monde voit tout | Pas de solution Git native (!)  |
| **Cognitive load** | Beaucoup de code à comprendre | Bonne documentation, structure claire |
| **Tooling complexity** | Setup initial complexe | Nx, Turborepo, etc. |

---

### Quand utiliser un Monorepo ?

**[OK] BON pour :**
- Plusieurs apps partageant du code
- Équipe qui travaille sur plusieurs projets
- Besoin de refactorings cross-project
- Startup/scale-up (< 500 devs)
- Projets étroitement liés

**[X] MAUVAIS pour :**
- Projets complètement indépendants
- Accès restreint requis (secret projects)
- Très grande entreprise (> 1000 devs)
- Projets avec des cycles de release différents

---

### Architecture d'un Monorepo

**Structure typique :**

```
monorepo/
├── apps/                    # Applications déployables
│   ├── web/                # App web (React)
│   ├── mobile/             # App mobile (React Native)
│   ├── desktop/            # App desktop (Electron)
│   └── api/                # Backend API
├── packages/               # Bibliothèques internes
│   ├── ui/                 # UI components
│   ├── utils/              # Utility functions
│   ├── types/              # TypeScript types
│   └── config/             # Shared configs
├── tools/                  # Scripts de build/deploy
├── docs/                   # Documentation
├── package.json            # Root package.json
├── nx.json                 # Nx configuration
├── tsconfig.base.json      # Base TypeScript config
└── .eslintrc.json          # Base ESLint config
```

---

### Technologies Monorepo

**Tools d'orchestration :**

| Tool | Société | Caractéristiques | Complexité |
|------|---------|------------------|------------|
| **npm workspaces** | npm | Natif, simple | * |
| **Yarn workspaces** | Facebook | Rapide, hoisting | ** |
| **Lerna** | - | Versioning, publishing | *** |
| **Nx** | Nrwl | Caching, graph, plugins | **** |
| **Turborepo** | Vercel | Ultra-rapide, simple | *** |
| **Rush** | Microsoft | Enterprise-scale | ***** |

**On va utiliser : npm workspaces + Nx** (équilibre puissance/complexité)

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Restructurer en Monorepo

**Créer la nouvelle structure :**

```bash
cd ~/taskmaster-pro
git switch develop
git pull origin develop

# Créer la branche de migration
git switch -c refactor/migrate-to-monorepo
```

---

**Créer la structure de base :**

```bash
# Créer les dossiers
mkdir -p apps/api
mkdir -p apps/web
mkdir -p apps/mobile
mkdir -p packages/ui
mkdir -p packages/utils
mkdir -p packages/types
mkdir -p packages/config
mkdir -p tools
mkdir -p docs
```

---

**Déplacer le code existant :**

```bash
# Déplacer backend -> apps/api
git mv backend/* apps/api/ 2>/dev/null || true
git mv backend/.* apps/api/ 2>/dev/null || true
rmdir backend 2>/dev/null || true

# Déplacer frontend -> apps/web
git mv frontend/* apps/web/ 2>/dev/null || true
git mv frontend/.* apps/web/ 2>/dev/null || true
rmdir frontend 2>/dev/null || true
```

---

**Créer le package.json root :**

```bash
cat > package.json << 'EOF'
{
  "name": "taskmaster-monorepo",
  "version": "1.0.0",
  "private": true,
  "description": "TaskMaster Pro - Monorepo containing all applications and packages",
  "workspaces": [
    "apps/*",
    "packages/*"
  ],
  "scripts": {
    "dev": "nx run-many --target=dev --all",
    "build": "nx run-many --target=build --all",
    "test": "nx run-many --target=test --all",
    "lint": "nx run-many --target=lint --all",
    "clean": "nx reset && rm -rf node_modules apps/*/node_modules packages/*/node_modules"
  },
  "devDependencies": {
    "@nrwl/workspace": "^17.0.0",
    "nx": "^17.0.0",
    "typescript": "^5.3.0"
  },
  "engines": {
    "node": ">=18.0.0",
    "npm": ">=7.0.0"
  }
}
EOF
```

**Explication :**

**`"private": true`** : Le root package ne sera jamais publié
**`"workspaces": ["apps/*", "packages/*"]`** : Définit les workspaces npm
**`nx run-many`** : Nx va exécuter les commandes sur tous les packages

---

### ÉTAPE 2 : Configurer npm Workspaces

**Qu'est-ce que npm workspaces ?**

**npm workspaces** permet de gérer plusieurs packages dans un seul repo.

**Avantages :**
- [OK] Un seul `node_modules/` à la racine (hoisting)
- [OK] Symlinks automatiques entre packages
- [OK] Installation rapide (pas de duplication)
- [OK] Natif dans npm 7+

---

**Structure après workspaces :**

```
taskmaster-monorepo/
├── node_modules/           # UN SEUL node_modules partagé
│   ├── react/
│   ├── express/
│   └── ...
├── apps/
│   └── api/
│       └── package.json    # Dépend de @taskmaster/utils
├── packages/
│   └── utils/
│       └── package.json    # Exporté comme @taskmaster/utils
└── package.json            # Définit les workspaces
```

**Quand `apps/api` importe `@taskmaster/utils`, npm crée un symlink !**

---

**Créer le package @taskmaster/utils :**

```bash
cat > packages/utils/package.json << 'EOF'
{
  "name": "@taskmaster/utils",
  "version": "1.0.0",
  "description": "Shared utility functions for TaskMaster",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "tsc",
    "dev": "tsc --watch",
    "test": "jest",
    "lint": "eslint src/"
  },
  "keywords": ["utilities", "helpers"],
  "license": "MIT",
  "devDependencies": {
    "@types/node": "^20.0.0",
    "typescript": "^5.3.0",
    "jest": "^29.7.0"
  }
}
EOF
```

---

**Créer le code de @taskmaster/utils :**

```bash
mkdir -p packages/utils/src

cat > packages/utils/src/index.ts << 'EOF'
/**
 * @taskmaster/utils
 * Shared utility functions
 */

/**
 * Format a date to ISO string
 */
export function formatDate(date: Date): string {
  return date.toISOString();
}

/**
 * Generate a unique ID
 */
export function generateId(): string {
  return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
}

/**
 * Delay execution
 */
export function delay(ms: number): Promise<void> {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

/**
 * Deep clone an object
 */
export function deepClone<T>(obj: T): T {
  return JSON.parse(JSON.stringify(obj));
}

/**
 * Validate email format
 */
export function isValidEmail(email: string): boolean {
  const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
  return emailRegex.test(email);
}

/**
 * Capitalize first letter
 */
export function capitalize(str: string): string {
  return str.charAt(0).toUpperCase() + str.slice(1);
}

/**
 * Truncate string with ellipsis
 */
export function truncate(str: string, maxLength: number): string {
  if (str.length <= maxLength) return str;
  return str.slice(0, maxLength - 3) + '...';
}

/**
 * Group array by key
 */
export function groupBy<T>(array: T[], key: keyof T): Record<string, T[]> {
  return array.reduce((result, item) => {
    const groupKey = String(item[key]);
    if (!result[groupKey]) {
      result[groupKey] = [];
    }
    result[groupKey].push(item);
    return result;
  }, {} as Record<string, T[]>);
}
EOF
```

---

**Configuration TypeScript pour utils :**

```bash
cat > packages/utils/tsconfig.json << 'EOF'
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "dist",
    "rootDir": "src",
    "declaration": true,
    "declarationMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "**/*.test.ts"]
}
EOF
```

---

**Créer le tsconfig.base.json à la racine :**

```bash
cat > tsconfig.base.json << 'EOF'
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "lib": ["ES2020"],
    "moduleResolution": "node",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "incremental": true,
    "baseUrl": ".",
    "paths": {
      "@taskmaster/utils": ["packages/utils/src"],
      "@taskmaster/ui": ["packages/ui/src"],
      "@taskmaster/types": ["packages/types/src"],
      "@taskmaster/config": ["packages/config/src"]
    }
  }
}
EOF
```

**Explication :**

**`"paths"`** : Permet d'importer directement les packages :
```typescript
import { generateId } from '@taskmaster/utils';
```

Au lieu de :
```typescript
import { generateId } from '../../../packages/utils/src';
```

---

**Installer les dépendances du workspace :**

```bash
npm install
```

**Résultat :**

```
added 1247 packages, and audited 1248 packages in 45s

found 0 vulnerabilities
```

**npm a installé toutes les dépendances de tous les packages dans UN SEUL `node_modules/` à la racine ! [BRAVO]**

---

**Vérifier les symlinks :**

```bash
ls -la node_modules/@taskmaster/
```

**Résultat :**

```
lrwxr-xr-x  1 user  staff  24 Jan  4 10:00 utils -> ../../packages/utils
```

**[OK] Symlink créé automatiquement !**

---

**Builder le package utils :**

```bash
cd packages/utils
npm run build
```

**Résultat :**

```
dist/
├── index.js
├── index.d.ts
└── index.d.ts.map
```

**[OK] Package compilé en JavaScript !**

---

### ÉTAPE 3 : Créer le package @taskmaster/types

**Créer le package pour les types TypeScript partagés :**

```bash
cat > packages/types/package.json << 'EOF'
{
  "name": "@taskmaster/types",
  "version": "1.0.0",
  "description": "Shared TypeScript types for TaskMaster",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "tsc",
    "dev": "tsc --watch",
    "lint": "eslint src/"
  },
  "keywords": ["types", "typescript"],
  "license": "MIT",
  "devDependencies": {
    "typescript": "^5.3.0"
  }
}
EOF
```

---

**Créer les types :**

```bash
mkdir -p packages/types/src

cat > packages/types/src/index.ts << 'EOF'
/**
 * @taskmaster/types
 * Shared TypeScript types and interfaces
 */

/**
 * User entity
 */
export interface User {
  id: string;
  email: string;
  name: string;
  role: UserRole;
  createdAt: Date;
  updatedAt: Date;
}

/**
 * User roles
 */
export enum UserRole {
  ADMIN = 'admin',
  USER = 'user',
  GUEST = 'guest',
}

/**
 * Task entity
 */
export interface Task {
  id: string;
  title: string;
  description: string;
  status: TaskStatus;
  priority: TaskPriority;
  assigneeId?: string;
  createdBy: string;
  createdAt: Date;
  updatedAt: Date;
  dueDate?: Date;
}

/**
 * Task status
 */
export enum TaskStatus {
  TODO = 'todo',
  IN_PROGRESS = 'in-progress',
  DONE = 'done',
  CANCELLED = 'cancelled',
}

/**
 * Task priority
 */
export enum TaskPriority {
  LOW = 'low',
  MEDIUM = 'medium',
  HIGH = 'high',
  URGENT = 'urgent',
}

/**
 * Comment entity
 */
export interface Comment {
  id: string;
  taskId: string;
  userId: string;
  content: string;
  createdAt: Date;
  updatedAt: Date;
}

/**
 * API Response wrapper
 */
export interface ApiResponse<T> {
  success: boolean;
  data?: T;
  error?: ApiError;
  meta?: ApiMeta;
}

/**
 * API Error
 */
export interface ApiError {
  code: string;
  message: string;
  details?: Record<string, any>;
}

/**
 * API Metadata
 */
export interface ApiMeta {
  page?: number;
  pageSize?: number;
  totalCount?: number;
  totalPages?: number;
}

/**
 * Pagination parameters
 */
export interface PaginationParams {
  page: number;
  pageSize: number;
}

/**
 * Filter parameters for tasks
 */
export interface TaskFilters {
  status?: TaskStatus;
  priority?: TaskPriority;
  assigneeId?: string;
  createdBy?: string;
  search?: string;
}
EOF
```

---

**Configuration TypeScript :**

```bash
cat > packages/types/tsconfig.json << 'EOF'
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "dist",
    "rootDir": "src",
    "declaration": true,
    "declarationMap": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist"]
}
EOF
```

---

**Builder :**

```bash
cd ~/taskmaster-pro
npm run build --workspace=@taskmaster/types
```

---

### ÉTAPE 4 : Créer le package @taskmaster/ui (UI Components)

**Créer le package :**

```bash
cat > packages/ui/package.json << 'EOF'
{
  "name": "@taskmaster/ui",
  "version": "1.0.0",
  "description": "Shared UI components for TaskMaster",
  "main": "dist/index.js",
  "types": "dist/index.d.ts",
  "scripts": {
    "build": "tsc",
    "dev": "tsc --watch",
    "test": "jest",
    "lint": "eslint src/",
    "storybook": "storybook dev -p 6006",
    "build-storybook": "storybook build"
  },
  "peerDependencies": {
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  },
  "devDependencies": {
    "@types/react": "^18.0.0",
    "@types/react-dom": "^18.0.0",
    "typescript": "^5.3.0",
    "react": "^18.0.0",
    "react-dom": "^18.0.0"
  },
  "dependencies": {
    "@taskmaster/types": "*"
  }
}
EOF
```

**Note : `"@taskmaster/types": "*"`** = Dépend du package local

---

**Créer un composant Button :**

```bash
mkdir -p packages/ui/src/components

cat > packages/ui/src/components/Button.tsx << 'EOF'
import React from 'react';

export interface ButtonProps {
  /**
   * Button variant
   */
  variant?: 'primary' | 'secondary' | 'danger';
  
  /**
   * Button size
   */
  size?: 'small' | 'medium' | 'large';
  
  /**
   * Button content
   */
  children: React.ReactNode;
  
  /**
   * Click handler
   */
  onClick?: () => void;
  
  /**
   * Disabled state
   */
  disabled?: boolean;
  
  /**
   * Full width button
   */
  fullWidth?: boolean;
  
  /**
   * Button type
   */
  type?: 'button' | 'submit' | 'reset';
}

/**
 * Reusable Button component
 */
export const Button: React.FC<ButtonProps> = ({
  variant = 'primary',
  size = 'medium',
  children,
  onClick,
  disabled = false,
  fullWidth = false,
  type = 'button',
}) => {
  const baseStyles = 'rounded font-medium transition-colors focus:outline-none focus:ring-2';
  
  const variantStyles = {
    primary: 'bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500',
    secondary: 'bg-gray-200 text-gray-900 hover:bg-gray-300 focus:ring-gray-400',
    danger: 'bg-red-600 text-white hover:bg-red-700 focus:ring-red-500',
  };
  
  const sizeStyles = {
    small: 'px-3 py-1.5 text-sm',
    medium: 'px-4 py-2 text-base',
    large: 'px-6 py-3 text-lg',
  };
  
  const widthStyles = fullWidth ? 'w-full' : '';
  const disabledStyles = disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer';
  
  const className = [
    baseStyles,
    variantStyles[variant],
    sizeStyles[size],
    widthStyles,
    disabledStyles,
  ].join(' ');
  
  return (
    <button
      type={type}
      className={className}
      onClick={onClick}
      disabled={disabled}
    >
      {children}
    </button>
  );
};
EOF
```

---

**Créer un composant TaskCard :**

```bash
cat > packages/ui/src/components/TaskCard.tsx << 'EOF'
import React from 'react';
import { Task, TaskPriority, TaskStatus } from '@taskmaster/types';

export interface TaskCardProps {
  /**
   * Task data
   */
  task: Task;
  
  /**
   * Click handler
   */
  onClick?: (task: Task) => void;
}

/**
 * Task Card component
 */
export const TaskCard: React.FC<TaskCardProps> = ({ task, onClick }) => {
  const priorityColors = {
    [TaskPriority.LOW]: 'bg-gray-100 text-gray-800',
    [TaskPriority.MEDIUM]: 'bg-blue-100 text-blue-800',
    [TaskPriority.HIGH]: 'bg-orange-100 text-orange-800',
    [TaskPriority.URGENT]: 'bg-red-100 text-red-800',
  };
  
  const statusColors = {
    [TaskStatus.TODO]: 'bg-gray-200',
    [TaskStatus.IN_PROGRESS]: 'bg-yellow-200',
    [TaskStatus.DONE]: 'bg-green-200',
    [TaskStatus.CANCELLED]: 'bg-red-200',
  };
  
  return (
    <div
      className="border rounded-lg p-4 hover:shadow-md transition-shadow cursor-pointer"
      onClick={() => onClick?.(task)}
    >
      <div className="flex items-start justify-between mb-2">
        <h3 className="font-semibold text-lg">{task.title}</h3>
        <span className={`px-2 py-1 rounded text-xs font-medium ${priorityColors[task.priority]}`}>
          {task.priority}
        </span>
      </div>
      
      <p className="text-gray-600 text-sm mb-3">{task.description}</p>
      
      <div className="flex items-center justify-between">
        <span className={`px-3 py-1 rounded-full text-xs ${statusColors[task.status]}`}>
          {task.status}
        </span>
        
        {task.dueDate && (
          <span className="text-xs text-gray-500">
            Due: {new Date(task.dueDate).toLocaleDateString()}
          </span>
        )}
      </div>
    </div>
  );
};
EOF
```

---

**Exporter les composants :**

```bash
cat > packages/ui/src/index.ts << 'EOF'
/**
 * @taskmaster/ui
 * Shared UI components
 */

export * from './components/Button';
export * from './components/TaskCard';
EOF
```

---

**Configuration TypeScript :**

```bash
cat > packages/ui/tsconfig.json << 'EOF'
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "dist",
    "rootDir": "src",
    "declaration": true,
    "declarationMap": true,
    "jsx": "react"
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules", "dist", "**/*.test.tsx", "**/*.stories.tsx"]
}
EOF
```

---

### ÉTAPE 5 : Utiliser les packages dans apps/api

**Mettre à jour apps/api/package.json :**

```bash
cat > apps/api/package.json << 'EOF'
{
  "name": "@taskmaster/api",
  "version": "1.0.1",
  "description": "TaskMaster API Backend",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js",
    "dev": "nodemon src/index.js",
    "build": "tsc",
    "test": "jest",
    "lint": "eslint src/"
  },
  "dependencies": {
    "@taskmaster/types": "*",
    "@taskmaster/utils": "*",
    "express": "^4.18.2",
    "jsonwebtoken": "^9.0.2",
    "bcryptjs": "^2.4.3"
  },
  "devDependencies": {
    "@types/express": "^4.17.21",
    "@types/node": "^20.0.0",
    "typescript": "^5.3.0",
    "nodemon": "^3.0.1",
    "jest": "^29.7.0"
  }
}
EOF
```

---

**Utiliser les packages dans le code :**

```bash
cat > apps/api/src/services/task.service.ts << 'EOF'
import { Task, TaskStatus, TaskPriority } from '@taskmaster/types';
import { generateId } from '@taskmaster/utils';

/**
 * Task Service
 */
export class TaskService {
  private tasks: Task[] = [];

  /**
   * Create a new task
   */
  async createTask(data: {
    title: string;
    description: string;
    priority?: TaskPriority;
    createdBy: string;
  }): Promise<Task> {
    const task: Task = {
      id: generateId(),
      title: data.title,
      description: data.description,
      status: TaskStatus.TODO,
      priority: data.priority || TaskPriority.MEDIUM,
      createdBy: data.createdBy,
      createdAt: new Date(),
      updatedAt: new Date(),
    };

    this.tasks.push(task);
    return task;
  }

  /**
   * Get all tasks
   */
  async getTasks(): Promise<Task[]> {
    return this.tasks;
  }

  /**
   * Get task by ID
   */
  async getTaskById(id: string): Promise<Task | undefined> {
    return this.tasks.find((task) => task.id === id);
  }

  /**
   * Update task status
   */
  async updateTaskStatus(id: string, status: TaskStatus): Promise<Task | undefined> {
    const task = await this.getTaskById(id);
    if (!task) return undefined;

    task.status = status;
    task.updatedAt = new Date();
    return task;
  }

  /**
   * Delete task
   */
  async deleteTask(id: string): Promise<boolean> {
    const index = this.tasks.findIndex((task) => task.id === id);
    if (index === -1) return false;

    this.tasks.splice(index, 1);
    return true;
  }
}

export const taskService = new TaskService();
EOF
```

**[OK] On importe directement `@taskmaster/types` et `@taskmaster/utils` !**

---

**Réinstaller les dépendances :**

```bash
cd ~/taskmaster-pro
npm install
```

**npm va créer les symlinks automatiquement !**

---

### ÉTAPE 6 : Installer et configurer Nx

**Pourquoi Nx ?**

**Nx** = Outil d'orchestration pour monorepos

**Fonctionnalités :**
- [RAPIDE] **Computation Caching** : Ne rebuild que ce qui a changé
- [GRAPHIQUE] **Dependency Graph** : Visualise les dépendances
- [RAPIDE] **Parallel Execution** : Build/test en parallèle
- [OBJECTIF] **Affected Commands** : Exécute seulement les projets affectés
- [PLUGIN] **Plugins** : React, Angular, Node, etc.

---

**Installer Nx :**

```bash
npm install --save-dev nx @nrwl/workspace
```

---

**Initialiser Nx :**

```bash
npx nx init
```

**Questions :**
```
? Enable distributed caching to make your CI faster? No (for now)
```

---

**Créer nx.json :**

```bash
cat > nx.json << 'EOF'
{
  "$schema": "./node_modules/nx/schemas/nx-schema.json",
  "targetDefaults": {
    "build": {
      "dependsOn": ["^build"],
      "cache": true
    },
    "test": {
      "cache": true
    },
    "lint": {
      "cache": true
    }
  },
  "affected": {
    "defaultBase": "main"
  },
  "tasksRunnerOptions": {
    "default": {
      "runner": "nx/tasks-runners/default",
      "options": {
        "cacheableOperations": ["build", "test", "lint"],
        "parallel": 3
      }
    }
  },
  "workspaceLayout": {
    "appsDir": "apps",
    "libsDir": "packages"
  }
}
EOF
```

**Explication :**

**`"dependsOn": ["^build"]`** : Build les dépendances d'abord
**`"cache": true`** : Cache les résultats
**`"parallel": 3`** : Exécute 3 tâches en parallèle
**`"cacheableOperations"`** : Quelles opérations cacher

---

**Créer project.json pour chaque package :**

**packages/utils/project.json :**

```bash
cat > packages/utils/project.json << 'EOF'
{
  "name": "@taskmaster/utils",
  "$schema": "../../node_modules/nx/schemas/project-schema.json",
  "sourceRoot": "packages/utils/src",
  "projectType": "library",
  "targets": {
    "build": {
      "executor": "nx:run-commands",
      "options": {
        "command": "tsc",
        "cwd": "packages/utils"
      },
      "outputs": ["{projectRoot}/dist"]
    },
    "test": {
      "executor": "nx:run-commands",
      "options": {
        "command": "jest",
        "cwd": "packages/utils"
      }
    },
    "lint": {
      "executor": "nx:run-commands",
      "options": {
        "command": "eslint src/",
        "cwd": "packages/utils"
      }
    }
  },
  "tags": ["type:library", "scope:shared"]
}
EOF
```

---

**apps/api/project.json :**

```bash
cat > apps/api/project.json << 'EOF'
{
  "name": "@taskmaster/api",
  "$schema": "../../node_modules/nx/schemas/project-schema.json",
  "sourceRoot": "apps/api/src",
  "projectType": "application",
  "targets": {
    "build": {
      "executor": "nx:run-commands",
      "options": {
        "command": "tsc",
        "cwd": "apps/api"
      },
      "outputs": ["{projectRoot}/dist"],
      "dependsOn": ["^build"]
    },
    "serve": {
      "executor": "nx:run-commands",
      "options": {
        "command": "node src/index.js",
        "cwd": "apps/api"
      }
    },
    "dev": {
      "executor": "nx:run-commands",
      "options": {
        "command": "nodemon src/index.js",
        "cwd": "apps/api"
      }
    },
    "test": {
      "executor": "nx:run-commands",
      "options": {
        "command": "jest",
        "cwd": "apps/api"
      }
    },
    "lint": {
      "executor": "nx:run-commands",
      "options": {
        "command": "eslint src/",
        "cwd": "apps/api"
      }
    }
  },
  "tags": ["type:application", "scope:backend"]
}
EOF
```

---

**Tester Nx :**

```bash
# Builder seulement utils
nx build @taskmaster/utils
```

**Résultat :**

```
> nx run @taskmaster/utils:build

> tsc

[OK] Successfully ran target build for project @taskmaster/utils (2s)
```

---

**Builder tout :**

```bash
nx run-many --target=build --all
```

**Résultat :**

```
[OK]  Successfully ran target build for 3 projects (5s)

   [OK]  @taskmaster/utils
   [OK]  @taskmaster/types
   [OK]  @taskmaster/api
```

**[OK] Build en parallèle ! [RAPIDE]**

---

**Visualiser le graph de dépendances :**

```bash
nx graph
```

**Ouvre une interface web montrant :**

```
@taskmaster/api
    v
@taskmaster/utils
    v
@taskmaster/types
```

**Visual graph en HTML ! [DESIGN]**

---

### ÉTAPE 7 : Caching Nx - Ne builder que ce qui a changé

**Modifier un fichier dans utils :**

```bash
echo "export const newFunction = () => 'new';" >> packages/utils/src/index.ts
```

---

**Builder à nouveau :**

```bash
nx build @taskmaster/utils
```

**Résultat :**

```
> nx run @taskmaster/utils:build

> tsc

[OK] Successfully ran target build for project @taskmaster/utils (2s)

Nx read the output from the cache instead of running the command for 0 out of 1 tasks.
```

---

**Builder sans changement :**

```bash
nx build @taskmaster/utils
```

**Résultat :**

```
> nx run @taskmaster/utils:build  [existing outputs match the cache, left as is]

[OK] Successfully ran target build for project @taskmaster/utils (12ms)

Nx read the output from the cache instead of running the command for 1 out of 1 tasks.
```

**[OK] 12ms au lieu de 2s ! Cache hit ! [BRAVO]**

---

### ÉTAPE 8 : Affected Commands - Builder seulement ce qui a changé

**Nx peut détecter quels projets sont "affectés" par tes changements.**

**Modifier seulement utils :**

```bash
echo "// Comment" >> packages/utils/src/index.ts
git add packages/utils/src/index.ts
git commit -m "feat(utils): add comment"
```

---

**Builder seulement les projets affectés :**

```bash
nx affected:build
```

**Résultat :**

```
[OK] Successfully ran target build for project @taskmaster/utils (2s)
[OK] Successfully ran target build for project @taskmaster/api (3s)

Nx read the output from the cache instead of running the command for 0 out of 2 tasks.
```

**Explication :**
- `@taskmaster/utils` a changé -> rebuild
- `@taskmaster/api` dépend de `utils` -> rebuild
- `@taskmaster/types` n'a PAS changé -> skip [OK]

---

**Voir quels projets sont affectés :**

```bash
nx affected:graph
```

**Ouvre un graphe visuel montrant seulement les projets affectés ! [DESIGN]**

---

**Tester seulement les projets affectés :**

```bash
nx affected:test
```

**Lint seulement les projets affectés :**

```bash
nx affected:lint
```

**[OK] ÉNORME gain de temps en CI/CD !**

---

### ÉTAPE 9 : Gestion du versioning - Lerna

**Lerna** = Tool pour versionner et publier des packages dans un monorepo

**Installer Lerna :**

```bash
npm install --save-dev lerna
```

---

**Initialiser Lerna :**

```bash
npx lerna init
```

---

**Configurer lerna.json :**

```bash
cat > lerna.json << 'EOF'
{
  "$schema": "node_modules/lerna/schemas/lerna-schema.json",
  "version": "independent",
  "npmClient": "npm",
  "useWorkspaces": true,
  "command": {
    "version": {
      "conventionalCommits": true,
      "createRelease": "github",
      "message": "chore(release): publish %s"
    },
    "publish": {
      "registry": "https://registry.npmjs.org/"
    }
  }
}
EOF
```

**Explication :**

**`"version": "independent"`** : Chaque package a sa propre version
- Alternative : `"version": "1.0.0"` (fixed, tous à la même version)

**`"conventionalCommits": true`** : Utilise conventional commits pour déterminer le bump (major/minor/patch)

**`"createRelease": "github"`** : Crée des releases GitHub automatiquement

---

**Versioning strategy :**

**1. Independent Versioning** (recommandé pour librairies)
```
@taskmaster/utils@1.2.3
@taskmaster/types@2.0.1
@taskmaster/ui@1.5.0
```

Chaque package évolue indépendamment.

**2. Fixed Versioning** (recommandé pour apps liées)
```
@taskmaster/utils@1.0.0
@taskmaster/types@1.0.0
@taskmaster/ui@1.0.0
```

Tous les packages à la même version (comme Babel, Angular).

---

**Créer une nouvelle version :**

```bash
npx lerna version
```

**Lerna va :**
1. Détecter les packages changés (via Git)
2. Déterminer le bump (major/minor/patch) selon conventional commits
3. Demander confirmation
4. Mettre à jour les `package.json`
5. Créer un commit de version
6. Créer des tags Git
7. Pousser vers GitHub

---

**Publier les packages (si public npm registry) :**

```bash
npx lerna publish
```

**Pour notre monorepo interne, on ne publie PAS sur npm.**

---

### ÉTAPE 10 : Scripts NPM pour le monorepo

**Mettre à jour les scripts root package.json :**

```bash
cat > package.json << 'EOF'
{
  "name": "taskmaster-monorepo",
  "version": "1.0.0",
  "private": true,
  "description": "TaskMaster Pro - Monorepo",
  "workspaces": [
    "apps/*",
    "packages/*"
  ],
  "scripts": {
    "dev": "nx run-many --target=dev --all --parallel=3",
    "dev:api": "nx dev @taskmaster/api",
    "dev:web": "nx dev @taskmaster/web",
    "build": "nx run-many --target=build --all",
    "build:affected": "nx affected:build",
    "test": "nx run-many --target=test --all",
    "test:affected": "nx affected:test",
    "test:watch": "nx run-many --target=test --all -- --watch",
    "lint": "nx run-many --target=lint --all",
    "lint:affected": "nx affected:lint",
    "lint:fix": "nx run-many --target=lint --all -- --fix",
    "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md}\"",
    "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md}\"",
    "graph": "nx graph",
    "affected:graph": "nx affected:graph",
    "clean": "nx reset && rm -rf node_modules apps/*/node_modules packages/*/node_modules apps/*/dist packages/*/dist",
    "clean:cache": "nx reset",
    "version": "lerna version",
    "prepare": "husky install"
  },
  "devDependencies": {
    "@nrwl/workspace": "^17.0.0",
    "nx": "^17.0.0",
    "lerna": "^8.0.0",
    "typescript": "^5.3.0",
    "prettier": "^3.1.0",
    "husky": "^8.0.0"
  },
  "engines": {
    "node": ">=18.0.0",
    "npm": ">=7.0.0"
  }
}
EOF
```

---

**Utilisation :**

```bash
# Dev tous les projets en parallèle
npm run dev

# Dev seulement l'API
npm run dev:api

# Build tout
npm run build

# Build seulement les projets affectés (CI/CD)
npm run build:affected

# Tests
npm test

# Tests en mode watch
npm run test:watch

# Lint et fix
npm run lint:fix

# Formater tout le code
npm run format

# Visualiser le graph
npm run graph

# Nettoyer complètement
npm run clean
```

---

### ÉTAPE 11 : Configuration CI/CD pour Monorepo

**Adapter GitHub Actions pour le monorepo :**

```bash
cat > .github/workflows/ci.yml << 'EOF'
name: CI

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

jobs:
  setup:
    runs-on: ubuntu-latest
    outputs:
      affected: ${{ steps.affected.outputs.projects }}
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 18
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Determine affected projects
        id: affected
        run: |
          echo "projects=$(npx nx show projects --affected --base=origin/main --head=HEAD --json)" >> $GITHUB_OUTPUT

  lint:
    needs: setup
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 18
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Lint affected projects
        run: npx nx affected:lint --base=origin/main --head=HEAD --parallel=3

  test:
    needs: setup
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 18
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Test affected projects
        run: npx nx affected:test --base=origin/main --head=HEAD --parallel=3 --coverage

      - name: Upload coverage
        uses: codecov/codecov-action@v3
        with:
          directory: ./coverage

  build:
    needs: [lint, test]
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 18
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build affected projects
        run: npx nx affected:build --base=origin/main --head=HEAD --parallel=3

      - name: Upload build artifacts
        uses: actions/upload-artifact@v3
        with:
          name: build-artifacts
          path: |
            apps/*/dist
            packages/*/dist
          retention-days: 7

  # Nx Cloud (optionnel, pour distributed caching)
  nx-cloud:
    runs-on: ubuntu-latest
    if: github.event_name == 'pull_request'
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: 18
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run affected with Nx Cloud
        run: npx nx affected --target=build,test,lint --base=origin/main --head=HEAD
        env:
          NX_CLOUD_ACCESS_TOKEN: ${{ secrets.NX_CLOUD_ACCESS_TOKEN }}
EOF
```

**Explication :**

**`fetch-depth: 0`** : Clone l'historique complet (nécessaire pour `nx affected`)

**`npx nx affected:*`** : Exécute seulement sur les projets affectés
- [OK] Économie massive de temps CI
- Sur un monorepo de 50 packages, si tu changes 1 package, seulement 1-5 packages sont testés au lieu de 50 !

**`--parallel=3`** : Exécute 3 jobs en parallèle

---

**Exemple de gain de temps :**

**Sans Nx affected (test tout) :**
```
Job 1: test all 20 packages -> 30 minutes
```

**Avec Nx affected (test seulement 2 packages modifiés) :**
```
Job 1: test 2 packages -> 3 minutes
```

**[OK] 10x plus rapide ! [RAPIDE]**

---

### ÉTAPE 12 : Documentation du Monorepo

**Créer MONOREPO.md :**

```bash
cat > MONOREPO.md << 'EOF'
# TaskMaster Monorepo

## Overview

This is a monorepo containing all TaskMaster applications and shared packages.

## Structure

```
taskmaster-monorepo/
├── apps/                       # Applications (deployable)
│   ├── api/                   # Backend API (Node.js/Express)
│   ├── web/                   # Web app (React)
│   ├── mobile/                # Mobile app (React Native)
│   └── desktop/               # Desktop app (Electron)
├── packages/                  # Shared packages (internal)
│   ├── ui/                    # UI components
│   ├── utils/                 # Utility functions
│   ├── types/                 # TypeScript types
│   └── config/                # Shared configurations
├── tools/                     # Build/deploy scripts
├── docs/                      # Documentation
└── node_modules/              # Shared dependencies (hoisted)
```

## Package Naming Convention

All internal packages use the `@taskmaster/` scope:

- `@taskmaster/api` - Backend API
- `@taskmaster/web` - Web application
- `@taskmaster/ui` - UI components library
- `@taskmaster/utils` - Utility functions
- `@taskmaster/types` - TypeScript types

## Technologies

- **Workspaces**: npm workspaces
- **Build System**: Nx
- **Versioning**: Lerna (independent)
- **Package Manager**: npm 7+
- **Language**: TypeScript
- **Testing**: Jest
- **Linting**: ESLint + Prettier

## Getting Started

### Prerequisites

- Node.js 18+
- npm 7+
- Git

### Installation

```bash
# Clone the repository
git clone git@github.com:alice/taskmaster-pro.git
cd taskmaster-pro

# Install all dependencies (for all packages)
npm install
```

### Development

```bash
# Start all apps in development mode
npm run dev

# Start specific app
npm run dev:api
npm run dev:web

# Build all projects
npm run build

# Build only affected projects (after git changes)
npm run build:affected

# Run all tests
npm test

# Run tests in watch mode
npm run test:watch

# Lint all code
npm run lint

# Fix linting issues
npm run lint:fix

# Format all code
npm run format
```

### Working with Packages

#### Creating a New Package

```bash
# 1. Create directory
mkdir packages/new-package

# 2. Create package.json
cd packages/new-package
npm init -y

# 3. Update package name
# Edit package.json: "name": "@taskmaster/new-package"

# 4. Install at root
cd ../..
npm install

# 5. Create project.json for Nx
# See existing packages for examples
```

#### Using Internal Packages

```typescript
// In any app or package, import like this:
import { generateId } from '@taskmaster/utils';
import { Task, TaskStatus } from '@taskmaster/types';
import { Button } from '@taskmaster/ui';
```

Dependencies are automatically resolved via npm workspaces (symlinks).

#### Adding External Dependencies

```bash
# For a specific package
npm install <package> --workspace=@taskmaster/api

# For all packages
npm install <package> --workspaces

# For root only
npm install <package> -w root
```

## Build System (Nx)

### Why Nx?

- **Caching**: Builds are cached, subsequent builds are instant
- **Affected**: Only build/test what changed
- **Parallel**: Run tasks in parallel
- **Graph**: Visualize dependencies

### Nx Commands

```bash
# Build a specific project
nx build @taskmaster/utils

# Build all projects
nx run-many --target=build --all

# Build only affected projects
nx affected:build

# Test affected projects
nx affected:test

# Lint affected projects
nx affected:lint

# Visualize dependency graph
nx graph

# See affected projects
nx affected:graph

# Clear cache
nx reset
```

### Nx Cache

Nx caches the output of builds, tests, and lint operations.

**Cache location**: `node_modules/.cache/nx`

If a task is run again with the same inputs (code + dependencies), Nx uses the cached result instead of re-running.

**Example:**
```
First build:  nx build @taskmaster/utils  ->  2.5s
Second build: nx build @taskmaster/utils  ->  12ms (from cache!)
```

## Versioning & Releases

We use **Lerna** with **independent versioning**.

Each package has its own version number and can be released independently.

### Creating a Release

```bash
# 1. Ensure you're on main branch
git switch main
git pull origin main

# 2. Run lerna version (auto-detects changes)
npm run version

# Lerna will:
# - Detect changed packages
# - Determine version bump (major/minor/patch) from conventional commits
# - Update package.json files
# - Create git tags
# - Push to GitHub

# 3. CI/CD will handle deployment
```

### Version Bump Rules

Based on conventional commits:

- `feat:` -> **minor** version (1.0.0 -> 1.1.0)
- `fix:` -> **patch** version (1.0.0 -> 1.0.1)
- `BREAKING CHANGE:` -> **major** version (1.0.0 -> 2.0.0)

## CI/CD

GitHub Actions runs on every PR and push to main/develop.

### Affected-based CI

CI only runs tasks (lint, test, build) on **affected** projects.

**Example:**
- You change `packages/utils`
- CI will run tests for: `utils`, `api` (depends on utils), `web` (depends on utils)
- CI will **skip** tests for: `types`, `ui` (not affected)

**Time savings:**
- Full test suite: 30 minutes
- Affected only: 3-5 minutes (on average)

### Workflows

- **CI** (`.github/workflows/ci.yml`): Lint, test, build on every PR
- **Deploy** (`.github/workflows/deploy.yml`): Deploy to production on merge to main

## Common Tasks

### Adding a New Dependency

```bash
# To a specific package
npm install express --workspace=@taskmaster/api

# To all packages
npm install lodash --workspaces

# Dev dependency to root
npm install --save-dev @types/node
```

### Updating Dependencies

```bash
# Update all packages
npm update --workspaces

# Update specific package
npm update react --workspaces
```

### Cleaning

```bash
# Remove all node_modules and dist folders
npm run clean

# Clear Nx cache only
npm run clean:cache
```

### Troubleshooting

#### "Cannot find module '@taskmaster/utils'"

```bash
# Reinstall dependencies (creates symlinks)
rm -rf node_modules
npm install
```

#### Nx cache issues

```bash
# Clear Nx cache
nx reset

# Rebuild
npm run build
```

#### TypeScript path mapping not working

Check that `tsconfig.base.json` includes path mappings:

```json
{
  "compilerOptions": {
    "paths": {
      "@taskmaster/utils": ["packages/utils/src"]
    }
  }
}
```

## Best Practices

### 1. One feature, one package

Don't create giant monolithic packages. Split by feature/domain.

**Bad:**
```
packages/shared/  (everything in here)
```

**Good:**
```
packages/auth/
packages/payments/
packages/notifications/
```

### 2. Clear dependencies

Make dependencies explicit in `package.json`:

```json
{
  "dependencies": {
    "@taskmaster/utils": "*",
    "@taskmaster/types": "*"
  }
}
```

### 3. Use Nx affected in CI

Always use `nx affected:*` in CI to save time:

```bash
nx affected:test --base=origin/main
```

### 4. Keep packages focused

Each package should have a single, clear purpose.

### 5. Document package APIs

Every package should have a README explaining its purpose and API.

### 6. Version intelligently

Use semantic versioning and conventional commits.

## Architecture Decisions

### Why Monorepo?

- [OK] Share code easily between apps
- [OK] Atomic changes across multiple packages
- [OK] Simplified dependency management
- [OK] Unified tooling (TypeScript, ESLint, etc.)
- [OK] Better collaboration (everyone sees all code)

### Why npm workspaces over Yarn?

- npm 7+ has built-in workspaces
- No additional tools needed
- Works with Nx out of the box

### Why Nx over Turborepo/Rush?

- More mature and battle-tested
- Excellent plugin ecosystem
- Great developer experience (graph visualization)
- Nx Cloud for distributed caching (optional)

## Resources

- [Nx Documentation](https://nx.dev)
- [npm Workspaces](https://docs.npmjs.com/cli/v7/using-npm/workspaces)
- [Lerna Documentation](https://lerna.js.org)
- [Monorepo Tools](https://monorepo.tools)

---

**Last updated**: January 2024
EOF
```

---

### ÉTAPE 13 : Git Strategies pour Monorepos

#### 13.1 Sparse Checkout (ne clone que certains dossiers)

**Problème :** Le monorepo est énorme (10 GB), mais tu travailles seulement sur `apps/api`.

**Solution : Sparse Checkout**

```bash
# Clone seulement l'historique
git clone --no-checkout git@github.com:alice/taskmaster-pro.git
cd taskmaster-pro

# Activer sparse checkout
git sparse-checkout init --cone

# Définir les dossiers à checkout
git sparse-checkout set apps/api packages/utils packages/types

# Checkout
git checkout main
```

**Résultat :**
```
taskmaster-pro/
├── apps/
│   └── api/           [OK] Présent
├── packages/
│   ├── utils/         [OK] Présent
│   └── types/         [OK] Présent
└── .git/
```

**`apps/web`, `apps/mobile` ne sont PAS clonés ! [OK]**

---

#### 13.2 Git LFS pour gros fichiers

**Problème :** Design files, videos dans le monorepo.

**Solution : Git LFS (Large File Storage)**

```bash
# Installer Git LFS
git lfs install

# Tracker les gros fichiers
git lfs track "*.psd"
git lfs track "*.mp4"
git lfs track "*.zip"

# Commiter .gitattributes
git add .gitattributes
git commit -m "chore: setup Git LFS"
```

**Les gros fichiers sont stockés sur un serveur LFS, pas dans Git !**

---

#### 13.3 Shallow Clone

**Clone seulement les N derniers commits :**

```bash
git clone --depth=1 git@github.com:alice/taskmaster-pro.git
```

**Plus rapide ! Utile en CI/CD.**

---

### [OK] TESTS DE VALIDATION

**1. Workspaces configurés**

```bash
npm ls
ls -la node_modules/@taskmaster/
```

- [ ] Symlinks créés
- [ ] Packages accessibles

---

**2. Build fonctionne**

```bash
npm run build
ls packages/*/dist apps/*/dist
```

- [ ] Tous les packages buildés
- [ ] Dossiers `dist/` créés

---

**3. Imports inter-packages**

```typescript
// Dans apps/api
import { generateId } from '@taskmaster/utils';
import { Task } from '@taskmaster/types';
```

- [ ] Imports fonctionnent sans erreur
- [ ] TypeScript trouve les types

---

**4. Nx caching**

```bash
# Build 2 fois
nx build @taskmaster/utils
nx build @taskmaster/utils
```

- [ ] Deuxième build instantané (cache)

---

**5. Affected detection**

```bash
# Changer un fichier
echo "// comment" >> packages/utils/src/index.ts
git add .
git commit -m "test"

# Voir affectés
nx affected:graph
```

- [ ] Seulement utils + projets dépendants affichés

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : "Cannot find module '@taskmaster/utils'"

**Cause :** Symlinks pas créés

**Solution :**

```bash
rm -rf node_modules
npm install
```

---

#### Erreur 2 : TypeScript ne trouve pas les types

**Cause :** `tsconfig.json` paths mal configurés

**Solution :**

Vérifier `tsconfig.base.json` :

```json
{
  "compilerOptions": {
    "paths": {
      "@taskmaster/utils": ["packages/utils/src"]
    }
  }
}
```

---

#### Erreur 3 : Nx cache des anciennes builds

**Solution :**

```bash
nx reset
npm run build
```

---

#### Erreur 4 : "ENOENT: no such file or directory"

**Cause :** Package pas buildé

**Solution :**

```bash
# Build les dépendances d'abord
nx build @taskmaster/utils
nx build @taskmaster/types
nx build @taskmaster/api
```

Ou :

```bash
nx run-many --target=build --all
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. Monorepo Structure**

```
monorepo/
├── apps/          # Applications
├── packages/      # Librairies
├── tools/         # Scripts
├── package.json   # Root avec workspaces
└── nx.json        # Nx config
```

---

**2. npm Workspaces**

```json
{
  "workspaces": ["apps/*", "packages/*"]
}
```

**Bénéfices :**
- Un seul `node_modules/`
- Symlinks automatiques
- Installation rapide

---

**3. Nx Benefits**

| Feature | Benefit |
|---------|---------|
| Caching | 10-100x faster builds |
| Affected | Only test what changed |
| Parallel | Utilize all CPU cores |
| Graph | Visualize dependencies |

---

**4. Commands**

```bash
# Build tout
nx run-many --target=build --all

# Build affecté
nx affected:build

# Visualiser graph
nx graph

# Clear cache
nx reset
```

---

**5. Versioning**

```bash
# Lerna independent versioning
npx lerna version

# Conventional commits -> auto bump
feat: -> minor
fix: -> patch
BREAKING CHANGE: -> major
```

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Nx Cloud (Distributed Caching)**

```bash
# Activer Nx Cloud
npx nx connect-to-nx-cloud
```

**Bénéfices :**
- Cache partagé entre toute l'équipe
- Cache partagé entre local et CI
- Plus rapide que jamais

---

**2. Module Federation (Micro-frontends)**

Partager des composants React entre apps à runtime.

```javascript
// webpack.config.js
module.exports = {
  plugins: [
    new ModuleFederationPlugin({
      name: 'app1',
      filename: 'remoteEntry.js',
      exposes: {
        './Button': './src/Button',
      },
    }),
  ],
};
```

---

**3. Turborepo (Alternative à Nx)**

```bash
npm install turbo --global

# turbo.json
{
  "pipeline": {
    "build": {
      "dependsOn": ["^build"],
      "outputs": ["dist/**"]
    }
  }
}

# Run
turbo run build
```

**Plus simple que Nx, mais moins de features.**

---

**4. Changesets (Alternative à Lerna)**

```bash
npm install @changesets/cli

# Créer changeset
npx changeset add

# Version
npx changeset version

# Publish
npx changeset publish
```

---

**5. Bazel (Google's build system)**

Pour TRÈS gros monorepos (1000+ packages).

```python
# BUILD file
ts_library(
  name = "utils",
  srcs = glob(["src/**/*.ts"]),
)
```

---

**6. GitHub CodeSpaces pour monorepos**

```json
// .devcontainer/devcontainer.json
{
  "name": "TaskMaster Monorepo",
  "image": "mcr.microsoft.com/devcontainers/typescript-node:18",
  "postCreateCommand": "npm install",
  "customizations": {
    "vscode": {
      "extensions": [
        "dbaeumer.vscode-eslint",
        "esbenp.prettier-vscode"
      ]
    }
  }
}
```

**Un click -> environnement de dev complet dans le cloud !**

---

## [COURS] CONCLUSION DE L'EXERCICE 9

**[OK] Félicitations ! Tu maîtrises les Monorepos !**

**Ce que tu as appris :**
- Concepts de monorepo (avantages/inconvénients)
- npm workspaces
- Nx pour orchestration et caching
- Packages internes et dépendances
- Build optimization (cache, parallelization)
- Versioning avec Lerna
- CI/CD adapté aux monorepos
- Git strategies (sparse-checkout, LFS)

**Compétences acquises :**
- [OK] Monorepo Management (niveau expert)
- [OK] npm Workspaces
- [OK] Nx (caching, affected, graph)
- [OK] Package management
- [OK] Build optimization
- [OK] CI/CD monorepo
- [OK] Architecture at scale

**Temps moyen de réalisation :** 5-6 heures

**Prochaine étape :** Exercice 10 - GitOps & Déploiement Automatisé ! [RAPIDE]

---

(Dernier exercice ! Continue ?)

# [ROUGE] EXERCICE 10 : GITOPS & DÉPLOIEMENT AUTOMATISÉ

## [LISTE] ÉNONCÉ

### Contexte professionnel

TaskMaster Pro est maintenant en production avec des milliers d'utilisateurs ! [BRAVO]

**Problèmes actuels avec déploiement manuel :**
- [X] Déploiements manuels via SSH -> erreurs humaines
- [X] Configuration serveur non versionnée -> "ça marche sur ma machine"
- [X] Rollback difficile en cas de problème
- [X] Pas de traçabilité (qui a déployé quoi/quand)
- [X] Environnements dev/staging/prod incohérents
- [X] Secrets hardcodés dans le code
- [X] Déploiements lents (30+ minutes)
- [X] Impossible de reproduire l'infra

**Alice** décide d'implémenter **GitOps** pour :
- [OK] Infrastructure as Code (tout versionné dans Git)
- [OK] Déploiements automatiques (merge = deploy)
- [OK] Rollback facile (revert commit = rollback)
- [OK] Environnements reproductibles
- [OK] Audit trail complet
- [OK] Self-healing (auto-correction)
- [OK] Progressive delivery (canary, blue/green)

**Aujourd'hui, l'équipe va :**
1. Comprendre GitOps en profondeur
2. Mettre en place Infrastructure as Code (IaC)
3. Configurer Kubernetes pour orchestration
4. Implémenter CI/CD complet avec GitHub Actions
5. Déployer avec ArgoCD (GitOps operator)
6. Gérer les secrets avec Sealed Secrets
7. Mettre en place multi-environment (dev/staging/prod)
8. Configurer monitoring et alerting
9. Implémenter progressive delivery
10. Automatiser les rollbacks

### Objectifs

- Maîtriser les principes GitOps
- Infrastructure as Code complète
- Pipeline CI/CD end-to-end
- Déploiement automatique sur Kubernetes
- Gestion des secrets sécurisée
- Multi-environment strategy
- Monitoring et observabilité
- Progressive delivery

### Livrables

- Infrastructure définie en code (Terraform/Kubernetes manifests)
- Pipeline CI/CD complet
- ArgoCD configuré et fonctionnel
- 3 environnements (dev, staging, production)
- Secrets gérés avec Sealed Secrets
- Monitoring avec Prometheus + Grafana
- Documentation complète
- Runbook pour incidents

### Contraintes

- Tout dans Git (infrastructure, config, secrets chiffrés)
- Zero-downtime deployments
- Rollback automatique si healthcheck fail
- Environments isolés (namespaces Kubernetes)
- Secrets jamais en clair dans Git
- Temps estimé : 6-8 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :

- [OK] Comprendre GitOps (principes, avantages)
- [OK] Infrastructure as Code (IaC) avec Terraform
- [OK] Kubernetes (deployments, services, ingress)
- [OK] CI/CD pipelines avancés (GitHub Actions)
- [OK] ArgoCD pour GitOps
- [OK] Gestion des secrets (Sealed Secrets, Vault)
- [OK] Multi-environment deployments
- [OK] Progressive delivery (canary, blue/green)
- [OK] Monitoring et observabilité
- [OK] Incident response et rollbacks

---

## [DOCS] PRÉREQUIS

- Exercices 1 à 9 terminés (surtout 9 - monorepo)
- Docker installé et compris
- Compréhension basique de Kubernetes
- Compte cloud (AWS/GCP/Azure) ou cluster local (minikube/k3d)
- kubectl installé

---

## [GUIDE] THÉORIE : GitOps expliqué

### Qu'est-ce que GitOps ?

**GitOps** = Pratique où Git est la **single source of truth** pour l'infrastructure et les applications.

**Inventé par :** Weaveworks (2017)

**Principe :**
```
Git commit -> Automated deployment -> Production
```

**Au lieu de :**
```
Developer -> SSH -> kubectl apply -> Production (manual, error-prone)
```

---

### Les 4 principes de GitOps

#### 1. **Declarative** (Déclaratif)

L'infrastructure est définie de manière déclarative (état désiré, pas procédural).

**Impératif (mauvais) :**
```bash
kubectl create deployment nginx --image=nginx:1.20
kubectl expose deployment nginx --port=80
kubectl scale deployment nginx --replicas=3
```

**Déclaratif (bon) :**
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 3
  template:
    spec:
      containers:
      - name: nginx
        image: nginx:1.20
```

**Avantage :** Git peut versionner le YAML, pas les commandes impératives.

---

#### 2. **Versioned and Immutable** (Versionné et Immuable)

Tout est dans Git :
- [OK] Code source
- [OK] Configuration
- [OK] Infrastructure as Code
- [OK] Kubernetes manifests
- [OK] CI/CD pipelines

**Avantages :**
- Historique complet (qui a changé quoi/quand)
- Rollback facile (git revert)
- Audit trail
- Disaster recovery (git clone)

---

#### 3. **Pulled Automatically** (Pull automatique)

Un agent dans le cluster **pull** les changements depuis Git (au lieu de push).

**Push model (traditionnel) :**
```
CI/CD -> kubectl apply -> Cluster
         (credentials needed)
```

**Pull model (GitOps) :**
```
Git <- ArgoCD/Flux (running in cluster)
      v
   Cluster
```

**Avantages :**
- [OK] Pas besoin d'exposer le cluster (sécurité)
- [OK] Self-healing (agent détecte les drifts et corrige)
- [OK] Pas de credentials à gérer dans CI

---

#### 4. **Continuously Reconciled** (Réconciliation continue)

L'agent vérifie constamment que l'état réel = état désiré (Git).

**Si quelqu'un fait `kubectl delete deployment` manuellement :**
```
ArgoCD: "Hey, ce deployment devrait exister selon Git!"
        -> Re-crée automatiquement le deployment
```

**Self-healing automatique ! [BRAVO]**

---

### GitOps Workflow

```
┌─────────────┐
│ Developer   │
│  git push   │
└──────┬──────┘
       │
       [BLACK_DOWN-POINTING_TRIANGLE]
┌─────────────────┐
│   Git Repo      │
│ (Source of      │
│  Truth)         │
└────────┬────────┘
         │
         │ Pull (ArgoCD watches)
         [BLACK_DOWN-POINTING_TRIANGLE]
┌──────────────────────┐
│  GitOps Operator     │
│  (ArgoCD/Flux)       │
│  Running in cluster  │
└──────────┬───────────┘
           │
           │ Apply
           [BLACK_DOWN-POINTING_TRIANGLE]
┌──────────────────────┐
│  Kubernetes Cluster  │
│  (Production)        │
└──────────────────────┘
```

---

### Outils GitOps

| Tool | Société | Caractéristiques | Popularité |
|------|---------|------------------|------------|
| **ArgoCD** | Intuit/CNCF | UI web, App-of-Apps, multi-cluster | ***** |
| **Flux** | Weaveworks/CNCF | Natif GitOps, HelmRelease CRDs | **** |
| **Jenkins X** | CloudBees | Opinionated, preview environments | *** |
| **Spinnaker** | Netflix | Multi-cloud, advanced deployment | *** |
| **Tekton** | Google/CNCF | Kubernetes-native CI/CD | *** |

**On va utiliser : ArgoCD** (le plus populaire, excellent UI)

---

### Exemples réels

**Companies using GitOps :**
- **Weaveworks** : 100% GitOps (inventeurs)
- **Adobe** : GitOps pour 1000+ microservices
- **Intuit** : TurboTax sur GitOps
- **Red Hat** : OpenShift GitOps (ArgoCD)
- **Apple** : Plusieurs équipes sur Flux

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Setup Kubernetes Local (k3d)

**k3d** = Kubernetes local léger (k3s in Docker)

**Installer k3d :**

```bash
# macOS
brew install k3d

# Linux
wget -q -O - https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash

# Vérifier
k3d version
```

---

**Créer un cluster local :**

```bash
k3d cluster create taskmaster \
  --api-port 6443 \
  --servers 1 \
  --agents 2 \
  --port "8080:80@loadbalancer" \
  --port "8443:443@loadbalancer" \
  --k3s-arg "--disable=traefik@server:0"
```

**Explication :**

**`--servers 1`** : 1 master node
**`--agents 2`** : 2 worker nodes
**`--port "8080:80"`** : Map port 8080 (local) -> 80 (cluster)
**`--disable=traefik`** : Désactiver Traefik (on va utiliser nginx-ingress)

---

**Vérifier :**

```bash
kubectl cluster-info
kubectl get nodes
```

**Résultat :**

```
NAME                      STATUS   ROLES                  AGE   VERSION
k3d-taskmaster-server-0   Ready    control-plane,master   1m    v1.28.2+k3s1
k3d-taskmaster-agent-0    Ready    <none>                 1m    v1.28.2+k3s1
k3d-taskmaster-agent-1    Ready    <none>                 1m    v1.28.2+k3s1
```

**[OK] Cluster Kubernetes prêt ! [BRAVO]**

---

### ÉTAPE 2 : Structurer le dépôt GitOps

**Créer la structure :**

```bash
cd ~/taskmaster-pro
git switch develop
git pull origin develop

git switch -c feature/gitops-setup

# Créer structure GitOps
mkdir -p gitops/{base,overlays,apps,infrastructure}
mkdir -p gitops/overlays/{dev,staging,production}
mkdir -p gitops/apps/{api,web}
mkdir -p gitops/infrastructure/{nginx-ingress,argocd,monitoring}
```

**Structure finale :**

```
gitops/
├── base/                      # Manifests de base (communs)
│   ├── api/
│   └── web/
├── overlays/                  # Overlays par environnement (Kustomize)
│   ├── dev/
│   ├── staging/
│   └── production/
├── apps/                      # Application configs
│   ├── api/
│   └── web/
└── infrastructure/            # Infrastructure components
    ├── nginx-ingress/
    ├── argocd/
    └── monitoring/
```

**Philosophie :**

**`base/`** = Configuration de base (applicable partout)
**`overlays/`** = Customization par environnement (replicas, resources, images)
**`apps/`** = Application-level config
**`infrastructure/`** = Cluster-level infrastructure

---

### ÉTAPE 3 : Créer les Kubernetes Manifests (base)

**Créer le Deployment pour l'API :**

```bash
cat > gitops/base/api/deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: taskmaster-api
  labels:
    app: taskmaster-api
    tier: backend
spec:
  replicas: 2
  selector:
    matchLabels:
      app: taskmaster-api
  template:
    metadata:
      labels:
        app: taskmaster-api
        tier: backend
    spec:
      containers:
      - name: api
        image: taskmaster/api:latest  # Sera overridé par Kustomize
        ports:
        - containerPort: 3000
          name: http
        env:
        - name: NODE_ENV
          value: "production"
        - name: PORT
          value: "3000"
        - name: DB_HOST
          valueFrom:
            secretKeyRef:
              name: database-credentials
              key: host
        - name: DB_PASSWORD
          valueFrom:
            secretKeyRef:
              name: database-credentials
              key: password
        - name: JWT_SECRET
          valueFrom:
            secretKeyRef:
              name: jwt-secret
              key: secret
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "500m"
        livenessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 30
          periodSeconds: 10
          timeoutSeconds: 5
          failureThreshold: 3
        readinessProbe:
          httpGet:
            path: /ready
            port: 3000
          initialDelaySeconds: 10
          periodSeconds: 5
          timeoutSeconds: 3
          failureThreshold: 3
      # Security context
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        fsGroup: 1000
---
apiVersion: v1
kind: Service
metadata:
  name: taskmaster-api
  labels:
    app: taskmaster-api
spec:
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 3000
    protocol: TCP
    name: http
  selector:
    app: taskmaster-api
EOF
```

**Explication détaillée :**

**Deployment :**
- **replicas: 2** : 2 pods pour haute disponibilité
- **image** : Sera overridé par environnement
- **env** : Variables d'environnement (secrets via secretKeyRef)
- **resources** : CPU/Memory limits (évite qu'un pod consomme tout)
- **livenessProbe** : Kubernetes redémarre le pod si fail
- **readinessProbe** : Kubernetes ne route pas le traffic si not ready
- **securityContext** : Sécurité (pas root)

**Service :**
- **ClusterIP** : IP interne au cluster (pas exposé directement)
- **port 80** : Expose le service sur le port 80
- **targetPort 3000** : Forwarde vers le container port 3000

---

**Créer le Kustomization pour base/api :**

```bash
cat > gitops/base/api/kustomization.yaml << 'EOF'
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
- deployment.yaml

commonLabels:
  app.kubernetes.io/name: taskmaster-api
  app.kubernetes.io/part-of: taskmaster
  app.kubernetes.io/managed-by: kustomize

namespace: taskmaster
EOF
```

**Kustomize** = Tool pour gérer les variations de manifests Kubernetes (alternative à Helm)

---

**Créer le Deployment pour Web :**

```bash
cat > gitops/base/web/deployment.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: taskmaster-web
  labels:
    app: taskmaster-web
    tier: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
      app: taskmaster-web
  template:
    metadata:
      labels:
        app: taskmaster-web
        tier: frontend
    spec:
      containers:
      - name: web
        image: taskmaster/web:latest
        ports:
        - containerPort: 80
          name: http
        env:
        - name: API_URL
          value: "http://taskmaster-api"
        resources:
          requests:
            memory: "64Mi"
            cpu: "50m"
          limits:
            memory: "128Mi"
            cpu: "200m"
        livenessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 10
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /
            port: 80
          initialDelaySeconds: 5
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: taskmaster-web
  labels:
    app: taskmaster-web
spec:
  type: ClusterIP
  ports:
  - port: 80
    targetPort: 80
    protocol: TCP
    name: http
  selector:
    app: taskmaster-web
EOF
```

---

```bash
cat > gitops/base/web/kustomization.yaml << 'EOF'
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

resources:
- deployment.yaml

commonLabels:
  app.kubernetes.io/name: taskmaster-web
  app.kubernetes.io/part-of: taskmaster

namespace: taskmaster
EOF
```

---

**Créer l'Ingress (routing externe) :**

```bash
cat > gitops/base/ingress.yaml << 'EOF'
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: taskmaster-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - taskmaster.example.com
    secretName: taskmaster-tls
  rules:
  - host: taskmaster.example.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: taskmaster-api
            port:
              number: 80
      - path: /
        pathType: Prefix
        backend:
          service:
            name: taskmaster-web
            port:
              number: 80
EOF
```

**Explication :**

**Ingress** = Reverse proxy pour router le traffic externe vers les services

**Rules :**
- `taskmaster.example.com/api/*` -> `taskmaster-api` service
- `taskmaster.example.com/*` -> `taskmaster-web` service

**TLS** : Certificat SSL (Let's Encrypt)

---

### ÉTAPE 4 : Overlays par environnement (Kustomize)

**Kustomize** permet de créer des variations sans dupliquer les manifests.

**Overlay dev :**

```bash
cat > gitops/overlays/dev/kustomization.yaml << 'EOF'
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: taskmaster-dev

bases:
- ../../base/api
- ../../base/web

commonLabels:
  environment: dev

# Patches spécifiques à dev
patchesStrategicMerge:
- api-patch.yaml
- web-patch.yaml

images:
- name: taskmaster/api
  newTag: dev-latest
- name: taskmaster/web
  newTag: dev-latest

replicas:
- name: taskmaster-api
  count: 1
- name: taskmaster-web
  count: 1
EOF
```

---

**Patch pour API dev (moins de resources) :**

```bash
cat > gitops/overlays/dev/api-patch.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: taskmaster-api
spec:
  template:
    spec:
      containers:
      - name: api
        env:
        - name: NODE_ENV
          value: "development"
        - name: LOG_LEVEL
          value: "debug"
        resources:
          requests:
            memory: "64Mi"
            cpu: "50m"
          limits:
            memory: "128Mi"
            cpu: "200m"
EOF
```

---

**Patch pour Web dev :**

```bash
cat > gitops/overlays/dev/web-patch.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: taskmaster-web
spec:
  template:
    spec:
      containers:
      - name: web
        env:
        - name: API_URL
          value: "http://taskmaster-api.taskmaster-dev"
EOF
```

---

**Overlay staging (milieu de gamme) :**

```bash
cat > gitops/overlays/staging/kustomization.yaml << 'EOF'
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: taskmaster-staging

bases:
- ../../base/api
- ../../base/web

commonLabels:
  environment: staging

images:
- name: taskmaster/api
  newTag: staging-latest
- name: taskmaster/web
  newTag: staging-latest

replicas:
- name: taskmaster-api
  count: 2
- name: taskmaster-web
  count: 2
EOF
```

---

**Overlay production (haute disponibilité) :**

```bash
cat > gitops/overlays/production/kustomization.yaml << 'EOF'
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

namespace: taskmaster-prod

bases:
- ../../base/api
- ../../base/web

commonLabels:
  environment: production

patchesStrategicMerge:
- production-patch.yaml

images:
- name: taskmaster/api
  newTag: v1.0.0  # Version spécifique (pas latest)
- name: taskmaster/web
  newTag: v1.0.0

replicas:
- name: taskmaster-api
  count: 5
- name: taskmaster-web
  count: 5
EOF
```

---

**Patch production (resources supérieures) :**

```bash
cat > gitops/overlays/production/production-patch.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: taskmaster-api
spec:
  template:
    spec:
      containers:
      - name: api
        resources:
          requests:
            memory: "256Mi"
            cpu: "200m"
          limits:
            memory: "512Mi"
            cpu: "1000m"
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: taskmaster-web
spec:
  template:
    spec:
      containers:
      - name: web
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "500m"
EOF
```

---

**Tester Kustomize localement :**

```bash
# Voir les manifests générés pour dev
kubectl kustomize gitops/overlays/dev

# Voir les manifests pour production
kubectl kustomize gitops/overlays/production
```

**Résultat :**
YAML complet avec toutes les customizations appliquées ! [OK]

---

### ÉTAPE 5 : Installer ArgoCD

**ArgoCD** = Outil GitOps pour Kubernetes

**Installer ArgoCD :**

```bash
# Créer namespace
kubectl create namespace argocd

# Installer ArgoCD
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
```

**Attendre que tout soit prêt :**

```bash
kubectl wait --for=condition=Ready pods --all -n argocd --timeout=300s
```

---

**Exposer l'UI ArgoCD :**

```bash
# Port-forward pour accéder localement
kubectl port-forward svc/argocd-server -n argocd 8080:443 &
```

**Ou créer un Ingress (production) :**

```bash
cat > gitops/infrastructure/argocd/ingress.yaml << 'EOF'
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: argocd-server-ingress
  namespace: argocd
  annotations:
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
  ingressClassName: nginx
  rules:
  - host: argocd.taskmaster.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: argocd-server
            port:
              name: https
  tls:
  - hosts:
    - argocd.taskmaster.example.com
    secretName: argocd-tls
EOF
```

---

**Obtenir le mot de passe initial :**

```bash
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
echo
```

**Copier le mot de passe.**

---

**Se connecter à ArgoCD UI :**

1. Ouvrir : https://localhost:8080
2. Username : `admin`
3. Password : (celui obtenu ci-dessus)

**[OK] ArgoCD UI accessible ! [BRAVO]**

---

**Installer ArgoCD CLI :**

```bash
# macOS
brew install argocd

# Linux
curl -sSL -o /usr/local/bin/argocd https://github.com/argoproj/argo-cd/releases/latest/download/argocd-linux-amd64
chmod +x /usr/local/bin/argocd
```

---

**Login via CLI :**

```bash
argocd login localhost:8080 --username admin --password <password> --insecure
```

---

**Changer le mot de passe :**

```bash
argocd account update-password
```

---

### ÉTAPE 6 : Créer les Applications ArgoCD

**ArgoCD Application** = Ressource qui définit comment déployer une app depuis Git

**Créer l'application dev :**

```bash
cat > gitops/apps/api/dev-application.yaml << 'EOF'
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: taskmaster-api-dev
  namespace: argocd
spec:
  project: default
  
  source:
    repoURL: https://github.com/alice/taskmaster-pro.git
    targetRevision: develop
    path: gitops/overlays/dev
  
  destination:
    server: https://kubernetes.default.svc
    namespace: taskmaster-dev
  
  syncPolicy:
    automated:
      prune: true      # Supprime les ressources qui ne sont plus dans Git
      selfHeal: true   # Auto-corrige les drifts (self-healing)
      allowEmpty: false
    syncOptions:
    - CreateNamespace=true
    retry:
      limit: 5
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 3m
  
  # Health assessment
  ignoreDifferences:
  - group: apps
    kind: Deployment
    jsonPointers:
    - /spec/replicas  # Ignore replicas (HPA might change it)
EOF
```

**Explication :**

**source.repoURL** : Dépôt Git
**source.path** : Dossier contenant les manifests
**targetRevision** : Branche Git (dev -> `develop`, prod -> `main`)

**syncPolicy.automated** :
- **prune: true** : Supprime les ressources qui ne sont plus dans Git
- **selfHeal: true** : Si quelqu'un fait `kubectl delete`, ArgoCD re-crée automatiquement !

**syncOptions.CreateNamespace=true** : Crée le namespace automatiquement

---

**Créer l'application production :**

```bash
cat > gitops/apps/api/prod-application.yaml << 'EOF'
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: taskmaster-api-prod
  namespace: argocd
spec:
  project: default
  
  source:
    repoURL: https://github.com/alice/taskmaster-pro.git
    targetRevision: main  # Production suit main
    path: gitops/overlays/production
  
  destination:
    server: https://kubernetes.default.svc
    namespace: taskmaster-prod
  
  syncPolicy:
    automated:
      prune: true
      selfHeal: false  # Pas de self-heal en prod (sécurité)
    syncOptions:
    - CreateNamespace=true
    retry:
      limit: 3
      backoff:
        duration: 5s
        factor: 2
        maxDuration: 2m
  
  # Rollback automatique si health check fail
  syncPolicy:
    automated:
      prune: true
    syncOptions:
    - CreateNamespace=true
EOF
```

**Note :** En production, `selfHeal: false` pour éviter les changements automatiques non contrôlés.

---

**Appliquer les applications ArgoCD :**

```bash
kubectl apply -f gitops/apps/api/dev-application.yaml
kubectl apply -f gitops/apps/api/prod-application.yaml
```

---

**Vérifier dans ArgoCD UI :**

Rafraîchir l'UI ArgoCD -> Tu verras les applications `taskmaster-api-dev` et `taskmaster-api-prod` !

**Status :**
- **OutOfSync** : Git ≠ Cluster
- **Synced** : Git = Cluster
- **Healthy** : Pods running

---

**Synchroniser manuellement (première fois) :**

```bash
argocd app sync taskmaster-api-dev
argocd app sync taskmaster-api-prod
```

**Ou via UI : Cliquer "Sync" -> "Synchronize"**

---

**Voir les pods déployés :**

```bash
kubectl get pods -n taskmaster-dev
kubectl get pods -n taskmaster-prod
```

**[OK] Applications déployées par ArgoCD ! [BRAVO]**

---

### ÉTAPE 7 : Pipeline CI/CD complet

**Workflow :**

```
Developer
   v git push
GitHub
   v webhook
GitHub Actions (CI)
   v build + test
Docker Registry
   v image pushed
Update manifest in Git
   v
ArgoCD (CD)
   v pull & apply
Kubernetes
```

---

**Créer le workflow GitHub Actions :**

```bash
cat > .github/workflows/cd.yml << 'EOF'
name: CD - Continuous Deployment

on:
  push:
    branches:
      - main      # Production
      - develop   # Development
    paths:
      - 'apps/**'
      - 'packages/**'

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build-and-push:
    name: Build and Push Docker Images
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    
    outputs:
      api-tag: ${{ steps.meta-api.outputs.tags }}
      web-tag: ${{ steps.meta-web.outputs.tags }}
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      
      - name: Log in to Container Registry
        uses: docker/login-action@v2
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      
      # Build API
      - name: Extract API metadata
        id: meta-api
        uses: docker/metadata-action@v4
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/api
          tags: |
            type=ref,event=branch
            type=sha,prefix={{branch}}-
            type=semver,pattern={{version}}
      
      - name: Build and push API image
        uses: docker/build-push-action@v4
        with:
          context: ./apps/api
          push: true
          tags: ${{ steps.meta-api.outputs.tags }}
          labels: ${{ steps.meta-api.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
      
      # Build Web
      - name: Extract Web metadata
        id: meta-web
        uses: docker/metadata-action@v4
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}/web
          tags: |
            type=ref,event=branch
            type=sha,prefix={{branch}}-
            type=semver,pattern={{version}}
      
      - name: Build and push Web image
        uses: docker/build-push-action@v4
        with:
          context: ./apps/web
          push: true
          tags: ${{ steps.meta-web.outputs.tags }}
          labels: ${{ steps.meta-web.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

  update-manifests:
    name: Update Kubernetes Manifests
    needs: build-and-push
    runs-on: ubuntu-latest
    permissions:
      contents: write
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
        with:
          token: ${{ secrets.GITHUB_TOKEN }}
      
      - name: Determine environment
        id: env
        run: |
          if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then
            echo "environment=production" >> $GITHUB_OUTPUT
            echo "overlay=production" >> $GITHUB_OUTPUT
          else
            echo "environment=development" >> $GITHUB_OUTPUT
            echo "overlay=dev" >> $GITHUB_OUTPUT
          fi
      
      - name: Update image tags in Kustomization
        run: |
          cd gitops/overlays/${{ steps.env.outputs.overlay }}
          
          # Extract SHA from tag
          API_TAG=$(echo "${{ needs.build-and-push.outputs.api-tag }}" | grep -oP '(?<=:).*' | head -1)
          WEB_TAG=$(echo "${{ needs.build-and-push.outputs.web-tag }}" | grep -oP '(?<=:).*' | head -1)
          
          # Update kustomization.yaml
          sed -i "s|newTag:.*# api|newTag: $API_TAG # api|g" kustomization.yaml
          sed -i "s|newTag:.*# web|newTag: $WEB_TAG # web|g" kustomization.yaml
      
      - name: Commit and push changes
        run: |
          git config user.name "GitHub Actions"
          git config user.email "actions@github.com"
          
          git add gitops/overlays/${{ steps.env.outputs.overlay }}/kustomization.yaml
          git commit -m "chore(gitops): update image tags for ${{ steps.env.outputs.environment }}

          API: ${{ needs.build-and-push.outputs.api-tag }}
          Web: ${{ needs.build-and-push.outputs.web-tag }}
          
          Triggered by: ${{ github.sha }}"
          
          git push

  notify:
    name: Notify Deployment
    needs: [build-and-push, update-manifests]
    runs-on: ubuntu-latest
    if: always()
    
    steps:
      - name: Send Slack notification
        uses: slackapi/slack-github-action@v1
        with:
          payload: |
            {
              "text": "Deployment to ${{ needs.update-manifests.outputs.environment }}",
              "blocks": [
                {
                  "type": "section",
                  "text": {
                    "type": "mrkdwn",
                    "text": "*Deployment Status:* ${{ job.status }}\n*Environment:* ${{ needs.update-manifests.outputs.environment }}\n*Commit:* ${{ github.sha }}"
                  }
                }
              ]
            }
        env:
          SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }}
EOF
```

**Explication du workflow :**

**Job 1 : build-and-push**
- Build les images Docker (API + Web)
- Tag avec branch + SHA git
- Push vers GitHub Container Registry (GHCR)
- Cache Docker layers pour speed

**Job 2 : update-manifests**
- Update les tags d'images dans `gitops/overlays/*/kustomization.yaml`
- Commit + push les changements
- ArgoCD détecte le changement et déploie automatiquement !

**Job 3 : notify**
- Envoie notification Slack

---

**GitOps Pull Model en action :**

```
1. Developer: git push apps/api/
2. GitHub Actions: Build image -> Push to registry
3. GitHub Actions: Update gitops/overlays/dev/kustomization.yaml
4. ArgoCD (polling Git): "New commit detected!"
5. ArgoCD: Pull new manifest -> Apply to cluster
6. Kubernetes: Deploy new version
```

**[OK] Fully automated deployment ! [BRAVO]**

---

### ÉTAPE 8 : Gestion des Secrets avec Sealed Secrets

**Problème :** On ne peut PAS commiter les secrets en clair dans Git !

**Solution : Sealed Secrets**

**Sealed Secrets** = Chiffre les secrets pour qu'ils soient safe dans Git

**Comment ça marche :**

```
1. Tu as un secret (password)
2. Tu le chiffres avec kubeseal -> SealedSecret
3. Tu commites le SealedSecret dans Git (safe!)
4. Sealed Secrets controller déchiffre dans le cluster -> Secret
```

---

**Installer Sealed Secrets Controller :**

```bash
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml
```

---

**Installer kubeseal CLI :**

```bash
# macOS
brew install kubeseal

# Linux
wget https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/kubeseal-0.24.0-linux-amd64.tar.gz
tar xfz kubeseal-0.24.0-linux-amd64.tar.gz
sudo install -m 755 kubeseal /usr/local/bin/kubeseal
```

---

**Créer un secret classique (pas encore chiffré) :**

```bash
cat > /tmp/database-secret.yaml << 'EOF'
apiVersion: v1
kind: Secret
metadata:
  name: database-credentials
  namespace: taskmaster-prod
type: Opaque
stringData:
  host: "postgres.taskmaster-prod.svc.cluster.local"
  database: "taskmaster"
  username: "taskmaster_user"
  password: "super_secret_password_123"
EOF
```

**[ATTENTION] NE PAS commiter ce fichier ! Il contient le password en clair !**

---

**Chiffrer avec kubeseal :**

```bash
kubeseal -f /tmp/database-secret.yaml -w gitops/overlays/production/sealed-database-secret.yaml \
  --controller-namespace sealed-secrets \
  --controller-name sealed-secrets \
  --format yaml
```

**Résultat : `gitops/overlays/production/sealed-database-secret.yaml` :**

```yaml
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: database-credentials
  namespace: taskmaster-prod
spec:
  encryptedData:
    host: AgB7... (chiffré)
    database: AgC9... (chiffré)
    username: AgD2... (chiffré)
    password: AgE5... (chiffré)
  template:
    metadata:
      name: database-credentials
      namespace: taskmaster-prod
    type: Opaque
```

**[OK] Ce fichier est SAFE à commiter dans Git ! Le password est chiffré ! [VERROUILLE]**

---

**Ajouter au Kustomization :**

```bash
cat >> gitops/overlays/production/kustomization.yaml << 'EOF'

resources:
- sealed-database-secret.yaml
EOF
```

---

**Commiter :**

```bash
git add gitops/overlays/production/sealed-database-secret.yaml
git add gitops/overlays/production/kustomization.yaml
git commit -m "feat(secrets): add sealed database credentials for production"
git push
```

---

**ArgoCD sync -> Sealed Secret controller déchiffre -> Secret créé !**

**Vérifier :**

```bash
kubectl get secret database-credentials -n taskmaster-prod -o yaml
```

**Le secret est déchiffré et disponible pour les pods ! [OK]**

---

**Créer un SealedSecret pour JWT :**

```bash
cat > /tmp/jwt-secret.yaml << 'EOF'
apiVersion: v1
kind: Secret
metadata:
  name: jwt-secret
  namespace: taskmaster-prod
type: Opaque
stringData:
  secret: "my-super-secret-jwt-key-change-this-in-production"
EOF

kubeseal -f /tmp/jwt-secret.yaml -w gitops/overlays/production/sealed-jwt-secret.yaml --format yaml
```

---

**Nettoyer les fichiers temporaires (secrets en clair) :**

```bash
rm /tmp/database-secret.yaml /tmp/jwt-secret.yaml
```

**[OK] Plus aucun secret en clair sur le disque ! [OK]**

---

### ÉTAPE 9 : Monitoring avec Prometheus + Grafana

**Installer kube-prometheus-stack (Prometheus + Grafana + Alertmanager) :**

```bash
# Ajouter le repo Helm
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts
helm repo update

# Installer
helm install kube-prometheus-stack prometheus-community/kube-prometheus-stack \
  --namespace monitoring \
  --create-namespace \
  --set prometheus.prometheusSpec.serviceMonitorSelectorNilUsesHelmValues=false \
  --set grafana.adminPassword=admin
```

---

**Exposer Grafana :**

```bash
kubectl port-forward -n monitoring svc/kube-prometheus-stack-grafana 3000:80 &
```

**Ouvrir : http://localhost:3000**

**Login :**
- Username: `admin`
- Password: `admin`

---

**Dashboards pré-configurés disponibles :**
- Kubernetes Cluster Monitoring
- Node Exporter
- Pods / Deployments
- CPU / Memory usage
- Network I/O

**[OK] Monitoring opérationnel ! [GRAPHIQUE]**

---

**Créer un ServiceMonitor pour exposer les métriques de l'API :**

```bash
cat > gitops/base/api/servicemonitor.yaml << 'EOF'
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
  name: taskmaster-api
  labels:
    app: taskmaster-api
spec:
  selector:
    matchLabels:
      app: taskmaster-api
  endpoints:
  - port: http
    path: /metrics
    interval: 30s
EOF
```

---

**Modifier l'API pour exposer des métriques Prometheus :**

```bash
cat > apps/api/src/metrics.js << 'EOF'
const promClient = require('prom-client');

// Create a Registry
const register = new promClient.Registry();

// Add default metrics
promClient.collectDefaultMetrics({ register });

// Custom metrics
const httpRequestDuration = new promClient.Histogram({
  name: 'http_request_duration_seconds',
  help: 'Duration of HTTP requests in seconds',
  labelNames: ['method', 'route', 'status_code'],
  buckets: [0.1, 0.5, 1, 2, 5]
});

const httpRequestTotal = new promClient.Counter({
  name: 'http_requests_total',
  help: 'Total number of HTTP requests',
  labelNames: ['method', 'route', 'status_code']
});

register.registerMetric(httpRequestDuration);
register.registerMetric(httpRequestTotal);

module.exports = {
  register,
  httpRequestDuration,
  httpRequestTotal
};
EOF
```

---

**Exposer l'endpoint /metrics :**

```bash
cat >> apps/api/src/index.js << 'EOF'

const { register } = require('./metrics');

app.get('/metrics', async (req, res) => {
  res.set('Content-Type', register.contentType);
  res.end(await register.metrics());
});
EOF
```

---

**Créer un dashboard Grafana pour TaskMaster :**

```json
{
  "dashboard": {
    "title": "TaskMaster API",
    "panels": [
      {
        "title": "Request Rate",
        "targets": [
          {
            "expr": "rate(http_requests_total[5m])"
          }
        ]
      },
      {
        "title": "Request Duration (p95)",
        "targets": [
          {
            "expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))"
          }
        ]
      }
    ]
  }
}
```

**Importer dans Grafana -> Dashboards -> Import**

---

### ÉTAPE 10 : Progressive Delivery - Canary Deployment

**Progressive Delivery** = Déployer graduellement (pas 100% d'un coup)

**Stratégies :**
- **Canary** : 10% des users -> nouvelle version, 90% -> ancienne
- **Blue/Green** : 2 environnements complets, switch instantané
- **Rolling Update** : Update pods un par un

---

**Installer Argo Rollouts (pour canary) :**

```bash
kubectl create namespace argo-rollouts
kubectl apply -n argo-rollouts -f https://github.com/argoproj/argo-rollouts/releases/latest/download/install.yaml
```

---

**Installer kubectl plugin :**

```bash
curl -LO https://github.com/argoproj/argo-rollouts/releases/latest/download/kubectl-argo-rollouts-linux-amd64
chmod +x kubectl-argo-rollouts-linux-amd64
sudo mv kubectl-argo-rollouts-linux-amd64 /usr/local/bin/kubectl-argo-rollouts
```

---

**Créer un Rollout (au lieu de Deployment) :**

```bash
cat > gitops/base/api/rollout.yaml << 'EOF'
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
  name: taskmaster-api
spec:
  replicas: 5
  revisionHistoryLimit: 3
  selector:
    matchLabels:
      app: taskmaster-api
  template:
    metadata:
      labels:
        app: taskmaster-api
    spec:
      containers:
      - name: api
        image: taskmaster/api:latest
        ports:
        - containerPort: 3000
  
  strategy:
    canary:
      steps:
      - setWeight: 20   # 20% traffic to new version
      - pause:
          duration: 2m  # Wait 2 minutes
      - setWeight: 40   # 40% traffic
      - pause:
          duration: 2m
      - setWeight: 60   # 60% traffic
      - pause:
          duration: 2m
      - setWeight: 80   # 80% traffic
      - pause:
          duration: 2m
      # If everything is OK, promote to 100%
      
      # Automatic rollback on failure
      analysis:
        templates:
        - templateName: success-rate
        args:
        - name: service-name
          value: taskmaster-api
EOF
```

**Explication :**

**Canary steps :**
1. Deploy nouvelle version avec 20% traffic
2. Attendre 2 minutes
3. Si OK -> 40% traffic
4. Si OK -> 60% traffic
5. Si OK -> 80% traffic
6. Si OK -> 100% (promotion complète)

**Si error rate > threshold -> Automatic rollback !**

---

**Créer l'AnalysisTemplate (pour décider si rollback) :**

```bash
cat > gitops/base/api/analysis-template.yaml << 'EOF'
apiVersion: argoproj.io/v1alpha1
kind: AnalysisTemplate
metadata:
  name: success-rate
spec:
  args:
  - name: service-name
  
  metrics:
  - name: success-rate
    interval: 1m
    failureLimit: 3
    provider:
      prometheus:
        address: http://kube-prometheus-stack-prometheus.monitoring:9090
        query: |
          sum(rate(
            http_requests_total{
              service="{{ args.service-name }}",
              status_code!~"5.."
            }[2m]
          )) 
          / 
          sum(rate(
            http_requests_total{
              service="{{ args.service-name }}"
            }[2m]
          ))
    
    successCondition: result >= 0.95  # 95% success rate minimum
    failureCondition: result < 0.95
EOF
```

**Si success rate < 95% pendant le canary -> Automatic rollback ! [OBJECTIF]**

---

**Déployer une nouvelle version :**

```bash
# Changer l'image tag dans kustomization.yaml
# Push vers Git
# ArgoCD sync

# Suivre le rollout
kubectl argo rollouts get rollout taskmaster-api -n taskmaster-prod --watch
```

**Résultat :**

```
Name:            taskmaster-api
Namespace:       taskmaster-prod
Status:          ॥ Paused
Message:         CanaryPauseStep
Strategy:        Canary
  Step:          1/8
  SetWeight:     20
  ActualWeight:  20
Images:          taskmaster/api:v1.1.0 (canary)
                 taskmaster/api:v1.0.0 (stable)
Replicas:
  Desired:       5
  Current:       6
  Updated:       1
  Ready:         6
  Available:     6

NAME                                      KIND        STATUS     AGE
⟳ taskmaster-api                          Rollout     ॥ Paused   5m
├──# revision:2
│  └──⧉ taskmaster-api-789d5c            ReplicaSet  [OK] Healthy  30s
│     └──[WHITE_SQUARE] taskmaster-api-789d5c-abcde   Pod         [OK] Running  30s
└──# revision:1
   └──⧉ taskmaster-api-5bf4d6            ReplicaSet  [OK] Healthy  5m
      ├──[WHITE_SQUARE] taskmaster-api-5bf4d6-12345   Pod         [OK] Running  5m
      ├──[WHITE_SQUARE] taskmaster-api-5bf4d6-23456   Pod         [OK] Running  5m
      ├──[WHITE_SQUARE] taskmaster-api-5bf4d6-34567   Pod         [OK] Running  5m
      ├──[WHITE_SQUARE] taskmaster-api-5bf4d6-45678   Pod         [OK] Running  5m
      └──[WHITE_SQUARE] taskmaster-api-5bf4d6-56789   Pod         [OK] Running  5m
```

**20% traffic -> nouvelle version (1 pod sur 5) !**

---

**Promouvoir manuellement (si tout va bien) :**

```bash
kubectl argo rollouts promote taskmaster-api -n taskmaster-prod
```

---

**Rollback manuel (si problème détecté) :**

```bash
kubectl argo rollouts abort taskmaster-api -n taskmaster-prod
```

**[OK] Progressive delivery opérationnel ! [RAPIDE]**

---

### ÉTAPE 11 : Observabilité - Logs, Traces, Metrics

**Les 3 piliers de l'observabilité :**

1. **Logs** -> Ce qui s'est passé
2. **Metrics** -> Combien/Quand
3. **Traces** -> Comment (distributed tracing)

---

**Installer Loki (logs) :**

```bash
helm repo add grafana https://grafana.github.io/helm-charts
helm repo update

helm install loki grafana/loki-stack \
  --namespace monitoring \
  --set grafana.enabled=false \
  --set prometheus.enabled=false
```

---

**Configurer Grafana pour utiliser Loki :**

```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: grafana-datasources
  namespace: monitoring
data:
  loki.yaml: |
    apiVersion: 1
    datasources:
    - name: Loki
      type: loki
      access: proxy
      url: http://loki:3100
      isDefault: false
```

---

**Query logs dans Grafana :**

```
{app="taskmaster-api"} |= "error"
```

**Voir tous les logs d'erreur de l'API ! [DOC]**

---

**Installer Jaeger (distributed tracing) :**

```bash
kubectl create namespace observability

kubectl apply -f https://github.com/jaegertracing/jaeger-operator/releases/download/v1.50.0/jaeger-operator.yaml -n observability
```

---

**Créer une instance Jaeger :**

```bash
cat > gitops/infrastructure/monitoring/jaeger.yaml << 'EOF'
apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:
  name: jaeger
  namespace: observability
spec:
  strategy: allInOne
  allInOne:
    image: jaegertracing/all-in-one:latest
    options:
      log-level: debug
  storage:
    type: memory
  ingress:
    enabled: true
  ui:
    options:
      dependencies:
        menuEnabled: true
      tracking:
        gaID: UA-000000-2
EOF
```

---

**Instrumenter l'API avec OpenTelemetry :**

```bash
npm install --save @opentelemetry/api @opentelemetry/sdk-node @opentelemetry/auto-instrumentations-node
```

```javascript
// apps/api/src/tracing.js
const { NodeSDK } = require('@opentelemetry/sdk-node');
const { getNodeAutoInstrumentations } = require('@opentelemetry/auto-instrumentations-node');
const { JaegerExporter } = require('@opentelemetry/exporter-jaeger');

const sdk = new NodeSDK({
  traceExporter: new JaegerExporter({
    endpoint: 'http://jaeger-collector.observability:14268/api/traces',
  }),
  instrumentations: [getNodeAutoInstrumentations()],
});

sdk.start();
```

**Maintenant tous les requests HTTP sont tracés automatiquement ! [RECHERCHE]**

---

### ÉTAPE 12 : Documentation GitOps

**Créer GITOPS.md :**

```bash
cat > GITOPS.md << 'EOF'
# GitOps Deployment Guide

## Overview

TaskMaster uses **GitOps** for all deployments. This means:

- [OK] Git is the single source of truth
- [OK] All changes go through Pull Requests
- [OK] Automated deployments on merge
- [OK] Self-healing infrastructure
- [OK] Full audit trail

## Architecture

```
Developer
   v git push
GitHub
   v webhook
GitHub Actions (CI)
   v build image
Container Registry
   v
Update gitops/overlays/
   v
ArgoCD (GitOps operator)
   v sync
Kubernetes Cluster
```

## Environments

| Environment | Branch | Namespace | Replicas | Auto-deploy |
|-------------|--------|-----------|----------|-------------|
| **Development** | `develop` | `taskmaster-dev` | 1 | [OK] Yes |
| **Staging** | `staging` | `taskmaster-staging` | 2 | [OK] Yes |
| **Production** | `main` | `taskmaster-prod` | 5 | [X] Manual |

## Deployment Process

### Development

1. Create feature branch from `develop`
2. Make changes to code
3. Push to GitHub
4. CI builds image -> `ghcr.io/alice/taskmaster/api:develop-sha123`
5. CI updates `gitops/overlays/dev/kustomization.yaml`
6. ArgoCD detects change and deploys automatically

**Time to production:** ~5 minutes

### Staging

1. Merge feature branch -> `staging`
2. CI builds image -> `ghcr.io/alice/taskmaster/api:staging-sha456`
3. CI updates `gitops/overlays/staging/kustomization.yaml`
4. ArgoCD deploys to staging

### Production

1. Create release PR: `staging` -> `main`
2. Code review + approval required
3. Merge PR
4. CI builds image -> `ghcr.io/alice/taskmaster/api:v1.2.3`
5. CI updates `gitops/overlays/production/kustomization.yaml`
6. **Manual sync required in ArgoCD** (safety)
7. Canary deployment (20% -> 40% -> 60% -> 80% -> 100%)

**Time to production:** ~15-20 minutes (with canary)

## ArgoCD Access

**URL:** https://argocd.taskmaster.example.com

**Login:**
- Username: `admin`
- Password: (in 1Password)

### Common Tasks

**Sync an application manually:**
```bash
argocd app sync taskmaster-api-prod
```

**Check application status:**
```bash
argocd app get taskmaster-api-prod
```

**View logs:**
```bash
argocd app logs taskmaster-api-prod
```

## Rollback

### Automatic Rollback

If canary deployment detects error rate > 5%, automatic rollback happens.

**Triggers:**
- HTTP 5xx errors > 5%
- Pod crash loops
- Failed health checks

### Manual Rollback

**Option 1: Git revert (recommended)**

```bash
# Find the bad commit
git log gitops/overlays/production/kustomization.yaml

# Revert
git revert <commit-hash>
git push

# ArgoCD will deploy the previous version
```

**Option 2: ArgoCD rollback**

```bash
# List history
argocd app history taskmaster-api-prod

# Rollback to revision
argocd app rollback taskmaster-api-prod 5
```

**Option 3: Argo Rollouts abort**

```bash
kubectl argo rollouts abort taskmaster-api -n taskmaster-prod
```

## Secrets Management

Secrets are managed with **Sealed Secrets**.

**Never commit plain secrets to Git!**

### Creating a Secret

```bash
# 1. Create secret YAML (don't commit!)
cat > /tmp/secret.yaml << EOF
apiVersion: v1
kind: Secret
metadata:
  name: my-secret
  namespace: taskmaster-prod
stringData:
  password: "super-secret"
EOF

# 2. Seal it
kubeseal -f /tmp/secret.yaml \
  -w gitops/overlays/production/sealed-my-secret.yaml \
  --format yaml

# 3. Commit the sealed secret
git add gitops/overlays/production/sealed-my-secret.yaml
git commit -m "feat(secrets): add my-secret"

# 4. Delete the plain secret
rm /tmp/secret.yaml
```

### Updating a Secret

Same process as creating. The new SealedSecret will replace the old one.

## Monitoring

**Grafana:** https://grafana.taskmaster.example.com

**Dashboards:**
- Kubernetes Cluster Overview
- TaskMaster API Metrics
- TaskMaster Web Metrics
- Error Rate & Latency

**Alerts** are configured in Alertmanager:
- High error rate (> 5%)
- High latency (p95 > 2s)
- Pod crash loops
- High memory usage (> 80%)

## Incident Response

### High Error Rate

1. Check Grafana dashboard
2. Check logs: `kubectl logs -n taskmaster-prod -l app=taskmaster-api`
3. Check Jaeger traces for slow requests
4. If needed, rollback (see above)

### Pod Not Starting

```bash
# Check pod status
kubectl get pods -n taskmaster-prod

# Describe pod
kubectl describe pod <pod-name> -n taskmaster-prod

# Check logs
kubectl logs <pod-name> -n taskmaster-prod

# Common issues:
# - Image pull error -> check image tag
# - CrashLoopBackOff -> check logs
# - Pending -> check resources
```

### Out of Sync

If ArgoCD shows "OutOfSync":

```bash
# Check what's different
argocd app diff taskmaster-api-prod

# Sync
argocd app sync taskmaster-api-prod
```

## Best Practices

### DO [OK]

- Always create feature branches
- Write descriptive commit messages
- Test in dev/staging before production
- Use semantic versioning for production images
- Keep manifests DRY (use Kustomize overlays)
- Document changes in PR descriptions
- Monitor deployments in Grafana

### DON'T [X]

- Never `kubectl apply` directly to production
- Never commit plain secrets
- Never force-push to `main`
- Never skip staging
- Don't use `latest` tag in production

## Troubleshooting

### ArgoCD not syncing

```bash
# Check application status
argocd app get taskmaster-api-prod

# Check ArgoCD logs
kubectl logs -n argocd -l app.kubernetes.io/name=argocd-application-controller

# Force refresh
argocd app refresh taskmaster-api-prod
```

### Image not found

```bash
# Check image exists
docker pull ghcr.io/alice/taskmaster/api:v1.2.3

# Check imagePullSecrets
kubectl get secrets -n taskmaster-prod
```

### Sealed Secret not decrypting

```bash
# Check sealed-secrets controller logs
kubectl logs -n sealed-secrets -l name=sealed-secrets-controller

# Verify certificate
kubectl get secret -n sealed-secrets sealed-secrets-key -o yaml
```

## Resources

- [ArgoCD Documentation](https://argo-cd.readthedocs.io/)
- [Kustomize Documentation](https://kustomize.io/)
- [Sealed Secrets](https://github.com/bitnami-labs/sealed-secrets)
- [Argo Rollouts](https://argoproj.github.io/argo-rollouts/)

---

**Last updated:** January 2024
EOF
```

---

### [OK] TESTS DE VALIDATION

**1. ArgoCD opérationnel**

```bash
kubectl get pods -n argocd
argocd app list
```

- [ ] Tous les pods ArgoCD running
- [ ] Applications visibles

---

**2. Applications déployées**

```bash
kubectl get deployments -n taskmaster-dev
kubectl get deployments -n taskmaster-prod
```

- [ ] Deployments créés
- [ ] Pods running

---

**3. GitOps sync fonctionne**

```bash
# Modifier un manifest
echo "  # test" >> gitops/overlays/dev/kustomization.yaml
git add .
git commit -m "test: gitops sync"
git push

# Attendre ~30s
argocd app get taskmaster-api-dev
```

- [ ] ArgoCD détecte le changement
- [ ] Status: Synced

---

**4. Secrets déchiffrés**

```bash
kubectl get secret database-credentials -n taskmaster-prod
kubectl get secret database-credentials -n taskmaster-prod -o jsonpath='{.data.password}' | base64 -d
```

- [ ] Secret existe
- [ ] Password déchiffré

---

**5. Monitoring actif**

- [ ] Grafana accessible
- [ ] Dashboards chargent
- [ ] Métriques visibles

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : ArgoCD "OutOfSync"

**Cause :** Manifest Git ≠ Cluster

**Solution :**

```bash
argocd app sync <app-name>
```

---

#### Erreur 2 : Sealed Secret ne déchiffre pas

**Cause :** Mauvais namespace ou mauvaise clé

**Solution :**

```bash
# Vérifier le controller
kubectl logs -n sealed-secrets -l name=sealed-secrets-controller

# Re-sealer avec le bon namespace
kubeseal -f secret.yaml -w sealed-secret.yaml --namespace taskmaster-prod
```

---

#### Erreur 3 : Image pull error

**Cause :** Image n'existe pas ou credentials manquants

**Solution :**

```bash
# Vérifier que l'image existe
docker pull ghcr.io/user/repo:tag

# Créer imagePullSecret si privé
kubectl create secret docker-registry ghcr-secret \
  --docker-server=ghcr.io \
  --docker-username=<username> \
  --docker-password=<token> \
  -n taskmaster-prod
```

---

#### Erreur 4 : Canary stuck

**Cause :** Analysis template fail ou pause infinie

**Solution :**

```bash
# Promouvoir manuellement
kubectl argo rollouts promote <rollout-name>

# Ou abort
kubectl argo rollouts abort <rollout-name>
```

---

### [IMPORTANT] POINTS CLÉS À RETENIR

**1. GitOps Principles**

- Declarative
- Versioned
- Pulled automatically
- Continuously reconciled

---

**2. GitOps Workflow**

```
Code Change -> Git Push -> CI Build -> Update Manifest -> ArgoCD Sync -> Deploy
```

---

**3. ArgoCD Benefits**

| Benefit | Explanation |
|---------|-------------|
| Self-healing | Auto-corrects drifts |
| Audit trail | All changes in Git |
| Easy rollback | Git revert |
| Multi-cluster | Manage many clusters |

---

**4. Secrets Management**

```bash
Secret (plain) -> kubeseal -> SealedSecret (encrypted) -> Git -> Cluster -> Secret (plain)
```

**Never commit plain secrets!**

---

**5. Progressive Delivery**

**Canary:** 20% -> 40% -> 60% -> 80% -> 100%

**Benefits:**
- Detect issues early (only 20% affected)
- Automatic rollback on failure
- Zero-downtime deployments

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Multi-Cluster GitOps**

```yaml
# ArgoCD ApplicationSet
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
  name: taskmaster-multi-cluster
spec:
  generators:
  - list:
      elements:
      - cluster: us-east
        url: https://k8s-us-east.example.com
      - cluster: eu-west
        url: https://k8s-eu-west.example.com
  template:
    spec:
      source:
        repoURL: https://github.com/alice/taskmaster-pro
        path: gitops/overlays/production
      destination:
        server: '{{url}}'
```

**Deploy to multiple clusters from one Git repo!**

---

**2. Policy as Code avec OPA/Gatekeeper**

```yaml
# Deny latest tag in production
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sNoLatestTag
metadata:
  name: deny-latest-tag-prod
spec:
  match:
    namespaces:
    - taskmaster-prod
```

---

**3. Cost Optimization avec Karpenter**

Auto-scale nodes based on workload:

```yaml
apiVersion: karpenter.sh/v1alpha5
kind: Provisioner
metadata:
  name: default
spec:
  requirements:
  - key: karpenter.sh/capacity-type
    operator: In
    values: ["spot", "on-demand"]
  limits:
    resources:
      cpu: 100
```

**Use spot instances -> save 70% on compute!**

---

**4. Feature Flags avec LaunchDarkly/Flagsmith**

```javascript
const flags = await client.variation('new-dashboard', user, false);

if (flags) {
  // Show new dashboard
} else {
  // Show old dashboard
}
```

**Deploy code without enabling feature -> safer releases!**

---

**5. Disaster Recovery**

**Velero** = Backup/Restore Kubernetes

```bash
# Install Velero
velero install --provider aws --bucket k8s-backups

# Backup entire cluster
velero backup create full-backup

# Restore
velero restore create --from-backup full-backup
```

---

**6. Chaos Engineering avec Chaos Mesh**

```yaml
# Kill random pods to test resilience
apiVersion: chaos-mesh.org/v1alpha1
kind: PodChaos
metadata:
  name: pod-kill-test
spec:
  action: pod-kill
  mode: one
  selector:
    namespaces:
    - taskmaster-prod
  scheduler:
    cron: '@every 1h'
```

**Test your system's resilience!**

---

## [COURS] CONCLUSION DE L'EXERCICE 10

**[OK] FÉLICITATIONS ! TU AS TERMINÉ LES 10 EXERCICES ! [BRAVO][BRAVO][BRAVO]**

**Ce que tu as appris dans cet exercice :**
- GitOps principles et workflow
- Infrastructure as Code avec Kubernetes
- ArgoCD pour GitOps automation
- Sealed Secrets pour gestion sécurisée
- Multi-environment deployments
- Progressive delivery (canary)
- Monitoring complet (Prometheus, Grafana, Loki, Jaeger)
- CI/CD pipeline end-to-end
- Incident response et rollbacks
- Production-ready deployments

**Compétences acquises dans LES 10 EXERCICES :**

### Exercice 1 : Git Fondamentaux *
- [OK] Installation et configuration Git
- [OK] Commits, staging, historique
- [OK] .gitignore
- [OK] git diff, git log

### Exercice 2 : Branches et Merges **
- [OK] Création et gestion de branches
- [OK] Fast-forward vs 3-way merge
- [OK] Git stash
- [OK] Visualisation de l'arbre

### Exercice 3 : Collaboration GitHub **
- [OK] Dépôts distants (remote)
- [OK] Push, pull, fetch, clone
- [OK] Pull Requests
- [OK] Protection de branches

### Exercice 4 : Gestion des Conflits **
- [OK] Identification de conflits
- [OK] Résolution manuelle
- [OK] Mergetools
- [OK] Stratégies de prévention

### Exercice 5 : Pull Requests & Code Review ***
- [OK] PRs professionnelles
- [OK] Code review constructif
- [OK] Templates de PR
- [OK] GitHub Actions (CI basique)

### Exercice 6 : Git Flow ***
- [OK] Modèle Git Flow complet
- [OK] Feature, release, hotfix branches
- [OK] Semantic versioning
- [OK] Release management
- [OK] Tags et GitHub Releases

### Exercice 7 : Opérations Avancées ****
- [OK] Rebase interactif
- [OK] Cherry-pick
- [OK] Reflog (récupération)
- [OK] Bisect (debugging)
- [OK] Reset vs Revert
- [OK] Blame

### Exercice 8 : Hooks et Automation ****
- [OK] Git Hooks (client-side)
- [OK] Husky
- [OK] Lint-staged
- [OK] Commitlint
- [OK] Secret detection
- [OK] Tests automatiques

### Exercice 9 : Monorepo Management *****
- [OK] Architecture monorepo
- [OK] npm Workspaces
- [OK] Nx (caching, affected)
- [OK] Packages internes
- [OK] Build optimization
- [OK] Lerna versioning

### Exercice 10 : GitOps & Déploiement *****
- [OK] GitOps principles
- [OK] Kubernetes deployments
- [OK] ArgoCD
- [OK] Sealed Secrets
- [OK] Multi-environment
- [OK] Progressive delivery
- [OK] Monitoring & Observability
- [OK] Production-ready CI/CD

---

**Niveau atteint :** [TROPHEE] **GIT MASTER** [TROPHEE]

**Tu maîtrises maintenant :**
- Git de A à Z (basique -> expert)
- Workflows professionnels (Git Flow, GitOps)
- Collaboration d'équipe (PRs, code review)
- Automation (hooks, CI/CD)
- Monorepos (Nx, workspaces)
- Déploiements modernes (Kubernetes, ArgoCD)
- Production engineering (monitoring, rollbacks)

**Total temps estimé des 10 exercices :** 40-50 heures

**Tu es maintenant prêt pour :**
- [PRO] Senior Developer roles
- [RAPIDE] DevOps Engineer positions
- [CONSTRUCTION] Platform Engineering
- [PERSONNE][ECOLE] Mentoring junior developers
- [GUIDE] Contributing to open source
- [OBJECTIF] Leading technical teams

---

## [WRAPPED_PRESENT] BONUS : Ressources pour aller encore plus loin

**Livres :**
- "Pro Git" by Scott Chacon (gratuit en ligne)
- "Git for Teams" by Emma Jane Hogbin Westby
- "Accelerate" by Nicole Forsgren (DevOps/GitOps)

**Certifications :**
- GitHub Actions Certification
- Certified Kubernetes Administrator (CKA)
- Certified Kubernetes Application Developer (CKAD)
- GitOps at Scale (Linux Foundation)

**Communautés :**
- GitOps Working Group (CNCF)
- Kubernetes Slack
- r/devops (Reddit)
- DevOps subreddit

**Outils à explorer :**
- Terraform (Infrastructure as Code)
- Vault (Secrets Management)
- Backstage (Developer Portal)
- Cilium (Network Security)

---

**[BRAVO] BRAVO POUR AVOIR COMPLÉTÉ CES 10 EXERCICES ! [BRAVO]**

**N'oublie pas de :**
- * Star le repo GitHub
- [NOTE] Partager tes learnings
- [ACCORD] Aider d'autres développeurs
- [RAPIDE] Appliquer ces compétences dans tes projets

**Continue à apprendre, continue à builder ! [FORCE]**

---

*Fin du programme complet Git & GitHub - Niveau Expert*