Jaki jest cel git-mv?

287

Z tego, co rozumiem, Git tak naprawdę nie musi śledzić operacji zmiany nazwy pliku / przenoszenia / kopiowania, więc jaki jest prawdziwy cel git mv ? Strona podręcznika nie jest specjalnie opisowa ...

Czy to jest przestarzałe? Czy to polecenie wewnętrzne, które nie jest przeznaczone dla zwykłych użytkowników?

Mauricio Scheffer
źródło

Odpowiedzi:

390
git mv oldname newname

jest po prostu skrótem dla:

mv oldname newname
git add newname
git rm oldname

tzn. automatycznie aktualizuje indeks zarówno starych, jak i nowych ścieżek.

CB Bailey
źródło
38
Ma też kilka wbudowanych zabezpieczeń.
Jakub Narębski
6
Dzięki @CharlesBailey - Czy git uważa wtedy pliki newNameFile i oldNameFile za różne? Jeśli tak, co się stanie, jeśli chcemy je połączyć? Załóżmy, że rozgałęziamy projekt mrówki na gałęzi A i tworzymy gałąź B, a następnie poddajemy projekty projektowi w B. Nazwy plików są takie same, ale umieszczone na różnych ścieżkach w miarę zmiany struktury projektu. Powiedzmy, że obie gałęzie rosły równolegle przez jakiś czas. W pewnym momencie, jeśli chcemy połączyć projekty, skąd dowiemy się, że to ten sam plik, którego nazwa właśnie się zmieniła? (jeśli „git mv” == „git add + git rm”)
Rose
2
@SergeyOrshanskiy Jeśli automatyczne wykrywanie się nie powiedzie mv oldname newname; git add newname; git rm oldname, również się nie powiedzie git mv oldname newname(zobacz tę odpowiedź ).
Ajedi32,
5
Zauważ, że git mvróżni się nieco od tego mv oldname newname; git add newname; git rm oldname, że jeśli dokonałeś zmian w pliku przed git mvjego wprowadzeniem, zmiany te nie zostaną wprowadzone do czasu, aż pojawi git addsię nowy plik.
Ajedi32,
2
git mv robi coś innego, ponieważ obsługuje zmiany wielkości liter w nazwach plików (foo.txt do Foo.txt), podczas gdy te polecenia są uruchamiane indywidualnie, nie (w OSX)
greg.kindel
66

Z oficjalnego GitFaq :

Git ma polecenie zmiany nazwy git mv, ale to tylko wygoda. Efekt jest nie do odróżnienia od usunięcia pliku i dodania innego o innej nazwie i tej samej treści

Adam Nofsinger
źródło
8
Czy tracisz historię plików? Przypuszczałem, że zmiana nazwy zachowałaby starą historię tego katalogu ...
Will Hancock,
17
Cóż, tak i nie. Przeczytaj oficjalny link GitFaq powyżej o nazwach, a następnie przeczytaj obszerny e-mail Linusa Torvaldsa o tym, dlaczego nie lubi pojęcia plików śledzenia narzędzia SCM: permalink.gmane.org/gmane.comp.version-control.git/ 217
Adam Nofsinger
3
@WillHancock Używałem git trochę więcej i mogę bardziej precyzyjnie odpowiedzieć: w zależności od twojego klienta git i jego opcji, będziesz mógł prześledzić plik po zmianie nazwy, jeśli plik zmienił się na tyle mało, że uważa go za Przemianować. Jeśli jednak za bardzo zmienisz plik ORAZ zmienisz jego nazwę, git go nie wykryje - w pewnym sensie mówi „nie, równie dobrze możesz uznać ten plik za zupełnie inny!”
Adam Nofsinger
7
@AdamNofsinger ten link jest martwy. Oto lustro: web.archive.org/web/20150209075907/http://…
Carl Walsh
2
Czy istnieje oficjalne odniesienie (tj. Bardziej godne uwagi niż FAQ), które określa równoważność między git mvpodejściem ręcznym? To nie jest oczywiste z git help mv.
tvo
40

Git próbuje zgadnąć, co próbujesz zrobić. Dokłada wszelkich starań, aby zachować nieprzerwaną historię. Oczywiście nie jest idealny. Dzięki git mvtemu możesz wyrazić swoją intencję i uniknąć błędów.

Rozważ ten przykład. Zaczynając od pustego repozytorium,

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

Wynik:

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

