Nagraj operację kopiowania plików za pomocą Git

141

Kiedy przenoszę plik w git za pomocą git-mv, status pokazuje, że plik został zmieniony i nawet jeśli zmienię niektóre fragmenty, nadal uważa się, że jest to prawie to samo (co jest dobre, ponieważ pozwala mi śledzić jego historię) .

Kiedy kopiuję plik, oryginalny plik ma pewną historię, którą chciałbym powiązać z nową kopią.

Próbowałem przenieść plik, a następnie spróbować ponownie wyewidencjonować go w pierwotnej lokalizacji - po przeniesieniu git nie pozwala mi wyewidencjonować oryginalnej lokalizacji.

Próbowałem zrobić kopię systemu plików, a następnie dodać plik - git wyświetla go jako nowy plik.

Czy istnieje sposób, aby git nagrał operację kopiowania pliku w podobny sposób, w jaki rejestruje zmianę nazwy / przeniesienie pliku, w którym można prześledzić historię do oryginalnego pliku?

Hexdoll
źródło

Odpowiedzi:

112

Git nie obsługuje śledzenia zmian nazw ani śledzenia kopii, co oznacza, że ​​nie rejestruje zmian nazw ani kopii. Co robi, a nie jest zmiana nazwy i kopia detekcji . Możesz zażądać wykrywania zmiany nazwy w git diff(i git show) za pomocą -Mopcji, możesz zażądać dodatkowego wykrywania kopii w zmienionych plikach za pomocą -Copcji ( -Cimplikuje -M) i możesz zażądać droższego wykrywania kopii wśród wszystkich plików z --find-copies-harderlub -C -C(co implikuje -C, co implikuje -M). Zobacz stronę podręcznika git-diff .

Możesz także skonfigurować git tak, aby zawsze wykrywał zmianę nazwy, ustawiając diff.renameswartość logiczną true (np. trueLub 1), a także możesz zażądać od gita wykrywania kopii, ustawiając go na copylub copies. Zobacz stronę podręcznika git-config .

Sprawdź także -lopcję git diffi powiązaną zmienną konfiguracyjną diff.renameLimit.


Zauważ, że git log <pathspec>działa inaczej w Git: tutaj <pathspec>jest zestaw ograniczników ścieżek, gdzie ścieżka może być nazwą (pod) katalogu. Filtruje i upraszcza historię przed zmianą nazwy i wykrywaniem kopii. Jeśli chcesz śledzić zmiany nazw i kopiowanie, użyj git log --follow <filename>(który obecnie jest nieco ograniczony i działa tylko dla pojedynczego pliku).

Jakub Narębski
źródło
1
@allyourcode: W czym jesteś zdezorientowany? Aby domyślnie włączyć wykrywanie kopii, należy ustawić diff.renamesopcję copies(np. „ git config diff.renames copies”). Zgadzam się, że jest to trochę sprzeczne z intuicją.
Jakub Narębski
Jedną z sekcji, której nie mogę przeanalizować, jest „i możesz poprosić o domyślne wykonanie również wykrywania zmiany nazwy”. Czy chcesz powiedzieć, że istnieją cztery wartości, których mogą używać nazwy diff.renames (prawda, 1, kopiowanie, kopiowanie) i że wszystkie robią to samo?
allyourcode
1
@allyourcode: Przepraszam, nie zauważyłem tego. Naprawiono teraz, dzięki.
Jakub Narębski
4
@ peschü: Git używa bazy danych obiektów adresowanych do treści jako magazynu repozytorium. Zawartość pliku jest przechowywana w zawartości „blob” pod adresem, który jest skrótem SHA-1 zawartości (dobrze, wpisz + długość + zawartość). Oznacza to, że dana zawartość jest przechowywana tylko raz. Nb. ta automatyczna deduplikacja była powodem stworzenia systemu kopii zapasowych „bup” przy użyciu formatu git pack.
Jakub Narębski
1
W przeciwieństwie do poniższego rozwiązania nie działa to ze śledzeniem zmian w zakresie. Git log dopuszcza argument zakresu ( git log -L123,456:file.xyz), który poprawnie podąża za zmianami nazw, ale nie kopiuje, i nie można w takim przypadku przekazać --follow; również, AFAICT, to nie działa z git blame.
Clément
57

19.05.2020: Zaletą poniższego rozwiązania jest brak zmiany dziennika oryginalnego pliku, brak tworzenia konfliktu scalania i krótsza długość.

Możesz wymusić na Gicie wykrycie historii kopiowanego pliku za pomocą trzech zatwierdzeń:

  • Zamiast kopiować, przełącz się do nowej gałęzi i przenieś plik do nowej lokalizacji.
  • Ponownie dodaj tam oryginalny plik.
  • Scal nową gałąź z oryginalną gałęzią z opcją braku szybkiego przewijania do przodu --no-ff.

(Kredyty idą do Raymonda Chena .)


Poprzednie rozwiązanie miało cztery zatwierdzenia:

  • Zamiast kopiować, przełącz się do nowej gałęzi i przenieś plik do nowej lokalizacji.
  • Przełącz się do oryginalnej gałęzi i zmień nazwę pliku.
  • Połącz nową gałąź z oryginalną, rozwiązując trywialny konflikt, zachowując oba pliki.
  • Przywróć oryginalną nazwę pliku w osobnym zatwierdzeniu.

(Rozwiązanie pobrane z https://stackoverflow.com/a/44036771/1389680 .)

Robert Pollak
źródło
7
Prostota, zwięzłość, 100% ... Ta odpowiedź to służba publiczna ... głosowanie za wszystkim w zasięgu wzroku
ptim
1
Jaka jest różnica między movei rename?
vovan
@vovan Czy masz na myśli fakt, że w bashu użyłbyś mvdo obu operacji? Użyłem `` move '' w przypadku, który może wymagać zmiany katalogu pliku i `` rename '' w przypadku, gdy tak nie jest.
Robert Pollak
2
Próbowałem zastosować się do tego (nowego) przepisu i to nie zadziałało. Może pomóc, jeśli pokażesz rzeczywiste polecenia.
Greg Lindahl
1
@RobertPollak Próbowałem różnych wersji tego, ale nie działały. Czy masz na myśli „przenieś plik” git mv orig new? Czy masz na myśli „przeczytaj oryginał” cp new orig && git add orig?
ᆼ ᆺ ᆼ