Wat is het doel van git-mv?

Voor zover ik begrijp, hoeft Git niet echt bewerkingen voor het hernoemen/verplaatsen/kopiëren van bestandbij te houden, dus wat is het echte doel
van git mv? De man-pagina is niet speciaal beschrijvend…

Is het verouderd? Is het een intern commando, niet bedoeld voor gebruik door gewone gebruikers?


Antwoord 1, autoriteit 100%

git mv oldname newname

is gewoon een afkorting voor:

mv oldname newname
git add newname
git rm oldname

d.w.z. het werkt de index automatisch bij voor zowel oude als nieuwe paden.


Antwoord 2, autoriteit 17%

Van de officiële GitFaq:

Git heeft een hernoem-commando git mv, maar dat is slechts voor het gemak. Het effect
is niet te onderscheiden van het verwijderen van het bestand en het toevoegen van een ander met verschillende
naam en dezelfde inhoud


Antwoord 3, autoriteit 9%

Git probeert gewoon voor je te raden wat je probeert te doen. Het doet er alles aan om de ononderbroken geschiedenis te bewaren. Natuurlijk is het niet perfect. Dus git mvstelt je in staat om expliciet te zijn met je intentie en om fouten te voorkomen.

Beschouw dit voorbeeld. Beginnend met een lege repo,

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

Resultaat:

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

Autodetectie mislukt🙁
Of deed het dat?

$ 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

en dan

$ 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

Probeer het nu in plaats daarvan (vergeet niet om de map .gitte verwijderen tijdens het experimenteren):

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

Tot nu toe goed:

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

Nu is niemand perfect:

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

Echt? Maar natuurlijk…

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

…en het resultaat is hetzelfde als hierboven: alleen --followtoont de volledige geschiedenis.


Wees nu voorzichtig met hernoemen, want beide opties kunnen nog steeds rare effecten produceren.
Voorbeeld:

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

Contrast het met:

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

Resultaat:

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… Nu gaat de geschiedenis terug naar begin ain plaats van begin b, wat niet klopt. Dus toen we twee zetten tegelijk deden, raakte Git in de war en hield de wijzigingen niet goed bij. Trouwens, in mijn experimenten gebeurde hetzelfde toen ik bestanden verwijderde/maakte in plaats van git mvte gebruiken. Ga voorzichtig te werk; je bent gewaarschuwd…


Antwoord 4, autoriteit 8%

Zoals @Charles zegt, is git mveen afkorting.

De echte vraag hier is “Andere versiebeheersystemen (bijv. Subversion en Perforce) behandelen bestandshernamen speciaal. Waarom doet Git dat niet?”

Linus legt uit op http://permalink.gmane.org/gmane.comp.version-control.git/217met karakteristieke tact:

Stop alsjeblieft met deze “track files” onzin. Git volgt precieswat belangrijk is,
namelijk “verzamelingen van bestanden”. Niets anders is relevant, en zelfs
denkendat het relevant is, beperkt alleen je wereldbeeld. Merk op hoe de
het idee van ‘annoteren’ van CVS leidt altijd onvermijdelijk tot een beperking van hoe mensen gebruiken
het. Ik denk dat het een totaal nutteloos stuk onzin is, en ik heb beschreven:
iets waarvan ik denk dat het een miljoen keer nuttiger is, en het viel allemaal uit
preciesomdat ik mijn denken niet beperk tot het verkeerde model van de
wereld.


Antwoord 5, autoriteit 3%

Er is een nichegeval waarin git mverg handig blijft: wanneer je de hoofdletters van een bestandsnaam op een hoofdletterongevoelig bestandssysteem wilt wijzigen. Zowel APFS (mac) als NTFS (windows) zijn standaard niet hoofdlettergevoelig (maar hoofdletterbehoud).

greg.kindel vermeldt dit in een opmerking over het antwoord van CB Bailey.

Stel dat je op een mac werkt en een bestand MyTest.txthebt beheerd door git. U wilt de bestandsnaam wijzigen in MyTest.txt.

Je zou kunnen proberen:

$ 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 schat. Git erkent niet dat er enige wijziging in het bestand is geweest.

U zoudit kunnen omzeilen door het bestand volledig te hernoemen en vervolgens terug te hernoemen:

$ 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

Hoera!

Of je kunt jezelf al die moeite besparen door git mvte gebruiken:

$ 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

Antwoord 6, autoriteit 2%

Ik heb nog een ander gebruik voor git mvdat hierboven niet is genoemd.

