Comme vous le savez sans doute Git est un projet open source très utilisé, qui a été initialement développé par Linus Torvald en 2005. C’est aujourd’hui le logiciel de versionning le plus répandu, principalement en développement informatique.

Cet article présente plusieurs jeux de commandes, mais n’est pas exhaustif. En effet, Il tient compte des use-case que j’ai traités ces derniers temps (si vous avez d’autres use case en tête, n’hésitez pas à me DM sur twitter pour que je puisse améliorer cet article). Certaines commandes, vous seront familières, j’ai essayé d’énumérer des exemples simples intermédiaires et avancés, afin que cet article puisse servir au plus grand nombre.

Les commandes git ci-dessous vous permettrons d’améliorer votre workflow et de toujours avoir un repository Git propre et sécurisé. Les titres ci-dessous seront aussi donnés en anglais, car parfois Git et la langue française = charabia.

Pour réaliser les différentes démonstrations, j’utilise un repository volontairement vulnérable avec des informations sensibles comme un mot de passe, une IP, etc. Le nom de ce repository sera « Gitalpha ». vous pouvez le forker de votre côté afin de vous entrainer, avant de tester certaines de vos commandes sur vos propres repos.

  • https://github.com/archidote/gitalpha (Les noms des commits ainsi que le contenu a varié et n’est plus le même sur mon repository suite aux différents tests que j’ai effectués)

Pour finir, les exemples sont classés en fonction de la complexité des commandes.

Bonne lecture !

[ERRATUM]

Après avoir consulté un expert en versionning (surnommé le poète, il se reconnaîtra ^^), il m’a fait remarquer que je n’utilisais pas l’argument --force-with-lease mais --force, ce qui en cas de repository collaboratif peut impacter le workflow des autres devs, car ils feront face à des conflits de commits, s’ils ont déjà commités en local. Ils devront alors faire quelques galipettes techniques afin de se resynchroniser de nouveau avec la branche distante « référente », et de pousser à leur tour les modifications. (Rien de bien grave, mais ça risque de souffler dans l’open-space).

Donc pour les lignes suivantes, si-vous voyez --force, utilisez de préférence l’argument suivant --force-with-lease

Pour plus de détails : https://itnext.io/git-force-vs-force-with-lease-9d0e753e8c41

I. Améliorer la qualité de votre workflow

A. Editer le message de commit le plus récent (commit local) – (EN : Edit a local Git commit message)

Rien de plus simple que d’utiliser l’argument --amend suffixé à la commande git commit :

git commit --amend -m "edited"

Récapitulatif du workflow avec modification et push des modifications (--force ne devrait pas être utilisée dans cette situation, c’est une erreur de ma part)

B. Sauvegarder des changements non commité pour plus tard – Git stash (EN : save my files without committing with Git)

Cette commande a plusieurs cas d’utilisation. Le plus fréquent est le cas que je vous ai exposé ci-dessus, mais avec une subtilité. Vous souhaitez garder les changements de côté « au cas ou ». Tel est le but de cette commande.

git stash va supprimer vos modifications de votre répertoire de travail actuel et les enregistre pour une utilisation ultérieure sans les commiter, mais en les « méttant de côté ».

Cela vous permettra de pouvoir synchroniser votre branche distante avec votre branche locale, et de conserve vos modifications staché de côté. En effet, si dans le futur si-vous souhaitez les réintégrer les changements dans votre repository, vous n’aurez cas utiliser la commande git stash pop

Schéma reprennant les principaux « état » d’un workflow de git. Source : https://medium.com/

Si-vous possédez une version de git récente, lors de l’exécution de la commande git pull, si-vous avez des modifications dans votre référentiel non validé, git va les « auto stashé »

Je ne détail pas plus cette commande, mais vous pouvez plus d’informations et d’exemple ici :

Sachez, que celle-ci est simple à utiliser (n’ayez pas peur).

C. Modifier un message d’un commit git déjà « poussé » sur Github/Gitlab (EN : Change a git commit message after push)

