web-dev-qa-db-fra.com

Détache (déplace) le sous-répertoire dans un référentiel Git séparé

J'ai un Git référentiel qui contient un certain nombre de sous-répertoires. Maintenant, j'ai constaté que l'un des sous-répertoires est indépendant de l'autre et doit être détaché dans un référentiel distinct.

Comment puis-je faire cela tout en conservant l'historique des fichiers dans le sous-répertoire?

Je suppose que je pourrais créer un clone et supprimer les parties indésirables de chaque clone, mais je suppose que cela me donnerait l’arborescence complète lors de la vérification d’une version plus ancienne, etc. Deux dépôts n'ont pas d'historique partagé.

Juste pour que ce soit clair, j'ai la structure suivante:

XYZ/
    .git/
    XY1/
    ABC/
    XY2/

Mais j'aimerais plutôt ceci:

XYZ/
    .git/
    XY1/
    XY2/
ABC/
    .git/
    ABC/
1643
matli

Mise à jour : ce processus est si commun que l'équipe git l'a simplifiée beaucoup plus facilement avec un nouvel outil, git subtree. Voir ici: sous-répertoire Detach (move) dans un référentiel Git séparé


Vous souhaitez cloner votre référentiel, puis utiliser git filter-branch pour tout marquer sauf le sous-répertoire que vous souhaitez dans votre nouveau référentiel.

  1. Pour cloner votre référentiel local:

    git clone /XYZ /ABC
    

    (Remarque: le référentiel sera cloné à l'aide de liens physiques, mais ce n'est pas un problème car les fichiers liés ne seront pas modifiés en eux-mêmes - de nouveaux seront créés.)

  2. Maintenant, conservons les branches intéressantes que nous voulons également réécrire, puis supprimons l’Origine pour éviter d’y appuyer et pour nous assurer que les anciens commits ne seront pas référencés par l’Origine:

    cd /ABC
    for i in branch1 br2 br3; do git branch -t $i Origin/$i; done
    git remote rm Origin
    

    ou pour toutes les branches distantes:

    cd /ABC
    for i in $(git branch -r | sed "s/.*Origin\///"); do git branch -t $i Origin/$i; done
    git remote rm Origin
    
  3. Maintenant, vous voudrez peut-être aussi supprimer les étiquettes qui n’ont aucune relation avec le sous-projet; vous pouvez également le faire plus tard, mais vous devrez peut-être élaguer à nouveau votre rapport. Je ne l'ai pas fait et j'ai reçu un WARNING: Ref 'refs/tags/v0.1' is unchanged pour toutes les balises (car elles n'étaient pas liées au sous-projet); de plus, après la suppression de telles balises, davantage d'espace sera récupéré. Apparemment, git filter-branch devrait pouvoir réécrire d'autres balises, mais je n'ai pas pu le vérifier. Si vous souhaitez supprimer toutes les balises, utilisez git tag -l | xargs git tag -d.

  4. Ensuite, utilisez filter-branch and reset pour exclure les autres fichiers, afin qu’ils puissent être supprimés. Ajoutons également --tag-name-filter cat --Prune-empty pour supprimer les commits vides et réécrire les balises (notez que cela devra effacer leur signature):

    git filter-branch --tag-name-filter cat --Prune-empty --subdirectory-filter ABC -- --all
    

    ou bien, réécrire uniquement la branche HEAD et ignorer les balises et autres branches:

    git filter-branch --tag-name-filter cat --Prune-empty --subdirectory-filter ABC HEAD
    
  5. Supprimez ensuite les modifications de sauvegarde afin que l'espace puisse être véritablement récupéré (bien que l'opération soit maintenant destructive)

    git reset --hard
    git for-each-ref --format="%(refname)" refs/original/ | xargs -n 1 git update-ref -d
    git reflog expire --expire=now --all
    git gc --aggressive --Prune=now
    

    et maintenant vous avez un dépôt git local du sous-répertoire ABC avec tout son historique préservé.

