Czy można przenosić / zmieniać nazwy plików w Git i przechowywać ich historię?


667

Chciałbym zmienić nazwę / przenieść poddrzewo projektu w Git, przenosząc je z

/project/xyz

do

/components/xyz

Jeśli użyję zwykłego git mv project components, cała historia zatwierdzeń xyz projectzgubi się. Czy istnieje sposób, aby przenieść to tak, aby historia była zachowana?



2
Chcę tylko zauważyć, że właśnie przetestowałem przenoszenie plików za pośrednictwem systemu plików i po zatwierdzeniu (przez intellij) mogę zobaczyć całą historię (w tym historię, gdy była w innym miejscu) podczas przeglądania historii (ponownie w intellij). Zakładam, że intellij nie robi w tym celu nic specjalnego, więc miło jest wiedzieć, że przynajmniej historię można prześledzić.
BT

Aby zapoznać się z regułami stosowanymi przez Gita podczas wykrywania zmiany nazwy katalogu, zobacz moją odpowiedź poniżej
VonC

Tutaj napisałem odpowiedź. Mam nadzieję, że to zadziała. stackoverflow.com/questions/10828267/…
Mahmut EFE

Odpowiedzi:


651

Git wykrywa zmiany nazw zamiast przetrwania operacji z zatwierdzeniem, więc czy używasz git mvczy mvnie ma znaczenia.

logKomenda bierze --followargument, że kontynuuje historię przed operacją zmiany nazwy, tzn wyszukuje podobnej treści używając heurystyki:

http://git-scm.com/docs/git-log

Aby przejrzeć pełną historię, użyj następującego polecenia:

git log --follow ./path/to/file

63
Podejrzewam, że jest to kwestia wydajności. Jeśli nie potrzebujesz pełnej historii, skanowanie zawartości zajmie więcej czasu. Najłatwiej jest skonfigurować alias git config alias.logf "log --follow"i po prostu napisać git logf ./path/to/file.
Troels Thomsen

13
@TroelsThomsen ten e-mail Linusa Torvaldsa, połączony z tą odpowiedzią , wskazuje, że jest to celowy wybór Gita, ponieważ jest rzekomo znacznie potężniejszy niż śledzenie nazw itp.
Emil Lundberg

127
Ta odpowiedź jest nieco myląca. Git „wykrywa zmiany nazw”, ale bardzo późno w grze; pytanie brzmi: w jaki sposób upewniasz się, że Git śledzi nazwy, a osoba czytająca to może łatwo wywnioskować, że Git wykrywa je automatycznie i odnotowuje. To nie. Git nie obsługuje prawdziwych nazw, a zamiast tego istnieją narzędzia do łączenia / rejestrowania, które próbują dowiedzieć się, co się stało - i rzadko to robią. Linus ma błędny, ale gwałtowny argument, dlaczego git nigdy nie powinien po prostu robić tego we właściwy sposób i jawnie śledzić zmiany nazw. Utknęliśmy tutaj.
Chris Moschini,

29
Ważne: jeśli zmienisz nazwę katalogu, na przykład podczas zmiany nazwy pakietu Java, pamiętaj o wykonaniu dwóch zatwierdzeń, po pierwsze dla polecenia „git mv {old} {new}”, a po drugie dla aktualizacji wszystkich plików Java, które odwołują się do zmieniono katalog pakietu. W przeciwnym razie git nie będzie mógł śledzić pojedynczych plików, nawet z parametrem --follow.
nn4l

44
Chociaż Linus prawdopodobnie popełnia bardzo niewiele błędów, wydaje się, że jest to jeden z nich. Po prostu zmiana nazwy folderu powoduje przesłanie ogromnej delty do GitHub. Co sprawia, że ​​jestem ostrożny przy zmianie nazw folderów ... ale to jest całkiem duża prosta kurtka dla programisty. Czasami muszę ponownie zdefiniować znaczenie czegoś lub zmienić sposób kategoryzacji rzeczy. Linus: „Innymi słowy, mam rację. Zawsze mam rację, ale czasami mam rację bardziej niż innym razem. I do cholery, kiedy mówię„ pliki nie mają znaczenia ”, naprawdę mam rację ( tm). ” ... mam co do tego wątpliwości.
Gabe Halsmer,

