我正在一个新项目中使用Git,该项目有两个并行但目前处于试验阶段的开发分支:

master:导入现有的代码库,再加上一些我通常确信的修改exp1:实验分支#1exp2:实验分支#2

exp1和exp2表示两种非常不同的体系结构方法。在我进一步相处之前,我无法知道哪一个(如果有的话)会起作用。当我在一个分支中取得进展时,我有时会对另一个分支进行有用的编辑,并希望仅合并这些编辑。

将一个开发分支中的选择性更改合并到另一个分支中,同时保留其他所有内容的最佳方法是什么?

我考虑过的方法:

gitmerge——无需提交,然后手动取消大量编辑,我不想在分支之间共享这些编辑。手动将通用文件复制到临时目录中,然后git checkout移动到另一个分支,然后更多地手动将临时目录复制到工作树中。在上述基础上的变化。暂时放弃exp分支,使用另外两个本地存储库进行实验。这使得手动复制文件更加简单。

所有这三种方法都显得乏味且容易出错。我希望有更好的方法;类似于过滤器路径参数,可以使gitmerge更有选择性。


当前回答

我编写了自己的脚本“pmerge”来部分合并目录。这是一项正在进行的工作,我仍然在学习Git和Bash脚本。

该命令使用gitmerge--no提交,然后取消应用与提供的路径不匹配的更改。

用法:git pmerge分支路径示例:gitmerge-devel-src/

我还没有对它进行广泛的测试。工作目录应该没有任何未提交的更改和未跟踪的文件。

#!/bin/bash

E_BADARGS=65

if [ $# -ne 2 ]
then
    echo "Usage: `basename $0` branch path"
    exit $E_BADARGS
fi

git merge $1 --no-commit
IFS=$'\n'

# List of changes due to merge | replace nulls with newlines | strip lines to just filenames | ensure lines are unique
for f in $(git status --porcelain -z -uno | tr '\000' '\n' | sed -e 's/^[[:graph:]][[:space:]]\{1,\}//' | uniq); do
    [[ $f == $2* ]] && continue
    if git reset $f >/dev/null 2>&1; then
        # Reset failed... file was previously unversioned
        echo Deleting $f
        rm $f
    else
        echo Reverting $f
        git checkout -- $f >/dev/null 2>&1
    fi
done
unset IFS

其他回答

对我来说,git reset-soft分支是有选择地从另一个分支中选择更改的最简单方法,因为这个命令将所有diff更改放在我的工作树中,我可以轻松地选择或还原我需要的更改。

通过这种方式,我可以完全控制提交的文件。

这是我合并选定文件的工作流。

# Make a new branch (this will be temporary)
git checkout -b newbranch

# Grab the changes
git merge --no-commit  featurebranch

# Unstage those changes
git reset HEAD
(You can now see the files from the merge are unstaged)

# Now you can chose which files are to be merged.
git add -p

# Remember to "git add" any new files you wish to keep
git commit

如果您只需要合并一个特定的目录并保留所有其他内容,同时保留历史记录,那么您可以尝试这样做。。。在实验之前,从主对象创建一个新的目标分支。

下面的步骤假设您有两个分支目标分支和源分支,并且要合并的目录目录位于源分支中。另外,假设您有其他目录(如dir)保留在目标中,您不想更改并保留历史记录。此外,假设要合并的目录中存在合并冲突。

git checkout target-branch
git merge --no-ff --no-commit -X theirs source-branch
# the option "-X theirs", will pick theirs when there is a conflict. 
# the options "--no--ff --no-commit" prevent a commit after a merge, and give you an opportunity to fix other directories you want to retain, before you commit this merge.

# the above, would have messed up the other directories that you want to retain.
# so you need to reset them for every directory that you want to retain.
git reset HEAD dir-to-retain
# verify everything and commit.

tl;博士

git checkout source_branch -- path/to/file
# resolve conflicts if any
git commit -am '...'

我遇到了和你上面提到的完全相同的问题。但我在解释答案时发现这一点更清楚。

摘要:

检查要合并的分支的路径,$git签出source_branch--<paths>。。。提示:它也可以像链接文章中看到的那样,不使用“--”。或者选择性地合并大块$git checkout-p source_branch--<paths>。。。

或者,使用reset,然后添加选项-p,

    $ git reset <paths>...
    $ git add -p <paths>...

最后提交$gitcommit-m“'合并'这些更改”

我不喜欢上述方法。使用cherry pick可以很好地选择一个改变,但如果你想带来所有的改变,除了一些不好的改变,这是一种痛苦。这是我的方法。

没有可以传递给gitmerge的--interactive参数。

以下是备选方案:

您在分支“特性”中进行了一些更改,您希望以一种不拖泥带水的方式将其中的一些(但不是全部)提交给“主”(即,您不希望逐一挑选和提交)

git checkout feature
git checkout -b temp
git rebase -i master

# Above will drop you in an editor and pick the changes you want ala:
pick 7266df7 First change
pick 1b3f7df Another change
pick 5bbf56f Last change

# Rebase b44c147..5bbf56f onto b44c147
#
# Commands:
# pick = use commit
# edit = use commit, but stop for amending
# squash = use commit, but meld into previous commit
#
# If you remove a line here THAT COMMIT WILL BE LOST.
# However, if you remove everything, the rebase will be aborted.
#

git checkout master
git pull . temp
git branch -d temp

所以,只需将其包装在shell脚本中,将master更改为$to,并将feature更改为$from,就可以了:

#!/bin/bash
# git-interactive-merge
from=$1
to=$2
git checkout $from
git checkout -b ${from}_tmp
git rebase -i $to
# Above will drop you in an editor and pick the changes you want
git checkout $to
git pull . ${from}_tmp
git branch -d ${from}_tmp