Remarque: Pour la plupart des utilisations, git filter-branch devrait en effet avoir le paramètre ajouté -- --all. Oui c'est vraiment --space-- allname__. Ce doit être le dernier paramètre de la commande. Comme Matli l'a découvert, les branches et les balises du projet sont incluses dans le nouveau référentiel.

Éditer: diverses suggestions issues des commentaires ci-dessous ont été incorporées pour s’assurer, par exemple, que le référentiel est réellement réduit (ce qui n’était pas toujours le cas auparavant).

1190
Paul

La manière facile ™

Il s’avère que c’est une pratique tellement courante et utile que les seigneurs de git ont rendu la tâche vraiment facile, mais vous devez disposer d’une version plus récente de git (> = 1.7.11 mai 2012). Voir le appendix pour savoir comment installer le dernier git. De plus, il y a un exemple real-world dans la procédure pas à pas ci-dessous.

  1. Préparer le vieux repo

    pushd <big-repo>
    git subtree split -P <name-of-folder> -b <name-of-new-branch>
    popd
    

    Remarque:<name-of-folder> ne doit PAS contenir de caractères de début ou de fin. Par exemple, le dossier nommé subproject DOIT être passé sous la forme subproject, NOT ./subproject/

    Remarque pour les utilisateurs de Windows: lorsque la profondeur de votre dossier est> 1, <name-of-folder> doit comporter un séparateur de dossiers de style * nix (/). Par exemple, le dossier nommé path1\path2\subproject DOIT être passé en tant que path1/path2/subproject

  2. Créer le nouveau repo

    mkdir <new-repo>
    pushd <new-repo>
    
    git init
    git pull </path/to/big-repo> <name-of-new-branch>
    
  3. Liez le nouveau repo à Github ou ailleurs

    git remote add Origin <[email protected]:my-user/new-repo.git>
    git Push Origin -u master
    
  4. Nettoyage, si désiré

    popd # get out of <new-repo>
    pushd <big-repo>
    
    git rm -rf <name-of-folder>
    

    Note: toutes les références historiques sont conservées dans le référentiel. Reportez-vous à la Annexe ci-dessous si vous êtes vraiment préoccupé par la création d'un mot de passe ou si vous devez réduire la taille du fichier .git.

...

Procédure pas à pas

Ce sont les mêmes étapes que ci-dessus, mais en suivant mes étapes exactes pour mon référentiel au lieu d'utiliser <meta-named-things>.

Voici un projet que j'ai pour implémenter des modules de navigateur JavaScript dans le noeud:

tree ~/Code/node-browser-compat

node-browser-compat
├── ArrayBuffer
├── Audio
├── Blob
├── FormData
├── atob
├── btoa
├── location
└── navigator

Je souhaite séparer un seul dossier, btoa, dans un référentiel git distinct

pushd ~/Code/node-browser-compat/
git subtree split -P btoa -b btoa-only
popd

J'ai maintenant une nouvelle branche, btoa-only, qui n'a validé que pour btoa et je veux créer un nouveau référentiel.

mkdir ~/Code/btoa/
pushd ~/Code/btoa/
git init
git pull ~/Code/node-browser-compat btoa-only

Ensuite, je crée un nouveau dépôt sur Github ou bitbucket, ou quoi que ce soit, et l'ajoute à la variable Origin (d'ailleurs, "Origin" est simplement une convention, pas une partie de la commande - vous pouvez l'appeler "serveur distant" ou ce que vous aimez).

git remote add Origin [email protected]:node-browser-compat/btoa.git
git Push Origin -u master

Bonne journée!

Remarque: Si vous avez créé un dépôt avec README.md, .gitignore et LICENSE, vous devez d'abord extraire:

git pull Origin -u master
git Push Origin -u master

Enfin, je veux supprimer le dossier du dépôt principal

git rm -rf btoa

...

Appendice

Dernier git sur OS X

Pour obtenir la dernière version de git:

brew install git

Pour obtenir une infusion pour OS X:

http://brew.sh

Dernier git sur Ubuntu

Sudo apt-get update
Sudo apt-get install git
git --version

