web-dev-qa-db-ja.com

ファイル履歴を壊さずに2つのGitリポジトリをマージする

2つのGitリポジトリを新しい3番目のリポジトリにマージする必要があります。サブツリーのマージを使用してこれを行う方法の多くの説明を見つけました(たとえば JakubNarębskiの答え on 2つのGitリポジトリをマージするにはどうすればよいですか? )ただし、サブツリーマージをコミットすると、古いリポジトリのすべてのファイルが新しい追加ファイルとして記録されます。 git logを実行すると、古いリポジトリのコミット履歴が表示されますが、git log <file>を実行すると、そのファイルのコミットが1つだけ表示されます(サブツリーのマージ)。上記の答えに対するコメントから判断すると、この問題を見るのは私だけではありませんが、公開されている解決策は見つかりませんでした。

リポジトリをマージし、個々のファイル履歴をそのまま残す方法はありますか?

198
Eric Lee

外部の依存関係を管理するのではなく、2つのリポジトリを単に接着して、そのように見せることだけを試みている場合、答えははるかに簡単であることがわかります。古いリポジトリにリモートを追加し、それらを新しいマスターにマージし、ファイルとフォルダーをサブディレクトリに移動し、移動をコミットし、すべての追加リポジトリに対して繰り返す必要があります。サブモジュール、サブツリーのマージ、派手なリベースは、わずかに異なる問題を解決することを目的としており、私がやろうとしていたものには適していません。

次に、2つのリポジトリを接着するPowershellスクリプトの例を示します。

# Assume the current directory is where we want the new repository to be created
# Create the new repository
git init

# Before we do a merge, we have to have an initial commit, so we'll make a dummy commit
git commit --allow-empty -m "Initial dummy commit"

# Add a remote for and fetch the old repo
git remote add -f old_a <OldA repo URL>

# Merge the files from old_a/master into new/master
git merge old_a/master --allow-unrelated-histories

# Move the old_a repo files and folders into a subdirectory so they don't collide with the other repo coming later
mkdir old_a
dir -exclude old_a | %{git mv $_.Name old_a}

# Commit the move
git commit -m "Move old_a files into subdir"

# Do the same thing for old_b
git remote add -f old_b <OldB repo URL>
git merge old_b/master --allow-unrelated-histories
mkdir old_b
dir –exclude old_a,old_b | %{git mv $_.Name old_b}
git commit -m "Move old_b files into subdir"

もちろん、代わりにold_bをold_a(新しい結合リポジトリになります)にマージすることもできます。スクリプトを修正してください。

進行中の機能ブランチも持ち込む場合は、これを使用します。

# Bring over a feature branch from one of the old repos
git checkout -b feature-in-progress
git merge -s recursive -Xsubtree=old_a old_a/feature-in-progress

これはプロセスの唯一の非自明な部分です-これはサブツリーマージではなく、Gitにターゲットの名前を変更し、Gitがすべてを正しく整列させるのに役立つ通常の再帰マージへの引数です。

もう少し詳細な説明を書きました こちら

248
Eric Lee

履歴を書き換えない方法は次のとおりです。したがって、すべてのコミットIDは有効なままです。最終結果は、2番目のリポジトリのファイルがサブディレクトリに配置されることです。

  1. 2番目のリポジトリをリモートとして追加します。

    cd firstgitrepo/
    git remote add secondrepo username@servername:andsoon
    
  2. Secondrepoのコミットをすべてダウンロードしたことを確認してください。

    git fetch secondrepo
    
  3. 2番目のリポジトリのブランチからローカルブランチを作成します。

    git branch branchfromsecondrepo secondrepo/master
    
  4. すべてのファイルをサブディレクトリに移動します。

    git checkout branchfromsecondrepo
    mkdir subdir/
    git ls-tree -z --name-only HEAD | xargs -0 -I {} git mv {} subdir/
    git commit -m "Moved files to subdir/"
    
  5. 2番目のブランチを最初のリポジトリのmasterブランチにマージします。

    git checkout master
    git merge --allow-unrelated-histories branchfromsecondrepo
    

リポジトリには複数のルートコミットがありますが、問題は発生しません。

132
Flimm

使用して見てください

git rebase --root --preserve-merges --onto

