Frage

Betrachten Sie das folgende Szenario:

Ich entwickelte ein kleines experimentelles Projekt in Ihrer eigenen Git-repo.Es hat sich mittlerweile gereift, und ich möchte Ein Teil des größeren Projekts B, die hat seine eigenen großen repository.Ich würde jetzt gern Ein, als ein Unterverzeichnis des B.

Wie führe ich Eine in B, ohne die Geschichte auf jeder Seite?

War es hilfreich?

Lösung

Ein einzelner Zweig eines anderen Repository kann problemlos unter einem Unterverzeichnis ihre Geschichte Halt platziert werden. Zum Beispiel:

git subtree add --prefix=rails git://github.com/rails/rails.git master

Dies wird als eine einzige erscheinen begehen, in der alle Dateien von Rails Master-Zweig in „Schienen“ Verzeichnis hinzugefügt. Doch die commit Titel enthält einen Verweis auf die alte Geschichte Baum:

  

Add 'Schienen /' von commit <rev>

Wo <rev> ist ein SHA-1-Hash begehen. Sie können immer noch die Geschichte sehen, die einige Änderungen schuld.

git log <rev>
git blame <rev> -- README.md

Beachten Sie, dass Sie nicht das Verzeichnispräfix von hier aus sehen können, da dies ein tatsächlicher alter Zweig links intakt ist. Sie sollten dies wie eine gewöhnliche Datei bewegen behandeln begehen, werden Sie gleich einen zusätzlichen Sprung brauchen, wenn es zu erreichen

.
# finishes with all files added at once commit
git log rails/README.md

# then continue from original tree
git log <rev> -- README.md

Es gibt komplexere Lösungen wie dies manuell tun oder die Geschichte umzuschreiben, wie in anderen Antworten beschrieben.

Der git-subtree Befehl ist ein Teil der offiziellen git-contrib, einig Paket-Manager installieren Sie es standardmäßig (O X Homebrew). Aber Sie können es selbst installieren zusätzlich zu git.

Andere Tipps

Wenn Sie project-a in project-b fusionieren:

cd path/to/project-b
git remote add project-a path/to/project-a
git fetch project-a --tags
git merge --allow-unrelated-histories project-a/master # or whichever branch you want to merge
git remote remove project-a

Entnommen aus: git unterschiedliche Repositories fusionieren

Diese Methode ist für mich ziemlich gut funktioniert, ist es kürzer und meiner Meinung nach viel sauberer.

Hinweis: Der --allow-unrelated-histories Parameter existiert erst seit git> = 2,9. Siehe Git - git merge Dokumentation / --allow- in keinem Zusammenhang Geschichten

Aktualisieren :. Hinzugefügt --tags wie @jstadler vorgeschlagen, um Tags zu halten

Es gibt zwei mögliche Lösungen:

Submodule

So oder Kopie Repository A in ein separates Verzeichnis in größerem Projekt B, oder (vielleicht besser) Klon-Repository A in ein Unterverzeichnis im Projekt B. Verwenden Sie dann git Submodul dieses Repository eine machen < strong> Submodul ein Endlager B.

Dies ist eine gute Lösung für lose gekoppelte Repositories, wo die Entwicklung in Repository A weiter, und der größte Teil der Entwicklung ist eine separate eigenständige Entwicklung in A. Siehe auch SubmoduleSupport und GitSubmoduleTutorial Seiten auf Git Wiki.

Subtree merge

Sie können Repository A in ein Unterverzeichnis eines Projekts B mit der subtree merge Strategie verschmelzen. Dies ist in der Subtree Merging und Sie von Markus Prinz.

git remote add -f Bproject /path/to/B
git merge -s ours --allow-unrelated-histories --no-commit Bproject/master
git read-tree --prefix=dir-B/ -u Bproject/master
git commit -m "Merge B project as our subdirectory"
git pull -s subtree Bproject master

(Option --allow-unrelated-histories für Git benötigt> = 2.9.0).

Sie können auch git subtree Werkzeug ( Repository auf GitHub ) von apenwarr (Avery Pennarun), zum Beispiel in seiner Blog-Post angekündigt Eine neue Alternative zu Git Submodule. git subtree


Ich denke, in Ihrem Fall (A ist Teil des größeren Projekts B sein) die richtige Lösung wäre, verwenden subtree merge .

Die submodule Ansatz ist gut, wenn Sie wollen zu erhalten das Projekt separat.Allerdings, wenn Sie wirklich wollen, zu verschmelzen beide Projekte in der selben repository, dann haben Sie etwas mehr Arbeit zu tun.