Si cela ne fonctionne pas (vous avez une très ancienne version d'ubuntu), essayez

Sudo add-apt-repository ppa:git-core/ppa
Sudo apt-get update
Sudo apt-get install git

Si cela ne fonctionne toujours pas, essayez

Sudo chmod +x /usr/share/doc/git/contrib/subtree/git-subtree.sh
Sudo ln -s \
/usr/share/doc/git/contrib/subtree/git-subtree.sh \
/usr/lib/git-core/git-subtree

Merci à rui.araujo des commentaires.

effacer votre histoire

Par défaut, supprimer des fichiers de git ne les supprime pas réellement, cela signifie simplement qu'ils ne sont plus là. Si vous voulez réellement supprimer les références historiques (c'est-à-dire que vous avez un mot de passe validé), procédez comme suit:

git filter-branch --Prune-empty --tree-filter 'rm -rf <name-of-folder>' HEAD

Après cela, vous pouvez vérifier que votre fichier ou dossier n'apparaît plus du tout dans l'historique git.

git log -- <name-of-folder> # should show nothing

Cependant, vous ne pouvez pas "Pousser" les suppressions dans github, etc. Si vous essayez, vous obtiendrez une erreur et vous devrez git pull avant de pouvoir git Push - et vous reviendrez alors à tout avoir dans votre historique.

Donc, si vous souhaitez supprimer l'historique de "l'origine", c'est-à-dire le supprimer de github, bitbucket, etc., vous devrez supprimer le référentiel et ré-envoyer une copie de ce dernier au format. Mais attendez - il y a plus! - Si vous souhaitez vraiment vous débarrasser d'un mot de passe ou de quelque chose du genre, vous devez modifier la sauvegarde (voir ci-dessous).

.git plus petit

La commande de suppression de l'historique mentionnée ci-dessus laisse encore un tas de fichiers de sauvegarde, car git est très gentil pour vous aider à ne pas ruiner votre repo par accident. Il supprimera éventuellement les fichiers orphelins au fil des jours et des mois, mais les conservera pendant un certain temps au cas où vous réaliseriez que vous avez supprimé accidentellement quelque chose que vous ne vouliez pas.

Donc si vous voulez vraiment vider la corbeille pour réduire immédiatement la taille du clone d'un dépôt, vous devez faire tout ce travail vraiment bizarre:

rm -rf .git/refs/original/ && \
git reflog expire --all && \
git gc --aggressive --Prune=now

git reflog expire --all --expire-unreachable=0
git repack -A -d
git Prune

Cela dit, je vous recommande de ne pas suivre ces étapes, sauf si vous savez que vous devez le faire - juste au cas où vous élagueriez le mauvais sous-répertoire, vous savez? Les fichiers de sauvegarde ne doivent pas être clonés lorsque vous appuyez sur le référentiel, ils seront simplement dans votre copie locale.

Crédit

1189
CoolAJ86

La réponse de Paul crée un nouveau référentiel contenant/ABC, mais ne supprime pas/ABC de/XYZ. La commande suivante supprimera/ABC de/XYZ:

git filter-branch --tree-filter "rm -rf ABC" --Prune-empty HEAD

Bien sûr, testez-le d'abord dans un référentiel 'clone --no-hardlinks' et suivez-le avec les listes de Paul de reset, gc et Prune.

131
pgs

J'ai constaté que pour supprimer correctement l'ancien historique du nouveau référentiel, vous devez effectuer un peu plus de travail après l'étape filter-branch.

  1. Faites le clone et le filtre:

    git clone --no-hardlinks foo bar; cd bar
    git filter-branch --subdirectory-filter subdir/you/want
    
  2. Supprimez toutes les références à l'ancienne histoire. "Origin" gardait une trace de votre clone, et "original" est l'endroit où filter-branch enregistre l'ancien:

    git remote rm Origin
    git update-ref -d refs/original/refs/heads/master
    git reflog expire --expire=now --all
    
  3. Même maintenant, votre historique pourrait être bloquée dans un fichier pack que fsck ne touchera pas. Déchirez-le en lambeaux, créez un nouveau fichier pack et supprimez les objets inutilisés:

    git repack -ad
    

Il y a une explication de ceci dans le manuel pour le filtre-branche .

94
Josh Lee

Modifier: script Bash ajouté.

Les réponses données ici ne fonctionnaient que partiellement pour moi; Beaucoup de gros fichiers sont restés dans la mémoire cache. Ce qui a finalement fonctionné (après des heures dans #git sur freenode):

git clone --no-hardlinks file:///SOURCE /tmp/blubb
cd blubb
git filter-branch --subdirectory-filter ./PATH_TO_EXTRACT  --Prune-empty --tag-name-filter cat -- --all
git clone file:///tmp/blubb/ /tmp/blooh
cd /tmp/blooh
git reflog expire --expire=now --all
git repack -ad
git gc --Prune=now

Avec les solutions précédentes, la taille du référentiel était d'environ 100 Mo. Celui-ci l'a ramené à 1,7 MB. Peut-être que ça aide quelqu'un :)


Le script bash suivant automatise la tâche:

!/bin/bash

if (( $# < 3 ))
then
    echo "Usage:   $0 </path/to/repo/> <directory/to/extract/> <newName>"
    echo
    echo "Example: $0 /Projects/42.git first/answer/ firstAnswer"
    exit 1
fi


clone=/tmp/${3}Clone
newN=/tmp/${3}

git clone --no-hardlinks file://$1 ${clone}
cd ${clone}

git filter-branch --subdirectory-filter $2  --Prune-empty --tag-name-filter cat -- --all

git clone file://${clone} ${newN}
cd ${newN}

git reflog expire --expire=now --all
git repack -ad
git gc --Prune=now
39
Simon A. Eugster

Ce n'est plus si complexe que vous pouvez simplement utiliser la commande git filter-branch sur un clone de votre référentiel pour supprimer les sous-répertoires que vous ne voulez pas, puis transférer vers la nouvelle télécommande.

git filter-branch --Prune-empty --subdirectory-filter <YOUR_SUBDIR_TO_KEEP> master
git Push <MY_NEW_REMOTE_URL> -f .
22
jeremyjjbrown

Update: le module git-subtree était si utile que l'équipe git l'a intégré au noyau et l'a rendu git subtree. Voir ici: Détachez (déplacez) le sous-répertoire dans un référentiel Git séparé

git-subtree peut être utile pour cela

http://github.com/apenwarr/git-subtree/blob/master/git-subtree.txt (obsolète)

http://psionides.jogger.pl/2010/02/04/sharing-code-between-projects-with-git-subtree/

19
D W

Voici une petite modification à CoolAJ86 's "The Easy Way ™" answer afin de scinder plusieurs sous-dossiers (disons sub1et sub2) dans un nouveau référentiel git.

The Easy Way ™ (plusieurs sous-dossiers)

  1. Préparer le vieux repo

    pushd <big-repo>
    git filter-branch --tree-filter "mkdir <name-of-folder>; mv <sub1> <sub2> <name-of-folder>/" HEAD
    git subtree split -P <name-of-folder> -b <name-of-new-branch>
    popd
    

    Remarque:<name-of-folder> ne doit PAS contenir de caractères de début ou de fin. Par exemple, le dossier nommé subproject DOIT être passé sous la forme subproject, NOT ./subproject/

    Remarque pour les utilisateurs de Windows: lorsque la profondeur de votre dossier est> 1, <name-of-folder> doit comporter un séparateur de dossiers de style * nix (/). Par exemple, le dossier nommé path1\path2\subproject DOIT être passé en tant que path1/path2/subproject. De plus, n'utilisez pas mvcommand mais move.

    Note finale: l'unique et grande différence avec la réponse de base est la deuxième ligne du script "git filter-branch..."

  2. Créer le nouveau repo

    mkdir <new-repo>
    pushd <new-repo>
    
    git init
    git pull </path/to/big-repo> <name-of-new-branch>
    
  3. Liez le nouveau repo à Github ou ailleurs

    git remote add Origin <[email protected]:my-user/new-repo.git>
    git Push Origin -u master
    
  4. Nettoyage, si désiré

    popd # get out of <new-repo>
    pushd <big-repo>
    
    git rm -rf <name-of-folder>
    

    Note: cela laisse toutes les références historiques dans le référentiel. Voir le Annexe dans la réponse d'origine si vous êtes réellement préoccupé par la saisie d'un mot de passe ou si vous devez réduire la taille du fichier .git. .

16
Anthony O.

La question initiale veut que les fichiers XYZ/ABC/(*) deviennent ABC/ABC/(*). Après avoir implémenté la réponse acceptée pour mon propre code, j'ai remarqué que cela transforme réellement XYZ/ABC/(* fichiers) en ABC/(* fichiers). La page de manuel filter-branch dit même: 

Le résultat contiendra ce répertoire (et seulement celui-là) en tant que racine du projet . " 

En d’autres termes, le dossier de niveau supérieur est "promu" d’un niveau. C'est une distinction importante car, par exemple, dans mon historique, j'avais renommé un dossier de niveau supérieur. En promouvant les dossiers "up" d'un niveau, git perd la continuité au commit où je l'ai renommé.

I lost contiuity after filter-branch

Ma réponse à la question est alors de faire 2 copies du référentiel et de supprimer manuellement le ou les dossiers que vous souhaitez conserver dans chacun. La page de manuel me soutient avec ceci:

[...] évitez d'utiliser [cette commande] si un simple commit suffit à résoudre votre problème

11
MM.

Pour ajouter à la réponse de Paul , j'ai trouvé que pour récupérer de l'espace, je dois pousser HEAD dans un référentiel propre et réduire la taille du répertoire .git/objects/pack.

c'est à dire.

 $ mkdir ... ABC.git 
 $ cd ... ABC.git 
 $ git init --bare 

Après le gc Prune, faites aussi:

 $ git Push ... ABC.git HEAD 

Alors tu peux faire

 $ git clone ... ABC.git 

et la taille de ABC/.git est réduite

En fait, certaines étapes fastidieuses (par exemple, git gc) ne sont pas nécessaires avec le référentiel Push to clean, à savoir:

 $ git clone --no-hardlinks/XYZ /ABC
$ git filtre-branche - sous-répertoire-filtre ABC HEAD 
 $ git reset --hard 
 $ git Push ... ABC. git HEAD 
7
Case Larsen

La manière correcte est maintenant la suivante:

git filter-branch --Prune-empty --subdirectory-filter FOLDER_NAME [first_branch] [another_branch]

GitHub a même maintenant un petit article sur de tels cas.

Mais assurez-vous de cloner d'abord votre référentiel d'origine dans un répertoire séparé (car cela supprimerait tous les fichiers et autres répertoires et vous auriez probablement besoin de les utiliser).

Donc, votre algorithme devrait être:

  1. cloner votre dépôt distant dans un autre répertoire
  2. en utilisant git filter-branch, il ne restait que des fichiers dans un sous-répertoire, Push to new remote
  3. créer un engagement pour supprimer ce sous-répertoire de votre référentiel distant d'origine
6

Il semble que la plupart (toutes?) Des réponses ici reposent sur une forme de git filter-branch --subdirectory-filter et ses semblables. Cela peut fonctionner "la plupart du temps", cependant, dans certains cas, par exemple lorsque vous avez renommé le dossier, par exemple:

 ABC/
    /move_this_dir # did some work here, then renamed it to

ABC/
    /move_this_dir_renamed

Si vous effectuez un style de filtre git normal pour extraire "move_me_renamed", vous perdrez l'historique des modifications de fichiers survenu depuis la date initiale de move_this_dir ( ref ).

Il semble donc que le seul moyen de conserver réellement l'historique des modifications de all (si votre cas est semblable à celui-ci) consiste essentiellement à copier le référentiel (créer un nouveau référentiel, définissant ainsi l'origine), puis tout le reste et renommez le sous-répertoire en parent comme ceci:

  1. Cloner le projet multi-module localement
  2. Branches - vérifiez ce qu’il y a: git branch -a
  3. Effectuez une commande dans chaque branche à inclure dans le groupe pour obtenir une copie locale sur votre poste de travail: git checkout --track Origin/branchABC
  4. Faites une copie dans un nouveau répertoire: cp -r oldmultimod simple
  5. Accédez à la nouvelle copie du projet: cd simple
  6. Supprimez les autres modules inutiles dans ce projet: 
  7. git rm otherModule1 other2 other3
  8. Maintenant il ne reste que le sous-répertoire du module cible 
  9. Supprimez le sous-répertoire du module afin que la racine du module devienne la nouvelle racine du projet.
  10. git mv moduleSubdir1/* .
  11. Supprimer le sous-répertoire de la relique: rmdir moduleSubdir1
  12. Vérifier les modifications à tout moment: git status
  13. Créez le nouveau dépôt git et copiez son URL pour y diriger ce projet:
  14. git remote set-url Origin http://mygithost:8080/git/our-splitted-module-repo
  15. Vérifiez que c'est bon: git remote -v
  16. Poussez les modifications vers le référentiel distant: git Push
  17. Allez au dépôt à distance et vérifiez que tout y est
  18. Répétez-le pour toute autre branche nécessaire: git checkout branch2

Ceci suit le document github "Diviser un sous-dossier dans un nouveau référentiel" étapes 6 à 11 pour pousser le module dans un nouveau référentiel.

Cela ne vous épargnera aucun espace dans votre dossier .git, mais conservera tout l'historique de vos modifications pour ces fichiers, même après les avoir renommés. Et cela ne vaut peut-être pas la peine s'il n'y a pas "beaucoup" d'histoire perdue, etc. Mais au moins, vous êtes assuré de ne pas perdre d'anciens commits!

5
rogerdpack

J'ai eu exactement ce problème, mais toutes les solutions standard basées sur git filter-branch étaient extrêmement lentes. Si vous avez un petit référentiel, alors ce n'est peut-être pas un problème, c'était pour moi. J'ai écrit un autre programme de filtrage git basé sur libgit2 qui, dans un premier temps, crée des branches pour chaque filtrage du référentiel principal, puis les pousse pour nettoyer les référentiels à l'étape suivante. Sur mon référentiel (500Mo 100000 validés), les méthodes standard de branchement de filtres git prenaient des jours. Mon programme prend quelques minutes pour faire le même filtrage.

Il a le nom fabuleux de git_filter et vit ici: 

https://github.com/slobobaby/git_filter

sur GitHub.

J'espère que c'est utile à quelqu'un.

4
slobobaby

Voici comment utiliser GitHub sur une machine Windows. Supposons que vous ayez un référentiel cloné en résidant dans C:\dir1. La structure du répertoire ressemble à ceci: C:\dir1\dir2\dir3. Le répertoire dir3 est celui que je veux être un nouveau dépôt séparé. 

Github:

  1. Créez votre nouveau référentiel: MyTeam/mynewrepo

Invite Bash:

  1. $ cd c:/Dir1
  2. $ git filter-branch --Prune-empty --subdirectory-filter dir2/dir3 HEAD
    Renvoyé: Ref 'refs/heads/master' was rewritten (fyi: dir2/dir3 est sensible à la casse.)

  3. $ git remote add some_name [email protected]:MyTeam/mynewrepo.git
    git remote add Origin etc. n'a pas fonctionné, a renvoyé "remote Origin already exists"

  4. $ git Push --progress some_name master 

4
James Lawruk

Le moyen le plus simple

  1. installer git splits . Je l'ai créé comme une extension git, basée sur la solution de jkeating
  2. Fractionner les répertoires dans une branche locale #change into your repo's directory cd /path/to/repo #checkout the branch git checkout XYZ
    #split multiple directories into new branch XYZ git splits -b XYZ XY1 XY2

  3. Créez un dépôt vide quelque part. Nous supposerons que nous avons créé un dépôt vide appelé xyz sur GitHub qui a le chemin suivant: [email protected]:simpliwp/xyz.git

  4. Appuyez sur le nouveau repo . #add a new remote Origin for the empty repo so we can Push to the empty repo on GitHub git remote add Origin_xyz [email protected]:simpliwp/xyz.git #Push the branch to the empty repo's master branch git Push Origin_xyz XYZ:master

  5. Cloner le référentiel distant nouvellement créé dans un nouveau répertoire local
    #change current directory out of the old repo cd /path/to/where/you/want/the/new/local/repo #clone the remote repo you just pushed to git clone [email protected]:simpliwp/xyz.git

3
AndrewD

Utilisez cette commande de filtrage pour supprimer un sous-répertoire tout en préservant vos balises et branches:

git filter-branch --index-filter \
"git rm -r -f --cached --ignore-unmatch DIR" --Prune-empty \
--tag-name-filter cat -- --all
3
cmcginty

Je recommande le guide de GitHub pour fractionner les sous-dossiers dans un nouveau référentiel . Les étapes sont similaires à réponse de Paul , mais j'ai trouvé leurs instructions plus faciles à comprendre.

J'ai modifié les instructions pour qu'elles s'appliquent à un référentiel local plutôt qu'à un hébergeur sur GitHub.


Division d'un sous-dossier dans un nouveau référentiel

  1. Ouvrez Git Bash.

  2. Modifiez le répertoire de travail actuel à l'emplacement où vous souhaitez créer votre nouveau référentiel.

  3. Clonez le référentiel contenant le sous-dossier.

git clone OLD-REPOSITORY-FOLDER NEW-REPOSITORY-FOLDER
  1. Changez le répertoire de travail actuel dans votre référentiel cloné.

cd REPOSITORY-NAME
  1. Pour filtrer le sous-dossier du reste des fichiers du référentiel, exécutez git filter-branch en fournissant les informations suivantes:
    • FOLDER-NAME: le dossier de votre projet dans lequel vous souhaitez créer un référentiel distinct de .
      • Conseil: Les utilisateurs Windows doivent utiliser / pour délimiter des dossiers.
    • BRANCH-NAME: branche par défaut de votre projet actuel, par exemple, master ou gh-pages.

git filter-branch --Prune-empty --subdirectory-filter FOLDER-NAME  BRANCH-NAME 
# Filter the specified branch in your directory and remove empty commits
Rewrite 48dc599c80e20527ed902928085e7861e6b3cbe6 (89/89)
Ref 'refs/heads/BRANCH-NAME' was rewritten
3

Comme je mentionné ci-dessus , je devais utiliser la solution inverse (supprimer tous les commits ne touchant pas mon dir/subdir/targetdir) qui semblait fonctionner plutôt bien en supprimant environ 95% des commits (comme vous le souhaitez). Il reste cependant deux petits problèmes.

FIRST, filter-branch a fait un travail remarquable en supprimant les validations qui introduisent ou modifient le code, mais apparemment, fusions validées se trouvent sous sa position dans le Gitivers.

C’est un problème esthétique avec lequel je peux probablement vivre (dit-il… reculant lentement, les yeux écartés) .

SECONDE les quelques commits qui restent sont à peu près ALL dupliqués! Il me semble avoir acquis une deuxième chronologie redondante qui couvre à peu près toute l’histoire du projet. La chose intéressante (que vous pouvez voir sur la photo ci-dessous), est que mes trois succursales locales ne sont pas toutes sur la même chronologie (ce qui explique certainement pourquoi elle existe et ne consiste pas seulement en une récupération de déchets).

La seule chose que je puisse imaginer, c’est que l’un des commits supprimés était, peut-être, le commit de la fusion unique que filter-branch a effectivement supprimé , et qui a créé le montage parallèle chaque brin maintenant non fusionné a pris sa propre copie des commits. ( haussement d'épaules Où est mon TARDiS?) Je suis à peu près sûr de pouvoir régler ce problème, bien que j'adore vraiment comprendre comment cela s'est passé.

Dans le cas de la folle mergefest-O-RAMA, je vais probablement laisser celle-ci seule, car elle est si fermement ancrée dans mon histoire de commit (me menaçant chaque fois que je m'approche), elle ne semble pas réellement causer aucun problème non esthétique et parce qu'il est assez joli dans Tower.app.

3
Jay Allen

Vous aurez peut-être besoin de quelque chose comme "git reflog expire --expire = now --all" avant la récupération de place pour nettoyer les fichiers. git filter-branch supprime simplement les références dans l'historique, mais ne supprime pas les entrées de reflog contenant les données. Bien sûr, testez ceci en premier.

Mon utilisation du disque a considérablement chuté, même si mes conditions initiales étaient quelque peu différentes. Peut-être que --subdirectory-filter annule ce besoin, mais j'en doute.

2

Découvrez le projet git_split à https://github.com/vangorra/git_split

Transformez les répertoires git en leurs propres référentiels, à leur propre emplacement. Aucune sous-affaire drôle. Ce script utilisera un répertoire existant dans votre référentiel git et transformera ce répertoire en un référentiel indépendant. En cours de route, il copiera l’ensemble de l’historique des modifications du répertoire fourni. 

./git_split.sh <src_repo> <src_branch> <relative_dir_path> <dest_repo>
        src_repo  - The source repo to pull from.
        src_branch - The branch of the source repo to pull from. (usually master)
        relative_dir_path   - Relative path of the directory in the source repo to split.
        dest_repo - The repo to Push to.
2
vangorra

Mettez ceci dans votre gitconfig:

reduce-to-subfolder = !sh -c 'git filter-branch --tag-name-filter cat --Prune-empty --subdirectory-filter cookbooks/Unicorn HEAD && git reset --hard && git for-each-ref refs/original/ | cut -f 2 | xargs -n 1 git update-ref -d && git reflog expire --expire=now --all && git gc --aggressive --Prune=now && git remote rm Origin'
1
grosser

Je suis sûr que git sous-arbre est parfait et merveilleux, mais mes sous-répertoires de code géré par git que je voulais déplacer étaient tous dans Eclipse . Donc, si vous utilisez egit, c’est terriblement facile . vous voulez déplacer et équipe-> déconnectez-le, puis équipe-> partagez-le au nouvel emplacement. Il essaiera par défaut d’utiliser l’ancien emplacement du référentiel, mais vous pourrez décocher la sélection existante et utiliser le nouvel emplacement pour le déplacer.

1
stu

Vous pouvez facilement essayer le https://help.github.com/enterprise/2.15/user/articles/splitting-a-subfolder-out-into-a-new-repository/

Cela a fonctionné pour moi. Les problèmes que j'ai rencontrés dans les étapes ci-dessus sont 

  1. dans cette commande git filter-branch --Prune-empty --subdirectory-filter FOLDER-NAME BRANCH-NAME Le BRANCH-NAME est maître

  2. si la dernière étape échoue lors de la validation en raison d’un problème de protection, suivez - https://docs.gitlab.com/ee/user/project/protected_branches.html

0
Barath Ravichander

J'ai trouvé une solution assez simple. L'idée est de copier le référentiel puis de supprimer les parties inutiles. Voilà comment cela fonctionne:

1) Clonez un référentiel que vous souhaitez scinder

git clone [email protected]:testrepo/test.git

2) Déplacer vers le dossier git

cd test/

2) Supprimer les dossiers inutiles et le commettre

rm -r ABC/
git add .
enter code here
git commit -m 'Remove ABC'

3) Supprimez l’historique de la forme des dossiers inutiles avec BFG

cd ..
Java -jar bfg.jar --delete-folders "{ABC}" test
cd test/
git reflog expire --expire=now --all && git gc --Prune=now --aggressive

pour multiplier les dossiers, vous pouvez utiliser une virgule

Java -jar bfg.jar --delete-folders "{ABC1,ABC2}" metric.git

4) Vérifiez que l'historique ne contient pas les fichiers/dossiers que vous venez de supprimer

git log --diff-filter=D --summary | grep delete

5) Maintenant, vous avez un référentiel propre sans ABC, alors il suffit de le pousser dans la nouvelle origine

remote add Origin [email protected]:username/new_repo
git Push -u Origin master

C'est tout. Vous pouvez répéter les étapes pour obtenir un autre référentiel,

il suffit de supprimer XY1, XY2 et de renommer XYZ -> ABC à l'étape 3

0
Vladislav Troyan