Est-il possible de déplacer / renommer des fichiers dans Git et de conserver leur historique?

667

Je voudrais renommer / déplacer un sous-arbre de projet dans Git en le déplaçant de

/project/xyz

à

/components/xyz

Si j'utilise un plain git mv project components, alors tout l'historique des validations pour le xyz projectest perdu. Y a-t-il un moyen de faire bouger cela de telle sorte que l'histoire soit maintenue?

sgargan
la source
2
Je veux juste noter que je viens de tester le déplacement de fichiers via le système de fichiers, et après avoir validé (via intellij) je peux alors voir tout l'historique (y compris l'historique quand il était à un endroit différent) lors de la visualisation de l'historique (à nouveau dans intellij). Je suppose qu'Intellij ne fait rien de particulièrement spécial pour le faire, donc il est bon de savoir qu'au moins l'histoire peut être retracée.
BT
Pour les règles suivies par Git lors de la détection d'un renommage de répertoire, voir ma réponse ci
VonC
J'ai écrit une réponse ici. J'espère que ca fonctionne. stackoverflow.com/questions/10828267/…
Mahmut EFE

Réponses:

651

Git détecte les renommages plutôt que de persister l'opération avec la validation, que vous utilisiez git mvou mvnon.

La logcommande prend un --followargument qui continue l'historique avant une opération de renommage, c'est-à-dire qu'elle recherche un contenu similaire en utilisant l'heuristique:

http://git-scm.com/docs/git-log

Pour rechercher l'historique complet, utilisez la commande suivante:

git log --follow ./path/to/file
Troels Thomsen
la source
63
Je soupçonne que c'est une considération de performance. Si vous n'avez pas besoin de l'historique complet, il faudra certainement plus de temps pour analyser le contenu. Le moyen le plus simple consiste à configurer un alias git config alias.logf "log --follow"et à simplement écrire git logf ./path/to/file.
Troels Thomsen
13
@TroelsThomsen cet e-mail de Linus Torvalds, lié à cette réponse , indique qu'il s'agit d'un choix de conception intentionnel de Git car il est prétendument beaucoup plus puissant que le suivi des renommages, etc.
Emil Lundberg
127
Cette réponse est un peu trompeuse. Git "détecte les renommages", mais très tard dans le jeu; la question est de savoir comment vous assurer que Git suit les renommages, et quelqu'un qui lit ceci peut facilement déduire que Git les détecte automatiquement pour vous et en prend note. Ce ne est pas. Git ne gère pas vraiment les renommages, et à la place, il existe des outils de fusion / journal qui tentent de comprendre ce qui s'est passé - et rarement de bien faire les choses. Linus a un argument erroné mais véhément pour expliquer pourquoi git ne devrait jamais simplement le faire de la bonne façon et suivre les renommages de manière explicite. Donc, nous sommes coincés ici.
Chris Moschini
29
Important: si vous renommez un répertoire, par exemple lors du changement de nom d'un package Java, assurez-vous d'exécuter deux validations, d'abord pour la commande 'git mv {old} {new}', ensuite pour les mises à jour de tous les fichiers Java qui référencent le répertoire de package modifié. Sinon, git ne peut pas suivre les fichiers individuels même avec le paramètre --follow.
nn4l
44
Bien que Linus fasse probablement très peu d'erreurs, cela semble en être une. Le simple fait de renommer un dossier entraîne le téléchargement d'un delta massif vers GitHub. Ce qui me rend prudent à propos de renommer mes dossiers ... mais c'est une veste assez grande pour un programmeur. Parfois, je DOIS redéfinir le sens de quelque chose, ou changer la façon dont les choses sont classées. Linus: "En d'autres termes, j'ai raison. J'ai toujours raison, mais parfois j'ai plus raison que les autres fois. Et bon sang, quand je dis 'les fichiers n'ont pas d'importance', j'ai vraiment vraiment raison ( tm). " ... J'ai des doutes à ce sujet.
Gabe Halsmer
94

