programing

어떻게 두 Git 저장소를 병합합니까?

subpage 2023. 4. 24. 23:11
반응형

어떻게 두 Git 저장소를 병합합니까?

다음 시나리오를 고려합니다.

자체 Git repo에서 작은 실험 프로젝트 A를 개발했습니다.이제 성숙해져서, A는 독자적인 큰 저장소를 가지고 있는 큰 프로젝트 B에 참가하고 싶습니다.이제 A를 B의 서브디렉토리로 추가하겠습니다.

어떻게 하면 A를 B로 병합할 수 있을까요?이력을 잃지 않고?

「」를 Marge 하는 project-aproject-b:

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

출처: git merge 다른 저장소를 사용하시겠습니까?

이 방법은 제게 꽤 효과가 있었고, 더 짧고 훨씬 깨끗하다고 생각합니다.

project-a(를 서브디렉토리에 넣을 수 있습니다).filter-branch권장되지 않습니다).위의 명령어보다 먼저 다음 명령어를 실행합니다.

cd path/to/project-a
git filter-repo --to-subdirectory-filter project-a

2개의 큰 저장소를 통합하고 그 중 하나를 서브디렉토리에 넣는 예:https://gist.github.com/x-yuri/9890ab1079cf4357d6f269d073fd9731

주의:--allow-unrelated-historiesgit > = 2.9 이후에만 파라미터가 존재합니다.Git - git merge Documentation / --allow-unrelated-histories를 참조하십시오.

업데이트: 추가됨--tags@jstadler @jstadler @jstadler @jstadler입니다.

다음으로 생각할 수 있는 두 가지 해결책을 제시하겠습니다.

서브 모듈

저장소 A를 더 큰 프로젝트 B의 다른 디렉터리에 복사하거나 저장소 A를 프로젝트 B의 하위 디렉터리에 복제하십시오.그런 다음 git 하위 모듈을 사용하여 이 저장소를 저장소 B의 하위 모듈로 만듭니다.

이 솔루션은 저장소 A에서 개발이 진행되는 느슨하게 연결된 저장소에 적합한 솔루션이며, 개발의 대부분은 A에서 별도의 독립형 개발입니다.Submodule Support 및 Git Submodule을 참조하십시오.Git Wiki의 튜토리얼 페이지.

서브트리 머지

하위 트리 병합 전략을 사용하여 리포지토리 A를 프로젝트 B의 하위 디렉터리로 병합할 수 있습니다.이는 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

(션)))--allow-unrelated-historiesGit > = 2.9.0입니다.)

또는 apenwarr(Avery Pennarun)의 Git 서브트리 툴(Git Hub의 저장소)을 사용할 수 있습니다.예를 들어, Git 서브모듈의 새로운 대안: git subtree를 블로그 투고하고 있습니다.


당신의 경우(A는 더 큰 프로젝트 B의 일부가 되는 것)에는 서브트리 머지를 사용하는 것이 올바른 해결책이라고 생각합니다.

다른 저장소의 단일 분기는 해당 이력을 유지하는 하위 디렉토리에 쉽게 배치할 수 있습니다.예를 들어 다음과 같습니다.

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

이것은 Rails 마스터 브랜치의 모든 파일이 "레일" 디렉토리에 추가되는 단일 커밋으로 나타납니다.그러나 커밋의 제목에는 오래된 이력 트리에 대한 참조가 포함되어 있습니다.

에서 "rails<rev>

서 ★★★★★<rev>해시입니다.당신은 여전히 역사를 볼 수 있고, 몇 가지 변화를 탓할 수 있습니다.

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

디렉토리 프리픽스는, 실제로는 낡은 브랜치이기 때문에, 이 브랜치에서는 확인할 수 있습니다.이것은 일반적인 파일 이동 커밋과 같이 취급해야 합니다.이 커밋에 도달하면 추가 점프가 필요합니다.

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

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

이 작업을 수동으로 수행하거나 다른 답변에 설명된 대로 이력을 다시 쓰는 등 더 복잡한 솔루션이 있습니다.

