【问题标题】:Can I remove individual files from a git branch without deleting them when merging into another branch?合并到另一个分支时,我可以从 git 分支中删除单个文件而不删除它们吗?
【发布时间】:2014-05-17 20:45:33
【问题描述】:

我在一个项目的中间,其中有两个主要的开发分支,因为该项目由一个库和一个库的实现组成,两者都在不同的分支中并行开发。

我的工作流程是将库分支与--no-ff 合并到实现(命名为core)分支中,一旦向库中添加了足够多的功能,并且库分支理想情况下应该永远不会看到实现中的任何文件,因为否则两个分支都会对相同的实现文件进行更改。不幸的是,情况并非如此,因为我从现有的源文件夹启动项目,该文件夹包含库中的一些文件和部分启动的实现,并且在最初从 master 拆分两个工作分支时我忘记删除实现文件。

工作流程:

$ git checkout library

..做一些改变..

$ git commit -am "updated library"
$ git checkout core
$ git merge --no-ff library

此时,我通常必须处理由于库更新而导致的冲突,这些冲突在更改 library 分支中的实现文件的同时与对 core 分支的更改合并,并更新相同的文件,即使core 分支已对这些文件进行了自己的更新。

当我将它们合并到实现分支时,如何从library 分支中删除实现目录而不删除它们?

【问题讨论】:

  • “库的实现”是指“库的客户端”或“使用库的代码”,对吧?
  • @ErikAllik 抱歉,是的。实现分支包含使用在库中编写的接口和类的代码。我合并,以便我可以从库包中导入它们以及任何 API 更改

标签: git merge branch


【解决方案1】:

当我将它们合并到实现分支时,如何从库分支中删除实现目录而不删除它们?

您可以使用 --no-commit 执行合并,恢复已删除的文件并完成提交。

请参阅“git merge: Removing files I want to keep!”作为具体示例。

git checkout dev1
git merge --no-commit dev2
git checkout dev1 implementationFile
git add implementationFile
git commit

