123 votes

Comment extraire un sous-répertoire git et en faire un sous-module ?

J'ai commencé un projet il y a quelques mois et j'ai tout stocké dans un répertoire principal. Dans mon répertoire principal "Project", il y a plusieurs sous-répertoires contenant différentes choses : Project/paper contient un document écrit en LaTeX Project/sourcecode/RailsApp contient mon application rails.

Le "Projet" est GITifié et il y a eu beaucoup de commits dans les répertoires "paper" et "RailsApp". Maintenant, comme j'aimerais utiliser cruisecontrol.rb pour mon "RailsApp", je me demande s'il existe un moyen de créer un sous-module à partir de "RailsApp" sans perdre l'historique.

Des suggestions ?

128voto

apenwarr Points 4956

Il existe aujourd'hui un moyen beaucoup plus facile de le faire que d'utiliser manuellement git filter-branch : sous-arbre git

Installation

git clone https://github.com/apenwarr/git-subtree.git

cd git-subtree
sudo rsync -a ./git-subtree.sh /usr/local/bin/git-subtree

Ou si vous voulez les pages de manuel et tout

make doc
make install

Utilisation

Divisez un gros morceau en petits morceaux :

# Go into the project root
cd ~/my-project

# Create a branch which only contains commits for the children of 'foo'
git subtree split --prefix=foo --branch=foo-only

# Remove 'foo' from the project
git rm -rf ./foo

# Create a git repo for 'foo' (assuming we already created it on github)
mkdir foo
pushd foo
git init
git remote add origin git@github.com:my-user/new-project.git
git pull ../ foo-only
git push origin -u master
popd

# Add 'foo' as a git submodule to `my-project`
git submodule add git@github.com:my-user/new-project.git foo

Pour une documentation détaillée (page de manuel), veuillez lire git-subtree.txt .

41voto

Pat Notz Points 46841

Checkout git filter-branch .

El Examples section de la page de manuel montre comment extraire un sous-répertoire dans son propre projet tout en gardant tout son historique et en supprimant l'historique des autres fichiers/répertoires (exactement ce que vous recherchez).

Pour réécrire le référentiel afin de faire croire que foodir/ avait été son projet Racine, et écarter toute autre histoire :

   git filter-branch --subdirectory-filter foodir -- --all

Ainsi, vous pouvez, par exemple, transformer un sous-répertoire de bibliothèque en un dépôt à part entière.
Notez le -- qui sépare filter-branch des options de révision, et l'option --all pour réécrire toutes les branches et les balises.

13voto

dbr Points 66401

Une façon de procéder est l'inverse : supprimez tout sauf le fichier que vous souhaitez conserver.

En gros, faire une copie du référentiel, puis utilisez git filter-branch pour supprimer tout sauf les fichiers/dossiers que vous souhaitez conserver.

Par exemple, j'ai un projet duquel je souhaite extraire le fichier tvnamer.py vers un nouveau référentiel :

git filter-branch --tree-filter 'for f in *; do if [ $f != "tvnamer.py" ]; then rm -rf $f; fi; done' HEAD

Cela utilise git filter-branch --tree-filter pour passer par chaque commit, exécuter la commande et recommencer le contenu des répertoires résultants. Ceci est extrêmement destructif (vous ne devriez donc le faire que sur une copie de votre référentiel !), et peut prendre un certain temps (environ 1 minute sur un référentiel avec 300 commits et environ 20 fichiers).

La commande ci-dessus exécute simplement le shell-script suivant sur chaque révision, que vous devrez modifier bien sûr (pour qu'il exclue votre sous-répertoire au lieu de tvnamer.py ):

for f in *; do
    if [ $f != "tvnamer.py" ]; then
        rm -rf $f;
    fi;
done

Le plus gros problème évident est qu'il laisse tous les messages de commit, même s'ils n'ont aucun rapport avec le fichier restant. Le script git-remove-empty-commits , répare ceci..

git filter-branch --commit-filter 'if [ z$1 = z`git rev-parse $3^{tree}` ]; then skip_commit "$@"; else git commit-tree "$@"; fi'

Vous devez utiliser le -f forcer l'exécution de l'argument filter-branch à nouveau avec tout ce qui est dans refs/original/ (qui est en fait une sauvegarde)

Bien sûr, cela ne sera jamais parfait, par exemple si vos messages de commit mentionnent d'autres fichiers, mais c'est à peu près ce qu'un courant git permet (pour autant que je sache, en tout cas).

Encore une fois, n'exécutez cette opération que sur une copie de votre référentiel ! - mais en résumé, pour supprimer tous les fichiers sauf "ceci-mon-nom-de-film.txt" :

git filter-branch --tree-filter 'for f in *; do if [ $f != "thisismyfilename.txt" ]; then rm -rf $f; fi; done' HEAD
git filter-branch -f --commit-filter 'if [ z$1 = z`git rev-parse $3^{tree}` ]; then skip_commit "$@"; else git commit-tree "$@"; fi'

3voto

Dietrich Epp Points 72865

Si vous voulez transférer un sous-ensemble de fichiers vers un nouveau dépôt tout en conservant l'historique, vous allez vous retrouver avec un historique complètement nouveau. La façon dont cela fonctionnerait est essentiellement la suivante :

  1. Créer un nouveau référentiel.
  2. Pour chaque révision de votre ancien référentiel, fusionnez les modifications de votre module dans le nouveau référentiel. Cela créera une "copie" de l'historique de votre projet existant.

Il devrait être assez simple d'automatiser cela si cela ne vous dérange pas d'écrire un script petit mais poilu. Simple, oui, mais aussi douloureux. Des gens ont fait de la réécriture d'historique dans Git dans le passé, vous pouvez faire une recherche à ce sujet.

Alternativement : cloner le dépôt, et supprimer le papier dans le clone, supprimer l'application dans l'original. Cela prendrait une minute, c'est garanti de fonctionner, et vous pouvez retourner à des choses plus importantes que d'essayer de purifier votre historique git. Et ne vous inquiétez pas de l'espace disque dur occupé par les copies redondantes de l'historique.

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