Il est possible de renommer un fichier et de conserver l'historique intact, bien que cela entraîne le changement de nom du fichier tout au long de l'historique du référentiel. C'est probablement seulement pour les amateurs de git-log obsessionnels, et a de sérieuses implications, y compris celles-ci:

  • Vous pourriez réécrire une histoire partagée, ce qui est le plus important à NE PAS FAIRE lorsque vous utilisez Git. Si quelqu'un d'autre a cloné le référentiel, vous le casserez en procédant ainsi. Ils devront recloner pour éviter les maux de tête. Cela pourrait être OK si le changement de nom est suffisamment important, mais vous devrez y réfléchir attentivement - vous pourriez finir par bouleverser toute une communauté opensource!
  • Si vous avez référencé le fichier en utilisant son ancien nom plus tôt dans l'historique du référentiel, vous rompez effectivement les versions antérieures. Pour y remédier, vous devrez faire un peu plus de saut de cerceau. Ce n'est pas impossible, juste fastidieux et peut-être pas la peine.

Maintenant, puisque vous êtes toujours avec moi, vous êtes probablement un développeur solo renommant un fichier complètement isolé. Déplaçons un fichier en utilisant filter-tree!

Supposons que vous allez déplacer un fichier olddans un dossier diret donnez-lui le nomnew

Cela pourrait être fait avec git mv old dir/new && git add -u dir/new, mais cela brise l'histoire.

Au lieu:

git filter-branch --tree-filter 'if [ -f old ]; then mkdir dir && mv old dir/new; fi' HEAD

