Quel est le but de git-mv?

287

D'après ce que je comprends, Git n'a pas vraiment besoin de suivre les opérations de renommage / déplacement / copie de fichiers , alors quel est le véritable objectif de git mv ? La page de manuel n'est pas spécialement descriptive ...

Est-ce obsolète? Est-ce une commande interne, non destinée à être utilisée par des utilisateurs réguliers?

Mauricio Scheffer
la source

Réponses:

390
git mv oldname newname

est juste un raccourci pour:

mv oldname newname
git add newname
git rm oldname

c'est-à-dire qu'il met automatiquement à jour l'index pour les anciens et les nouveaux chemins.

CB Bailey
la source
38
De plus, il a quelques sécurités intégrées.
Jakub Narębski
6
Merci @CharlesBailey - git considère-t-il alors les fichiers newNameFile et oldNameFile comme différents? Si oui, que se passe-t-il si nous voulons les fusionner? Supposons que nous branchons un projet ant sur la branche A et créons la branche B, puis mavenisons les projets sur B. Les noms de fichiers sont les mêmes mais placés sur des chemins différents lorsque la structure du projet a changé. Disons que les deux branches ont grandi pendant un certain temps en parallèle. À un moment donné, si nous voulons fusionner les projets, comment git saura-t-il que c'est le même fichier que vous venez de renommer son chemin? (si "git mv" == "git add + git rm")
Rose
2
@SergeyOrshanskiy Si la détection automatique se passe mal mv oldname newname; git add newname; git rm oldname, elle le sera également git mv oldname newname(voir cette réponse ).
Ajedi32
5
Notez que git mvc'est légèrement différent de la mv oldname newname; git add newname; git rm oldname, dans la mesure où si vous avez apporté des modifications au fichier avant de git mvle faire, ces modifications ne seront pas transférées tant que vous n'aurez pas créé git addle nouveau fichier.
Ajedi32
2
git mv fait quelque chose de différent, car il gère les changements dans le cas du nom de fichier (foo.txt en Foo.txt) alors que ces commandes ne s'exécutent pas individuellement (sous OSX)
greg.kindel
66

Du GitFaq officiel :

Git a une commande renommer git mv, mais c'est juste une commodité. L'effet ne se distingue pas de la suppression du fichier et de l'ajout d'un autre avec un nom différent et le même contenu

Adam Nofsinger
la source
8
Alors, perdez-vous l'historique des fichiers? J'étais supposé que le changement de nom conserverait l'historique de ce répertoire ...
Will Hancock
17
Eh bien, oui et non. Lisez le lien GitFaq officiel ci-dessus à propos des renommages, puis lisez les longs e-mails de Linus Torvalds expliquant pourquoi il n'aime pas la notion de fichiers de suivi d'outil SCM: permalink.gmane.org/gmane.comp.version-control.git/ 217
Adam Nofsinger
3
@WillHancock J'ai utilisé git un peu plus maintenant, et je peux vous répondre plus définitivement: en fonction de votre client git et de ses options, vous pourrez tracer le fichier après le renommer si le fichier a changé en interne suffisamment peu pour qu'il le considère comme un Renommer. Si vous modifiez trop le fichier ET le renommez cependant, git ne le détectera pas - dans un sens, il dit "non, vous pourriez aussi bien considérer qu'il s'agit d'un fichier complètement différent!"
Adam Nofsinger
7
@AdamNofsinger ce lien est mort. Voici un miroir: web.archive.org/web/20150209075907/http://…
Carl Walsh
2
Existe-t-il une référence officielle (c.-à-d. Plus digne d'intérêt qu'une FAQ) qui indique l'équivalence entre git mvet l'approche manuelle? Ce n'est pas évident de git help mv.
2017
40

Git essaie juste de deviner pour vous ce que vous essayez de faire. Il s'efforce de préserver une histoire ininterrompue. Bien sûr, ce n'est pas parfait. git mvVous permet donc d'être explicite avec votre intention et d'éviter certaines erreurs.

Considérez cet exemple. En commençant par un repo vide,

git init
echo "First" >a
echo "Second" >b
git add *
git commit -m "initial commit"
mv a c
mv b a
git status

Résultat:

# On branch master
# 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)
#
#   modified:   a
#   deleted:    b
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   c
no changes added to commit (use "git add" and/or "git commit -a")

