考虑以下场景:

我在自己的Git repo中开发了一个小型实验项目a。它现在已经成熟,我希望A成为更大的项目B的一部分,该项目有自己的大仓库。现在我想将A添加为B的子目录。

我如何将A合并为B,而不丢失任何方面的历史?


当前回答

我知道事情已经过去很久了,但我对我在这里找到的其他答案并不满意,所以我写了这样一篇文章:

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

其他回答

我稍微手动合并项目,这使我可以避免处理合并冲突。

首先,从另一个项目中复制文件,无论您需要什么。

cp -R myotherproject newdirectory
git add newdirectory

历史上的下一次拉力

git fetch path_or_url_to_other_repo

告诉git在上次获取的历史记录中合并

echo 'FETCH_HEAD' > .git/MERGE_HEAD

现在按您通常的方式提交

git commit

合并2个回购

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

另一个存储库的单个分支可以很容易地放在保留其历史的子目录下。例如:

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

这将显示为一次提交,其中Rails主分支的所有文件都添加到“Rails”目录中。然而,提交的标题包含对旧历史树的引用:

从提交添加“rails/”<rev>

其中<rev>是SHA-1提交哈希。你仍然可以看到历史,责怪一些变化。

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子树命令是git contrib的一部分,一些数据包管理器默认安装它(OS X Homebrew)。但除了git之外,您可能还需要自己安装它。

我知道事情已经过去很久了,但我对我在这里找到的其他答案并不满意,所以我写了这样一篇文章:

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

谷歌有一个Copybara工具,用于更复杂的用例-https://github.com/google/copybara