git - 如何将来自两个分支/存储库的提交/更改融合到同一提交时间线中?

标签 git

我有一个独特的需求,我正在寻找解决方案......我确实在网上查看了很多其他解决方案,但没有找到符合我期望的解决方案......
有两个存储库 - X 和 Y(下图)。我想永远删除 Repo X。但是有一个文件夹,比如说 A,我也想把它和所有 Git 提交历史一起带到第二个仓库(Y)中......篡改历史是可以的,我希望仓库 Y 的历史声称文件夹 A 存在于从一开始的 repo Y 和对该文件夹 A 的所有更改都是在它在 repo Y 中时进行的。我不希望 repo Y 的历史显示文件夹 A 是在从另一个分支/ repo merge 之后出现的。我也不想 rebase ,因为文件夹 A 的所有提交(~1000)将被转储到 Repo Y 的现有提交之上。

***** Original Commits on the timeline for Repo X and Repo Y *****
Repo Y: -------Cx--------Cy--------Cz----------
Repo X: -----------Ca----------Cb------Cc------  (only commits that touch Folder A)
我找到了一个命令来过滤分支并重写 repo X 的提交,这样根据 git 只有文件夹 A 的内容存在。
git filter-branch --subdirectory-filter ./A -- --all
然后使用以下脚本将“文件夹 A”的所有内容移动到 Repo X 中名为“A”的文件夹中
# https://gist.github.com/xkr47/f766f4082112c086af63ef8d378c4304
# placed the script above in PATH and executed the following command at Repo X
git filter-mv 's!^!A/!'
所以现在我有了 repo Y,这是我需要将文件夹 A 从 repo X 移动到的 repo,其中包含所有 Git 提交历史......
我也有修改后的 repo X,其中唯一的内容是文件夹 A
请注意,不存在提交冲突的可能性,因为 repo X 上的所有提交都对文件夹 (A) 进行,而 repo Y 中根本不存在该文件夹
现在,我将能够将本地 Repo X 标记为我的 Repo Y 的远程,并且可以 merge 或 rebase
# repo X lives in the same directory as repo Y
# commands run inside repo Y
git remote add repoX ../X

git pull repoX <branch> --allow-unrelated-histories
# or
git pull repoX <branch> --rebase
merge
***** MERGE's output *****
Repo Y: -------Cx--------Cy-------Cz-----(MERGE)----
                                         /
                                        /
              -----Ca---------Cb------Cc  (Folder A commits)
提交的时间线将保持不变,但在两者 merge 之前将处于不同的轨道上,并且我将无法在 merge 提交之前的任何时候看到两个存储库中的内容,因为它们将位于单独的提交行上
(我想要文件夹 A,以及它经历的所有更改,在 repo Y 和它的提交行)
(……这样我就可以回到 Repo Y 中的任何一点,并根据提交时间一起查看 Repo Y 和文件夹 A 的演变)
REBASE
***** REBASE's output *****
Repo Y: -------Cx--------Cy--------Cz------Ca-----Cb-----Cc----
但是我不希望 Repo X 的提交是在 Repo Y 的最新提交之后......在我的用例中,Repo X 将有大约 1000 次提交,并且一次重新调整它们会破坏 Repo Y 的提交时间表。
我希望它们根据提交发生的时间在 Repo Y 的提交中融合
预期
***** Expected Output *****
Repo Y: -------Cx--Ca----Cy----Cb--Cz--Cc------  (Repo Y commits fused with commits from repo X - only adds a folder (A) to repo Y)
我知道干涉 Git Histories 不是一件好事。但是对于我的用例,这可能是我能做的最好的事情……

最佳答案