94

Jest to możliwe , aby zmienić nazwę pliku i przechowywać historię nienaruszone, chociaż powoduje zmianę nazwy pliku w całej historii repozytorium. Jest to prawdopodobnie tylko dla obsesyjnych miłośników git-logów i ma pewne poważne implikacje, w tym:

  • Możesz przepisywać wspólną historię, która jest najważniejsza NIE używaj Gita. Jeśli ktoś sklonował repozytorium, przerwiesz to robiąc to. Będą musieli ponownie sklonować, aby uniknąć bólów głowy. Może to być OK, jeśli zmiana nazwy jest wystarczająco ważna, ale musisz to przemyśleć ostrożnie - możesz skończyć niepokojem całej społeczności open source!
  • Jeśli odwoływałeś się do pliku przy użyciu jego starej nazwy wcześniej w historii repozytorium, skutecznie łamiesz wcześniejsze wersje. Aby temu zaradzić, będziesz musiał wykonać nieco więcej skoków na obręcz. To nie jest niemożliwe, tylko żmudne i być może nie warte zachodu.

Ponieważ nadal jesteś ze mną, prawdopodobnie jesteś solistą, który zmienia nazwę całkowicie izolowanego pliku. Przenieśmy plik za pomocą filter-tree!

Załóżmy, że zamierzasz przenieść plik olddo folderu diri nadać mu nazwęnew

Można to zrobić git mv old dir/new && git add -u dir/new, ale to łamie historię.

Zamiast:

git filter-branch --tree-filter 'if [ -f old ]; then mkdir dir && mv old dir/new; fi' HEAD

będzie przerobić każdy popełnić w branży, wykonujące polecenia w kleszcze dla każdej iteracji. Wiele rzeczy może się nie udać, gdy to zrobisz. Zwykle sprawdzam, czy plik jest obecny (w przeciwnym razie nie ma go jeszcze do przeniesienia), a następnie wykonuję niezbędne kroki, aby podeprzeć drzewo według moich upodobań. Tutaj możesz przeglądać pliki, aby zmieniać odniesienia do pliku itd. Ogłusz się! :)

Po zakończeniu plik zostanie przeniesiony, a dziennik pozostanie nienaruszony. Czujesz się jak pirat ninja.

Również; Oczywiście katalog mkdir jest konieczny tylko wtedy, gdy plik zostanie przeniesiony do nowego folderu. Jeśli pozwoli uniknąć tworzenia tego folderu wcześniej w historii niż plik istnieje.


57
Jako obsesyjny miłośnik logów, nie wybrałbym tego. Pliki nie były nazywane w tamtych czasach, dlatego historia odzwierciedla sytuację, która nigdy nie istniała. Kto wie, jakie testy mogą przerwać się w przeszłości! Ryzyko uszkodzenia wcześniejszych wersji jest prawie w każdym przypadku nie warte.
Vincent

7
@Vincent Masz absolutną rację, a ja starałem się wyjaśnić, jak mało prawdopodobne jest to rozwiązanie. Myślę też, że w tym przypadku mówimy o dwóch znaczeniach słowa „historia”, doceniam oba.
Øystein Steimler

6
Uważam, że są sytuacje, w których można tego potrzebować. Powiedzmy, że opracowałem coś w moim osobistym oddziale, który chcę teraz połączyć w górę. Ale odkrywam, że nazwa pliku nie jest odpowiednia, więc zmieniam ją dla całej mojej osobistej gałęzi. W ten sposób mogę zachować czystą, właściwą historię od samego początku.
user2291758,

3
@ user2291758 to mój przypadek użycia. Te potężniejsze polecenia git są niebezpieczne, ale to nie znaczy, że nie mają bardzo interesujących przypadków użycia, jeśli wiesz, co robisz!
philix