Die erste Sache wäre, die Nutzung git filter-branch zu schreiben, die Namen von allem in der zweiten repository im Verzeichnis, wo Sie würde wie Sie zu Ende up.Also statt foo.c, bar.html, Sie würden haben projb/foo.c und projb/bar.html.

Dann, Sie sollten in der Lage sein, etwas zu tun, wie die folgenden:

git remote add projb [wherever]
git pull projb

Die git pull tun git fetch gefolgt von einem git merge.Es sollte keine Konflikte, wenn der repository-Sie ziehen, um nicht noch ein projb/ Verzeichnis.

Suche zeigt an, dass etwas ähnliches getan wurde, um Zusammenführen gitk in git.Junio C Hamano schreibt über ihn hier: http://www.mail-archive.com/git@vger.kernel.org/msg03395.html

git-subtree ist schön, aber es ist wahrscheinlich nicht die gewünschte ist.

Zum Beispiel, wenn projectA ist das Verzeichnis, in B erstellt, nach git subtree,

git log projectA

Listen nur ein begehen: die Zusammenführung. Die Commits aus dem fusionierten Projekt sind für verschiedene Pfade, so dass sie nicht auf.

Greg Hewgill Antwort am nächsten kommt, obwohl es eigentlich nicht sagen, wie die Pfade neu zu schreiben.


Die Lösung ist überraschend einfach.

(1) In A,

PREFIX=projectA #adjust this

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

. Hinweis: Diese neu schreibt Geschichte, wenn Sie also mit dieser Repo-A fortsetzen möchten, können Sie klonen (kopieren) eine Wegwerf-Kopie davon zuerst

(2) Dann in B, führt

git pull path/to/A

Voila! Sie haben ein projectA Verzeichnis in B. Wenn Sie git log projectA ausführen, werden Sie alle Commits von A sehen.


In meinem Fall wollte ich zwei Unterverzeichnisse, projectA und projectB. In diesem Fall hätte ich Schritt (1), als auch auf B.

Wenn beiden Repositories gleiche Art von Dateien (wie zwei Rails-Repositories für verschiedene Projekte) können Sie die Daten des sekundären Repository zu Ihrem aktuellen Repository holen:

git fetch git://repository.url/repo.git master:branch_name

und dann verschmelzen sie zu aktuellen Repository:

git merge --allow-unrelated-histories branch_name

Wenn Sie Ihre Git-Version kleiner als 2.9 ist, entfernen --allow-unrelated-histories.

Danach können Konflikte auftreten. Sie können sie zum Beispiel mit git mergetool lösen. kdiff3 kann ausschließlich mit der Tastatur verwendet werden, so 5 Konflikt Datei nimmt, wenn der Code nur ein paar Minuten zu lesen.

Denken Sie daran, die Zusammenführung zu beenden:

git commit

Ich hielt Geschichte zu verlieren, wenn verschmelzen mit, so landete ich da in meinem Fall mit bis rebase die beiden Repositories unterschiedlich genug sind, um nicht am Ende bei jeder Fusion begehen:

git clone git@gitorious/projA.git projA
git clone git@gitorious/projB.git projB

cd projB
git remote add projA ../projA/
git fetch projA 
git rebase projA/master HEAD

=> Konflikte lösen, dann weiter, so oft wie nötig ...

git rebase --continue

Dadurch führt zu einem Projekt aller Commits von projA von Commits von projB gefolgt von

In meinem Fall hatte ich einen my-plugin Repository und einen main-project Repository, und ich wollte, dass my-plugin, so zu tun hatte immer im plugins Unterverzeichnis von main-project entwickelt.

Im Grunde schrieb ich die Geschichte des my-plugin Repository, so dass es alle Entwicklung erschien fand im plugins/my-plugin Unterverzeichnis. Dann fügte ich die Entwicklungsgeschichte von my-plugin in die main-project Geschichte, und fusionierte die beiden zusammen Bäumen. Da es bereits kein plugins/my-plugin Verzeichnis im main-project Repository vorhanden war, war dies ein trivialer keine Konflikte verschmelzen. Das resultierende Repository alle Geschichte von den beiden ursprünglichen Projekte enthalten sind, und hatte zwei Wurzeln.

TL; DR

$ cp -R my-plugin my-plugin-dirty
$ cd my-plugin-dirty
$ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|plugins) plugins/my-plugin || true)'" -- --all
$ cd ../main-project
$ git checkout master
$ git remote add --fetch my-plugin ../my-plugin-dirty
$ git merge my-plugin/master --allow-unrelated-histories
$ cd ..
$ rm -rf my-plugin-dirty

Langfassung

Sie zunächst eine Kopie des my-plugin Repository erstellen, weil wir Umschreiben der Geschichte dieses Repository sein werden.

