Jak połączyć dwa repozytoria Git?

Rozważ następujący scenariusz:

Opracowałem mały eksperymentalny projekt A we własnym repo Git. Teraz dojrzał i chciałbym, aby A było częścią większego projektu B, który ma własne duże repozytorium. Chciałbym teraz dodać a jako podkatalog B.

Jak połączyć A Z B, nie tracąc historii po żadnej stronie?

Author: static_rtti, 2009-09-15

21 answers

Pojedyncza gałąź innego repozytorium może być łatwo umieszczona w podkatalogu zachowującym jego historię. Na przykład:

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

Pojawi się jako pojedynczy commit, w którym wszystkie pliki gałęzi rails master zostaną dodane do katalogu" rails". Jednak tytuł commita zawiera odniesienie do starego drzewa historii:

Dodaj 'rails /' z commit <rev>

Gdzie <rev> jest skrótem commit SHA-1. Można jeszcze zobaczyć historię, winić niektórych zmiany.

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

Zauważ, że nie możesz zobaczyć prefiksu katalogu stąd, ponieważ jest to prawdziwa stara gałąź pozostawiona nienaruszona. Powinieneś traktować to jak zwykły commit do przenoszenia plików: będziesz potrzebował dodatkowego skoku, gdy do niego dotrzesz.

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

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

Istnieją bardziej złożone rozwiązania, takie jak wykonywanie tego ręcznie lub przepisywanie historii, jak opisano w innych odpowiedziach.

Polecenie Git-subtree jest częścią oficjalnego git-contrib, niektóre Menedżery pakietów instalują je domyślnie (OS X Homebrew). Ale ty być może będziesz musiał zainstalować go samodzielnie oprócz git.

 331
Author: Simon Perepelitsa,
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-05-07 10:44:36

Jeśli chcesz połączyć project-a w project-b:

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

Wzięte z: git merge different repozytoria?

Ta metoda działa całkiem dobrze dla mnie, jest krótsza i moim zdaniem dużo czystsza.

Uwaga: parametr --allow-unrelated-histories istnieje tylko od git >= 2.9. Zobacz Git-Git merge Documentation / --allow-unknown-histories

 1350
Author: Andresch Serj,
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-03-19 13:34:12

Oto dwa możliwe rozwiązania:

Submodule

Albo skopiuj repozytorium A do oddzielnego katalogu w większym projekcie B, albo (być może lepiej) Sklonuj repozytorium A do podkatalogu w projekcie B. Następnie użyj Git submodule , aby to repozytorium stało się podmodel: z repozytorium B.

Jest to dobre rozwiązanie dla luźno powiązanych repozytoriów, w których rozwój w repozytorium A jest kontynuowany, a większa część rozwoju jest oddzielnym samodzielny rozwój w A. Zobacz także Strony SubmoduleSupport i GitSubmoduleTutorial na Git Wiki.

Subtree merge

Możesz scalić repozytorium A do podkatalogu projektu B używając scalanie podzbiorów strategia. Jest to opisane w Subtree scalanie i Ty Autor: 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

(Opcja --allow-unrelated-histories jest potrzebna dla Git >= 2.9.0.)

Lub możesz użyć git subtree tool (repozytorium na GitHub ) przez apenwarr (Avery Pennarun), ogłosił na przykład w swoim poście na blogu nową alternatywę dla podmodułów Git: Git subtree.


myślę, że w Twoim przypadku (A ma być częścią większego projektu B) właściwym rozwiązaniem byłoby użycie subtree merge.

 585
Author: Jakub Narębski,
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-08-03 12:17:50

Podejście podmodułowe jest dobre, jeśli chcesz utrzymać projekt osobno. Jeśli jednak naprawdę chcesz połączyć oba projekty w to samo repozytorium, masz nieco więcej pracy.

