Jak NAPRAWDĘ wyświetlić logi plików o zmienionych nazwach za pomocą git?

134

Jestem stosunkowo nowy w git. Wcześniej korzystałem z Subversion.

Zauważyłem, że większość graficznych interfejsów Git i wtyczek IDE nie wydaje się być w stanie wyświetlić historii pliku, jeśli nazwa pliku została zmieniona. Kiedy używam

git log --follow

w wierszu poleceń widzę cały dziennik po zmianie nazw.

Według Linusa Torvaldsa ( link alternatywny ) --followprzełącznik jest przyjemny dla „noob SVN”; poważni użytkownicy Git nie używają go:

--follow to totalny hack, mający po prostu zadowolić byłych użytkowników SVN, którzy i tak nigdy nie wiedzieli nic o takich rzeczach, jak rodzicielstwo czy ładne wykresy wersji.

Nie jest to całkowicie fundamentalne, ale obecna implementacja "--follow" jest naprawdę szybkim procesem wstępnym, przykręconym do logiki chodzenia po rewizji, a nie jest czymś naprawdę integralnym.

Dosłownie został zaprojektowany jako przyjemny element „SVN noob”, a nie jako „prawdziwa funkcjonalność gita”. Pomysł polegał na tym, że oderwałbyś się od (zepsutego) sposobu myślenia, że ​​zmiana nazwy ma znaczenie w szerszym kontekście.

Moje pytanie : W jaki sposób zagorzali użytkownicy git wśród was uzyskują historię pliku po zmianie jego nazwy? Jaki jest „prawdziwy” sposób na zrobienie tego?

Mikrofon
źródło
17
@David Hall: git mv oldfile newfilenie powoduje zmiany nazwy mają być rejestrowane w ogóle - to tak samo jak usuwanie jednego pliku i dodanie drugiego. git wyszukuje tylko zmiany nazwy i kopiuje stan drzewa przy każdym zatwierdzeniu po fakcie.
Mark Longair,
20
@David Hall: Jeśli zmienisz nazwę pliku za pomocą innego narzędzia poza git (np. /bin/mv oldfile newfile), Ale potem to zrobisz git add newfile; git rm oldfile, wynik jest nie do odróżnienia od wyniku git mv oldfile newfile.
Mark Longair
1
Ta ideologia rozpada się, jeśli kiedykolwiek przeniesiesz plik do nowego repozytorium, w którym to przypadku niemożność przeniesienia całej jego historii może być poważnym problemem. Chociaż oczywiście istnieją ograniczenia co do tego, ile prawdziwej historii może naprawdę pochodzić z pliku w złożonym projekcie.
Roman Starkov
1
Uwaga: git log --followpoprawia się nieco dzięki git 2.9 (czerwiec 2016): zobacz moją odpowiedź poniżej
VonC
1
Od wersji 2.15 możesz poeksperymentować z tym, --color-movedkiedy diff.
Michael

Odpowiedzi:

73

Myślę, że głównym motywem przewodnim Linusa jest to, że - i weź to z przymrużeniem oka - hardkorowi użytkownicy gita nigdy nie przejmują się historią „pliku”. Umieszczasz zawartość w repozytorium git, ponieważ zawartość jako całość ma znaczącą historię.

Zmiana nazwy pliku to mały, specjalny przypadek „zawartości” przemieszczającej się między ścieżkami. Możesz mieć funkcję, która przenosi się między plikami, które użytkownik git może śledzić za pomocą „kilofa” (np log -S.).

Inne zmiany „ścieżki” obejmują łączenie i dzielenie plików; git tak naprawdę nie obchodzi, który plik uważasz za zmieniony, a który uważasz za skopiowany (lub zmieniony i usunięty), po prostu śledzi całą zawartość twojego drzewa.

git zachęca do myślenia „całym drzewem”, gdzie wiele systemów kontroli wersji jest bardzo skoncentrowanych na plikach. To dlatego git częściej odwołuje się do „ścieżek” niż do „nazw plików”.

