Qual è lo scopo di git-mv?


287

Da quello che ho capito, Git non ha davvero bisogno di tracciare le operazioni di rinominare / spostare / copiare i file, quindi qual è il vero scopo di git mv ? La pagina man non è particolarmente descrittiva ...

È obsoleto? È un comando interno, non pensato per essere utilizzato dagli utenti normali?

Risposte:


390
git mv oldname newname

è solo una scorciatoia per:

mv oldname newname
git add newname
git rm oldname

cioè aggiorna automaticamente l'indice per i percorsi vecchi e nuovi.


38
Inoltre ha alcune sicurezze integrate.
Jakub Narębski

6
Grazie @CharlesBailey - Git considera i file newNameFile e oldNameFile diversi? Se sì, cosa succede se vogliamo unirli? Supponiamo che ramifichiamo un progetto di formica sul ramo A e creiamo il ramo B, quindi realizziamo i progetti su B. I nomi dei file sono gli stessi ma mettiamo su percorsi diversi quando cambia la struttura del progetto. Supponiamo che entrambi i rami siano cresciuti per qualche tempo in parallelo. Ad un certo punto se vogliamo unire i progetti come farà Git a sapere che è lo stesso file appena rinominato nel suo percorso? (se "git mv" == "git add + git rm")
Rose

2
@SergeyOrshanskiy Se il rilevamento automatico va storto per mv oldname newname; git add newname; git rm oldname, andrà anche male per git mv oldname newname(vedi questa risposta ).
Ajedi32,

5
Si noti che git mvè leggermente diverso dal mv oldname newname; git add newname; git rm oldname, in quanto se sono state apportate modifiche al file prima di git mvinserirlo, tali modifiche non verranno messe in scena fino a quando non si avrà git addil nuovo file.
Ajedi32,

2
git mv sta facendo qualcosa di diverso, poiché gestisce le modifiche nel caso del nome file (da foo.txt a Foo.txt) mentre quei comandi eseguiti individualmente non lo fanno (su OSX)
greg.kindel

66

Dal GitFaq ufficiale :

Git ha un comando di ridenominazione git mv, ma è solo una comodità. L'effetto è indistinguibile dalla rimozione del file e dall'aggiunta di un altro con un nome diverso e lo stesso contenuto


8
Quindi perdi la cronologia dei file? Mi presumevo che la ridenominazione avrebbe mantenuto la vecchia storia per quella directory ...
Will Hancock,

17
Bene, sì e no. Leggi il link GitFaq ufficiale sopra per rinominare, quindi leggi la lunga e-mail di Linus Torvald sul perché non gli piace la nozione di file di tracciamento di uno strumento SCM: permalink.gmane.org/gmane.comp.version-control.git/ 217
Adam Nofsinger

3
@WillHancock Ho usato git un po 'di più ora e posso risponderti in modo più definitivo: a seconda del tuo client git e delle sue opzioni, sarai in grado di rintracciare il file oltre la ridenominazione se il file è cambiato internamente abbastanza da considerarlo un rinominare. Se cambi troppo il file e lo rinomini, git non lo rileverà - in un certo senso sta dicendo "no, potresti anche considerare che un file completamente diverso!"
Adam Nofsinger,

7
@AdamNofsinger quel link è morto. Ecco uno specchio: web.archive.org/web/20150209075907/http://…
Carl Walsh

2
Esiste un riferimento ufficiale (vale a dire più degno di una FAQ) che indichi l'equivalenza tra git mvl'approccio manuale? Non è ovvio da git help mv.
tvo

40

Git sta solo cercando di indovinare per te quello che stai cercando di fare. Sta facendo ogni tentativo per preservare la storia ininterrotta. Certo, non è perfetto. Quindi git mvti permette di essere esplicito con le tue intenzioni e di evitare alcuni errori.

Considera questo esempio. A partire da un repository vuoto,

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

Risultato:

# 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")

