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

## [LIVRE] INTRODUCTION

Ce document contient **5 exercices pratiques corrigés** sur Git et GitHub, allant du niveau débutant au niveau expert. Chaque exercice est conçu pour :

- **Maîtriser** Git et GitHub dans un contexte professionnel
- **Comprendre** les workflows collaboratifs en entreprise
- **Appliquer** les meilleures pratiques DevOps
- **Automatiser** avec CI/CD et GitHub Actions
- **Gérer** des projets complexes à grande échelle

**Niveau de progression :**
- [VERT] Exercice 1 : Débutant (Fondamentaux Git)
- [JAUNE] Exercice 2 : Intermédiaire (Collaboration et Workflows)
- [JAUNE] Exercice 3 : Intermédiaire (Git Avancé + CI/CD)
- [ROUGE] Exercice 4 : Avancé (Projets Complexes)
- [ROUGE] Exercice 5 : Expert (DevOps Entreprise)

**Chaque exercice contient :**
- [OK] Contexte professionnel réaliste
- [OK] Cahier des charges détaillé
- [OK] Solution complète étape par étape
- [OK] Explications ligne par ligne
- [OK] Schémas et diagrammes
- [OK] Tests de validation
- [OK] Erreurs courantes et solutions
- [OK] Points clés à retenir
- [OK] Pour aller plus loin

**Prérequis généraux :**
- Linux/macOS/Windows avec terminal
- Compte GitHub (gratuit)
- Éditeur de texte (VS Code recommandé)
- Curiosité et envie d'apprendre ! [RAPIDE]

---

# [VERT] EXERCICE 1 : MAÎTRISER LES FONDAMENTAUX DE GIT

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es développeur junior dans une startup tech. C'est ton premier jour et le lead développeur te demande de :
1. Configurer Git sur ta machine
2. Créer ton premier projet versionné
3. Apprendre les commandes essentielles
4. Publier ton code sur GitHub
5. Collaborer avec l'équipe

### Cahier des charges

**Mission : Créer un site portfolio personnel versionné avec Git**

Le portfolio doit contenir :
- Une page d'accueil (index.html)
- Une page "À propos" (about.html)
- Une feuille de style (style.css)
- Des images dans un dossier dédié
- Un fichier README.md documentant le projet

**Contraintes techniques :**
- Utiliser Git pour versionner chaque modification
- Créer un historique propre avec des commits clairs
- Publier le projet sur GitHub
- Rédiger un README professionnel
- Ignorer les fichiers temporaires
- Temps estimé : 2-3 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :

- [OK] Installer et configurer Git
- [OK] Initialiser un dépôt Git
- [OK] Comprendre la zone de staging
- [OK] Créer des commits atomiques et clairs
- [OK] Consulter l'historique (log, show, diff)
- [OK] Créer et gérer des branches
- [OK] Utiliser .gitignore efficacement
- [OK] Créer un repository GitHub
- [OK] Connecter local et remote
- [OK] Pousser et récupérer du code

---

## [DOCS] PRÉREQUIS

- Ordinateur avec Linux/macOS/Windows
- Connexion Internet
- Notions de base en HTML/CSS (optionnel)
- Aucune connaissance préalable de Git requise

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Comprendre Git - Concepts fondamentaux

**Avant de commencer, comprenons ce qu'est Git.**

#### Qu'est-ce que Git ?

**Git** = Système de contrôle de version distribué (DVCS - Distributed Version Control System)

**Analogie de la machine à remonter le temps :**

Imagine que tu écris un roman. Sans Git :
```
roman_v1.doc
roman_v2.doc
roman_v2_final.doc
roman_v2_final_VRAIMENT_FINAL.doc
roman_v2_final_VRAIMENT_FINAL_jerevis.doc
```

Avec Git :
```
roman.doc (+ historique complet de toutes les versions)
```

**Tu peux :**
- Revenir à n'importe quelle version
- Voir qui a modifié quoi et quand
- Travailler sur plusieurs versions en parallèle (branches)
- Fusionner les modifications de plusieurs personnes

---

#### Architecture Git : Les 3 zones

```
┌─────────────────────────────────────────────────────────┐
│                    WORKING DIRECTORY                     │
│              (Ton dossier de travail)                    │
│                                                           │
│  index.html  style.css  README.md                        │
│                                                           │
│            git add <fichier>                              │
│                    v                                      │
├─────────────────────────────────────────────────────────┤
│                   STAGING AREA (INDEX)                   │
│              (Zone de préparation)                       │
│                                                           │
│  index.html [OK]  style.css [OK]                              │
│                                                           │
│            git commit -m "message"                        │
│                    v                                      │
├─────────────────────────────────────────────────────────┤
│                  LOCAL REPOSITORY                        │
│              (Dépôt Git local - .git)                    │
│                                                           │
│  Commit A -> Commit B -> Commit C                          │
│                                                           │
│            git push                                       │
│                    v                                      │
├─────────────────────────────────────────────────────────┤
│                  REMOTE REPOSITORY                       │
│                   (GitHub, GitLab...)                    │
│                                                           │
│  Commit A -> Commit B -> Commit C                          │
└─────────────────────────────────────────────────────────┘
```

**Les 3 zones :**

1. **Working Directory** (Répertoire de travail)
   - Tes fichiers actuels
   - Ce que tu vois dans ton explorateur de fichiers

2. **Staging Area** (Zone de staging / Index)
   - Fichiers prêts à être commités
   - Zone de préparation avant l'enregistrement

3. **Repository** (Dépôt)
   - Historique complet des commits
   - Base de données Git (dossier `.git`)

---

#### Workflow Git de base

```
1. Modifier des fichiers
   v
2. git add (ajouter à la staging area)
   v
3. git commit (enregistrer dans le repository)
   v
4. git push (envoyer sur GitHub)
```

---

### ÉTAPE 2 : Installer Git

#### Sur Linux (Ubuntu/Debian)

```bash
# Mettre à jour les paquets
sudo apt update

# Installer Git
sudo apt install git -y

# Vérifier l'installation
git --version
```

**Résultat attendu :**
```
git version 2.34.1
```

---

#### Sur macOS

**Option 1 : Avec Homebrew (recommandé)**

```bash
# Installer Homebrew si pas déjà fait
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

# Installer Git
brew install git

# Vérifier
git --version
```

**Option 2 : Avec Xcode Command Line Tools**

```bash
xcode-select --install
git --version
```

---

#### Sur Windows

**Télécharger Git for Windows :**

1. Aller sur https://git-scm.com/download/win
2. Télécharger l'installateur
3. Exécuter et suivre l'assistant (laisser les options par défaut)
4. Ouvrir Git Bash (nouveau terminal installé)

```bash
git --version
```

**[OK] Git installé !**

---

### ÉTAPE 3 : Configuration initiale de Git

**Configurer ton identité (OBLIGATOIRE) :**

```bash
# Nom (sera visible dans l'historique)
git config --global user.name "Ton Nom"

# Email (DOIT correspondre à ton email GitHub)
git config --global user.email "ton.email@example.com"
```

**Explication de `--global` :**

**`--global`** = Configuration pour TOUS tes projets Git

Alternatives :
- `--local` = Configuration pour le projet actuel uniquement
- `--system` = Configuration pour tous les utilisateurs du système

**Ordre de priorité : local > global > system**

---

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

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

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

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

**`--wait`** = Git attend que tu fermes l'éditeur avant de continuer

---

**Autres configurations utiles :**

```bash
# Couleurs dans le terminal
git config --global color.ui auto

# Nom de branche par défaut (main au lieu de master)
git config --global init.defaultBranch main

# Gestion des fins de ligne (important Windows/Linux)
# Sur Linux/macOS :
git config --global core.autocrlf input

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

**Explication de `autocrlf` :**

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

**Solutions :**
- `input` (Linux/macOS) : Convertit CRLF -> LF lors du commit
- `true` (Windows) : Convertit LF -> CRLF au checkout, CRLF -> LF au commit
- `false` : Aucune conversion (déconseillé)

---

**Vérifier ta configuration :**

```bash
# Voir toute la configuration
git config --list

# Voir une configuration spécifique
git config user.name
git config user.email
```

---

**Configurer les alias (raccourcis) :**

```bash
# Alias pratiques
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.unstage 'reset HEAD --'
git config --global alias.last 'log -1 HEAD'
git config --global alias.visual 'log --oneline --graph --decorate --all'
```

**Maintenant tu peux faire :**
```bash
git st  # au lieu de git status
git visual  # affichage graphique de l'historique
```

---

### ÉTAPE 4 : Créer le projet portfolio

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

```bash
# Créer et entrer dans le dossier
mkdir portfolio
cd portfolio
```

---

**Initialiser le dépôt Git :**

```bash
git init
```

**Résultat :**
```
Initialized empty Git repository in /home/user/portfolio/.git/
```

**Explication :**

`git init` crée un dossier caché `.git` qui contient toute la base de données Git.

**Structure du dossier `.git` :**
```
.git/
├── HEAD              <- Pointeur vers la branche actuelle
├── config            <- Configuration du projet
├── description       <- Description du dépôt
├── hooks/            <- Scripts automatiques (pre-commit, etc.)
├── objects/          <- Base de données des commits
├── refs/             <- Références (branches, tags)
└── ...
```

**[ATTENTION] Ne JAMAIS modifier manuellement le contenu de `.git` !**

---

**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)
```

**Décomposons ce message :**

1. **`On branch main`** = Tu es sur la branche "main"
2. **`No commits yet`** = Aucun commit n'a été créé
3. **`nothing to commit`** = Aucun fichier à commiter

---

### ÉTAPE 5 : Créer les fichiers du projet

**Créer la page d'accueil :**

```bash
cat > index.html << 'EOF'
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Mon Portfolio</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <header>
        <h1>[PERSONNE][CODE] Bienvenue sur mon Portfolio</h1>
        <nav>
            <a href="index.html">Accueil</a>
            <a href="about.html">À propos</a>
        </nav>
    </header>
    
    <main>
        <section class="hero">
            <h2>Développeur Full-Stack</h2>
            <p>Passionné par la création d'applications web modernes et performantes.</p>
        </section>
        
        <section class="skills">
            <h3>Mes Compétences</h3>
            <ul>
                <li>HTML5 / CSS3</li>
                <li>JavaScript / TypeScript</li>
                <li>React / Vue.js</li>
                <li>Node.js / Python</li>
                <li>Git / GitHub</li>
            </ul>
        </section>
    </main>
    
    <footer>
        <p>&copy; 2024 Mon Portfolio - Tous droits réservés</p>
    </footer>
</body>
</html>
EOF
```

---

**Créer la page "À propos" :**

```bash
cat > about.html << 'EOF'
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>À propos - Mon Portfolio</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <header>
        <h1>[PERSONNE][CODE] Mon Portfolio</h1>
        <nav>
            <a href="index.html">Accueil</a>
            <a href="about.html">À propos</a>
        </nav>
    </header>
    
    <main>
        <section>
            <h2>À propos de moi</h2>
            <p>
                Je suis développeur web avec 2 ans d'expérience dans la création 
                d'applications modernes. Passionné par les nouvelles technologies 
                et l'apprentissage continu.
            </p>
            
            <h3>Mon Parcours</h3>
            <ul>
                <li>2022 : Diplôme en Informatique</li>
                <li>2023 : Premier emploi en tant que développeur</li>
                <li>2024 : Maîtrise de Git et GitHub !</li>
            </ul>
        </section>
    </main>
    
    <footer>
        <p>&copy; 2024 Mon Portfolio - Tous droits réservés</p>
    </footer>
</body>
</html>
EOF
```

---

**Créer la feuille de style :**

```bash
cat > style.css << 'EOF'
/* Reset et styles de base */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    line-height: 1.6;
    color: #333;
    background: #f4f4f4;
}

/* Header */
header {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
    padding: 2rem;
    text-align: center;
}

header h1 {
    margin-bottom: 1rem;
}

nav a {
    color: white;
    text-decoration: none;
    margin: 0 1rem;
    padding: 0.5rem 1rem;
    border-radius: 5px;
    transition: background 0.3s;
}

nav a:hover {
    background: rgba(255, 255, 255, 0.2);
}

/* Main */
main {
    max-width: 1000px;
    margin: 2rem auto;
    padding: 0 2rem;
}

section {
    background: white;
    padding: 2rem;
    margin-bottom: 2rem;
    border-radius: 10px;
    box-shadow: 0 5px 15px rgba(0,0,0,0.1);
}

/* Hero */
.hero {
    text-align: center;
    padding: 3rem 2rem;
}

.hero h2 {
    color: #667eea;
    margin-bottom: 1rem;
}

/* Skills */
.skills ul {
    list-style: none;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 1rem;
    margin-top: 1rem;
}

.skills li {
    background: #667eea;
    color: white;
    padding: 1rem;
    text-align: center;
    border-radius: 5px;
}

/* Footer */
footer {
    background: #333;
    color: white;
    text-align: center;
    padding: 2rem;
    margin-top: 3rem;
}
EOF
```

---

**Créer un dossier pour les images :**

```bash
mkdir images
```

---

**Vérifier le statut maintenant :**

```bash
git status
```

**Résultat :**
```
On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        about.html
        images/
        index.html
        style.css

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

**Analyse du message :**

**`Untracked files`** = Fichiers non suivis par Git
- Git les voit mais ne les surveille pas encore
- Il faut faire `git add` pour les "tracker"

**`nothing added to commit`** = La staging area est vide

---

### ÉTAPE 6 : Premier commit - Comprendre add et commit

#### Ajouter des fichiers à la staging area

**Ajouter un fichier spécifique :**

```bash
git add index.html
```

**Vérifier :**

```bash
git status
```

**Résultat :**
```
On branch main

No commits yet

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

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        about.html
        images/
        style.css
```

**Analyse :**

- **`Changes to be committed`** = Fichiers dans la staging area (prêts)
- **`new file: index.html`** = index.html est prêt à être commité
- Les autres fichiers sont toujours "Untracked"

---

**Schéma de la situation actuelle :**

```
Working Directory     Staging Area          Repository
─────────────────     ────────────          ──────────
index.html       ->    index.html [OK]          (vide)
about.html
style.css
images/
```

---

**Ajouter plusieurs fichiers :**

```bash
git add about.html style.css
```

---

**Ajouter tous les fichiers d'un coup :**

```bash
git add .
```

**`.`** = Répertoire actuel (tous les fichiers/dossiers)

**Alternatives :**
```bash
git add -A         # Tout ajouter (même fichiers supprimés)
git add --all      # Identique à -A
git add *          # Tous les fichiers (shell expansion)
```

---

**Vérifier :**

```bash
git status
```

**Résultat :**
```
On branch main

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   about.html
        new file:   images/
        new file:   index.html
        new file:   style.css
```

**[OK] Tous les fichiers sont dans la staging area !**

---

#### Créer le premier commit

**Commit avec message inline :**

```bash
git commit -m "Initial commit: Ajout du portfolio de base"
```

**Explication de la commande :**

**`git commit`** = Enregistre le snapshot (instantané) de la staging area

**`-m "message"`** = Message du commit (inline)

Sans `-m`, Git ouvre un éditeur pour écrire le message.

---

**Résultat :**
```
[main (root-commit) a1b2c3d] Initial commit: Ajout du portfolio de base
 4 files changed, 150 insertions(+)
 create mode 100644 about.html
 create mode 100644 images/
 create mode 100644 index.html
 create mode 100644 style.css
```

**Décomposons ce message :**

**`[main (root-commit) a1b2c3d]`**
- `main` = Branche actuelle
- `root-commit` = Premier commit du dépôt
- `a1b2c3d` = Hash du commit (identifiant unique)

**`4 files changed, 150 insertions(+)`**
- 4 fichiers modifiés
- 150 lignes ajoutées

**`create mode 100644`**
- Fichiers créés avec permissions 644 (lecture/écriture)

---

**Vérifier le statut :**

```bash
git status
```

**Résultat :**
```
On branch main
nothing to commit, working tree clean
```

**`working tree clean`** = Aucune modification depuis le dernier commit

---

**Schéma après le commit :**

```
Working Directory     Staging Area          Repository
─────────────────     ────────────          ──────────
index.html            (vide)                Commit A
about.html                                  ├─ index.html
style.css                                   ├─ about.html
images/                                     ├─ style.css
                                            └─ images/
```

---

### ÉTAPE 7 : Consulter l'historique

**Voir l'historique des commits :**

```bash
git log
```

**Résultat :**
```
commit a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0 (HEAD -> main)
Author: Ton Nom <ton.email@example.com>
Date:   Mon Dec 16 10:00:00 2024 +0000

    Initial commit: Ajout du portfolio de base
```

**Décomposons :**

**`commit a1b2c3d...`** = Hash SHA-1 du commit (40 caractères)
- Identifiant unique
- Calculé à partir du contenu + métadonnées
- Impossible d'avoir deux commits avec le même hash

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

**`Author`** = Qui a créé le commit (user.name et user.email)

**`Date`** = Quand le commit a été créé

**Message** = Description du commit

---

**Formats d'affichage alternatifs :**

```bash
# Format court (une ligne par commit)
git log --oneline

# Résultat :
# a1b2c3d (HEAD -> main) Initial commit: Ajout du portfolio de base
```

```bash
# Format graphique
git log --graph --oneline --decorate --all

# Résultat (graphe ASCII) :
# * a1b2c3d (HEAD -> main) Initial commit: Ajout du portfolio de base
```

```bash
# Limiter à N commits
git log -3  # Les 3 derniers commits
git log -1  # Le dernier commit
```

```bash
# Afficher les fichiers modifiés
git log --stat

# Résultat :
# commit a1b2c3d...
# ...
#  about.html  | 32 ++++++++++++++++++++
#  index.html  | 45 +++++++++++++++++++++++++++++
#  style.css   | 73 ++++++++++++++++++++++++++++++++++++++++++++++
#  3 files changed, 150 insertions(+)
```

```bash
# Afficher les modifications ligne par ligne
git log -p

# Résultat : Diff complet de chaque commit
```

---

**Voir un commit spécifique :**

```bash
git show a1b2c3d
```

**Ou avec HEAD :**

```bash
git show HEAD      # Commit actuel
git show HEAD~1    # 1 commit avant
git show HEAD~2    # 2 commits avant
git show HEAD^     # Parent du commit actuel
```

---

### ÉTAPE 8 : Faire des modifications et créer d'autres commits

**Modifier index.html (ajouter une section projets) :**

```bash
# Ouvrir dans un éditeur
nano index.html

# Ou ajouter directement avec sed
sed -i '/<\/section>/a\        <section class="projects">\n            <h3>Mes Projets</h3>\n            <p>Bientôt disponible !</p>\n        </section>' index.html
```

---

**Vérifier les modifications :**

```bash
git status
```

**Résultat :**
```
On branch main
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   index.html

no changes added to commit (use "git add" and/or "git commit -a")
```

**Nouveau terme : `Changes not staged for commit`**
- Fichiers modifiés MAIS pas encore dans la staging area
- Différent de "Untracked" (nouveaux fichiers)

---

**Voir les différences :**

```bash
git diff
```

**Résultat :**
```diff
diff --git a/index.html b/index.html
index a1b2c3d..e5f6g7h 100644
--- a/index.html
+++ b/index.html
@@ -25,6 +25,11 @@
             </ul>
         </section>
         
+        <section class="projects">
+            <h3>Mes Projets</h3>
+            <p>Bientôt disponible !</p>
+        </section>
+        
     </main>
     
     <footer>
```

**Explication du diff :**

**`---`** = Ancienne version
**`+++`** = Nouvelle version
**`@@`** = Numéro de ligne
**`-`** = Ligne supprimée (rouge)
**`+`** = Ligne ajoutée (vert)

---

**Ajouter et commiter :**

```bash
git add index.html
git commit -m "feat: Ajout section Projets sur la page d'accueil"
```

**Note sur le format du message :**

`feat:` = Convention "Conventional Commits"
- `feat:` = Nouvelle fonctionnalité
- `fix:` = Correction de bug
- `docs:` = Documentation
- `style:` = Formatage (pas de changement de code)
- `refactor:` = Refactoring (pas de nouvelle feature/fix)
- `test:` = Ajout de tests
- `chore:` = Maintenance

**On verra ça en détail dans l'exercice 5.**

---

**Créer un fichier README.md :**

```bash
cat > README.md << 'EOF'
# [DOSSIER] Mon Portfolio Personnel

Portfolio professionnel développé avec HTML5, CSS3 et versionné avec Git.

## [RAPIDE] Fonctionnalités

- Page d'accueil avec présentation
- Page "À propos" détaillée
- Design responsive et moderne
- Section compétences
- Section projets

## [OUTILS] Technologies utilisées

- HTML5
- CSS3
- Git & GitHub

## [PACKAGE] Installation

1. Cloner le repository :
```bash
git clone https://github.com/TON-USERNAME/portfolio.git
```

2. Ouvrir `index.html` dans un navigateur

## [NOTE] Licence

Ce projet est sous licence MIT.

## [UTILISATEUR] Auteur

