web-dev-qa-db-fra.com

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

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 dossier demos et l'une des démos devrait maintenant avoir son propre référentiel. Je souhaite créer un référentiel distinct pour cette application de démonstration et en faire un sous-emballage 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, de créer un référentiel à partir de celui-ci et de l'utiliser comme sous-module ?

94
GabLeRoux

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 faire 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éplacement de fichiers d'un référentiel Git vers un autre, préservation de 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 vais appeler ceci 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 référentiel Git à partir d'un dossier situé dans un autre référentiel

Étape 1

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

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

Étape 2

Le dossier actuel sera le nouveau référentiel afin de supprimer 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 dépôt git avec les fichiers de directory 1 dans la racine de votre référentiel avec tous les historiques de validation associés.

É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 la branche upstream 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 de commit) de <git repository B> de <git repository A> donc l’historique de ce dossier n’est présent qu’une 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 souhaitez supprimer. -r le fera récursivement à l'intérieur du répertoire spécifié :). Maintenant, appuyez sur Origin/master avec --force

git Push Origin master --force

Boss Stage (voir note ci-dessous)

Créez un sous-module à partir de <git repository B> en <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 tout cela, je me suis rendu compte dans mon cas qu'il était plus approprié d'utiliser npm pour gérer mes propres dépendances. Nous pouvons spécifier les URLs et les versions de Git, voir les RL de package.json git en tant que dépendances .

Si vous procédez ainsi, le référentiel que vous souhaitez utiliser en tant qu'exigence doit être un module npm , de sorte qu'il doit contenir un package.json fichier ou vous obtiendrez cette erreur: Error: ENOENT, open 'tmp.tgz-unpack/package.json'.

tldr (solution alternative)

Vous trouverez peut-être qu'il est plus facile d'utiliser npm et gérer les dépendances à l'aide d'URL git :

  • Déplacer le dossier dans un nouveau référentiel
  • courir npm init à l'intérieur des deux référentiels
  • courir npm install --save git://github.com/user/project.git#commit-ish où vous voulez installer vos dépendances
165
GabLeRoux

La solution de @GabLeRoux écrase les branches et les commits correspondants.

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 - Cloner la télécommande, extraire toutes les branches, changer la télécommande, filtrer votre répertoire, Push

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
6
oodavid

La solution de GabLeRoux fonctionne bien sauf si vous utilisez git lfs et contient des fichiers volumineux dans 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 .gitattributes fichier en cours de suppression dans le processus de branche de filtrage.

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

cp .gitattributes .git/info/attributes

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

Lorsque le filtre-branche est terminé, n'oubliez pas de remettre le .gitattributes _ si vous voulez toujours utiliser git lfs pour le nouveau référentiel:

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