CB Bailey
źródło
25
Linus miał na myśli to, że „właściwy” interfejs użytkownika byłby w stanie śledzić fragmenty kodu w plikach i miał nadzieję, że już będziemy mieć takie narzędzia. Niestety nadal nie mamy tego luksusu, a funkcja - follow jest nadal przydatna.
Michael Parker
11
Czy git faktycznie daje rozwiązanie inne niż --followto?
Griwes,
14
Twierdziłbym, że myślenie „całego drzewa” jest wzmocnione przez --followbycie domyślnym. Chodzi mi o to, że kiedy chcę zobaczyć historię kodu w pliku, zwykle nie obchodzi mnie, czy plik został zmieniony, czy nie, po prostu chcę zobaczyć historię kodu, niezależnie od zmian. Więc moim zdaniem ma sens --followustawienie domyślne, ponieważ nie obchodzą mnie poszczególne pliki; --followpomaga mi zignorować indywidualne zmiany nazw plików, które zwykle nie mają znaczenia.
Eddified
2
Więc ... jeśli zdecyduję się przejmować „zawartością” zamiast pliku, w jaki sposób mogę wydrukować zatwierdzenia dotyczące treści, która jest obecnie w tym pliku? Byłbym zachwycony, gdyby git śledził to dla mnie w różnych plikach i zgłaszał dziennik wszystkich zmian - po prostu nie wiem, jak to uzyskać.
Ed Avis
1
Problem polega na tym, że nazwa pliku ma znaczenie. Na przykład użytkownik zastanawiający się, co się stało z plikiem, który istniał w projekcie obejmującym 20 lat historii. Kiedy taki użytkownik próbuje przeprowadzić aktualizację do nowszej wersji, a pliku, który zmodyfikował lata temu, w ogóle nie ma, w jaki sposób może dowiedzieć się, gdzie należy teraz zastosować ich lokalne zmiany?
Dread Quixadhal
37

Mam dokładnie ten sam problem, z którym masz do czynienia. Chociaż nie mogę udzielić ci odpowiedzi, myślę, że możesz przeczytać tego e-maila, który Linus napisał w 2005 roku, jest on bardzo trafny i może dać ci wskazówkę, jak rozwiązać problem:

… Twierdzę, że każdy SCM, który próbuje śledzić zmiany nazw, jest zasadniczo zepsuty, chyba że robi to z przyczyn wewnętrznych (tj. W celu umożliwienia efektywnych delt), dokładnie dlatego, że zmiany nazw nie mają znaczenia. Oni nie pomóc, a nie są one co byli zainteresowani w każdym razie .

Liczy się znalezienie „skąd się to wzięło”, a architektura git robi to naprawdę bardzo dobrze - znacznie lepiej niż cokolwiek innego. …

Znalazłem odniesienie do tego posta na blogu, który może być również przydatny w znalezieniu realnego rozwiązania:

W wiadomości Linus nakreślił, w jaki sposób idealny system śledzenia treści może pozwolić ci dowiedzieć się, w jaki sposób blok kodu przybrał obecny kształt. Zacząłbyś od bieżącego bloku kodu w pliku, cofnął się do historii, aby znaleźć zatwierdzenie, które zmieniło plik. Następnie sprawdzasz zmianę zatwierdzenia, aby zobaczyć, czy interesujący Cię blok kodu jest przez nią modyfikowany, ponieważ zatwierdzenie zmieniające plik może nie dotknąć bloku kodu, który Cię interesuje, ale tylko niektóre inne części plik.

Kiedy stwierdzisz, że przed zatwierdzeniem blok kodu nie istniał w pliku, przyjrzyj się dokładniej zatwierdzeniu. Może się okazać, że jest to jedna z wielu możliwych sytuacji, w tym:

  1. Zatwierdzenie naprawdę wprowadziło blok kodu. Autorem zmiany był twórca tej fajnej funkcji, na którą polowałeś, jej pochodzenie (lub winny, który wprowadził błąd); lub
  2. Blok kodu nie istniał w pliku, ale pięć identycznych kopii znajdowało się w różnych plikach, z których wszystkie zniknęły po zatwierdzeniu. Autor zatwierdzenia refaktoryzował zduplikowany kod, wprowadzając pojedynczą funkcję pomocniczą; lub
  3. (w szczególnym przypadku) Przed zatwierdzeniem plik, który aktualnie zawiera blok interesującego nas kodu, sam nie istniał, ale istniał inny plik o prawie identycznej zawartości, a blok kodu, który Cię interesował, wraz z całą pozostałą zawartością w pliku istniała wtedy, istniała w tym innym pliku. Odeszło po zatwierdzeniu. Autor zatwierdzenia zmienił nazwę pliku, wprowadzając niewielką modyfikację.

W git, najlepsze narzędzie Linusa do śledzenia treści nie istnieje jeszcze w pełni zautomatyzowany sposób. Ale większość ważnych składników jest już dostępna.

Prosimy o informowanie nas o swoich postępach w tej sprawie.