Pierwszą rzeczą byłoby użycie git filter-branch do przepisania nazw wszystkiego w drugim repozytorium, aby znaleźć się w podkatalogu, w którym mają się znaleźć. Więc zamiast foo.c, bar.html, mielibyście projb/foo.c i projb/bar.html.

Wtedy powinieneś być w stanie zrobić coś takiego jak po:

git remote add projb [wherever]
git pull projb

git pull wykona git fetch, a następnie git merge. Nie powinno być konfliktów, jeśli repozytorium, do którego ściągasz, nie ma jeszcze katalogu projb/.

Dalsze poszukiwania wskazują, że coś podobnego zostało zrobione, aby połączyć gitk w git. Junio C Hamano pisze o tym tutaj: http://www.mail-archive.com/[email protected]/msg03395.html

 189
Author: Greg Hewgill,
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-09-15 08:38:49

git-subtree jest miły, ale prawdopodobnie nie jest tym, którego chcesz.

Na przykład, jeśli projectA jest katalogiem utworzonym w B, po git subtree,

git log projectA

Wyświetla tylko jeden commit: połączenie. Commity z scalonego projektu są dla różnych ścieżek, więc nie pojawiają się.

Odpowiedź Grega Hewgilla jest najbliższa, chociaż nie mówi, jak przepisać ścieżki.


Rozwiązanie jest zaskakująco proste.

(1) w 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

uwaga: to przepisuje historię, więc jeśli zamierzasz nadal używać tego repo A, możesz najpierw sklonować (skopiować) jego kopię.

(2) Następnie w B Uruchom

git pull path/to/A
Voila! Masz katalog projectA W B. jeśli uruchomisz git log projectA, zobaczysz wszystkie commity z A.

W moim przypadku, chciałem dwa podkatalogi, projectA i projectB. W takim przypadku zrobiłem krok (1) do B, jak również.

 63
Author: Paul Draper,
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-08-01 15:06:06

Jeśli oba repozytoria mają ten sam rodzaj plików (jak dwa repozytoria Rails dla różnych projektów), możesz pobrać dane z repozytorium wtórnego do bieżącego repozytorium:

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

A następnie połącz go z bieżącym repozytorium:

git merge --allow-unrelated-histories branch_name

Jeśli Twoja wersja Git jest mniejsza niż 2.9, usuń --allow-unrelated-histories.

Po tym mogą wystąpić konflikty. Można je rozwiązać na przykład za pomocą git mergetool. kdiff3 może być używany wyłącznie z klawiaturą, więc 5 plików konfliktowych zajmuje podczas czytania kodu tylko kilka minut.

Pamiętaj, aby zakończyć połączenie:

git commit
 41
Author: Smar,
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-01-19 13:05:55

Traciłem historię podczas merge, więc skończyło się na rebase, ponieważ w moim przypadku oba repozytoria są na tyle różne, że nie kończą się scalaniem przy każdym commicie:

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

= > rozwiązuj konflikty, a następnie kontynuuj, tyle razy, ile potrzebujesz...

git rebase --continue

To prowadzi do tego, że jeden projekt ma wszystkie commity z projA, a następnie commity z projB

 22
Author: Calahad,
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-02-27 03:09:17

W moim przypadku miałem repozytorium my-plugin i repozytorium main-project i chciałem udawać, że my-plugin zawsze było rozwijane w plugins podkatalogu main-project.

W zasadzie przepisałem historię repozytorium my-plugin tak, aby wyglądało na to, że cały rozwój miał miejsce w podkatalogu plugins/my-plugin. Następnie dodałem historię rozwoju my-plugin do historii main-project i połączyłem oba drzewa razem. Ponieważ w repozytorium main-project nie było katalogu plugins/my-plugin, to było banalne połączenie bez konfliktów. Powstałe repozytorium zawierało całą historię z obu oryginalnych projektów i miało dwa korzenie.

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|my-plugin) 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

Wersja długa

Najpierw Utwórz kopię repozytorium my-plugin, ponieważ będziemy przepisywać historię tego repozytorium.

