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?
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.
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
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.
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
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ż.
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
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
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:
- https://git-scm.com/docs/git-filter-branch
- https://unix.stackexchange.com/questions/6393/how-do-you-move-all-files-including-hidden-from-one-directory-to-another
- http://www.refining-linux.org/archives/37/ZSH-Gem-2-Extended-globbing-and-expansion/
- usuwanie pliku z Git repo nie powiodło się, nie można utworzyć nowej kopii zapasowej
- git, filter-branch na wszystkich gałęziach
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:
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
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.
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.
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ę:
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:
- dodanie pilota do repo B, który wskazywał na repo A (git remote add...)
- Ściąganie bieżącej gałęzi (nie używaliśmy master do poprawek błędów) (git pull remoteForRepoA bugFixBranch)
- Pushing merges to github
Zadziałało:)
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 )
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.
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
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
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
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.
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
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