簡體   English   中英

git 合並多個副本保留歷史

[英]git merge multiple copies preserving history

我有一個項目,在不同的地方有一些文件的多個副本。 例如:

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 合並文件指定文件版本:

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-histories 來解決

所以我的問題是:

關於任務:

  1. 有沒有更好的辦法? 也許是一個我不知道的合並工具,就像我不知道 filter-repo
  2. 我認為多合並分支方式優於 git 合並文件是正確的嗎?

關於合並文件:

  1. 如何為 git 合並文件指定文件的特定版本
  2. 是否有自動查找共同祖先的命令或腳本。 就像是:
      git merge-file-wrapper location1 location2   -->

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

難道這不存在是因為有什么隱患?

我還沒有找到任何自動化工具來執行此操作,因此生態系統中可能存在缺口。

在我的例子中,我有多個文件要移動,其中一些文件比其他文件有更多的副本,這增加了一些有趣的復雜性,但在重構以刪除重復時並不少見。

我最后做的是:

  • 編寫一個腳本來創建一個新分支,其中每個變體都被移動到它的新位置。

  • 我的腳本首先識別要移動的文件。

  • 查找副本最多的文件並創建多個分支。

  • 對於每個分支,它嘗試將每個文件的一個副本移動到其新位置

  • 然后我手動合並每個分支。

    這些合並中的大多數都是微不足道的事情,例如更改每個子項目的名稱空間。

結果是一組文件,其中包含我想要的所有更改以及每個文件的所有更改歷史記錄。

為了使這個更具體一點:

  • 第 1 步:使用 filter-repo 創建一個只包含感興趣文件的項目

    (注意這應該在項目的新克隆上完成)

     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

重命名閾值有助於說服 git 這些文件具有相同的來源。 否則,一個版本可能會簡單地替換另一個版本,而不會保留鏈接它們的更改歷史記錄。 我認為結果相當於使用 git 提交樹鏈接多個提交,這將是解決此問題的另一種方法。

您可以使用git blame來驗證歷史記錄,以查看每個文件中每一行的來源,並git log來查看實際提交。

Raymond Chen 有一系列關於此的博客,您可能會感興趣。 他使用提交樹來處理這個任務。 我認為這可行,但我認為它對我的案例來說有點太低級了。

  • 第 4 步:將您的庫合並到它所屬的項目中

    這是為了完整性而包括在內,因為您可能將文件移動到另一個項目。 有關詳細信息,請參閱“ 如何合並兩個 Git 存儲庫?

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

我認為這個領域已經成熟,可以貢獻一個腳本來向 git 添加一個新的合並工具。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM