git合并多个副本保留历史

91zkwejq  于 2023-01-11  发布在  Git
关注(0)|答案(1)|浏览(106)

我有一个项目,其中一些文件在不同的地方有多个副本。例如:

src/location1/foobar.h
src/location1/foobar.cpp
src/location2/foobar.h
src/location2/foobar.cpp

我正在将这些内容提取到自己的库中。因此,我希望以以下内容结束:

src/location3/foobar.h        combining multiple versions of foobar.h
src/location3/foobar.cpp      combining multiple versions of foobar.cpp

我已经通过了第一个障碍,删除所有不需要的文件使用:

git filter-repo --path-glob \*foobar\*

在这个过程中发现filter-branch最近被更高级的filter-repo取代了(值得重复,因为filter-branch仍然出现在这里的许多热门答案中)。
现在我想把这些副本合并成一个保存它们所有历史的副本,两个候选对象是mergemerge-file
merge-file要求标识每个文件的共同祖先,这可能是一个难题:

src/location3/foobar.h

这是提交历史中未知的地方。我们用git merge-base来寻找最佳的共同祖先。
我不清楚如何指定git merge-file的文件版本:

git mv src/location1/foobar.h src/newlocation/foobar.h
git commit
git merge-file src/newlocation/foobar.h src/location3/foobar@<commitid> src/location2/foobar.h
...
git merge-file src/newlocation/foobar.h src/location3/foobar@<commitid> src/location3/foobar.h

这是相当辛苦的,必须为每个文件重复。另一种方法是创建多个临时分支:

git checkout -b newlibbranch
git mv src/location1/foobar.h src/newlocation/foobar.h
git mv src/location1/foobar.cpp src/newlocation/foobar.cpp
git commit
git checkout oldversion
git checkout -b v2
git mv src/location2/foobar.h src/newlocation/foobar.h
git mv src/location2/foobar.cpp src/newlocation/foobar.cpp
git commit
git checkout newlibbranch
git merge --allow-unrelated-histories v2

这也是相当辛苦的。虽然它可能是脚本化的。还有一个实际问题,因为合并是"重命名/重命名"冲突,而不是实际文件的合并。这似乎可以通过添加--allow-unrelated-histors来解决
所以我的问题是:
关于任务:
1.有没有更好的方法?也许是一个合并工具,我不知道就像我不知道过滤器-回购
1.我认为多个合并分支的方式比git merge-file更好是正确的吗?
关于合并文件:
1.如何为git merge-file指定文件特定版本
1.是否有一个命令或脚本可以自动找到共同的祖先。例如:

git merge-file-wrapper location1 location2   -->

      base = `git merge-base location1 location2`
      git merge-file location1 $base location2

难道这是不存在的,因为有一些隐藏的陷阱?

uxhixvfz

uxhixvfz1#

我还没有找到任何自动化的工具来做这件事,所以在生态系统中可能有一个缺口。
在我的例子中,我有多个文件要移动,其中一些文件比其他文件有更多的副本,这增加了一些有趣的复杂性,但在重构以删除重复时并不罕见。
我最后做的是:

  • 编写一个脚本来创建一个新分支,其中每个变量都被移动到它的新位置。
  • 我的脚本首先标识要移动的文件。
  • 查找副本最多的文件并创建相应数量的分支。
  • 对于每个分支,它尝试将每个文件的一个副本移动到新位置
  • 然后我手动合并了每个分支。

这些合并中的大多数都是琐碎的事情,比如为每个子项目更改名称空间。
结果是一组文件,其中包含我想要的所有更改以及每个文件的所有更改历史。
为了让这一点更具体一些:

  • 步骤1:使用filter-repo创建一个只包含感兴趣的文件的项目

(note这应该在项目的新克隆上完成)

git filter-repo --path-glob \*ThingIWant1\* --path-glob \*AnotherThingIWant\* 
     git filter-repo --invert --path-glob \*ThingIDontWant\*
  • 第二步:创建分支
#!/bin/bash
    
    # find unique filenames
    MAXLOCS=0
    FILES=`find . -not -path '*/.*' -type f | grep -v makebranch | xargs -ifile basename file | sort -u`
    for FILE in $FILES; do
        echo FILE=$FILE
        # find number of locations for each filename
        NUMLOCS=`find . -not -path '*/.*' -name $FILE | wc -l`
        if [ $NUMLOCS -gt $MAXLOCS ]; then
        MAXLOCS=$NUMLOCS
        fi
    done
    echo "$MAXLOCS branches required"
    
    # for each branch
    #  move one location of each file to its final destination
    L=0
    while [ $L -lt $MAXLOCS ]; do
        git checkout develop
        git checkout -b ps$L
        for FILE in $FILES; do
        echo FILE=$FILE
        LOCS=( $(find . -not -path '*/.*' -name $FILE) )
        NUMLOCS=${#LOCS[@]}
        if [ $L -lt $NUMLOCS ]; then
            LOC=${LOCS[$L]}
            echo "mv $LOC"
            # Move source files to one place and test files to another
            # In my case we have src and test
            echo $LOC | grep -q /src/
            if [ $? ]; then
                mkdir -p FinalDestinationForSource
                git mv $LOC FinalDestinationForSource/$FILE
                if [ $? -ne 0 ];then
                   echo "BAD: git mv $LOC FinalDestinationForSource/$FILE"
                fi
            else
                mkdir -p FinalDestinationForTests
                git mv $LOC FinalDestinationForTests/$FILE
                if [ $? -ne 0 ];then
                   echo "BAD: git mv $LOC FinalDestinationForTests/$FILE"
                fi
            fi
        fi 
        done
        git add -u
        git status
        git commit -m "#Ticket: move Things to new location $L"
        ((L = L + 1))
    done
  • 第三步:合并各分支
git checkout ps0
    git merge ps1 -X rename-threshold=5%
    # resolve manually... then
    git commit
    git merge ps2 -X rename-threshold=5%
    # resolve manually... then
    git commit

rename-threshold可以帮助git确信两个文件来自同一个地方,否则一个版本可能会简单地替换另一个版本,而不保留链接它们的修改历史。我认为结果等同于使用git commit-tree链接多个提交,这是解决这个问题的另一种方法。
您可以使用git blame验证历史记录,查看每个文件中的每一行来自何处,使用git log查看实际提交。
Raymond Chen对此有一个series of blogs,可能会感兴趣。他使用commit-tree来处理这个任务。我认为这会起作用,但我认为对于我的情况来说,这是一个有点太低级的方法。

  • 步骤4:将库合并到它所属的项目中

这是为了完整起见,因为您可能会将文件移动到另一个项目中。请参阅“如何合并两个Git仓库?“了解更多详细信息

cd targetProject
    git remote add sourceProject /path/to/sourceProject
    git fetch sourceProject
    git merge --allow-unrelated-histories sourceProject/ps0

我认为这方面的时机已经成熟,可以贡献一个脚本来为git添加一个新的合并工具。

相关问题