Nun, um die Wurzel des my-plugin Repository navigieren, überprüfen Sie Ihren Hauptzweig (wahrscheinlich master), und führen Sie den folgenden Befehl ein. Natürlich können Sie für my-plugin und plugins was auch immer Ihre tatsächlichen Namen ersetzen soll.

$ git filter-branch -f --tree-filter "zsh -c 'setopt extended_glob && setopt glob_dots && mkdir -p plugins/my-plugin && (mv ^(.git|plugins) plugins/my-plugin || true)'" -- --all

Jetzt für eine Erklärung. git filter-branch --tree-filter (...) HEAD läuft die (...) Befehl auf jedem Commit, dass von HEAD erreichbar ist. Beachten Sie, dass dies funktioniert direkt auf die Daten für jede gespeicherte begehen, so dass wir müssen nicht über Begriffe „Arbeitsverzeichnis“ Sorge „Index“, „staging“, und so weiter.

Wenn Sie einen filter-branch Befehl ausführen, der fehlschlägt, wird es hinter einigen Dateien im Verzeichnis .git verlassen und das nächste Mal, wenn Sie filter-branch versuchen, es wird darüber beschweren, wenn Sie die Option -f liefern filter-branch.

Wie für den eigentlichen Befehl, ich hatte nicht viel Glück bekommen bash zu tun, was ich wollte, so stattdessen verwende ich zsh -c zsh ausführen, um einen Befehl zu machen. Zuerst habe ich die extended_glob Option, das ist, was die ^(...) Syntax im mv Befehl ermöglicht, sowie die glob_dots Option, die mir dotfiles (wie .gitignore) mit einem glob (^(...)).

auswählen können

Als nächstes Ich verwende den mkdir -p Befehl sowohl plugins und plugins/my-plugin zugleich erstellen.

mit Ausnahme von zsh und den neu erstellten Ordner ^(.git|plugins)

Schließlich verwende ich die .git „negative glob“ -Funktion my-plugin alle Dateien im Stammverzeichnis des Repository übereinstimmen. (Ohne .git nicht notwendig sein könnte hier, sondern versuchen, ein Verzeichnis zu verschieben in sich selbst ein Fehler ist.)

In meinem Repository, das anfängliche begangen hat keine Dateien zu enthalten, so dass der mv Befehl einen Fehler auf der ursprünglichen zurück begehen (da nichts vorhanden war zu bewegen). Deshalb habe ich eine || true so dass git filter-branch nicht abbrechen würde.

Die --all Option weist filter-branch die Geschichte neu zu schreiben für alle Filialen in dem Repository und die zusätzlichen -- notwendig git zu sagen, es als Teil der Optionsliste zu interpretieren für Filialen neu zu schreiben, statt als eine Option selbst filter-branch.

Nun navigieren Sie zu Ihrem main-project Repository und überprüfen, was Zweig Sie in zusammenführen möchten. Fügen Sie Ihre lokale Kopie des my-plugin Repository (mit seiner Geschichte geändert) als Fern von main-project mit:

$ git remote add --fetch my-plugin $PATH_TO_MY_PLUGIN_REPOSITORY

Sie werden nun zwei unabhängige Bäume in Ihrer begehen Geschichte, die Sie gut mit visualisieren können:

$ git log --color --graph --decorate --all

So führen sie, zu verwenden:

$ git merge my-plugin/master --allow-unrelated-histories

Beachten Sie, dass in pre-2.9.0 Git, die --allow-unrelated-histories Option existiert nicht. Wenn Sie eine dieser Versionen verwenden, lassen Sie einfach die Option: die Fehlermeldung, die verhindert --allow-unrelated-histories war auch in 2.9.0 hinzugefügt

.

Sie sollten keine Zusammenführung Konflikte haben. Wenn Sie das tun, bedeutet es wahrscheinlich, dass entweder der filter-branch Befehl nicht richtig funktionierte, oder es wurde bereits ein plugins/my-plugin Verzeichnis in main-project.

Stellen Sie sicher, eine erläuternde Commit-Nachricht für alle zukünftigen Beitragszahler geben Sie fragen, was hackery vorging, um ein Repository mit zwei Wurzeln zu machen.

Sie können den neuen Visualisierung begeht Graph, der zwei Wurzel verpflichtet haben sollte, die oben git log Befehl. Beachten Sie, dass nur die master Zweig zusammengeführt werden . Dies bedeutet, dass, wenn Sie wichtige Arbeit auf andere my-plugin Zweige haben, die Sie in den main-project Baum zusammenführen möchten, sollten Sie von dem Löschen des my-plugin Fern verzichten, bis Sie diese verschmilzt getan haben. Wenn Sie dies nicht tun, dann wird die Commits aus diesen Filialen werden nach wie vor in der main-project Repository sein, aber einige werden auf eventuelle Garbage Collection nicht erreichbar und anfällig sein. (Auch Sie werden sie von SHA verweisen, weil ein Remote-Löschen entfernt seinen Remote-Tracking-Zweig.)

