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

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

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

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

我考虑过的方法:

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

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


当前回答

要有选择地将文件从一个分支合并到另一个分支,请运行

git merge --no-ff --no-commit branchX

其中branchX是要从中合并到当前分支的分支。

--no commit选项将暂存已被Git合并的文件,而无需实际提交。这将使您有机会根据需要修改合并的文件,然后自己提交。

根据您希望合并文件的方式,有四种情况:

1) 你想要真正的合并。

在这种情况下,您接受合并文件的方式是Git自动合并文件,然后提交它们。

2) 有些文件您不想合并。

例如,您希望保留当前分支中的版本,而忽略要从中合并的分支的版本。

要选择当前分支中的版本,请运行:

git checkout HEAD file1

这将在当前分支中检索file1的版本,并覆盖Git自动合并的file1。

3) 如果您希望branchX中的版本(而不是真正的合并)。

Run:

git checkout branchX file1

这将在branchX中检索file1的版本,并覆盖Git自动合并的file1。

4) 最后一种情况是,如果只选择文件1中的特定合并。

在这种情况下,您可以直接编辑修改后的file1,将其更新为您希望的file1版本,然后提交。

如果Git无法自动合并文件,它将报告该文件为“未合并”,并生成一个副本,您需要手动解决冲突。


为了用示例进一步解释,假设您希望将branchX合并到当前分支中:

git merge --no-ff --no-commit branchX

然后运行gitstatus命令查看修改文件的状态。

例如:

git status

# On branch master
# Changes to be committed:
#
#       modified:   file1
#       modified:   file2
#       modified:   file3
# Unmerged paths:
#   (use "git add/rm <file>..." as appropriate to mark resolution)
#
#       both modified:      file4
#

其中,file1、file2和file3是git已成功自动合并的文件。

这意味着所有这三个文件的master和branchX中的更改都已合并在一起,没有任何冲突。

您可以通过运行gitdiff-cached来检查合并是如何完成的;

git diff --cached file1
git diff --cached file2
git diff --cached file3

如果您发现不需要合并,那么可以

直接编辑文件拯救git提交

如果您不想合并文件1并希望在当前分支中保留版本

Run

git checkout HEAD file1

如果您不想合并file2,只想在branchX中使用版本

Run

git checkout branchX file2

如果希望自动合并file3,请不要执行任何操作。

Git目前已经将其合并。

上面的文件4是Git失败的合并。这意味着在同一行上发生的两个分支中都有更改。这是您需要手动解决冲突的地方。您可以通过直接编辑文件或为希望file4成为的分支中的版本运行签出命令来放弃合并的文件。

最后,不要忘记gitcommit。

其他回答

您可以使用cherry-pick命令从一个分支获取单个提交。

如果所需的更改不在单个提交中,则使用此处显示的方法将提交拆分为单个提交。大致来说,您使用git rebase-i获取原始提交以进行编辑,然后使用git reset HEAD^选择性地还原更改,然后使用git commit将该位提交为历史中的新提交。

Red Hat Magazine还有另一个很好的方法,他们使用git-add——补丁或者可能是git-add,如果你想将不同的更改拆分到一个单独的文件中(在该页面中搜索“拆分”),则可以只添加大块的一部分。

拆分更改后,您现在可以随意选择所需的更改。

1800 INFORMATION的答案完全正确。不过,作为Git的新手,“使用Git cherry pick”不足以让我在互联网上进行更多挖掘,所以我想我会发布一份更详细的指南,以防其他人也在类似的情况下。

我的用例是希望有选择地将别人的GitHub分支中的更改拉到我自己的分支中。如果您已经有一个具有更改的本地分支,则只需执行步骤2和5-7。

使用要引入的更改创建(如果未创建)本地分支。$git分支mybranch<基本分支>切换到它。$git结帐mybranch从其他人的帐户中删除所需的更改。如果还没有,则需要将它们添加为远程。$git远程添加repo-w-changes<git-url>把树枝上的东西都拔下来。$git pull-repos-w-changes-branch-i-want查看提交日志以查看所需的更改:$git日志切换回要将更改拉入的分支。$git原始付款分支Cherry用哈希一个接一个地选择你的提交。$git cherry-pick-x提交哈希

帽子提示:http://www.sourcemage.org/Git_Guide(存档副本)

当两个分支的当前提交之间只有几个文件发生了更改时,我通过遍历不同的文件来手动合并更改。

gitdifftool<branch-1><分支2>

另请参见https://sites.google.com/site/icusite/setup/git-difftool

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

下面的步骤假设您有两个分支目标分支和源分支,并且要合并的目录目录位于源分支中。另外,假设您有其他目录(如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.

我不喜欢上述方法。使用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