514 votes

Gestion des renommages de fichiers dans Git

J'avais lu que lorsque renommer des fichiers dans Git vous devez valider toutes les modifications, effectuer votre renommage et ensuite mettre en scène votre fichier renommé. Git reconnaîtra le fichier à partir de son contenu, plutôt que de le voir comme un nouveau fichier non suivi, et conservera l'historique des modifications.

Cependant, en faisant cela ce soir, j'ai fini par revenir à la situation suivante git mv .

> $ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#    modified:   index.html
#

J'ai renommé ma feuille de style dans Finder de iphone.css à mobile.css :

> $ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#    modified:   index.html
#
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#    deleted:    css/iphone.css
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#    css/mobile.css

Donc Git pense maintenant que j'ai supprimé un fichier CSS et que j'en ai ajouté un nouveau. Ce n'est pas ce que je veux. Annulons le renommage et laissons Git faire le travail.

> $ git reset HEAD .
Unstaged changes after reset:
M    css/iphone.css
M    index.html

Je suis de retour à mon point de départ :

> $ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#    modified:   index.html
#

Utilisons git mv à la place :

> $ git mv css/iphone.css css/mobile.css
> $ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#    renamed:    css/iphone.css -> css/mobile.css
#
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#    modified:   index.html
#

Il semble que nous soyons bons. Alors pourquoi Git n'a-t-il pas reconnu le renommage la première fois que j'ai utilisé le Finder ?

33 votes

Git suit le contenu, pas les fichiers, donc la façon dont vous mettez votre index dans l'état approprié n'a pas d'importance. add+rm ou mv - cela produit le même résultat. Git utilise alors sa détection de renommage/copie pour vous indiquer qu'il s'agit d'un renommage. La source que vous avez citée est également inexacte. Le fait que vous modifiez et renommiez dans le même commit n'a pas d'importance. Lorsque vous faites un diff sur la modification et le renommage, la détection de renommage le verra comme un renommage+modification, ou si la modification est une réécriture totale, il apparaîtra comme ajouté et supprimé - toujours sans importance de la façon dont vous l'avez effectué.

7 votes

Si c'est vrai, pourquoi ne l'a-t-il pas détecté lors de mon renommage avec le Finder ?

30 votes

git mv old new met automatiquement à jour l'index. Lorsque vous renommez en dehors de Git, vous devrez faire la démarche suivante git add new y git rm old pour mettre en scène les modifications de l'index. Une fois que vous avez fait cela git status fonctionnera comme vous le souhaitez.

381voto

tanascius Points 22712

Pour git mv la page du manuel dit

L'index est mis à jour après la réussite de l'opération, [ ]

Donc, dans un premier temps, vous devez mettre à jour l'index par vous-même (en utilisant git add mobile.css ). Toutefois, git status montrera toujours deux fichiers différents :

$ git status
# On branch master
warning: LF will be replaced by CRLF in index.html
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   index.html
#       new file:   mobile.css
#
# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#       deleted:    iphone.css
#

Vous pouvez obtenir un résultat différent en exécutant git commit --dry-run -a ce qui aboutit à ce que vous ce que vous attendez :

Tanascius@H181 /d/temp/blo (master)
$ git commit --dry-run -a
# On branch master
warning: LF will be replaced by CRLF in index.html
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#       modified:   index.html
#       renamed:    iphone.css -> mobile.css
#

Je ne peux pas vous dire exactement pourquoi nous voyons ces différences. entre git status y git commit --dry-run -a mais voici un conseil de Linus :

git ne fait même pas soins à propos de l'ensemble "détection des renommages" en interne, et tous les commits que vous avez fait fait avec des renommages sont totalement indépendants de la heuristique que nous utilisons ensuite pour montrer les renommages.

A dry-run utilise les véritables mécanismes de renommage, tandis qu'un git status ne le fait probablement pas.

1 votes

Vous avez omis de mentionner l'étape où vous avez fait git add mobile.css . Sans elle git status -a n'aurait "vu" que la suppression de l'élément précédemment suivi. iphone.css mais n'aurait pas touché le nouveau fichier non tracé. mobile.css fichier. Aussi, git status -a est invalide avec Git 1.7.0 et plus. ""git status" n'est plus "git commit --dry-run"" dans kernel.org/pub/software/scm/git/docs/RelNotes-1.7.0.txt . Utilisez git commit --dry-run -a si vous voulez cette fonctionnalité. Comme d'autres l'ont dit, il suffit de mettre à jour l'index et le fichier git status fonctionnera comme le souhaite le PO.