git-subtree 명령어는 공식 git-contrib의 일부이며, 일부 패킷 매니저는 기본적으로 이를 설치합니다(OS X Homebrew).하지만 git 외에 직접 설치해야 할 수도 있습니다.

프로젝트를 별도로 유지 관리하려는 경우 하위 모듈 방식이 좋습니다.그러나 두 프로젝트를 동일한 리포지토리로 병합하려면 더 많은 작업을 수행해야 합니다.

는 ''를 사용하는 입니다.git filter-branch두 번째 저장소에 있는 모든 파일의 이름을 최종 서브디렉토리에 재기입합니다. ★★★★★★★★★★★★★★★★★★★foo.c,bar.html , , , , , ,가 있을 것입니다.projb/foo.c ★★★★★★★★★★★★★★★★★」projb/bar.html.

그러면 다음과 같은 작업을 수행할 수 있습니다.

git remote add projb [wherever]
git pull projb

git pull git fetch에 '아예'가 붙습니다.git merge .이 하지 않아야 합니다.projb/디렉토리로 이동합니다.

, 비슷한 을 알 수 .gitkgitJunio C Hamano는 여기에 http://www.mail-archive.com/git@vger.kernel.org/msg03395.html에 대해 쓰고 있습니다.

git-subtree좋긴 한데 아마 당신이 원하는 건 아닐 거예요.

를 들어, 「」의 ,projectA B 로이며, B 로부터 작성되는 디렉토리입니다.git subtree ,

git log projectA

에 merge라는1개의 커밋만을 나타냅니다.병합된 프로젝트의 커밋은 다른 경로에 대한 것이므로 표시되지 않습니다.

Greg Hewgill의 답변은 가장 가까운 것이지만, 실제로 경로를 다시 쓰는 방법은 나와 있지 않습니다.


해결책은 놀라울 정도로 간단합니다.

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

참고: 그러면 기록이 다시 작성됩니다. 먼저 A의 백업을 만들 수 있습니다.

주의:파일명 또는 경로에 ASCII 이외의 문자(또는 흰색 문자)를 사용하는 경우 sed 명령어 내의 대체 스크립트를 변경해야 합니다.이 경우 "ls-files -s"에 의해 생성된 레코드 내의 파일 위치는 따옴표로 시작합니다.

(2) 다음으로 B에서 실행한다.

git pull path/to/A

Voila가 !projectAB 렉 、 B 렉 、 B 렉 、 B 、 B b<고객명>을git log projectAA ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★」


두 의 서브디렉토리를.「 」 「 」 「 」 「 」projectA ★★★★★★★★★★★★★★★★★」projectB그 b b, 에에11111 (1)11111111111111 b11 。

두 저장소에 동일한 종류의 파일이 있는 경우(다른 프로젝트에 대한 두 개의 Rails 저장소 등), 보조 저장소의 데이터를 현재 저장소로 가져올 수 있습니다.

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

현재 저장소로 병합합니다.

git merge --allow-unrelated-histories branch_name

Git 2.9를 합니다.--allow-unrelated-histories.

그 후 충돌이 발생할 수 있습니다. '어울리다'로 할 수 요.git mergetoolkdiff3키보드만으로 사용할 수 있기 때문에 코드를 읽을 때 5개의 충돌 파일이 몇 분밖에 걸리지 않습니다.

병합을 완료해야 합니다.

git commit

Marge를 사용할 때 이력을 계속 잃었기 때문에 결국 rebase를 사용하게 되었습니다.내 경우 두 저장소가 서로 달라서 매번 Marge를 할 수 없기 때문입니다.

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

=> 충돌을 해결한 다음 필요한 횟수만큼 계속합니다.

git rebase --continue

이를 통해 하나의 프로젝트는 projA의 모든 커밋과 projB의 커밋으로 이어집니다.