va refaire chaque commit dans la branche, exécutant la commande dans les ticks pour chaque itération. Beaucoup de choses peuvent mal tourner lorsque vous faites cela. Normalement, je teste pour voir si le fichier est présent (sinon il n'est pas encore là pour se déplacer), puis j'effectue les étapes nécessaires pour chausse-pied l'arbre à mon goût. Ici, vous pouvez parcourir les fichiers pour modifier les références au fichier, etc. Assommez-vous! :)

Une fois terminé, le fichier est déplacé et le journal est intact. Vous vous sentez comme un pirate ninja.

Aussi; Le répertoire mkdir n'est nécessaire que si vous déplacez le fichier vers un nouveau dossier, bien sûr. L' if évitera la création de ce dossier plus tôt dans l'historique que votre fichier n'existe.

Øystein Steimler
la source
57
En tant qu'amant obsessionnel de git-log, je n'irais pas pour cela. Les fichiers n'étaient pas nommés à ce moment-là, donc l'histoire reflète une situation inexistante. Qui sait quels tests pourraient échouer dans le passé! Dans la plupart des cas, le risque de casser des versions antérieures n'en vaut pas la peine.
Vincent
7
@Vincent Vous avez tout à fait raison, et j'ai essayé d'être aussi clair que possible sur le caractère inapproprié de cette solution. Je pense aussi que nous parlons de deux significations du mot "histoire" dans ce cas, j'apprécie les deux.
Øystein Steimler
6
Je trouve qu'il y a des situations où l'on pourrait en avoir besoin. Disons que j'ai développé quelque chose dans ma propre branche personnelle, que je veux maintenant fusionner en amont. Mais je découvre que le nom de fichier n'est pas approprié, donc je le change pour toute ma branche personnelle. De cette façon, je peux garder une histoire propre et propre et avoir le nom correct depuis le début.
user2291758
3
@ user2291758 c'est mon cas d'utilisation. Ces commandes git plus puissantes sont dangereuses mais cela ne signifie pas qu'elles n'ont pas de cas d'utilisation très convaincants si vous savez ce que vous faites!
philix
1
si possible, l'utilisation des --index-filterfor renames sera beaucoup plus rapide car l'arborescence n'aura pas à être récupérée et réintégrée à chaque commit. --index-filteragit directement sur chaque index de commit.
Thomas Guyot-Sionnest
87

Non.

La réponse courte est NON . Il n'est pas possible de renommer un fichier dans Git et de se souvenir de l'historique. Et c'est une douleur.

La rumeur veut que git log --follow--find-copies-hardercela fonctionne, mais cela ne fonctionne pas pour moi, même s'il n'y a aucun changement dans le contenu du fichier et que les déplacements ont été effectués avecgit mv .

(Initialement, j'ai utilisé Eclipse pour renommer et mettre à jour des packages en une seule opération, ce qui peut avoir gêné Git. Mais c'est une chose très courante à faire. --followSemble fonctionner si seulement un mvest effectué, puis un commitet le mvn'est pas trop loin.)

Linus dit que vous êtes censé comprendre l'ensemble du contenu d'un projet logiciel de manière holistique, sans avoir besoin de suivre les fichiers individuels. Eh bien, malheureusement, mon petit cerveau ne peut pas faire ça.

C'est vraiment ennuyeux que tant de gens aient répété sans réfléchir la déclaration selon laquelle Git suit automatiquement les mouvements. Ils ont perdu mon temps. Git ne fait rien de tel. De par sa conception (!) Git ne suit pas du tout les mouvements.

Ma solution consiste à renommer les fichiers à leur emplacement d'origine. Modifiez le logiciel pour l'adapter au contrôle de source. Avec Git, vous semblez juste avoir besoin de "git" correctement la première fois.

Malheureusement, cela casse Eclipse, qui semble utiliser --follow. git log --followparfois, n'affiche pas l'historique complet des fichiers avec des historiques de changement de nom compliqués, même s'il le git logfait. (Je ne sais pas pourquoi.)

(Il y a des hacks trop intelligents qui reviennent en arrière et recommencent du vieux travail, mais ils sont plutôt effrayants. Voir GitHub-Gist: emiller / git-mv-with-history .)

Tuntable
la source
2
Je pense que vous avez raison. J'essayais simplement d'utiliser php-cs-fixer pour reformater la source de mon projet Laravel 5, mais il insiste pour changer la capitalisation des clauses d'espace de noms pour qu'elles correspondent à la valeur en minuscule du dossier d'application. Mais les espaces de noms (ou les chargements automatiques du compositeur) ne fonctionnent qu'avec CamelCase. Je dois changer la mise en majuscule du dossier en App mais cela entraîne la perte de mes modifications. C'est l'exemple le plus trivial, mais montre comment l'heuristique git n'est pas en mesure de suivre même le plus simple des changements de nom (--follow et --find-copies-hardder devrait être la règle, pas l'exception).
Zack Morris
6
git -1, subversion +1
Cosmin
Est-ce toujours vrai? C'est une raison de plus pour moi de rester avec tfs pour l'instant, conserver l'historique d'un fichier déplacé / renommé est un must dans un grand projet.
Cesar
@Cesar Si par "se souvenir de l'historique", il veut dire "suivre les renommages lors de la visualisation du journal" (ce qui est la seule chose utile dont nous devrions nous soucier), alors ce n'était jamais vrai! Git n'enregistre pas les renommages, mais les outils peuvent facilement les détecter et nous présenter des renommages et des mouvements. Si "cela ne fonctionne pas" pour quelqu'un, il / elle devrait changer les outils qu'il / elle utilise. Il y a beaucoup de merveilleuses interfaces graphiques Git qui ont cette capacité.
Mohammad Dehghan
La reponse courte est oui. La version actuelle de Git prend également en charge "git log --follow". et je suis d'accord avec @MohammadDehghan
insung
43
git log --follow [file]

vous montrera l'historique à travers les renommage.

Erik Hesselink
la source
29
Il semble que cela vous oblige à valider uniquement le changement de nom avant de commencer à modifier le fichier. Si vous déplacez le fichier (dans le shell) puis le modifiez, tous les paris sont désactivés.
yoyo
22
@yoyo: c'est parce que git ne suit pas les renommages, il les détecte. A fait git mvessentiellement un git rm && git add. Il existe des options telles que -M90/ --find-renames=90pour considérer un fichier à renommer lorsqu'il est identique à 90%.
vdboor
22

Je fais:

git mv {old} {new}
git add -u {new}
James M. Greene
la source
3
Le -u ne semble rien faire pour moi, est-il supposé mettre à jour l'historique?
jeremy
1
Peut-être voulez-vous -Aplutôt le comportement de ? Encore une fois, voir ici: git-scm.com/docs/git-add
James M. Greene
1
Il ajoute les fichiers, mais il ne met pas à jour l'historique afin que «nom du fichier journal git» affiche l'historique complet. Il affiche uniquement l'historique complet si vous utilisez toujours l'option --follow.
jeremy
3
J'ai fait un refactoriseur compliqué qui a déplacé un répertoire include (en utilisant mv, pas git mv), puis changé beaucoup de chemins #include dans les fichiers renommés. git n'a pas pu trouver suffisamment de similitudes pour suivre l'historique. Mais git add -u était juste la chose dont j'avais besoin. git status indique désormais "renommé" alors qu'avant il montrait "supprimé" et "nouveau fichier".
AndyJost
1
Il y a beaucoup de questions sur SO qui répondent à l'objectif de git add -u. Les documents Git ont tendance à être inutiles et sont le dernier endroit où je voudrais regarder. Voici un article affiché git add -uen action: stackoverflow.com/a/2117202 .
nobar
17

Je voudrais renommer / déplacer un sous-arbre de projet dans Git en le déplaçant de

/project/xyz

à

/ composants / xyz

Si j'utilise un plain git mv project components, alors tout l'historique de validation du xyzprojet est perdu.

Non (8 ans plus tard, Git 2.19, Q3 2018), car Git détectera le renommage du répertoire , et cela est désormais mieux documenté.

Voir commit b00bf1c , commit 1634688 , commit 0661e49 , commit 4d34dff , commit 983f464 , commit c840e1a , commit 9929430 (27 juin 2018) et commit d4e8062 , commit 5dacd4a (25 juin 2018) par Elijah Newren ( newren) .
(Fusionné par Junio ​​C Hamano - gitster- en commit 0ce5a69 , 24 juil.2018 )

Cela est maintenant expliqué dans Documentation/technical/directory-rename-detection.txt:

Exemple:

Lorsque tous x/a, x/bet x/cont été déplacés vers z/a, z/bet z/c, il est probable que x/dajouté entre-temps souhaiterait également passer à z/den supposant que le répertoire entier ' x' a été déplacé ' z'.

Mais ce sont bien d'autres cas, comme:

un côté de l'historique renomme x -> zet l'autre renomme un fichier x/e, ce qui oblige la fusion à effectuer un changement de nom transitif.

Pour simplifier la détection de renommage de répertoire, ces règles sont appliquées par Git:

une couple de règles de base limite lorsque la détection de renommage de répertoire s'applique:

  1. Si un répertoire donné existe toujours des deux côtés d'une fusion, nous ne le considérons pas comme ayant été renommé.
  2. Si un sous-ensemble de fichiers à renommer contient un fichier ou un répertoire (ou serait le même), "désactivez" le répertoire renommé pour ces sous-chemins spécifiques et signalez le conflit à l'utilisateur .
  3. Si l'autre côté de l'historique a renommé un répertoire en un chemin que votre côté de l'historique a renommé, ignorez ce renommage particulier de l'autre côté de l'historique pour tout renommage de répertoire implicite (mais avertissez l'utilisateur).

Vous pouvez voir de nombreux tests dans t/t6043-merge-rename-directories.sh, qui soulignent également que:

  • a) Si les renommages divisent un répertoire en deux ou plusieurs autres, le répertoire avec le plus de noms, "gagne".
  • b) Évitez la détection de renommage de répertoire pour un chemin, si ce chemin est la source d'un renommage de chaque côté d'une fusion.
  • c) N'appliquez des renommages de répertoire implicites aux répertoires que si l'autre côté de l'historique est celui qui effectue le changement de nom.