Teraz przejdź do katalogu głównego repozytorium my-plugin, Sprawdź swoją główną gałąź (prawdopodobnie master) i uruchom następujące polecenie. Oczywiście należy zastąpić my-plugin i Nieważne, jak się nazywacie.

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

Teraz dla wyjaśnienia. git filter-branch --tree-filter (...) HEAD uruchamia komendę (...) na każdym commicie, który jest osiągalny z HEAD. Zauważ, że działa to bezpośrednio na danych przechowywanych dla każdego zatwierdzenia, więc nie musimy się martwić pojęciami "working directory", "index", "staging" itd.

Jeśli uruchomisz filter-branch polecenie, które się nie powiedzie, pozostawi kilka plików w katalogu .git i następnym razem, gdy spróbujesz filter-branch, będzie narzekać na to, chyba że podasz opcję -f do filter-branch.

Jeśli chodzi o rzeczywiste polecenie, nie miałem zbyt wiele szczęścia, aby bash zrobić to, co chciałem, więc zamiast tego używam zsh -c, aby zsh wykonać polecenie. Najpierw ustawiam opcję extended_glob, która włącza składnię ^(...) w poleceniu mv, a także opcję glob_dots, która pozwala mi wybrać pliki dotfiles (takie jak .gitignore) za pomocą glob (^(...)).

Następnie używam polecenia {[39] } do tworzenia zarówno plugins jak i plugins/my-plugin w tym samym czas.

Na koniec używam funkcji zsh "negative glob" ^(.git|my-plugin), aby dopasować wszystkie pliki w katalogu głównym repozytorium z wyjątkiem .git i nowo utworzonego folderu my-plugin. (Wyłączenie .git może nie być tutaj konieczne, ale próba przeniesienia katalogu do siebie jest błędem.)

W moim repozytorium początkowy commit nie zawierał żadnych plików, więc polecenie mv zwróciło błąd przy początkowym commicie (ponieważ nic nie było dostępne do przeniesienia). Dlatego dodałem || true żeby nie przerywać.

Opcja --all mówi filter-branch, aby przepisać historię dla wszystkich gałęzi w repozytorium, a dodatkowa -- jest konieczna, aby powiedzieć git, aby zinterpretować ją jako część listy opcji dla gałęzi do przepisania, zamiast jako samą opcję filter-branch.

Teraz przejdź do swojego repozytorium main-project i sprawdź, z jaką gałęzią chcesz się połączyć. Dodaj lokalną kopię repozytorium my-plugin (ze zmodyfikowaną historią) jako remote of main-project with:

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

Będziesz miał teraz dwa niepowiązane ze sobą drzewa w historii zmian, które możesz ładnie wizualizować używając:

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

Aby je połączyć, użyj:

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

zauważ, że w Git pre-2.9.0 opcja --allow-unrelated-histories nie istnieje. Jeśli używasz jednej z tych wersji, po prostu pomiń opcję: komunikat o błędzie, który zapobiega --allow-unrelated-histories, został dodany również w wersji 2.9.0.

Nie powinieneś mieć żadnych konfliktów scalania. Jeśli tak, to prawdopodobnie oznacza to, że albo polecenie filter-branch nie działało poprawnie, albo w main-project był już katalog plugins/my-plugin.

Upewnij się, że wprowadzasz objaśniający komunikat commit dla przyszłych współpracowników, którzy zastanawiają się, co się dzieje, aby utworzyć repozytorium z dwoma korzeniami.

Możesz wizualizować nowy wykres zmian, który powinien mieć dwa główne zmiany, używając powyższego polecenia git log. Zauważ, że tylko gałąź master zostanie połączona. Oznacza to, że jeśli masz ważne pracuj nad innymi gałęziami my-plugin, które chcesz połączyć z drzewem main-project, powinieneś powstrzymać się od usuwania my-plugin, dopóki nie wykonasz tych połączeń. Jeśli tego nie zrobisz, to commity z tych gałęzi nadal będą w repozytorium main-project, ale niektóre z nich będą nieosiągalne i podatne na ewentualne usuwanie śmieci. (Ponadto będziesz musiał odwołać się do nich przez SHA, ponieważ usunięcie pilota usuwa jego gałęzie zdalnego śledzenia.)