Sinds de ontdekking van git add -p(de patchmodus van git add; zie http://git -scm.com/docs/git-add), gebruik ik het graag om wijzigingen te bekijken terwijl ik ze aan de index toevoeg. Zo wordt mijn workflow (1) werken aan code, (2) beoordelen en toevoegen aan index, (3) vastleggen.

Hoe past git mvin? Als je een bestand direct verplaatst en dan git rmen git addgebruikt, worden alle wijzigingen toegevoegd aan de index, en het gebruik van git diff om wijzigingen te bekijken is minder eenvoudig (voordat je ze vastlegt). Het gebruik van git mvvoegt echter het nieuwe pad toe aan de index, maar geen wijzigingen aan het bestand, waardoor git diffen git add -pgewoon werken.


Antwoord 7

git mvverplaatst het bestand, werkt de index bij om het vervangen bestandspad vast te leggen, evenals alle betrokken git-submodules. In tegenstelling tot een handmatige verplaatsing, detecteert het ook alleen hoofdletters hernoemen die anders niet zouden worden gedetecteerd als een wijziging door git.

Het gedrag is vergelijkbaar (maar niet identiek) aan het extern verplaatsen van het bestand naar git, het verwijderen van het oude pad uit de index met behulp van git rm, en het toevoegen van het nieuwe aan de index met behulp van git add.

Motivatie voor het beantwoorden

Deze vraag heeft veel goede gedeeltelijke antwoorden. Dit antwoord is een poging om ze te combineren tot één samenhangend antwoord. Bovendien is een ding dat door geen van de andere antwoorden wordt genoemd, het feit dat de man-paginageeft eigenlijk meestal antwoord op de vraag, maar het is misschien minder voor de hand liggend dan het zou kunnen zijn.

Gedetailleerde uitleg

Drie verschillende effecten worden genoemd in de man-pagina:

  1. Het bestand, de map of de symbolische link is verplaatst in het bestandssysteem:

    git-mv – Verplaats of hernoem een bestand, een map of een symbolische link

  2. De index wordt bijgewerkt, het nieuwe pad wordt toegevoegd en het vorige verwijderd:

    De index wordt bijgewerkt na succesvolle voltooiing, maar de wijziging moet nog worden doorgevoerd.

  3. Verplaatste submodules zijn bijgewerkt om op de nieuwe locatie te werken:

    Het verplaatsen van een submodule met behulp van een gitfile (wat betekent dat ze gekloond zijn met een Git-versie 1.7.8 of nieuwer) zal het gitfile en core.worktree-instelling updaten om de submodule op de nieuwe locatie te laten werken. Het zal ook proberen om de submodule.<name>.path instelling in de gitmodules(5)bestand en stage dat bestand (tenzij -n wordt gebruikt).

Zoals vermeld in dit antwoord, lijkt git mverg op het verplaatsen van het bestand, waarbij de nieuw pad naar de index, en het verwijderen van het vorige pad uit de index:

mv oldname newname
git add newname
git rm oldname

Echter, zoals dit antwoordaangeeft, is git mvniet strikt identiek aan dit in gedrag . Het verplaatsen van het bestand via git mvvoegt het nieuwe pad toe aan de index, maar geen gewijzigde inhoud in het bestand. Door de drie afzonderlijke commando’s te gebruiken, wordt het volledige bestand aan de index toegevoegd, inclusief eventuele gewijzigde inhoud. Dit kan relevant zijn bij het gebruik van een workflow die de index patcht, in plaats van alle wijzigingen in het bestand toe te voegen.

Bovendien, zoals vermeld in dit antwoorden deze opmerking, git mvheeft het extra voordeel dat het alleen hoofdletters kan hernoemen op bestandssystemen die hoofdletterongevoeligmaar hoofdletterbehoud, zoals vaak het geval is in de huidige macOS- en Windows-bestandssystemen. In dergelijke systemen zou git bijvoorbeeld niet detecteren dat de bestandsnaam is gewijzigd na het verplaatsen van een bestand via mv Mytest.txt MyTest.txt, terwijl het gebruik van git mv Mytest.txt MyTest.txtzou de naam met succes bijwerken.


Antwoord 8

Misschien is git mvveranderd sinds deze antwoorden zijn gepost, dus ik zal het kort updaten. Naar mijn mening wordt git mvnietnauwkeurig omschreven als een afkorting voor:

# not accurate: #
 mv oldname newname
 git add newname
 git rm oldname

Ik gebruik git mv vaak om twee redenen die niet in eerdere antwoorden zijn beschreven:

  1. Grote mapstructuren verplaatsen, waar ik gemengde inhoud heb van zowel bijgehouden als niet-gevolgde bestanden. Zowel bijgehouden als niet-gevolgde bestanden worden verplaatst en behouden hun volg-/ophaalstatus

  2. Bij het verplaatsen van bestanden en mappen die groot zijn, heb ik altijd aangenomen dat git mvde grootte van de DB-geschiedenis van de repository zal verkleinen. Dit komt omdat het verplaatsen/hernoemen van een bestand indexatie/referentiedelta is. Ik heb deze veronderstelling niet geverifieerd, maar het lijkt logisch.

Other episodes