Jak zaimportować istniejące repozytorium Git do innego?

Mam repozytorium Git w folderze XXX , i mam drugie repozytorium Git o nazwie YYY .

Chcę zaimportować repozytorium XXX do repozytorium YYY jako podkatalog o nazwie ZZZ i dodać historię zmian XXX do YYY.

Struktura folderów przed:

├── XXX
│   ├── .git
│   └── (project files)
└── YYY
    ├── .git
    └── (project files)

Struktura folderów po:

YYY
├── .git  <-- This now contains the change history from XXX
├──  ZZZ  <-- This was originally XXX
│    └── (project files)
└──  (project files)

Czy można to zrobić, Czy muszę uciekać się do używania podmodułów?

Author: Bitswazsky, 2009-11-05

15 answers

Prawdopodobnie najprostszym sposobem byłoby wyciągnięcie XXX rzeczy do gałęzi w YYY , a następnie scalenie go w master:

W YYY :

git remote add other /path/to/XXX
git fetch other
git checkout -b ZZZ other/master
mkdir ZZZ
git mv stuff ZZZ/stuff                      # repeat as necessary for each file/dir
git commit -m "Moved stuff to ZZZ"
git checkout master                
git merge ZZZ --allow-unrelated-histories   # should add ZZZ/ to master
git commit
git remote rm other
git branch -d ZZZ                           # to get rid of the extra branch before pushing
git push                                    # if you have a remote, that is
[1]} właśnie próbowałem tego z kilku moich repo i to działa. W przeciwieństwie do Jörg ' s answer to nie pozwoli ci nadal korzystać z innego repo, ale i tak nie sądzę, że sprecyzowałeś to.

Uwaga: Ponieważ to zostało pierwotnie napisane w 2009 roku, git dodał połączenie podrzędne wymienione w odpowiedzi poniżej. Metodę tę pewnie bym dzisiaj zastosował, choć oczywiście metoda ta nadal działa.

 449
Author: ebneter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-10-02 05:36:13

Jeśli chcesz zachować dokładną historię zatwierdzeń drugiego repozytorium, a tym samym zachować możliwość łatwego łączenia zmian w przyszłości, oto metoda, którą chcesz. Powoduje to niezmodyfikowaną historię zaimportowanego poddrzewa do repo oraz jeden commit scalający, aby przenieść scalone repozytorium do podkatalogu.

git remote add XXX_remote <path-or-url-to-XXX-repo>
git fetch XXX_remote
git merge -s ours --no-commit --allow-unrelated-histories XXX_remote/master
git read-tree --prefix=ZZZ/ -u XXX_remote/master
git commit -m "Imported XXX as a subtree."

Możesz śledzić zmiany w źródle tak:

git pull -s subtree XXX_remote master

Git sam wymyśla, gdzie są korzenie, zanim zrobi Scal, więc nie musisz określać przedrostka przy kolejnych połączeniach.

Minusem jest to, że w Scalonej historii pliki są niefixowane (Nie w podkatalogu). W rezultacie git log ZZZ/a pokaże Ci wszystkie zmiany (jeśli występują) z wyjątkiem tych w historii Scalonej. Można zrobić:

git log --follow -- a

Ale to nie pokaże zmian innych niż w połączonej historii.

Innymi słowy, jeśli nie zmienisz plików ZZZ w repozytorium XXX, musisz podać --follow i niezrealizowana ścieżka. Jeśli zmienisz je w obu repozytoriach, masz 2 polecenia, z których żadna nie pokazuje wszystkich zmian.

Wersje Git przed 2.9: nie musisz przekazywać opcji --allow-unrelated-histories do git merge.

Metoda w drugiej odpowiedzi, która używa read-tree i pomija krok merge -s ours, nie różni się skutecznie od kopiowania plików z cp i zatwierdzania wyniku.

Oryginalne źródło pochodzi z github 's" subtree Merge " help article. Oraz kolejny przydatny link .

 377
Author: ColinM,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-30 15:31:23

git-subtree jest skryptem zaprojektowanym do tego właśnie przypadku łączenia wielu repozytoriów w jeden, zachowując historię (i / lub dzieląc historię podzbiorów, choć wydaje się to nieistotne dla tego pytania). Jest dystrybuowany jako część drzewa git od wydania 1.7.11.