Opcjonalnie, po połączeniu wszystkiego, co jeśli chcesz trzymać z my-plugin, możesz usunąć my-plugin zdalnego za pomocą:

$ git remote remove my-plugin

Możesz teraz bezpiecznie usunąć kopię repozytorium my-plugin, którego historię zmieniłeś. W moim przypadku dodałem również powiadomienie o dezaktualizacji do rzeczywistego repozytorium my-plugin po zakończeniu scalania i wypchnięciu.


Testowane na Mac OS X El Capitan z git --version 2.9.0 i zsh --version 5.2. Przebieg może / align = "left" /

Bibliografia:

 14
Author: Radon Rosborough,
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-05-23 11:55:02

Próbuję zrobić to samo od kilku dni, używam git 2.7.2. Subtree nie zachowuje historii.

Możesz użyć tej metody, jeśli nie będziesz używać starego projektu ponownie.

Sugerowałbym, abyś najpierw rozgałęził B i pracował w tej gałęzi.

Oto kroki bez rozgałęzień:

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

Jeśli teraz zalogujesz któryś z plików w subdir A, otrzymasz pełną historię

git log --follow A/<file>

To był post, który pomógł mi zrobić to:

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

 8
Author: Rian,
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-03-10 12:51:08

Wiem, że to długo po fakcie, ale nie byłem zadowolony z innych odpowiedzi, które tu znalazłem, więc napisałem to:

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
 6
Author: jettero,
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
2012-05-07 13:38:36

Zebrałem tu wiele informacji na temat Stack OverFlow, itp. i udało mi się stworzyć skrypt, który rozwiązuje problem dla mnie.

Zastrzeżenie jest takie, że bierze pod uwagę tylko gałąź 'develop' każdego repozytorium i łączy je w osobny katalog w zupełnie nowym repozytorium.

Tagi i inne gałęzie są ignorowane - to może nie być to, czego chcesz.

Skrypt obsługuje nawet gałęzie funkcji i znaczniki-zmieniając ich nazwę w nowym projekcie tak wiesz, skąd pochodzą.

#!/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 containing the URLs to the repositories
##   which are to be merged on separate lines.
##
## Author: Robert von Burg
##            [email protected]
##
## Version: 0.2.0
## Created: 2015-06-17
##
################################################################################
#

# Disallow using undefined variables
shopt -s -o nounset

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


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


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

function commit_merge() {
  current_branch="$(git symbolic-ref HEAD 2>/dev/null)"
  CHANGES=$(git status | grep "working directory clean")
  MERGING=$(git status | grep "merging")
  if [[ "$CHANGES" != "" ]] && [[ "$MERGING" == "" ]] ; then
    echo -e "INFO:   No commit required."
  else
    echo -e "INFO:   Committing ${sub_project}..."
    if ! git commit --quiet -m "[Project] Merged branch '$1' of ${sub_project}" ; then
      failed "Failed to commit merge of branch '$1' of ${sub_project} into ${current_branch}"
    fi
  fi
}


## Script variables
PROJECT_NAME="${1}"
PROJECT_PATH="${ROOT_DIR}/${PROJECT_NAME}"
REPO_FILE="${2}"
REPO_URL_FILE="${ROOT_DIR}/${REPO_FILE}"