VonC
la source
15

Objectif

  • Utilisation (inspiré de Smar , emprunté à Exherbo )git am
  • Ajouter l'historique de validation des fichiers copiés / déplacés
  • D'un répertoire à l'autre
  • Ou d'un référentiel à un autre

Limitation

  • Les balises et les branches ne sont pas conservées
  • L'historique est coupé lors du changement de nom du fichier de chemin (renommer le répertoire)

Sommaire

  1. Extraire l'historique au format e-mail à l'aide
    git log --pretty=email -p --reverse --full-index --binary
  2. Réorganiser l'arborescence des fichiers et mettre à jour les noms de fichiers
  3. Ajouter un nouvel historique à l'aide
    cat extracted-history | git am --committer-date-is-author-date

1. Extraire l'historique au format e-mail

Exemple: l' histoire Extrait du file3, file4etfile5

my_repo
├── dirA
│   ├── file1
│   └── file2
├── dirB            ^
│   ├── subdir      | To be moved
│   │   ├── file3   | with history
│   │   └── file4   | 
│   └── file5       v
└── dirC
    ├── file6
    └── file7

Définir / nettoyer la destination

export historydir=/tmp/mail/dir       # Absolute path
rm -rf "$historydir"    # Caution when cleaning the folder

Extraire l'historique de chaque fichier au format email