1
jeśli to możliwe, użycie --index-filterfor forames będzie znacznie szybsze, ponieważ drzewo nie będzie musiało być sprawdzane i włączane przy każdym zatwierdzeniu. --index-filterdziała bezpośrednio na każdy indeks zatwierdzenia.
Thomas Guyot-Sionnest,

87

Nie.

Krótka odpowiedź brzmi NIE . Zmiana nazwy pliku w Git i zapamiętanie historii nie jest możliwa. I to jest ból.

Plotka głosi, że git log --follow--find-copies-harderto zadziała, ale dla mnie to nie działa, nawet jeśli nie ma zerowych zmian w zawartości pliku, a ruchy zostały wykonane git mv.

(Początkowo użyłem Eclipse do zmiany nazwy i aktualizacji pakietów w jednej operacji, co mogło pomylić Git. Ale to jest bardzo powszechna rzecz. --followWydaje się, że działa, jeśli tylko a mvjest wykonywane, a następnie a commiti mvnie jest za daleko.)

Linus mówi, że należy rozumieć całą zawartość projektu oprogramowania w sposób holistyczny, bez konieczności śledzenia poszczególnych plików. Niestety mój mały mózg nie może tego zrobić.

To naprawdę denerwujące, że tak wiele osób bezmyślnie powtórzyło stwierdzenie, że Git automatycznie śledzi ruchy. Zmarnowali mi czas. Git nie robi czegoś takiego. Zgodnie z projektem (!) Git w ogóle nie śledzi ruchów.

Moim rozwiązaniem jest zmiana nazw plików z powrotem na ich oryginalne lokalizacje. Zmień oprogramowanie, aby pasowało do kontroli źródła. Z Gitem wydaje się, że po raz pierwszy musisz to zrobić „dobrze”.

Niestety psuje to Eclipse, które wydaje się używać --follow. git log --followczasami nie wyświetla pełnej historii plików ze skomplikowanymi historiami zmian nazw, chociaż git logtak jest. (Nie wiem dlaczego.)

(Istnieją pewne zbyt sprytne hacki, które cofają się i wznawiają stare prace, ale są raczej przerażające. Zobacz GitHub-Gist: emiller / git-mv-with-history .)