# 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: Creating new git repository ${PROJECT_NAME}..."
echo -e "===================================================="
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 th branches of this project
echo -e "INFO: Merging projects into new repository..."
echo -e "===================================================="
for url in $(cat ${REPO_URL_FILE}) ; do

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

  echo -e "INFO: Project ${sub_project}"
  echo -e "----------------------------------------------------"

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

  # Add remote branches
  echo -e "INFO:   Creating local branches for ${sub_project}..."
  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}..."

    # Create and checkout new merge branch off of master
    git checkout --quiet -b "${sub_project}/${branch_name}" master
    git reset --hard --quiet
    git clean -d --force --quiet

    # Merge the project
    echo -e "INFO:   Merging ${sub_project}..."
    if ! git merge --quiet --no-commit "remotes/${sub_project}/${branch_name}" 2>/dev/null ; 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."
    else
      echo -e "INFO:   Moving files in branch ${branch_name} of ${sub_project} so we have a single directory..."
      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" 2>/dev/null ; then
    failed "sub_project ${sub_project} is missing master branch!"
  fi

  # Copy remote tags
  echo -e "INFO:   Copying tags for ${sub_project}..."
  while read tag ; do 
    tag_ref=$(echo $tag | tr " " "\t" | cut -f 1)
    tag_name=$(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%%^*}"

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

  # Remove the remote to the old project
  echo -e "INFO:   Removing remote ${sub_project}..."
  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 "===================================================="
for url in $(cat ${REPO_URL_FILE}) ; do

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

  echo -e "INFO:   Merging ${sub_project}..."
  if ! git merge --quiet --no-commit "${sub_project}/master" 2>/dev/null ; 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

exit 0

Możesz go również pobrać z http://paste.ubuntu.com/11732805

Najpierw Utwórz plik z adresem URL do każdego repozytorium, np.:

[email protected]:eitchnet/ch.eitchnet.parent.git
[email protected]:eitchnet/ch.eitchnet.utils.git
[email protected]:eitchnet/ch.eitchnet.privilege.git

Następnie wywołaj skrypt podając nazwę projektu i ścieżkę do skryptu:

./mergeGitRepositories.sh eitchnet_test eitchnet.lst

Sam skrypt ma wiele komentarzy, które powinny wyjaśniać, co robi.

 6
Author: eitch,
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-08-03 12:21:22

Jeśli próbujesz po prostu sklejać dwa repozytoria, podmoduły i podtree są niewłaściwym narzędziem do użycia, ponieważ nie zachowują całej historii plików (jak ludzie zauważyli w innych odpowiedziach). Zobacz tę odpowiedź tutaj aby uzyskać prosty i poprawny sposób.

 5
Author: Eric Lee,
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-05-23 12:34:51

Jeśli chcesz umieścić pliki z gałęzi w repo B w subtree repo A oraz również zachować historię, Czytaj dalej. (W poniższym przykładzie zakładam, że chcemy, aby gałąź master repo B została połączona z gałęzią master repo A.)

W repo A, najpierw wykonaj następujące czynności, aby udostępnić repo B:

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

Teraz tworzymy zupełnie nową gałąź (z tylko jednym commitem) w repo A, którą nazywamy new_b_root. Wynikający commit będzie miał pliki, które zostały popełnione w pierwszym zatwierdzeniu gałęzi master repo B, ale umieszczone w podkatalogu o nazwie path/to/b-files/.

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

Wyjaśnienie: opcja --orphan polecenia checkout sprawdza pliki z gałęzi master a, ale nie tworzy żadnych zmian. Mogliśmy wybrać dowolny commit, ponieważ Następnie i tak wyczyścimy wszystkie pliki. Następnie, bez zatwierdzania (-n), wybieramy pierwszy commit z gałęzi master B. (Cherry-pick zachowuje oryginalny komunikat commit czego prosta kasa nie robi.) Następnie tworzymy poddrzewo, w którym chcemy umieścić wszystkie pliki z repo B. Następnie musimy przenieść wszystkie pliki, które zostały wprowadzone w cherry-pick do poddrzewa. W powyższym przykładzie jest tylko plik README do przeniesienia. Następnie zatwierdzamy nasz główny commit B-repo i jednocześnie zachowujemy znacznik czasu oryginalnego commita.