3 votes

Si vous faites un git commit il ne validera pas le fichier renommé et l'arbre de travail est toujours le même. git commit -a Cela va à l'encontre de presque tous les aspects du modèle de flux de travail/de pensée de Git - chaque modification est validée. index.html dans un autre commit ?

0 votes

@Chris : yep, bien sûr que j'ai ajouté mobile.css que j'aurais dû mentionner. Mais c'est le but de ma réponse : la page de manuel dit que the index is updated lorsque vous utilisez git-mv . Merci pour le status -a clarification, j'ai utilisé git 1.6.4

96voto

nonrectangular Points 598

Vous devez ajouter les deux fichiers modifiés à l'index avant que Git ne le reconnaisse comme un déplacement.

La seule différence entre mv old new y git mv old new est que le git mv ajoute également les fichiers à l'index.

mv old new puis git add -A aurait aussi fonctionné.

Notez que vous ne pouvez pas simplement utiliser git add . car cela n'ajoute pas les suppressions à l'index.

Ver Différence entre "git add -A" et "git add ."

3 votes

Merci pour le git add -A lien, très utile, car je cherchais un tel raccourci !

12 votes

Notez qu'avec git 2, git add . fait ajouter les suppressions à l'index.

19voto

dimuthu Points 98

La meilleure chose à faire est de l'essayer par vous-même.

mkdir test
cd test
git init
touch aaa.txt
git add .
git commit -a -m "New file"
mv aaa.txt bbb.txt
git add .
git status
git commit --dry-run -a

Maintenant git status y git commit --dry-run -a montre deux résultats différents où git status montre bbb.txt comme un nouveau fichier/ aaa.txt est supprimé, et le --dry-run montre le renommage réel.

~/test$ git status

# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   bbb.txt
#
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   deleted:    aaa.txt
#

/test$ git commit --dry-run -a

# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   renamed:    aaa.txt -> bbb.txt
#

Maintenant, allez-y et faites l'enregistrement.

git commit -a -m "Rename"

Maintenant, vous pouvez voir que le fichier est en fait renommé, et ce qui est montré en git status a tort, du moins dans ce cas. Il se peut que l'implémentation de GIT en ligne traite les deux commandes séparément.

Morale de l'histoire : Si vous n'êtes pas sûr que votre fichier a été renommé, faites un "git commit --dry-run -a". S'il montre que le fichier a été renommé, c'est bon.

3 votes

Pour ce qui importe pour Git, les deux sont corrects . Ce dernier point est plus proche de la façon dont vous, en tant que commiteur, le voyez probablement, cependant. Le site réel La différence entre renommer et supprimer + créer se situe uniquement au niveau du système d'exploitation/système de fichiers (par exemple, même inode# contre nouvel inode#), ce dont Git ne se soucie pas vraiment.

11voto

knittl Points 64110

Vous devez git add css/mobile.css le nouveau fichier et git rm css/iphone.css pour que Git soit au courant. Ensuite, il affichera la même sortie dans git status .

Vous pouvez le voir clairement dans la sortie d'état (le nouveau nom du fichier) :

# Untracked files:
#   (use "git add <file>..." to include in what will be committed)

Et (l'ancien nom) :

# Changed but not updated:
#   (use "git add/rm <file>..." to update what will be committed)

Je pense que dans les coulisses git mv n'est rien d'autre qu'un script qui fait exactement cela : supprimer le fichier de l'index et l'ajouter sous un nom différent.

0 votes

Je ne pensais pas que je devais git rm css/iphone.css parce que je pensais que cela supprimerait l'historique existant. Peut-être que je comprends mal le flux de travail dans git.

5 votes

@Greg K : git rm n'effacera pas l'histoire. Il supprime seulement une entrée de l'index de sorte que le prochain commit n'aura pas cette entrée. Cependant, elle existera toujours dans les commits ancestraux. Ce qui peut être confus, c'est que (par exemple) git log -- new s'arrêtera au point où vous avez commis git mv old new . Si vous voulez suivre les renommages, utilisez git log --follow -- new .

8voto

hasenj Points 36139

Git reconnaîtra le fichier à partir de son contenu, plutôt que de le voir comme un nouveau fichier non suivi.

C'est là que vous vous êtes trompé.

C'est seulement après vous ajoutez le fichier, que Git le reconnaîtra à partir du contenu.

1 votes

Exactement. Une fois mis en scène, git montrera le renommage correctement.

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