2
Wierzę, że masz rację. Właśnie próbowałem użyć php-cs-fixer, aby sformatować źródło dla mojego projektu Laravel 5, ale nalega na zmianę wielkich liter klauzul przestrzeni nazw, aby pasowały do ​​małej litery folderu aplikacji. Ale przestrzenie nazw (lub automatyczne ładowanie kompozytora) działają tylko z CamelCase. Muszę zmienić wielkość liter folderu na App, ale powoduje to utratę moich zmian. Jest to najbardziej trywialny przykład, ale pokazuje, że heurystyka git nie jest w stanie śledzić nawet najprostszych zmian nazw (reguła, a nie wyjątek, powinny być --follow i --find-copy-harder.
Zack Morris,

6
git -1, subversion +1
Cosmin

Czy to nadal prawda? To dla mnie kolejny powód, dla którego warto pozostać przy tfs. Przechowywanie historii przeniesionego / przemianowanego pliku jest koniecznością w dużym projekcie.
Cesar

@Cesar Jeśli przez „zapamiętaj historię”, ma on na myśli „obserwuj nazwy podczas przeglądania logu” (co jest jedyną przydatną rzeczą, na którą powinniśmy się troszczyć), to nigdy nie było to prawdą! Git nie „rejestruje” nazw, ale narzędzia mogą je łatwo wykryć i przedstawić nam nazwy i ruchy. Jeśli „to nie działa” dla kogoś, powinien zmienić narzędzia, których używa. Istnieje wiele wspaniałych GUI Git, które mają tę zdolność.
Mohammad Dehghan

Krótka odpowiedź brzmi: tak. Obecna wersja Git obsługuje również „git log - follow”. i zgadzam się z @MohammadDehghan
insung

42
git log --follow [file]

pokaże historię poprzez zmianę nazw.


29
Wygląda na to, że wymaga to zmiany nazwy przed rozpoczęciem modyfikowania pliku. Jeśli przeniesiesz plik (w powłoce), a następnie go zmienisz, wszystkie zakłady są wyłączone.
yoyo

22
@yoyo: to dlatego, że git nie śledzi nazw, wykrywa je. git mvZasadzie robi git rm && git add. Istnieją opcje takie jak -M90/, --find-renames=90aby rozważyć zmianę nazwy pliku, gdy jest on w 90% identyczny.
vdboor

22

Ja robię:

git mv {old} {new}
git add -u {new}

3
-U wydaje się nic dla mnie nie robić, czy należy aktualizować historię?
Jeremy

1
Być może chcesz -Azamiast tego zachowanie ? Ponownie patrz tutaj: git-scm.com/docs/git-add
James M. Greene

1
Dodaje pliki, jednak nie aktualizuje historii, więc „nazwa pliku dziennika git” pokazuje pełną historię. Pokazuje pełną historię tylko wtedy, gdy nadal korzystasz z opcji --follow.
Jeremy

3
Zrobiłem skomplikowany refaktor, który przeniósł katalog włączania (używając mv, a nie git mv), a następnie zmieniłem wiele ścieżek #include w plikach o zmienionych nazwach. git nie mógł znaleźć wystarczającego podobieństwa do śledzenia historii. Ale git add -u było tym, czego potrzebowałem. Status git wskazuje teraz „przemianowano” gdzie wcześniej pokazywał „usunięty” i „nowy plik”.
AndyJost

1
Istnieje wiele pytań na temat SO, które dotyczą celu git add -u. Dokumenty Git są zwykle nieprzydatne i są ostatnim miejscem, w którym chciałbym wyglądać. Oto jeden post pokazujący się git add -uw akcji: stackoverflow.com/a/2117202 .
nobar

17

Chciałbym zmienić nazwę / przenieść poddrzewo projektu w Git, przenosząc je z

/project/xyz

do

/ components / xyz

Jeśli użyję zwykłego git mv project components, cała historia zatwierdzeń dla xyzprojektu zostanie utracona.

Nie (8 lat później, Git 2.19, III kwartał 2018), ponieważ Git wykryje zmianę nazwy katalogu , co jest teraz lepiej udokumentowane.

Zobacz zatwierdzenie b00bf1c , zatwierdzenie 1634688 , zatwierdzenie 0661e49 , zatwierdzenie 4d34dff , zatwierdzenie 983f464 , zatwierdzenie c840e1a , zatwierdzenie 9929430 (27 czerwca 2018 r.) I zatwierdzenie d4e8062 , zatwierdzenie 5dacd4a (25 czerwca 2018 r.) Autorstwa Elijah Newren ( newren) .
(Połączone przez Junio ​​C Hamano - gitster- w commit 0ce5a69 , 24 lipca 2018)

Wyjaśnia to teraz Documentation/technical/directory-rename-detection.txt:

Przykład:

Kiedy wszystko x/a, x/bi x/cprzeniósł się do z/a, z/bi z/cjest prawdopodobne, że x/ddodany w międzyczasie również chce się przenieść do z/dbiorąc wskazówkę, że cały katalog „ x” przeniósł się do „ z”.

Ale to wiele innych przypadków, takich jak:

jedna strona historii zmienia nazwę x -> z, a druga zmienia nazwę pliku x/e, co powoduje konieczność zmiany nazwy przechodni.

Aby uprościć wykrywanie zmiany nazwy katalogu, Git wymusza te reguły:

kilka podstawowych zasad ogranicza się w przypadku wykrycia zmiany nazwy katalogu:

  1. Jeśli dany katalog nadal istnieje po obu stronach scalania, nie uważamy, że jego nazwa została zmieniona.
  2. Jeśli podzbiór plików, które mają zostać zmienione, ma plik lub katalog na swój sposób (lub będzie przeszkadzał sobie nawzajem), „wyłącz” nazwę katalogu dla tych określonych ścieżek podrzędnych i zgłoś konflikt użytkownikowi .
  3. Jeśli druga strona historii zmieniła nazwę katalogu na ścieżkę, którą zmieniła twoja strona historii, zignoruj ​​tę konkretną nazwę z drugiej strony historii dla wszelkich niejawnych nazw katalogów (ale ostrzegaj użytkownika).

Możesz zobaczyć wiele testów t/t6043-merge-rename-directories.sh, które również wskazują, że:

  • a) Jeśli zmiany nazw podzielą katalog na dwa lub więcej innych, katalog o największej liczbie nazw „wygrywa”.
  • b) Unikaj wykrywania zmiany katalogu dla ścieżki, jeśli ścieżka ta jest źródłem zmiany nazwy po obu stronach scalania.
  • c) Zastosuj niejawne nazwy katalogów do katalogów tylko wtedy, gdy druga strona historii dokonuje zmiany nazwy.