cd my_repo/dirB
find -name .git -prune -o -type d -o -exec bash -c 'mkdir -p "$historydir/${0%/*}" && git log --pretty=email -p --stat --reverse --full-index --binary -- "$0" > "$historydir/$0"' {} ';'

Malheureusement option --followou --find-copies-harderne peut pas être combinée avec --reverse. C'est pourquoi l'historique est coupé lorsque le fichier est renommé (ou lorsqu'un répertoire parent est renommé).

Historique temporaire au format e-mail:

/tmp/mail/dir
    ├── subdir
    │   ├── file3
    │   └── file4
    └── file5

Dan Bonachea suggère d'inverser les boucles de la commande de génération de journaux git dans cette première étape: plutôt que d'exécuter git log une fois par fichier, exécutez-le exactement une fois avec une liste de fichiers sur la ligne de commande et générez un seul journal unifié. De cette façon, les validations qui modifient plusieurs fichiers restent une seule validation dans le résultat, et toutes les nouvelles validations conservent leur ordre relatif d'origine. Notez que cela nécessite également des modifications à la deuxième étape ci-dessous lors de la réécriture des noms de fichiers dans le journal (désormais unifié).


2. Réorganisez l'arborescence des fichiers et mettez à jour les noms de fichiers

Supposons que vous souhaitiez déplacer ces trois fichiers dans cet autre référentiel (il peut s'agir du même référentiel).

my_other_repo
├── dirF
│   ├── file55
│   └── file56
├── dirB              # New tree
│   ├── dirB1         # from subdir
│   │   ├── file33    # from file3
│   │   └── file44    # from file4
│   └── dirB2         # new dir
│        └── file5    # from file5
└── dirH
    └── file77

Réorganisez donc vos fichiers:

cd /tmp/mail/dir
mkdir -p dirB/dirB1
mv subdir/file3 dirB/dirB1/file33
mv subdir/file4 dirB/dirB1/file44
mkdir -p dirB/dirB2
mv file5 dirB/dirB2

Votre historique temporaire est maintenant:

/tmp/mail/dir
    └── dirB
        ├── dirB1
        │   ├── file33
        │   └── file44
        └── dirB2
             └── file5

Modifiez également les noms de fichiers dans l'historique:

cd "$historydir"
find * -type f -exec bash -c 'sed "/^diff --git a\|^--- a\|^+++ b/s:\( [ab]\)/[^ ]*:\1/$0:g" -i "$0"' {} ';'

3. Appliquer une nouvelle histoire

Votre autre dépôt est:

my_other_repo
├── dirF
│   ├── file55
│   └── file56
└── dirH
    └── file77

Appliquer des validations à partir de fichiers d'historique temporaires:

cd my_other_repo
find "$historydir" -type f -exec cat {} + | git am --committer-date-is-author-date

--committer-date-is-author-dateconserve les horodatages de validation d'origine (commentaire de Dan Bonachea ).

Votre autre dépôt est maintenant:

my_other_repo
├── dirF
│   ├── file55
│   └── file56
├── dirB
│   ├── dirB1
│   │   ├── file33
│   │   └── file44
│   └── dirB2
│        └── file5
└── dirH
    └── file77

Utilisez git statuspour voir le nombre de commits prêts à être poussés :-)


Astuce supplémentaire: vérifiez les fichiers renommés / déplacés dans votre référentiel

Pour répertorier les fichiers renommés:

find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow {} ';' | grep '=>'

Plus de personnalisations: vous pouvez exécuter la commande à l' git logaide des options --find-copies-harderou --reverse. Vous pouvez également supprimer les deux premières colonnes en utilisant cut -f3-et en accueillant le motif complet '{. * =>. *}'.

find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow --find-copies-harder --reverse {} ';' | cut -f3- | grep '{.* => .*}'
olibre
la source
4
ATTENTION: cette technique divise les validations qui modifient 2 fichiers ou plus en fragments fragmentés, et en outre brouille leur ordre en triant le nom de fichier (de sorte que les fragments d'un commit d'origine n'apparaissent pas adjacents dans l'historique linéaire). L'historique qui en résulte n'est donc "correct" que fichier par fichier. Si vous déplacez plusieurs fichiers, AUCUN des nouveaux validations dans l'historique résultant ne représente un instantané cohérent des fichiers déplacés qui ont déjà existé dans l'historique du dépôt d'origine.
Dan Bonachea du
2
Salut @DanBonachea. Merci pour vos commentaires intéressants. J'ai réussi à migrer certains dépôts contenant plusieurs fichiers en utilisant cette technique (même avec des fichiers renommés et des fichiers déplacés dans des répertoires). Que proposez-vous de changer dans cette réponse. Pensez-vous que nous devrions ajouter une bannière AVERTISSEMENT en haut de cette réponse expliquant les limites de cette technique? Vive
olibre
2
J'ai adapté cette technique pour éviter le problème en inversant les boucles de la commande git log generation à l'étape 1. C'est-à-dire. au lieu d'exécuter git log une fois par fichier, exécutez-le exactement une fois avec une liste de fichiers sur la ligne de commande et générez un seul journal unifié. De cette façon, les validations qui modifient 2 fichiers ou plus restent une seule validation dans le résultat, et toutes les nouvelles validations conservent leur ordre relatif d'origine. Notez que cela nécessite également des modifications à l'étape 2 lors de la réécriture des noms de fichiers dans le journal (désormais unifié). J'ai également utilisé git am --committer-date-is-author-date pour conserver les horodatages de validation d'origine.
Dan Bonachea
1
Merci pour votre expérimentation et votre partage. J'ai mis à jour un peu la réponse pour les autres lecteurs. J'ai cependant pris le temps de tester votre traitement. N'hésitez pas à modifier cette réponse si vous souhaitez fournir des exemples de lignes de commande.
Santé
4

J'ai suivi ce processus en plusieurs étapes pour déplacer le code vers le répertoire parent et conserver l'historique.

Étape 0: création d'une «historique» de branche à partir de «maître» pour la conservation

Étape 1: utilisé l' outil git-filter-repo pour réécrire l'historique. Cette commande ci-dessous a déplacé le dossier «FolderwithContentOfInterest» à un niveau supérieur et modifié l'historique de validation correspondant

git filter-repo --path-rename ParentFolder/FolderwithContentOfInterest/:FolderwithContentOfInterest/ --force

Étape 2: à ce moment, le référentiel GitHub a perdu son chemin de référentiel distant. Ajout d'une référence à distance

git remote add origin [email protected]:MyCompany/MyRepo.git

Étape 3: extraire des informations sur le référentiel

git pull

Étape 4: connectez la branche locale perdue à la branche d'origine

git branch --set-upstream-to=origin/history history

Étape 5: résoudre le conflit de fusion pour la structure de dossiers si vous y êtes invité

Étape 6: Poussez !!

git push

Remarque: L'historique modifié et le dossier déplacé semblent déjà avoir été validés. enter code here

Terminé. Le code se déplace vers le répertoire parent / souhaité en gardant l'historique intact!

Parag Bangad
la source
2

Bien que le cœur de Git, la plomberie Git ne garde pas la trace des renommages, l'historique que vous affichez avec le journal Git "porcelaine" peut les détecter si vous le souhaitez.

Pour une git logutilisation donnée, l'option -M:

git log -p -M

Avec une version actuelle de Git.

Cela fonctionne aussi pour d'autres commandes git diff.

Il existe des options pour rendre les comparaisons plus ou moins rigoureuses. Si vous renommez un fichier sans apporter de modifications importantes au fichier en même temps, il est plus facile pour Git log et ses amis de détecter le renommage. Pour cette raison, certaines personnes renomment des fichiers dans un commit et les modifient dans un autre.

L'utilisation du processeur a un coût chaque fois que vous demandez à Git de trouver où les fichiers ont été renommés, que vous l'utilisiez ou non, et quand, c'est à vous de décider.

Si vous souhaitez que votre historique soit toujours signalé avec la détection de renommage dans un référentiel particulier, vous pouvez utiliser:

git config diff.renames 1

Les fichiers se déplaçant d'un répertoire à un autre sont détectés. Voici un exemple:

commit c3ee8dfb01e357eba1ab18003be1490a46325992
Author: John S. Gruber <[email protected]>
Date:   Wed Feb 22 22:20:19 2017 -0500

    test rename again

diff --git a/yyy/power.py b/zzz/power.py
similarity index 100%
rename from yyy/power.py
rename to zzz/power.py

commit ae181377154eca800832087500c258a20c95d1c3
Author: John S. Gruber <[email protected]>
Date:   Wed Feb 22 22:19:17 2017 -0500

    rename test

diff --git a/power.py b/yyy/power.py
similarity index 100%
rename from power.py
rename to yyy/power.py

Veuillez noter que cela fonctionne chaque fois que vous utilisez diff, pas seulement avec git log. Par exemple:

$ git diff HEAD c3ee8df
diff --git a/power.py b/zzz/power.py
similarity index 100%
rename from power.py
rename to zzz/power.py

À titre d'essai, j'ai apporté une petite modification dans un fichier dans une branche de fonctionnalité et je l'ai validée, puis dans la branche principale, j'ai renommé le fichier, validé, puis j'ai apporté une petite modification dans une autre partie du fichier et je l'ai validé. Lorsque je suis allé dans la branche de fonctionnalité et que j'ai fusionné à partir de master, la fusion a renommé le fichier et fusionné les modifications. Voici la sortie de la fusion:

 $ git merge -v master
 Auto-merging single
 Merge made by the 'recursive' strategy.
  one => single | 4 ++++
  1 file changed, 4 insertions(+)
  rename one => single (67%)

Le résultat a été un répertoire de travail avec le fichier renommé et les deux modifications de texte effectuées. Il est donc possible pour Git de faire la bonne chose malgré le fait qu'il ne suit pas explicitement les renommages.

Il s'agit d'une réponse tardive à une ancienne question, donc les autres réponses peuvent être correctes pour la version Git à l'époque.

John S Gruber
la source
1

Créez d'abord un commit autonome avec juste un renommage.

Ensuite, toute modification éventuelle du contenu du fichier est placée dans la validation distincte.

Jakub Pawlowski
la source
1

Pour renommer un répertoire ou un fichier (je ne connais pas grand-chose aux cas complexes, il peut donc y avoir quelques mises en garde):

git filter-repo --path-rename OLD_NAME:NEW_NAME

Pour renommer un répertoire dans des fichiers qui le mentionnent (il est possible d'utiliser des rappels, mais je ne sais pas comment):

git filter-repo --replace-text expressions.txt

expressions.txtest un fichier rempli de lignes comme literal:OLD_NAME==>NEW_NAME(il est possible d'utiliser RE avec Python regex:ou glob avecglob: ).

Pour renommer un répertoire dans les messages de commit:

git-filter-repo --message-callback 'return message.replace(b"OLD_NAME", b"NEW_NAME")'

Les expressions régulières de Python sont également prises en charge, mais elles doivent être écrites en Python, manuellement.

Si le référentiel est d'origine, sans télécommande, vous devrez ajouter --force pour forcer une réécriture. (Vous pouvez créer une sauvegarde de votre référentiel avant de le faire.)

Si vous ne souhaitez pas conserver les références (elles seront affichées dans l'historique des branches de Git GUI), vous devrez les ajouter --replace-refs delete-no-add.

Pugsley
la source
0

Déplacez simplement le fichier et mettez en scène avec:

git add .

Avant de valider, vous pouvez vérifier l'état:

git status

Cela montrera:

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        renamed:    old-folder/file.txt -> new-folder/file.txt

J'ai testé avec Git version 2.26.1.

Extrait de la page d'aide de GitHub .

Junior
la source
-3

Je fais déplacer les fichiers et ensuite

git add -A

qui met dans la zone de saturation tous les fichiers supprimés / nouveaux. Ici, git se rend compte que le fichier est déplacé.

git commit -m "my message"
git push

Je ne sais pas pourquoi mais cela fonctionne pour moi.

Xelian
la source
L'astuce ici est que vous n'avez pas à changer une seule lettre, même si vous changez quelque chose et appuyez sur Ctrl + Z, l'historique sera rompu. SO dans ce cas, si vous écrivez quelque chose, rétablissez le fichier, déplacez-le à nouveau et faites un seul add-> commit pour lui.
Xelian