it-swarm.com.de

Wie importiere ich ein vorhandenes Git-Repository in ein anderes?

Ich habe ein Git-Repository in einem Ordner namensXXX, und ich habe ein zweites Git-Repository mit dem NamenYYY.

Ich möchte das RepositoryXXXin das RepositoryYYYals Unterverzeichnis mit dem NamenZZZimportieren und den Änderungsverlauf allerXXXzuYYY.

Ordnerstruktur vor:

XXX
 |- .git
 |- (project files)
YYY
 |- .git
 |- (project files)

Ordnerstruktur nach:

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

Kann dies getan werden oder muss ich auf Submodule zurückgreifen?

416
Vijay Patel

Der einfachste Weg wäre wahrscheinlich, das XXX - Zeug in einen Zweig in YYY zu ziehen und dann in master zusammenzuführen:

In 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

Ich habe das gerade mit ein paar meiner Repos ausprobiert und es funktioniert. Anders als Jörgs Antwort lässt Sie das andere Repo nicht weiter verwenden, aber ich glaube nicht, dass Sie das sowieso angegeben haben.

Hinweis: Da dies ursprünglich 2009 geschrieben wurde, hat git die in der nachstehenden Antwort erwähnte Teilbaumzusammenführung hinzugefügt. Ich würde diese Methode wahrscheinlich heute verwenden, obwohl diese Methode natürlich immer noch funktioniert.

369
ebneter

Wenn Sie den genauen Commit-Verlauf des zweiten Repositorys beibehalten möchten und daher auch die Möglichkeit haben möchten, Upstream-Änderungen in der Zukunft problemlos zusammenzuführen, haben Sie hier die gewünschte Methode. Dies führt zu einem unveränderten Verlauf des Teilbaums, der in Ihr Repo importiert wird, und einer Zusammenführungsverpflichtung, um das zusammengeführte Repository in das Unterverzeichnis zu verschieben.

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."

Sie können Upstream-Änderungen wie folgt verfolgen:

git pull -s subtree XXX_remote master

Git ermittelt selbstständig, wo sich die Wurzeln befinden, bevor die Zusammenführung durchgeführt wird. Daher müssen Sie das Präfix für nachfolgende Zusammenführungen nicht angeben.

Git-Versionen vor 2.9: Sie müssen die Option --allow-unrelated-histories nicht an git merge übergeben.

Die Methode in der anderen Antwort, die read-tree verwendet und den merge -s ours-Schritt überspringt, unterscheidet sich praktisch nicht vom Kopieren der Dateien mit cp und dem Festlegen des Ergebnisses.

Ursprüngliche Quelle war von githubs Hilfeartikel "Subtree Merge" .

336
ColinM

git-subtree ist ein Skript, das genau für diesen Anwendungsfall entwickelt wurde, indem mehrere Repositorys zu einem zusammengefügt werden, während der Verlauf (und/oder das Aufteilen des Verlaufs von Unterbäumen, obwohl dies für diese Frage irrelevant zu sein scheint) erstellt wurde. Es wird als Teil des Git-Baums verteilt seit Release 1.7.11 .

Um ein Repository <repo> bei Revision <rev> als Unterverzeichnis <prefix> zusammenzuführen, verwenden Sie git subtree add wie folgt:

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

git-subtree implementiert die Zusammenführungsstrategie subtree benutzerfreundlicher.

Für Ihren Fall würden Sie im Repository YYY Folgendes ausführen:

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

Es gibt ein bekanntes Beispiel dafür im Git-Repository selbst, das in der Git-Community kollektiv als " die coolste Zusammenführung aller Zeiten " bezeichnet wird (nach der Betreffzeile, die Linus Torvalds in der E-Mail an den Git verwendet Mailingliste, die diese Zusammenführung beschreibt). In diesem Fall war die gitk Git-GUI, die jetzt Bestandteil von Git ist, tatsächlich ein separates Projekt. Es gelang Linus, dieses Repository auf eine Weise mit dem Git-Repository zusammenzuführen 

  • es erscheint im Git-Repository, als wäre es schon immer als Teil von Git entwickelt worden, 
  • die ganze geschichte bleibt erhalten und 
  • es kann immer noch unabhängig in seinem alten Repository entwickelt werden, wobei die Änderungen einfach git pulled sind.