15

Cel

  • Zastosowanie (inspirowane Smar , zapożyczone z Exherbo )git am
  • Dodaj historię zatwierdzania skopiowanych / przeniesionych plików
  • Z jednego katalogu do drugiego
  • Lub z jednego repozytorium do drugiego

Ograniczenie

  • Tagi i gałęzie nie są przechowywane
  • Historia jest wycinana przy zmianie nazwy pliku ścieżki (zmiana nazwy katalogu)

Podsumowanie

  1. Wyodrębnij historię w formacie wiadomości e-mail za pomocą
    git log --pretty=email -p --reverse --full-index --binary
  2. Zreorganizuj drzewo plików i zaktualizuj nazwy plików
  3. Dołącz nową historię za pomocą
    cat extracted-history | git am --committer-date-is-author-date

1. Wyodrębnij historię w formacie wiadomości e-mail

Przykład: Historia Ekstrakt file3, file4ifile5

my_repo
├── dirA
│   ├── file1
│   └── file2
├── dirB            ^
│   ├── subdir      | To be moved
│   │   ├── file3   | with history
│   │   └── file4   | 
│   └── file5       v
└── dirC
    ├── file6
    └── file7

Ustaw / wyczyść miejsce docelowe

export historydir=/tmp/mail/dir       # Absolute path
rm -rf "$historydir"    # Caution when cleaning the folder

Wyodrębnij historię każdego pliku w formacie e-mail

cd my_repo/dirB
find -name .git -prune -o -type d -o -exec bash -c 'mkdir -p "$historydir/${0%/*}" && git log --pretty=email -p --stat --reverse --full-index --binary -- "$0" > "$historydir/$0"' {} ';'

Niestety opcja --followlub --find-copies-hardernie można jej połączyć --reverse. Dlatego historia jest wycinana, gdy nazwa pliku jest zmieniana (lub gdy nazwa katalogu nadrzędnego jest zmieniana).

Historia tymczasowa w formacie e-mail:

/tmp/mail/dir
    ├── subdir
    │   ├── file3
    │   └── file4
    └── file5

Dan Bonachea sugeruje odwrócenie pętli polecenia generowania dziennika git w pierwszym kroku: zamiast uruchamiać dziennik git raz na plik, uruchom go dokładnie raz z listą plików w wierszu poleceń i wygeneruj pojedynczy zunifikowany dziennik. W ten sposób zatwierdzenia, które modyfikują wiele plików, pozostaną w wyniku pojedynczym zatwierdzeniem, a wszystkie nowe zatwierdzenia zachowają swoją pierwotną względną kolejność. Uwaga: wymaga to również zmian w drugim kroku poniżej podczas przepisywania nazw plików w (teraz ujednoliconym) dzienniku.


2. Zreorganizuj drzewo plików i zaktualizuj nazwy plików

Załóżmy, że chcesz przenieść te trzy pliki w tym drugim repozytorium (może to być to samo repo).