Je suis sur que cela vous est déjà arrivé ! Faire une faute dans un message de commit, oubliez un mot, ou écrire quelques chose qui ne vous convient plus dans votre commit. Pour ce cas d’utilisation, je vais utiliser la commande git rebase. Dans mon cas, je vais choisir d’afficher les trois derniers commit (d’ou le HEAD~3), afin d’éditer le commit précédent à savoir « third commit » : 2b10b32 dans mon cas.

git rebase -i HEAD~3

Déplacez-vous sur la ligne du message de commit que vous souhaitez éditer et remplacer le mot pick par reword. Cette action permettra d’informer au « moteur git », que vous souhaitez agir sur cette ligne en particulier.

N’oubliez pas que vous pouvez faire cette action pour de multiple commit si-vous le souhaitez.

Enregistrez les modifications, et vous verrez qu’une seconde fenêtre va s’ouvrir. c’est ici que vous allez être en mesure de changer le message du (ou des commit) sélectionné(s). Dans mon cas, j’ai uniquement choisi de renommer le commit identifié par le hash 2b10b32.

Il ne manque plus qu’a poussé nos modifications sur notre référentiel distant, suffixant notre commande avec --force, car en effet, nous allons devoir réécrire une partie de l’histoire du repository. Attention cela peut avoir des effets de bord. (surtout si-vous travaillez en équipe)

git push --force 

Récapitulatif des commandes saisies

En regardant de plus près les deux images (ci-dessus/ci-dessous), on constate que le message du troisième commit a bel et bien été modifié en local et sur github.

***Attention*** ! Pour les points suivants, les commandes peuvent, si-elles sont mal utilisées endommager de manière définitive votre repository. Je vous conseille vivement de réaliser vos différents tests avec un repository de test pour une première approche.

II. Modifications « spatio-temporelle » dans votre repository

A. Forcer la synchronisation – Effacer les modifications local désuètes (EN : How to force « git pull » for overwriting local files?)

Si-vous travaillez sur plusieurs ordinateurs/VMs, il vous est surement déjà arrivé d’avoir des modifications en local, mais de ne jamais commiter vos modifications . Quelque temps plus tard, si-vous souhaitez de nouveau utiliser cette machine, vous vous trouvez en présence d’un repo non à jour et le rapatriement « classique » des changements avec git pull va poser problème. En effet, Git ne va pas accepter par défaut le rapatriement des changements depuis votre branche distante (et donc l’écrasement de vos modifications locales).

Nous devons donc indiquer à Git que nous ne souhaitons pas conserver nos changements locaux, qui sont désuets. L’exécution des commandes suivante vous permettra de forcer la synchronisation du repository en détruisant des modifications locales.

git fetch --all

Puis :

git reset --hard origin/main 