La détection automatique a échoué :( Ou est-ce le cas?

$ git add *
$ git commit -m "change"
$ git log c

commit 0c5425be1121c20cc45df04734398dfbac689c39
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:24:56 2013 -0400

    change

puis

$ git log --follow c

Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:24:56 2013 -0400

    change

commit 50c2a4604a27be2a1f4b95399d5e0f96c3dbf70a
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:24:45 2013 -0400

    initial commit

Essayez maintenant à la place (n'oubliez pas de supprimer le .gitdossier lors de l'expérimentation):

git init
echo "First" >a
echo "Second" >b
git add *
git commit -m "initial commit"
git mv a c
git status

Jusqu'ici tout va bien:

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


git mv b a
git status

Maintenant, personne n'est parfait:

# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   modified:   a
#   deleted:    b
#   new file:   c
#

Vraiment? Mais bien sûr...

git add *
git commit -m "change"
git log c
git log --follow c

... et le résultat est le même que ci-dessus: --followaffiche uniquement l'historique complet.


Maintenant, soyez prudent avec le changement de nom, car l'une ou l'autre option peut toujours produire des effets étranges . Exemple:

git init
echo "First" >a
git add a
git commit -m "initial a"
echo "Second" >b
git add b
git commit -m "initial b"

git mv a c
git commit -m "first move"
git mv b a
git commit -m "second move"

git log --follow a

commit 81b80f5690deec1864ebff294f875980216a059d
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:35:58 2013 -0400

    second move

commit f284fba9dc8455295b1abdaae9cc6ee941b66e7f
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:34:54 2013 -0400

    initial b

Comparez-le avec:

git init
echo "First" >a
git add a
git commit -m "initial a"
echo "Second" >b
git add b
git commit -m "initial b"

git mv a c
git mv b a
git commit -m "both moves at the same time"

git log --follow a

Résultat:

commit 84bf29b01f32ea6b746857e0d8401654c4413ecd
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:37:13 2013 -0400

    both moves at the same time

commit ec0de3c5358758ffda462913f6e6294731400455
Author: Sergey Orshanskiy <*****@gmail.com>
Date:   Sat Oct 12 00:36:52 2013 -0400

    initial a

Ups ... Maintenant, l'histoire revient à l' initiale a au lieu de l' initiale b , ce qui est faux. Donc, quand nous avons fait deux mouvements à la fois, Git est devenu confus et n'a pas suivi correctement les changements. Soit dit en passant, dans mes expériences, la même chose s'est produite lorsque j'ai supprimé / créé des fichiers au lieu de les utiliser git mv. Procédez avec soin; tu as été prévenu...

osa
la source
5
+1 pour l'explication détaillée. Je cherchais des problèmes qui pourraient survenir dans l'historique des journaux si les fichiers sont déplacés dans git, votre réponse était vraiment intéressante. Je vous remercie! Btw, connaissez-vous d'autres pièges que nous devrions éviter lors du déplacement de fichiers dans git? (ou toute référence que vous pourriez indiquer ... pas très chanceux sur Google)
pabrantes
1
Eh bien, mes exemples sont pessimistes. Lorsque les fichiers sont vides, il est beaucoup plus difficile d'interpréter correctement les modifications. J'imagine que si vous vous engagez juste après chaque ensemble de renommages, tout ira bien.
osa
27

Comme le dit @Charles, git mvc'est un raccourci.

La vraie question ici est "Les autres systèmes de contrôle de version (par exemple Subversion et Perforce) traitent spécialement les renommages de fichiers. Pourquoi Git ne fonctionne-t-il pas?"

Linus explique à http://permalink.gmane.org/gmane.comp.version-control.git/217 avec tact caractéristique:

Veuillez arrêter cette merde de "fichiers de suivi". Git suit exactement ce qui compte, à savoir les "collections de fichiers". Rien d'autre n'est pertinent, et même penser qu'il est pertinent ne fait que limiter votre vision du monde. Remarquez comment la notion de CVS "annoter" finit toujours inévitablement par limiter la façon dont les gens l'utilisent. Je pense que c'est une merde totalement inutile, et j'ai décrit quelque chose qui, je pense, est un million de fois plus utile, et tout est tombé exactement parce que je ne limite pas ma pensée au mauvais modèle du monde.

Colonel Panic
la source
9

Il y a une autre utilisation que j'ai pour git mv pas mentionnée ci-dessus.

Depuis la découverte git add -p(mode patch de git add; voir http://git-scm.com/docs/git-add ), j'aime l'utiliser pour revoir les modifications lorsque je les ajoute à l'index. Ainsi mon workflow devient (1) travailler sur du code, (2) réviser et ajouter à l'index, (3) valider.

Comment git mvs'intègre-t-il? Si vous déplacez un fichier directement, puis en utilisant git rmet git add, toutes les modifications sont ajoutées à l'index, et l'utilisation de git diff pour afficher les modifications est moins facile (avant de valider). L' utilisation git mv, cependant, ajoute le nouveau chemin d' accès à l'index , mais pas les modifications apportées au fichier, permettant ainsi git diffet git add -pau travail comme d' habitude.

dur
la source
5

Il existe un cas de niche où git mvreste très utile: lorsque vous souhaitez modifier la casse d'un nom de fichier sur un système de fichiers insensible à la casse. APFS (mac) et NTFS (windows) sont, par défaut, insensibles à la casse (mais en respectant la casse).

greg.kindel le mentionne dans un commentaire sur la réponse de CB Bailey.

Supposons que vous travaillez sur un Mac et que votre fichier soit Mytest.txtgéré par git. Vous voulez changer le nom du fichier en MyTest.txt.

Tu pourrais essayer:

$ mv Mytest.txt MyTest.txt
overwrite MyTest.txt? (y/n [n]) y
$ git status
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

Oh cher. Git ne reconnaît pas que le fichier a été modifié.

Vous pouvez contourner ce problème en renommant complètement le fichier, puis en le renommant:

$ mv Mytest.txt temp.txt
$ git rm Mytest.txt
rm 'Mytest.txt'
$ mv temp.txt MyTest.txt
$ git add MyTest.txt 
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    Mytest.txt -> MyTest.txt

Hourra!

Ou vous pourriez vous épargner tout cela en utilisant git mv:

$ git mv Mytest.txt MyTest.txt
$ git status
On branch master
Your branch is up to date with 'origin/master'.

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    renamed:    Mytest.txt -> MyTest.txt
duncan
la source