my_other_repo
├── dirF
│   ├── file55
│   └── file56
├── dirB              # New tree
│   ├── dirB1         # from subdir
│   │   ├── file33    # from file3
│   │   └── file44    # from file4
│   └── dirB2         # new dir
│        └── file5    # from file5
└── dirH
    └── file77

Dlatego zreorganizuj swoje pliki:

cd /tmp/mail/dir
mkdir -p dirB/dirB1
mv subdir/file3 dirB/dirB1/file33
mv subdir/file4 dirB/dirB1/file44
mkdir -p dirB/dirB2
mv file5 dirB/dirB2

Twoja tymczasowa historia jest teraz:

/tmp/mail/dir
    └── dirB
        ├── dirB1
        │   ├── file33
        │   └── file44
        └── dirB2
             └── file5

Zmień także nazwy plików w historii:

cd "$historydir"
find * -type f -exec bash -c 'sed "/^diff --git a\|^--- a\|^+++ b/s:\( [ab]\)/[^ ]*:\1/$0:g" -i "$0"' {} ';'

3. Zastosuj nową historię

Twoje inne repo to:

my_other_repo
├── dirF
│   ├── file55
│   └── file56
└── dirH
    └── file77

Zastosuj zatwierdzenia z tymczasowych plików historii:

cd my_other_repo
find "$historydir" -type f -exec cat {} + | git am --committer-date-is-author-date

--committer-date-is-author-datezachowuje oryginalne znaczniki czasu zatwierdzenia ( komentarz Dana Bonachei ).

Twoje drugie repo to teraz:

my_other_repo
├── dirF
│   ├── file55
│   └── file56
├── dirB
│   ├── dirB1
│   │   ├── file33
│   │   └── file44
│   └── dirB2
│        └── file5
└── dirH
    └── file77

Użyj, git statusaby zobaczyć liczbę zatwierdzeń gotowych do wypchnięcia :-)


Dodatkowa sztuczka: sprawdź przemianowane / przeniesione pliki w repozytorium

Aby wyświetlić listę plików, których nazwy zostały zmienione:

find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow {} ';' | grep '=>'

Więcej dostosowań: Możesz wykonać polecenie, git logużywając opcji --find-copies-harderlub --reverse. Możesz także usunąć pierwsze dwie kolumny, używając cut -f3-i grepując pełny wzór „{. * =>. *}”.

find -name .git -prune -o -exec git log --pretty=tformat:'' --numstat --follow --find-copies-harder --reverse {} ';' | cut -f3- | grep '{.* => .*}'

4
UWAGA: Ta technika dzieli zatwierdzenia, które zmieniają 2 lub więcej plików na osobne fragmentowane zatwierdzenia, a ponadto szyfruje ich kolejność, sortując według nazwy pliku (więc fragmenty jednego oryginalnego zatwierdzenia nie pojawiają się obok siebie w historii liniowej). Historia wynikowa jest zatem „poprawna” tylko dla poszczególnych plików. Jeśli przenosisz więcej niż jeden plik, BRAK nowych zatwierdzeń w wynikowej historii reprezentuje spójną migawkę przeniesionych plików, które kiedykolwiek istniały w historii oryginalnego repozytorium.
Dan Bonachea

2
Cześć @DanBonachea. Dziękujemy za interesujące opinie. Z powodzeniem przeprowadziłem migrację niektórych repozytoriów zawierających kilka plików przy użyciu tej techniki (nawet w przypadku plików o zmienionych nazwach i plików przenoszonych między katalogami). Co sugerujesz zmienić w tej odpowiedzi. Czy uważasz, że powinniśmy dodać sztandar OSTRZEŻENIE u góry tej odpowiedzi wyjaśniający ograniczenia tej techniki? Pozdrawiam
olibre