Commentaires pour la capture ci-dessus :

  • ici, git pull ne retourne pas une erreur car comme je possède la dernière version de Git qui par défaut si j’ai des modifications en local, « auto stash » mes modifications au cas où. (comprendre : sauvegardé et mis de côté, afin de ne pas impacter le workflow ». Mais ce n’a pas toujours été le cas !
  • Ensuite, vous devez synchroniser les informations entre vous (local) et votre repository (distant), avant de demander explicitement à git de forcer la réécriture des données en local avec la dernière commande.

B. Retour dans le passé – Substituer une chaine de caractère / supprimer un fichier dans l’historique git (EN : How to substitute text from files in git history?)

Il y a peu de temps, je me suis apperçu que j’avais laissé trainé sur un de mes repositorys des informations légèrement sensibles comme l’IP public d’un de mes VPS dans un fichier .yml. (Merci @JM2K69). Même si ces informations ne sont pas critiques, j’ai quand même décidé de trouver un moyen afin de corriger ce petit oublie.

Cependant cette IP, était présente depuis quelques mois dans ce fichier que je n’avais plus touché depuis des mois (donc présente à de multiples reprises dans mon historique Git). C’est là que je me suis demandé : Existe-t-il un moyen permettant de « réécrire l’histoire » de mon historique Git » afin de remplacer cette IP par une autre chaine de caractère ?

La réponse est oui. à ma connaissance il existe deux solutions :

  • git filter-branch (inbuilt) mais annoncé comme étant dépréciée depuis 2019
  • git filter-repo (version améliorée de filter-branch, mais non native pour le moment)

Ces commandes interviendront sur l’historique Git (intervention sur tous les fichiers de toutes les branches)

Comme vous le pouvez constater mon repository « Gitalpha » contient des informations sensible dans son historique comme une IP, et un mot de passe hardcodé :

1. git filter-branch

Je vais quand même vous préseter comme utiliser la commande git filter-branch, (même si elle devrait être dépréciée prochainement), car elle s’avère tout de même rapide et efficace pour effectuer ce changement à condition de l’utiliser correctement, sans quoi vous risquez de casser votre repository. Cette annonce a été faite en il y a quelques années (2019 de mémoire), mais celle-ci est toujours disponible. Peux-être qu’elle ne sera jamais définitivement retirée ^^ selon certaines rumeurs

un exemple d’utilisation de la commande git filter-repo est présenté dans la section suivante. Comme vous pouvez le voir, nous avons une deprecated notice quand on exécute cette commande.

Néanmoins, je vais quand même vous montrer comment réaliser cela avec cette commande.

git filter-branch --tree-filter "find . -name '*.py' -exec sed -i -e \
's/172.16.0.1/x.x.x.x/g' {} \;"

La commande ci-dessus permet de rechercher dans tous les fichiers (*. py) à la racine de mon repository la chaine de caractère « 172.16.0.1 ». Le cas échéant, un remplacement sera effectué au profit de la string x.x.x.x.

Une fois que la commande a fini de s’exécuter (cela peut être long en fonction de la taille de l’historique du repository), je peux donc mettre à jour mon repository distant avec la commande git push --force. En effet, vous êtes obligé de « forcer le push » car « Git ne comprend pas pourquoi il y a eu des changements au sein de l’historique » par défaut. (ce qui au passage est normal hein^^)

2. git filter-repo (plugin non natif de git)

git filter-repo, n’est pas une commande « inbuilt » git. Vous devez donc l’installer. La manière la plus simple est de procéder avec l’utilitaire python pip.

python3 -m pip install --user git-filter-repo

Si vous obtenez le warning suivant :

WARNING: The script git-filter-repo is installed in '/home/gvmh7717/.local/bin' which is not on PATH.

Ajoutez la ligne suivante à votre fichier de démarrage de shell (tel que .bashrc, .zshrc, etc.) Dans mon cas j’utilise bash :

nano ~/.bashrc 
export PATH=$PATH:/home/gvmh7717/.local/bin

N’oubliez pas de « recharger » votre bashrc comme cela :

source ~/.bashrc

Maintenant, vous devriez pouvoir exécuter git filter-repo -h :

L’un des avantages de git filter-repo est qu’elle a la capacité de remplacer plusieurs string à la fois dans l’historique git. Me concernant, je souhaite remplacer les chaines de caractère suivants par une donnée « anonyme » :

  • 172.16.0.1 –> x.x.x.x
  • archidote –> xxxxxxxx
  • 123+aze –> xxxxxxx

Ce sont des informations sensibles présentes dans l’historique de mon repository gitalpha.

Pour ce faire je vais procéder comme suit :

echo '172.16.0.1==>x.x.x.x' >> replace.txt
echo 'archidote==>xxxxxxx' >> replace.txt
echo '123+aze==>xxxxxxx' >> replace.txt

Dès lors, j’exécute la commande suivante pour initier le remplacement « spatio-temporel »

git filter-repo --replace-text /home/kali/replace.txt

Le remplacement a bien été éffectué en local !

Comme pour la commande git filter branch, je dois forcer le « push » pour mettre à jour l’historique git aussi sur mon repository distant.

Juste pour avoir bonne conscience, vérifiez aussi sur votre repository distant après le push. (en affichant la code en fonction des commits) et non uniquement depuis la (les) branche(s) de votre repository.

3. git filter-repo – supprimer un fichier de l’historique git

git filter-repo --invert-paths --path <path to the file or directory>

Pour plus d’info : https://stackoverflow.com/a/64563565

C. Supprimer un commit distant depuis github/gitlab – (EN : Remove a remote commit from github/gitlab)

Parfois, après quelques modifications, vous pouvez envisager de revenir en arrière de 1,2 voir 3 commits afin de retrouver une version « plus stable » de votre code dans le cas où vous auriez foutu un peu le bazare….

Mon projet à cet instant T contient 6 commits.

git log 

Dans mon cas, je souhaite réajuster mon « curseur temporel git » sur le commit 9a4cb87 « 4e commit ».

git rebase -i HEAD~3

Je vais donc effacer complètement les lignes contenant les commits 4caf8f0 et b1b5dda qui sont devant mon commit 9a4cb87

Comme cela :

Enregistrez le fichier.

Comme vous pouvez le voir, la modification n’est effective qu’en local

Mais pas sur mon repository distant… enfin pour le moment 😉

Pour mettre à jour celui-ci et faire en sorte que notre « commit roll back » soit effectif aussi sur github/gitlab, exécutez la commande suivante :

git push --force # Pour une autre branche cf ci-dessous : 
git push origin +main --force

Actualisé la liste de vos branches sur github/gitlab et vous verrez que vos changements auront bien été pris en compte.

D. Git secret – Faire transiter des informations confidentielles de façon sécurisée au sein de votre repository (EN : How to keep your repository’s sensitive data secure)

git secret est une commande qui n’est pas livrée lors de l’installation de Git. Ce plugiciel octroie la possibilité de chiffrer des fichiers sensibles comme (config.yml, .env, etc.) en utilisant le chiffrement asymétrique. Afin de conserver ses fichiers en toute sécurité au sein de votre référentiel git. Ces fichiers ne pourront être déchiffrés que par les utilisateurs en qui « vous avez confiance ».

Cet utilitaire s’installe via les repositorys officiels (debian/ubuntu). git secret vous permettra d’améliorer l’automatisation des phases de déploiement automatique. En effet, dans le cas où vous souhaiteriez ajouter d’autres informations confidentielles au sein d’un ou plusieurs fichiers (.env par exemple), il vous suffira d’utiliser git secret côté production afin que le processus de déchiffrement et d’actualisation des fichiers puisse se faire sans encombre.

Si-vous souhaitez mettre en place ce procédé, je vous invite à consulter la documentation du projet user-friendly suivante :

Je passe volontairement les détails techniques, car cela serait trop long, et mon article n’est pas dédié à uniquement à git secret. Cette commande demande de comprendre le concept de chiffrement asymétrique (avec GPG). Ce n’est pas forcément très difficile, mais ça va vous demander un peu de temps afin de bien comprendre comme cela fonctionne. (Il y a pas mal de ressources sur le net pour vous aider).

III. Conclusion et sources

J’espère que l’article aura été clair pour vous (désolé pour le franglais charabia, mais souvent ce n’est pas simple et contre-productif de s’exprimer en 100% Français dans l’informatique comme vous le savez). Si-vous connaissez d’autres commandes / tip / smart moove sur git, n’hésitez pas à me contacter pour que je mette à jour l’article !

Catégories : DevSecOpsGit

Geoffrey Sauvageot-Berland

Ingénieur diplômé par l’état en Informatique et Cybersécurité. Généraliste, à l'origine administrateur systèmes et réseaux, j’occupe actuellement un poste d’auditeur en sécurité offensive. J’apprécie également la programmation/automatisation. Fondateur du blog : "Le Guide du SecOps", anciennement "Le Guide du SysOps"