Optional, nachdem Sie alles zusammengeschlossen haben Sie von my-plugin behalten möchten, können Sie die my-plugin entfernen Fernbedienung verwenden:

$ git remote remove my-plugin

Sie können nun sicher die Kopie des my-plugin Repository, dessen Geschichte löschen Sie geändert haben. In meinem Fall habe ich auch noch eine deprecation Mitteilung an die realen my-plugin Repository nach der Zusammenführung abgeschlossen war und gedrückt wird.


Getestet auf Mac OS X El Capitan mit git --version 2.9.0 und zsh --version 5.2. Ihre Ergebnisse können variieren.

Referenzen:

Ich habe versucht, das gleiche für Tag zu tun, ich bin mit git 2.7.2. Subtree nicht die Geschichte bewahren.

Sie können diese Methode verwenden, wenn Sie nicht wieder das alte Projekt verwenden werden.

Ich würde vorschlagen, dass Sie Zweig B erste und die Arbeit in der Branche.

Hier sind die Schritte ohne Verzweigung:

cd B

# You are going to merge A into B, so first move all of B's files into a sub dir
mkdir B

# Move all files to B, till there is nothing in the dir but .git and B
git mv <files> B

git add .

git commit -m "Moving content of project B in preparation for merge from A"


# Now merge A into B
git remote add -f A <A repo url>

git merge A/<branch>

mkdir A

# move all the files into subdir A, excluding .git
git mv <files> A

git commit -m "Moved A into subdir"