2
Dostosowałem tę technikę, aby uniknąć problemu, odwracając pętle komendy generowania dziennika git w kroku 1. Tj. zamiast uruchamiać dziennik git raz na plik, uruchom go dokładnie raz z listą plików w wierszu poleceń i wygeneruj pojedynczy zunifikowany dziennik. W ten sposób zatwierdzenia, które modyfikują 2 lub więcej plików, pozostają w wyniku pojedynczym zatwierdzeniem, a wszystkie nowe zatwierdzenia zachowują swoją pierwotną względną kolejność. Uwaga: wymaga to również zmian w kroku 2 podczas przepisywania nazw plików w (teraz ujednoliconym) dzienniku. Użyłem również git am - data-autora-daty-autora-daty, aby zachować oryginalne znaczniki czasu zatwierdzenia.
Dan Bonachea,

1
Dziękujemy za eksperymentowanie i udostępnianie. Zaktualizowałem nieco odpowiedź dla innych czytelników. Jednak poświęciłem czas na przetestowanie przetwarzania. Edytuj tę odpowiedź, jeśli chcesz podać przykłady wierszy poleceń. Pozdrawiam;)
olibre

4

Postępowałem zgodnie z tym wieloetapowym procesem, aby przenieść kod do katalogu nadrzędnego i zachować historię.

Krok 0: Utworzono gałąź „historia” z „głównego” do przechowywania

Krok 1: Użyłem narzędzia git-filter-repo do przepisania historii. To polecenie poniżej przeniosło folder „FolderwithContentOfInterest” o jeden poziom wyżej i zmodyfikowało odpowiednią historię zatwierdzeń

git filter-repo --path-rename ParentFolder/FolderwithContentOfInterest/:FolderwithContentOfInterest/ --force

Krok 2: Do tego czasu repozytorium GitHub straciło ścieżkę zdalnego repozytorium. Dodano zdalne odniesienie

git remote add origin git@github.com:MyCompany/MyRepo.git

Krok 3: Wyciągnij informacje z repozytorium

git pull

Krok 4: Połącz lokalną utraconą gałąź z gałęzią początkową

git branch --set-upstream-to=origin/history history

Krok 5: Jeśli pojawi się monit, konflikt scalania adresu dla struktury folderów

Krok 6: Naciśnij !!

git push

Uwaga: zmodyfikowana historia i przeniesiony folder wydają się być już zatwierdzone. enter code here

Gotowy. Kod przenosi się do katalogu nadrzędnego / pożądanego, nie zmieniając historii!


2

Chociaż rdzeń Git, hydraulika Git nie śledzi nazw, historia, którą wyświetlasz za pomocą „porcelany” w dzienniku Git, może je wykryć, jeśli chcesz.

Dla danego git logzastosowania użyj opcji -M:

git log -p -M

Z bieżącą wersją Git.

Działa to w przypadku innych poleceń, takich jak git diff .

Istnieją opcje, aby porównania były mniej lub bardziej rygorystyczne. Jeśli zmienisz nazwę pliku bez wprowadzania znaczących zmian w pliku w tym samym czasie, Git log i znajomi mogą wykryć zmianę nazwy. Z tego powodu niektórzy ludzie zmieniają nazwy plików w jednym zatwierdzeniu i zmieniają je w innym.

Korzystanie z procesora wiąże się z pewnym kosztem za każdym razem, gdy poprosisz Gita o znalezienie nazwy pliku, więc czy go użyjesz, czy nie, i kiedy to zależy od ciebie.

Jeśli chcesz, aby Twoja historia była zawsze raportowana z wykrywaniem zmiany nazwy w określonym repozytorium, możesz użyć:

git config diff.renames 1

Pliki przenoszenie z jednego katalogu do drugiego jest wykrywane. Oto przykład:

commit c3ee8dfb01e357eba1ab18003be1490a46325992
Author: John S. Gruber <JohnSGruber@gmail.com>
Date:   Wed Feb 22 22:20:19 2017 -0500

    test rename again

diff --git a/yyy/power.py b/zzz/power.py
similarity index 100%
rename from yyy/power.py
rename to zzz/power.py

commit ae181377154eca800832087500c258a20c95d1c3
Author: John S. Gruber <JohnSGruber@gmail.com>
Date:   Wed Feb 22 22:19:17 2017 -0500

    rename test

