53 votes

Comment puis-je re-root un repo git dans un dossier parent tout en préservant l'historique?

J'ai un dépôt Git dans /foo/bar avec un historique de validation important et plusieurs branches.

Je veux maintenant que /foo/baz soit dans le même dépôt que /foo/bar , ce qui (je pense) signifie que je dois créer un nouveau dépôt dans /foo . Cependant, je souhaite conserver l'historique des modifications que j'ai apportées à /foo/bar .

J'ai d'abord pensé à git format-patch suivi de apply, mais les messages de validation ne sont pas conservés.

26voto

Walter Mundt Points 9160

Ce que vous voulez est - git filter-branch, qui peuvent se déplacer d'un ensemble de référentiel dans une sous-arborescence, la préservation de l'histoire en faisant croire que si elle l'a toujours été. Sauvegarder votre dépôt avant d'utiliser ce!

Voici la magie. En /foo/bar, exécutez:

git filter-branch --commit-filter '
    TREE="$1";
    shift;
    SUBTREE=`echo -e 040000 tree $TREE"\tbar" | git mktree`
    git commit-tree $SUBTREE "$@"' -- --all

Que va faire l' /foo/bar respository avoir un autre", le " bar du sous-répertoire avec l'ensemble de son contenu tout au long de toute l'histoire. Vous pouvez ensuite déplacer l'ensemble de pensions jusqu'à l' foo niveau et ajouter baz code.

Mise à jour:

Bon, voilà ce qu'il se passe. Un commit est un lien vers un "arbre" (pensez-y comme un SHA représentant un ensemble de système de fichiers sous-répertoire de contenu) et de certaines "parent" SHA et certaines métadonnées lien auteur/message/etc. L' git commit-tree de commande est le faible niveau des bits qui enveloppe tout cela ensemble. Le paramètre --commit-filter se fait traiter comme une fonction shell et les exécuter à la place de git commit-tree pendant le processus de filtrage, et a à agir comme il.

Ce que je fais est de prendre le premier paramètre -- l'origine de l'arbre de commettre -- et de construire un nouvel "objet de l'arborescence" qui indique que c'est dans un sous-dossier par git mktree, un autre bas-niveau de la commande git. Pour ce faire, j'ai des tuyaux en elle quelque chose qui ressemble à un arbre git -- un ensemble de (mode SP SP SHA ONGLET nom de fichier) lignes; ainsi, la commande echo. La sortie de l' mktree remplace alors le premier paramètre quand j'ai de la chaîne du réel commit-tree; "$@" est un moyen de passer tous les autres paramètres intact, ayant dépouillé le premier sur avec shift. Voir git help mktree et git help commit-tree pour l'info.

Donc, si vous avez besoin de plusieurs niveaux, vous devez nid à quelques niveaux supplémentaires, des objets de l'arborescence (ce n'est pas testé mais l'idée générale):

git filter-branch --commit-filter '
    TREE="$1"
    shift
    SUBTREE1=`echo -e 040000 tree $TREE"\tbar" | git mktree`
    SUBTREE2=`echo -e 040000 tree $SUBTREE1"\tb" | git mktree`
    SUBTREE3=`echo -e 040000 tree $SUBTREE2"\ta" | git mktree`
    git commit-tree $SUBTREE3 "$@"' -- --all

Qui devrait passer au contenu réel vers le bas en a/b/bar (noter l'inversion de l'ordre).

Mise à jour: Intégré des améliorations De Matthieu Alpert de réponse ci-dessous. Sans -- --all cela ne fonctionne que sur les extraits de la branche, mais puisque la question se pose à propos de l'ensemble de pensions, il est plus logique de le faire de cette façon que la succursale par ranch.

16voto

Andrew Aylett Points 16469

Plutôt que de créer un nouveau référentiel, déplacer ce qui est dans votre référentiel en cours dans le bon endroit: créer un nouveau répertoire bar dans votre répertoire courant et déplacer le contenu actuel dans (de sorte que votre code est en /foo/bar/bar). Puis créer un baz le répertoire à côté de votre nouveau bar répertoire (/foo/bar/baz). mv /foo /foo2; mv /foo2/bar /foo; rmdir /foo2 et vous avez terminé :).

Git de renommer le suivi signifie que votre histoire va encore travailler et Git du hachage du contenu signifie que même si vous avez déplacé les choses autour de vous, vous êtes toujours référence à la même des objets dans le référentiel.

6voto

Jon Carter Points 351

J'ai eu une solution personne ne semble avoir encore dit:

Ce que j'ai particulièrement nécessaire d'inclure des fichiers à partir du répertoire parent dans mon référentiel (à faire passer le repo vers le répertoire).

J'ai réalisé ce via:

  • déplacer tous les fichiers (sauf pour .git) dans un nouveau sous-répertoire avec le même nom. Et d' indiquer à git à ce sujet (avec git mv)
  • déplacer tous les fichiers du répertoire parent dans le vide (à l'exception des .git/) répertoire courant et indiquer à git à ce sujet (avec git add)
  • engager l'ensemble de la chose dans le repo, qui n'a pas bougé (git commit).
  • déplacer le répertoire courant un niveau dans la hiérarchie des répertoires. (avec la ligne de commande jiggery-pokery)

J'espère que cela aide le gars à côté, à venir le long, je suis probablement juste d'avoir une cervelle jour, mais j'ai trouvé les réponses ci-dessus sur-élaborer et effrayant (pour ce que j'ai besoin.) Je sais que c'est similaire à Andrew Aylett la réponse ci-dessus, mais ma situation semblait un peu différent et je voulais une vue plus générale.

5voto

Matthew Alpert Points 958

Cela s'ajoute à Walter Mundt accepté de répondre. J'aurais plutôt formulé des commentaires sur sa réponse, mais je n'ai pas la réputation.

Donc, Walter Mundt la méthode fonctionne très bien, mais il ne fonctionne que pour une branche à la fois. Et après la première branche, il peut y avoir des mises en garde qui nécessitent -f pour forcer l'action de grâce. Donc pour faire ce pour toutes les branches à la fois, il suffit d'ajouter "-- --tous les" à la fin:

git filter-branch --commit-filter '
    tree="$1";
    shift;
    subtree=`echo -e 040000 tree $tree"\tsrc" | git mktree`
    git commit-tree $subtree "$@"' -- --all

Et pour ce faire, pour certaines branches, ajouter leurs noms à la fin au lieu de cela, bien que je ne peux pas imaginer pourquoi vous serait de changer la structure de répertoire de quelques-uns seulement des branches.

Lire plus à ce sujet dans la page de man pour git filter-branch. Cependant, veuillez noter le message d'avertissement concernant d'éventuelles difficultés de pousser après l'utilisation d'une telle commande. Assurez-vous de savoir ce que vous faites.

J'apprécierais davantage d'idées sur les problèmes potentiels avec cette méthode.

2voto

Joris Hilhorst Points 154

Un ajout à la réponse acceptée, qui m'a aidé à le faire fonctionner: lorsque j'ai mis le texte répertorié dans un script shell, pour une raison quelconque, le -e a été conservé. (très probablement parce que je suis trop gros pour travailler avec des scripts shell)

quand j'ai enlevé le -e et déplacé les guillemets pour tout englober, cela a fonctionné. SUBTREE2 = echo "040000 tree $SUBTREE1 modules" | git mktree

notez qu'il existe un onglet entre $ SUBTREE1 et modules, qui est le même que celui que -e doit interpréter.

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