Git pour data analyst : les 8 situations qui font vraiment mal (et comment en sortir)
Le problème avec Git chez les data analysts n'est pas la commande commit. C'est ce qui arrive après : un notebook en conflit et illisible, un push rejeté avec un message hostile, un fichier de données de 300 Mo devenu impossible à retirer de l'historique.
Voici les 8 situations qui bloquent réellement, avec la sortie de secours pour chacune.
Situation 1 : le push est refusé
! [rejected] main -> main (fetch first)
error: failed to push some refs
Traduction : quelqu'un a poussé avant vous. Votre historique local et le distant ont divergé.
git pull --rebase
# resoudre les eventuels conflits, puis
git push
--rebase rejoue vos commits au-dessus de ceux des autres, au lieu de créer un commit de merge parasite. Sur un dépôt à plusieurs analystes, ça garde un historique linéaire et lisible.
Le réflexe à installer : git pull --rebase avant de commencer à travailler, pas seulement quand le push échoue.
Situation 2 : un merge conflict dans un notebook
Le pire cas, parce que le .ipynb est un JSON qui contient les sorties, les numéros d'exécution et les métadonnées. Deux personnes qui exécutent le même notebook produisent un conflit même sans changer une ligne de code.
La solution durable : ne jamais versionner les sorties.
uv add --dev nbstripout
uv run nbstripout --install
nbstripout s'installe comme filtre Git : il retire automatiquement les sorties et les compteurs d'exécution au moment du commit. Le fichier sur disque garde ses sorties, le fichier dans Git est propre. Les conflits chutent de 80 à 90 %.
Pour un conflit déjà en cours sur un notebook :
git checkout --ours notebooks/analyse.ipynb # garder ma version
git checkout --theirs notebooks/analyse.ipynb # garder la sienne
git add notebooks/analyse.ipynb
Résoudre un JSON de notebook à la main est presque toujours une perte de temps : choisissez une version, réappliquez la modif manquante dans Jupyter.
Situation 3 : j'ai committé un fichier de données de 300 Mo
Le git rm ne suffit pas. Le blob reste dans l'historique, et le dépôt reste lourd pour tout le monde, à chaque clone.
Si le commit n'est pas encore poussé :
git reset --soft HEAD~1 # annule le commit, garde les fichiers
git restore --staged data/gros_fichier.csv
S'il est déjà poussé, l'historique doit être réécrit avec git filter-repo, ce qui impose une coordination avec toute l'équipe (chacun doit recloner). C'est une opération à ne pas improviser.
La prévention coûte une ligne :
data/raw/*
data/processed/*
*.csv
*.parquet
!data/**/.gitkeep
Le .gitignore complet pour un projet data est détaillé dans structurer un projet Python data.
Situation 4 : j'ai committé un mot de passe
Cas particulier de la situation 3, avec une différence majeure : retirer le fichier ne règle rien. Dès qu'un secret est parti sur un dépôt distant, il est compromis.
L'ordre correct des opérations :
- Révoquer la clé ou changer le mot de passe. Immédiatement.
- Générer un nouveau secret.
- Le sortir du code, dans une variable d'environnement.
- Nettoyer l'historique si le dépôt est privé et l'équipe petite.
Inverser 1 et 4 est l'erreur classique : pendant que vous nettoyez l'historique, la clé est toujours valide.
Situation 5 : je veux annuler mon dernier commit
Trois cas, trois commandes différentes. La confusion entre les trois est la première cause de travail perdu.
| Besoin | Commande | Effet sur vos fichiers |
|---|---|---|
| Annuler le commit, garder les modifs | git reset --soft HEAD~1 |
Intactes, prêtes à recommitter |
| Annuler le commit et le staging | git reset HEAD~1 |
Intactes, non stagées |
| Tout jeter | git reset --hard HEAD~1 |
Détruites définitivement |
| Annuler un commit déjà poussé | git revert <sha> |
Nouveau commit inverse, historique préservé |
--hard est la seule commande Git qui détruit du travail sans filet. Sur une branche partagée, utilisez toujours git revert : il ne réécrit pas l'historique des autres.
Situation 6 : je dois changer de sujet, mais mon travail n'est pas fini
git stash push -m "analyse cohortes en cours"
git switch main
git pull --rebase
# ... urgence traitee ...
git switch ma-branche
git stash pop
git stash list montre la pile. Nommer les stash (-m) évite de retrouver trois stash@{0} anonymes une semaine plus tard.
Situation 7 : j'ai perdu un commit
Il n'est presque jamais perdu. Git conserve un journal de toutes les positions de HEAD pendant 90 jours.
git reflog
# 3f2a1b0 HEAD@{2}: commit: calcul des cohortes
git checkout 3f2a1b0
git switch -c recuperation
git reflog est la commande qui sauve, y compris après un reset --hard malencontreux. Elle mérite d'être connue avant d'en avoir besoin.
Situation 8 : mon collègue et moi modifions le même script
Le conflit sur un .py est normal et se résout à la main. Git marque la zone :
<<<<<<< HEAD
df = df[df["quantite"] > 0]
=======
df = df[df["quantite"] >= 1]
>>>>>>> feature/nettoyage
Vous gardez ce qui est correct, vous supprimez les trois marqueurs, git add, git rebase --continue. Rien de magique.
Ce qui réduit la fréquence de ces conflits, c'est le découpage en modules : deux personnes qui travaillent sur extract.py et transform.py ne se marchent pas dessus. Un fichier unique de 900 lignes garantit le conflit à chaque itération.
Le workflow minimal pour une équipe data
Pas de GitFlow, pas de release branches. Trois règles suffisent :
mainest toujours exécutable. Rien n'y arrive directement.- Une branche par sujet, nommée
type/sujet:feat/cohortes,fix/jointure-doublons. - Une pull request, même sans relecteur. Elle sert de trace, et elle déclenche la CI qui lance les tests.
Le troisième point est celui qu'on saute quand on est seul, et c'est une erreur : la PR est ce qui branche vos tests pytest sur chaque modification.
Des commits qu'on peut relire six mois plus tard
Le format Conventional Commits demande dix secondes et rend l'historique navigable :
feat(cohortes): ajoute le calcul de retention a 90 jours
fix(jointure): corrige la duplication sur le referentiel produits
chore(deps): passe pandas en 2.3
Le test du bon message de commit : est-ce qu'il explique pourquoi, pas ce que le diff montre déjà ? update script échoue au test. fix(jointure): corrige la duplication sur le referentiel produits le passe.
S'entraîner sans risquer son dépôt d'équipe
Lire des commandes Git ne suffit pas. Ce qui bloque en situation réelle, c'est la panique quand le message d'erreur arrive et que trois personnes attendent le livrable.
Le simulateur Git gratuit rejoue exactement ces scénarios d'équipe : push rejeté, conflit sur notebook, commit à récupérer, dans un environnement où casser quelque chose n'a aucune conséquence. C'est la façon la plus rapide de transformer ces 8 situations en réflexes.
Une fois Git acquis, la suite logique est le reste de la chaîne : structure de projet, tests, CI, revue de code. C'est le parcours de la formation DataCraft, pour passer au niveau ingénierie.
Questions fréquentes
Faut-il versionner les notebooks ?
Oui, mais sans les sorties, avec nbstripout. Un notebook sans sorties se diffe et se merge presque comme un fichier Python.
Faut-il versionner les données ? Non, sauf jeux de référence très petits (moins de 1 Mo, stables). Pour le reste, versionnez le code qui produit la donnée et le chemin vers la source.
GitHub, GitLab ou Bitbucket ? Sans importance pour un analyste : les commandes Git sont identiques. Seule l'interface de pull request change.
Faut-il apprendre rebase ou se contenter de merge ?
git pull --rebase au quotidien suffit. Le rebase interactif pour réécrire l'historique est un outil de confort, pas une nécessité.

Approfondir avec mon livre
"Business Intelligence avec Python" - Le guide complet pour maîtriser l'analyse de données
Voir sur Amazon →