134 votes

Créer un dépôt de sous-module à partir d'un dossier et conserver son historique de commit git.

J'ai une application web qui explore d'autres applications web d'une manière particulière. Elle contient quelques démos web dans un demos et l'une des démos devrait maintenant avoir son propre référentiel. Je voudrais créer un dépôt séparé pour cette application de démo et en faire un sous-paquetage sous-module du dépôt principal sans perdre l'historique des livraisons.

Est-il possible de conserver l'historique des livraisons des fichiers dans le dossier d'un dépôt, de créer un dépôt à partir de celui-ci et de l'utiliser comme dépôt de base ? sous-module à la place ?

224voto

GabLeRoux Points 1502

Solution détaillée

Voir la note à la fin de cette réponse (dernier paragraphe) pour une alternative rapide aux submodules git en utilisant npm ;)

Dans la réponse suivante, vous saurez comment extraire un dossier d'un dépôt et en faire un dépôt git, puis l'inclure en tant que sous-module au lieu d'un dossier.

Inspiré de l'article de Gerg Bayer Déplacer des fichiers d'un dépôt Git à un autre en préservant l'historique

Au début, nous avons quelque chose comme ça :

<git repository A>
    someFolders
    someFiles
    someLib <-- we want this to be a new repo and a git submodule!
        some files

Dans les étapes ci-dessous, je vais faire référence à ce someLib comme <directory 1> .

À la fin, nous aurons quelque chose comme ceci :

<git repository A>
    someFolders
    someFiles
    @submodule --> <git repository B>

<git repository B>
    someFolders
    someFiles

Créer un nouveau dépôt git à partir d'un dossier dans un autre dépôt

Étape 1

Obtenez une copie fraîche du référentiel à diviser.

git clone <git repository A url>
cd <git repository A directory>

Étape 2

Le dossier actuel sera le nouveau référentiel, donc supprimez le remote actuel.

git remote rm origin

Étape 3

Extraire l'historique du dossier souhaité et le livrer

git filter-branch --subdirectory-filter <directory 1> -- --all

Vous devriez maintenant avoir un dépôt git avec les fichiers de directory 1 dans le Root de votre repo avec tout l'historique de commit associé.

Étape 4

Créez votre référentiel en ligne et poussez votre nouveau référentiel !

git remote add origin <git repository B url>
git push

Vous devrez peut-être définir le upstream pour votre premier push

git push --set-upstream origin master

Nettoyer <git repository A> (facultatif, voir commentaires)

Nous voulons supprimer les traces (fichiers et historique de commit) de <git repository B> de <git repository A> pour que l'historique de ce dossier ne soit présent qu'une seule fois.

Ceci est basé sur Suppression des données sensibles de github.

Allez dans un nouveau dossier et

git clone <git repository A url>
cd <git repository A directory>
git filter-branch --force --index-filter 'git rm --cached --ignore-unmatch <directory 1> -r' --prune-empty --tag-name-filter cat -- --all

Remplacer <directory 1> par le dossier que vous voulez supprimer. -r le fera de manière récursive dans le répertoire spécifié :). Maintenant, poussez vers origin/master con --force

git push origin master --force

Boss Stage (voir note ci-dessous)

Créer un sous-module de <git repository B> en <git repository A>

git submodule add <git repository B url>
git submodule update
git commit

Vérifier si tout a fonctionné comme prévu et push

git push origin master

Note

Après avoir fait tout cela, j'ai réalisé que dans mon cas, il était plus approprié d'utiliser npm pour gérer mes propres dépendances à la place. Nous pouvons spécifier les urls et les versions de git, voir la section package.json urls git comme dépendances .

Si vous procédez de cette façon, le référentiel que vous voulez utiliser comme exigence doit être un référentiel de type module npm il doit donc contenir un package.json ou vous obtiendrez cette erreur : Error: ENOENT, open 'tmp.tgz-unpack/package.json' .

tldr (solution alternative)

Vous trouverez peut-être plus facile d'utiliser npm y gérer les dépendances avec les urls git :

  • Déplacer un dossier vers un nouveau dépôt
  • exécuter npm init dans les deux référentiels
  • exécuter npm install --save git://github.com/user/project.git#commit-ish où vous voulez que vos dépendances soient installées

13voto

oodavid Points 378

La solution de @GabLeRoux écrase les branches, et les commits associés.

Un moyen simple de cloner et de conserver toutes ces branches et commits supplémentaires :

1 - Assurez-vous que vous avez cet alias git

git config --global alias.clone-branches '! git branch -a | sed -n "/\/HEAD /d; /\/master$/d; /remotes/p;" | xargs -L1 git checkout -t'

2 - Clonez le remote, tirez toutes les branches, changez le remote, filtrez votre répertoire, poussez

git clone git@github.com:user/existing-repo.git new-repo
cd new-repo
git clone-branches
git remote rm origin
git remote add origin git@github.com:user/new-repo.git
git remote -v
git filter-branch --subdirectory-filter my_directory/ -- --all
git push --all
git push --tags

6voto

ls. Points 90

La solution de GabLeRoux fonctionne bien sauf si vous utilisez git lfs et avoir de gros fichiers sous le répertoire que vous voulez détacher. Dans ce cas, après l'étape 3, tous les fichiers volumineux resteront des fichiers pointeurs au lieu de fichiers réels. Je suppose que c'est probablement dû à la .gitattributes qui est supprimé dans le processus de la branche de filtrage.

En réalisant cela, j'ai trouvé la solution suivante qui fonctionne pour moi :

cp .gitattributes .git/info/attributes

Copie de .gitattributes que git lfs utilise pour suivre les gros fichiers vers .git/ pour éviter d'être supprimé.

Quand le filtre-branche est terminé, n'oubliez pas de remettre le .gitattributes si vous voulez toujours utiliser git lfs pour le nouveau dépôt :

mv .git/info/attributes .gitattributes
git add .gitattributes
git commit -m 'added back .gitattributes'

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X