my-plugin 및 " " " 입니다.main-project는 '아예'라고 .my-plugin항상 개발되어 왔습니다.plugins「」 main-project.

제가 역사, 역사, 역사, 역사 등을 다시 .my-plugin모든 개발이 이루어진 것처럼 보이는 저장소plugins/my-plugin'아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아,아my-pluginmain-project★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★ 없음plugins/my-plugin 「」에 합니다.main-project저장소입니다. 이것은 단순한 무손실 결합입니다.결과 저장소에는 두 원래 프로젝트의 모든 이력이 포함되어 있으며 두 개의 루트가 있습니다.

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

롱 버전

'을 보세요.my-plugin이 저장소의 이력을 다시 쓸 예정이기 때문입니다.

이번에는 '으로 이동해 .my-plugin본점, 본점)을해 보세요.master 을 하다, 신, 신, 당, 당, 니, 니, 니, 니, 니, of, of, of, of, of, of, ofmy-plugin ★★★★★★★★★★★★★★★★★」plugins★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★

$ 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

이제 설명을 해드릴게요. git filter-branch --tree-filter (...) HEAD runs runs runs the the the the runs 。(...)에서 도달 HEAD이것은 각 커밋에 대해 저장된 데이터에 직접 적용되므로 "작업 디렉토리", "인덱스", "스테이징" 등의 개념에 대해 걱정할 필요가 없습니다.

「 」를하고 있는 경우filter-branch는 일부 ..git 및 할 때filter-branch문제에 은, 「이러다」라고 하는 「이러다」라고 하는 「라고 하는 것은, 「이러한 문제는 없습니다」라고 하는 것입니다.-f " " " option to " " option to " )filter-branch.