Aby scalić repozytorium <repo> w revision <rev> jako podkatalog <prefix>, użyj git subtree add w następujący sposób:

git subtree add -P <prefix> <repo> <rev>

Git-subtree implementuje strategię scalania subtree w bardziej przyjazny dla użytkownika sposób.

W Twoim przypadku, wewnątrz repozytorium YYY, można uruchomić:

git subtree add -P ZZZ /path/to/XXX.git master

Minusem jest to, że w Scalonej historii pliki są niefixowane (Nie w podkatalogu). W rezultacie git log ZZZ/a pokaże Ci wszystkie zmiany (jeśli występują) z wyjątkiem tych w historii Scalonej. Można zrobić:

git log --follow -- a

Ale to nie pokaże zmian innych niż w połączonej historii.

Innymi słowy, jeśli nie zmienisz ZZZ plików w repozytorium XXX, następnie należy podać --follow i ścieżkę bez zmian. Jeśli zmienisz je w obu repozytoriach, masz 2 polecenia, z których żadna nie pokazuje wszystkich zmian.

Więcej na ten temat tutaj .

 131
Author: kynan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-30 15:36:40

W samym repozytorium Git jest znana taka instancja, która jest znana w społeczności Git jako " The coolest merge ever " (po temacie Linusa Torvaldsa używanym w e-mailu do listy mailingowej Git, która opisuje to połączenie). W tym przypadku, gitk Git GUI, który teraz jest częścią Git proper, w rzeczywistości był oddzielnym projektem. Linusowi udało się połączyć to repozytorium z repozytorium Git w sposób, który

  • pojawia się w Git repozytorium tak, jakby zawsze było rozwijane jako część Git,
  • cała historia jest nienaruszona i
  • nadal może być rozwijany niezależnie w swoim starym repozytorium, przy czym zmiany są po prostu git pulled.

E-mail zawiera kroki potrzebne do odtworzenia, ale nie jest to dla osób o słabym sercu: po pierwsze, Linus napisał Git, więc prawdopodobnie wie o nim trochę więcej niż ty Czy ja, a po drugie, to było prawie 5 lat temu i Git poprawił się znacznie od tego czasu, więc może teraz jest znacznie łatwiej.

W szczególności, wydaje mi się, że obecnie używa się submodule gitk, w tym konkretnym przypadku.

 51
Author: Jörg W Mittag,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55

Prostym sposobem na to jest użycie git format-patch.

Załóżmy, że mamy 2 repozytoria Gita fooi bar.

Foo zawiera:

  • foo.txt
  • .git

Bar zawiera:

  • bar.txt
  • .git

I chcemy skończyć z foo zawierającym pasek historia i te pliki:

  • foo.txt
  • .git
  • foobar / bar.txt