diff --git a/power.py b/yyy/power.py
similarity index 100%
rename from power.py
rename to yyy/power.py

Pamiętaj, że działa to zawsze, gdy używasz diff, nie tylko z git log. Na przykład:

$ git diff HEAD c3ee8df
diff --git a/power.py b/zzz/power.py
similarity index 100%
rename from power.py
rename to zzz/power.py

W ramach próby dokonałem niewielkiej zmiany w jednym pliku w gałęzi funkcji i zatwierdziłem go, a następnie w gałęzi głównej zmieniłem nazwę pliku, zatwierdziłem, a następnie dokonałem niewielkiej zmiany w innej części pliku i dokonałem tego. Kiedy poszedłem do gałęzi funkcji i połączyłem z master, scalanie zmieniło nazwę pliku i połączyło zmiany. Oto wynik scalenia:

 $ git merge -v master
 Auto-merging single
 Merge made by the 'recursive' strategy.
  one => single | 4 ++++
  1 file changed, 4 insertions(+)
  rename one => single (67%)

W rezultacie powstał katalog roboczy, którego nazwa pliku została zmieniona i dokonano obu zmian tekstu. Jest więc możliwe, że Git postąpi właściwie, mimo że nie śledzi wyraźnie zmian nazw.

To jest późna odpowiedź na stare pytanie, więc inne odpowiedzi mogły być w tym czasie prawidłowe dla wersji Git.


1

Najpierw utwórz samodzielne zatwierdzenie, zmieniając tylko nazwę.

Następnie wszelkie ewentualne zmiany zawartości pliku umieszczane w osobnym zatwierdzeniu.


1

Aby zmienić nazwę katalogu lub pliku (niewiele wiem o skomplikowanych przypadkach, więc mogą istnieć pewne zastrzeżenia):

git filter-repo --path-rename OLD_NAME:NEW_NAME

Aby zmienić nazwę katalogu w plikach, które go wspominają (możliwe jest użycie wywołań zwrotnych, ale nie wiem jak):

git filter-repo --replace-text expressions.txt

expressions.txtto plik wypełniony liniami typu literal:OLD_NAME==>NEW_NAME(możliwe jest użycie RE w Pythonie z regex:lub glob z glob:).

Aby zmienić nazwę katalogu w wiadomości commits:

git-filter-repo --message-callback 'return message.replace(b"OLD_NAME", b"NEW_NAME")'

Obsługiwane są również wyrażenia regularne Pythona, ale muszą być one napisane ręcznie w Pythonie.

Jeśli repozytorium jest oryginalne, bez zdalnego, będziesz musiał dodać, --forceaby wymusić przepisanie. (Możesz to zrobić przed utworzeniem kopii zapasowej repozytorium).

Jeśli nie chcesz zachować referencji (będą one wyświetlane w historii oddziału Git GUI), musisz dodać --replace-refs delete-no-add.


0

Po prostu przenieś plik i etap za pomocą:

git add .

Przed zatwierdzeniem możesz sprawdzić status:

git status

To pokaże:

Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        renamed:    old-folder/file.txt -> new-folder/file.txt

Testowałem z Git w wersji 2.26.1.

Wyodrębniono ze strony pomocy GitHub .


-3

Robię przenoszenie plików, a następnie robię

git add -A

które umieszczają w obszarze przesiewania wszystkie usunięte / nowe pliki. Tutaj git zdaje sobie sprawę, że plik został przeniesiony.

git commit -m "my message"
git push

Nie wiem dlaczego, ale to działa dla mnie.


Sztuczka polega na tym, że nie musisz zmieniać ani jednej litery, nawet jeśli coś zmienisz i naciśniesz Ctrl + Z, historia zostanie zepsuta. SO w takim przypadku, jeśli coś napiszesz, przywróć plik, a następnie przenieś go ponownie i wykonaj dla niego pojedynczy add-> commit.
Xelian
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.