# Move B's files back to root    
git mv B/* ./

rm -rf B

git commit -m "Reset B to original state"

git push

Wenn Sie nun eine der Dateien in subdir A log erhalten Sie die vollständige Geschichte bekommen

git log --follow A/<file>

Das war die Post, die mir dabei helfen tun:

http: // saintgimp.org/2013/01/22/merging-two-git-repositories-into-one-repository-without-losing-file-history/

Wenn Sie die Dateien von einem Zweig in Repo-B in einem setzen subtree von Repo-A und auch die Geschichte bewahren, lesen. (Im Beispiel unten, gehe ich davon aus, dass wir Repo-B Master-Zweig übertragen wollen in Master-Zweig des repo A).

Bei der Repo-A, zunächst den folgenden tun, um Repo-B zur Verfügung zu stellen:

git remote add B ../B # Add repo B as a new remote.
git fetch B

Nun erstellen wir eine brandneue Filiale (mit nur einer Festschreibung) in Repo-A, dass wir new_b_root nennen. Die sich ergebende begehen werden die Dateien, die von Repo-B Master-Zweig in der ersten begehen begangen wurden, sondern in einem Unterverzeichnis namens path/to/b-files/ setzen.

git checkout --orphan new_b_root master
git rm -rf . # Remove all files.
git cherry-pick -n `git rev-list --max-parents=0 B/master`
mkdir -p path/to/b-files
git mv README path/to/b-files/
git commit --date="$(git log --format='%ai' $(git rev-list --max-parents=0 B/master))"

Erläuterung: Die --orphan Option zur Kasse Befehl checkt die Dateien von einem Master-Zweig aber schafft keine begehen. Wir könnten ausgewählt jede begehen, weil nächst wir alle sowieso die Dateien löschen. Dann, ohne noch (-n) zu begehen, wir herauspicken die erste vom Master Niederlassung B begehen. (Der Kirsch Pick bewahrt die ursprüngliche Nachricht begehen, die eine gerade Kasse scheint nicht zu tun.) Dann erstellen wir den Teilbaum, wo wir alle Dateien aus Repo B. setzen wollen Wir müssen dann alle Dateien verschieben, die in die eingeführt wurden, Kirsche-Pick in die Unterstruktur. In dem obigen Beispiel gibt es nur eine README Datei zu bewegen. Dann begehen wir unsere B-Repo-Wurzel begehen, und zugleich, wir erhalten auch die Zeitstempel des ursprünglichen begehen.

Jetzt werden wir einen neuen B/master Zweig auf dem neu geschaffenen new_b_root erstellen. Wir nennen die Niederlassung b:

git checkout -b b B/master
git rebase -s recursive -Xsubtree=path/to/b-files/ new_b_root

Nun verschmelzen wir unsere b Niederlassung in A/master:

git checkout master
git merge --allow-unrelated-histories --no-commit b
git commit -m 'Merge repo B into repo A.'

Schließlich können Sie die B Remote- und temporäre Zweige entfernen:

git remote remove B
git branch -D new_b_root b

Der letzte Graph wird eine Struktur wie folgt aussehen:

 image description hier

Ich weiß, es ist lange nach der Tat, aber ich war mit den anderen Antworten nicht glücklich, dass ich hier gefunden, so dass ich dies schrieb:

me=$(basename $0)

TMP=$(mktemp -d /tmp/$me.XXXXXXXX)
echo 
echo "building new repo in $TMP"
echo
sleep 1

set -e

cd $TMP
mkdir new-repo
cd new-repo
    git init
    cd ..

x=0
while [ -n "$1" ]; do
    repo="$1"; shift
    git clone "$repo"
    dirname=$(basename $repo | sed -e 's/\s/-/g')
    if [[ $dirname =~ ^git:.*\.git$ ]]; then
        dirname=$(echo $dirname | sed s/.git$//)
    fi

    cd $dirname
        git remote rm origin
        git filter-branch --tree-filter \
            "(mkdir -p $dirname; find . -maxdepth 1 ! -name . ! -name .git ! -name $dirname -exec mv {} $dirname/ \;)"
        cd ..

    cd new-repo
        git pull --no-commit ../$dirname
        [ $x -gt 0 ] && git commit -m "merge made by $me"
        cd ..

    x=$(( x + 1 ))
done

Ich habe eine Menge Informationen hier auf Stack Overflow gesammelt usw. und haben verwalten ein Skript zusammengestellt, die das Problem für mich gelöst.

Der Nachteil ist, dass es nur berücksichtigt die ‚Entwicklung‘ Zweig jedes Repository und verschmilzt sie in ein separates Verzeichnis in einem völlig neuen Repository.

Tags und andere Zweige ignoriert werden -. Dies nicht sein könnte, was Sie wollen

Das Skript selbst verfügen über Griffe Zweige und Tags - sie in das neue Projekt umbenennen, damit Sie wissen, wo sie herkamen

.
#!/bin/bash
#
################################################################################
## Script to merge multiple git repositories into a new repository
## - The new repository will contain a folder for every merged repository
## - The script adds remotes for every project and then merges in every branch
##   and tag. These are renamed to have the origin project name as a prefix
##
## Usage: mergeGitRepositories.sh <new_project> <my_repo_urls.lst>
## - where <new_project> is the name of the new project to create
## - and <my_repo_urls.lst> is a file contaning the URLs to the respositories
##   which are to be merged on separate lines.
##
## Author: Robert von Burg
##            eitch@eitchnet.ch
##
## Version: 0.3.2
## Created: 2018-02-05
##
################################################################################
#

# disallow using undefined variables
shopt -s -o nounset

# Script variables
declare SCRIPT_NAME="${0##*/}"
declare SCRIPT_DIR="$(cd ${0%/*} ; pwd)"
declare ROOT_DIR="$PWD"
IFS=$'\n'

# Detect proper usage
if [ "$#" -ne "2" ] ; then
  echo -e "ERROR: Usage: $0 <new_project> <my_repo_urls.lst>"
  exit 1
fi


## Script variables
PROJECT_NAME="${1}"
PROJECT_PATH="${ROOT_DIR}/${PROJECT_NAME}"
TIMESTAMP="$(date +%s)"
LOG_FILE="${ROOT_DIR}/${PROJECT_NAME}_merge.${TIMESTAMP}.log"
REPO_FILE="${2}"
REPO_URL_FILE="${ROOT_DIR}/${REPO_FILE}"


# Script functions
function failed() {
  echo -e "ERROR: Merging of projects failed:"
  echo -e "ERROR: Merging of projects failed:" >>${LOG_FILE} 2>&1
  echo -e "$1"
  exit 1
}

function commit_merge() {
  current_branch="$(git symbolic-ref HEAD 2>/dev/null)"
  if [[ ! -f ".git/MERGE_HEAD" ]] ; then
    echo -e "INFO:   No commit required."
    echo -e "INFO:   No commit required." >>${LOG_FILE} 2>&1
  else
    echo -e "INFO:   Committing ${sub_project}..."
    echo -e "INFO:   Committing ${sub_project}..." >>${LOG_FILE} 2>&1
    if ! git commit -m "[Project] Merged branch '$1' of ${sub_project}" >>${LOG_FILE} 2>&1 ; then
      failed "Failed to commit merge of branch '$1' of ${sub_project} into ${current_branch}"
    fi
  fi
}


# Make sure the REPO_URL_FILE exists
if [ ! -e "${REPO_URL_FILE}" ] ; then
  echo -e "ERROR: Repo file ${REPO_URL_FILE} does not exist!"
  exit 1
