首页 文章

如何在git-subtree添加后重新定义?

提问于
浏览
53

我正在尝试学习在Git 1.7.11中添加的新git-subtree命令 . 添加子树后,我似乎失去了变基的能力 . 我有一个带有README文件的主存储库和一个库存储库,它也有一个README文件 . 我用 subtree add 将它添加到lib目录:

$ git subtree add -P lib/mylib myliborigin master

这很好,但现在历史看起来像这样:

*   22c1fe6 (HEAD, master) Merge commit 'b6e698d9f4985825efa06dfdd7bba8d2930cd40e' as 'lib/mylib' - 
|\                                                                                                                
| * b6e698d Squashed 'lib/mylib/' content from commit d7dbd3d
* b99d55b Add readme
* 020e372 Initial

现在,当我想要对 origin/master 重新设置我的仓库并且它失败是因为压缩提交直接应用于其不适用的父提交,因为它应用于仓库的根,而不是我添加的前缀子树 .

如果我看一下壁球提交,原因很清楚 . 没有关于前缀的信息 . 这只是原始的mylib提交压扁在一起 . 只有下一个合并提交知道它的任何内容,但rebase不会在此处考虑它 .

是否有任何变通方法(除了从不重新提交子树提交)?

6 回答

  • 6

    这不是一个解决方案,但它目前的工作我使用...

    使用您的初始示例:

    *   22c1fe6 (HEAD, master) Merge commit 'b6e698d9f4985825efa06dfdd7bba8d2930cd40e' as 'lib/mylib' - 
    |\                                                                                                                
    | * b6e698d Squashed 'lib/mylib/' content from commit d7dbd3d
    * b99d55b Add readme
    * 020e372 Initial
    

    在子树添加之前,以交互方式重新引导到第二次提交:

    $ git rebase -i 020e372
    

    删除先前提交的两个子树条目和标记编辑:

    e b99d55b Add readme
    

    保存文件/关闭,然后当它进入“添加自述文件”提交时,运行修改命令:

    $ git commit --amend
    

    然后重新添加新的子树:

    $ git subtree add -P lib/mylib myliborigin master
    

    继续变革:

    $ git rebase --continue
    

    然后你的分支应该从主人那里重新开始,并且子树将是“正常的”,并且合并的壁球完好无损:

    *   22c1fe6 (HEAD, master) Merge commit 'b6e698d9f4985825efa06dfdd7bba8d2930cd40e' as 'lib/mylib' - 
    |\                                                                                                                
    | * b6e698d Squashed 'lib/mylib/' content from commit d7dbd3d
    
  • 1

    这是一个古老的问题,但我的回购问题与我有同样的问题,我终于找到了一个完整的解决方案,它(希望)保留了所有的子树元数据 .

    假设我们有这个提交树:

    B   (master) Add README.md
    |     
    A            Initial commit
    

    我们用一个位于 lib/ 的子树分叉了 feature 分支:

    git remote add -f githublib https://github.com/lib/lib.git
    git subtree add --prefix lib/ githublib master --squash
    

    它创建了一个包含两个父项的合并提交D:我们当前的 master (B),以及与外部仓库的压扁历史无关的提交 F . 此提交还在其提交消息中包含一些 git subtree 元数据(即 git-subtree-dirgit-subtree-split ) .

    D     (feature) Merged commit 'F' as 'lib/'
      / \    
     /   F             Squashed 'lib/' content from GGGGGG
    B        (master)  Add README.md
    |     
    A                  Initial commit
    

    稍后,我们会独立地向两个分支添加一些提交 .

    E     (feature) Remove .gitignore from lib/
    C  |     (master)  Add LICENSE.md
    |  D               Merged commit 'F' as 'lib/'
    | / \    
    |/   F             Squashed 'lib/' content from GGGGGG
    B                  Add README.md
    |     
    A                  Initial commit
    

    现在我们想将 feature 重新定义为 master . 这是如何做:

    1. Cherry-从功能中逐一选择提交,以在master上创建功能分支的新副本 .

    git branch -f feature C
    git checkout feature
    git cherry-pick D E
    
    E'       (feature) Remove .gitignore from lib/
    |
    D'                 Merged commit 'F' as 'lib/'
    |
    |  E               Remove .gitignore from lib/
    C  |     (master)  Add LICENSE.md
    |  D               Merged commit 'F' as 'lib/'
    | / \    
    |/   F             Squashed 'lib/' content from GGGGGG
    B                  Add README.md
    |     
    A                  Initial commit
    

    现在我们有相当于一个rebase,但我们已经丢失了有关外部仓库的所有信息, git subtree 所需 . 要恢复它:

    2.将缺少的父链接添加为移植,并重写功能的历史记录以使其成为永久性 .

    git checkout feature
    git replace --graft D' C F
    git filter-branch --tag-name-filter cat -- master..
    

    现在我们得到的图片与开头的图片完全相同 . 旧的提交D和E仍在那里,但它们可以在以后进行垃圾收集 .

    E'       (feature) Remove .gitignore from lib/
    |
    D'                 Merged commit 'F' as 'lib/'
    |\
    | \                
    C  \     (master)  Add LICENSE.md
    |   \              
    |    \   
    |     F            Squashed 'lib/' content from GGGGGG
    B                  Add README.md
    |     
    A                  Initial commit
    

    Warning: 这会重写 feature 的历史记录,因此如果有人在此分支上与您合作,请小心发布 . 但是,既然你想首先做一个变基,你可能会意识到这一点:-)

  • 9

    显然这是预期的行为(对于"expected behaviour."的一些反常定义)参见:http://git.661346.n2.nabble.com/subtree-merges-lose-prefix-after-rebase-td7332850.html .

    并不是说这对任何人都有很大的帮助 . 我也很想为此找到解决方法 .

  • 0

    这适用于简单的情况:

    git rebase --preserve-merges master
    

    感谢@Techlive Zheng的评论 .


    你可能会看到

    fatal: refusing to merge unrelated histories
    Error redoing merge a95986e...
    

    这意味着git无法自动应用您的子树 . 这让你了解@ericpeters在他的回答中描述的情况 . 解:

    重新添加子树(使用您最初使用的相同命令):

    git subtree add -P lib lib-origin master
    

    继续变革:

    git rebase --continue
    

    你们都准备好了!


    如果您想知道它是否成功运行,您可以在重新定位后与原始版本进行比较,以确保您没有更改任何内容:

    git diff <ref-before-rebase> <ref-after-rebase> -- .
    

    (最后 -- . 指示git只有 diff 当前目录中的文件) .


    如果所有其他方法都失败了并且您不关心保留提交本身,您可以简单地 git cherry-pick 原始子树提交 .

    提交消息看起来像 Add 'lib/' from commit '9767e6...' - 这就是你想要的那个 .

  • 3

    我有一个类似的问题:我想在做一个子树添加后进行rebase,并且使用--preserve-merges仍然让我发生合并冲突(由于 .gitignore 文件冲突等) .

    在我的情况下,我没有't necessarily plan on using any subtree functionality: I was simply pulling in a repo that should have been part of the superproject originally. In case it helps anyone else, here'我最终做的事情,基于我发现的其他related _1052772 .

    假设我在同一目录中有两个项目main_project和sub_project . 我想将sub_project拉入main_project中名为sub_project的目录中,假设repo都没有名为sub_project的目录:

    cd main_project
    git fetch ../sub_project
    git checkout -b sub_project FETCH_HEAD
    git filter-branch --prune-empty --tree-filter '
        if [[ ! -e sub_project ]]; then
            mkdir -p sub_project
            git ls-tree --name-only $GIT_COMMIT | xargs -I files mv files sub_project
        fi'
    git checkout branch-to-merge-within
    git merge sub_project
    git branch -d sub_project
    

    如果我发现这种方法有任何问题,我会更新 .

  • 2

    你需要使用

    git rebase --preserve-merges --preserve-committer --onto new_place start end
    

相关问题