Alberto Bacchelli
źródło
Dzięki za opublikowanie tych artykułów. Dopiero gdy ich przeczytałem, w pełni pojąłem ideę historii treści! Myślałem o tym w niewłaściwy sposób!
DavidG
Ten e-mail od Linusa jest świetny, dzięki za opublikowanie tego.
mik01aj
Zabawny fakt, Git v2.15 dodaje--color-moved krok w kierunku tego „idealnego systemu śledzenia”. Bawiłem się nim, aby zobaczyć, jak śledzi przesunięte linie w pliku, ale przypadkowo zdałem sobie sprawę, że śledzi przesunięte linie w całym
Michael
2
Linus wyjaśnia złożoną sytuację. Ale tutaj mamy prostą sytuację: plik został właśnie zmieniony (lub przeniesiony do innego katalogu). Dlatego powinno być proste rozwiązanie. Myślę, że problem wynika z faktu, że w przeciwieństwie do Subversion użytkownik nie może poinstruować Gita w momencie zatwierdzenia, skąd pochodzi plik, a to --followmoże być błędne (np. Jeśli 2 pliki mają tę samą zawartość lub jeśli jest modyfikacja oprócz przenoszenia pliku).
vinc17
13

Zauważyłem, że większość graficznych interfejsów git i wtyczek IDE nie wydaje się być w stanie wyświetlić historii pliku, jeśli nazwa pliku została zmieniona

Z przyjemnością dowiesz się, że niektóre popularne narzędzia interfejsu użytkownika Git obsługują to teraz. Istnieją dziesiątki narzędzi Git UI, więc nie będę ich wszystkich wymieniać, ale na przykład:

  • SourceTree podczas przeglądania dziennika plików ma w lewym dolnym rogu pole wyboru „Śledź pliki o zmienionej nazwie”
  • TortoiseGit ma pole wyboru „śledź zmiany nazw” w oknie dziennika w lewym dolnym rogu.

Więcej informacji o narzędziach interfejsu użytkownika Git:

Michael Parker
źródło
Źródło działa świetnie w przypadku jednorazowej zmiany nazwy, podczas dwukrotnej zmiany nazwy szczegóły zmiany nie są dostępne dla zatwierdzeń przed zmianą nazwy. Zgłosiłem błąd tutaj: jira.atlassian.com/browse/SRCTREE-5715
Intel
gitk działa świetnie, nawet jeśli nazwa pliku jest zmieniana dwukrotnie w historii. Polecenie wygląda następująco: „gitk --follow path / to / file”
Intel
6

Uwaga: git 2.9 (czerwiec 2016) poprawi nieco "błędny" charakter git log --follow:

Zobacz commit ca4e3ca (30 marca 2016) autorstwa SZEDER Gábor ( szeder) .
(Scalone przez Junio ​​C Hamano - gitster- w zobowiązaniu 26effb8 , 13 kwietnia 2016 r.)

diffcore: napraw kolejność iteracji identycznych plików podczas wykrywania zmiany nazwy

Jeśli dwie ścieżki „ dir/A/file” i „ dir/B/file” mają identyczną treść, a nazwa katalogu nadrzędnego została zmieniona, np. „ git mv dir other-dir”, diffcoreZgłasza następujące dokładne zmiany:

renamed:    dir/B/file -> other-dir/A/file
renamed:    dir/A/file -> other-dir/B/file

(zwróć uwagę na inwersję tutaj: B/file -> A/filei A/file -> B/file)

Chociaż technicznie nie jest źle, jest to mylące nie tylko dla użytkownika, ale także dla poleceń git, które podejmują decyzje na podstawie informacji o zmianie nazwy, np. git log --follow other-dir/A/file„Podąża” dir/B/fileza zmianą nazwy.

To zachowanie jest efektem ubocznym zatwierdzenia v2.0.0-rc4 ~ 8 ^ 2 ~ 14 ( diffcore-rename.c: uprość znajdowanie dokładnych zmian nazw, 2013-11-14): źródła przechowujące hashmap zwracają wpisy z tego samego zasobnika, tj. Źródła pasujące do bieżącego miejsca docelowego , w kolejności LIFO.
W związku z tym iteracja najpierw sprawdza „ other-dir/A/file” i „ dir/B/file”, a po znalezieniu identycznej treści i podstawowej nazwy zgłasza dokładną zmianę nazwy.

VonC
źródło
2

W systemie Linux sprawdziłem, że SmartGit i GitEye są w stanie śledzić zmiany nazw podczas śledzenia historii określonego pliku. Jednak w przeciwieństwie do gitk i GitEye, SmartGit pokazuje osobny widok plików i widok repozytorium (który zawiera strukturę katalogów, ale nie zawiera listy plików)

prusswan
źródło