【讨论】:

    【解决方案2】:

    您似乎有点不对劲——您不应该为此目的使用分支;相反,您应该使用 2 个单独的存储库,一个库和一个实现/客户端。然后,客户将拥有图书馆回购,例如它的子模块(尽管还有其他方法)。

    这样,这两个存储库之间的文件不会有任何重叠,这是您想要的,由您的要求决定。

    为了转换当前 2 个分支的形式,您可以使用 git filter-branch 从分支 A 中过滤掉属于分支 B 的文件,反之亦然,因此您最终只会得到 2 个 repos具有库或客户端(实现)的文件。这当然有修改历史的缺点,所以如果你想避免这种情况,你只需使用普通的git rmgit commit 进行删除。

    【讨论】:

    • 所以实现应该包含一个包含库文件的子目录?不是我不想重叠,更多的是实现分支应该包含所有文件,而库分支应该只包含库文件
    • @adnan_252:完全正确! — 库应该是实现 repo 的子文件夹;请参阅Git Submodules,或者,或者,甚至更好,Git Subtree。否则,我看不到实现您想要的长期可持续/自动化的方式,而使用单独的存储库和使用子树/子模块,它会毫不费力地工作。
    【解决方案3】:

    避免被cherry-pick-ing 删除:

    是的,你可以很简单地做到这一点:你必须避免合并那些删除文件的提交。因此,当然,您最好不要进行会导致更改删除文件的提交。

    例子:

    创建一个带有master 分支的git repo,该分支有两个文件:

    ~$ mkdir gittest
    ~$ cd gittest
    ~/gittest$ git init
    Initialized empty Git repository in /home/kaz/gittest/.git/
    ~/gittest$ echo foo > foo
    ~/gittest$ echo bar > bar
    ~/gittest$ git add foo bar
    ~/gittest$ git commit -m "root commit"
    [master (root-commit) 1c1860f] root commit
     2 files changed, 2 insertions(+)
     create mode 100644 bar
     create mode 100644 foo
    

    从根提交中拍摄 topic 分支:

    ~/gittest$ git branch topic
    

    master 上创建两个文件的第二个版本:

    ~/gittest$ echo foo >> foo
    ~/gittest$ echo bar >> bar
    ~/gittest$ git diff
    diff --git a/bar b/bar
    index 5716ca5..a486f1a 100644
    --- a/bar
    +++ b/bar
    @@ -1 +1,2 @@
     bar
    +bar
    diff --git a/foo b/foo
    index 257cc56..0d55bed 100644
    --- a/foo
    +++ b/foo
    @@ -1 +1,2 @@
     foo
    +foo
    ~/gittest$ git commit -a -m "hack 1"
    [master ded65d8] hack 1
     2 files changed, 2 insertions(+)
    

    切换到topic。删除bar 并提交。以与master 冲突的方式修改foo 并提交:

    ~/gittest$ git checkout topic
    Switched to branch 'topic'
    ~/gittest$ git rm bar
    rm 'bar'
    ~/gittest$ git commit -m "delete bar"
    [topic 8a4009d] delete bar
     1 file changed, 1 deletion(-)
     delete mode 100644 bar
    ~/gittest$ echo "foo2" > foo
    ~/gittest$ git commit -a -m "replace foo"
    [topic abfa329] replace foo
     1 file changed, 1 insertion(+), 1 deletion(-)
    

    回顾topic的历史:

    ~/gittest$ git log --oneline
    abfa329 replace foo
    8a4009d delete bar
    1c1860f root commit
    

    返回master

    ~/gittest$ git checkout master
    Switched to branch 'master'
    ~/gittest$ git log --oneline
    ded65d8 hack 1
    1c1860f root commit
    

    现在从主题中挑选abfa329 replace foo,避免8a4009d delete bar

    $ git cherry-pick abfa329
    error: could not apply abfa329... replace foo
    hint: after resolving the conflicts, mark the corrected paths
    hint: with 'git add <paths>' or 'git rm <paths>'
    hint: and commit the result with 'git commit'
    

    修复foo上的冲突并提交:

    ~/gittest$ cat foo
    <<<<<<< HEAD
    foo
    foo
    =======
    foo2
    >>>>>>> abfa329... replace foo
    ~/gittest$ cat > foo
    foo
    foo2
    ~/gittest$ git add foo
    ~/gittest$ git commit -m "merged topic, avoiding deletion"
    [master 320818f] merged topic, avoiding deletion
     1 file changed, 1 insertion(+), 1 deletion(-)
    

    请注意,bar 仍然存在:

    ~/gittest$ ls bar
    bar
    ~/gittest$ cat bar
    bar
    bar
    

    在合并期间或之后使用git reset 撤消不需要的删除:

    如果您确实选择了不需要的删除,您可以轻松撤消它。

    让我们将示例master 分支回滚到hack 1 提交并重写它,以便bar 不会在master 上被修改,只有foo

    ~/gittest$ git reset --hard ded65d8
    HEAD is now at ded65d8 hack 1
    ~/gittest$ git reset HEAD^ -- bar
    Unstaged changes after reset:
    M   bar
    ~/gittest$ git commit --amend -m "hack1: bar only"
    [master 623908a] hack1: bar only
     1 file changed, 1 insertion(+)
    ~/gittest$ git log --oneline
    623908a hack1: bar only
    1c1860f root commit
    ~/gittest$ git show -p
    commit 623908a3bf6b2fb81b0bf8309fa2e83cd602986a
    Author: Kaz <kaz@stackexchange.example.com>
    Date:   Sat Dec 20 08:03:27 2014 -0800
    
        hack1: bar only
    
    diff --git a/foo b/foo
    index 257cc56..0d55bed 100644
    --- a/foo
    +++ b/foo
    @@ -1 +1,2 @@
     foo
    +foo
    

    现在,让我们合并topic,不要怀疑有不需要的删除:

    ~/gittest$ git merge topic
    error: Your local changes to the following files would be overwritten by merge:
        bar
    Please, commit your changes or stash them before you can merge.
    Aborting
    

    哎呀!在撤消bar 更改后,我忘记了git reset --hard。拿两个:

    ~/gittest$ git reset --hard
    HEAD is now at 623908a hack1: bar only
    ~/gittest$ git merge topic
    Auto-merging foo
    CONFLICT (content): Merge conflict in foo
    Removing bar
    Automatic merge failed; fix conflicts and then commit the result.
    

    好的,所以合并自动删除了bar(没有冲突,因为bar 没有在master 上修改)。 foo 有冲突,和cherry-pick 的情况完全一样,所以我们先处理一下:

    ~/gittest$ cat > foo
    foo
    foo2
    ~/gittest$ git add foo
    

    现在,回滚删除bar。请注意,bar 不在工作树中,暂时保持这种状态:

    ~/gittest$ git reset HEAD^ -- bar
    Unstaged changes after reset:
    D   bar
    

    我们承诺一切:

    ~/gittest$ git commit -m "merged topic, except deletion of bar"
    [master 57b7fca] merged topic, except deletion of bar
    

    现在我们带回bar:

    ~/gittest$ git reset
    Unstaged changes after reset:
    D   bar
    

    糟糕,我的意思是--hard

    ~/gittest$ git reset --hard
    HEAD is now at 57b7fca merged topic, except deletion of bar
    

    查看日志,--graph:

    ~/gittest$ git log --graph --oneline
    *   57b7fca merged topic, except deletion of bar
    |\  
    | * abfa329 replace foo
    | * 8a4009d delete bar
    * | 623908a hack1: bar only
    |/  
    * 1c1860f root commit
    

    验证文件内容。 foo 被合并,bar 在那里:

    ~/gittest$ cat foo
    foo
    foo2
    ~/gittest$ cat bar
    bar
    

    【讨论】:

      猜你喜欢
      • 2014-10-02
      • 1970-01-01
      • 2021-04-17
      • 2014-03-18
      • 1970-01-01
      • 2015-07-31
      • 2012-02-24
      • 2023-03-22
      • 1970-01-01
      相关资源
      最近更新 更多