我在一个名为XXX的文件夹中有一个Git存储库,还有一个名为YYY的Git存储库。

我想将XXX存储库作为名为ZZZ的子目录导入到YYY存储库中,并将所有XXX的更改历史添加到YYY中。

之前的文件夹结构:

├── XXX
│   ├── .git
│   └── (project files)
└── YYY
    ├── .git
    └── (project files)

文件夹结构后:

YYY
├── .git  <-- This now contains the change history from XXX
├──  ZZZ  <-- This was originally XXX
│    └── (project files)
└──  (project files)

这可以做到吗,或者我必须使用子模块?


当前回答

这个函数会将远程repo复制到本地repo目录,合并后所有的提交都会被保存,git日志会显示原始的提交和正确的路径:

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

如果做一点改变,你甚至可以移动文件/dirs合并repo到不同的路径,例如:

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-histories参数只存在于git >= 2.9之后。

其他回答

在我的例子中,我只想从另一个存储库(XXX)导入一些文件。子树对我来说太复杂了,其他的解都不行。这就是我所做的:

ALL_COMMITS=$(git log --reverse --pretty=format:%H -- ZZZ | tr '\n' ' ')

这将为您提供一个以空格分隔的列表,其中包括所有影响我想要导入的文件(ZZZ)的反向顺序(您可能还必须添加——follow以捕获重命名)。然后我进入目标存储库(YYY),将另一个存储库(XXX)添加为远程,从它中获取,最后:

git cherry-pick $ALL_COMMITS

这会将所有提交添加到分支,因此您将拥有所有具有历史记录的文件,并且可以对它们做任何您想做的事情,就像它们一直在这个存储库中一样。

这个函数会将远程repo复制到本地repo目录,合并后所有的提交都会被保存,git日志会显示原始的提交和正确的路径:

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

如果做一点改变,你甚至可以移动文件/dirs合并repo到不同的路径,例如:

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-histories参数只存在于git >= 2.9之后。

我认为你可以使用'git mv'和'git pull'来做到这一点。

我是一个公平的git新手-所以要小心你的主存储库-但我刚刚在一个临时目录中尝试了这一点,它似乎工作。

首先-重命名XXX的结构,以匹配你想要它在YYY中的样子:

cd XXX
mkdir tmp
git mv ZZZ tmp/ZZZ
git mv tmp ZZZ

现在XXX是这样的:

XXX
 |- ZZZ
     |- ZZZ

现在使用'git pull'来获取更改:

cd ../YYY
git pull ../XXX

现在YYY是这样的:

YYY
 |- ZZZ
     |- ZZZ
 |- (other folders that already were in YYY)

添加另一个答案,因为我认为这有点简单。将repo_dest拉入到repo_to_import中,然后推入——set-upstream url:repo_dest master。

这种方法对我来说很有效,我把几个较小的回购导入一个较大的回购中。

如何将repo1_to_import导入到repo_dest

# checkout your repo1_to_import if you don't have it already 
git clone url:repo1_to_import repo1_to_import
cd repo1_to_import

# now. pull all of repo_dest
git pull url:repo_dest
ls 
git status # shows Your branch is ahead of 'origin/master' by xx commits.
# now push to repo_dest
git push --set-upstream url:repo_dest master

# repeat for other repositories you want to import

重命名或移动文件和dirs到原始回购所需的位置,然后再进行导入。如。

cd repo1_to_import
mkdir topDir
git add topDir
git mv this that and the other topDir/
git commit -m"move things into topDir in preparation for exporting into new repo"
# now do the pull and push to import

以下链接中描述的方法启发了这个答案。我喜欢它,因为它看起来更简单。但是要小心!有龙!https://help.github.com/articles/importing-an-external-git-repository git push——镜像url:repo_dest将本地的回购历史和状态推送到远程(url:repo_dest)。但是它会删除旧的历史记录和远程状态。乐趣随之而来!: - e

简单的方法是使用git format-patch。

假设我们有两个git存储库foo和bar。

foo包含:

福.txt 。去

栏包含:

酒吧.txt 。去

我们希望以foo结束,其中包含bar历史记录和这些文件:

foo . txt。 git。 雷丁酒吧。. txt。

要做到这一点:

 1. create a temporary directory eg PATH_YOU_WANT/patch-bar
 2. go in bar directory
 3. git format-patch --root HEAD --no-stat -o PATH_YOU_WANT/patch-bar --src-prefix=a/foobar/ --dst-prefix=b/foobar/
 4. go in foo directory
 5. git am PATH_YOU_WANT/patch-bar/*

如果我们想重写所有从bar提交的消息,我们可以这样做,例如在Linux上:

git filter-branch --msg-filter 'sed "1s/^/\[bar\] /"' COMMIT_SHA1_OF_THE_PARENT_OF_THE_FIRST_BAR_COMMIT..HEAD

这将在每条提交消息的开头添加“[bar]”。