Tak to zrobić:

 1. create a temporary directory eg PATH_YOU_WANT/patch-bar
 2. go in bar directory
 3. git format-patch --root HEAD --no-stat -o PATH_YOU_WANT/patch-bar --src-prefix=a/foobar/ --dst-prefix=b/foobar/
 4. go in foo directory
 5. git am PATH_YOU_WANT/patch-bar/*

I jeśli chcemy przepisać wszystkie commity wiadomości z paska możemy to zrobić np. na Linuksie:

git filter-branch --msg-filter 'sed "1s/^/\[bar\] /"' COMMIT_SHA1_OF_THE_PARENT_OF_THE_FIRST_BAR_COMMIT..HEAD

Spowoduje dodanie "[bar] " na początku każdej wiadomości commit.

 12
Author: Damien R.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-11-10 09:58:26

Ta funkcja sklonuje zdalne repo do lokalnego katalogu repo, po połączeniu wszystkie zmiany zostaną zapisane, git log pokaże oryginalne zmiany i właściwe ścieżki:

function git-add-repo
{
    repo="$1"
    dir="$(echo "$2" | sed 's/\/$//')"
    path="$(pwd)"

    tmp="$(mktemp -d)"
    remote="$(echo "$tmp" | sed 's/\///g'| sed 's/\./_/g')"

    git clone "$repo" "$tmp"
    cd "$tmp"

    git filter-branch --index-filter '
        git ls-files -s |
        sed "s,\t,&'"$dir"'/," |
        GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
        mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
    ' HEAD

    cd "$path"
    git remote add -f "$remote" "file://$tmp/.git"
    git pull "$remote/master"
    git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
    git remote remove "$remote"
    rm -rf "$tmp"
}

Jak używać:

cd current/package
git-add-repo https://github.com/example/example dir/to/save

Jeśli dokonasz drobnych zmian, możesz nawet przenieść pliki / dirs scalonych repo do różnych ścieżek, na przykład:

repo="https://github.com/example/example"
path="$(pwd)"

tmp="$(mktemp -d)"
remote="$(echo "$tmp" | sed 's/\///g' | sed 's/\./_/g')"

git clone "$repo" "$tmp"
cd "$tmp"

GIT_ADD_STORED=""

function git-mv-store
{
    from="$(echo "$1" | sed 's/\./\\./')"
    to="$(echo "$2" | sed 's/\./\\./')"

    GIT_ADD_STORED+='s,\t'"$from"',\t'"$to"',;'
}

# NOTICE! This paths used for example! Use yours instead!
git-mv-store 'public/index.php' 'public/admin.php'
git-mv-store 'public/data' 'public/x/_data'
git-mv-store 'public/.htaccess' '.htaccess'
git-mv-store 'core/config' 'config/config'
git-mv-store 'core/defines.php' 'defines/defines.php'
git-mv-store 'README.md' 'doc/README.md'
git-mv-store '.gitignore' 'unneeded/.gitignore'

git filter-branch --index-filter '
    git ls-files -s |
    sed "'"$GIT_ADD_STORED"'" |
    GIT_INDEX_FILE="$GIT_INDEX_FILE.new" git update-index --index-info &&
    mv "$GIT_INDEX_FILE.new" "$GIT_INDEX_FILE"
' HEAD

GIT_ADD_STORED=""

cd "$path"
git remote add -f "$remote" "file://$tmp/.git"
git pull "$remote/master"
git merge --allow-unrelated-histories -m "Merge repo $repo into master" --edit "$remote/master"
git remote remove "$remote"
rm -rf "$tmp"

Ogłoszenia
Paths zastępuje via sed, więc upewnij się, że została przeniesiona w odpowiednie ścieżki po scaleniu.
Parametr --allow-unrelated-histories istnieje tylko od git >= 2.9.

 10
Author: Andrey Izman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-11 12:17:26

Bazując na tym artykule , użycie subtree działa dla mnie i tylko odpowiednia historia została przeniesiona. Zamieszczanie tutaj w przypadku, gdyby ktoś potrzebował tych kroków (upewnij się, że zastąpisz symbole zastępcze wartościami mającymi zastosowanie do ciebie):

W repozytorium źródłowym podziel podfolder na nową gałąź

git subtree split --prefix=<source-path-to-merge> -b subtree-split-result

W destination repo merge w rozdzielonej gałęzi wynikowej

git remote add merge-source-repo <path-to-your-source-repository>
git fetch merge-source-repo
git merge -s ours --no-commit merge-source-repo/subtree-split-result
git read-tree --prefix=<destination-path-to-merge-into> -u merge-source-repo/subtree-split-result

Zweryfikuj zmiany i zatwierdź

git status
git commit

Nie zapomnij

Clean up poprzez usunięcie gałęzi subtree-split-result

git branch -D subtree-split-result

Usuń dodany pilot, aby pobrać dane ze źródłowego repo

git remote rm merge-source-repo

 7
Author: Alex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-04-29 17:16:22

Użyję nazw a (zamiast XXX i ZZZ) i b (zamiast YYY), ponieważ to sprawia, że opis jest nieco łatwiejszy do odczytania.

Powiedz, że chcesz połączyć repozytorium a W b (zakładam, że znajdują się obok siebie):

cd a
git filter-repo --to-subdirectory-filter a
cd ..
cd b
git remote add a ../a
git fetch a
git merge --allow-unrelated-histories a/master
git remote remove a

Do tego potrzebujeszgit-filter-repo installed (filter-branch is ).

Przykład połączenia dwóch dużych repozytoriów, umieszczając jeden z nich w podkatalogu: https://gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731

Więcej na ten temat tutaj .

 5
Author: x-yuri,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-05-30 14:47:15

Dodanie kolejnej odpowiedzi, ponieważ myślę, że jest to nieco prostsze. Pobranie repo_dest jest wykonywane do repo_to_import, a następnie push --set-upstream URL: repo_dest master jest wykonywane.

Ta metoda zadziałała dla mnie importując kilka mniejszych repo do większego.

Jak zaimportować: repo1_to_import do repo_dest

# checkout your repo1_to_import if you don't have it already 
git clone url:repo1_to_import repo1_to_import
cd repo1_to_import

# now. pull all of repo_dest
git pull url:repo_dest
ls 
git status # shows Your branch is ahead of 'origin/master' by xx commits.
# now push to repo_dest
git push --set-upstream url:repo_dest master

# repeat for other repositories you want to import

Zmień nazwę lub Przenieś pliki i dirs do żądanej pozycji w oryginalnym repo przed zaimportowaniem. np.

cd repo1_to_import
mkdir topDir
git add topDir
git mv this that and the other topDir/
git commit -m"move things into topDir in preparation for exporting into new repo"
# now do the pull and push to import

Metoda opisana pod poniższym linkiem zainspirowało to odpowiedź. Podobało mi się, ponieważ wydawało się to bardziej proste. Ale uważaj! Są smoki! https://help.github.com/articles/importing-an-external-git-repository git push --mirror url:repo_dest wypycha lokalną historię i stan repo do remote (url: repo_dest). Ale usuwa starą historię i stan pilota. Zabawa trwa! :- E

 3
Author: gaoithe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-20 09:50:47

W moim przypadku chciałem zaimportować tylko niektóre pliki z innego repozytorium (XXX). Subtree było dla mnie zbyt skomplikowane, a inne rozwiązania nie działały. Tak zrobiłem:

ALL_COMMITS=$(git log --reverse --pretty=format:%H -- ZZZ | tr '\n' ' ')

Daje to oddzieloną spacjami listę wszystkich zmian, które mają wpływ na pliki, które chciałem zaimportować (ZZZ) w odwrotnej kolejności (być może będziesz musiał dodać --follow, aby przechwycić zmiany nazw). Następnie weszłam do repozytorium docelowego (YYY), dodałam drugie repozytorium (XXX) jako zdalne, pobrałam z niego i wreszcie:

git cherry-pick $ALL_COMMITS

Który dodaje wszystkie commity do Twojej gałęzi, będziesz miał wszystkie pliki z ich historią i możesz z nimi robić co chcesz, tak jakby zawsze były w tym repozytorium.

 1
Author: Sebastian Blask,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-27 09:22:14

Zobacz podstawowy przykład W Ten artykuł i rozważ takie mapowanie w repozytoriach:

  • A YYY,
  • B XXX

Po całej aktywności opisanej w tym rozdziale (po połączeniu) Usuń gałąź B-master:

$ git branch -d B-master

Następnie wciśnij zmiany.

U mnie działa.
 1
Author: VeLKerr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-18 16:59:27

Byłem w sytuacji, w której szukałem -s theirs ale oczywiście ta strategia nie istnieje. Moja historia była taka, że rozwidlałem projekt na Githubie, a teraz z jakiegoś powodu mój lokalny master nie mógł zostać połączony z upstream/master, chociaż nie wprowadziłem żadnych lokalnych zmian w tej gałęzi. (Naprawdę Nie wiem , co się tam stało-myślę, że upstream zrobił jakieś brudne pchnięcia za kulisami, może?)

Skończyło się to

# as per https://help.github.com/articles/syncing-a-fork/
git fetch upstream
git checkout master
git merge upstream/master
....
# Lots of conflicts, ended up just abandonging this approach
git reset --hard   # Ditch failed merge
git checkout upstream/master
# Now in detached state
git branch -d master # !
git checkout -b master   # create new master from upstream/master

Więc teraz moje master jest znowu zsynchronizowane z upstream/master (i możesz powtórzyć powyższe dla każdej innej gałęzi, którą również chcesz zsynchronizować w podobny sposób).

 0
Author: tripleee,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-05 11:16:00

Mogę zaproponować inne rozwiązanie (alternatywa dla Git-submodules) dla Twojego problemu - gil (Git links) tool

Pozwala opisywać i zarządzać złożonymi zależnościami od repozytoriów git.

Zapewnia również rozwiązanie problemu zależności rekurencyjnych podmodułów git.

Weź pod uwagę następujące zależności projektu: przykładowy wykres zależności repozytorium git

Następnie można zdefiniować .gitlinks Plik z repozytoriami opis relacji:

# Projects
CppBenchmark CppBenchmark https://github.com/chronoxor/CppBenchmark.git master
CppCommon CppCommon https://github.com/chronoxor/CppCommon.git master
CppLogging CppLogging https://github.com/chronoxor/CppLogging.git master

# Modules
Catch2 modules/Catch2 https://github.com/catchorg/Catch2.git master
cpp-optparse modules/cpp-optparse https://github.com/weisslj/cpp-optparse.git master
fmt modules/fmt https://github.com/fmtlib/fmt.git master
HdrHistogram modules/HdrHistogram https://github.com/HdrHistogram/HdrHistogram_c.git master
zlib modules/zlib https://github.com/madler/zlib.git master

# Scripts
build scripts/build https://github.com/chronoxor/CppBuildScripts.git master
cmake scripts/cmake https://github.com/chronoxor/CppCMakeScripts.git master

Każda linia opisuje git link w następującym formacie:

  1. unikalna nazwa repozytorium
  2. względna ścieżka repozytorium (zaczynająca się od ścieżki .plik gitlinks)
  3. repozytorium Git, które zostanie użyte w Komendzie git clone Oddział repozytorium do kasy
  4. Pusta linia lub linia zaczynająca się od # nie są przetwarzane (traktowane jako komentarz).

W końcu musisz zaktualizować swoją próbkę root repozytorium:

# Clone and link all git links dependencies from .gitlinks file
gil clone
gil link

# The same result with a single command
gil update

W rezultacie sklonujesz wszystkie wymagane projekty i połączysz je ze sobą w odpowiedni sposób.

Jeśli Chcesz zatwierdzić wszystkie zmiany w jakimś repozytorium ze wszystkimi zmianami w powiązanych repozytoriach potomnych, możesz to zrobić za pomocą jednego polecenia:

gil commit -a -m "Some big update"

Polecenia Pull, push działają w podobny sposób:

gil pull
gil push

Narzędzie Gil (Git links) obsługuje następujące polecenia:

usage: gil command arguments
Supported commands:
    help - show this help
    context - command will show the current git link context of the current directory
    clone - clone all repositories that are missed in the current context
    link - link all repositories that are missed in the current context
    update - clone and link in a single operation
    pull - pull all repositories in the current directory
    push - push all repositories in the current directory
    commit - commit all repositories in the current directory

Więcej o Git recursive submodules dependency problem .

 0
Author: chronoxor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-12-30 19:28:14

Nie znam łatwego sposobu na to. Możesz to zrobić:

  1. Użyj git filter-branch, aby dodać super-katalog ZZZ do repozytorium XXX
  2. Przenieś nową gałąź do repozytorium YYY
  3. Połącz popchniętą gałąź z pniem YYY.

Mogę edytować ze szczegółami, jeśli brzmi to atrakcyjnie.

 -1
Author: Walter Mundt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-05 21:06:03

Myślę, że możesz to zrobić używając 'git mv' i 'git pull'.

Jestem uczciwym git noobem - więc uważaj na swoje główne repozytorium - ale właśnie próbowałem tego w temp dir i wydaje się działać.

Po pierwsze-Zmień nazwę struktury XXX, aby pasowała do tego, jak chcesz, aby wyglądała, gdy jest w yyy:

cd XXX
mkdir tmp
git mv ZZZ tmp/ZZZ
git mv tmp ZZZ

Teraz XXX wygląda tak:

XXX
 |- ZZZ
     |- ZZZ

Teraz użyj 'git pull', aby pobrać zmiany w poprzek:

cd ../YYY
git pull ../XXX

Teraz YYY wygląda tak:

YYY
 |- ZZZ
     |- ZZZ
 |- (other folders that already were in YYY)
 -2
Author: Aaron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-05 21:05:09