Créez un référentiel de sous-modules à partir d'un dossier et conservez son historique de validation git

111

J'ai une application Web qui explore d'autres applications Web d'une manière particulière. Il contient des démos Web dans un demosdossier et l'une des démos devrait maintenant avoir son propre référentiel. Je voudrais créer un référentiel séparé pour cette application de démonstration et en faire unsous-paquet sous-module du référentiel principal sans perdre son historique de validation.

Est-il possible de conserver l'historique de validation des fichiers dans le dossier d'un référentiel et de créer un référentiel à partir de celui-ci et de l'utiliser à la place comme sous - module ?

GabLeRoux
la source
J'ai cherché comment déplacer le répertoire 1 du référentiel Git A vers le référentiel Git B. +1 pour le lien vers l'article.
Chetabahana
Oui, c'est en effet très similaire, les solutions diffèrent un peu, merci de partager ceci
GabLeRoux

Réponses:

191

Solution détaillée

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

Dans la réponse suivante, vous saurez comment extraire un dossier d'un référentiel et en créer un référentiel 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 référentiel Git à un autre, Préserver l'historique

Au début, nous avons quelque chose comme ceci:

<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 ferai référence à cela someLibcomme <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 référentiel git à partir d'un dossier dans un autre référentiel

Étape 1

Obtenez une nouvelle copie du référentiel à fractionner.

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

Étape 2

Le dossier actuel sera le nouveau référentiel donc supprimez la télécommande actuelle.

git remote rm origin

Étape 3

Extraire l'historique du dossier souhaité et le valider

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

Vous devriez maintenant avoir un référentiel git avec les fichiers de directory 1la racine de votre dépôt avec tout l'historique de validation associé.

Étape 4

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

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

Vous devrez peut-être définir la upstreambranche pour votre premier push

git push --set-upstream origin master

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

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

Ceci est basé sur la 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

Remplacez <directory 1>par le dossier que vous souhaitez supprimer. -rle fera récursivement dans le répertoire spécifié :). Maintenant poussez origin/masteravec--force

git push origin master --force

Boss Stage (voir la note ci-dessous)

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

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

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

git push origin master

Remarque

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

Si vous le faites de cette façon, le dépôt que vous souhaitez utiliser comme une exigence doit être un module de NPM il doit contenir un package.jsonfichier 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 et de gérer les dépendances avec les urls git :

  • Déplacer le dossier vers un nouveau référentiel
  • s'exécuter npm initdans les deux référentiels
  • exécuter npm install --save git://github.com/user/project.git#commit-ishlà où vous voulez que vos dépendances soient installées
GabLeRoux
la source
39
L'étape «Nettoyer <git repository A>» doit être évitée. En faisant cela, vous ne pouvez pas restaurer / extraire complètement les anciennes versions / commits de votre historique. Vous devez simplement git rm le dossier et ajouter le sous-module. Vous vous assurez donc d'avoir une copie entièrement fonctionnelle lors de l'extraction d'anciens commits.
Cybot
Ne devriez-vous pas faire cd someLibavant l'étape 2? Vous dites "Le dossier actuel sera le nouveau référentiel" mais en réalité ce ne sera pas le cas; le nouveau référentiel (sous-module) se trouve dans ce dossier.
Jago
1
confirmation: oui, cela fonctionne pour plus d'un sous-module. Merci beaucoup pour la réponse détaillée. De plus, je n'ai pas eu besoin d'utiliser npm.
Breno Inojosa
2
Je voudrais ajouter des informations sur le refs/original/...qui est créé à l'étape 3.
Emile Bergeron
6
GitHub a fait un article sur la façon de réaliser l'extraction d'un dossier dans un nouveau référentiel: help.github.com/articles
...
9

La solution de @GabLeRoux écrase les branches et les validations associées.

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

1 - Assurez-vous d'avoir 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 la télécommande, tirez toutes les branches, changez la télécommande, filtrez votre répertoire, poussez

git clone [email protected]:user/existing-repo.git new-repo
cd new-repo
git clone-branches
git remote rm origin
git remote add origin [email protected]:user/new-repo.git
git remote -v
git filter-branch --subdirectory-filter my_directory/ -- --all
git push --all
git push --tags
oodavid
la source
3

La solution de GabLeRoux fonctionne bien sauf si vous utilisez git lfset contient des fichiers volumineux sous le répertoire que vous souhaitez détacher. Dans ce cas, après l'étape 3, tous les fichiers volumineux resteront des fichiers de pointeur au lieu de fichiers réels. Je suppose que c'est probablement dû au fait que le .gitattributesfichier a été supprimé dans le processus de branche de filtre.

En réalisant cela, je trouve que la solution suivante fonctionne pour moi:

cp .gitattributes .git/info/attributes

Copie de .gitattributesce que git lfs utilise pour suivre les gros fichiers dans le .git/répertoire pour éviter d'être supprimés.

Lorsque filter-branch est terminé, n'oubliez pas de remettre le .gitattributessi 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'
ls.
la source