Aller au contenu
Développement medium

Git bundle : transférer un dépôt hors-ligne

10 min de lecture

git bundle crée un fichier autonome contenant tout ou partie d'un dépôt Git, transférable par clé USB, email ou tout autre moyen hors-ligne. C'est la solution pour les environnements air-gapped, les transferts entre réseaux isolés et les sauvegardes portables. Ce guide vous apprend à créer un bundle, vérifier son contenu et cloner ou puller depuis un bundle, avec un exemple complet de workflow hors-ligne.

Prérequis : Remotes fondamentaux et Sélection de révisions.

  • Créer un bundle complet d'un dépôt pour transfert hors-ligne
  • Créer des bundles incrémentaux pour des mises à jour légères
  • Vérifier l'intégrité d'un bundle avant de l'utiliser
  • Cloner ou puller depuis un fichier bundle

Le point commun de ces situations est l'absence de canal réseau direct entre les deux dépôts. Tant qu'un git push ou un git fetch peut aboutir, un remote classique reste plus simple. Le bundle prend le relais dès que le transport devient un fichier à déplacer à la main, avec l'avantage décisif de conserver l'historique complet et les empreintes des commits : le dépôt reconstitué à l'arrivée est identique, pas une copie approximative.

  • Environnement air-gapped : serveurs sans accès internet
  • Transfert entre réseaux : les deux machines ne se voient pas
  • Sauvegarde portable : un fichier unique contenant tout l'historique
  • Bande passante limitée : envoyer un fichier compressé par email
  • Onboarding : donner le repo complet à un nouveau développeur sur clé USB

git bundle create prend un nom de fichier suivi des mêmes arguments que git rev-list : c'est ce qui permet de choisir précisément ce qu'on emporte. L'option --all désigne toutes les références du dépôt, branches et tags compris. Le fichier produit est un objet Git valide que la machine de destination traitera exactement comme un dépôt distant.

Fenêtre de terminal
# Bundle contenant TOUT le dépôt (toutes les branches, tous les tags)
git bundle create mon-projet.bundle --all

Le fichier mon-projet.bundle contient l'intégralité du dépôt : tous les commits, branches, tags et objets Git. C'est l'équivalent d'un clone complet dans un fichier unique.

Pour bundler uniquement une branche :

Fenêtre de terminal
git bundle create main-only.bundle main

Un bundle contenant un historique complet se clone comme n'importe quelle URL. Git enregistre alors le chemin du fichier comme remote origin, ce qui pose problème dès que la clé USB est retirée : toute commande réseau échouera. Repointer origin juste après le clone évite d'y revenir plus tard dans l'urgence.

Fenêtre de terminal
git clone mon-projet.bundle mon-projet
cd mon-projet

Le bundle est traité comme un remote. Après le clone, configurez le vrai remote :

Fenêtre de terminal
git remote set-url origin https://github.com/org/mon-projet.git

Quand le dépôt existe déjà côté destination, il n'y a rien à cloner : on ajoute le bundle comme remote supplémentaire et on récupère ses références. L'intérêt est de garder l'origin existant intact et de pouvoir comparer les deux sources avant de fusionner quoi que ce soit.

Fenêtre de terminal
git remote add usb /media/usb/mon-projet.bundle
git fetch usb
git merge usb/main

Le fetch affiche une ligne par référence importée, préfixée de * [new branch]. Tant que vous n'avez pas lancé le merge, votre branche locale n'a pas bougé : git log --oneline usb/main permet donc d'inspecter ce qui arrive avant de l'intégrer.

Avant d'importer, validez l'intégrité :

Fenêtre de terminal
git bundle verify mon-projet.bundle
mon-projet.bundle is okay
The bundle contains these 4 refs:
1fefc49f24e0414d7ff326e6c41cd9cff17220c0 refs/heads/main
fb239952b2534d694c54abd6c55ab26a78801656 refs/heads/develop
1fefc49f24e0414d7ff326e6c41cd9cff17220c0 refs/tags/v1.0.0
1fefc49f24e0414d7ff326e6c41cd9cff17220c0 HEAD
The bundle records a complete history.
The bundle uses this hash algorithm: sha1

Trois lignes méritent votre attention. HEAD apparaît dans la liste parce que --all l'inclut : c'est normal, ce n'est pas une branche en double. The bundle records a complete history confirme qu'aucun commit préalable n'est requis, donc que ce fichier suffit à reconstruire le dépôt. Enfin, l'algorithme d'empreinte doit correspondre à celui du dépôt de destination : un bundle sha256 est illisible par un dépôt sha1.

Si le bundle est incomplet (incrémental), verify remplace cette ligne par The bundle requires this ref: suivie des commits absents. Aucun import n'est possible tant que ces commits ne sont pas déjà présents en local.