fi


# Make sure the required directories don't exist
if [ -e "${PROJECT_PATH}" ] ; then
  echo -e "ERROR: Project ${PROJECT_NAME} already exists!"
  exit 1
fi


# create the new project
echo -e "INFO: Logging to ${LOG_FILE}"
echo -e "INFO: Creating new git repository ${PROJECT_NAME}..."
echo -e "INFO: Creating new git repository ${PROJECT_NAME}..." >>${LOG_FILE} 2>&1
echo -e "===================================================="
echo -e "====================================================" >>${LOG_FILE} 2>&1
cd ${ROOT_DIR}
mkdir ${PROJECT_NAME}
cd ${PROJECT_NAME}
git init
echo "Initial Commit" > initial_commit
# Since this is a new repository we need to have at least one commit
# thus were we create temporary file, but we delete it again.
# Deleting it guarantees we don't have conflicts later when merging
git add initial_commit
git commit --quiet -m "[Project] Initial Master Repo Commit"
git rm --quiet initial_commit
git commit --quiet -m "[Project] Initial Master Repo Commit"
echo


# Merge all projects into the branches of this project
echo -e "INFO: Merging projects into new repository..."
echo -e "INFO: Merging projects into new repository..." >>${LOG_FILE} 2>&1
echo -e "===================================================="
echo -e "====================================================" >>${LOG_FILE} 2>&1
for url in $(cat ${REPO_URL_FILE}) ; do

  if [[ "${url:0:1}" == '#' ]] ; then
    continue
  fi

  # extract the name of this project
  export sub_project=${url##*/}
  sub_project=${sub_project%*.git}

  echo -e "INFO: Project ${sub_project}"
  echo -e "INFO: Project ${sub_project}" >>${LOG_FILE} 2>&1
  echo -e "----------------------------------------------------"
  echo -e "----------------------------------------------------" >>${LOG_FILE} 2>&1

  # Fetch the project
  echo -e "INFO:   Fetching ${sub_project}..."
  echo -e "INFO:   Fetching ${sub_project}..." >>${LOG_FILE} 2>&1
  git remote add "${sub_project}" "${url}"
  if ! git fetch --tags --quiet ${sub_project} >>${LOG_FILE} 2>&1 ; then
    failed "Failed to fetch project ${sub_project}"
  fi

  # add remote branches
  echo -e "INFO:   Creating local branches for ${sub_project}..."
  echo -e "INFO:   Creating local branches for ${sub_project}..." >>${LOG_FILE} 2>&1
  while read branch ; do
    branch_ref=$(echo $branch | tr " " "\t" | cut -f 1)
    branch_name=$(echo $branch | tr " " "\t" | cut -f 2 | cut -d / -f 3-)

    echo -e "INFO:   Creating branch ${branch_name}..."
    echo -e "INFO:   Creating branch ${branch_name}..." >>${LOG_FILE} 2>&1

    # create and checkout new merge branch off of master
    if ! git checkout -b "${sub_project}/${branch_name}" master >>${LOG_FILE} 2>&1 ; then failed "Failed preparing ${branch_name}" ; fi
    if ! git reset --hard ; then failed "Failed preparing ${branch_name}" >>${LOG_FILE} 2>&1 ; fi
    if ! git clean -d --force ; then failed "Failed preparing ${branch_name}" >>${LOG_FILE} 2>&1 ; fi

    # Merge the project
    echo -e "INFO:   Merging ${sub_project}..."
    echo -e "INFO:   Merging ${sub_project}..." >>${LOG_FILE} 2>&1
    if ! git merge --allow-unrelated-histories --no-commit "remotes/${sub_project}/${branch_name}" >>${LOG_FILE} 2>&1 ; then
      failed "Failed to merge branch 'remotes/${sub_project}/${branch_name}' from ${sub_project}"
    fi

    # And now see if we need to commit (maybe there was a merge)
    commit_merge "${sub_project}/${branch_name}"

    # relocate projects files into own directory
    if [ "$(ls)" == "${sub_project}" ] ; then
      echo -e "WARN:   Not moving files in branch ${branch_name} of ${sub_project} as already only one root level."
      echo -e "WARN:   Not moving files in branch ${branch_name} of ${sub_project} as already only one root level." >>${LOG_FILE} 2>&1
    else
      echo -e "INFO:   Moving files in branch ${branch_name} of ${sub_project} so we have a single directory..."
      echo -e "INFO:   Moving files in branch ${branch_name} of ${sub_project} so we have a single directory..." >>${LOG_FILE} 2>&1
      mkdir ${sub_project}
      for f in $(ls -a) ; do
        if  [[ "$f" == "${sub_project}" ]] ||
            [[ "$f" == "." ]] ||
            [[ "$f" == ".." ]] ; then
          continue
        fi
        git mv -k "$f" "${sub_project}/"
      done

      # commit the moving
      if ! git commit --quiet -m  "[Project] Move ${sub_project} files into sub directory" ; then
        failed "Failed to commit moving of ${sub_project} files into sub directory"
      fi
    fi
    echo
  done < <(git ls-remote --heads ${sub_project})


  # checkout master of sub probject
  if ! git checkout "${sub_project}/master" >>${LOG_FILE} 2>&1 ; then
    failed "sub_project ${sub_project} is missing master branch!"
  fi

  # copy remote tags
  echo -e "INFO:   Copying tags for ${sub_project}..."
  echo -e "INFO:   Copying tags for ${sub_project}..." >>${LOG_FILE} 2>&1
  while read tag ; do
    tag_ref=$(echo $tag | tr " " "\t" | cut -f 1)
    tag_name_unfixed=$(echo $tag | tr " " "\t" | cut -f 2 | cut -d / -f 3)

    # hack for broken tag names where they are like 1.2.0^{} instead of just 1.2.0
    tag_name="${tag_name_unfixed%%^*}"

    tag_new_name="${sub_project}/${tag_name}"
    echo -e "INFO:     Copying tag ${tag_name_unfixed} to ${tag_new_name} for ref ${tag_ref}..."
    echo -e "INFO:     Copying tag ${tag_name_unfixed} to ${tag_new_name} for ref ${tag_ref}..." >>${LOG_FILE} 2>&1
    if ! git tag "${tag_new_name}" "${tag_ref}" >>${LOG_FILE} 2>&1 ; then
      echo -e "WARN:     Could not copy tag ${tag_name_unfixed} to ${tag_new_name} for ref ${tag_ref}"
      echo -e "WARN:     Could not copy tag ${tag_name_unfixed} to ${tag_new_name} for ref ${tag_ref}" >>${LOG_FILE} 2>&1
    fi
  done < <(git ls-remote --tags --refs ${sub_project})

  # Remove the remote to the old project
  echo -e "INFO:   Removing remote ${sub_project}..."
  echo -e "INFO:   Removing remote ${sub_project}..." >>${LOG_FILE} 2>&1
  git remote rm ${sub_project}

  echo
done


# Now merge all project master branches into new master
git checkout --quiet master
echo -e "INFO: Merging projects master branches into new repository..."
echo -e "INFO: Merging projects master branches into new repository..." >>${LOG_FILE} 2>&1
echo -e "===================================================="
echo -e "====================================================" >>${LOG_FILE} 2>&1
for url in $(cat ${REPO_URL_FILE}) ; do

  if [[ ${url:0:1} == '#' ]] ; then
    continue
  fi

  # extract the name of this project
  export sub_project=${url##*/}
  sub_project=${sub_project%*.git}

  echo -e "INFO:   Merging ${sub_project}..."
  echo -e "INFO:   Merging ${sub_project}..." >>${LOG_FILE} 2>&1
  if ! git merge --allow-unrelated-histories --no-commit "${sub_project}/master" >>${LOG_FILE} 2>&1 ; then
    failed "Failed to merge branch ${sub_project}/master into master"
  fi

  # And now see if we need to commit (maybe there was a merge)
  commit_merge "${sub_project}/master"

  echo
done


# Done
cd ${ROOT_DIR}
echo -e "INFO: Done."
echo -e "INFO: Done." >>${LOG_FILE} 2>&1
echo

exit 0

Sie können auch erhalten von http://paste.ubuntu.com/11732805

Sie zuerst eine Datei mit der URL zu jedem Repository erstellen, z.B .:

git@github.com:eitchnet/ch.eitchnet.parent.git
git@github.com:eitchnet/ch.eitchnet.utils.git
git@github.com:eitchnet/ch.eitchnet.privilege.git

Dann das Skript rufen Sie einen Namen des Projektes und den Pfad zum Skript geben:

./mergeGitRepositories.sh eitchnet_test eitchnet.lst

Das Skript selbst hat eine Menge Kommentare, die erklären sollte, was es tut.

Wenn Sie versuchen, einfach zwei Repositories zusammenkleben, Submodule und Unterstruktur verschmilzt ist das falsche Werkzeug zu benutzen, weil sie nicht alle der Datei Geschichte bewahren (wie die Menschen auf anderen Antworten festgestellt haben). Sehen Sie diese Antwort hier für den einfachen und richtigen Weg, dies zu tun.

Ähnlich wie @Smar verwendet aber Dateisystempfade, setzen in Primär- und Sekundär:

PRIMARY=~/Code/project1
SECONDARY=~/Code/project2
cd $PRIMARY
git remote add test $SECONDARY && git fetch test
git merge test/master

Dann manuell zusammenführen.

(angepasst von Beitrag von Anar Manafov )

Ich hatte ein ähnliches Problem, aber in meinem Fall hatten wir eine Version der Codebasis in Repo-A entwickelt, geklont dann, dass in eine neue Repo, Repo-B, für die neue Version des Produkts. einige Fehler in Repo-A nach dem Fixieren, mussten wir die Änderungen in Repo B. an FI am Ende dabei wie folgt vor:

  1. Hinzufügen eines Remote-B Repo, die mit A (git remote hinzufügen ...) Repo wies
  2. den aktuellen Zweig Pulling (wir waren nicht für Fehlerbehebungen mit Master) (Git-Pull-remoteForRepoA bugFixBranch)
  3. Pushing verschmilzt auf Github

Arbeitet ein Genuss :)

Wenn Sie drei zusammenführen möchten oder mehrere Projekte in einem einzigen verpflichten, die Schritte tun, wie in den anderen Antworten beschrieben (remote add -f, merge). Dann (soft) zurückgesetzt, den Index zu altem Kopf (wo keine merge geschah). Fügen Sie alle Dateien (git add -A) und begehen sie (Meldung „Merging Projekte A, B, C und D in ein Projekt). Dies ist nun die Commit-ID des Master.

Erstellen Sie jetzt .git/info/grafts mit folgendem Inhalt:

<commit-id of master> <list of commit ids of all parents>

Ausführen git filter-branch -- head^..head head^2..head head^3..head. Wenn Sie mehr als drei Zweige haben, fügen Sie einfach so viel head^n..head wie Sie Zweige haben. So aktualisieren Tags, hängen --tag-name-filter cat. Nicht immer, dass hinzufügen, weil dies eine Rewrite einiger Commits verursachen könnte. Weitere Einzelheiten finden Sie unter Manpage von filter-branch , wenn nach "Grafts".

Nun, Ihre letzten Commit haben die richtig Eltern verbunden.

Um ein A in B fusionieren:

1) Im Rahmen des Projekts A