线性历史场景
如果您的“Repo Y”是单分支和线性的(没有 merge ;但是可以使用简单的 git rebase 将 merge 线性化),那么我能想到的一种可能的解决方案是将生成的 rebase 序列替换为包含融合提交的 rebase 序列依次。

  • 在你的“Repo Y”中,确保你在 master 分支上并标记当前 master如果出现问题,分支提交:
  • git checkout master
    git tag old-master-before-fuse
    
  • 将“Repo X”提取到存储库中,以便此存储库知道两个历史记录中的对象:
  • git remote add X ...path-to-your-Repo-X...
    git fetch X
    
  • 通过从最旧到最新对两个存储库的提交进行排序来准备交互式 rebase 序列:
  • git rev-list --format='%at %ct %H' refs/heads/master refs/remotes/X/master \ # produce a table of all commits from both repositories (fields: author timestamp, committer timestamp, commit hash)
        | grep -P '^\d+' \         # don't know to remove the "commit XXXX" lines from the output above otherway
        | sort -k1 -n \            # sort by the author timestamp
        | cut -d' ' -f3 \          # take hashes only
        | while read LINE; do      # prepare each hash for the interactive rebase sequencing
            echo "pick $LINE"
        done \
    > .git-rebase-todo # save it to a temporary file to be used later
    
  • 从第一次提交开始交互式 rebase :
  • git rebase -i --root
    
    一旦你的编辑器出现,用一个“break”删除所有“pick”命令,然后退出编辑器。 Git 将在此处停止( git rebase --continue 没有进一步的步骤将使 master 分支空白)。
  • 用之前生成的文件 .git-rebase-todo 替换当前的待办事项序列并继续 rebase :
  • cp .git-rebase-todo .git/rebase-merge/git-rebase-todo
    git rebase --continue
    
    这将使 master包含融合提交历史的分支。
  • 检查主分支,如果一切正常,删除检查点标记(当然要小心使用: git tag -d old-master-before-fuse )和远程链接( git remote remove X )。

  • 以下是我用于测试的存储库的转储:
    “ repo X”
    blob
    mark :1
    data 0
    
    reset refs/heads/master
    commit refs/heads/master
    mark :2
    author - <-> 1577886449 +0200
    committer - <-> 1606398449 +0200
    data 3
    Cx
    M 100644 :1 Cx
    
    commit refs/heads/master
    mark :3
    author - <-> 1580564850 +0200
    committer - <-> 1606398450 +0200
    data 3
    Cy
    from :2
    M 100644 :1 Cy
    
    commit refs/heads/master
    mark :4
    author - <-> 1583070451 +0200
    committer - <-> 1606398451 +0200
    data 3
    Cz
    from :3
    M 100644 :1 Cz
    
    
    “ repo Y”
    blob
    mark :1
    data 0
    
    reset refs/heads/master
    commit refs/heads/master
    mark :2
    author - <-> 1577972852 +0200
    committer - <-> 1606398452 +0200
    data 3
    Ca
    M 100644 :1 Ca
    
    commit refs/heads/master
    mark :3
    author - <-> 1580651253 +0200
    committer - <-> 1606398453 +0200
    data 3
    Cb
    from :2
    M 100644 :1 Cb
    
    commit refs/heads/master
    mark :4
    author - <-> 1583156854 +0200
    committer - <-> 1606398454 +0200
    data 3
    Cc
    from :3
    M 100644 :1 Cc
    
    
    可以使用 git fast-import 恢复转储。 .
    使用这两个示例存储库,熔断器例程将生成以下 git log --oneline结果:
    7efcbbd (HEAD -> master) Cc
    6cc905d Cz
    a20f00e Cb
    6650162 Cy
    e8e9bf1 Ca
    40678ea Cx
    

    关于git - 如何将来自两个分支/存储库的提交/更改融合到同一提交时间线中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65015989/

    相关文章:

    java - 与 JGit 的最后一次提交的文件差异

    git - .gitignore 和 .gitkeep 之间有什么区别?

    git - 如何在自托管的 GIT 存储库中 fork

    linux - 在 Linux 终端中将文件内容复制到剪贴板

    git 错误 : couldn't connect to host while accessing

    git - 如何获得git标签的总数

    git - 当当前分支不管理具有子模块的路径时,如何检查分支?

    git - 在 Sublime Merge 中显示分阶段更改

    Git checkout 不会丢弃我的更改

    git - 重写 Git 历史 : How do I remove a sign-off?