명령어는 운이 bash가 하고할 수 대신 는 가가하 to to to to를 사용한다.zsh -c 만들다zsh명령을 실행합니다.먼저 설정해 보겠습니다.extended_globoption(옵션)을 .^(...)mv 및 " " " 명령어"를 참조해 주세요.glob_dots 닷 파일: ( option ( dot me option option(((((((((((((((((((((((((()을 선택할 수 ..gitignore ^(...)를 참조해 주세요.

다음, 제가 , 쓰다, 쓰다, 쓰다.mkdir -p 다 명령어plugins ★★★★★★★★★★★★★★★★★」plugins/my-plugin★★★★★★★★★★★★★★★★★★.

finally막로, 는는 를 사용합니다.zshglob negative glob ""^(.git|plugins)합니다..git 새로 된 """ " " " " " " 。my-plugin).git여기서 필요하지 않을 수도 있지만 디렉토리를 그 자체로 이동하려고 하면 오류가 발생합니다.

첫 되어 있지 않기 에, 「」는 「」를 해 주세요.mv명령어가 초기 커밋에서 오류를 반환했습니다(이동할 수 있는 것이 없었기 때문입니다).저는 '', '아까', '아까', '아까'를 넣었습니다.|| true 해서git filter-branch중단되지 않습니다.

--all을 지정하면, 「알 수 있습니다」라고 표시됩니다.filter-branch저장소의 모든 브랜치 및 추가 브랜치 기록을 다시 작성합니다.-- 가 있다git 리스트의 filter-branch그 자체입니다.

그럼 에는 '찾아보세요'로 가보세요.main-project저장소를 선택하고 병합할 브랜치를 체크 아웃합니다.를 추가해 주세요.my-plugin변경된 를 "" ("리모트")main-project 라이선스:

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

커밋 이력에 관련 없는 2개의 트리가 생깁니다.이 트리는 다음을 사용하여 시각화할 수 있습니다.

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

병합하려면 다음을 사용합니다.

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

2.9.0 Git 이전 버전에서는 옵션이 존재하지 않습니다. 이러한 버전 중 하나를 사용하고 있는 경우는, 에러 메세지도 2.9.0 추가되었습니다.

병합 충돌이 없어야 합니다. '보다', '아까보다', '아까보다', '아까보다', '아까보다',filter-branch """가 .plugins/my-plugin「」의 main-project.

2개의 루트를 가진 저장소를 만들기 위해 어떤 해킹이 있었는지 궁금해하는 미래의 기여자를 위해 설명 커밋 메시지를 반드시 입력하십시오.

커밋을 커밋그래프를 할 수 그래프는 커밋을 수 .이 그래프는 2개의 루트 커밋을 가져야 합니다.git log명령어를 입력합니다.브랜치만이 Marge 되는 에 주의해 주세요.이것은 당신이 다른 중요한 일을 하고 있다면my-plugin"Marge"에 main-project 트리 .my-plugin이 병합을 완료할 때까지 리모트합니다.않은 , 「커밋」에.main-project할 필요가 (또한 리모트를 삭제하면 리모트트래킹브런치가 삭제되기 때문에 SHA에서 참조할 필요가 있습니다).

) 에서 것을 후, 「」에서 보관합니다.my-plugin 때, 을(를) 제거할 수 my-plugin다음 중 하나:

$ git remote remove my-plugin

하게 이 할 수 .my-plugin이치노 제, 천, , 추, 추, 추, 추, in, in, in, in, in, in, in, in, in, in, in, in, in, in, in, in, in, in, in, in,my-plugin저장소로 이동합니다.


X git --version 2.9.0 ★★★★★★★★★★★★★★★★★」zsh --version 5.2마일리지가 다를 수 있습니다.

참고 자료:

며칠째 같은 일을 하려고 하고 있는데, git 2.7.2를 사용하고 있습니다.하위 트리는 기록을 보존하지 않습니다.

이전 프로젝트를 다시 사용하지 않을 경우 이 방법을 사용할 수 있습니다.

B 지점부터 먼저 가서 지점에서 일하는 게 좋을 것 같아요.

분기하지 않는 절차는 다음과 같습니다.

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

서브디르 A에 있는 파일 중 하나를 로그에 기록하면 전체 이력을 얻을 수 있습니다.

git log --follow A/<file>

이 게시물이 도움이 되었습니다.

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

repo B의 분기 파일을 repo A서브트리에 넣고 이력을 보존하고 싶다면 계속 읽어주세요.(아래 예에서는 repo B의 마스터 브런치를 repo A의 마스터 브런치로 통합하고 싶다고 상정하고 있습니다.

repo A에서 먼저 다음을 수행하여 repo B를 사용할 수 있도록 합니다.

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

에 완전히 브랜치를 만듭니다.이 브랜치는 repo A라고 .new_b_root. 첫 되지만 repo B라는 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))"

★★★★--orphan체크 아웃 명령의 옵션은 A의 마스터 브랜치에서 파일을 체크아웃하지만 커밋은 생성하지 않습니다.다음으로는 모든 파일을 삭제하기 때문에 어떤 커밋도 선택할 수 있습니다. 후 않고( 「 」 「 」 「 」 「 」 「 」 ).-nB의 마스터 브랜치로부터 첫 번째 커밋을 체리픽합니다.(체리픽은 스트레이트 체크 아웃에서는 할 수 없는 원래의 커밋 메시지를 유지합니다.)그런 다음 Repo B의 모든 파일을 저장할 서브트리를 만듭니다.그런 다음 체리픽에서 소개된 모든 파일을 하위 트리로 이동해야 합니다.위의 예에서는 다음과 같은 명령어만 있습니다.README이동할 파일입니다.다음으로 B-repo 루트 커밋을 커밋함과 동시에 원래 커밋의 타임스탬프도 유지합니다.

이번에는것을 보겠습니다.B/master new_b_root을 새 지점이라고 b:

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

그럼 ㅇㅇ, ㅇㅇㅇ, ,, 제, 제, 제, now, now.b로 분기하다A/master:

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

뺄 수 요, 뺄 수 있어요.B"CHANGE: "CHANGE: "CHANGE: " 。

git remote remove B
git branch -D new_b_root b

마지막 그래프는 다음과 같은 구조를 가집니다.

여기에 이미지 설명 입력

Stack Over Flow 등에 대해 많은 정보를 수집하여 문제를 해결하는 스크립트를 작성했습니다.

주의: 각 저장소의 '개발' 분기만 고려하고 완전히 새로운 저장소의 개별 디렉토리에 병합합니다.

태그 및 기타 브랜치는 무시됩니다.원하는 브랜치가 아닐 수 있습니다.

이 스크립트는 기능 브랜치 및 태그도 처리합니다.이러한 브랜치 및 태그의 이름을 새 프로젝트에서 변경하여 어디서 왔는지 알 수 있습니다.

#!/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

http://paste.ubuntu.com/11732805에서도 구할 수 있습니다.

먼저 각 저장소의 URL을 사용하여 파일을 만듭니다.다음은 예를 제시하겠습니다.

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

그런 다음 프로젝트 이름과 스크립트의 경로를 제공하는 스크립트를 호출합니다.

./mergeGitRepositories.sh eitchnet_test eitchnet.lst

대본 자체에는 어떤 역할을 하는지 설명할 수 있는 많은 댓글이 있습니다.

그 일이 있은 지 한참 지난 건 알지만, 여기서 찾은 다른 답변이 마음에 들지 않아서 이렇게 썼어요.

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

2개의 리포지토리를 단순히 접착하려는 경우 하위 모듈과 하위 트리 병합은 모든 파일 기록을 보존하지 않기 때문에 사용하기에 적합하지 않습니다(다른 답변에서 언급한 바와 같이).간단하고 올바른 방법에 대해서는, 여기를 참조해 주세요.

저도 같은 문제가 있었습니다만, 제 경우, repo A에서 코드 베이스의 1버전을 개발하고, 그것을 새로운 버전의 repo B로 복제했습니다.repo A의 버그를 수정한 후 repo B로 변경을 FI로 해야 했습니다.다음과 같은 작업을 수행하게 되었습니다.

  1. repo A를 가리킨 repo B에 리모트 추가(git remote add...)
  2. 현재 브랜치 풀(버그 수정에 마스터를 사용하지 않았습니다)(git pull remoteForRepoA bugFixBranch)
  3. 병합을 github에 푸시합니다.

효과가 있었다:)

2개의 저장소를 Marge하고 있습니다.

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

@Smar와 비슷하지만 PRIMAY 및 Secondary로 설정된 파일 시스템 경로를 사용합니다.

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

그런 다음 수동으로 병합합니다.

(Anar Manafov가 직위에서 채택)

3개 이상의 프로젝트를 1개의 커밋으로 Marge 하는 경우는, 다른 응답에 기재되어 있는 순서를 실행합니다( ).remote add -f,merge그런 다음 (소프트) 인덱스를 이전 헤드로 리셋합니다(머지는 발생하지 않았습니다).파일 추가( 「」 「」 「 」git add -A('프로젝트 A, B, C, D를 하나의 프로젝트로 병합' 메시지)를 커밋합니다.commit-id를 지정하다

이제 제, 제를 만듭니다..git/info/grafts하다

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

실행합니다.git filter-branch -- head^..head head^2..head head^3..head head^n..head뭇가나하려면 , 「」를 합니다.--tag-name-filter cat. 반드시 추가하지는 마십시오.이것에 의해, 커밋이 고쳐지는 일이 있습니다.자세한 내용은 filter-branch의 man 페이지를 참조하고 "grafts"를 검색하십시오.

자, 마지막 약속에는 올바른 부모가 관련되어 있습니다.

A를 B 내에서 Marge하려면:

1) 프로젝트 A에서

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

2) 프로젝트 B에서

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

이 브랜치에서는 필요한 모든 작업을 수행하고 커밋합니다.

C) 다음으로 마스터로 돌아가 두 브랜치 간의 기존 결합을 수행합니다.

git checkout master
git merge projectA

스크립트 베이스의 솔루션에 대해서는, https://github.com/hraban/tomono 를 참조해 주세요.

나는 저자가 아니지만 그것을 사용했고 그것은 그 일을 해낸다.

한 가지 긍정적인 측면은 모든 지점과 모든 이력을 최종 보고서에 포함시키는 것입니다.내 저장소(저장소에 중복 폴더가 없음 - 실제로 tfs2git 마이그레이션에서 나온 폴더)의 경우 충돌은 없었고 모든 것이 자동으로 실행되었습니다.

주로 모노레오를 작성하기 위해 사용됩니다(이름 참조).

Windows 사용자의 경우: git bash는 .sh 파일을 실행할 수 있습니다.표준 git 설치가 포함되어 있습니다.

Google이 보다 복잡한 사용 사례에 사용하는 Copybara 툴이 있습니다. https://github.com/google/copybara

프로젝트를 약간 수동으로 병합하기 때문에 병합 충돌을 피할 수 있습니다.

먼저 다른 프로젝트의 파일을 원하는 대로 복사합니다.

cp -R myotherproject newdirectory
git add newdirectory

역사의 다음 승부수

git fetch path_or_url_to_other_repo

git에게 마지막으로 가져온 것의 역사에 병합하도록 지시하다

echo 'FETCH_HEAD' > .git/MERGE_HEAD

이제 일반적인 커밋 방식으로 커밋합니다.

git commit

오늘 다음과 같이 해결해야 했습니다.프로젝트 A는 비트버킷으로, 프로젝트 B는 코드 커밋으로 되어 있었습니다.두 프로젝트 모두 같은 프로젝트이지만 A에서B로 변경을 병합해야 했습니다.(프로젝트 B와 같은 이름의 브랜치를 프로젝트A에 작성하는 것이 요령입니다).

  • git 체크 아웃프로젝트 A
  • git 원격 제거 오리진
  • git remote add origin 프로젝트 B
  • git체크아웃브런치
  • git add *
  • git - m "우리는 코드를 이동했습니다"
  • git push

dir로 복제하여 모든 후 repo dir를 git log원래의 커밋과 적절한 패스가 표시됩니다.

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

사용방법:

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

약간의 변경을 가하면 다음과 같이 병합된 repo의 파일/dir를 다른 경로로 이동할 수도 있습니다.

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"


는 사다리를 통해 됩니다.sed머지 후 올바른 경로로 이동했는지 확인합니다.
--allow-unrelated-historiesgit > = 2.9 이후에만 파라미터가 존재합니다.

주어진 명령어가 최선의 해결책입니다.

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

모든 에 덧붙여, 「아주머니」를 사용해 주세요.remote add->fetch->mergestrategy: 않은 수 ), 할 수 .

git fetch --no-tags other_repo
git fetch --no-tags other_repo 'refs/tags/*:refs/tags/other_repo/*'

번째 를 정상적으로 하고 두 는 일반 합니다.git help fetch내용은 를 참조해 주세요.또, 이러한 모든 합니다.X로로 합니다.other_repo/X합니다.refspec 기능을 사용합니다.

참조(브런치, 태그)는 git 내의 파일일 뿐이며 네임스페이스에 디렉토리를 사용할 수 있습니다.첫되며, 다른 1개의 에서는 "2"가 .other_repo/

조작 후에는 다른 쪽 리모콘을 분리하는 것이 가장 좋기 때문에 실수로 태그를 가져와 혼란을 일으키는 일이 없습니다.

나는 작은 프로젝트를 큰 프로젝트의 하위 디렉토리로 옮기고 싶었다.저의 작은 프로젝트에는 커밋이 많지 않았기 때문에git format-patch --output-directory /path/to/patch-dir 큰 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★git am --directory=dir/in/project /path/to/patch-dir/*.

필터 브런치보다 훨씬 덜 무섭고 깨끗해.단, 모든 경우에 해당되지 않을 수 있습니다.

언급URL : https://stackoverflow.com/questions/1425892/how-do-you-merge-two-git-repositories

반응형