git fast-export --all --date-order > /tmp/ProjectAExport

2) Im Rahmen des Projekts B

git checkout -b projectA
git fast-import --force < /tmp/ProjectAExport

In dieser Branche haben alle Operationen, die Sie brauchen, um sie zu tun und zu begehen.

C) und dann zurück zu dem Master und einem klassischen merge zwischen den beiden Zweigen:

git checkout master
git merge projectA

Merging 2 repos

git clone ssh://<project-repo> project1
cd project1
git remote add -f project2 project2
git merge --allow-unrelated-histories project2/master
git remote rm project2

delete the ref to avoid errors
git update-ref -d refs/remotes/project2/master

Diese Funktion klonen Remote-Repo in lokale Repo dir, nachdem alle Commits Verschmelzung wird gespeichert, wird git log die Original-Commits und richtige Wege zeigen sein:

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 verwenden:

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

Wenn eine wenig Änderungen, die Sie sogar Dateien / Verzeichnisse von fusionierten Repo in verschiedenen Wege, zum Beispiel bewegen können:

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
Paths ersetzt durch sed, so stellen Sie sicher, dass es in der richtigen Pfade bewegt nach dem Zusammenführen.
Der --allow-unrelated-histories Parameter existiert erst seit git> = 2.9.

ich fusionieren geringfügig manuell, was mich um zu vermeiden, können mit Merge Konflikte zu behandeln.

zuerst, in den Dateien aus dem anderen Projekt kopiert aber Sie wollen.

cp -R myotherproject newdirectory
git add newdirectory

nächster Zug in der Geschichte

git fetch path_or_url_to_other_repo

tell git in der Geschichte der letzten geholt, was zu fusionieren

echo 'FETCH_HEAD' > .git/MERGE_HEAD

jetzt begehen jedoch normalerweise würden Sie verpflichten

git commit

Bei Befehl ist die beste Lösung, die ich vorschlagen.

git subtree add --prefix=MY_PROJECT git://github.com/project/my_project.git master

Ich wollte ein kleines Projekt zu einem Unterverzeichnis eines größeren bewegen. Da mein kleines Projekt nicht viele Commits hatte, benutzte ich git format-patch --output-directory /path/to/patch-dir. Dann auf dem größeren Projekt, habe ich git am --directory=dir/in/project /path/to/patch-dir/*.

Das fühlt sich an Weg weniger beängstigend und Weise mehr sauberer als ein Filter-Zweig. Zugegeben, es kann nicht auf alle Fälle anwendbar sein.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top