Teraz utworzymy nową gałąź B/master na nowo utworzonym new_b_root. Nazywamy nowy oddział b:

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

Teraz łączymy naszą b gałąź w A/master:

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

Wreszcie, możesz usunąć B zdalne i tymczasowe gałęzie:

git remote remove B
git branch -D new_b_root b

Ostateczny wykres będzie miał taką strukturę:

Tutaj wpisz opis obrazka

 5
Author: Finn Haakansson,
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-05-05 16:44:28

Miałem podobne wyzwanie, ale w moim przypadku opracowaliśmy jedną wersję bazy kodu w repo A, a następnie sklonowaliśmy ją do nowego repo, repo B, dla nowej wersji produktu. Po naprawieniu niektórych błędów w repo A, musieliśmy wprowadzić zmiany do repo B. skończyło się na wykonaniu następujących czynności:

  1. dodanie pilota do repo B, który wskazywał na repo A (git remote add...)
  2. Ściąganie bieżącej gałęzi (nie używaliśmy master do poprawek błędów) (git pull remoteForRepoA bugFixBranch)
  3. Pushing merges to github

Zadziałało:)

 4
Author: David Lemphers,
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
2011-11-18 19:36:17

Podobne do @Smar, ale używa ścieżek systemu plików, ustawionych w PRIMARY i SECONDARY:

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

Następnie ręcznie łączysz.

(zaadaptowane z post by Anar Manafov )

 3
Author: Turadg,
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
2011-08-12 15:15:48

Jeśli chcesz połączyć trzy lub więcej projektów wpojedynczy commit, wykonaj kroki opisane w pozostałych odpowiedziach (remote add -f, merge). Następnie (soft) zresetuj indeks do starej głowicy(gdzie nie doszło do scalenia). Dodaj wszystkie pliki (git add -A) i zatwierdź je (komunikat " łączenie projektów A, B, C i D w jeden projekt). To jest teraz commit-id mistrza.

Teraz Utwórz {[4] } o następującej treści:

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

Run git filter-branch -- head^..head head^2..head head^3..head. Jeśli masz więcej niż trzy gałęzie, po prostu dodaj tyle head^n..head, co masz gałęzie. Aby zaktualizować znaczniki, dodaj --tag-name-filter cat. Nie zawsze dodawaj tego, ponieważ może to spowodować przepisanie niektórych commitów. Po szczegóły patrz strona podręcznika filter-branch , wyszukaj "grafts".

Teraz, twój ostatni commit ma odpowiednich rodziców powiązanych.

 3
Author: koppor,
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-05-09 23:22:01

Aby połączyć A a w B:

1) w projekcie a

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

2) w projekcie B

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

W tej gałęzi wykonaj wszystkie potrzebne operacje i zatwierdź je.

C) potem powrót do mistrza i klasyczne połączenie między dwoma gałęziami:

git checkout master
git merge projectA
 3
Author: user123568943685,
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-07-28 16:47:33

Łączenie 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
 3
Author: RahulMohan Kolakandy,
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-08-14 14:02:01

Podane polecenie jest najlepszym możliwym rozwiązaniem, jakie proponuję.

git subtree add --prefix=MY_PROJECT git://github.com/project/my_project.git master
 0
Author: Praveen Kumar,
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-11-24 05:47:32

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.

 0
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 08:47:56

Scalam projekty lekko ręcznie, co pozwala mi uniknąć konieczności radzenia sobie z konfliktami scalania.

Najpierw skopiuj pliki z innego projektu tak, jak chcesz.

cp -R myotherproject newdirectory
git add newdirectory

Następny ciąg w historii

git fetch path_or_url_to_other_repo

Powiedz gitowi, aby połączył się w historii ostatniej ściągniętej rzeczy

echo 'FETCH_HEAD' > .git/MERGE_HEAD

Teraz Zatwierdź, jakkolwiek normalnie byś zatwierdził

git commit
 0
Author: Collin Anderson,
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-02-15 17:38:07