**Ton Nom**
- GitHub: [@TON-USERNAME](https://github.com/TON-USERNAME)
- Email: ton.email@example.com
EOF
```

---

**Ajouter et commiter le README :**

```bash
git add README.md
git commit -m "docs: Ajout du README avec documentation du projet"
```

---

**Voir l'historique maintenant :**

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

**Résultat :**
```
c7d8e9f (HEAD -> main) docs: Ajout du README avec documentation du projet
b4c5d6e feat: Ajout section Projets sur la page d'accueil
a1b2c3d Initial commit: Ajout du portfolio de base
```

**[OK] Trois commits dans l'historique !**

---

### ÉTAPE 9 : Créer et utiliser .gitignore

**Problème : Fichiers à ne PAS versionner**

Certains fichiers ne doivent jamais être dans Git :
- Fichiers temporaires (`.DS_Store`, `Thumbs.db`)
- Fichiers de configuration locaux (`.vscode/`, `.idea/`)
- Dépendances (`node_modules/`, `venv/`)
- Fichiers sensibles (`.env`, mots de passe)
- Fichiers générés (`dist/`, `build/`, `*.log`)

---

**Créer des fichiers temporaires (simulation) :**

```bash
touch .DS_Store
touch debug.log
mkdir node_modules
touch node_modules/package.json
```

---

**Vérifier le statut :**

```bash
git status
```

**Résultat :**
```
On branch main
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        .DS_Store
        debug.log
        node_modules/

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

**[X] Git voit ces fichiers temporaires !**

---

**Créer .gitignore :**

```bash
cat > .gitignore << 'EOF'
# ═══════════════════════════════════════════════════════════════
# .gitignore - Fichiers à ignorer par Git
# ═══════════════════════════════════════════════════════════════

# Fichiers système
.DS_Store
Thumbs.db
desktop.ini

# Éditeurs et IDEs
.vscode/
.idea/
*.swp
*.swo
*~

# Logs
*.log
logs/
npm-debug.log*

# Dépendances
node_modules/
bower_components/
vendor/

# Fichiers de build
dist/
build/
*.min.js
*.min.css

# Fichiers de configuration locaux
.env
.env.local
config.local.js

# Fichiers temporaires
*.tmp
*.temp
*.cache

# OS
*.pid
*.seed
*.pid.lock
EOF
```

---

**Vérifier le statut :**

```bash
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] Les fichiers temporaires ont disparu !**

---

**Explication des patterns .gitignore :**

**`file.txt`** = Ignore `file.txt` partout
**`folder/`** = Ignore le dossier `folder`
**`*.log`** = Ignore tous les fichiers `.log`
**`!important.log`** = Exception : ne pas ignorer `important.log`
**`/root-file.txt`** = Ignore `root-file.txt` SEULEMENT à la racine
**`**/file.txt`** = Ignore `file.txt` dans tous les sous-dossiers
**`folder/**/*.log`** = Tous les `.log` dans `folder` et sous-dossiers

---

**Commiter .gitignore :**

```bash
git add .gitignore
git commit -m "chore: Ajout .gitignore pour fichiers temporaires"
```

---

### ÉTAPE 10 : Travailler avec les branches

**Concept des branches :**

```
        main
         │
         A <- Initial commit
         │
         B <- Ajout section Projets
         │
         C <- Ajout README
         │
         D <- Ajout .gitignore
```

**Créer une branche = Créer une ligne de développement parallèle**

```
        main                 feature/contact
         │                        │
         A <-──────────────────────┤
         │                        │
         B                        E <- Ajout formulaire
         │                        │
         C                        F <- Styling formulaire
         │
         D
```

**Quand utiliser des branches ?**

- Nouvelle fonctionnalité -> `feature/nom-feature`
- Correction de bug -> `bugfix/nom-bug` ou `hotfix/nom-bug`
- Expérimentation -> `experiment/nom-test`
- Release -> `release/v1.0.0`

---

**Voir les branches existantes :**

```bash
git branch
```

**Résultat :**
```
* main
```

**`*`** = Branche actuelle

---

**Créer une nouvelle branche :**

```bash
git branch feature/contact-form
```

**Vérifier :**

```bash
git branch
```

**Résultat :**
```
  feature/contact-form
* main
```

**Branche créée mais on est toujours sur `main`**

---

**Basculer vers la branche :**

```bash
git checkout feature/contact-form
```

**Ou en une commande (créer + basculer) :**

```bash
git checkout -b feature/contact-form
```

**`-b`** = branch (créer une nouvelle branche)

---

**Nouvelle syntaxe (Git 2.23+) :**

```bash
git switch feature/contact-form      # Basculer
git switch -c feature/contact-form   # Créer + basculer
```

**`switch` est plus clair que `checkout`** (qui fait plusieurs choses)

---

**Vérifier la branche actuelle :**

```bash
git branch
```

**Résultat :**
```
* feature/contact-form
  main
```

**[OK] On est maintenant sur `feature/contact-form` !**

---

**Créer une page de contact :**

```bash
cat > contact.html << 'EOF'
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Contact - Mon Portfolio</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <header>
        <h1>[PERSONNE][CODE] Mon Portfolio</h1>
        <nav>
            <a href="index.html">Accueil</a>
            <a href="about.html">À propos</a>
            <a href="contact.html">Contact</a>
        </nav>
    </header>
    
    <main>
        <section>
            <h2>Me Contacter</h2>
            <form>
                <div class="form-group">
                    <label for="name">Nom :</label>
                    <input type="text" id="name" name="name" required>
                </div>
                
                <div class="form-group">
                    <label for="email">Email :</label>
                    <input type="email" id="email" name="email" required>
                </div>
                
                <div class="form-group">
                    <label for="message">Message :</label>
                    <textarea id="message" name="message" rows="5" required></textarea>
                </div>
                
                <button type="submit">Envoyer</button>
            </form>
        </section>
    </main>
    
    <footer>
        <p>&copy; 2024 Mon Portfolio - Tous droits réservés</p>
    </footer>
</body>
</html>
EOF
```

---

**Ajouter le styling du formulaire dans style.css :**

```bash
cat >> style.css << 'EOF'

/* Formulaire de contact */
.form-group {
    margin-bottom: 1.5rem;
}

.form-group label {
    display: block;
    margin-bottom: 0.5rem;
    font-weight: bold;
    color: #667eea;
}

.form-group input,
.form-group textarea {
    width: 100%;
    padding: 0.8rem;
    border: 2px solid #ddd;
    border-radius: 5px;
    font-size: 1rem;
    font-family: inherit;
}

.form-group input:focus,
.form-group textarea:focus {
    outline: none;
    border-color: #667eea;
}

button[type="submit"] {
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
    padding: 1rem 2rem;
    border: none;
    border-radius: 5px;
    font-size: 1.1rem;
    cursor: pointer;
    transition: transform 0.3s;
}

button[type="submit"]:hover {
    transform: translateY(-2px);
}
EOF
```

---

**Ajouter le lien Contact dans les autres pages :**

```bash
# Dans index.html
sed -i 's|<a href="about.html">À propos</a>|<a href="about.html">À propos</a>\n            <a href="contact.html">Contact</a>|' index.html

# Dans about.html
sed -i 's|<a href="about.html">À propos</a>|<a href="about.html">À propos</a>\n            <a href="contact.html">Contact</a>|' about.html
```

---

**Commiter les changements :**

```bash
git add .
git commit -m "feat: Ajout page contact avec formulaire"
```

---

**Voir l'historique :**

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

**Résultat :**
```
* e8f9g0h (HEAD -> feature/contact-form) feat: Ajout page contact avec formulaire
| * c7d8e9f (main) docs: Ajout du README avec documentation du projet
| * b4c5d6e feat: Ajout section Projets sur la page d'accueil
|/
* a1b2c3d Initial commit: Ajout du portfolio de base
```

**Lecture du graphe :**

```
e8f9g0h <- feature/contact-form (HEAD)
   v
c7d8e9f <- main
   v
b4c5d6e
   v
a1b2c3d
```

---

**Revenir sur la branche main :**

```bash
git checkout main
```

**Vérifier :**

```bash
ls
```

**Résultat :**
```
README.md  about.html  images  index.html  style.css
```

**[X] `contact.html` n'existe pas sur `main` !**

---

**Revenir sur feature/contact-form :**

```bash
git checkout feature/contact-form
ls
```

**Résultat :**
```
README.md  about.html  contact.html  images  index.html  style.css
```

**[OK] `contact.html` existe ici !**

---

### ÉTAPE 11 : Fusionner les branches (Merge)

**Fusionner feature/contact-form dans main :**

**1. Basculer sur la branche de destination (main) :**

```bash
git checkout main
```

---

**2. Fusionner :**

```bash
git merge feature/contact-form
```

**Résultat :**
```
Updating c7d8e9f..e8f9g0h
Fast-forward
 contact.html | 45 +++++++++++++++++++++++++++++++++++++++++++++
 index.html   |  1 +
 about.html   |  1 +
 style.css    | 35 +++++++++++++++++++++++++++++++++++
 4 files changed, 82 insertions(+)
 create mode 100644 contact.html
```

**Explication de "Fast-forward" :**

```
AVANT merge :

main:                c7d8e9f
                       v
feature/contact:     e8f9g0h


APRÈS merge (fast-forward) :

main:                e8f9g0h <- Avancé jusqu'ici
                       v
feature/contact:     e8f9g0h
```

**Fast-forward** = Avance simplement le pointeur (pas de nouveau commit)

**Possible seulement si main n'a pas évolué depuis la création de la branche.**

---

**Vérifier :**

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

**Résultat :**
```
e8f9g0h (HEAD -> main, feature/contact-form) feat: Ajout page contact avec formulaire
c7d8e9f chore: Ajout .gitignore pour fichiers temporaires
b4c5d6e docs: Ajout du README avec documentation du projet
a1b2c3d feat: Ajout section Projets sur la page d'accueil
```

**[OK] `contact.html` existe maintenant sur `main` !**

---

**Supprimer la branche fusionnée :**

```bash
git branch -d feature/contact-form
```

**`-d`** = delete (suppression sécurisée, refuse si pas fusionnée)
**`-D`** = force delete (suppression forcée)

---

### ÉTAPE 12 : Publier sur GitHub

#### Créer un compte GitHub

1. Aller sur https://github.com
2. Cliquer sur "Sign up"
3. Créer un compte avec le MÊME email que dans `git config user.email`

---

#### Créer un nouveau repository

1. Cliquer sur le `+` en haut à droite
2. Sélectionner "New repository"
3. Remplir :
   - **Repository name** : `portfolio`
   - **Description** : "Mon portfolio personnel"
   - **Visibilité** : Public (ou Private)
   - **[X] NE PAS cocher "Initialize with README"** (on en a déjà un)
4. Cliquer sur "Create repository"

---

**GitHub affiche les instructions :**

```bash
# Ajouter le remote
git remote add origin https://github.com/TON-USERNAME/portfolio.git

# Renommer la branche en main (si nécessaire)
git branch -M main

# Pousser le code
git push -u origin main
```

---

#### Comprendre les remotes

**Remote** = Dépôt distant (sur GitHub, GitLab, etc.)

**`origin`** = Nom conventionnel du remote principal

```bash
# Voir les remotes configurés
git remote -v
```

**Résultat (après avoir ajouté origin) :**
```
origin  https://github.com/TON-USERNAME/portfolio.git (fetch)
origin  https://github.com/TON-USERNAME/portfolio.git (push)
```

**`fetch`** = URL pour récupérer du code
**`push`** = URL pour envoyer du code

---

**Ajouter le remote :**

```bash
git remote add origin https://github.com/TON-USERNAME/portfolio.git
```

**[ATTENTION] Remplacer `TON-USERNAME` par ton vrai nom d'utilisateur !**

---

**Pousser le code :**

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

**Explication de la commande :**

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

**`-u`** = `--set-upstream` (définir la branche upstream)
- Lie la branche locale `main` à la branche distante `origin/main`
- Après ça, tu peux juste faire `git push` (sans préciser)

**`origin`** = Nom du remote
**`main`** = Nom de la branche à pousser

---

**Résultat :**
```
Enumerating objects: 15, done.
Counting objects: 100% (15/15), done.
Delta compression using up to 8 threads
Compressing objects: 100% (12/12), done.
Writing objects: 100% (15/15), 3.45 KiB | 3.45 MiB/s, done.
Total 15 (delta 2), reused 0 (delta 0)
To https://github.com/TON-USERNAME/portfolio.git
 * [new branch]      main -> main
Branch 'main' set up to track remote branch 'main' from 'origin'.
```

**[OK] Code envoyé sur GitHub !**

---

**Vérifier sur GitHub :**

1. Actualiser la page du repository
2. Tous les fichiers sont là !
3. Le README.md s'affiche automatiquement en bas

---

#### Authentification GitHub

**Depuis août 2021, GitHub n'accepte plus les mots de passe pour Git.**

**Options d'authentification :**

**1. Personal Access Token (PAT)** - Recommandé

```bash
# GitHub te demandera un mot de passe
# Entre ton PAT au lieu du mot de passe
```

**Créer un PAT :**
1. GitHub -> Settings -> Developer settings
2. Personal access tokens -> Tokens (classic)
3. Generate new token
4. Cocher : `repo`, `workflow`
5. Copier le token (tu ne le reverras plus !)

---

**2. SSH Keys** - Plus pratique

```bash
# Générer une clé SSH
ssh-keygen -t ed25519 -C "ton.email@example.com"

# Copier la clé publique
cat ~/.ssh/id_ed25519.pub
```

**Ajouter la clé sur GitHub :**
1. GitHub -> Settings -> SSH and GPG keys
2. New SSH key
3. Coller la clé publique

**Changer l'URL du remote en SSH :**

```bash
git remote set-url origin git@github.com:TON-USERNAME/portfolio.git
```

---

### ÉTAPE 13 : Récupérer du code depuis GitHub (Pull)

**Simuler une modification sur GitHub :**

1. Sur GitHub, cliquer sur `README.md`
2. Cliquer sur l'icône crayon (Edit)
3. Ajouter une ligne : `## * Démo en ligne`
4. Commit directement sur GitHub

---

**En local, ton README n'a pas changé.**

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

```bash
git pull
```

**Résultat :**
```
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), done.
From https://github.com/TON-USERNAME/portfolio
   e8f9g0h..f0g1h2i  main       -> origin/main
Updating e8f9g0h..f0g1h2i
Fast-forward
 README.md | 2 ++
 1 file changed, 2 insertions(+)
```

---

**Explication de `git pull` :**

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

```
git fetch    -> Télécharge les commits depuis GitHub
               (met à jour origin/main)
               
git merge    -> Fusionne origin/main dans main local
```

---

**Vérifier :**

```bash
cat README.md
```

**La nouvelle ligne apparaît ! [OK]**

---

### [OK] TESTS DE VALIDATION

**1. Configuration Git**

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

- [ ] Nom et email configurés
- [ ] Email correspond à celui de GitHub

---

**2. Repository local**

```bash
ls -la
git status
git log --oneline
```

- [ ] Dossier `.git` existe
- [ ] Working tree clean
- [ ] Au moins 4 commits dans l'historique

---

**3. Fichiers versionnés**

```bash
ls
```

- [ ] index.html
- [ ] about.html
- [ ] contact.html
- [ ] style.css
- [ ] README.md
- [ ] .gitignore

---

**4. .gitignore fonctionne**

```bash
touch test.log
git status
```

- [ ] `test.log` n'apparaît pas dans `git status`

---

**5. Branches**

```bash
git branch -a
```

- [ ] Branche `main` existe
- [ ] Remote `origin/main` existe

---

**6. Remote configuré**

```bash
git remote -v
```

- [ ] Remote `origin` pointe vers GitHub

---

**7. GitHub à jour**

- [ ] Tous les fichiers sur GitHub
- [ ] README.md affiché
- [ ] Nombre de commits identique local/remote

---

### [ROUGE] ERREURS COURANTES ET SOLUTIONS

#### Erreur 1 : "Author identity unknown"

**Symptôme :**
```
*** Please tell me who you are.

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

**Solution :**

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

---

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

**Symptôme :**
```
nothing added to commit but untracked files present
```

**Cause :** Tu as oublié `git add`

**Solution :**

```bash
git add .
git commit -m "message"
```

---

#### Erreur 3 : "failed to push some refs"

**Symptôme :**
```
! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'https://github.com/...'
```

**Cause :** GitHub a des commits que tu n'as pas en local

**Solution :**

```bash
git pull
git push
```

---

#### Erreur 4 : "Support for password authentication was removed"

**Symptôme :**
```
remote: Support for password authentication was removed on August 13, 2021.
```

**Cause :** Tentative d'authentification avec mot de passe

**Solution :** Utiliser un Personal Access Token ou SSH (voir ÉTAPE 12)

---

#### Erreur 5 : Commit sur la mauvaise branche

**Symptôme :**

Tu as commité sur `main` au lieu de `feature/nouvelle-feature`

**Solution :**

```bash
# Créer une nouvelle branche à partir du commit actuel
git branch feature/nouvelle-feature

# Revenir main au commit précédent
git reset --hard HEAD~1

# Basculer sur la bonne branche
git checkout feature/nouvelle-feature
```

---

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

**1. Les 3 zones de Git**
- Working Directory (fichiers actuels)
- Staging Area (préparation)
- Repository (historique)

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

**3. Branches**
- Créer : `git branch nom` ou `git checkout -b nom`
- Basculer : `git checkout nom` ou `git switch nom`
- Fusionner : `git merge nom`
- Supprimer : `git branch -d nom`

**4. Synchronisation avec GitHub**
- Envoyer : `git push`
- Récupérer : `git pull` (`fetch` + `merge`)

**5. Bonnes pratiques**
- Commits atomiques (1 fonctionnalité = 1 commit)
- Messages clairs et descriptifs
- .gitignore pour fichiers temporaires
- Branches pour nouvelles features
- Pull avant Push

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Alias Git utiles**

```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.undo 'reset --soft HEAD~1'
git config --global alias.tree 'log --graph --oneline --all'
```

---

**2. Modifier le dernier commit**

```bash
# Oublié un fichier dans le dernier commit
git add fichier-oublie.txt
git commit --amend --no-edit

# Modifier le message du dernier commit
git commit --amend -m "Nouveau message"
```

---

**3. Annuler des modifications**

```bash
# Fichier modifié mais pas staged
git restore fichier.txt

# Fichier staged mais pas commité
git restore --staged fichier.txt

# Revenir au dernier commit (ATTENTION : perd les modifs)
git reset --hard HEAD
```

---

**4. Voir qui a modifié quoi (Blame)**

```bash
git blame fichier.txt
```

**Affiche ligne par ligne qui a fait la dernière modification.**

---

**5. Rechercher dans l'historique**

```bash
# Chercher "fonction" dans l'historique
git log -S "fonction"

# Chercher dans les messages de commit
git log --grep="bug"
```

---

## [COURS] CONCLUSION DE L'EXERCICE 1

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

**Ce que tu as appris :**
- Installer et configurer Git
- Créer un repository local
- Comprendre les 3 zones (working/staging/repository)
- Faire des commits atomiques
- Consulter l'historique
- Créer et fusionner des branches
- Utiliser .gitignore
- Publier sur GitHub
- Synchroniser local et remote

**Compétences acquises :**
- [OK] Git de base (niveau débutant-intermédiaire)
- [OK] Workflow solo
- [OK] GitHub basics
- [OK] Versioning de projet

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

**Prochaine étape :** Exercice 2 - Collaboration et workflows d'équipe ! [UTILISATEURS]

---

Je continue avec les exercices 2, 3, 4 et 5 dans le prochain message pour respecter la limite de longueur. Veux-tu que je continue ?

# [JAUNE] EXERCICE 2 : COLLABORATION ET WORKFLOWS D'ÉQUIPE

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu rejoins une équipe de 5 développeurs qui travaille sur une application web. L'équipe utilise Git et GitHub pour collaborer. Tu dois :
1. Comprendre le workflow de l'équipe (Git Flow)
2. Collaborer via Pull Requests
3. Gérer les conflits de merge
4. Faire des code reviews
5. Travailler avec des forks
6. Utiliser les issues et projects GitHub

### Cahier des charges

**Mission : Contribuer à un projet collaboratif**

L'équipe développe une application de gestion de tâches. Tu dois :
- Forker le projet principal
- Créer une branche feature
- Développer une nouvelle fonctionnalité
- Créer une Pull Request
- Gérer les retours de code review
- Résoudre des conflits de merge
- Collaborer via GitHub Issues

**Workflow de l'équipe :**
```
main (production)
  v
develop (développement)
  v
feature/* (nouvelles fonctionnalités)
hotfix/* (corrections urgentes)
release/* (préparation de release)
```

**Contraintes :**
- Respecter Git Flow
- Pull Requests obligatoires (pas de push direct sur develop)
- Minimum 1 reviewer par PR
- Tests passants avant merge
- Messages de commit conventionnels
- Temps estimé : 3-4 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :

- [OK] Comprendre et appliquer Git Flow
- [OK] Forker un repository
- [OK] Créer des Pull Requests (PR)
- [OK] Faire des code reviews
- [OK] Résoudre des conflits de merge
- [OK] Utiliser les branches protégées
- [OK] Gérer les GitHub Issues
- [OK] Utiliser GitHub Projects
- [OK] Squash commits
- [OK] Rebase vs Merge

---

## [DOCS] PRÉREQUIS

- Exercice 1 terminé (fondamentaux Git)
- Compte GitHub actif
- Compréhension des branches
- Notions de travail en équipe

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Comprendre Git Flow

#### Qu'est-ce que Git Flow ?

**Git Flow** = Stratégie de branching pour projets avec releases planifiées

**Créé par Vincent Driessen en 2010**

---

#### Les 5 types de branches

```
┌─────────────────────────────────────────────────────────────┐
│                         MAIN                                 │
│  Production (code stable, versionné)                         │
│  Tags: v1.0.0, v1.1.0, v2.0.0                               │
└─────────────────────────────────────────────────────────────┘
                              ^
                              │ merge (via release)
                              │
┌─────────────────────────────────────────────────────────────┐
│                        DEVELOP                               │
│  Intégration des features (branche de développement)         │
│  Code prêt pour la prochaine release                         │
└─────────────────────────────────────────────────────────────┘
        ^                     ^                     ^
        │                     │                     │
  ┌─────────┐          ┌─────────┐          ┌─────────┐
  │ FEATURE │          │ FEATURE │          │ FEATURE │
  │ login   │          │ profile │          │ tasks   │
  └─────────┘          └─────────┘          └─────────┘
  
  
                    ┌──────────────────┐
                    │    HOTFIX        │
                    │  (bug critique)  │
                    └──────────────────┘
                      v             v
                    main        develop
                    
                    
              ┌───────────────────────┐
              │     RELEASE           │
              │  (préparation v1.1)   │
              └───────────────────────┘
                      v
                  main + develop
```

---

**1. main (ou master)**
- Code en production
- Versionné avec tags (v1.0.0, v1.1.0, etc.)
- JAMAIS de développement direct dessus

**2. develop**
- Branche d'intégration
- Code de la prochaine release
- Reçoit les features fusionnées

**3. feature/***
- Nouvelles fonctionnalités
- Créées depuis develop
- Fusionnées dans develop
- Exemples : `feature/user-authentication`, `feature/dark-mode`

**4. release/***
- Préparation d'une release
- Créées depuis develop
- Corrections de bugs mineurs seulement
- Fusionnées dans main ET develop
- Exemple : `release/1.1.0`

**5. hotfix/***
- Corrections urgentes en production
- Créées depuis main
- Fusionnées dans main ET develop
- Exemple : `hotfix/critical-security-bug`

---

#### Cycle de vie typique

```
1. Nouvelle feature
   develop -> feature/login -> develop

2. Préparer une release
   develop -> release/1.1.0 -> main + develop

3. Bug en production
   main -> hotfix/security -> main + develop
```

---

#### Workflow détaillé : Feature

```bash
# 1. Créer la branche feature depuis develop
git checkout develop
git pull origin develop
git checkout -b feature/user-authentication

# 2. Développer la feature (commits multiples)
git add .
git commit -m "feat: Add user model"
git commit -m "feat: Add authentication logic"
git commit -m "test: Add authentication tests"

# 3. Mettre à jour depuis develop (si d'autres features ont été mergées)
git checkout develop
git pull origin develop
git checkout feature/user-authentication
git merge develop

# 4. Pusher la feature
git push origin feature/user-authentication

# 5. Créer une Pull Request sur GitHub
# develop <- feature/user-authentication

# 6. Après approbation et merge, supprimer la branche
git checkout develop
git pull origin develop
git branch -d feature/user-authentication
git push origin --delete feature/user-authentication
```

---

### ÉTAPE 2 : Simuler un projet collaboratif

**Pour cet exercice, on va créer un projet d'équipe simulé.**

#### Créer le repository principal (Organization)

**Option 1 : Créer une organisation GitHub (simuler une entreprise)**

1. GitHub -> `+` -> New organization
2. Nom : `TaskMasterTeam` (ou autre)
3. Plan : Free
4. Créer

---

**Option 2 : Utiliser ton compte personnel**

On va simplement créer un repository "principal" qui simule le repo de l'équipe.

---

#### Créer le repository principal

**Sur GitHub :**

1. Nouveau repository
2. Nom : `task-manager`
3. Description : "Application collaborative de gestion de tâches"
4. Public
5. [OK] Initialize with README
6. [OK] Add .gitignore : Node
7. [OK] Choose a license : MIT
8. Create repository

---

#### Cloner le repository

```bash
# Cloner
git clone https://github.com/TON-USERNAME/task-manager.git
cd task-manager
```

---

#### Créer la structure Git Flow

```bash
# Créer la branche develop
git checkout -b develop
git push origin develop
```

---

**Sur GitHub, protéger les branches :**

1. Repository -> Settings -> Branches
2. Add branch protection rule
3. Branch name pattern : `main`
4. Cocher :
   - [OK] Require pull request reviews before merging
   - [OK] Require status checks to pass before merging
5. Save changes

**Répéter pour `develop`**

**Effet : Impossible de pusher directement sur main ou develop -> Obligation de passer par PR**

---

#### Créer la structure du projet

```bash
# Créer les dossiers
mkdir -p src/{models,controllers,views,utils}
mkdir tests

# Créer package.json
cat > package.json << 'EOF'
{
  "name": "task-manager",
  "version": "1.0.0",
  "description": "Application collaborative de gestion de tâches",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js",
    "test": "echo 'Tests not implemented yet' && exit 0"
  },
  "keywords": ["tasks", "productivity", "collaboration"],
  "author": "TaskMaster Team",
  "license": "MIT"
}
EOF
```

---

**Créer un fichier principal :**

```bash
cat > src/index.js << 'EOF'
/**
 * Task Manager Application
 * Main entry point
 */

console.log('[RAPIDE] Task Manager Application');
console.log('Version: 1.0.0');

// TODO: Implement main application logic
EOF
```

---

**Créer CONTRIBUTING.md (guide de contribution) :**

```bash
cat > CONTRIBUTING.md << 'EOF'
# [ACCORD] Guide de Contribution

Merci de contribuer à Task Manager !

## [LISTE] Workflow Git Flow

Nous utilisons Git Flow :

### Branches principales
- `main` : Code en production
- `develop` : Code en développement

### Branches de travail
- `feature/*` : Nouvelles fonctionnalités
- `hotfix/*` : Corrections urgentes
- `release/*` : Préparation de release

## [SYNC] Processus de contribution

### 1. Créer une branche feature

```bash
git checkout develop
git pull origin develop
git checkout -b feature/nom-de-la-feature
```

### 2. Développer

```bash
# Commits atomiques avec messages conventionnels
git commit -m "feat: Description de la fonctionnalité"
```

### 3. Pusher et créer une Pull Request

```bash
git push origin feature/nom-de-la-feature
```

Puis sur GitHub :
- Créer une Pull Request vers `develop`
- Assigner au moins 1 reviewer
- Lier à une issue si applicable

### 4. Code Review

- Répondre aux commentaires
- Faire les modifications demandées
- Pousser les corrections

### 5. Merge

Après approbation :
- Squash and merge (pour garder un historique propre)

## [NOTE] Conventions de commit

Format : `<type>(<scope>): <description>`

Types :
- `feat` : Nouvelle fonctionnalité
- `fix` : Correction de bug
- `docs` : Documentation
- `style` : Formatage (pas de changement de code)
- `refactor` : Refactoring
- `test` : Ajout de tests
- `chore` : Maintenance

Exemples :
```
feat(auth): Add user login functionality
fix(tasks): Fix task deletion bug
docs(readme): Update installation instructions
```

## [OK] Checklist avant PR

- [ ] Code formaté et sans linting errors
- [ ] Tests ajoutés/mis à jour
- [ ] Documentation à jour
- [ ] Commit messages respectent les conventions
- [ ] Branch à jour avec develop

## [BUG] Signaler un bug

Utiliser les GitHub Issues avec le template "Bug Report"

## [IDEE] Proposer une feature

Utiliser les GitHub Issues avec le template "Feature Request"
EOF
```

---

**Commiter la structure initiale :**

```bash
git add .
git commit -m "chore: Initialize project structure with Git Flow"
git push origin develop
```

---

### ÉTAPE 3 : Travailler avec des forks

**Scénario : Tu es un contributeur externe (ou membre de l'équipe qui travaille sur son fork)**

#### Forker le repository

**Sur GitHub :**

1. Aller sur le repository `task-manager`
2. Cliquer sur "Fork" (en haut à droite)
3. Créer le fork dans ton compte

**Résultat : Tu as maintenant une copie du repo dans ton compte**

```
Repository original:
github.com/ORGANISATION/task-manager

Ton fork:
github.com/TON-USERNAME/task-manager
```

---

#### Cloner ton fork

```bash
# Supprimer le clone précédent (si tu l'avais)
cd ..
rm -rf task-manager

# Cloner TON fork
git clone https://github.com/TON-USERNAME/task-manager.git
cd task-manager
```

---

#### Ajouter le remote upstream

**upstream** = Repository original (source de vérité)

```bash
git remote add upstream https://github.com/ORGANISATION/task-manager.git
```

**Vérifier :**

```bash
git remote -v
```

**Résultat :**
```
origin    https://github.com/TON-USERNAME/task-manager.git (fetch)
origin    https://github.com/TON-USERNAME/task-manager.git (push)
upstream  https://github.com/ORGANISATION/task-manager.git (fetch)
upstream  https://github.com/ORGANISATION/task-manager.git (push)
```

**Explication :**

- **origin** = Ton fork (où tu pousses ton code)
- **upstream** = Repo original (d'où tu récupères les updates)

---

#### Synchroniser avec upstream

```bash
# Récupérer les changements de upstream
git fetch upstream

# Basculer sur develop
git checkout develop

# Fusionner les changements de upstream/develop
git merge upstream/develop

# Pousser vers ton fork
git push origin develop
```

**À faire régulièrement pour rester à jour !**

---

### ÉTAPE 4 : Développer une feature avec Pull Request

#### Créer une branche feature

```bash
# S'assurer d'être à jour
git checkout develop
git pull upstream develop

# Créer la branche feature
git checkout -b feature/add-task-model
```

---

#### Développer le modèle Task

**Créer le modèle :**

```bash
cat > src/models/Task.js << 'EOF'
/**
 * Task Model
 * Represents a task in the task manager
 */

class Task {
    constructor(id, title, description, status = 'todo', priority = 'medium') {
        this.id = id;
        this.title = title;
        this.description = description;
        this.status = status; // 'todo', 'in-progress', 'done'
        this.priority = priority; // 'low', 'medium', 'high'
        this.createdAt = new Date();
        this.updatedAt = new Date();
    }

    /**
     * Update task status
     * @param {string} newStatus - New status value
     */
    updateStatus(newStatus) {
        const validStatuses = ['todo', 'in-progress', 'done'];
        if (!validStatuses.includes(newStatus)) {
            throw new Error(`Invalid status: ${newStatus}`);
        }
        this.status = newStatus;
        this.updatedAt = new Date();
    }

    /**
     * Update task priority
     * @param {string} newPriority - New priority value
     */
    updatePriority(newPriority) {
        const validPriorities = ['low', 'medium', 'high'];
        if (!validPriorities.includes(newPriority)) {
            throw new Error(`Invalid priority: ${newPriority}`);
        }
        this.priority = newPriority;
        this.updatedAt = new Date();
    }

    /**
     * Convert task to JSON
     * @returns {Object} Task as JSON object
     */
    toJSON() {
        return {
            id: this.id,
            title: this.title,
            description: this.description,
            status: this.status,
            priority: this.priority,
            createdAt: this.createdAt.toISOString(),
            updatedAt: this.updatedAt.toISOString()
        };
    }
}

module.exports = Task;
EOF
```

---

**Créer des tests :**

```bash
cat > tests/Task.test.js << 'EOF'
/**
 * Task Model Tests
 */

const Task = require('../src/models/Task');

describe('Task Model', () => {
    test('should create a new task with default values', () => {
        const task = new Task(1, 'Test Task', 'Test Description');
        
        expect(task.id).toBe(1);
        expect(task.title).toBe('Test Task');
        expect(task.description).toBe('Test Description');
        expect(task.status).toBe('todo');
        expect(task.priority).toBe('medium');
        expect(task.createdAt).toBeInstanceOf(Date);
        expect(task.updatedAt).toBeInstanceOf(Date);
    });

    test('should update task status', () => {
        const task = new Task(1, 'Test', 'Description');
        task.updateStatus('in-progress');
        
        expect(task.status).toBe('in-progress');
    });

    test('should throw error for invalid status', () => {
        const task = new Task(1, 'Test', 'Description');
        
        expect(() => {
            task.updateStatus('invalid');
        }).toThrow('Invalid status: invalid');
    });

    test('should convert to JSON', () => {
        const task = new Task(1, 'Test', 'Description');
        const json = task.toJSON();
        
        expect(json).toHaveProperty('id');
        expect(json).toHaveProperty('title');
        expect(json).toHaveProperty('status');
        expect(json.createdAt).toBeDefined();
    });
});
EOF
```

---

**Commiter le modèle :**

```bash
git add src/models/Task.js
git commit -m "feat(models): Add Task model with status and priority management"
```

---

**Commiter les tests :**

```bash
git add tests/Task.test.js
git commit -m "test(models): Add comprehensive tests for Task model"
```

---

**Mettre à jour la documentation :**

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

## [DOCS] Models

### Task

Represents a task with the following properties:

- `id` (number): Unique identifier
- `title` (string): Task title
- `description` (string): Detailed description
- `status` (string): 'todo', 'in-progress', or 'done'
- `priority` (string): 'low', 'medium', or 'high'
- `createdAt` (Date): Creation timestamp
- `updatedAt` (Date): Last update timestamp

#### Methods

- `updateStatus(newStatus)`: Update task status
- `updatePriority(newPriority)`: Update task priority
- `toJSON()`: Convert task to JSON object
EOF
```

---

**Commiter la doc :**

```bash
git add README.md
git commit -m "docs(readme): Add Task model documentation"
```

---

**Pousser la branche feature :**

```bash
git push origin feature/add-task-model
```

---

#### Créer une Pull Request

**Sur GitHub (ton fork) :**

1. Message apparaît : "Compare & pull request"
2. Cliquer dessus

**Ou manuellement :**

1. Aller sur le repository ORIGINAL (upstream)
2. Pull requests -> New pull request
3. Compare across forks
4. **Base repository** : ORGANISATION/task-manager, **base** : develop
5. **Head repository** : TON-USERNAME/task-manager, **compare** : feature/add-task-model
6. Create pull request

---

**Remplir la Pull Request :**

**Title :** `feat(models): Add Task model with comprehensive tests`

**Description :**

```markdown
## [NOTE] Description

This PR adds the Task model to manage tasks in the application.

## * Changes

- [OK] Task model with id, title, description, status, and priority
- [OK] Methods to update status and priority with validation
- [OK] Comprehensive unit tests (4 test cases)
- [OK] Documentation in README.md

## [TEST] Testing

```bash
npm test
```

All tests passing [OK]

## [CAMERA_WITH_FLASH] Screenshots

N/A (backend model)

## [OK] Checklist

- [x] Code follows project conventions
- [x] Tests added and passing
- [x] Documentation updated
- [x] Branch up-to-date with develop
- [x] No merge conflicts

## [LIEN] Related Issues

Closes #1 (si une issue existe)
```

**Labels :** `enhancement`, `feature`

**Assignees :** Toi-même

**Reviewers :** @teammate1 (simuler un collègue)

**Create pull request**

---

### ÉTAPE 5 : Code Review et itérations

#### Simuler une code review

**En tant que reviewer (simule un collègue) :**

**Commentaires possibles :**

**1. Sur Task.js, ligne 25 :**

```
[SPEECH_BALLOON] Suggestion: Consider adding input validation for title and description

```javascript
constructor(id, title, description, status = 'todo', priority = 'medium') {
    if (!title || title.trim() === '') {
        throw new Error('Title is required');
    }
    // ...
}
```
```

---

**2. Sur Task.js, ligne 60 :**

```
[SPEECH_BALLOON] Question: Should we add a method to mark task as completed directly?

Something like:
```javascript
markAsCompleted() {
    this.updateStatus('done');
}
```
```

---

**3. Commentaire général :**

```
Overall looks good! Just a couple of suggestions:

[OK] Good test coverage
[OK] Clear documentation
[ATTENTION] Consider adding input validation
[ATTENTION] Maybe add a helper method for completion

Requesting changes for the validation.
```

---

#### Répondre aux commentaires

**En tant que contributeur :**

**Sur le commentaire 1 :**

```
Good catch! I'll add validation in the constructor.
```

---

**Sur le commentaire 2 :**

```
That's a great idea! I'll add both `markAsCompleted()` and `markAsInProgress()` helper methods.
```

---

#### Implémenter les modifications

```bash
# Toujours sur la branche feature
git checkout feature/add-task-model
```

---

**Modifier Task.js :**

```bash
cat > src/models/Task.js << 'EOF'
/**
 * Task Model
 * Represents a task in the task manager
 */

class Task {
    constructor(id, title, description, status = 'todo', priority = 'medium') {
        // Validation
        if (!id || typeof id !== 'number') {
            throw new Error('ID must be a valid number');
        }
        if (!title || title.trim() === '') {
            throw new Error('Title is required');
        }
        if (!description || description.trim() === '') {
            throw new Error('Description is required');
        }
        
        this.id = id;
        this.title = title.trim();
        this.description = description.trim();
        this.status = status;
        this.priority = priority;
        this.createdAt = new Date();
        this.updatedAt = new Date();
    }

    /**
     * Update task status
     * @param {string} newStatus - New status value
     */
    updateStatus(newStatus) {
        const validStatuses = ['todo', 'in-progress', 'done'];
        if (!validStatuses.includes(newStatus)) {
            throw new Error(`Invalid status: ${newStatus}`);
        }
        this.status = newStatus;
        this.updatedAt = new Date();
    }

    /**
     * Mark task as in progress
     */
    markAsInProgress() {
        this.updateStatus('in-progress');
    }

    /**
     * Mark task as completed
     */
    markAsCompleted() {
        this.updateStatus('done');
    }

    /**
     * Update task priority
     * @param {string} newPriority - New priority value
     */
    updatePriority(newPriority) {
        const validPriorities = ['low', 'medium', 'high'];
        if (!validPriorities.includes(newPriority)) {
            throw new Error(`Invalid priority: ${newPriority}`);
        }
        this.priority = newPriority;
        this.updatedAt = new Date();
    }

    /**
     * Convert task to JSON
     * @returns {Object} Task as JSON object
     */
    toJSON() {
        return {
            id: this.id,
            title: this.title,
            description: this.description,
            status: this.status,
            priority: this.priority,
            createdAt: this.createdAt.toISOString(),
            updatedAt: this.updatedAt.toISOString()
        };
    }
}

module.exports = Task;
EOF
```

---

**Ajouter des tests pour la validation :**

```bash
cat >> tests/Task.test.js << 'EOF'

describe('Task Validation', () => {
    test('should throw error for missing title', () => {
        expect(() => {
            new Task(1, '', 'Description');
        }).toThrow('Title is required');
    });

    test('should throw error for missing description', () => {
        expect(() => {
            new Task(1, 'Title', '');
        }).toThrow('Description is required');
    });

    test('should throw error for invalid ID', () => {
        expect(() => {
            new Task('invalid', 'Title', 'Description');
        }).toThrow('ID must be a valid number');
    });
});

describe('Task Helper Methods', () => {
    test('should mark task as in progress', () => {
        const task = new Task(1, 'Test', 'Description');
        task.markAsInProgress();
        expect(task.status).toBe('in-progress');
    });

    test('should mark task as completed', () => {
        const task = new Task(1, 'Test', 'Description');
        task.markAsCompleted();
        expect(task.status).toBe('done');
    });
});
EOF
```

---

**Commiter les modifications :**

```bash
git add .
git commit -m "fix(models): Add input validation and helper methods per code review"
```

---

**Pousser les modifications :**

```bash
git push origin feature/add-task-model
```

**-> La Pull Request se met à jour automatiquement !**

---

#### Re-review et approbation

**En tant que reviewer :**

```
[OK] Changes look great! 

- Input validation added
- Helper methods implemented
- Tests updated

Approving! [BRAVO]
```

**Approve the pull request**

---

### ÉTAPE 6 : Merger la Pull Request

**Sur GitHub, dans la PR :**

**Options de merge :**

1. **Create a merge commit**
   - Crée un commit de merge
   - Garde tout l'historique des commits de la feature
   - Graphe avec branches visibles

```
main: A ─────────── M (merge commit)
                   ╱
feature:    B ─ C ─ D
```

---

2. **Squash and merge** * (RECOMMANDÉ pour les features)
   - Combine tous les commits en un seul
   - Historique propre et linéaire
   - Plus facile à revert

```
Before squash:
feature: feat: Add model -> test: Add tests -> fix: Add validation -> docs: Update README

After squash:
develop: feat(models): Add Task model with comprehensive tests (#5)
```

---

3. **Rebase and merge**
   - Rejoue les commits un par un
   - Historique linéaire sans merge commit
   - Garde tous les commits individuels

```
main: A ─ B ─ C ─ D (tous les commits de feature)
```

---

**Choisir "Squash and merge" :**

1. Cliquer sur "Squash and merge"
2. Vérifier le message du commit squashé :

```
feat(models): Add Task model with comprehensive tests (#5)

* feat(models): Add Task model with status and priority management
* test(models): Add comprehensive tests for Task model
* docs(readme): Add Task model documentation
* fix(models): Add input validation and helper methods per code review
```

3. Confirm squash and merge

---

**Supprimer la branche feature :**

GitHub propose : "Delete branch"

Cliquer dessus [OK]

---

**En local, mettre à jour :**

```bash
# Basculer sur develop
git checkout develop

# Récupérer les changements (depuis upstream si c'est un fork)
git pull upstream develop

# Ou depuis origin si c'est ton propre repo
git pull origin develop

# Supprimer la branche feature locale
git branch -d feature/add-task-model
```

---

### ÉTAPE 7 : Gérer les conflits de merge

**Scénario : Deux développeurs modifient le même fichier**

#### Simuler un conflit

**Développeur A (toi) : Créer une branche feature/add-task-controller**

```bash
git checkout develop
git pull origin develop
git checkout -b feature/add-task-controller
```

---

**Créer un controller :**

```bash
cat > src/controllers/TaskController.js << 'EOF'
/**
 * Task Controller
 * Handles task-related operations
 */

const Task = require('../models/Task');

class TaskController {
    constructor() {
        this.tasks = [];
        this.nextId = 1;
    }

    /**
     * Create a new task
     */
    createTask(title, description, priority = 'medium') {
        const task = new Task(this.nextId++, title, description, 'todo', priority);
        this.tasks.push(task);
        return task;
    }

    /**
     * Get all tasks
     */
    getAllTasks() {
        return this.tasks;
    }
}

module.exports = TaskController;
EOF
```

---

**Commiter :**

```bash
git add src/controllers/TaskController.js
git commit -m "feat(controllers): Add TaskController with create and list methods"
```

---

**Modifier package.json (ajouter un script) :**

```bash
cat > package.json << 'EOF'
{
  "name": "task-manager",
  "version": "1.0.0",
  "description": "Application collaborative de gestion de tâches",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js",
    "dev": "nodemon src/index.js",
    "test": "jest"
  },
  "keywords": ["tasks", "productivity", "collaboration"],
  "author": "TaskMaster Team",
  "license": "MIT"
}
EOF
```

---

**Commiter :**

```bash
git add package.json
git commit -m "chore: Add dev and test scripts to package.json"
```

---

**Pousher :**

```bash
git push origin feature/add-task-controller
```

---

**Développeur B (simule un collègue) : Modifier package.json différemment**

**PENDANT CE TEMPS, sur develop, un collègue a aussi modifié package.json :**

```bash
# Revenir sur develop
git checkout develop

# Modifier package.json (différemment)
cat > package.json << 'EOF'
{
  "name": "task-manager",
  "version": "1.0.0",
  "description": "Application collaborative de gestion de tâches",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js",
    "build": "webpack --mode production",
    "test": "echo 'Tests not implemented yet' && exit 0"
  },
  "keywords": ["tasks", "productivity", "collaboration"],
  "author": "TaskMaster Team",
  "license": "MIT"
}
EOF
```

---

**Commiter directement sur develop (simule un merge d'une autre PR) :**

```bash
git add package.json
git commit -m "chore: Add build script to package.json"
git push origin develop
```

---

#### Créer le conflit

**Revenir sur ta branche feature :**

```bash
git checkout feature/add-task-controller
```

---

**Essayer de merger develop :**

```bash
git merge develop
```

---

**[IMPACT] CONFLIT ! [IMPACT]**

**Résultat :**

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

---

**Vérifier le statut :**

```bash
git status
```

**Résultat :**

```
On branch feature/add-task-controller
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:   package.json

no changes added to commit (use "git add" and/or "git commit -a")
```

---

#### Résoudre le conflit

**Ouvrir package.json :**

```bash
cat package.json
```

**Contenu avec marqueurs de conflit :**

```json
{
  "name": "task-manager",
  "version": "1.0.0",
  "description": "Application collaborative de gestion de tâches",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js",
<<<<<<< HEAD
    "dev": "nodemon src/index.js",
    "test": "jest"
=======
    "build": "webpack --mode production",
    "test": "echo 'Tests not implemented yet' && exit 0"
>>>>>>> develop
  },
  "keywords": ["tasks", "productivity", "collaboration"],
  "author": "TaskMaster Team",
  "license": "MIT"
}
```

---

**Explication des marqueurs :**

**`<<<<<<< HEAD`** = Début de TES modifications (branche actuelle)

**`=======`** = Séparateur

**`>>>>>>> develop`** = Fin des modifications de la branche mergée (develop)

---

**Stratégies de résolution :**

**1. Garder seulement HEAD (tes modifications) :**
```bash
git checkout --ours package.json
```

**2. Garder seulement develop (leurs modifications) :**
```bash
git checkout --theirs package.json
```

**3. Résoudre manuellement (RECOMMANDÉ) :**

Éditer le fichier et garder le meilleur des deux mondes.

---

**Résolution manuelle :**

```bash
cat > package.json << 'EOF'
{
  "name": "task-manager",
  "version": "1.0.0",
  "description": "Application collaborative de gestion de tâches",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js",
    "dev": "nodemon src/index.js",
    "build": "webpack --mode production",
    "test": "jest"
  },
  "keywords": ["tasks", "productivity", "collaboration"],
  "author": "TaskMaster Team",
  "license": "MIT"
}
EOF
```

**On a gardé les 3 scripts : dev, build, et test !**

---

**Marquer le conflit comme résolu :**

```bash
git add package.json
```

---

**Vérifier :**

```bash
git status
```

**Résultat :**

```
On branch feature/add-task-controller
All conflicts fixed but you are still merging.
  (use "git commit" to conclude merge)

Changes to be committed:
        modified:   package.json
```

---

**Finaliser le merge :**

```bash
git commit -m "merge: Resolve package.json conflict between feature and develop"
```

---

**Pousser :**

```bash
git push origin feature/add-task-controller
```

---

**Créer la Pull Request et merger comme précédemment.**

---

### ÉTAPE 8 : Utiliser GitHub Issues et Projects

#### Créer des Issues

**Sur GitHub -> Issues -> New issue**

---

**Issue 1 : Feature Request**

**Title :** `Add task deletion functionality`

**Description :**

```markdown
## [NOTE] Description

As a user, I want to be able to delete tasks so that I can remove completed or cancelled tasks from my list.

## [OK] Acceptance Criteria

- [ ] `deleteTask(id)` method in TaskController
- [ ] Proper error handling if task doesn't exist
- [ ] Unit tests for deletion
- [ ] Update documentation

## [IDEE] Additional Context

This should be part of the CRUD operations for tasks.

## [LABEL] Labels

enhancement, good first issue
```

**Labels :** `enhancement`, `good first issue`

**Assignees :** Toi-même ou un collègue

**Projects :** (on va créer un project)

**Create issue -> Issue #6 créée**

---

**Issue 2 : Bug Report**

**Title :** `Task status can be set to invalid value`

**Description :**

```markdown
## [BUG] Bug Description

Currently, if we bypass the `updateStatus()` method and set `task.status` directly, we can assign invalid values.

## [RECHERCHE] Steps to Reproduce

```javascript
const task = new Task(1, 'Test', 'Description');
task.status = 'invalid-status'; // Should throw error but doesn't
console.log(task.status); // Outputs: 'invalid-status'
```

## [OK] Expected Behavior

Task status should ALWAYS be validated, even with direct assignment.

## [IDEE] Suggested Fix

Make `status` private or use getters/setters with validation.

## [LABEL] Labels

bug, priority: high
```

**Labels :** `bug`, `priority: high`

**Create issue -> Issue #7 créée**

---

#### Créer un GitHub Project

**Sur GitHub -> Projects -> New project**

**Template :** Team backlog

**Project name :** Task Manager Development

**Create project**

---

**Colonnes par défaut :**

```
[LISTE] Backlog  ->  [CONSTRUCTION] In Progress  ->  [OK] Done
```

---

**Ajouter les issues au project :**

1. Dans le Project, cliquer sur "+ Add item"
2. Chercher l'issue #6
3. L'ajouter dans "Backlog"
4. Répéter pour l'issue #7

---

**Déplacer une issue en "In Progress" :**

Quand tu commences à travailler dessus, drag & drop vers "In Progress"

---

**Lier une PR à une issue :**

Dans la description de la PR :

```markdown
## [LIEN] Related Issues

Closes #6
```

**Quand la PR est mergée, l'issue se ferme automatiquement !**

---

### ÉTAPE 9 : Rebase vs Merge

#### Comprendre Rebase

**Merge :**

```
main:      A ─ B ─ C ─ M (merge commit)
                      ╱
feature:        D ─ E
```

**Rebase :**

```
main:      A ─ B ─ C ─ D' ─ E'
```

**Rebase "rejoue" les commits de feature AU-DESSUS de main**

---

**Pourquoi rebase ?**

[OK] Historique linéaire (plus propre)
[OK] Pas de commits de merge
[OK] Plus facile à lire (`git log`)

**Inconvénients :**

[X] Réécrit l'historique (change les SHA)
[X] Dangereux sur branches partagées
[X] Conflits potentiellement multiples

---

**Règle d'or du rebase :**

**[X] NE JAMAIS rebaser une branche publique (poussée sur GitHub) !**

**[OK] OK pour rebaser des branches locales ou features personnelles avant de pousser**

---

#### Exemple de rebase

**Scénario : Ta branche feature est en retard sur develop**

```bash
# Ta branche
git checkout feature/add-deletion

# Commits dans develop que tu n'as pas
git fetch origin

# Rebaser (rejouer tes commits au-dessus de develop)
git rebase origin/develop
```

---

**Si conflits pendant le rebase :**

```bash
# Résoudre les conflits dans les fichiers
# Puis :
git add .
git rebase --continue

# Ou abandonner le rebase :
git rebase --abort
```

---

**Après rebase, force push nécessaire (si déjà poussé) :**

```bash
git push --force-with-lease origin feature/add-deletion
```

**`--force-with-lease`** = Force push sécurisé (échoue si quelqu'un a poussé entre temps)

**[ATTENTION] Jamais sur develop ou main !**

---

### ÉTAPE 10 : Hotfix en production

**Scénario : Bug critique en production !**

#### Workflow Hotfix

```bash
# 1. Créer branche hotfix depuis main
git checkout main
git pull origin main
git checkout -b hotfix/critical-validation-bug
```

---

**2. Corriger le bug :**

```bash
# Exemple : Fix dans Task.js
# (Simulation d'un bug critique)

cat > src/models/Task.js << 'EOF'
// ... (code avec le fix)
EOF
```

---

**3. Commiter :**

```bash
git add .
git commit -m "hotfix: Fix critical validation bypass vulnerability"
```

---

**4. Merger dans main ET develop :**

```bash
# Merger dans main
git checkout main
git merge hotfix/critical-validation-bug
git tag -a v1.0.1 -m "Hotfix: Critical validation fix"
git push origin main --tags

# Merger dans develop
git checkout develop
git merge hotfix/critical-validation-bug
git push origin develop

# Supprimer la branche hotfix
git branch -d hotfix/critical-validation-bug
git push origin --delete hotfix/critical-validation-bug
```

---

### [OK] TESTS DE VALIDATION

**1. Fork et upstream configurés**

```bash
git remote -v
```

- [ ] Remote `origin` (ton fork)
- [ ] Remote `upstream` (repo original)

---

**2. Pull Request créée**

- [ ] PR créée sur GitHub
- [ ] Description complète
- [ ] Labels assignés
- [ ] Reviewer assigné

---

**3. Code Review effectuée**

- [ ] Commentaires ajoutés
- [ ] Modifications demandées
- [ ] Approbation reçue

---

**4. Conflit résolu**

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

- [ ] Commit de merge de conflit présent
- [ ] Pas de marqueurs de conflit dans les fichiers

---

**5. Issues et Projects**

- [ ] Au moins 2 issues créées
- [ ] Project board créé
- [ ] Issues liées au project

---

**6. Historique propre**

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

- [ ] Commits squashés (si squash and merge)
- [ ] Messages de commit clairs

---

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

**1. Git Flow**
```
main -> Production
develop -> Intégration
feature/* -> Nouvelles fonctionnalités
hotfix/* -> Corrections urgentes
release/* -> Préparation release
```

**2. Workflow avec fork**
```
Fork -> Clone -> Branch -> Code -> Push -> Pull Request -> Review -> Merge
```

**3. Résolution de conflits**
```
<<<<<<< HEAD (tes modifs)
=======
>>>>>>> branch (leurs modifs)
```

**4. Pull Request**
- Description détaillée
- Tests passants
- Code review obligatoire
- Squash and merge (historique propre)

**5. Rebase vs Merge**
- Merge : Garde l'historique complet
- Rebase : Historique linéaire (jamais sur branche publique !)

---

### [RAPIDE] POUR ALLER PLUS LOIN

**1. Protected branches avec CI/CD**

```yaml
# .github/workflows/ci.yml
name: CI

on: [pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - run: npm install
      - run: npm test
```

---

**2. Conventional Commits avec linter**

```bash
npm install --save-dev @commitlint/cli @commitlint/config-conventional
```

---

**3. Hooks Git**

```bash
# .git/hooks/pre-commit
#!/bin/sh
npm run lint
npm test
```

---

**4. Semantic Versioning automatique**

```bash
npm install --save-dev semantic-release
```

---

## [COURS] CONCLUSION DE L'EXERCICE 2

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

**Ce que tu as appris :**
- Git Flow workflow
- Forking et upstream
- Pull Requests professionnelles
- Code Reviews
- Résolution de conflits
- GitHub Issues et Projects
- Rebase vs Merge
- Hotfix workflow

**Compétences acquises :**
- [OK] Collaboration en équipe
- [OK] Workflows professionnels
- [OK] Code review
- [OK] Gestion de conflits
- [OK] Project management

**Temps moyen :** 3-4 heures

**Prochaine étape :** Exercice 3 - Git Avancé + CI/CD ! [RAPIDE]

---

Je continue avec les exercices 3, 4 et 5 dans le prochain message.

# [JAUNE] EXERCICE 3 : GIT AVANCÉ + CI/CD AVEC GITHUB ACTIONS

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es DevOps dans une scale-up qui développe une API REST. L'équipe veut :
1. Automatiser les tests et déploiements
2. Maîtriser les commandes Git avancées
3. Mettre en place une CI/CD complète
4. Gérer des dépendances externes (submodules)
5. Optimiser le workflow de développement

### Cahier des charges

**Mission : Mettre en place un pipeline CI/CD complet**

**Fonctionnalités à implémenter :**
- Pipeline de tests automatisés (GitHub Actions)
- Build et déploiement automatiques
- Gestion de versions sémantiques
- Quality gates (linting, tests, coverage)
- Environnements multiples (dev, staging, prod)
- Rollback automatique en cas d'échec

**Outils Git avancés à maîtriser :**
- `git rebase -i` (rebase interactif)
- `git cherry-pick` (sélectionner des commits)
- `git stash` (mettre de côté des modifications)
- `git bisect` (trouver un bug par dichotomie)
- `git submodule` (dépendances externes)
- `git reflog` (historique complet)

**Contraintes :**
- Tests obligatoires avant merge
- Couverture de code > 80%
- Déploiement automatique sur merge dans main
- Notifications sur Slack/Discord
- Temps estimé : 4-5 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :

- [OK] Utiliser rebase interactif pour nettoyer l'historique
- [OK] Cherry-picker des commits spécifiques
- [OK] Utiliser stash pour gérer le travail en cours
- [OK] Débugger avec git bisect
- [OK] Gérer des submodules
- [OK] Créer des GitHub Actions workflows
- [OK] Mettre en place des tests automatisés
- [OK] Déployer automatiquement
- [OK] Gérer les secrets
- [OK] Créer des matrices de tests

---

## [DOCS] PRÉREQUIS

- Exercices 1 et 2 terminés
- Compréhension des branches et merges
- Notions de CI/CD (concepts)
- Compte GitHub avec Actions activées

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Créer le projet API REST

**Initialiser le projet :**

```bash
# Créer le dossier
mkdir api-rest-advanced
cd api-rest-advanced

# Initialiser Git
git init
git branch -M main

# Créer la structure
mkdir -p src/{routes,controllers,models,middleware,utils}
mkdir -p tests/{unit,integration}
mkdir -p .github/workflows
```

---

**Créer package.json :**

```bash
cat > package.json << 'EOF'
{
  "name": "api-rest-advanced",
  "version": "1.0.0",
  "description": "API REST avancée avec CI/CD",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js",
    "dev": "nodemon src/index.js",
    "test": "jest --coverage",
    "test:unit": "jest tests/unit",
    "test:integration": "jest tests/integration",
    "lint": "eslint src/**/*.js",
    "lint:fix": "eslint src/**/*.js --fix",
    "build": "echo 'Build step placeholder'",
    "deploy": "echo 'Deploy step placeholder'"
  },
  "keywords": ["api", "rest", "ci-cd"],
  "author": "Your Name",
  "license": "MIT",
  "devDependencies": {
    "jest": "^29.0.0",
    "eslint": "^8.0.0",
    "nodemon": "^3.0.0"
  },
  "dependencies": {
    "express": "^4.18.0"
  }
}
EOF
```

---

**Créer l'application Express :**

```bash
cat > src/index.js << 'EOF'
/**
 * API REST - Entry Point
 */

const express = require('express');
const app = express();
const PORT = process.env.PORT || 3000;

// Middleware
app.use(express.json());

// Routes
app.get('/', (req, res) => {
    res.json({
        message: 'API REST Avancée',
        version: '1.0.0',
        status: 'running'
    });
});

app.get('/health', (req, res) => {
    res.json({
        status: 'healthy',
        timestamp: new Date().toISOString()
    });
});

// Start server (seulement si pas en test)
if (require.main === module) {
    app.listen(PORT, () => {
        console.log(`[RAPIDE] Server running on port ${PORT}`);
    });
}

module.exports = app;
EOF
```

---

**Créer un modèle User :**

```bash
cat > src/models/User.js << 'EOF'
/**
 * User Model
 */

class User {
    constructor(id, username, email) {
        if (!username || username.trim() === '') {
            throw new Error('Username is required');
        }
        if (!email || !this.isValidEmail(email)) {
            throw new Error('Valid email is required');
        }
        
        this.id = id;
        this.username = username.trim();
        this.email = email.toLowerCase().trim();
        this.createdAt = new Date();
    }

    /**
     * Validate email format
     */
    isValidEmail(email) {
        const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        return emailRegex.test(email);
    }

    /**
     * Convert to JSON
     */
    toJSON() {
        return {
            id: this.id,
            username: this.username,
            email: this.email,
            createdAt: this.createdAt.toISOString()
        };
    }
}

module.exports = User;
EOF
```

---

**Créer des tests :**

```bash
cat > tests/unit/User.test.js << 'EOF'
const User = require('../../src/models/User');

describe('User Model', () => {
    test('should create a valid user', () => {
        const user = new User(1, 'john_doe', 'john@example.com');
        
        expect(user.id).toBe(1);
        expect(user.username).toBe('john_doe');
        expect(user.email).toBe('john@example.com');
        expect(user.createdAt).toBeInstanceOf(Date);
    });

    test('should throw error for invalid email', () => {
        expect(() => {
            new User(1, 'john', 'invalid-email');
        }).toThrow('Valid email is required');
    });

    test('should throw error for missing username', () => {
        expect(() => {
            new User(1, '', 'john@example.com');
        }).toThrow('Username is required');
    });

    test('should convert to JSON', () => {
        const user = new User(1, 'john_doe', 'john@example.com');
        const json = user.toJSON();
        
        expect(json).toHaveProperty('id');
        expect(json).toHaveProperty('username');
        expect(json).toHaveProperty('email');
        expect(json).toHaveProperty('createdAt');
    });

    test('should normalize email to lowercase', () => {
        const user = new User(1, 'john', 'JOHN@EXAMPLE.COM');
        expect(user.email).toBe('john@example.com');
    });
});
EOF
```

---

```bash
cat > tests/integration/api.test.js << 'EOF'
const request = require('supertest');
const app = require('../../src/index');

describe('API Integration Tests', () => {
    test('GET / should return API info', async () => {
        const response = await request(app).get('/');
        
        expect(response.status).toBe(200);
        expect(response.body).toHaveProperty('message');
        expect(response.body).toHaveProperty('version');
        expect(response.body.status).toBe('running');
    });

    test('GET /health should return health status', async () => {
        const response = await request(app).get('/health');
        
        expect(response.status).toBe(200);
        expect(response.body.status).toBe('healthy');
        expect(response.body).toHaveProperty('timestamp');
    });
});
EOF
```

---

**Configuration Jest :**

```bash
cat > jest.config.js << 'EOF'
module.exports = {
    testEnvironment: 'node',
    collectCoverageFrom: [
        'src/**/*.js',
        '!src/index.js'
    ],
    coverageThreshold: {
        global: {
            branches: 80,
            functions: 80,
            lines: 80,
            statements: 80
        }
    }
};
EOF
```

---

**Configuration ESLint :**

```bash
cat > .eslintrc.json << 'EOF'
{
    "env": {
        "node": true,
        "es2021": true,
        "jest": true
    },
    "extends": "eslint:recommended",
    "parserOptions": {
        "ecmaVersion": 12
    },
    "rules": {
        "indent": ["error", 4],
        "quotes": ["error", "single"],
        "semi": ["error", "always"],
        "no-unused-vars": "warn",
        "no-console": "off"
    }
}
EOF
```

---

**Créer .gitignore :**

```bash
cat > .gitignore << 'EOF'
# Dependencies
node_modules/
package-lock.json

# Logs
*.log
logs/

# Environment
.env
.env.local

# Coverage
coverage/
.nyc_output/

# OS
.DS_Store
Thumbs.db

# IDE
.vscode/
.idea/
*.swp
*.swo

# Build
dist/
build/
EOF
```

---

**Premier commit :**

```bash
git add .
git commit -m "chore: Initial project setup with Express and tests"
```

---

### ÉTAPE 2 : Git Stash - Gérer le travail en cours

**Scénario : Tu travailles sur une feature mais dois corriger un bug urgent**

#### Comprendre git stash

**git stash** = "Mettre de côté" des modifications temporairement

```
Working Directory (modifié) -> Stash (stockage temporaire)
                              v
Working Directory (propre)
```

---

**Créer des modifications (work in progress) :**

```bash
# Créer une nouvelle feature (pas finie)
cat > src/models/Product.js << 'EOF'
/**
 * Product Model (WIP - Work In Progress)
 */

class Product {
    constructor(id, name, price) {
        this.id = id;
        this.name = name;
        this.price = price;
        // TODO: Add validation
        // TODO: Add category
        // TODO: Add stock management
    }
}

module.exports = Product;
EOF
```

---

**Vérifier le statut :**

```bash
git status
```

**Résultat :**
```
On branch main
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        src/models/Product.js

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

---

**Urgence : Bug critique à corriger !**

**Problème : On ne peut pas commiter Product.js (pas fini)**

**Solution : Stash !**

```bash
# Ajouter au staging (nécessaire pour stash)
git add src/models/Product.js

# Mettre de côté
git stash push -m "WIP: Product model in progress"
```

**Explication de la commande :**

**`git stash push`** = Nouvelle syntaxe (recommandée)
- Ancienne : `git stash` ou `git stash save`

**`-m "message"`** = Message descriptif du stash
- Sans `-m`, message par défaut : "WIP on branch: commit message"

---

**Vérifier :**

```bash
git status
```

**Résultat :**
```
On branch main
nothing to commit, working tree clean
```

**[OK] Working directory propre ! Product.js a disparu (temporairement)**

---

**Voir les stashes :**

```bash
git stash list
```

**Résultat :**
```
stash@{0}: On main: WIP: Product model in progress
```

**`stash@{0}`** = Identifiant du stash (0 = le plus récent)

---

**Corriger le bug urgent :**

```bash
# Créer une branche hotfix
git checkout -b hotfix/user-email-validation

# Corriger le bug dans User.js
cat > src/models/User.js << 'EOF'
/**
 * User Model
 */

class User {
    constructor(id, username, email) {
        if (!username || username.trim() === '') {
            throw new Error('Username is required');
        }
        if (!email || !this.isValidEmail(email)) {
            throw new Error('Valid email is required');
        }
        
        this.id = id;
        this.username = username.trim();
        this.email = email.toLowerCase().trim();
        this.createdAt = new Date();
    }

    /**
     * Validate email format (IMPROVED REGEX)
     */
    isValidEmail(email) {
        // Regex améliorée pour validation email
        const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
        return emailRegex.test(email);
    }

    /**
     * Convert to JSON
     */
    toJSON() {
        return {
            id: this.id,
            username: this.username,
            email: this.email,
            createdAt: this.createdAt.toISOString()
        };
    }
}

module.exports = User;
EOF
```

---

**Commiter le fix :**

```bash
git add src/models/User.js
git commit -m "fix: Improve email validation regex"
```

---

**Merger le hotfix :**

```bash
git checkout main
git merge hotfix/user-email-validation
git branch -d hotfix/user-email-validation
```

---

**Récupérer le travail en cours (stash) :**

```bash
# Appliquer le dernier stash
git stash pop
```

**Résultat :**
```
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   src/models/Product.js
```

**`git stash pop`** = Applique le stash ET le supprime de la liste

**Alternative : `git stash apply`** = Applique SANS supprimer (garde le stash)

---

**Vérifier la liste des stash :**

```bash
git stash list
```

**Résultat :**
```
(vide)
```

**[OK] Le stash a été appliqué et supprimé !**

---

**Autres commandes stash utiles :**

```bash
# Voir le contenu d'un stash
git stash show stash@{0}

# Voir les différences
git stash show -p stash@{0}

# Appliquer un stash spécifique (pas forcément le dernier)
git stash apply stash@{1}

# Supprimer un stash
git stash drop stash@{0}

# Supprimer TOUS les stash
git stash clear

# Créer une branche depuis un stash
git stash branch feature/new-branch stash@{0}
```

---

### ÉTAPE 3 : Rebase Interactif - Nettoyer l'historique

**Scénario : Tu as fait plusieurs commits "désordonnés" et tu veux nettoyer avant de pousser**

#### Créer des commits "sales"

```bash
# Commit 1
echo "console.log('Debug 1');" >> src/models/Product.js
git add src/models/Product.js
git commit -m "wip"

# Commit 2
echo "// TODO: refactor this" >> src/models/Product.js
git add src/models/Product.js
git commit -m "fix typo"

# Commit 3
cat >> src/models/Product.js << 'EOF'

    validate() {
        if (this.price < 0) {
            throw new Error('Price cannot be negative');
        }
    }
}

module.exports = Product;
EOF
git add src/models/Product.js
git commit -m "Add validation"

# Commit 4
cat > tests/unit/Product.test.js << 'EOF'
const Product = require('../../src/models/Product');

describe('Product Model', () => {
    test('should create a product', () => {
        const product = new Product(1, 'Laptop', 999);
        expect(product.id).toBe(1);
        expect(product.name).toBe('Laptop');
        expect(product.price).toBe(999);
    });

    test('should validate negative price', () => {
        const product = new Product(1, 'Laptop', -100);
        expect(() => product.validate()).toThrow('Price cannot be negative');
    });
});
EOF
git add tests/unit/Product.test.js
git commit -m "add tests"
```

---

**Voir l'historique :**

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

**Résultat :**
```
a1b2c3d (HEAD -> main) add tests
e4f5g6h Add validation
i7j8k9l fix typo
m0n1o2p wip
q3r4s5t fix: Improve email validation regex
...
```

**[X] Historique sale : "wip", "fix typo", messages non conventionnels**

---

#### Utiliser rebase interactif

**Objectif : Squash (fusionner) les 4 commits en 1 seul commit propre**

```bash
# Rebase interactif sur les 4 derniers commits
git rebase -i HEAD~4
```

**`HEAD~4`** = 4 commits avant HEAD

---

**Un éditeur s'ouvre avec :**

```
pick m0n1o2p wip
pick i7j8k9l fix typo
pick e4f5g6h Add validation
pick a1b2c3d add tests

# Rebase q3r4s5t..a1b2c3d onto q3r4s5t (4 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
# d, drop <commit> = remove commit
```

---

**Explication des commandes :**

**`pick`** = Garder le commit tel quel

**`reword`** = Garder le commit mais changer le message

**`edit`** = Arrêter pour modifier le commit (ajouter/supprimer fichiers)

**`squash`** = Fusionner avec le commit précédent, garder les messages

**`fixup`** = Fusionner avec le commit précédent, jeter le message

**`drop`** = Supprimer complètement le commit

---

**Modifier pour squash :**

```
pick m0n1o2p wip
squash i7j8k9l fix typo
squash e4f5g6h Add validation
squash a1b2c3d add tests
```

**Ou avec raccourcis :**

```
p m0n1o2p wip
s i7j8k9l fix typo
s e4f5g6h Add validation
s a1b2c3d add tests
```

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

---

**Un nouvel éditeur s'ouvre pour le message du commit squashé :**

```
# This is a combination of 4 commits.
# This is the 1st commit message:

wip

# This is the commit message #2:

fix typo

# This is the commit message #3:

Add validation

# This is the commit message #4:

add tests
```

---

**Remplacer tout par un message propre :**

```
feat(models): Add Product model with price validation

- Create Product class with id, name, price
- Add validate() method for price constraints
- Add comprehensive unit tests
- Prevent negative prices
```

**Sauvegarder et fermer.**

---

**Résultat :**

```
Successfully rebased and updated refs/heads/main.
```

---

**Vérifier l'historique :**

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

**Résultat :**
```
b5c6d7e (HEAD -> main) feat(models): Add Product model with price validation
q3r4s5t fix: Improve email validation regex
...
```

**[OK] 4 commits désordonnés -> 1 commit propre !**

---

**[ATTENTION] ATTENTION : Rebase change les SHA !**

```
Avant rebase:
m0n1o2p -> i7j8k9l -> e4f5g6h -> a1b2c3d

Après rebase:
b5c6d7e (nouveau SHA !)
```

**Conséquences :**

- [X] Ne JAMAIS rebaser des commits déjà poussés sur une branche partagée
- [OK] OK sur branches features personnelles avant de pousser
- [ATTENTION] Si déjà poussé, force push nécessaire : `git push --force-with-lease`

---

**Autres exemples de rebase interactif :**

**Réordonner des commits :**

```
pick e4f5g6h Add validation
pick m0n1o2p wip
pick a1b2c3d add tests
pick i7j8k9l fix typo
```

**Éditer un commit (pour ajouter un fichier oublié) :**

```
edit m0n1o2p wip
pick i7j8k9l fix typo
...
```

Git s'arrêtera sur `m0n1o2p` pour que tu modifies :

```bash
# Modifier des fichiers
git add fichier-oublie.js
git commit --amend --no-edit
git rebase --continue
```

---

### ÉTAPE 4 : Cherry-pick - Sélectionner des commits

**Scénario : Un commit de la branche develop contient un fix que tu veux sur main**

#### Comprendre cherry-pick

**git cherry-pick** = Copier un commit spécifique vers la branche actuelle

```
main:      A ─ B ─ C
                    v cherry-pick E
develop:   A ─ B ─ D ─ E ─ F

Résultat:
main:      A ─ B ─ C ─ E'
develop:   A ─ B ─ D ─ E ─ F
```

**E' = Copie de E (nouveau SHA)**

---

**Créer une branche develop avec un fix :**

```bash
# Créer develop
git checkout -b develop

# Ajouter une fonctionnalité
cat > src/utils/logger.js << 'EOF'
/**
 * Logger utility
 */

const logger = {
    info: (message) => console.log(`[INFO] ${message}`),
    error: (message) => console.error(`[ERROR] ${message}`),
    warn: (message) => console.warn(`[WARN] ${message}`)
};

module.exports = logger;
EOF

git add src/utils/logger.js
git commit -m "feat(utils): Add logger utility"
```

---

**Ajouter un fix important :**

```bash
cat > src/utils/validator.js << 'EOF'
/**
 * Validation utilities
 */

const validator = {
    isEmail: (email) => {
        const emailRegex = /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/;
        return emailRegex.test(email);
    },
    
    isNotEmpty: (str) => {
        return str && str.trim().length > 0;
    }
};

module.exports = validator;
EOF

git add src/utils/validator.js
git commit -m "fix(utils): Add critical input validation utilities"
```

---

**Noter le SHA du commit de fix :**

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

**Résultat :**
```
c7d8e9f (HEAD -> develop) fix(utils): Add critical input validation utilities
b5c6d7e feat(utils): Add logger utility
...
```

**SHA du fix : `c7d8e9f`**

---

**Revenir sur main et cherry-pick le fix :**

```bash
# Basculer sur main
git checkout main

# Cherry-pick le commit de fix
git cherry-pick c7d8e9f
```

---

**Résultat :**

```
[main e9f0g1h] fix(utils): Add critical input validation utilities
 Date: Mon Dec 16 14:30:00 2024 +0000
 1 file changed, 15 insertions(+)
 create mode 100644 src/utils/validator.js
```

---

**Vérifier :**

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

**Résultat :**
```
e9f0g1h (HEAD -> main) fix(utils): Add critical input validation utilities
b5c6d7e feat(models): Add Product model with price validation
...
```

**[OK] Le fix est maintenant sur main !**

**Sur develop :**

```bash
git checkout develop
git log --oneline
```

**Résultat :**
```
c7d8e9f (HEAD -> develop) fix(utils): Add critical input validation utilities
b5c6d7e feat(utils): Add logger utility
...
```

**Le commit original est toujours là (SHA différent : c7d8e9f vs e9f0g1h)**

---

**Cherry-pick multiple commits :**

```bash
# Cherry-pick plusieurs commits
git cherry-pick abc123 def456 ghi789

# Cherry-pick une plage de commits
git cherry-pick abc123..ghi789
```

---

**Gérer les conflits pendant cherry-pick :**

```bash
# Si conflit
git status
# Résoudre les conflits
git add .
git cherry-pick --continue

# Ou abandonner
git cherry-pick --abort
```

---

### ÉTAPE 5 : Git Bisect - Trouver un bug par dichotomie

**Scénario : Un bug est apparu mais tu ne sais pas dans quel commit**

#### Comprendre git bisect

**git bisect** = Recherche binaire (dichotomie) dans l'historique

```
Commits: A ─ B ─ C ─ D ─ E ─ F ─ G ─ H (bug ici)
                 ^
          Bisect teste C
          -> Bon ? Cherche entre C et H
          -> Mauvais ? Cherche entre A et C
```

**Complexité : O(log n) au lieu de O(n)**

**Exemple : 100 commits -> Maximum 7 tests au lieu de 100**

---

**Simuler un bug introduit dans un commit :**

```bash
# Commit sans bug
git checkout main

echo "Version 1.0" > VERSION
git add VERSION
git commit -m "chore: Add version file"

# Commit 2
echo "Version 1.1" > VERSION
git add VERSION
git commit -m "chore: Bump version to 1.1"

# Commit 3 - INTRODUIRE LE BUG
cat > src/utils/calculator.js << 'EOF'
/**
 * Calculator utility
 */

const calculator = {
    add: (a, b) => a + b,
    subtract: (a, b) => a - b,
    multiply: (a, b) => a * b,
    divide: (a, b) => {
        // BUG ICI : Pas de vérification de division par zéro
        return a / b;
    }
};

module.exports = calculator;
EOF

git add src/utils/calculator.js
git commit -m "feat(utils): Add calculator utility"

# Commit 4
echo "Version 1.2" > VERSION
git add VERSION
git commit -m "chore: Bump version to 1.2"

# Commit 5
cat > README.md << 'EOF'
# API REST Advanced

Version 1.2
EOF
git add README.md
git commit -m "docs: Update README"
```

---

**Détecter le bug (plus tard) :**

```bash
# Tester la division par zéro
node -e "const calc = require('./src/utils/calculator'); console.log(calc.divide(10, 0));"
```

**Résultat :**
```
Infinity
```

**[X] Bug : Devrait throw une erreur, pas retourner Infinity**

---

**Démarrer bisect :**

```bash
# Initialiser bisect
git bisect start

# Marquer le commit actuel comme mauvais (avec le bug)
git bisect bad

# Trouver un commit bon (avant le bug)
git log --oneline
# Supposons que le commit b5c6d7e était bon
git bisect good b5c6d7e
```

---

**Git checkout automatiquement un commit au milieu :**

```
Bisecting: 2 revisions left to test after this (roughly 1 step)
[commit-sha] chore: Bump version to 1.1
```

---

**Tester si ce commit est bon ou mauvais :**

```bash
# Tester (le fichier calculator.js existe ?)
ls src/utils/calculator.js 2>/dev/null && echo "Bug présent" || echo "Pas de bug"
```

**Si bug présent :**

```bash
git bisect bad
```

**Si pas de bug :**

```bash
git bisect good
```

---

**Git continue la dichotomie :**

```
Bisecting: 0 revisions left to test after this (roughly 0 steps)
[commit-sha] feat(utils): Add calculator utility
```

---

**Tester à nouveau :**

```bash
ls src/utils/calculator.js && echo "Bug présent" || echo "Pas de bug"
# Bug présent
git bisect bad
```

---

**Git trouve le commit coupable :**

```
abc123def456 is the first bad commit
commit abc123def456
Author: Your Name <you@example.com>
Date:   Mon Dec 16 15:00:00 2024 +0000

    feat(utils): Add calculator utility

 src/utils/calculator.js | 15 +++++++++++++++
 1 file changed, 15 insertions(+)
 create mode 100644 src/utils/calculator.js
```

**[OK] Commit trouvé : `feat(utils): Add calculator utility`**

---

**Terminer bisect :**

```bash
git bisect reset
```

**Retourne sur la branche/commit d'origine**

---

**Bisect automatisé avec un script :**

```bash
# Créer un script de test
cat > test-divide.sh << 'EOF'
#!/bin/bash
if [ ! -f src/utils/calculator.js ]; then
    exit 0  # Bon (fichier n'existe pas encore)
fi

# Tester la division par zéro
result=$(node -e "const calc = require('./src/utils/calculator'); console.log(calc.divide(10, 0));")

if [ "$result" == "Infinity" ]; then
    exit 1  # Mauvais (bug présent)
else
    exit 0  # Bon (pas de bug)
fi
EOF

chmod +x test-divide.sh
```

---

**Lancer bisect automatique :**

```bash
git bisect start HEAD b5c6d7e
git bisect run ./test-divide.sh
```

**Git va automatiquement tester chaque commit avec le script !**

---

### ÉTAPE 6 : Git Submodules - Gérer des dépendances externes

**Scénario : Ton projet dépend d'une librairie interne versionnée séparément**

#### Comprendre les submodules

**Submodule** = Repository Git dans un autre repository Git

```
projet-principal/
├── src/
├── lib/
│   └── ma-lib-interne/  <- Submodule (repo Git séparé)
└── .gitmodules         <- Configuration des submodules
```

---

**Créer un repository pour la librairie :**

```bash
# Créer un repo séparé pour une librairie
cd /tmp
mkdir shared-utils
cd shared-utils
git init

cat > index.js << 'EOF'
/**
 * Shared Utilities
 */

module.exports = {
    formatDate: (date) => date.toISOString().split('T')[0],
    capitalize: (str) => str.charAt(0).toUpperCase() + str.slice(1),
    generateId: () => Math.random().toString(36).substr(2, 9)
};
EOF

git add index.js
git commit -m "feat: Add shared utilities"

# Pousser sur GitHub (créer le repo sur GitHub d'abord)
git remote add origin https://github.com/TON-USERNAME/shared-utils.git
git push -u origin main
```

---

**Ajouter le submodule au projet principal :**

```bash
# Revenir au projet principal
cd ~/api-rest-advanced

# Ajouter le submodule
git submodule add https://github.com/TON-USERNAME/shared-utils.git lib/shared-utils
```

---

**Résultat :**

```
Cloning into '/path/to/api-rest-advanced/lib/shared-utils'...
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
Receiving objects: 100% (3/3), done.
```

---

**Fichiers créés :**

```bash
ls -la
```

**Nouveau fichier :** `.gitmodules`

```bash
cat .gitmodules
```

**Contenu :**

```
[submodule "lib/shared-utils"]
    path = lib/shared-utils
    url = https://github.com/TON-USERNAME/shared-utils.git
```

---

**Structure :**

```
api-rest-advanced/
├── lib/
│   └── shared-utils/     <- Submodule
│       └── index.js
├── .gitmodules
└── ...
```

---

**Commiter le submodule :**

```bash
git add .gitmodules lib/shared-utils
git commit -m "chore: Add shared-utils as submodule"
```

---

**[ATTENTION] Attention : Git ne stocke PAS le contenu du submodule**

**Il stocke seulement :**
- Le commit SHA du submodule
- L'URL du repository

```bash
git ls-tree HEAD lib/shared-utils
```

**Résultat :**
```
160000 commit abc123def456 lib/shared-utils
```

**`160000`** = Type "gitlink" (lien vers un commit)

---

**Utiliser le submodule dans le code :**

```bash
cat > src/index.js << 'EOF'
const express = require('express');
const sharedUtils = require('../lib/shared-utils');

const app = express();
const PORT = process.env.PORT || 3000;

app.use(express.json());

app.get('/', (req, res) => {
    res.json({
        message: 'API REST Avancée',
        version: '1.0.0',
        status: 'running',
        requestId: sharedUtils.generateId(),
        date: sharedUtils.formatDate(new Date())
    });
});

app.get('/health', (req, res) => {
    res.json({
        status: 'healthy',
        timestamp: new Date().toISOString()
    });
});

if (require.main === module) {
    app.listen(PORT, () => {
        console.log(`[RAPIDE] Server running on port ${PORT}`);
    });
}

module.exports = app;
EOF
```

---

**Cloner un projet avec submodules :**

```bash
# Méthode 1 : Clone récursif (recommandé)
git clone --recursive https://github.com/TON-USERNAME/api-rest-advanced.git

# Méthode 2 : Clone puis init des submodules
git clone https://github.com/TON-USERNAME/api-rest-advanced.git
cd api-rest-advanced
git submodule init
git submodule update
```

---

**Mettre à jour les submodules :**

```bash
# Aller dans le submodule
cd lib/shared-utils

# Pull les derniers changements
git pull origin main

# Revenir au projet principal
cd ../..

# Commiter la nouvelle version du submodule
git add lib/shared-utils
git commit -m "chore: Update shared-utils to latest version"
```

---

**Commandes submodules utiles :**

```bash
# Voir le statut des submodules
git submodule status

# Mettre à jour TOUS les submodules
git submodule update --remote

# Supprimer un submodule
git submodule deinit lib/shared-utils
git rm lib/shared-utils
git commit -m "chore: Remove shared-utils submodule"
```

---

### ÉTAPE 7 : GitHub Actions - CI/CD Pipeline

**Créer un workflow de tests automatisés**

#### Workflow de base : Tests

```bash
cat > .github/workflows/ci.yml << 'EOF'
# ═══════════════════════════════════════════════════════════════
# CI Pipeline - Tests Automatisés
# ═══════════════════════════════════════════════════════════════

name: CI

# Déclencheurs
on:
  # Sur chaque push
  push:
    branches: [ main, develop ]
  
  # Sur chaque Pull Request
  pull_request:
    branches: [ main, develop ]
  
  # Workflow manuel
  workflow_dispatch:

# Variables d'environnement globales
env:
  NODE_VERSION: '18.x'

# Jobs à exécuter
jobs:
  # ─────────────────────────────────────────────────────────────
  # JOB 1: LINTING
  # ─────────────────────────────────────────────────────────────
  lint:
    name: [RECHERCHE] Linting
    runs-on: ubuntu-latest
    
    steps:
      # Checkout du code
      - name: Checkout code
        uses: actions/checkout@v3
      
      # Setup Node.js
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      
      # Installer les dépendances
      - name: Install dependencies
        run: npm ci
      
      # Linter le code
      - name: Run ESLint
        run: npm run lint

  # ─────────────────────────────────────────────────────────────
  # JOB 2: TESTS UNITAIRES
  # ─────────────────────────────────────────────────────────────
  test:
    name: [TEST] Tests
    runs-on: ubuntu-latest
    needs: lint  # Attend que lint soit terminé
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run tests with coverage
        run: npm test -- --coverage
      
      # Upload du rapport de couverture
      - name: Upload coverage to Codecov
        uses: codecov/codecov-action@v3
        with:
          file: ./coverage/coverage-final.json
          fail_ci_if_error: false

  # ─────────────────────────────────────────────────────────────
  # JOB 3: BUILD
  # ─────────────────────────────────────────────────────────────
  build:
    name: [CONSTRUCTION] Build
    runs-on: ubuntu-latest
    needs: test
    
    steps:
      - name: Checkout code
        uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build project
        run: npm run build
      
      # Sauvegarder l'artefact de build
      - name: Upload build artifact
        uses: actions/upload-artifact@v3
        with:
          name: build-artifact
          path: dist/
          retention-days: 7
EOF
```

---

**Explication détaillée du workflow :**

**`name: CI`** = Nom du workflow (affiché dans l'interface GitHub)

---

**`on:`** = Événements qui déclenchent le workflow

**`push: branches: [ main, develop ]`**
- Déclenché sur push vers main ou develop

**`pull_request:`**
- Déclenché sur création/mise à jour de PR

**`workflow_dispatch:`**
- Permet de lancer manuellement (bouton "Run workflow" dans GitHub)

---

**`env:`** = Variables d'environnement globales

```yaml
env:
  NODE_VERSION: '18.x'
```

Utilisable partout avec `${{ env.NODE_VERSION }}`

---

**`jobs:`** = Liste des jobs à exécuter

**`runs-on: ubuntu-latest`** = Machine virtuelle Ubuntu

Autres options :
- `windows-latest`
- `macos-latest`
- `ubuntu-20.04` (version spécifique)

---

**`needs: lint`** = Dépendance entre jobs

```
lint -> test -> build
```

Si `lint` échoue, `test` et `build` ne s'exécutent pas.

---

**`uses: actions/checkout@v3`** = Action GitHub préfabriquée

Checkout = Clone le repository dans le runner

---

**`uses: actions/setup-node@v3`** = Installer Node.js

```yaml
with:
  node-version: ${{ env.NODE_VERSION }}
  cache: 'npm'
```

`cache: 'npm'` = Cache node_modules (accélère les builds)

---

**`run: npm ci`** = Installer les dépendances

**`npm ci`** vs **`npm install`** :

| npm install | npm ci |
|-------------|--------|
| Utilise package.json | Utilise package-lock.json |
| Peut modifier package-lock.json | N'y touche JAMAIS |
| Plus lent | * Plus rapide |
| Dev | * CI/CD |

---

**`uses: actions/upload-artifact@v3`** = Sauvegarder un artefact

```yaml
with:
  name: build-artifact
  path: dist/
  retention-days: 7
```

L'artefact est téléchargeable depuis l'interface GitHub pendant 7 jours.

---

**Commiter le workflow :**

```bash
git add .github/workflows/ci.yml
git commit -m "ci: Add GitHub Actions CI pipeline"
git push origin main
```

---

**Vérifier sur GitHub :**

1. Repository -> Actions
2. Le workflow "CI" apparaît
3. Click pour voir les détails

**[OK] Les 3 jobs (lint, test, build) s'exécutent !**

---

#### Workflow avancé : Tests Matrix

```bash
cat > .github/workflows/test-matrix.yml << 'EOF'
# ═══════════════════════════════════════════════════════════════
# Test Matrix - Tests sur plusieurs versions
# ═══════════════════════════════════════════════════════════════

name: Test Matrix

on:
  pull_request:
    branches: [ main ]

jobs:
  test-matrix:
    name: Test Node ${{ matrix.node-version }} on ${{ matrix.os }}
    runs-on: ${{ matrix.os }}
    
    strategy:
      matrix:
        os: [ubuntu-latest, windows-latest, macos-latest]
        node-version: ['16.x', '18.x', '20.x']
        # Total: 3 OS × 3 versions = 9 combinaisons
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js ${{ matrix.node-version }}
        uses: actions/setup-node@v3
        with:
          node-version: ${{ matrix.node-version }}
          cache: 'npm'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Run tests
        run: npm test
EOF
```

**Résultat : 9 jobs en parallèle !**

```
[OK] ubuntu-latest / Node 16.x
[OK] ubuntu-latest / Node 18.x
[OK] ubuntu-latest / Node 20.x
[OK] windows-latest / Node 16.x
...
```

---

#### Workflow de déploiement

```bash
cat > .github/workflows/deploy.yml << 'EOF'
# ═══════════════════════════════════════════════════════════════
# Déploiement Automatique
# ═══════════════════════════════════════════════════════════════

name: Deploy

on:
  push:
    branches: [ main ]
    tags:
      - 'v*'

jobs:
  deploy-staging:
    name: [RAPIDE] Deploy to Staging
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    
    environment:
      name: staging
      url: https://staging.mon-api.com
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18.x'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build
        run: npm run build
      
      - name: Deploy to staging
        run: |
          echo "Deploying to staging..."
          # Commandes de déploiement (ex: scp, rsync, etc.)
        env:
          DEPLOY_KEY: ${{ secrets.STAGING_DEPLOY_KEY }}
  
  deploy-production:
    name: [OBJECTIF] Deploy to Production
    runs-on: ubuntu-latest
    if: startsWith(github.ref, 'refs/tags/v')
    
    environment:
      name: production
      url: https://api.mon-api.com
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18.x'
      
      - name: Install dependencies
        run: npm ci
      
      - name: Build
        run: npm run build
      
      - name: Deploy to production
        run: |
          echo "Deploying to production..."
          # Commandes de déploiement
        env:
          DEPLOY_KEY: ${{ secrets.PROD_DEPLOY_KEY }}
      
      - name: Create GitHub Release
        uses: actions/create-release@v1
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          tag_name: ${{ github.ref }}
          release_name: Release ${{ github.ref }}
          draft: false
          prerelease: false
EOF
```

---

**Explication des secrets :**

**`${{ secrets.STAGING_DEPLOY_KEY }}`** = Variable secrète

**Créer un secret sur GitHub :**

1. Repository -> Settings -> Secrets and variables -> Actions
2. New repository secret
3. Name : `STAGING_DEPLOY_KEY`
4. Value : (ta clé SSH ou token)
5. Add secret

**Secrets disponibles automatiquement :**
- `GITHUB_TOKEN` : Token automatique pour l'API GitHub
- `GITHUB_ACTOR` : Utilisateur qui a déclenché le workflow
- `GITHUB_REPOSITORY` : Nom du repository

---

**Explication des environments :**

```yaml
environment:
  name: production
  url: https://api.mon-api.com
```

**Environments** = Environnements de déploiement configurables

**Avantages :**
- Protection branches (approbation manuelle avant deploy)
- Variables d'environnement spécifiques
- URL de déploiement affichée dans l'interface
- Historique des déploiements

**Configurer un environment :**

1. Settings -> Environments -> New environment
2. Name : `production`
3. Required reviewers : (optionnel, pour approbation manuelle)
4. Environment secrets

---

### ÉTAPE 8 : Git Reflog - L'historique ultime

**Scénario : Tu as fait une grosse boulette et tu veux récupérer du code "perdu"**

#### Comprendre reflog

**git reflog** = Journal de TOUTES les modifications de HEAD

```
Même les commits supprimés sont dans reflog !
```

---

**Simuler une erreur (reset hard) :**

```bash
# Voir l'historique
git log --oneline

# Faire un reset hard (DANGER : supprime des commits)
git reset --hard HEAD~3
```

**[X] Les 3 derniers commits ont disparu !**

---

**Vérifier :**

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

**Les commits ont disparu de log !**

---

**Reflog à la rescousse :**

```bash
git reflog
```

**Résultat :**

```
abc123d (HEAD -> main) HEAD@{0}: reset: moving to HEAD~3
def456e HEAD@{1}: commit: ci: Add GitHub Actions CI pipeline
ghi789f HEAD@{2}: commit: chore: Add shared-utils as submodule
jkl012m HEAD@{3}: commit: feat(models): Add Product model
...
```

**`HEAD@{1}` = État de HEAD il y a 1 action**

---

**Récupérer les commits perdus :**

```bash
# Revenir à l'état avant le reset
git reset --hard HEAD@{1}
```

**Ou avec le SHA :**

```bash
git reset --hard def456e
```

---

**Vérifier :**

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

**[OK] Les commits sont revenus !**

---

**Autres usages de reflog :**

```bash
# Voir reflog d'une branche spécifique
git reflog show develop

# Voir reflog détaillé
git log -g

# Récupérer un commit supprimé
git cherry-pick abc123d
```

---

### [OK] TESTS DE VALIDATION

**1. Git Stash**

```bash
# Créer des modifications
echo "WIP" >> test.txt

# Stash
git stash

# Vérifier
git stash list
```

- [ ] Modifications sauvegardées dans stash
- [ ] Working directory propre

---

**2. Rebase Interactif**

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

- [ ] Historique propre (commits squashés)
- [ ] Messages conventionnels

---

**3. Cherry-pick**

```bash
git log --oneline develop
git log --oneline main
```

- [ ] Commit copié de develop vers main
- [ ] SHA différents

---

**4. Bisect**

- [ ] Bug trouvé avec bisect
- [ ] Commit coupable identifié

---

**5. Submodules**

```bash
git submodule status
```

- [ ] Submodule initialisé
- [ ] Code du submodule utilisable

---

**6. GitHub Actions**

- [ ] Workflow CI créé
- [ ] Jobs exécutés avec succès
- [ ] Artifacts générés

---

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

**1. Git Stash**
```bash
git stash push -m "WIP"
git stash pop
```

**2. Rebase Interactif**
```bash
git rebase -i HEAD~4
# pick, squash, reword, edit, drop
```

**3. Cherry-pick**
```bash
git cherry-pick abc123
```

**4. Bisect**
```bash
git bisect start
git bisect bad
git bisect good abc123
```

**5. Submodules**
```bash
git submodule add URL path
git submodule update --remote
```

**6. GitHub Actions**
```yaml
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
```

**7. Reflog**
```bash
git reflog
git reset --hard HEAD@{1}
```

---

## [COURS] CONCLUSION DE L'EXERCICE 3

**[OK] Félicitations ! Tu maîtrises Git avancé et CI/CD !**

**Ce que tu as appris :**
- Commandes Git avancées (stash, rebase -i, cherry-pick, bisect)
- Gestion de submodules
- GitHub Actions et CI/CD
- Pipeline automatisé complet
- Reflog pour récupérer du code

**Compétences acquises :**
- [OK] Git avancé (niveau intermédiaire-avancé)
- [OK] CI/CD avec GitHub Actions
- [OK] Automatisation des tests
- [OK] Déploiement automatique
- [OK] Recovery et debugging

**Temps moyen :** 4-5 heures

**Prochaine étape :** Exercice 4 - Projets Complexes ! [CONSTRUCTION]

---

Je continue avec les exercices 4 et 5 dans le prochain message pour couvrir les sujets experts.

# [ROUGE] EXERCICE 4 : GESTION DE PROJETS COMPLEXES

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es Tech Lead dans une entreprise qui développe un produit SaaS multi-tenant. Le projet compte :
- 15 développeurs répartis sur 3 équipes
- 5 microservices interdépendants
- Releases toutes les 2 semaines
- Support de 3 environnements (dev, staging, production)

### Cahier des charges

**Mission : Gérer un monorepo complexe avec stratégie de branching avancée**

**Défis à relever :**
- Monorepo avec plusieurs services
- Git LFS pour fichiers volumineux
- Stratégie de versioning sémantique
- Gestion de releases complexes
- Hooks Git personnalisés
- Attribution et CODEOWNERS
- Protected branches avancées
- Signed commits (GPG)

**Architecture du projet :**
```
saas-platform/
├── services/
│   ├── api-gateway/
│   ├── auth-service/
│   ├── billing-service/
│   ├── notification-service/
│   └── analytics-service/
├── packages/
│   ├── shared-ui/
│   ├── shared-utils/
│   └── shared-config/
├── docs/
└── infrastructure/
```

**Contraintes :**
- Monorepo avec dépendances croisées
- Versioning indépendant par service
- Changelog automatique
- Git LFS pour assets (vidéos, images HD)
- Commits signés obligatoires
- Temps estimé : 5-6 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :

- [OK] Gérer un monorepo complexe
- [OK] Utiliser Git LFS (Large File Storage)
- [OK] Configurer des hooks Git avancés
- [OK] Mettre en place CODEOWNERS
- [OK] Signer des commits avec GPG
- [OK] Gérer des releases multi-services
- [OK] Utiliser git worktree
- [OK] Stratégies de merge avancées
- [OK] Git attributes et normalization
- [OK] Automatiser le changelog

---

## [DOCS] PRÉREQUIS

- Exercices 1, 2 et 3 terminés
- Compréhension des monorepos
- Notions d'architecture microservices
- GPG installé (pour signatures)

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Créer la structure du monorepo

**Initialiser le projet :**

```bash
# Créer le dossier principal
mkdir saas-platform
cd saas-platform

# Initialiser Git
git init
git branch -M main
```

---

**Créer la structure complète :**

```bash
# Services
mkdir -p services/{api-gateway,auth-service,billing-service,notification-service,analytics-service}

# Packages partagés
mkdir -p packages/{shared-ui,shared-utils,shared-config}

# Documentation
mkdir -p docs/{architecture,api,deployment}

# Infrastructure
mkdir -p infrastructure/{terraform,kubernetes,docker}

# Assets (pour Git LFS)
mkdir -p assets/{images,videos,datasets}

# Configuration racine
mkdir -p .github/{workflows,ISSUE_TEMPLATE,PULL_REQUEST_TEMPLATE}
```

---

**Créer le package.json racine (monorepo) :**

```bash
cat > package.json << 'EOF'
{
  "name": "saas-platform",
  "version": "1.0.0",
  "private": true,
  "description": "SaaS Platform - Monorepo",
  "workspaces": [
    "services/*",
    "packages/*"
  ],
  "scripts": {
    "install:all": "npm install",
    "test:all": "npm run test --workspaces",
    "lint:all": "npm run lint --workspaces",
    "build:all": "npm run build --workspaces",
    "version": "conventional-changelog -p angular -i CHANGELOG.md -s && git add CHANGELOG.md",
    "postinstall": "husky install"
  },
  "devDependencies": {
    "husky": "^8.0.0",
    "conventional-changelog-cli": "^3.0.0",
    "lerna": "^7.0.0"
  },
  "engines": {
    "node": ">=18.0.0",
    "npm": ">=9.0.0"
  }
}
EOF
```

**Explication des workspaces :**

**`workspaces: ["services/*", "packages/*"]`** = NPM Workspaces

**Avantages :**
- Une seule installation de dépendances à la racine
- Liens symboliques automatiques entre packages
- `npm run test --workspaces` exécute les tests partout

---

**Créer un service exemple (API Gateway) :**

```bash
cat > services/api-gateway/package.json << 'EOF'
{
  "name": "@saas/api-gateway",
  "version": "1.0.0",
  "description": "API Gateway Service",
  "main": "src/index.js",
  "scripts": {
    "start": "node src/index.js",
    "dev": "nodemon src/index.js",
    "test": "jest",
    "lint": "eslint src/**/*.js",
    "build": "echo 'Building API Gateway...'"
  },
  "dependencies": {
    "express": "^4.18.0",
    "@saas/shared-utils": "^1.0.0"
  },
  "devDependencies": {
    "jest": "^29.0.0",
    "nodemon": "^3.0.0"
  }
}
EOF
```

---

```bash
cat > services/api-gateway/src/index.js << 'EOF'
/**
 * API Gateway Service
 * Entry point for all API requests
 */

const express = require('express');
const app = express();
const PORT = process.env.PORT || 8000;

app.use(express.json());

// Health check
app.get('/health', (req, res) => {
    res.json({
        service: 'api-gateway',
        status: 'healthy',
        version: '1.0.0',
        timestamp: new Date().toISOString()
    });
});

// Route proxy (vers autres microservices)
app.get('/api/*', (req, res) => {
    res.json({
        message: 'Gateway routing',
        path: req.path
    });
});

if (require.main === module) {
    app.listen(PORT, () => {
        console.log(`[SORTIE] API Gateway running on port ${PORT}`);
    });
}

module.exports = app;
EOF
```

---

**Créer un package partagé (shared-utils) :**

```bash
cat > packages/shared-utils/package.json << 'EOF'
{
  "name": "@saas/shared-utils",
  "version": "1.0.0",
  "description": "Shared utilities across all services",
  "main": "src/index.js",
  "scripts": {
    "test": "jest",
    "lint": "eslint src/**/*.js"
  },
  "devDependencies": {
    "jest": "^29.0.0"
  }
}
EOF
```

---

```bash
cat > packages/shared-utils/src/index.js << 'EOF'
/**
 * Shared Utilities
 */

module.exports = {
    logger: {
        info: (msg) => console.log(`[INFO] ${msg}`),
        error: (msg) => console.error(`[ERROR] ${msg}`),
        warn: (msg) => console.warn(`[WARN] ${msg}`)
    },
    
    formatters: {
        date: (date) => date.toISOString().split('T')[0],
        currency: (amount, currency = 'USD') => {
            return new Intl.NumberFormat('en-US', {
                style: 'currency',
                currency
            }).format(amount);
        }
    },
    
    validators: {
        isEmail: (email) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email),
        isUUID: (uuid) => /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(uuid)
    }
};
EOF
```

---

**Créer .gitignore global :**

```bash
cat > .gitignore << 'EOF'
# Dependencies
node_modules/
package-lock.json

# Logs
*.log
logs/

# Environment
.env
.env.local
.env.*.local

# Coverage
coverage/
.nyc_output/

# Build
dist/
build/
*.tsbuildinfo

# OS
.DS_Store
Thumbs.db

# IDE
.vscode/
.idea/
*.swp
*.swo

# Temporary
*.tmp
*.temp
.cache/

# Husky
.husky/_
EOF
```

---

**Premier commit :**

```bash
git add .
git commit -m "chore: Initialize monorepo structure with services and packages"
```

---

### ÉTAPE 2 : Configurer Git LFS pour fichiers volumineux

**Git LFS** = Large File Storage (pour fichiers > 100 MB)

#### Pourquoi Git LFS ?

**Problème avec Git classique :**

```
Fichier de 500 MB -> 10 commits -> 5 GB dans le repository !
(chaque version est stockée)
```

**Avec Git LFS :**

```
Fichier de 500 MB -> Stocké ailleurs (serveur LFS)
Git stocke seulement un pointeur (quelques octets)
```

---

**Installer Git LFS :**

```bash
# Ubuntu/Debian
sudo apt install git-lfs

# macOS
brew install git-lfs

# Windows
# Télécharger depuis https://git-lfs.github.com/
```

---

**Initialiser Git LFS dans le repo :**

```bash
git lfs install
```

**Résultat :**
```
Updated Git hooks.
Git LFS initialized.
```

**Crée des hooks dans `.git/hooks/`**

---

**Configurer les types de fichiers à tracker avec LFS :**

```bash
# Fichiers vidéo
git lfs track "*.mp4"
git lfs track "*.mov"
git lfs track "*.avi"

# Fichiers images HD
git lfs track "*.psd"
git lfs track "*.ai"

# Datasets
git lfs track "*.csv"
git lfs track "*.parquet"

# Archives
git lfs track "*.zip"
git lfs track "*.tar.gz"
```

**Crée/modifie le fichier `.gitattributes` :**

```bash
cat .gitattributes
```

**Contenu :**

```
*.mp4 filter=lfs diff=lfs merge=lfs -text
*.mov filter=lfs diff=lfs merge=lfs -text
*.avi filter=lfs diff=lfs merge=lfs -text
*.psd filter=lfs diff=lfs merge=lfs -text
*.ai filter=lfs diff=lfs merge=lfs -text
*.csv filter=lfs diff=lfs merge=lfs -text
*.parquet filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text
*.tar.gz filter=lfs diff=lfs merge=lfs -text
```

**Explication :**

**`filter=lfs`** = Utiliser Git LFS pour ce type de fichier
**`diff=lfs`** = Git LFS gère les diffs
**`merge=lfs`** = Git LFS gère les merges
**`-text`** = Fichier binaire (pas de texte)

---

**Commiter .gitattributes :**

```bash
git add .gitattributes
git commit -m "chore: Configure Git LFS for large files"
```

---

**Ajouter des fichiers tests (simuler) :**

```bash
# Créer un fichier "lourd" simulé
dd if=/dev/zero of=assets/videos/demo.mp4 bs=1M count=50
# Crée un fichier de 50 MB

# Ajouter avec Git
git add assets/videos/demo.mp4
```

---

**Vérifier que LFS est utilisé :**

```bash
git lfs ls-files
```

**Résultat :**
```
abc123def4 * assets/videos/demo.mp4
```

---

**Commiter :**

```bash
git commit -m "feat(assets): Add demo video for onboarding"
```

---

**Voir la taille réelle dans Git :**

```bash
git show HEAD:assets/videos/demo.mp4
```

**Résultat (pointeur LFS) :**

```
version https://git-lfs.github.com/spec/v1
oid sha256:abc123...
size 52428800
```

**Git stocke seulement 3 lignes au lieu de 50 MB ! [OK]**

---

**Commandes Git LFS utiles :**

```bash
# Voir tous les fichiers LFS
git lfs ls-files

# Voir le statut LFS
git lfs status

# Télécharger manuellement les fichiers LFS
git lfs pull

# Voir l'espace utilisé
git lfs env
```

---

**Migrer des fichiers existants vers LFS :**

```bash
# Si tu as déjà commité des gros fichiers sans LFS
git lfs migrate import --include="*.mp4" --everything
```

---

### ÉTAPE 3 : Configurer CODEOWNERS

**CODEOWNERS** = Attribuer automatiquement des reviewers selon les fichiers modifiés

#### Créer le fichier CODEOWNERS

```bash
cat > .github/CODEOWNERS << 'EOF'
# ═══════════════════════════════════════════════════════════════
# CODEOWNERS - Ownership et Review Assignments
# ═══════════════════════════════════════════════════════════════
#
# Format: path/to/file @username @team
#
# Les utilisateurs/équipes listés seront automatiquement assignés
# comme reviewers sur les Pull Requests qui modifient ces fichiers.
#
# ═══════════════════════════════════════════════════════════════

# ───────────────────────────────────────────────────────────────
# GLOBAL OWNERS (par défaut)
# ───────────────────────────────────────────────────────────────

* @tech-lead @senior-dev

# ───────────────────────────────────────────────────────────────
# SERVICES - Ownership par équipe
# ───────────────────────────────────────────────────────────────

# Team Backend
/services/api-gateway/       @backend-team @api-team-lead
/services/auth-service/      @backend-team @security-team
/services/billing-service/   @backend-team @billing-team @finance-team

# Team Platform
/services/notification-service/  @platform-team
/services/analytics-service/     @platform-team @data-team

# ───────────────────────────────────────────────────────────────
# PACKAGES PARTAGÉS
# ───────────────────────────────────────────────────────────────

/packages/shared-ui/         @frontend-team @design-team
/packages/shared-utils/      @backend-team @frontend-team
/packages/shared-config/     @devops-team @tech-lead

# ───────────────────────────────────────────────────────────────
# INFRASTRUCTURE
# ───────────────────────────────────────────────────────────────

/infrastructure/terraform/   @devops-team @sre-team
/infrastructure/kubernetes/  @devops-team @sre-team
/infrastructure/docker/      @devops-team

# ───────────────────────────────────────────────────────────────
# DOCUMENTATION
# ───────────────────────────────────────────────────────────────

/docs/architecture/          @tech-lead @architects
/docs/api/                   @backend-team @tech-writer
/docs/deployment/            @devops-team @tech-writer

# ───────────────────────────────────────────────────────────────
# CONFIGURATION ET SÉCURITÉ
# ───────────────────────────────────────────────────────────────

# Fichiers sensibles (sécurité)
*.env.example                @security-team @tech-lead
.github/workflows/           @devops-team @tech-lead
CODEOWNERS                   @tech-lead

# Dependencies
package.json                 @tech-lead
package-lock.json            @tech-lead

# CI/CD
/.github/workflows/deploy*.yml  @devops-team @sre-team @tech-lead

# ───────────────────────────────────────────────────────────────
# EXEMPLES AVANCÉS
# ───────────────────────────────────────────────────────────────

# Tous les fichiers .js dans un dossier spécifique
/services/billing-service/**/*.js  @billing-team @senior-backend-dev

# Tests nécessitent review QA
**/*.test.js                 @qa-team

# Migrations DB nécessitent review senior
**/migrations/               @senior-backend-dev @dba-team

# Fichiers de config sensibles
**/secrets.yaml              @security-team @tech-lead
**/config.production.json    @devops-team @tech-lead
EOF
```

---

**Explication de la syntaxe :**

**`*`** = Tous les fichiers (wildcard)

**`/path/`** = Dossier spécifique

**`*.js`** = Tous les fichiers .js

**`**/*.js`** = Tous les .js récursivement

**`@username`** = Utilisateur GitHub

**`@org/team-name`** = Équipe GitHub (dans une organisation)

---

**Ordre de priorité :**

```
# Dernier match gagne !

* @tech-lead                        # Tous les fichiers
/services/billing-service/ @billing-team   # Sauf billing (override)
```

---

**Commiter CODEOWNERS :**

```bash
git add .github/CODEOWNERS
git commit -m "chore: Add CODEOWNERS for automatic review assignment"
```

---

**Sur GitHub, activer la protection :**

1. Settings -> Branches -> Edit protection rule pour `main`
2. Cocher : **Require review from Code Owners**
3. Save changes

**Effet : Impossible de merger une PR sans approbation des CODEOWNERS !**

---

### ÉTAPE 4 : Signer les commits avec GPG

**Commits signés** = Preuve cryptographique que c'est TOI qui as créé le commit

#### Pourquoi signer les commits ?

**Problème sans signature :**

```bash
# N'importe qui peut usurper ton identité
git config user.name "Linus Torvalds"
git config user.email "torvalds@linux-foundation.org"
git commit -m "I approve this"
# Commit paraît venir de Linus !
```

**Avec signature GPG :**

```
Commit vérifié cryptographiquement avec ta clé privée
GitHub affiche un badge "Verified" [OK]
```

---

**Installer GPG :**

```bash
# Ubuntu/Debian
sudo apt install gnupg

# macOS
brew install gnupg

# Windows
# Télécharger depuis https://www.gnupg.org/download/
```

---

**Générer une clé GPG :**

```bash
gpg --full-generate-key
```

**Répondre aux questions :**

```
1) RSA and RSA (default)
2) 4096 (taille de clé)
3) 0 (pas d'expiration) ou 1y (1 an)
4) y (confirmer)
5) Real name: Ton Nom
6) Email: ton.email@example.com (DOIT correspondre à Git)
7) Comment: GitHub signing key
8) O (OK)
9) Entrer une passphrase (IMPORTANT : retiens-la !)
```

---

**Lister les clés GPG :**

```bash
gpg --list-secret-keys --keyid-format=long
```

**Résultat :**

```
/home/user/.gnupg/pubring.kbx
-----------------------------
sec   rsa4096/ABC123DEF456 2024-12-16 [SC]
      ABCDEF1234567890ABCDEF1234567890ABCDEF12
uid           [ultimate] Ton Nom <ton.email@example.com>
ssb   rsa4096/GHI789JKL012 2024-12-16 [E]
```

**`ABC123DEF456`** = ID de ta clé (8-16 caractères)

**Copier l'ID complet (ligne avec ABCDEF...)**

---

**Exporter la clé publique :**

```bash
gpg --armor --export ABC123DEF456
```

**Résultat (clé publique) :**

```
-----BEGIN PGP PUBLIC KEY BLOCK-----

mQINBGaBC...
(plein de lignes)
...xyz123==
-----END PGP PUBLIC KEY BLOCK-----
```

**Copier TOUTE la sortie (de BEGIN à END inclus)**

---

**Ajouter la clé sur GitHub :**

1. GitHub -> Settings -> SSH and GPG keys
2. New GPG key
3. Coller la clé publique
4. Add GPG key

---

**Configurer Git pour signer automatiquement :**

```bash
# Spécifier la clé à utiliser
git config --global user.signingkey ABC123DEF456

# Signer tous les commits automatiquement
git config --global commit.gpgsign true

# Signer tous les tags
git config --global tag.gpgsign true
```

---

**Tester avec un commit signé :**

```bash
# Créer un fichier
echo "GPG Test" > test-gpg.txt
git add test-gpg.txt

# Commiter (sera signé automatiquement)
git commit -m "test: Verify GPG signing"
```

**Tu devras entrer ta passphrase GPG**

---

**Vérifier la signature :**

```bash
git log --show-signature -1
```

**Résultat :**

```
commit abc123def456 (HEAD -> main)
gpg: Signature made Mon Dec 16 16:00:00 2024 UTC
gpg:                using RSA key ABCDEF1234567890
gpg: Good signature from "Ton Nom <ton.email@example.com>" [ultimate]
Author: Ton Nom <ton.email@example.com>
Date:   Mon Dec 16 16:00:00 2024 +0000

    test: Verify GPG signing
```

**`Good signature`** = Signature valide [OK]

---

**Sur GitHub, le commit affiche :**

```
[OK] Verified
This commit was signed with a verified signature.
```

---

**Configurer GPG pour ne pas demander la passphrase à chaque fois :**

```bash
# Ajouter dans ~/.gnupg/gpg-agent.conf
echo "default-cache-ttl 3600" >> ~/.gnupg/gpg-agent.conf
echo "max-cache-ttl 86400" >> ~/.gnupg/gpg-agent.conf

# Redémarrer l'agent
gpg-connect-agent reloadagent /bye
```

**Passphrase gardée en cache pendant 1h (3600s)**

---

**Signer un tag :**

```bash
git tag -s v1.0.0 -m "Release version 1.0.0"
```

**`-s`** = Sign (signer le tag)

---

**Vérifier un tag signé :**

```bash
git tag -v v1.0.0
```

**Résultat :**

```
object abc123def456
type commit
tag v1.0.0
tagger Ton Nom <ton.email@example.com> 1702746000 +0000

Release version 1.0.0
gpg: Signature made ...
gpg: Good signature from "Ton Nom <ton.email@example.com>"
```

---

### ÉTAPE 5 : Hooks Git avancés

**Hooks Git** = Scripts exécutés automatiquement à certains moments

#### Types de hooks

**Client-side (local) :**
- `pre-commit` : Avant chaque commit
- `prepare-commit-msg` : Avant l'ouverture de l'éditeur de message
- `commit-msg` : Valider le message de commit
- `post-commit` : Après le commit
- `pre-push` : Avant chaque push

**Server-side (GitHub) :**
- `pre-receive`
- `update`
- `post-receive`

---

**Utiliser Husky pour gérer les hooks :**

```bash
# Installer Husky
npm install --save-dev husky

# Initialiser
npx husky install

# Créer le hook pre-commit
npx husky add .husky/pre-commit "npm run lint:all"
```

---

**Créer un hook commit-msg (valider le format) :**

```bash
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'
```

---

**Installer commitlint :**

```bash
npm install --save-dev @commitlint/cli @commitlint/config-conventional
```

---

**Configuration de commitlint :**

```bash
cat > .commitlintrc.json << 'EOF'
{
  "extends": ["@commitlint/config-conventional"],
  "rules": {
    "type-enum": [
      2,
      "always",
      [
        "feat",
        "fix",
        "docs",
        "style",
        "refactor",
        "perf",
        "test",
        "chore",
        "revert",
        "ci",
        "build"
      ]
    ],
    "scope-enum": [
      2,
      "always",
      [
        "api-gateway",
        "auth-service",
        "billing-service",
        "notification-service",
        "analytics-service",
        "shared-ui",
        "shared-utils",
        "shared-config",
        "infrastructure",
        "docs"
      ]
    ],
    "subject-max-length": [2, "always", 100],
    "subject-case": [2, "always", "lower-case"],
    "header-max-length": [2, "always", 120]
  }
}
EOF
```

---

**Tester le hook :**

```bash
# Essayer un mauvais message
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]

[X]   found 2 problems, 0 warnings
husky - commit-msg hook exited with code 1 (error)
```

**[X] Commit refusé !**

---

**Bon message :**

```bash
git commit -m "feat(api-gateway): Add rate limiting middleware"
```

**[OK] Commit accepté !**

---

**Hook pre-push (exécuter les tests) :**

```bash
cat > .husky/pre-push << 'EOF'
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

echo "[TEST] Running tests before push..."
npm run test:all

if [ $? -ne 0 ]; then
    echo "[X] Tests failed. Push aborted."
    exit 1
fi

echo "[OK] Tests passed. Pushing..."
EOF

chmod +x .husky/pre-push
```

---

**Hook prepare-commit-msg (ajouter issue number automatiquement) :**

```bash
cat > .husky/prepare-commit-msg << 'EOF'
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

COMMIT_MSG_FILE=$1
COMMIT_SOURCE=$2

# Si commit depuis une branche feature/ISSUE-123
BRANCH_NAME=$(git rev-parse --abbrev-ref HEAD)

if [[ $BRANCH_NAME =~ ^feature/([A-Z]+-[0-9]+) ]]; then
    ISSUE_NUMBER="${BASH_REMATCH[1]}"
    
    # Ajouter [ISSUE-123] au début du message
    COMMIT_MSG=$(cat "$COMMIT_MSG_FILE")
    echo "[$ISSUE_NUMBER] $COMMIT_MSG" > "$COMMIT_MSG_FILE"
fi
EOF

chmod +x .husky/prepare-commit-msg
```

**Résultat automatique :**

```bash
# Sur branche feature/PROJ-456
git commit -m "feat: Add new feature"

# Message devient automatiquement :
# [PROJ-456] feat: Add new feature
```

---

### ÉTAPE 6 : Git Worktree - Travailler sur plusieurs branches simultanément

**Problème :**

```
Tu travailles sur feature/auth
Urgence : Bug critique à fixer sur main
-> Tu dois stash, checkout main, fix, checkout feature, stash pop
```

**Solution : Git Worktree**

```
Un worktree = Un dossier séparé avec une branche différente
```

---

**Créer des worktrees :**

```bash
# Créer un worktree pour hotfix
git worktree add ../saas-platform-hotfix main

# Créer un worktree pour develop
git worktree add ../saas-platform-develop develop

# Créer un worktree pour une nouvelle feature
git worktree add ../saas-platform-feature-auth -b feature/improved-auth
```

**Structure des dossiers :**

```
/home/user/
├── saas-platform/              <- Main repo (branche main)
├── saas-platform-hotfix/       <- Worktree (branche main)
├── saas-platform-develop/      <- Worktree (branche develop)
└── saas-platform-feature-auth/ <- Worktree (branche feature/improved-auth)
```

---

**Lister les worktrees :**

```bash
git worktree list
```

**Résultat :**

```
/home/user/saas-platform               abc123d [main]
/home/user/saas-platform-hotfix        abc123d [main]
/home/user/saas-platform-develop       def456e [develop]
/home/user/saas-platform-feature-auth  ghi789f [feature/improved-auth]
```

---

**Travailler dans un worktree :**

```bash
# Aller dans le worktree hotfix
cd ../saas-platform-hotfix

# Tu es automatiquement sur la branche main
git branch
# * main

# Faire des modifications
echo "Fix critical bug" > hotfix.txt
git add hotfix.txt
git commit -m "hotfix: Fix critical security vulnerability"

# Pusher
git push origin main
```

---

**Pendant ce temps, dans le repo principal :**

```bash
cd ~/saas-platform

# Tu es toujours sur ta branche de travail
git branch
# * feature/auth-improvements
```

**Aucun conflit ! Tu peux travailler sur 2 branches en parallèle ! [BRAVO]**

---

**Supprimer un worktree :**

```bash
# Supprimer le dossier
rm -rf ../saas-platform-hotfix

# Nettoyer Git
git worktree prune
```

**Ou directement :**

```bash
git worktree remove ../saas-platform-hotfix
```

---

**Cas d'usage typiques :**

1. **Hotfix urgent pendant développement feature**
2. **Code review (checkout PR dans worktree séparé)**
3. **Comparaison de branches côte à côte**
4. **Tests sur plusieurs versions en parallèle**

---

### ÉTAPE 7 : Stratégie de release complexe

**Gérer les releases dans un monorepo multi-services**

#### Utiliser Lerna pour versioning

```bash
# Installer Lerna
npm install --save-dev lerna

# Initialiser
npx lerna init
```

---

**Configuration Lerna :**

```bash
cat > lerna.json << 'EOF'
{
  "version": "independent",
  "npmClient": "npm",
  "command": {
    "version": {
      "allowBranch": ["main", "develop"],
      "conventionalCommits": true,
      "message": "chore(release): Publish %s",
      "changelogPreset": "angular"
    },
    "publish": {
      "ignoreChanges": [
        "*.md",
        "*.test.js",
        "*.spec.js"
      ]
    }
  },
  "packages": [
    "services/*",
    "packages/*"
  ]
}
EOF
```

**Explication :**

**`"version": "independent"`** = Chaque package a sa propre version

```
services/api-gateway: v1.0.0
services/auth-service: v1.2.3
packages/shared-utils: v2.1.0
```

**`"conventionalCommits": true`** = Détecte automatiquement le type de version

```
feat: -> Minor (1.0.0 -> 1.1.0)
fix:  -> Patch (1.0.0 -> 1.0.1)
BREAKING CHANGE: -> Major (1.0.0 -> 2.0.0)
```

---

**Créer une release :**

```bash
# Lerna détecte les changements et propose les versions
npx lerna version
```

**Lerna demande pour chaque service modifié :**

```
? Select a new version for api-gateway (currently 1.0.0)
  Patch (1.0.1)
> Minor (1.1.0)
  Major (2.0.0)
  Custom Version
```

**Résultat :**

```
Changes:
 - api-gateway: 1.0.0 => 1.1.0
 - shared-utils: 1.0.0 => 1.0.1

? Are you sure you want to publish these packages? (Y/n)
```

---

**Lerna va automatiquement :**

1. Mettre à jour les `package.json`
2. Créer un CHANGELOG.md par package
3. Créer un commit
4. Créer des tags Git (`api-gateway@1.1.0`)
5. Pusher les tags

---

**Génération automatique de CHANGELOG :**

```bash
# Installer conventional-changelog
npm install --save-dev conventional-changelog-cli

# Générer le CHANGELOG
npx conventional-changelog -p angular -i CHANGELOG.md -s
```

**Résultat (CHANGELOG.md) :**

```markdown
# Changelog

## [1.1.0](https://github.com/org/repo/compare/v1.0.0...v1.1.0) (2024-12-16)

### Features

* **api-gateway:** Add rate limiting middleware ([abc123d](https://github.com/org/repo/commit/abc123d))
* **auth-service:** Implement OAuth2 support ([def456e](https://github.com/org/repo/commit/def456e))

### Bug Fixes

* **billing-service:** Fix invoice calculation ([ghi789f](https://github.com/org/repo/commit/ghi789f))

### BREAKING CHANGES

* **api-gateway:** Old rate limiting config format deprecated
```

---

**Script automatique de release :**

```bash
cat > scripts/release.sh << 'EOF'
#!/bin/bash

echo "[RAPIDE] Starting release process..."

# 1. Vérifier que la branche est propre
if [[ -n $(git status -s) ]]; then
    echo "[X] Working directory is not clean. Commit or stash changes first."
    exit 1
fi

# 2. Mettre à jour depuis origin
git pull origin main

# 3. Lancer les tests
echo "[TEST] Running tests..."
npm run test:all
if [ $? -ne 0 ]; then
    echo "[X] Tests failed. Aborting release."
    exit 1
fi

# 4. Bumper les versions avec Lerna
echo "[PACKAGE] Versioning packages..."
npx lerna version --conventional-commits --yes

# 5. Générer le CHANGELOG global
echo "[NOTE] Generating CHANGELOG..."
npx conventional-changelog -p angular -i CHANGELOG.md -s
git add CHANGELOG.md
git commit -m "docs: Update CHANGELOG for release"

# 6. Pusher
echo "^  Pushing to GitHub..."
git push --follow-tags

echo "[OK] Release complete!"
EOF

chmod +x scripts/release.sh
```

---

### ÉTAPE 8 : Git Attributes et Normalization

**`.gitattributes`** = Contrôler comment Git traite les fichiers

```bash
cat > .gitattributes << 'EOF'
# ═══════════════════════════════════════════════════════════════
# Git Attributes - File Handling Configuration
# ═══════════════════════════════════════════════════════════════

# ───────────────────────────────────────────────────────────────
# LINE ENDINGS
# ───────────────────────────────────────────────────────────────

# Auto-detect text files and normalize to LF
* text=auto

# Force LF for specific text files
*.js text eol=lf
*.json text eol=lf
*.md text eol=lf
*.yml text eol=lf
*.yaml text eol=lf

# Force CRLF for Windows batch files
*.bat text eol=crlf
*.cmd text eol=crlf

# ───────────────────────────────────────────────────────────────
# BINARY FILES (no line ending conversion)
# ───────────────────────────────────────────────────────────────

*.png binary
*.jpg binary
*.jpeg binary
*.gif binary
*.ico binary
*.mov binary
*.mp4 binary
*.mp3 binary
*.flv binary
*.fla binary
*.swf binary
*.gz binary
*.zip binary
*.7z binary
*.ttf binary
*.eot binary
*.woff binary
*.woff2 binary

# ───────────────────────────────────────────────────────────────
# DIFF STRATEGIES
# ───────────────────────────────────────────────────────────────

# Better diff for package.json (JSON)
package.json diff=json
package-lock.json -diff

# Better diff for images (show that they changed)
*.png diff=image
*.jpg diff=image

# ───────────────────────────────────────────────────────────────
# MERGE STRATEGIES
# ───────────────────────────────────────────────────────────────

# Always take ours for generated files
package-lock.json merge=ours

# ───────────────────────────────────────────────────────────────
# EXPORT-IGNORE (excluded from archives)
# ───────────────────────────────────────────────────────────────

.gitattributes export-ignore
.gitignore export-ignore
.github/ export-ignore
tests/ export-ignore
*.test.js export-ignore

# ───────────────────────────────────────────────────────────────
# LINGUIST (GitHub language detection)
# ───────────────────────────────────────────────────────────────

# Exclude vendored code from language statistics
lib/* linguist-vendored
node_modules/* linguist-vendored

# Mark as documentation
docs/* linguist-documentation
*.md linguist-documentation
EOF
```

**Explication des directives :**

**`text=auto`** = Auto-détection texte/binaire + normalisation LF

**`eol=lf`** = Force Line Feed (Unix)
**`eol=crlf`** = Force Carriage Return + Line Feed (Windows)

**`binary`** = Fichier binaire (pas de conversion)

**`diff=json`** = Utiliser un differ JSON (plus lisible)

**`merge=ours`** = En cas de conflit, toujours prendre notre version

**`export-ignore`** = Exclu de `git archive` (création de releases)

**`linguist-vendored`** = Exclu des statistiques de langage sur GitHub

---

**Définir des diff drivers personnalisés :**

```bash
# Configurer le driver JSON
git config --global diff.json.textconv 'python -m json.tool'
```

**Maintenant les diffs de JSON sont formatés ! [BRAVO]**

---

### [OK] TESTS DE VALIDATION

**1. Monorepo configuré**

```bash
ls -la
npm run test:all
```

- [ ] Structure monorepo créée
- [ ] Workspaces fonctionnels
- [ ] Tests passent globalement

---

**2. Git LFS**

```bash
git lfs ls-files
git lfs env
```

- [ ] LFS initialisé
- [ ] Fichiers volumineux trackés avec LFS
- [ ] Pointeurs dans Git (pas le fichier complet)

---

**3. CODEOWNERS**

- [ ] Fichier `.github/CODEOWNERS` créé
- [ ] Reviewers assignés automatiquement sur PR
- [ ] Protection activée sur GitHub

---

**4. GPG Signing**

```bash
git log --show-signature -1
```

- [ ] Clé GPG générée
- [ ] Ajoutée sur GitHub
- [ ] Commits signés automatiquement
- [ ] Badge "Verified" sur GitHub

---

**5. Hooks**

```bash
ls -la .husky/
git commit -m "bad message"
```

- [ ] Husky installé
- [ ] Hooks configurés (pre-commit, commit-msg, pre-push)
- [ ] Validation fonctionne

---

**6. Worktrees**

```bash
git worktree list
```

- [ ] Worktrees créés
- [ ] Travail simultané sur plusieurs branches

---

**7. Releases**

```bash
npx lerna version --dry-run
```

- [ ] Lerna configuré
- [ ] Versioning indépendant
- [ ] CHANGELOG généré automatiquement

---

**8. Attributes**

```bash
cat .gitattributes
```

- [ ] Line endings normalisés
- [ ] Fichiers binaires correctement identifiés
- [ ] Export-ignore configuré

---

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

**1. Monorepo**
- Workspaces NPM pour packages liés
- Lerna pour versioning indépendant
- Scripts globaux à la racine

**2. Git LFS**
- Pour fichiers > 100 MB
- Stocke un pointeur dans Git
- `git lfs track "*.mp4"`

**3. CODEOWNERS**
- Review automatique selon fichiers
- Format : `path @owner`
- Protection de branches

**4. GPG Signing**
- Preuve cryptographique d'identité
- Badge "Verified" sur GitHub
- `git config commit.gpgsign true`

**5. Hooks**
- Scripts automatiques
- Husky pour gestion
- Validation pre-commit/pre-push

**6. Worktrees**
- Branches multiples simultanées
- `git worktree add path branch`
- Idéal pour hotfix urgents

**7. Releases**
- Conventional Commits
- CHANGELOG automatique
- Tags sémantiques

**8. Attributes**
- Normalisation line endings
- Diff/merge strategies
- Export-ignore

---

## [COURS] CONCLUSION DE L'EXERCICE 4

**[OK] Félicitations ! Tu maîtrises la gestion de projets complexes !**

**Ce que tu as appris :**
- Monorepo avec NPM Workspaces et Lerna
- Git LFS pour fichiers volumineux
- CODEOWNERS pour review automatique
- Signatures GPG pour sécurité
- Hooks Git avancés avec Husky
- Worktrees pour multi-branches
- Releases complexes avec versioning
- Git attributes et normalization

**Compétences acquises :**
- [OK] Gestion monorepo (niveau avancé)
- [OK] Sécurité et validation (GPG, hooks)
- [OK] Workflows d'équipe complexes
- [OK] Release management
- [OK] Optimisations Git

**Temps moyen :** 5-6 heures

**Prochaine étape :** Exercice 5 - DevOps et pratiques d'entreprise (Expert) ! [ENTREPRISE]

---

Je termine avec l'exercice 5 (niveau expert) dans le prochain message.


# [ROUGE] EXERCICE 5 : DEVOPS ENTREPRISE ET GITOPS (EXPERT)

## [LISTE] ÉNONCÉ

### Contexte professionnel

Tu es Principal DevOps Engineer dans une entreprise Fortune 500 avec :
- 200+ développeurs répartis sur 15 équipes internationales
- 50+ microservices en production
- Infrastructure multi-cloud (AWS, Azure, GCP)
- Conformité SOC2, HIPAA, GDPR obligatoire
- Déploiements quotidiens avec zero-downtime
- SLA 99.99% (52 minutes de downtime/an maximum)

### Cahier des charges

**Mission : Implémenter une stratégie Git/GitOps d'entreprise complète**

**Défis à relever :**

1. **GitOps avec Infrastructure as Code**
   - Terraform, Kubernetes manifests versionnés
   - Reconciliation automatique
   - Drift detection

2. **Sécurité et Compliance**
   - Secrets management (Vault, SOPS)
   - Security scanning automatique
   - Audit trails et compliance
   - Branch protection avancée

3. **Scalabilité et Performance**
   - Shallow clones et partial clones
   - Sparse checkout
   - Git bundles pour DR

4. **Disaster Recovery**
   - Stratégie de backup
   - Point-in-time recovery
   - Multi-region replication

5. **Gouvernance**
   - Policies automatiques
   - Compliance as Code
   - Audit logging complet

**Contraintes :**
- Aucun secret en clair dans Git
- Audit trail complet
- Rollback < 5 minutes
- Zero-trust security model
- Temps estimé : 6-8 heures

---

## [OBJECTIF] OBJECTIFS PÉDAGOGIQUES

À la fin de cet exercice, tu sauras :

- [OK] Implémenter GitOps avec ArgoCD/Flux
- [OK] Gérer les secrets avec SOPS et Vault
- [OK] Mettre en place le scanning de sécurité
- [OK] Optimiser Git pour grandes équipes
- [OK] Stratégies de backup et DR
- [OK] Compliance et audit trails
- [OK] Gestion de permissions complexes
- [OK] Git à l'échelle (monorepos géants)
- [OK] Multi-repository orchestration
- [OK] Incident response avec Git

---

## [DOCS] PRÉREQUIS

- Exercices 1-4 terminés et maîtrisés
- Connaissance Kubernetes (bases)
- Connaissance Terraform (bases)
- Compréhension CI/CD avancée
- Linux/Bash intermédiaire

---

## [OK] SOLUTION COMPLÈTE

### ÉTAPE 1 : Architecture GitOps complète

**GitOps** = Infrastructure gérée par Git comme source de vérité

```
┌─────────────────────────────────────────────────────────────┐
│                      GIT REPOSITORIES                        │
├─────────────────────────────────────────────────────────────┤
│  app-repo/          infra-repo/         config-repo/        │
│  (Code)             (Terraform)         (K8s manifests)     │
└──────┬───────────────────┬────────────────────┬─────────────┘
       │                   │                    │
       v                   v                    v
┌──────────────────┐ ┌──────────────┐ ┌──────────────────┐
│   CI Pipeline    │ │   Terraform  │ │   ArgoCD/Flux    │
│   (Build/Test)   │ │   (Provision)│ │   (Deploy K8s)   │
└──────┬───────────┘ └──────┬───────┘ └──────┬───────────┘
       │                   │                    │
       v                   v                    v
┌─────────────────────────────────────────────────────────────┐
│                    INFRASTRUCTURE                            │
│  Kubernetes Clusters  |  Cloud Resources  |  Databases      │
└─────────────────────────────────────────────────────────────┘
```

---

**Créer la structure multi-repo :**

```bash
# Repository 1: Application Code
mkdir -p enterprise-app && cd enterprise-app
git init
git branch -M main

# Repository 2: Infrastructure (Terraform)
cd ..
mkdir -p enterprise-infra && cd enterprise-infra
git init
git branch -M main

# Repository 3: Kubernetes Configurations
cd ..
mkdir -p enterprise-k8s && cd enterprise-k8s
git init
git branch -M main

# Repository 4: Platform Configuration
cd ..
mkdir -p enterprise-platform && cd enterprise-platform
git init
git branch -M main
```

---

**Structure du repo infrastructure (Terraform) :**

```bash
cd enterprise-infra

mkdir -p environments/{dev,staging,prod}
mkdir -p modules/{networking,compute,database,monitoring}
mkdir -p policies
```

```bash
cat > environments/prod/main.tf << 'EOF'
# ═══════════════════════════════════════════════════════════════
# Production Infrastructure - Terraform
# ═══════════════════════════════════════════════════════════════

terraform {
  required_version = ">= 1.5"
  
  # Remote state avec versioning
  backend "s3" {
    bucket         = "enterprise-terraform-state"
    key            = "prod/terraform.tfstate"
    region         = "us-east-1"
    encrypt        = true
    dynamodb_table = "terraform-locks"
    
    # Versioning pour DR
    versioning = true
  }
  
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
    kubernetes = {
      source  = "hashicorp/kubernetes"
      version = "~> 2.20"
    }
  }
}

provider "aws" {
  region = var.aws_region
  
  # Assume role pour sécurité
  assume_role {
    role_arn = "arn:aws:iam::ACCOUNT_ID:role/TerraformExecutionRole"
  }
  
  default_tags {
    tags = {
      Environment = "production"
      ManagedBy   = "Terraform"
      CostCenter  = "platform-engineering"
      Compliance  = "SOC2-HIPAA-GDPR"
    }
  }
}

# ───────────────────────────────────────────────────────────────
# VPC et Networking
# ───────────────────────────────────────────────────────────────

module "vpc" {
  source = "../../modules/networking"
  
  environment         = "prod"
  vpc_cidr            = "10.0.0.0/16"
  availability_zones  = ["us-east-1a", "us-east-1b", "us-east-1c"]
  enable_nat_gateway  = true
  enable_vpn_gateway  = false
  
  # Compliance: Flow logs obligatoires
  enable_flow_logs = true
  flow_logs_retention_days = 90
  
  tags = {
    Name = "prod-vpc"
  }
}

# ───────────────────────────────────────────────────────────────
# EKS Cluster
# ───────────────────────────────────────────────────────────────

module "eks" {
  source = "../../modules/compute"
  
  cluster_name    = "prod-eks-cluster"
  cluster_version = "1.28"
  
  vpc_id          = module.vpc.vpc_id
  subnet_ids      = module.vpc.private_subnet_ids
  
  # Node groups avec autoscaling
  node_groups = {
    general = {
      desired_capacity = 3
      max_capacity     = 10
      min_capacity     = 3
      instance_types   = ["t3.xlarge"]
      
      labels = {
        role = "general"
      }
      
      taints = []
    }
    
    compute_optimized = {
      desired_capacity = 2
      max_capacity     = 8
      min_capacity     = 2
      instance_types   = ["c6i.2xlarge"]
      
      labels = {
        role = "compute"
      }
      
      taints = [{
        key    = "compute"
        value  = "true"
        effect = "NoSchedule"
      }]
    }
  }
  
  # Security: Pod security standards
  enable_pod_security_policy = true
  
  # Logging vers CloudWatch
  enabled_cluster_log_types = [
    "api",
    "audit",
    "authenticator",
    "controllerManager",
    "scheduler"
  ]
  
  tags = {
    Name = "prod-eks"
  }
}

# ───────────────────────────────────────────────────────────────
# RDS Database avec Multi-AZ
# ───────────────────────────────────────────────────────────────

module "database" {
  source = "../../modules/database"
  
  identifier = "prod-postgresql"
  engine     = "postgres"
  engine_version = "15.4"
  
  instance_class = "db.r6g.2xlarge"
  allocated_storage = 100
  
  # High availability
  multi_az               = true
  backup_retention_period = 35  # 35 jours pour compliance
  backup_window          = "03:00-04:00"
  maintenance_window     = "sun:04:00-sun:05:00"
  
  # Encryption obligatoire
  storage_encrypted = true
  kms_key_id        = aws_kms_key.rds.arn
  
  # Performance Insights pour monitoring
  enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"]
  performance_insights_enabled    = true
  
  # Deletion protection
  deletion_protection = true
  
  tags = {
    Name = "prod-database"
  }
}

# ───────────────────────────────────────────────────────────────
# Monitoring et Alerting
# ───────────────────────────────────────────────────────────────

module "monitoring" {
  source = "../../modules/monitoring"
  
  environment = "prod"
  
  # Prometheus pour métriques
  enable_prometheus = true
  
  # Grafana pour dashboards
  enable_grafana = true
  
  # Alertmanager pour alertes
  alertmanager_config = {
    slack_webhook   = var.slack_webhook_url
    pagerduty_key   = var.pagerduty_integration_key
    email_receivers = ["ops-team@enterprise.com"]
  }
  
  # SLO tracking
  slo_targets = {
    availability = 99.99
    latency_p95  = 200  # ms
    error_rate   = 0.1  # %
  }
}

# ───────────────────────────────────────────────────────────────
# Outputs
# ───────────────────────────────────────────────────────────────

output "eks_cluster_endpoint" {
  description = "Endpoint for EKS control plane"
  value       = module.eks.cluster_endpoint
  sensitive   = true
}

output "database_endpoint" {
  description = "RDS database endpoint"
  value       = module.database.endpoint
  sensitive   = true
}

output "vpc_id" {
  description = "VPC ID"
  value       = module.vpc.vpc_id
}
EOF
```

---

**Variables avec validation :**

```bash
cat > environments/prod/variables.tf << 'EOF'
variable "aws_region" {
  description = "AWS region for resources"
  type        = string
  default     = "us-east-1"
  
  validation {
    condition     = contains(["us-east-1", "us-west-2", "eu-west-1"], var.aws_region)
    error_message = "Region must be one of: us-east-1, us-west-2, eu-west-1"
  }
}

variable "environment" {
  description = "Environment name"
  type        = string
  default     = "prod"
  
  validation {
    condition     = var.environment == "prod"
    error_message = "This is the production configuration. Environment must be 'prod'."
  }
}

variable "slack_webhook_url" {
  description = "Slack webhook for alerts"
  type        = string
  sensitive   = true
}

variable "pagerduty_integration_key" {
  description = "PagerDuty integration key"
  type        = string
  sensitive   = true
}
EOF
```

---

**Commiter l'infrastructure :**

```bash
git add .
git commit -m "feat(infra): Add production infrastructure with Terraform

- Multi-AZ VPC with 3 availability zones
- EKS cluster with autoscaling node groups
- RDS PostgreSQL with Multi-AZ and 35-day backups
- Comprehensive monitoring with Prometheus/Grafana
- Encryption at rest and in transit
- Compliance tags (SOC2, HIPAA, GDPR)
- CloudWatch logging enabled

BREAKING CHANGE: Initial infrastructure setup"

git tag -s v1.0.0 -m "Initial production infrastructure"
```

---

### ÉTAPE 2 : Gestion des secrets avec SOPS et Vault

**Problème : NE JAMAIS commiter de secrets en clair dans Git !**

#### Option 1 : Mozilla SOPS (Secrets OPerationS)

**Installer SOPS :**

```bash
# Linux
wget https://github.com/mozilla/sops/releases/download/v3.8.1/sops-v3.8.1.linux.amd64
sudo mv sops-v3.8.1.linux.amd64 /usr/local/bin/sops
sudo chmod +x /usr/local/bin/sops

# macOS
brew install sops

# Windows
choco install sops
```

---

**Créer une clé GPG pour SOPS :**

```bash
gpg --full-generate-key
# Suivre les instructions (comme avant)

# Noter le Key ID
gpg --list-keys
```

---

**Configuration SOPS :**

```bash
cd enterprise-k8s

cat > .sops.yaml << 'EOF'
# ═══════════════════════════════════════════════════════════════
# SOPS Configuration
# ═══════════════════════════════════════════════════════════════

creation_rules:
  # Production secrets
  - path_regex: environments/prod/.*\.yaml$
    encrypted_regex: "^(data|stringData)$"
    pgp: "FINGERPRINT_GPG_KEY"  # Remplacer par ton fingerprint
    age: "age1xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"  # Age public key
    
  # Staging secrets
  - path_regex: environments/staging/.*\.yaml$
    encrypted_regex: "^(data|stringData)$"
    pgp: "FINGERPRINT_GPG_KEY"
    
  # Development secrets (moins strict)
  - path_regex: environments/dev/.*\.yaml$
    encrypted_regex: "^(data|stringData)$"
    pgp: "FINGERPRINT_GPG_KEY"
EOF
```

---

**Créer un secret Kubernetes :**

```bash
mkdir -p environments/prod/secrets

cat > environments/prod/secrets/database.yaml << 'EOF'
apiVersion: v1
kind: Secret
metadata:
  name: database-credentials
  namespace: production
type: Opaque
stringData:
  DB_HOST: "prod-postgresql.c9akrhsi3uaf.us-east-1.rds.amazonaws.com"
  DB_PORT: "5432"
  DB_NAME: "production_db"
  DB_USER: "app_user"
  DB_PASSWORD: "SuperSecretPassword123!"  # À CHIFFRER !
  DB_SSL_MODE: "require"
EOF
```

---

**Chiffrer le secret avec SOPS :**

```bash
sops -e environments/prod/secrets/database.yaml > environments/prod/secrets/database.enc.yaml
```

**Résultat (database.enc.yaml) :**

```yaml
apiVersion: v1
kind: Secret
metadata:
    name: database-credentials
    namespace: production
type: Opaque
stringData:
    DB_HOST: ENC[AES256_GCM,data:xE8...,iv:...,tag:...,type:str]
    DB_PORT: ENC[AES256_GCM,data:qR4...,iv:...,tag:...,type:str]
    DB_NAME: ENC[AES256_GCM,data:pL9...,iv:...,tag:...,type:str]
    DB_USER: ENC[AES256_GCM,data:mK3...,iv:...,tag:...,type:str]
    DB_PASSWORD: ENC[AES256_GCM,data:wN7...,iv:...,tag:...,type:str]
    DB_SSL_MODE: ENC[AES256_GCM,data:vM2...,iv:...,tag:...,type:str]
sops:
    kms: []
    gcp_kms: []
    azure_kv: []
    hc_vault: []
    age: []
    lastmodified: "2024-12-16T18:00:00Z"
    mac: ENC[AES256_GCM,data:abc123...,iv:...,tag:...,type:str]
    pgp:
        - created_at: "2024-12-16T18:00:00Z"
          enc: |
            -----BEGIN PGP MESSAGE-----
            ...
            -----END PGP MESSAGE-----
          fp: ABC123DEF456...
    encrypted_regex: ^(data|stringData)$
    version: 3.8.1
```

**[OK] Valeurs chiffrées ! Sûr de commiter dans Git !**

---

**Supprimer le fichier en clair :**

```bash
rm environments/prod/secrets/database.yaml
```

---

**Ajouter au .gitignore (ne JAMAIS commiter les fichiers déchiffrés) :**

```bash
cat >> .gitignore << 'EOF'

# SOPS - Never commit decrypted files
**/secrets/*.yaml
!**/secrets/*.enc.yaml
EOF
```

---

**Déchiffrer pour utilisation (en local uniquement) :**

```bash
# Déchiffrer temporairement
sops -d environments/prod/secrets/database.enc.yaml > /tmp/database.yaml

# Utiliser
kubectl apply -f /tmp/database.yaml

# Supprimer immédiatement
rm /tmp/database.yaml
```

---

**Automatiser avec un script :**

```bash
cat > scripts/apply-secrets.sh << 'EOF'
#!/bin/bash

# Script pour déchiffrer et appliquer les secrets chiffrés avec SOPS

set -e

ENVIRONMENT="${1:-prod}"
SECRETS_DIR="environments/${ENVIRONMENT}/secrets"

if [[ ! -d "$SECRETS_DIR" ]]; then
    echo "[X] Secrets directory not found: $SECRETS_DIR"
    exit 1
fi

echo "[DEVERROUILLE] Decrypting and applying secrets for environment: $ENVIRONMENT"

for encrypted_file in "$SECRETS_DIR"/*.enc.yaml; do
    if [[ -f "$encrypted_file" ]]; then
        echo "  [FICHIER] Processing: $(basename "$encrypted_file")"
        
        # Déchiffrer et appliquer directement
        sops -d "$encrypted_file" | kubectl apply -f -
        
        if [[ $? -eq 0 ]]; then
            echo "  [OK] Applied successfully"
        else
            echo "  [X] Failed to apply"
            exit 1
        fi
    fi
done

echo "[OK] All secrets applied successfully"
EOF

chmod +x scripts/apply-secrets.sh
```

---

**Commiter les secrets chiffrés :**

```bash
git add environments/prod/secrets/database.enc.yaml
git add .sops.yaml
git add scripts/apply-secrets.sh
git add .gitignore
git commit -m "feat(secrets): Add encrypted database credentials with SOPS

- Configure SOPS with GPG encryption
- Add encrypted production database credentials
- Add helper script for applying secrets
- Update .gitignore to prevent committing plaintext secrets"
```

---

#### Option 2 : HashiCorp Vault Integration

**Vault External Secrets Operator dans Kubernetes :**

```bash
cat > base/external-secrets-operator.yaml << 'EOF'
# ═══════════════════════════════════════════════════════════════
# External Secrets Operator - Vault Integration
# ═══════════════════════════════════════════════════════════════

apiVersion: v1
kind: Namespace
metadata:
  name: external-secrets
---
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: vault-backend
  namespace: production
spec:
  provider:
    vault:
      server: "https://vault.enterprise.internal:8200"
      path: "secret"
      version: "v2"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "production-app"
          serviceAccountRef:
            name: "external-secrets-sa"
---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: database-credentials
  namespace: production
spec:
  refreshInterval: 1h
  secretStoreRef:
    name: vault-backend
    kind: SecretStore
  
  target:
    name: database-credentials
    creationPolicy: Owner
  
  data:
    # Mapping Vault -> Kubernetes Secret
    - secretKey: DB_HOST
      remoteRef:
        key: production/database
        property: host
    
    - secretKey: DB_PORT
      remoteRef:
        key: production/database
        property: port
    
    - secretKey: DB_PASSWORD
      remoteRef:
        key: production/database
        property: password
EOF
```

**Avantage Vault :**
- [OK] Secrets jamais dans Git (même chiffrés)
- [OK] Rotation automatique
- [OK] Audit trail complet
- [OK] TTL et lease management

---

### ÉTAPE 3 : Security Scanning automatique

**Intégrer plusieurs scanners de sécurité dans le pipeline**

#### GitHub Actions avec security scanning

```bash
cd enterprise-app

cat > .github/workflows/security.yml << 'EOF'
# ═══════════════════════════════════════════════════════════════
# Security Scanning Pipeline
# ═══════════════════════════════════════════════════════════════

name: Security Scanning

on:
  push:
    branches: [ main, develop ]
  pull_request:
    branches: [ main ]
  schedule:
    # Scan quotidien à 2h du matin
    - cron: '0 2 * * *'

jobs:
  # ─────────────────────────────────────────────────────────────
  # SECRET SCANNING
  # ─────────────────────────────────────────────────────────────
  secret-scan:
    name: [SECURISE] Secret Detection
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0  # Full history pour détecter secrets dans l'historique
      
      - name: TruffleHog Secrets Scan
        uses: trufflesecurity/trufflehog@main
        with:
          path: ./
          base: ${{ github.event.repository.default_branch }}
          head: HEAD
          extra_args: --debug --only-verified

      - name: GitLeaks Scan
        uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}

  # ─────────────────────────────────────────────────────────────
  # DEPENDENCY SCANNING
  # ─────────────────────────────────────────────────────────────
  dependency-scan:
    name: [PACKAGE] Dependency Vulnerabilities
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18.x'
      
      - name: Install dependencies
        run: npm ci
      
      # NPM Audit
      - name: NPM Audit
        run: npm audit --audit-level=moderate
        continue-on-error: true
      
      # Snyk scanning
      - name: Run Snyk to check for vulnerabilities
        uses: snyk/actions/node@master
        env:
          SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
        with:
          args: --severity-threshold=high
      
      # OWASP Dependency Check
      - name: OWASP Dependency Check
        uses: dependency-check/Dependency-Check_Action@main
        with:
          project: 'enterprise-app'
          path: '.'
          format: 'HTML'
          out: 'reports'
      
      - name: Upload Dependency Check report
        uses: actions/upload-artifact@v3
        with:
          name: dependency-check-report
          path: reports/

  # ─────────────────────────────────────────────────────────────
  # CODE SCANNING (SAST)
  # ─────────────────────────────────────────────────────────────
  code-scan:
    name: [RECHERCHE] Static Code Analysis
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      # CodeQL (GitHub native)
      - name: Initialize CodeQL
        uses: github/codeql-action/init@v2
        with:
          languages: javascript
          queries: security-and-quality
      
      - name: Autobuild
        uses: github/codeql-action/autobuild@v2
      
      - name: Perform CodeQL Analysis
        uses: github/codeql-action/analyze@v2
      
      # SonarCloud
      - name: SonarCloud Scan
        uses: SonarSource/sonarcloud-github-action@master
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        with:
          args: >
            -Dsonar.projectKey=enterprise-app
            -Dsonar.organization=your-org
            -Dsonar.sources=src
            -Dsonar.tests=tests
            -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info

  # ─────────────────────────────────────────────────────────────
  # CONTAINER SCANNING
  # ─────────────────────────────────────────────────────────────
  container-scan:
    name: [DOCKER] Container Image Scan
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Build Docker image
        run: docker build -t enterprise-app:${{ github.sha }} .
      
      # Trivy scanner
      - name: Run Trivy vulnerability scanner
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: 'enterprise-app:${{ github.sha }}'
          format: 'sarif'
          output: 'trivy-results.sarif'
          severity: 'CRITICAL,HIGH'
      
      - name: Upload Trivy results to GitHub Security
        uses: github/codeql-action/upload-sarif@v2
        with:
          sarif_file: 'trivy-results.sarif'
      
      # Grype scanner
      - name: Anchore Grype Scan
        uses: anchore/scan-action@v3
        with:
          image: 'enterprise-app:${{ github.sha }}'
          fail-build: true
          severity-cutoff: high

  # ─────────────────────────────────────────────────────────────
  # LICENSE COMPLIANCE
  # ─────────────────────────────────────────────────────────────
  license-scan:
    name: [SCALES] License Compliance
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Setup Node.js
        uses: actions/setup-node@v3
        with:
          node-version: '18.x'
      
      - name: Install dependencies
        run: npm ci
      
      - name: License Checker
        run: |
          npx license-checker --production --json --out licenses.json
          npx license-checker --production \
            --onlyAllow 'MIT;Apache-2.0;BSD-2-Clause;BSD-3-Clause;ISC' \
            --failOn 'GPL;AGPL;LGPL'
      
      - name: Upload license report
        uses: actions/upload-artifact@v3
        with:
          name: license-report
          path: licenses.json

  # ─────────────────────────────────────────────────────────────
  # INFRASTRUCTURE SCANNING
  # ─────────────────────────────────────────────────────────────
  infra-scan:
    name: [CONSTRUCTION] IaC Security Scan
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
        with:
          repository: enterprise-infra
          path: infra
      
      # Checkov - Terraform scanner
      - name: Checkov IaC Scan
        uses: bridgecrewio/checkov-action@master
        with:
          directory: infra/
          framework: terraform
          output_format: sarif
          output_file_path: checkov-report.sarif
          quiet: true
          soft_fail: false
      
      # tfsec - Terraform security scanner
      - name: tfsec
        uses: aquasecurity/tfsec-action@v1.0.0
        with:
          working_directory: infra/
          soft_fail: false
      
      # Terrascan
      - name: Terrascan IaC scanner
        uses: tenable/terrascan-action@main
        with:
          iac_type: 'terraform'
          iac_dir: 'infra/'
          policy_type: 'aws'
          sarif_upload: true

  # ─────────────────────────────────────────────────────────────
  # SECURITY REPORTING
  # ─────────────────────────────────────────────────────────────
  security-report:
    name: [GRAPHIQUE] Generate Security Report
    runs-on: ubuntu-latest
    needs: [secret-scan, dependency-scan, code-scan, container-scan, license-scan]
    if: always()
    
    steps:
      - name: Download all artifacts
        uses: actions/download-artifact@v3
      
      - name: Generate consolidated report
        run: |
          echo "# Security Scan Report" > security-report.md
          echo "Date: $(date)" >> security-report.md
          echo "Commit: ${{ github.sha }}" >> security-report.md
          echo "" >> security-report.md
          
          # Ajouter les résultats de chaque scan
          echo "## Dependency Scan" >> security-report.md
          cat dependency-check-report/dependency-check-report.html >> security-report.md || echo "No vulnerabilities found" >> security-report.md
          
          # ... (autres scans)
      
      - name: Upload consolidated report
        uses: actions/upload-artifact@v3
        with:
          name: security-report
          path: security-report.md
      
      - name: Notify security team
        if: failure()
        uses: slackapi/slack-github-action@v1
        with:
          webhook-url: ${{ secrets.SLACK_SECURITY_WEBHOOK }}
          payload: |
            {
              "text": "[ALERTE] Security scan failed for ${{ github.repository }}",
              "blocks": [
                {
                  "type": "section",
                  "text": {
                    "type": "mrkdwn",
                    "text": "*Security Scan Alert*\nRepository: ${{ github.repository }}\nCommit: ${{ github.sha }}\nBranch: ${{ github.ref }}\n\n[X] One or more security scans failed. Review the results immediately."
                  }
                }
              ]
            }
EOF
```

---

**Configuration de policies (OPA - Open Policy Agent) :**

```bash
cat > .github/workflows/policy-check.yml << 'EOF'
# ═══════════════════════════════════════════════════════════════
# Policy as Code - OPA Validation
# ═══════════════════════════════════════════════════════════════

name: Policy Validation

on: [pull_request]

jobs:
  policy-check:
    name: [LISTE] Policy Compliance
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
      
      - name: Install OPA
        run: |
          curl -L -o opa https://openpolicyagent.org/downloads/latest/opa_linux_amd64
          chmod +x opa
          sudo mv opa /usr/local/bin/
      
      - name: Validate against policies
        run: |
          # Validate Terraform files
          opa test policies/ -v
          
          # Validate Kubernetes manifests
          find . -name "*.yaml" -type f | while read file; do
            echo "Validating $file"
            opa eval -d policies/kubernetes.rego -i "$file" "data.kubernetes.deny"
          done
EOF
```

---

**Exemple de policy OPA :**

```bash
mkdir -p policies

cat > policies/kubernetes.rego << 'EOF'
package kubernetes

# ═══════════════════════════════════════════════════════════════
# Kubernetes Security Policies
# ═══════════════════════════════════════════════════════════════

# ───────────────────────────────────────────────────────────────
# DENY: Containers running as root
# ───────────────────────────────────────────────────────────────

deny[msg] {
    input.kind == "Pod"
    container := input.spec.containers[_]
    not container.securityContext.runAsNonRoot
    msg := sprintf("Container '%s' must not run as root", [container.name])
}

deny[msg] {
    input.kind == "Deployment"
    container := input.spec.template.spec.containers[_]
    not container.securityContext.runAsNonRoot
    msg := sprintf("Container '%s' in deployment must not run as root", [container.name])
}

# ───────────────────────────────────────────────────────────────
# DENY: Privileged containers
# ───────────────────────────────────────────────────────────────

deny[msg] {
    input.kind == "Pod"
    container := input.spec.containers[_]
    container.securityContext.privileged == true
    msg := sprintf("Container '%s' cannot be privileged", [container.name])
}

# ───────────────────────────────────────────────────────────────
# DENY: hostNetwork usage
# ───────────────────────────────────────────────────────────────

deny[msg] {
    input.spec.hostNetwork == true
    msg := "Pods cannot use hostNetwork"
}

# ───────────────────────────────────────────────────────────────
# DENY: Missing resource limits
# ───────────────────────────────────────────────────────────────

deny[msg] {
    input.kind == "Deployment"
    container := input.spec.template.spec.containers[_]
    not container.resources.limits
    msg := sprintf("Container '%s' must have resource limits", [container.name])
}

# ───────────────────────────────────────────────────────────────
# DENY: Using 'latest' tag
# ───────────────────────────────────────────────────────────────

deny[msg] {
    container := input.spec.containers[_]
    endswith(container.image, ":latest")
    msg := sprintf("Container '%s' cannot use 'latest' tag", [container.name])
}

# ───────────────────────────────────────────────────────────────
# WARN: Missing liveness/readiness probes
# ───────────────────────────────────────────────────────────────

warn[msg] {
    input.kind == "Deployment"
    container := input.spec.template.spec.containers[_]
    not container.livenessProbe
    msg := sprintf("Container '%s' should have a livenessProbe", [container.name])
}

warn[msg] {
    input.kind == "Deployment"
    container := input.spec.template.spec.containers[_]
    not container.readinessProbe
    msg := sprintf("Container '%s' should have a readinessProbe", [container.name])
}
EOF
```

---

### ÉTAPE 4 : Optimisation Git pour grandes équipes

**Techniques d'optimisation pour monorepos géants (> 10 GB)**

#### Shallow Clone - Cloner seulement l'historique récent

```bash
# Clone normal
git clone https://github.com/enterprise/monorepo.git
# Télécharge TOUT l'historique (peut être 10+ GB)

# Shallow clone (1 commit)
git clone --depth 1 https://github.com/enterprise/monorepo.git
# Télécharge seulement le dernier commit (peut être 100 MB)

# Shallow clone (50 derniers commits)
git clone --depth 50 https://github.com/enterprise/monorepo.git
```

---

**Configurer shallow clone dans CI/CD :**

```yaml
# GitHub Actions
- uses: actions/checkout@v3
  with:
    fetch-depth: 1  # Shallow clone
```

---

**Approfondir un shallow clone (si besoin) :**

```bash
# Récupérer plus d'historique
git fetch --deepen=100  # 100 commits de plus

# Récupérer tout l'historique
git fetch --unshallow
```

---

#### Partial Clone - Cloner seulement certains objets

**Git 2.17+ feature : Partial clone**

```bash
# Clone sans les blobs (fichiers)
git clone --filter=blob:none https://github.com/enterprise/monorepo.git
# Télécharge seulement l'arbre et les commits, pas les fichiers

# Clone sans les gros fichiers (> 1 MB)
git clone --filter=blob:limit=1m https://github.com/enterprise/monorepo.git

# Clone sans l'historique des blobs
git clone --filter=tree:0 https://github.com/enterprise/monorepo.git
```

**Les fichiers sont téléchargés à la demande (lazy loading)**

---

#### Sparse Checkout - Cloner seulement certains dossiers

**Pour monorepo : Checkout seulement ton service**

```bash
# Initialiser sparse checkout
git clone --filter=blob:none --no-checkout https://github.com/enterprise/monorepo.git
cd monorepo

# Activer sparse checkout
git sparse-checkout init --cone

# Sélectionner les dossiers à checkout
git sparse-checkout set services/api-gateway packages/shared-utils

# Checkout
git checkout main
```

**Résultat : Seulement `services/api-gateway` et `packages/shared-utils` sont présents !**

---

**Configuration sparse-checkout avancée :**

```bash
cat > .git/info/sparse-checkout << 'EOF'
# Checkout seulement ces patterns

# Service principal
/services/api-gateway/

# Packages utilisés
/packages/shared-utils/
/packages/shared-config/

# Configuration racine
/package.json
/package-lock.json
/.gitignore
/README.md

# CI/CD du service
/.github/workflows/api-gateway-*.yml
EOF

git sparse-checkout reapply
```

---

#### Git Maintenance - Optimisation automatique

```bash
# Activer maintenance automatique
git maintenance start

# Configuration de maintenance
git config maintenance.auto true
git config maintenance.strategy incremental
```

**Git va automatiquement :**
- Compresser les objets (gc)
- Optimiser les pack files
- Nettoyer les refs
- Mettre à jour les commit-graph

---

**Maintenance manuelle :**

```bash
# Garbage collection agressive
git gc --aggressive --prune=now

# Optimiser le repository
git repack -a -d -f --depth=250 --window=250

# Mettre à jour commit-graph
git commit-graph write --reachable --changed-paths
```

---

#### Git LFS avec Prune - Nettoyer les vieux fichiers

```bash
# Lister les fichiers LFS
git lfs ls-files

# Nettoyer les fichiers LFS non référencés
git lfs prune

# Nettoyer les fichiers LFS de plus de 30 jours
git lfs prune --verify-remote --recent 30
```

---

### ÉTAPE 5 : Disaster Recovery et Backup Strategy

**Stratégie de backup complète pour Git**

#### Backup automatique avec Git Bundle

**Git Bundle** = Archive portable d'un repository

```bash
cat > scripts/backup-repository.sh << 'EOF'
#!/bin/bash

# ═══════════════════════════════════════════════════════════════
# Git Repository Backup Script
# ═══════════════════════════════════════════════════════════════

set -e

REPO_PATH="${1:-.}"
BACKUP_DIR="${2:-/backup/git}"
DATE=$(date +%Y%m%d_%H%M%S)
REPO_NAME=$(basename "$REPO_PATH")

echo "[SYNC] Starting backup for repository: $REPO_NAME"

# Créer le dossier de backup
mkdir -p "$BACKUP_DIR"

# ───────────────────────────────────────────────────────────────
# 1. Git Bundle (repository complet)
# ───────────────────────────────────────────────────────────────

echo "[PACKAGE] Creating git bundle..."
cd "$REPO_PATH"

git bundle create "$BACKUP_DIR/${REPO_NAME}_${DATE}.bundle" --all

if [[ $? -eq 0 ]]; then
    echo "[OK] Bundle created: ${REPO_NAME}_${DATE}.bundle"
else
    echo "[X] Bundle creation failed"
    exit 1
fi

# ───────────────────────────────────────────────────────────────
# 2. Backup de configuration
# ───────────────────────────────────────────────────────────────

echo "[CONFIG]  Backing up Git configuration..."

tar -czf "$BACKUP_DIR/${REPO_NAME}_config_${DATE}.tar.gz" \
    .git/config \
    .git/hooks/ \
    .gitattributes \
    .gitignore 2>/dev/null || true

# ───────────────────────────────────────────────────────────────
# 3. Git LFS objects (si présents)
# ───────────────────────────────────────────────────────────────

if [[ -d .git/lfs ]]; then
    echo "[DOSSIER] Backing up Git LFS objects..."
    tar -czf "$BACKUP_DIR/${REPO_NAME}_lfs_${DATE}.tar.gz" .git/lfs/
fi

# ───────────────────────────────────────────────────────────────
# 4. Métadonnées du backup
# ───────────────────────────────────────────────────────────────

cat > "$BACKUP_DIR/${REPO_NAME}_${DATE}.metadata" << METADATA
Repository: $REPO_NAME
Backup Date: $(date)
Git Version: $(git --version)
Branches: $(git branch -a | wc -l)
Tags: $(git tag | wc -l)
Latest Commit: $(git rev-parse HEAD)
Latest Commit Message: $(git log -1 --pretty=%B)
Backup Size: $(du -h "$BACKUP_DIR/${REPO_NAME}_${DATE}.bundle" | cut -f1)
METADATA

echo "[LISTE] Metadata saved"

# ───────────────────────────────────────────────────────────────
# 5. Upload vers S3 (stockage distant)
# ───────────────────────────────────────────────────────────────

if command -v aws &> /dev/null; then
    echo "[CLOUD]  Uploading to S3..."
    
    aws s3 cp "$BACKUP_DIR/${REPO_NAME}_${DATE}.bundle" \
        "s3://enterprise-git-backups/$REPO_NAME/" \
        --storage-class GLACIER_IR  # Instant Retrieval for DR
    
    aws s3 cp "$BACKUP_DIR/${REPO_NAME}_${DATE}.metadata" \
        "s3://enterprise-git-backups/$REPO_NAME/"
    
    echo "[OK] Uploaded to S3"
fi

# ───────────────────────────────────────────────────────────────
# 6. Nettoyer les anciens backups (garder 30 jours)
# ───────────────────────────────────────────────────────────────

echo "[NETTOYAGE] Cleaning old backups..."
find "$BACKUP_DIR" -name "${REPO_NAME}_*.bundle" -mtime +30 -delete
find "$BACKUP_DIR" -name "${REPO_NAME}_*.tar.gz" -mtime +30 -delete
find "$BACKUP_DIR" -name "${REPO_NAME}_*.metadata" -mtime +30 -delete

echo "[OK] Backup completed successfully!"
echo "Backup location: $BACKUP_DIR"
EOF

chmod +x scripts/backup-repository.sh
```

---

**Restaurer depuis un bundle :**

```bash
cat > scripts/restore-repository.sh << 'EOF'
#!/bin/bash

# ═══════════════════════════════════════════════════════════════
# Git Repository Restore Script
# ═══════════════════════════════════════════════════════════════

set -e

BUNDLE_PATH="$1"
RESTORE_PATH="${2:-./restored-repo}"

if [[ ! -f "$BUNDLE_PATH" ]]; then
    echo "[X] Bundle file not found: $BUNDLE_PATH"
    exit 1
fi

echo "[SYNC] Restoring repository from bundle..."

# Vérifier l'intégrité du bundle
echo "[RECHERCHE] Verifying bundle integrity..."
git bundle verify "$BUNDLE_PATH"

if [[ $? -ne 0 ]]; then
    echo "[X] Bundle verification failed"
    exit 1
fi

# Cloner depuis le bundle
echo "[PACKAGE] Cloning from bundle..."
git clone "$BUNDLE_PATH" "$RESTORE_PATH"

cd "$RESTORE_PATH"

# Vérifier les branches
echo "[HERB] Branches restored:"
git branch -a

# Vérifier les tags
echo "[LABEL]  Tags restored:"
git tag

echo "[OK] Repository restored successfully to: $RESTORE_PATH"
EOF

chmod +x scripts/restore-repository.sh
```

---

**Automatiser les backups avec cron :**

```bash
# Ajouter au crontab
crontab -e

# Backup quotidien à 2h du matin
0 2 * * * /path/to/scripts/backup-repository.sh /path/to/repo /backup/git >> /var/log/git-backup.log 2>&1
```

---

#### Point-in-Time Recovery avec Git Reflog

**Récupérer un état à une date précise :**

```bash
cat > scripts/point-in-time-restore.sh << 'EOF'
#!/bin/bash

# ═══════════════════════════════════════════════════════════════
# Point-in-Time Recovery
# ═══════════════════════════════════════════════════════════════

TARGET_DATE="$1"  # Format: "2024-12-16 14:30:00"

if [[ -z "$TARGET_DATE" ]]; then
    echo "Usage: $0 'YYYY-MM-DD HH:MM:SS'"
    exit 1
fi

echo "[HEURE] Recovering repository state at: $TARGET_DATE"

# Trouver le commit le plus proche de la date
COMMIT=$(git rev-list -1 --before="$TARGET_DATE" HEAD)

if [[ -z "$COMMIT" ]]; then
    echo "[X] No commit found before $TARGET_DATE"
    exit 1
fi

echo "[IMPORTANT] Found commit: $COMMIT"
git log -1 --pretty=fuller $COMMIT

# Créer une branche de recovery
BRANCH_NAME="recovery-$(date +%Y%m%d_%H%M%S)"
git checkout -b "$BRANCH_NAME" "$COMMIT"

echo "[OK] Recovery branch created: $BRANCH_NAME"
echo "Current state restored to: $TARGET_DATE"
EOF

chmod +x scripts/point-in-time-restore.sh
```

---

### ÉTAPE 6 : Multi-Region Replication

**Stratégie de réplication pour haute disponibilité**

```bash
cat > scripts/setup-multi-region.sh << 'EOF'
#!/bin/bash

# ═══════════════════════════════════════════════════════════════
# Multi-Region Git Replication Setup
# ═══════════════════════════════════════════════════════════════

set -e

# Configuration
PRIMARY_REGION="us-east-1"
SECONDARY_REGIONS=("eu-west-1" "ap-southeast-1")
REPO_NAME="enterprise-monorepo"

echo "[MONDE] Setting up multi-region replication for $REPO_NAME"

# ───────────────────────────────────────────────────────────────
# Ajouter des remotes pour chaque région
# ───────────────────────────────────────────────────────────────

git remote add primary "git@github-${PRIMARY_REGION}:enterprise/${REPO_NAME}.git"

for region in "${SECONDARY_REGIONS[@]}"; do
    echo "[RESEAU] Adding remote for region: $region"
    git remote add "mirror-$region" "git@github-${region}:enterprise/${REPO_NAME}.git"
done

# ───────────────────────────────────────────────────────────────
# Configurer push automatique vers toutes les régions
# ───────────────────────────────────────────────────────────────

git config --add remote.all.url "$(git remote get-url primary)"

for region in "${SECONDARY_REGIONS[@]}"; do
    git config --add remote.all.url "$(git remote get-url mirror-$region)"
done

echo "[OK] Multi-region setup complete"
echo "Push to all regions with: git push all --all"
EOF

chmod +x scripts/setup-multi-region.sh
```

---

**GitHub Actions pour réplication automatique :**

```yaml
name: Multi-Region Replication

on:
  push:
    branches: [ main ]

jobs:
  replicate:
    name: [MONDE] Replicate to all regions
    runs-on: ubuntu-latest
    
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0
      
      - name: Setup SSH
        uses: webfactory/ssh-agent@v0.8.0
        with:
          ssh-private-key: |
            ${{ secrets.SSH_KEY_US_EAST_1 }}
            ${{ secrets.SSH_KEY_EU_WEST_1 }}
            ${{ secrets.SSH_KEY_AP_SOUTHEAST_1 }}
      
      - name: Push to US-EAST-1 (primary)
        run: |
          git remote add us-east-1 git@github-us-east-1:enterprise/monorepo.git
          git push us-east-1 --all --tags
      
      - name: Push to EU-WEST-1 (mirror)
        run: |
          git remote add eu-west-1 git@github-eu-west-1:enterprise/monorepo.git
          git push eu-west-1 --all --tags
      
      - name: Push to AP-SOUTHEAST-1 (mirror)
        run: |
          git remote add ap-southeast-1 git@github-ap-southeast-1:enterprise/monorepo.git
          git push ap-southeast-1 --all --tags
```

---

### [OK] TESTS DE VALIDATION

**1. GitOps Infrastructure**

```bash
cd enterprise-infra
terraform validate
```

- [ ] Terraform configuré avec remote state
- [ ] Modules structurés correctement
- [ ] Tags de compliance présents

---

**2. Secrets Management**

```bash
cd enterprise-k8s
sops -d environments/prod/secrets/database.enc.yaml
```

- [ ] SOPS configuré et fonctionnel
- [ ] Secrets chiffrés
- [ ] .gitignore protège les fichiers déchiffrés

---

**3. Security Scanning**

```bash
cd enterprise-app
git push origin feature/test
```

- [ ] Workflows de sécurité exécutés
- [ ] Scans multiples (secrets, dependencies, code, containers)
- [ ] Policies OPA validées

---

**4. Git Optimization**

```bash
# Tester shallow clone
git clone --depth 1 https://github.com/test/repo.git

# Tester sparse checkout
git sparse-checkout set services/api-gateway
```

- [ ] Shallow clone fonctionne
- [ ] Sparse checkout opérationnel
- [ ] Maintenance configurée

---

**5. Backup & Recovery**

```bash
./scripts/backup-repository.sh .
./scripts/restore-repository.sh backup.bundle ./test-restore
```

- [ ] Backup bundle créé
- [ ] Restauration fonctionnelle
- [ ] Métadonnées générées

---

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

**1. GitOps**
- Infrastructure as Code versionnée
- ArgoCD/Flux pour déploiement K8s
- Reconciliation automatique

**2. Secrets**
- SOPS pour chiffrement
- Vault pour stockage
- Jamais de secrets en clair

**3. Security**
- Scanning multi-couches
- Policies as Code (OPA)
- Audit trails complets

**4. Performance**
- Shallow clone pour CI/CD
- Sparse checkout pour monorepos
- Partial clone pour gros repos

**5. Disaster Recovery**
- Git bundles pour backup
- Point-in-time recovery
- Multi-region replication

**6. Compliance**
- Tags obligatoires
- Protected branches
- Signed commits
- Audit logging

---

## [COURS] CONCLUSION DE L'EXERCICE 5

**[BRAVO] FÉLICITATIONS ! Tu maîtrises Git au niveau Expert Enterprise !**

**Ce que tu as appris :**
- GitOps complet avec IaC
- Gestion sécurisée des secrets (SOPS, Vault)
- Security scanning automatisé multi-couches
- Optimisations Git pour grande échelle
- Disaster Recovery et stratégies de backup
- Compliance et gouvernance
- Multi-region replication
- Policies as Code

**Compétences niveau Expert acquises :**
- [OK] GitOps et Infrastructure as Code
- [OK] Sécurité niveau entreprise
- [OK] Performance à grande échelle
- [OK] Disaster Recovery
- [OK] Compliance SOC2/HIPAA/GDPR
- [OK] Multi-cloud et multi-region
- [OK] Automation avancée

**Tu es maintenant capable de :**
- [ENTREPRISE] Gérer Git dans une entreprise Fortune 500
- [VERROUILLE] Sécuriser un repository à tous les niveaux
- [HAUSSE] Scaler Git pour 200+ développeurs
- [MONDE] Architecturer une solution multi-region
- [RAPIDE] Implémenter GitOps en production
- [SECURITE] Assurer compliance et audit

**Temps total des 5 exercices :** 20-25 heures

---

## [TROPHEE] CERTIFICATION FINALE

**Tu as complété les 5 exercices Git/GitHub !**

**Niveau atteint :** Expert Git & GitHub

**Résumé des compétences :**

**Exercice 1 (Débutant) :**
- Fondamentaux Git
- Branches et merges
- GitHub basics

**Exercice 2 (Intermédiaire) :**
- Git Flow
- Pull Requests
- Collaboration en équipe

**Exercice 3 (Intermédiaire-Avancé) :**
- Git avancé (stash, rebase, cherry-pick)
- CI/CD avec GitHub Actions
- Submodules

**Exercice 4 (Avancé) :**
- Monorepos complexes
- Git LFS
- GPG signing
- Hooks avancés

**Exercice 5 (Expert) :**
- GitOps entreprise
- Security à tous les niveaux
- Performance et scalabilité
- Disaster Recovery

---

## [DOCS] PROCHAINES ÉTAPES

**Pour aller encore plus loin :**

1. **Contribuer à des projets open source**
   - Appliquer tes compétences sur de vrais projets
   - Collaborer avec la communauté

2. **Approfondir GitOps**
   - ArgoCD avancé
   - Flux CD
   - Progressive delivery (Flagger, Argo Rollouts)

3. **Explorer Git internals**
   - Comprendre le format des objets Git
   - Écrire des scripts Git custom
   - Optimisations de bas niveau

4. **Certifications**
   - GitHub Actions certification
   - CKA (Certified Kubernetes Administrator)
   - AWS DevOps Professional

---

**Merci d'avoir suivi ce tutoriel ultra-détaillé ! [MERCI]**

**Bon courage dans ta carrière de développeur ! [RAPIDE]**