Autodetekcja nie powiodła się :( A może to?

$ 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

i wtedy

$ 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

Teraz spróbuj zamiast tego (pamiętaj, aby usunąć .gitfolder podczas eksperymentowania):

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

Na razie w porządku:

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


git mv b a
git status

Teraz nikt nie jest idealny:

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

Naprawdę? Ale oczywiście...

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

... a wynik jest taki sam jak powyżej: --followpokazuje tylko pełną historię.


Teraz zachowaj ostrożność przy zmianie nazwy, ponieważ każda z opcji może nadal wywoływać dziwne efekty . Przykład:

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

Porównaj to z:

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

Wynik:

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 ... Teraz historia wraca do początkowej a zamiast początkowej b , co jest złe. Więc kiedy wykonaliśmy dwa ruchy na raz, Git był zdezorientowany i nie śledził poprawnie zmian. Nawiasem mówiąc, w moich eksperymentach to samo stało się, gdy usunąłem / utworzyłem pliki zamiast ich używać git mv. Postępuj ostrożnie; Zostałeś ostrzeżony...

osa
źródło
5
+1 za szczegółowe wyjaśnienie. Szukałem problemów, które mogą się zdarzyć w historii dziennika, jeśli pliki zostaną przeniesione do git, twoja odpowiedź była naprawdę interesująca. Dziękuję Ci! Btw, czy znasz jakieś inne pułapki, których powinniśmy unikać podczas przenoszenia plików w git? (lub jakiekolwiek odniesienie, na które można wskazać ... niezbyt szczęśliwego
googlowania
1
Cóż, moje przykłady są pesymistyczne. Gdy pliki są puste, o wiele trudniej jest poprawnie zinterpretować zmiany. Wyobrażam sobie, że jeśli popełnisz po każdym zestawie nazw, wszystko powinno być w porządku.
osa
27

Jak mówi @Charles, git mv jest to skrót.

Prawdziwe pytanie brzmi: „Inne systemy kontroli wersji (np. Subversion i Perforce) traktują specjalnie nazwy plików. Dlaczego Git?”

Linus wyjaśnia na http://permalink.gmane.org/gmane.comp.version-control.git/217 charakterystycznym taktem:

Proszę, przestań to badziewie. Git śledzi dokładnie to , co się liczy, a mianowicie „kolekcje plików”. Nic innego nie jest istotne, a nawet myślenie, że jest to istotne, ogranicza jedynie twoje spojrzenie na świat. Zauważ, że pojęcie „adnotacji” CVS zawsze nieuchronnie ogranicza sposób, w jaki ludzie go używają. Myślę, że to zupełnie bezużyteczne badziewie i opisałem coś, co moim zdaniem jest milion razy bardziej przydatne, i wszystko to wypadło dokładnie dlatego, że nie ograniczam mojego myślenia do niewłaściwego modelu świata.

Pułkownik Panika
źródło
9

Jest jeszcze jedno zastosowanie, którego git mvnie wspomniałem powyżej.

Od czasu odkrycia git add -p(tryb łatki git add; patrz http://git-scm.com/docs/git-add ), lubię go używać do sprawdzania zmian podczas dodawania ich do indeksu. Tak więc mój przepływ pracy staje się (1) pracą nad kodem, (2) przeglądaniem i dodawaniem do indeksu, (3) zatwierdzaniem.

Jak git mvpasuje? Jeśli przenosisz plik bezpośrednio, wtedy używając git rmi git add, wszystkie zmiany zostaną dodane do indeksu, a użycie git diff do przeglądania zmian jest mniej łatwe (przed zatwierdzeniem). git mvJednak użycie dodaje nową ścieżkę do indeksu, ale nie wprowadza zmian w pliku, co pozwala git diffi git add -pdziała jak zwykle.

opóźnione
źródło
5

Istnieje niszowy przypadek, w którym git mvpozostaje bardzo przydatny: gdy chcesz zmienić obudowę nazwy pliku w systemie plików bez rozróżniania wielkości liter. Zarówno APFS (Mac), jak i NTFS (Windows) domyślnie nie rozróżniają wielkości liter (ale zachowują wielkość liter).

greg.kindel wspomina o tym w komentarzu do odpowiedzi CB Baileya.

Załóżmy, że pracujesz na komputerze Mac i masz plik Mytest.txtzarządzany przez git. Chcesz zmienić nazwę pliku na MyTest.txt.

Możesz spróbować:

$ 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

O jej. Git nie potwierdza, że ​​doszło do jakichkolwiek zmian w pliku.

Państwo mogłoby obejść się przez zmianę nazwy pliku całkowicie następnie zmieniając jego nazwę z powrotem:

$ 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

Hurra!

Lub możesz zaoszczędzić sobie tyle trudu, używając 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
źródło