Rilevamento automatico non riuscito :( o l'ha fatto?

$ 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

e poi

$ 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

Ora prova invece (ricorda di eliminare la .gitcartella durante gli esperimenti):

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

Fin qui tutto bene:

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


git mv b a
git status

Ora, nessuno è perfetto:

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

Veramente? Ma certo...

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

... e il risultato è lo stesso di sopra: --followmostra solo la cronologia completa.


Ora, fai attenzione a rinominare, poiché entrambe le opzioni possono ancora produrre strani effetti . Esempio:

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

Contrastalo con:

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

Risultato:

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 ... Ora la cronologia sta tornando alla iniziale a anziché alla iniziale b , il che è sbagliato. Quindi, quando abbiamo fatto due mosse alla volta, Git è diventato confuso e non ha seguito correttamente le modifiche. A proposito, nei miei esperimenti lo stesso è accaduto quando ho eliminato / creato file invece di utilizzare git mv. Procedere con cura; sei stato avvisato ...


5
+1 per la spiegazione dettagliata. Ho cercato problemi che potrebbero verificarsi nella cronologia dei registri se i file vengono spostati in git, la tua risposta è stata davvero interessante. Grazie! A proposito, conosci altre insidie ​​che dovremmo evitare mentre spostiamo i file in git? (o qualsiasi riferimento che potresti indicare .... non molto fortunato a
cercarlo su Google

1
Bene, i miei esempi sono pessimisti. Quando i file sono vuoti, è molto più difficile interpretare correttamente le modifiche. Immagino che se ti impegni dopo ogni serie di rinominazioni, dovresti andare bene.
osa,

27

Come dice @Charles, git mvè una scorciatoia.

La vera domanda qui è "Altri sistemi di controllo della versione (ad es. Subversion e Perforce) trattano in particolare i nomi dei file. Perché non Git?"

Linus spiega su http://permalink.gmane.org/gmane.comp.version-control.git/217 con un tocco caratteristico:

Si prega di fermare questa schifezza "traccia file". Git tiene traccia esattamente di ciò che conta, vale a dire "raccolte di file". Nient'altro è rilevante e anche solo pensare che sia rilevante limita solo la tua visione del mondo. Si noti come il concetto di "annotazione" di CVS finisca inevitabilmente per limitare il modo in cui le persone lo usano. Penso che sia un pezzo di merda totalmente inutile, e ho descritto qualcosa che penso sia un milione di volte più utile, e tutto è caduto esattamente perché non sto limitando il mio pensiero al modello sbagliato del mondo.


9

C'è un altro uso che ho per git mvnon menzionato sopra.

Da quando ho scoperto git add -p(modalità patch di git add; vedi http://git-scm.com/docs/git-add ), mi piace usarlo per rivedere le modifiche mentre le aggiungo all'indice. Così il mio flusso di lavoro diventa (1) lavoro sul codice, (2) revisione e aggiunta all'indice, (3) commit.

Come si git mvadatta? Se si sposta direttamente un file quindi si utilizza git rme git add, tutte le modifiche vengono aggiunte all'indice e l'utilizzo di git diff per visualizzare le modifiche è meno semplice (prima di eseguire il commit). L'uso git mv, tuttavia, aggiunge il nuovo percorso all'indice ma non le modifiche apportate al file, permettendo così git diffe git add -pdi funzionare come al solito.


5

C'è un caso di nicchia in cui git mvrimane molto utile: quando si desidera modificare il case di un nome di file su un file system senza distinzione tra maiuscole e minuscole. Sia APFS (mac) che NTFS (windows) sono, per impostazione predefinita, senza distinzione tra maiuscole e minuscole (ma preservano le maiuscole).

greg.kindel menziona questo in un commento sulla risposta di CB Bailey.

Supponiamo che tu stia lavorando su un Mac e abbia un file Mytest.txtgestito da Git. Si desidera modificare il nome del file in MyTest.txt.

Puoi provare:

$ 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 caro. Git non riconosce che ci sono state modifiche al file.

È possibile aggirare questo problema rinominando completamente il file e rinominandolo nuovamente:

$ 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

Evviva!

Oppure potresti salvarti tutto quel fastidio usando 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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.