人生の早い段階で2つの履歴をリンクします。

重複するパスがある場合は、それらを修正します

git filter-branch --index-filter

ログを使用するときは、「より困難なコピーを見つける」ようにしてください

git log -CC

そうすれば、パス内のファイルの動きを見つけることができます。

8
Adam Dymitruk

私は solution を@Flimmからこのようなgit aliasに変えました(私の~/.gitconfigに追加されました):

[alias]
 mergeRepo = "!mergeRepo() { \
  [ $# -ne 3 ] && echo \"Three parameters required, <remote URI> <new branch> <new dir>\" && exit 1; \
  git remote add newRepo $1; \
  git fetch newRepo; \
  git branch \"$2\" newRepo/master; \
  git checkout \"$2\"; \
  mkdir -vp \"${GIT_PREFIX}$3\"; \
  git ls-tree -z --name-only HEAD | xargs -0 -I {} git mv {} \"${GIT_PREFIX}$3\"/; \
  git commit -m \"Moved files to '${GIT_PREFIX}$3'\"; \
  git checkout master; git merge --allow-unrelated-histories --no-edit -s recursive -X no-renames \"$2\"; \
  git branch -D \"$2\"; git remote remove newRepo; \
}; \
mergeRepo"
5

この関数は、リモートリポジトリをローカルリポジトリディレクトリにクローンします。

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

利益!

3
Andrey Izman

数年が経過し、適切なベースのアップ投票ソリューションがありますが、私はそれを少し共有したいと思います。なぜなら、2つのリモートリポジトリを削除せずに新しいものにマージしたかったからです以前のリポジトリの履歴。

  1. Githubで新しいリポジトリを作成します。

    enter image description here

  2. 新しく作成したリポジトリをダウンロードし、古いリモートリポジトリを追加します。

    git clone https://github.com/alexbr9007/Test.git
    cd Test
    git remote add OldRepo https://github.com/alexbr9007/Django-React.git
    git remote -v
    
  3. 古いリポジトリからすべてのファイルを取得して、新しいブランチを作成します。

    git fetch OldRepo
    git branch -a
    

    enter image description here

  4. Masterブランチで、マージを実行して、古いリポジトリと新しく作成されたリポジトリを結合します。

    git merge remotes/OldRepo/master --allow-unrelated-histories
    

    enter image description here

  5. OldRepoから追加されたすべての新しく作成されたコンテンツを保存する新しいフォルダーを作成し、その新しいフォルダーにファイルを移動します。

  6. 最後に、結合されたリポジトリからファイルをアップロードし、GitHubからOldRepoを安全に削除できます。

これがリモートリポジトリのマージを扱うすべての人に役立つことを願っています。

2
abautista

手順に従って、1つのリポジトリを別のリポジトリに埋め込み、両方のgit履歴をマージして1つの単一のgit履歴を作成します。

  1. マージする両方のリポジトリを複製します。

git clone [email protected]:user/parent-repo.git

git clone [email protected]:user/child-repo.git

  1. 子リポジトリに移動

cd child-repo /

  1. 以下のコマンドを実行し、パスmy/new/subdir(3回の出現)を、子リポジトリを作成するディレクトリ構造に置き換えます。

git filter-branch --Prune-empty --tree-filter 'if [! -e my/new/subdir]; mkdir -p my/new/subdir git ls-tree --name-only $ GIT_COMMIT | xargs -I files mv files my/new/subdir fi '

  1. 親リポジトリに移動

cd ../parent-repo/

  1. 親リポジトリにリモートを追加し、子リポジトリへのパスを指します

git remote add child-remote ../child-repo/

  1. 子レポを取得する

git fetch child-remote

  1. 履歴をマージする

git merge --allow-unrelated-histories child-remote/master

親リポジトリでgitログをチェックすると、子リポジトリのコミットがマージされているはずです。また、コミットソースから示すタグを見ることができます。

以下の記事は、1つのリポジトリを別のリポジトリに埋め込み、両方のgit履歴をマージして1つの単一のgit履歴を作成するのに役立ちました。

http://ericlathrop.com/2014/01/combining-git-repositories/

お役に立てれば。ハッピーコーディング!

0
AnoopGoudar