Die E-Mail enthält die Schritte, die zum Reproduzieren erforderlich sind, ist jedoch nicht für schwache Nerven gedacht: erstens Linus wrote Git, daher weiß er wahrscheinlich etwas mehr darüber als Sie oder ich und Das war vor fast 5 Jahren und Git hat beträchtlich seitdem verbessert, daher ist es jetzt vielleicht viel einfacher.

Ich denke, heutzutage würde man in diesem speziellen Fall ein Gitk-Submodul verwenden.

46
Jörg W Mittag

Der einfachste Weg ist, git format-patch zu verwenden.

Angenommen, wir haben 2 Git-Repositories foo und bar.

foo enthält:

  • foo.txt
  • .git

bar enthält:

  • bar.txt
  • .git

und wir wollen mit foo enden, das die bar - Historie und diese Dateien enthält: 

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

Also das zu tun:

 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/*

Und wenn wir alle Message-Commits von Bar neu schreiben wollen, können wir das zB unter Linux tun:

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

Dies fügt am Anfang jeder Commit-Nachricht "[bar]" hinzu.

12
Damien R.

Basierend auf auf diesem Artikel , ist die Verwendung von Teilbaum das, was für mich funktioniert hat und nur die anwendbare Historie übertragen wurde. Posten Sie hier, falls jemand die Schritte benötigt (stellen Sie sicher, dass die Platzhalter durch für Sie zutreffende Werte ersetzt werden):

in Ihrem Quell-Repository Unterordner in einen neuen Zweig aufteilen

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

in Ihrem Ziel-Repo-Merge im Split-Ergebnis-Zweig

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

überprüfen Sie Ihre Änderungen und verpflichten Sie sich

git status
git commit

Vergiss es nicht 

Bereinigen Sie den Zweig subtree-split-result, indem Sie ihn löschen

git branch -D subtree-split-result

Entfernen Sie die hinzugefügte Fernbedienung, um die Daten aus dem Quell-Repo abzurufen

git remote rm merge-source-repo

6
Alex

Mit dieser Funktion wird das Remote-Repo in das lokale Repo-Verzeichnis geklont. Nach dem Zusammenführen aller Commits werden git log die ursprünglichen Commits und die richtigen Pfade angezeigt:

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"
}

Wie benutzt man:

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

Wenn Sie ein paar Änderungen vornehmen, können Sie sogar Dateien/Verzeichnisse des zusammengeführten Repos in verschiedene Pfade verschieben, zum Beispiel:

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"

Hinweise
Pfade werden durch sed ersetzt. Stellen Sie daher sicher, dass die Pfade nach dem Zusammenführen in die richtigen Pfade verschoben wurden.
Der Parameter --allow-unrelated-histories existiert nur seit git> = 2.9.

5
Andrey Izman

Das Hinzufügen einer weiteren Antwort ist meiner Meinung nach etwas einfacher. Repo_dest wird in repo_to_import gezogen, und anschließend wird ein Push --set-upstream-url: repo_dest-Master ausgeführt.

Diese Methode hat für mich funktioniert, indem ich mehrere kleinere Repos in einen größeren importierte.

So importieren Sie: repo1_to_import zu 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

Umbenennen oder verschieben Sie Dateien und Verzeichnisse an die gewünschte Stelle im Original-Repo, bevor Sie den Import durchführen. z.B. 

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

Die unter dem folgenden Link beschriebene Methode inspirierte diese Antwort. Ich mochte es, weil es einfacher schien. Aber Vorsicht! Es gibt Drachen! https://help.github.com/articles/importing-an-external-git-repositorygit Push --mirror url:repo_dest schiebt den lokalen Repo-Verlauf und Status in den Remote-Bereich (URL: repo_dest). ABER es löscht die alte Geschichte und den Zustand der Fernbedienung. Spaß folgt! : -E

3
gaoithe

In meinem Fall wollte ich nur einige Dateien aus dem anderen Repository (XXX) importieren. Der Teilbaum war für mich zu kompliziert und die anderen Lösungen funktionierten nicht. Das habe ich gemacht:

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

Dadurch erhalten Sie eine durch Leerzeichen getrennte Liste aller Commits, die sich auf die zu importierenden Dateien (ZZZ) in umgekehrter Reihenfolge auswirken (möglicherweise müssen Sie --follow hinzufügen, um auch Umbenennungen zu erfassen). Ich ging dann in das Ziel-Repository (YYY), fügte das andere Repository (XXX) als entfernt hinzu, machte einen Abruf davon und schließlich:

git cherry-pick $ALL_COMMITS

sie fügt Ihrem Zweig alle Commits hinzu. Sie haben also alle Dateien mit ihrem Verlauf und können mit ihnen machen, was Sie wollen, als wären sie immer in diesem Repository gewesen.

1
Sebastian Blask

Siehe Basic-Beispiel in diesem Artikel und erwäge eine solche Zuordnung zu Repositorys:

  • A <-> YYY
  • B <-> XXX

Entfernen Sie nach allen in diesem Kapitel beschriebenen Aktivitäten (nach dem Zusammenführen) den Zweig B-master:

$ git branch -d B-master

Dann drücken Sie die Änderungen.

Für mich geht das.

0
VeLKerr

Ich kann eine andere Lösung (alternativ zu git-submodules ) für Ihr Problem vorschlagen - gil (git links) tool

Es ermöglicht die Beschreibung und Verwaltung komplexer Git-Repositories-Abhängigkeiten. 

Außerdem bietet es eine Lösung für das Abhängigkeitsproblem der rekursiven Submodule git .

Stellen Sie sich vor, Sie haben die folgenden Projektabhängigkeiten: Beispielgit-Repository-Abhängigkeitsdiagramm

Dann können Sie eine .gitlinks-Datei mit der Beschreibung der Repositories definieren:

# 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

Jede Zeile beschreibt Git-Link im folgenden Format:

  1. Eindeutiger Name des Repositorys
  2. Relativer Pfad des Repositorys (ausgehend vom Pfad der .gitlinks-Datei)
  3. Git-Repository, das im Befehl git clone verwendet wird Repository-Zweig zur Kasse
  4. Leerzeilen oder Zeilen, die mit # beginnen, werden nicht analysiert (als Kommentar behandelt).

Zum Schluss müssen Sie Ihr Root-Beispiel-Repository aktualisieren:

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

# The same result with a single command
gil update

Als Ergebnis klonen Sie alle erforderlichen Projekte und verknüpfen sie auf geeignete Weise miteinander.

Wenn Sie alle Änderungen in einem Repository mit allen Änderungen in untergeordneten verknüpften Repositorys festschreiben möchten, können Sie dies mit einem einzigen Befehl tun:

gil commit -a -m "Some big update"

Pull, Push-Befehle funktionieren auf ähnliche Weise:

gil pull
gil Push

Gil (git links) unterstützt folgende Befehle:

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

Weitere Informationen zum Abhängigkeitsproblem der rekursiven Submodule git .

0
chronoxor

Ich war in einer Situation, in der ich nach -s theirs suchte, aber diese Strategie existiert natürlich nicht. Meine Geschichte war, dass ich auf GitHub ein Projekt gegabelt hatte, und aus irgendeinem Grund konnte mein lokales master nicht mit upstream/master zusammengeführt werden, obwohl ich an diesem Zweig keine lokalen Änderungen vorgenommen hatte. (Weiß wirklich nicht, was dort passiert ist - ich glaube, der Upstream hatte hinter den Kulissen vielleicht ein paar schmutzige Stöße gemacht?)

Was ich am Ende tat, war

# 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

Nun ist meine master wieder synchron mit upstream/master (und Sie können das Obige für jeden anderen Zweig wiederholen, den Sie ebenfalls synchronisieren möchten).

0
tripleee