Pour les mises à jour régulières, inutile de re-bundler tout le dépôt. Créez des bundles contenant uniquement les nouveaux commits :

  1. Premier transfert, bundle complet :

    Fenêtre de terminal
    git bundle create initial.bundle --all
    # Notez le dernier commit inclus
    git rev-parse HEAD
    # a1b2c3d4...
  2. Transferts suivants, uniquement les nouveautés :

    Fenêtre de terminal
    # Depuis le dernier commit transféré
    git bundle create update-1.bundle a1b2c3d4..HEAD --all
  3. Sur la machine de destination, appliquer l'incrément :

    Fenêtre de terminal
    git bundle verify update-1.bundle # Vérifier les pré-requis
    git fetch /chemin/update-1.bundle main:remotes/usb/main
    git merge usb/main

    Le fetch crée la référence locale refs/remotes/usb/main, ce qui rend l'incrément consultable avec git log usb/main avant de fusionner.

Dans la commande de l'étape 2, la plage et --all se combinent sans se contredire : l'exclusion a1b2c3d4.. s'applique à toutes les références sélectionnées par --all. Vous obtenez donc un fichier qui contient toutes les branches, mais amputées de l'historique déjà transféré. Le verify sur la machine de destination affichera The bundle requires this ref: avec l'empreinte a1b2c3d4 : c'est la preuve que l'incrément a bien fonctionné, pas une erreur.

Retenir un identifiant de commit à la main ne tient pas dans la durée. Un tag posé après chaque transfert matérialise le point de reprise directement dans le dépôt : au transfert suivant, la plage à empaqueter se lit sans consulter de notes extérieures. Le tag reste local tant que vous ne le poussez pas, il ne pollue donc pas le dépôt partagé.

Fenêtre de terminal
# Après chaque transfert, tagger le dernier commit envoyé
git tag last-bundle-v1
# Au prochain transfert
git bundle create update.bundle last-bundle-v1..HEAD --all
git tag last-bundle-v2

Le schéma ci-dessous résume l'enchaînement sur deux tours : un transfert initial complet, puis un incrément. Lisez-le colonne par colonne, la colonne du milieu représentant le seul élément physique qui traverse la frontière entre les deux réseaux. Le point à ne pas rater est le tag posé après chaque envoi sur la machine connectée : c'est lui qui garantit qu'aucun commit ne sera oublié entre deux transferts.

Machine A (connectée) Clé USB Machine B (isolée)
───────────────────── ──────────── ─────────────────────
git bundle create copier git clone bundle
projet.bundle --all projet.bundle projet
git remote set-url
origin ...
[... plus tard ...]
git bundle create copier git bundle verify
update.bundle update.bundle git fetch update
tag-v1..HEAD --all git merge
git tag tag-v2

list-heads répond à une question précise : quelles branches et quels tags ce fichier contient-il. Contrairement à verify, il ne contrôle rien et ne dit pas si l'historique est complet. Utilisez-le pour vérifier qu'une branche attendue est bien du voyage avant de transporter le fichier.

Fenêtre de terminal
git bundle list-heads mon-projet.bundle
1fefc49f24e0414d7ff326e6c41cd9cff17220c0 refs/heads/main
fb239952b2534d694c54abd6c55ab26a78801656 refs/heads/develop
1fefc49f24e0414d7ff326e6c41cd9cff17220c0 refs/tags/v1.0.0
1fefc49f24e0414d7ff326e6c41cd9cff17220c0 HEAD

Ces quatre cas représentent la quasi-totalité des blocages rencontrés avec les bundles. Le dernier est le plus déroutant parce qu'il ne produit aucune erreur : unbundle importe bien les objets dans le dépôt, mais ne crée aucune référence, si bien que git log et git branch ne montrent rien de nouveau. C'est le comportement attendu de la commande, pas un dysfonctionnement.

SymptômeCause probableSolution
verify dit « prerequisite commit missing »Bundle incrémental, historique manquantImportez d'abord le bundle complet
Bundle très volumineuxContient tout l'historique + gros fichiersUtilisez --since ou des plages de commits
clone depuis bundle : remote invalideURL du bundle comme remotegit remote set-url origin <vraie-url>
unbundle ne fait rien de visibleLes objets sont importés mais pas les branchesUtilisez fetch au lieu de unbundle
  • git bundle create empaquète tout ou partie d'un dépôt dans un fichier unique
  • git bundle verify valide l'intégrité avant import
  • Clone ou fetch depuis un bundle comme depuis un remote
  • Les bundles incrémentaux (plage de commits) réduisent la taille des transferts
  • Solution idéale pour les environnements air-gapped et les transferts hors-ligne

Ce site vous est utile ?

Sachez que moins de 1% des lecteurs soutiennent ce site.

Je maintiens +700 guides gratuits, sans pub ni tracking. Un soutien, même symbolique, m'aide à couvrir l'hébergement et à garder ces ressources gratuites. Merci pour votre appui.

Le formulaire ne s'affiche pas ? Ouvrir Ko-fi dans un onglet.

Abonnez-vous et suivez mon actualité DevSecOps sur LinkedIn