2015-11-23 1 views
2

Je voudrais extraire certains fichiers à un nouveau dépôt, en gardant leur historique, y compris les fichiers renommant.nettoyer l'histoire git de fichiers supprimés, en gardant l'historique des fichiers renommés

La réponse la meilleure et la plus proche que j'ai pu trouver était new-repo-with-copied-history-of-only-currently-tracked-files, en utilisant git filter-branch --index-filter. Il conserve avec succès l'historique des fichiers existants, mais il ne conserve pas l'historique des fichiers renommés.

(Une autre réponse que je pouvais trouver employais git filter-branch --subdirectory-filter Mais il a deux problèmes:. Ne semble pas fonctionner pour l'ensemble repo (dossier) et ne conserve pas l'historique des fichiers renommés. ».)

(une autre réponse utilisait git subtree. Mais il ne tient pas l'histoire du tout.)

Je suis probablement à la recherche d'un moyen d'améliorer la commande git ls-files > keep-these.txt de réponse la plus proche aussi liste tous les noms de fichiers précédents . Peut-être un script?

Répondre

3

Git ne stocke pas les modifications de nom de fichier.

Chaque commettras stocke un arbre complet, par exemple, peut-être commettre 1234567... a des fichiers README et foo.txt et engager fedcba9 ... a des fichiers readme.txt et foo. Si vous demandez à git de comparer commettre 1234567 pour commettre fedcba9, et README est suffisamment similaire à readme.txt, git dira que le moyen de transformer l'un commit à l'autre est de renommer le fichier. (Si l'un commit est le parent de l'autre, git show de l'enfant engager montrera le changement de nom, parce que git show calcule ce changement à git show temps.)

D'autre part, si le second fichier readme est trop différent, mais README est suffisamment similaire à foo, git dira que le moyen de changer 1234567 pour atteindre fedcba9 est de renommer README en foo.

La clé est que git calcule que lorsque vous demandez la comparaison, et pas un instant plus tôt. Il n'y a rien entre les commits qui dit "renommer certains fichiers". Git compare simplement les commits et décide puis si les fichiers sont assez similaires. Pour vos besoins, ce que cela signifie en fin de compte, c'est que pour chaque commit de votre séquence-de-commits-à-copier-ou-partiellement-copier, vous devrez décider quels noms de chemins conserver et lesquels ignorer . Comment atteindre cela est principalement à vous. La commande git log a un indicateur --follow pour activer une quantité limitée de détection de renommer car il fonctionne en arrière depuis les validations enfants vers leurs parents, et git blame essaie automatiquement de faire la même chose; vous pouvez utiliser ces (un nom de chemin à la fois) pour arriver à une cartographie de la forme:

 in: commits A..B C..D    E..F 
use path: dir/file.ext dir/frill.txt lib/frill.next 

par exemple. Mais il n'y a rien de construit pour le faire, et ce ne sera pas particulièrement facile. Je commencerais par combiner git log --follow avec --raw ou --name-status sortie et de voir si il y a des Renommées intéressantes détectées.Si et quand il y en a, ce sont les limites de validation auxquelles vous voudrez changer les chemins que vous gardez et abandonner pendant que vous travaillez à travers les commits (que ce soit avec filter-branch ou une autre méthode).

Si cela ne fonctionne pas ou si vous avez besoin de plus de contrôle, pensez à exécuter git diff --name-status entre différentes paires de validation (avec les informations de paire de validation provenant de git rev-list).


Tant que vous avez demandé pour la détection de changement de nom, « exactement le même » est assez similaire, comme quelque chose à environ « 50% similaires ». Vous pouvez ajuster la similarité requise avec la valeur optionnelle que vous fournissez au drapeau -M de git diff.


Modifier: cela semble fonctionner OK. Je l'ai utilisé sur son propre builtin/var.c git, qui l'habitude d'avoir deux noms précédents selon cette:

$ git log --follow --raw --diff-filter=R --pretty=format:%H builtin/var.c 
81b50f3ce40bfdd66e5d967bf82be001039a9a98 
:100644 100644 2280518... 2280518... R100  builtin-var.c builtin/var.c 

55b6745d633b9501576eb02183da0b0fb1cee964 
:100644 100644 d9892f8... 2280518... R096  var.c builtin-var.c 

Le --diff-filter supprime tout sauf renomme sorties de sorte que nous pouvons voir ce qui semble engager de renommer le fichier. Transformer cela en quelque chose de plus utile nécessite un peu plus de travail, mais cela pourrait vous arriver assez loin:

git log --follow --raw --diff-filter=R --pretty=format:%H builtin/var.c | 
while true; do 
    if ! read hash; then break; fi 
    IFS=$'\t' read mode_etc oldname newname 
    read blankline 
    echo in $hash, rename $oldname to $newname 
done 

qui produit:

in 81b50f3ce40bfdd66e5d967bf82be001039a9a98, rename builtin-var.c to builtin/var.c 
in 55b6745d633b9501576eb02183da0b0fb1cee964, rename var.c to builtin-var.c 
+0

Pouvez-vous fournir un script pour la liste des noms de fichiers précédents? Je ne peux pas comprendre comment le faire, et j'ai des centaines de fichiers à traiter, donc je ne peux pas analyser manuellement les fichiers journaux à la fois. Par défaut 50% similaire est très bien pour moi. –

+0

Grâce à vous, j'ai récupéré la liste des anciens noms de fichiers avec 'git ls-files | tandis que read -r line; do (git log --follow --raw --diff-filtre = R --pretty = format:% H "$ ligne" | while true; fait si! read hash; alors break; fi; IFS = $ '\ t 'read mode_etc oldname nouveau nom; read blankline; echo $ oldname; done); fait –