Nasze repozytoria Git powstały jako części jednego repozytorium SVN z potworami, w którym każdy projekt miał swoje własne drzewo:
project1/branches
/tags
/trunk
project2/branches
/tags
/trunk
Oczywiście przenoszenie plików między nimi było dość łatwe svn mv
. Ale w Git każdy projekt znajduje się we własnym repozytorium, a dziś zostałem poproszony o przeniesienie podkatalogu z project2
do project1
. Zrobiłem coś takiego:
$ git clone project2
$ cd project2
$ git filter-branch --subdirectory-filter deeply/buried/java/source/directory/A -- --all
$ git remote rm origin # so I don't accidentally overwrite the repo ;-)
$ mkdir -p deeply/buried/different/java/source/directory/B
$ for f in *.java; do
> git mv $f deeply/buried/different/java/source/directory/B
> done
$ git commit -m "moved files to new subdirectory"
$ cd ..
$
$ git clone project1
$ cd project1
$ git remote add p2 ../project2
$ git fetch p2
$ git branch p2 remotes/p2/master
$ git merge p2 # --allow-unrelated-histories for git 2.9+
$ git remote rm p2
$ git push
Ale to wydaje się dość skomplikowane. Czy jest lepszy sposób na robienie tego w ogóle? Czy też zastosowałem właściwe podejście?
Zauważ, że obejmuje to scalenie historii w istniejące repozytorium, a nie po prostu utworzenie nowego autonomicznego repozytorium z części innego ( jak we wcześniejszym pytaniu ).
git
repository
ebneter
źródło
źródło
git fetch p2 && git merge p2
zamiast tego nie robiszgit fetch p2 && git branch .. && git merge p2
? Edycja: w porządku, wygląda na to, że chcesz uzyskać zmiany w nowej gałęzi o nazwie p2, a nie w bieżącej gałęzi.--allow-unrelated-histories
do ostatniego,git merge
aby działało.Odpowiedzi:
Tak, trafiając na
--subdirectory-filter
odfilter-branch
był klucz. Fakt, że użyłeś go zasadniczo, dowodzi, że nie ma prostszego sposobu - nie miałeś innego wyboru, jak przepisać historię, ponieważ chciałeś skończyć tylko z (zmienioną nazwą) podzbiorem plików, a to z definicji zmienia skróty. Ponieważ żadne ze standardowych poleceń (np.pull
) Nie przepisuje historii, nie ma możliwości użycia ich do osiągnięcia tego celu.Można oczywiście dopracować szczegóły - niektóre z klonowania i rozgałęziania nie były absolutnie konieczne - ale ogólne podejście jest dobre! Szkoda, że to skomplikowane, ale oczywiście celem git nie jest ułatwienie przepisywania historii.
źródło
--index-filter
stronęfilter-branch
podręcznika.Jeśli twoja historia jest zdrowa, możesz wziąć zatwierdzenia jako łatkę i zastosować je w nowym repozytorium:
Lub w jednej linii
(Zaczerpnięte z dokumentów Exherbo )
źródło
git log --pretty=email --patch-with-stat --full-index --binary --reverse -- client > patch
. Działa bez problemów AFAICT.--committer-date-is-author-date
opcję zachowania pierwotnej daty zatwierdzenia zamiast daty przeniesienia plików.git log
, aby nie działał z obydwoma--follow
i--reverse
poprawnie). Użyłem tej odpowiedzi , a oto kompletny skrypt, którego używam teraz do przenoszenia plikówPo wypróbowaniu różnych metod przenoszenia pliku lub folderu z jednego repozytorium Git do drugiego, jedyne, które wydaje się działać niezawodnie, zostało przedstawione poniżej.
Polega on na klonowaniu repozytorium, z którego chcesz przenieść plik lub folder, przenoszeniu tego pliku lub folderu do katalogu głównego, przepisywaniu historii Git, klonowaniu docelowego repozytorium i pobieraniu pliku lub folderu z historią bezpośrednio do tego docelowego repozytorium.
Scena pierwsza
Stwórz kopię repozytorium A, ponieważ poniższe kroki wprowadzają poważne zmiany w tej kopii, których nie powinieneś wypychać!
cd do tego
Usuń link do oryginalnego repozytorium, aby uniknąć przypadkowego wprowadzenia jakichkolwiek zdalnych zmian (np. Przez popchnięcie)
Przejrzyj swoją historię i pliki, usuwając wszystko, czego nie ma w katalogu 1. Wynikiem jest zawartość katalogu 1 wyrzucona do bazy repozytorium A.
Tylko w przypadku przenoszenia pojedynczego pliku: przejrzyj to, co zostało i usuń wszystko oprócz żądanego pliku. (Może być konieczne usunięcie niepotrzebnych plików o tej samej nazwie i zatwierdzenie).
Etap drugi
Krok czyszczenia
Krok czyszczenia
Krok czyszczenia
Możesz zaimportować te pliki do repozytorium B w katalogu innym niż katalog główny:
Utwórz ten katalog
Przenieś pliki do tego katalogu
Dodaj pliki do tego katalogu
Zatwierdź zmiany i jesteśmy gotowi scalić te pliki w nowym repozytorium
Etap trzeci
Zrób kopię repozytorium B, jeśli jeszcze go nie masz
(zakładając, że FOLDER_TO_KEEP to nazwa nowego repozytorium, do którego kopiujesz)
cd do tego
Utwórz zdalne połączenie z repozytorium A jako gałąź w repozytorium B
Wyciągnij z tej gałęzi (zawierającej tylko katalog, który chcesz przenieść) do repozytorium B.
Pull kopiuje zarówno pliki, jak i historię. Uwaga: Możesz użyć scalenia zamiast pull, ale pull działa lepiej.
Wreszcie, prawdopodobnie chcesz trochę posprzątać, usuwając zdalne połączenie z repozytorium A.
Naciśnij i wszystko gotowe.
źródło
Uważam to za bardzo przydatne. Jest to bardzo proste podejście, w którym tworzysz łatki, które są stosowane do nowego repozytorium. Zobacz link do strony po więcej szczegółów.
Zawiera tylko trzy kroki (skopiowane z bloga):
Jedynym problemem, jaki miałem, było to, że nie mogłem zastosować wszystkich łatek jednocześnie
W systemie Windows wystąpił błąd InvalidArgument. Musiałem więc nakładać wszystkie łatki jedna po drugiej.
źródło
UTRZYMANIE NAZWY KATALOGU
Filtr podkatalogów (lub krótsze polecenie git poddrzewo) działa dobrze, ale nie działał dla mnie, ponieważ usuwają nazwę katalogu z informacji o zatwierdzeniu. W moim scenariuszu chcę po prostu połączyć części jednego repozytorium w inne i zachować historię Z pełną nazwą ścieżki.
Moim rozwiązaniem było użycie filtru drzewa i usunięcie niechcianych plików i katalogów z tymczasowego klonu źródłowego repozytorium, a następnie pobranie tego klonu do mojego docelowego repozytorium w 5 prostych krokach.
źródło
Ten, którego zawsze używam, jest tutaj http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-history/ . Prosty i szybki.
Aby zachować zgodność ze standardami przepływu stosu, oto procedura:
źródło
Ta odpowiedź dostarcza ciekawych poleceń opartych na
git am
przykładach i krok po kroku.Cel
Procedura
git log --pretty=email -p --reverse --full-index --binary
git am
1. Wyodrębnij historię w formacie wiadomości e-mail
Przykład: Historia Ekstrakt
file3
,file4
ifile5
Wyczyść tymczasowy katalog docelowy
Wyczyść źródło repozytorium
Wyodrębnij historię każdego pliku w formacie e-mail
Niestety opcja
--follow
lub--find-copies-harder
nie można jej połączyć--reverse
. Dlatego historia jest wycinana, gdy nazwa pliku jest zmieniana (lub gdy nazwa katalogu nadrzędnego jest zmieniana).Po: historia tymczasowa w formacie wiadomości e-mail
2. Zreorganizuj drzewo plików i zaktualizuj zmianę nazwy pliku w historii [opcjonalnie]
Załóżmy, że chcesz przenieść te trzy pliki w tym drugim repozytorium (może to być to samo repo).
Dlatego zreorganizuj swoje pliki:
Twoja tymczasowa historia jest teraz:
Zmień także nazwy plików w historii:
Uwaga: Przepisuje historię, aby odzwierciedlić zmianę ścieżki i nazwy pliku.
(tj. zmiana nowej lokalizacji / nazwy w ramach nowego repozytorium)
3. Zastosuj nową historię
Twoje inne repo to:
Zastosuj zatwierdzenia z tymczasowych plików historii:
Twoje drugie repo to teraz:
Użyj,
git status
aby zobaczyć liczbę zatwierdzeń gotowych do wypchnięcia :-)Uwaga: Ponieważ historia została przepisana w celu odzwierciedlenia zmiany ścieżki i nazwy pliku:
(tj. W porównaniu do lokalizacji / nazwy w poprzednim repozytorium)
git mv
zmieniać lokalizacji / nazwy pliku.git log --follow
uzyskiwania dostępu do pełnej historii.Dodatkowa sztuczka: wykryj pliki o zmienionej nazwie / przeniesione w repozytorium
Aby wyświetlić listę plików, których nazwy zostały zmienione:
Więcej dostosowań: Możesz wykonać polecenie,
git log
używając opcji--find-copies-harder
lub--reverse
. Możesz także usunąć pierwsze dwie kolumny, używająccut -f3-
i grepując pełny wzór „{. * =>. *}”.źródło
Skrypt miał podobny świąd do zera (chociaż tylko dla niektórych plików z danego repozytorium). Ten skrypt okazał się bardzo pomocny: git-import
Krótka wersja polega na tym, że tworzy pliki łatek z danego pliku lub katalogu (
$object
) z istniejącego repozytorium:które następnie zostaną zastosowane do nowego repozytorium:
Aby uzyskać szczegółowe informacje, wyszukaj:
źródło
Spróbuj tego
cd repo1
Spowoduje to usunięcie wszystkich katalogów oprócz wymienionych, zachowując historię tylko dla tych katalogów
Teraz możesz dodać swoje nowe repozytorium do pilota git i wcisnąć je do tego
dodaj
-f
do zastąpieniaźródło
Korzystając z inspiracji z http://blog.neutrino.es/2012/git-copy-a-file-or-directory-from-another-repository-preserving-history/ , stworzyłem tę funkcję PowerShell do robienia tego samego, która ma do tej pory działało świetnie dla mnie:
Zastosowanie w tym przykładzie:
Po wykonaniu tej czynności możesz ponownie uporządkować pliki w
migrate-from-project2
oddziale przed scaleniem.źródło
Chciałem czegoś solidnego i wielokrotnego użytku (jedna komenda-i-go + cofnij), więc napisałem następujący skrypt bash. Kilkakrotnie pracował dla mnie, więc pomyślałem, że podzielę się tutaj.
Jest w stanie przenieść dowolny folder
/path/to/foo
zrepo1
do/some/other/folder/bar
dorepo2
(ścieżki folderów mogą być takie same lub różne, odległość od folderu głównego może być inna).Ponieważ dotyczy tylko zatwierdzeń dotykających plików w folderze wejściowym (nie wszystkich zatwierdzeń repozytorium źródłowego), powinno być dość szybkie nawet w przypadku dużych repozytoriów źródłowych, jeśli tylko wyodrębnisz głęboko zagnieżdżony podfolder, który nie został dotknięty w każdym popełnić.
Ponieważ polega to na utworzeniu osieroconej gałęzi z całą historią starego repozytorium, a następnie scaleniu jej z HEAD, zadziała nawet w przypadku kolizji nazw plików (wtedy oczywiście musisz rozwiązać scalenie na końcu) .
Jeśli nie ma konfliktów nazw plików, wystarczy
git commit
na końcu, aby zakończyć scalanie.Minusem jest to, że prawdopodobnie nie będzie śledzić zmian nazw plików (poza
REWRITE_FROM
folderem) w repozytorium źródłowym - mile widziane żądania ściągania w GitHub w celu uwzględnienia tego.Łącze GitHub: git-move-folder-between-repos-keep-history
źródło
W moim przypadku nie musiałem zachowywać repozytorium, z którego migrowałem, ani żadnej wcześniejszej historii. Miałem łatkę z tej samej gałęzi, z innego pilota
W tych dwóch krokach udało mi się sprawić, aby gałąź drugiego repozytorium pojawiła się w tym samym repozytorium.
Na koniec ustawiłem tę gałąź (którą zaimportowałem z drugiego repozytorium), aby podążała za główną linią repozytorium docelowego (aby móc je dokładnie różnicować)
Teraz zachowywał się tak, jakby to była kolejna gałąź, którą naciskałem na to samo repo.
źródło
Jeśli ścieżki dla danych plików są takie same w dwóch repozytoriach i chcesz przenieść tylko jeden plik lub mały zestaw powiązanych plików, jednym z łatwych sposobów jest użycie
git cherry-pick
.Pierwszym krokiem jest przeniesienie zatwierdzeń z drugiego repozytorium do twojego lokalnego repozytorium przy użyciu
git fetch <remote-url>
. Spowoduje to pozostawienieFETCH_HEAD
wskazywania na zatwierdzenie głowy z drugiego repozytorium; jeśli chcesz zachować odniesienie do tego zatwierdzenia po wykonaniu innych operacji pobierania, możesz je oznaczyć tagiemgit tag other-head FETCH_HEAD
.Następnie musisz utworzyć wstępne zatwierdzenie dla tego pliku (jeśli nie istnieje) lub zatwierdzenie, aby doprowadzić plik do stanu, który można załatać za pomocą pierwszego zatwierdzenia z innego repozytorium, które chcesz wprowadzić. być w stanie zrobić to z
git cherry-pick <commit-0>
gdybycommit-0
wprowadzono żądane pliki, lub być może trzeba skonstruować commit „ręcznie”. Dodaj-n
do opcji cherry-pick, jeśli chcesz zmodyfikować początkowe zatwierdzenie, np. Upuść pliki z tego zatwierdzenia, którego nie chcesz wprowadzać.Następnie możesz przejść do
git cherry-pick
kolejnych zatwierdzeń, używając ponownie w-n
razie potrzeby. W najprostszym przypadku (wszystkie rewizje są dokładnie to, co chcesz i zaaplikowana) można podać pełną listę zatwierdzeń w linii poleceń cherry-pick:git cherry-pick <commit-1> <commit-2> <commit-3> ...
.źródło
To staje się prostsze dzięki użyciu git-filter-repo.
Aby przejść
project2/sub/dir
doproject1/sub/dir
:Aby zainstalować narzędzie po prostu:
pip3 install git-filter-repo
( więcej szczegółów i opcji w README )źródło
Poniższa metoda migracji mojego GIT Stash do GitLab poprzez utrzymanie wszystkich gałęzi i zachowanie historii.
Sklonuj stare repozytorium do lokalnego.
Utwórz puste repozytorium w GitLab.
Powyższe zrobiłem, gdy przeprowadziliśmy migrację naszego kodu ze skrytki do GitLab i działało to bardzo dobrze.
źródło