# Prior to rebase: feature_branch
# received new commits while
# master did too
#
# master
# x
# | feature_branch
# x y
# | |
# x y
# | /
# git merge-base ────► x--y--y--y
# master feature_branch |
# x
#
#
# After rebase: feature_branch has
# been completely cherry-picked onto
# the end of master
#
# feature_branch
# y'
# |
# y'
# /
# y'--y'--y'
# |
# master x
# |
# x
# |
# x
# |
# x
# |
# x
git checkout feature_branch
# create a new commit to undo the changes from some_previous_commit
# within feature_branch
git revert some_previous_commit
1.* 关于这个问题的一些详细的低级机制,请参见my other answer here和this very long and detailed answer by @torek here底部的"结果和结论"部分。* 1.* *"us "/" ours"= HEAD,也就是feature_branch,因为在运行git revert some_previous_commit时,您位于分支feature_branch上。更具体地说,"us "/" ours"**包含git diff some_previous_commit..HEAD表示的更改,它们是从正被恢复的提交(some_previous_commit)被提交的点到我们现在正在进行的提交的改变。
# 1. Merge `feature_branch` into `master`, accepting ALL of
# `master`'s (`ours`) changes in the event of
# any merge conflicts!
git checkout master
git merge -X ours feature_branch
# 2. Merge `feature_branch` into `master`, accepting ALL of
# `feature_branch`'s (`theirs`) changes in the event of
# any merge conflicts!
git checkout master
git merge -X theirs feature_branch
这里还有一些。这些是我最常用的技术,而不是上面的两个例子。
# 3. Assuming this merge attempt results in merge conflicts in
# these 3 files, but we know all of the changes on the `master`
# branch side are the ones we wish to keep, check out these 3
# files from `master` (`--ours`) to overwrite the conflicted
# files. Then, add these now-fixed files to stage them for
# committing, and continue (finish) the merge.
git checkout master
git merge feature_branch
git checkout --ours -- path/to/somefile1.c path/to/somefile2.c path/to/somefile3.c
git add path/to/somefile1.c path/to/somefile2.c path/to/somefile3.c
git status
git merge --continue
# 4. Assuming this merge attempt results in merge conflicts in
# these 3 files, but we know all of the changes on the `feature_branch`
# side are the ones we wish to keep, check out these 3
# files from `feature_branch` (`--theirs`) to overwrite the conflicted
# files. Then, add these now-fixed files to stage them for
# committing, and continue (finish) the merge.
git checkout master
git merge feature_branch
git checkout --theirs -- path/to/somefile1.c path/to/somefile2.c path/to/somefile3.c
git add path/to/somefile1.c path/to/somefile2.c path/to/somefile3.c
git status
git merge --continue
# 5. [BEST EXAMPLE] Assuming this merge attempt results in merge conflicts in
# a bunch of files, some of which are inside `path/to/some/dir`, I can
# choose to accept the changes from one side or the other **for
# all conflicts within files inside this directory**, like this!:
git checkout master
git merge feature_branch
# Keep `--theirs` for all conflicts within files inside this dir
git checkout --theirs -- path/to/some/dir
# OR: keep `--ours` for all conflicts within files inside this dir
git checkout --ours -- path/to/some/dir
# Add (stage for committing) all changes within files inside this dir
# all at once
git add path/to/some/dir
git status
git merge --continue
处理path does not have our version或path does not have their version错误:
如果你运行这样的程序:
git checkout --ours -- path/to/some/dir
......但它不起作用!它什么也没做。相反,它输出了以下错误:
error: path 'path/to/some/dir/file1.cpp' does not have our version
error: path 'path/to/some/dir/file2.cpp' does not have our version
error: path 'path/to/some/dir/file3.cpp' does not have our version
有关下面描述的问题的更详细示例,请参见see my other answer here。 不要在冲突解决过程中执行git checkout -- path/to/some/dir或git checkout some_branch -- path/to/some/dir(例如在merge冲突期间,如上面的示例),除非您打算分别从HEAD或some_branch检出目录path/to/some/dir中的所有文件,并使用这些文件覆盖本地文件,从而 * 不 * 仅仅 * 接受来自一方或另一方的冲突的改变。* 换句话说,在冲突解决过程中,这:
良好:
# GOOD :)
# Accept all conflicts from one side or the other (while still
# merging changes from both sides into one, in the event of being
# in the middle of a `git merge`).
git checkout --ours -- path/to/some/dir
# OR
git checkout --ours -- path/to/some/file
只接受来自--ours端的修改,如果我们想要的话,它总是好的和安全的,而这个:
不良:
# BAD :(
# OVERWRITE all files with these files from `some_branch` instead,
# thereby _losing_ any changes and/or files contained in the other
# side but which are not in `some_branch`.
git checkout some_branch -- path/to/some/dir
# OR
git checkout some_branch -- path/to/some/file
1.你也可以 checkout 整个目录,而不是单独指定文件!请参见this answer here和我的答案,以及my other answer here。我刚刚测试过它。这也可以工作。但是,再次注意上面的“警告警告警告”部分。这是一个完整的目录替换,而不是仅仅接受来自一方或另一方对整个文件夹的冲突更改:
# Check out ALL files from feature_branch which are in
# directory "path/to/dir". See "WARNING WARNING WARNING"
# section above.
git checkout feature_branch -- path/to/dir
--ours, --theirs
When checking out paths from the index, check out stage #2 (ours) or #3 (theirs) for
unmerged paths.
Note that during git rebase and git pull --rebase, ours and theirs may appear swapped;
--ours gives the version from the branch the changes are rebased onto, while --theirs
gives the version from the branch that holds your work that is being rebased.
This is because rebase is used in a workflow that treats the history at the remote as
the shared canonical one, and treats the work done on the branch you are rebasing as the
third-party work to be integrated, and you are temporarily assuming the role of the
keeper of the canonical history during the rebase. As the keeper of the canonical
history, you need to view the history from the remote as ours (i.e. "our shared
canonical history"), while what you did on your side branch as theirs (i.e. "one
contributor’s work on top of it").
2条答案
按热度按时间disho6za1#
当您 merge 时,
us
指的是您要合并到的分支,而them
指的是要合并的分支。当你 rebase 时,
us
指的是上游分支,them
指的是你移动的分支,在rebase的情况下,这有点违反直觉。原因是Git使用了相同的合并引擎来进行rebase,实际上是在上游分支中挑选你的东西。
us
= into,them
= from。xdnvmnnf2#
根据Git,谁是"我们"/"我们的"和"他们"/"他们的"?
(This还回答了以下问题:"git rebase是如何工作的,它到底发生了什么?")
在所有情况下:
用外行人的话来说(谢谢,@user20358):
"us"是已经在repo中的代码,"them"是[您]试图合并的代码
更完整的说法是:
1.* "us"(或"ours"**)=当前 checkout 的提交(
HEAD
) 在git执行 * 导致冲突的动作时(稍后会详细介绍),以及:1.* *"them"(或"their"**)=另一个提交,在git执行引起冲突的动作 * 时 * 没有被git checkout (稍后会详细介绍)。
HEAD
在它执行引起冲突的动作时不一定是你输入git命令时的HEAD
。这一点需要理解。Git可能会执行一些 checkout 操作并更改HEAD
(哪个提交被 checkout )在运行引起冲突的动作之前,从而使得"我们"和"他们"对于未经训练的眼睛来说看起来是交换的或反向的。这4例病例的详细信息:合并、挑选、重定基、还原:
git merge
(直观):1.示例命令:
1.* "us "/" ours"**=
HEAD
,也就是master
,因为在运行git merge feature_branch
时,您位于分支master
上。1. *"them "/" theirs"**=
feature_branch
,这是要合并到master
中的分支。git cherry-pick
(直观):1.示例命令:
1.* "us "/" ours"**=
HEAD
,即feature_branch
,因为在运行git cherry-pick some_commit
时您位于分支feature_branch
上。1. *"them "/" theirs"**=
some_commit
,这是您正在挑选的提交feature_branch
。git rebase
(违反直觉,但一旦你理解了它的工作原理,就完全有意义了):1.示例命令:
1.这张图(绘制在https://asciiflow.com),* latest * 或 * newest * 提交位于顶部和/或右侧:
1.* "我们"/"我们的"=
HEAD
,为上游分行:最初在master
上提交最后一个x
,然后在此之上提交新的y'
、cherry-picked
这是因为当你输入git rebase master
的时候, git首先 checkoutmaster
作为开始挑选feature_branch
提交到 * 的起点,则它确定从feature_branch
中选择哪些提交(即:feature_branch
提交中的哪些还没有在master
上).它通过查找merge-base
(feature_branch
和master
共有的提交,并且可以通过git merge-base master feature_branch
找到)来完成此操作,然后它从merge-base
* 之后 * 的第一个提交开始挑选提交,一次一个地工作,指向feature_branch
上的最后一次提交,到master
的顶端,从而将所有添加到feature_branch
的"新"y
提交"重定基"到最新的master
上,作为新的y'
提交。因此,"us "/" ours"=HEAD
,但由于git在后台执行了一个新的 checkout 来执行此重定基,HEAD
不是您键入git rebase master
时所在的分支。相反,如果在第一个cherry-pick
期间发生冲突,则us或HEAD
是master
上的最后一个x
提交,或者是任何新提交y'
,如果冲突发生在任何稍后的挑选期间,则最后一次被成功地挑选到master
上。它们因此是另一提交,其是来自x1M51N1x的某个x1M50N1x提交,该提交经由樱桃选择被应用于该新的x1M52N1x,按顺序,从feature_branch
上的第一次y
提交(紧接 *git merge-base master feature_branch
之后)一直到feature_branch
上的最后一次y
提交。 请参见下面对"它们"的解释。1.* "them "/" theirs"**=来自
feature_branch
的正被应用于新检出的HEAD
的某个y
提交,其中HEAD
是用于在变基期间的第一挑选操作的master
上的最后x
提交,或者这些新创建的y'
之一在master
之上提交,因为feature_branch
被"重定基",还是一次只提交一个(沿着从git merge-base master feature_branch
到feature_branch
上的最后一次提交的新提交串)到master
上。 请参见上面对"us"的解释。*git revert
(有点直观):1.示例命令:
1.* 关于这个问题的一些详细的低级机制,请参见my other answer here和this very long and detailed answer by @torek here底部的"结果和结论"部分。*
1.* *"us "/" ours"=
HEAD
,也就是feature_branch
,因为在运行git revert some_previous_commit
时,您位于分支feature_branch
上。更具体地说,"us "/" ours"**包含git diff some_previous_commit..HEAD
表示的更改,它们是从正被恢复的提交(some_previous_commit
)被提交的点到我们现在正在进行的提交的改变。1.“他们”/“他们的”=
some_previous_commit
(的逆或相反值),它是您要还原其更改的提交(撤销,通过在feature_branch
之上创建新提交)。换句话说,**“them”/“their”*包含由git diff some_previous_commit..some_previous_commit~
表达的改变,其中some_previous_commit~
是some_previous_commit
的 * 父提交 .这意味着“them”/“theirs”**是some_previous_commit
的 * 相反项 *,因为它包含其改变的相反项,以便撤消some_previous_commit
的改变.用例示例:
合并冲突解决示例:
这里还有一些。这些是我最常用的技术,而不是上面的两个例子。
非常有用:如果存在 * 整个文件夹 * 的冲突,您还可以指定接受来自
--ours
或--theirs
分支 * 的所有冲突更改,一次接受 * 整个文件夹,如下所示!:最佳合并冲突解决方案示例:
处理
path does not have our version
或path does not have their version
错误:如果你运行这样的程序:
......但它不起作用!它什么也没做。相反,它输出了以下错误:
问题是这些错误文件是
our
端的 * 删除 * 文件,因此在运行git checkout --ours -- path/to/some/dir
之前,我们必须手动git rm
每个文件。您也可以这样做来自动化该过程:
有关上述命令的详细说明,请参阅我的回答:git checkout --当文件规范包含已删除文件时我们的。
警告警告!
有关下面描述的问题的更详细示例,请参见see my other answer here。
不要在冲突解决过程中执行
git checkout -- path/to/some/dir
或git checkout some_branch -- path/to/some/dir
(例如在merge
冲突期间,如上面的示例),除非您打算分别从HEAD
或some_branch
检出目录path/to/some/dir
中的所有文件,并使用这些文件覆盖本地文件,从而 * 不 * 仅仅 * 接受来自一方或另一方的冲突的改变。*换句话说,在冲突解决过程中,这:
良好:
只接受来自
--ours
端的修改,如果我们想要的话,它总是好的和安全的,而这个:不良:
将完全 * checkout 并覆盖 * 指定的整个目录或整个文件,而不是 * 仅冲突的更改本身。* 这意味着您可能会无意中使用
git checkout some_branch
执行完全 checkout 而不是使用git checkout --ours
或git checkout --theirs
执行冲突解决,从而从一端或另一端删除更改。因此,每当您正在解决冲突(例如git merge
、git cherry-pick
、git rebase
或git revert
)时,建议使用git checkout --ours -- file_or_dir_paths
或git checkout --theirs -- file_or_dir_paths
,而不是git checkout some_branch -- file_or_dir_paths
。但是,如果您确实运行
git checkout some_branch -- file_or_dir_paths
,因为您了解这种行为,并且这正是您所 * 想要 * 的,那么您也需要注意这一点:像这样 checkout 整个目录并不会像您所期望的那样删除该目录中不存在于some_branch
的本地文件。相反,如果这些文件存在于本地但不存在于some_branch
,则会将它们单独留在本地文件系统中。* 因此,你必须手动删除所有这些文件代替。* 记住这一点,否则它会离开你非常非常困惑,在最后。请在我的其他答案here (this answer has a good solution to that too)和here中阅读更多信息。进一步/附加注解和提示:
1.上述文件冲突解决示例可以应用于各种类型的操作(
git merge
、git cherry-pick
、git rebase
、git revert
等)中发生冲突的情况,除非您需要记住theirs
和ours
对于每种类型的冲突解决的含义,如上所述。1.您也可以只使用
master
或feature_branch
等分支名称来代替-X ours
/-X theirs
以及--ours
和--theirs
。警告:传递分支名称可能看起来更容易和更清楚,但可能会对您的更改造成危险,因为这样做会整个文件或目录替换,而不是仅针对文件中的冲突的冲突解决。请参阅上面的“警告警告警告”部分。但是,如果您确实想进行整个文件替换,而不是仅接受来自一方或另一方的冲突更改,下面是方法:1.你也可以 checkout 整个目录,而不是单独指定文件!请参见this answer here和我的答案,以及my other answer here。我刚刚测试过它。这也可以工作。但是,再次注意上面的“警告警告警告”部分。这是一个完整的目录替换,而不是仅仅接受来自一方或另一方对整个文件夹的冲突更改:
1.请记住,如果您确实有意使用
git checkout feature_branch -- path/to/dir
,期望/希望它会删除目录path/to/dir
中不存在于feature_branch
的本地文件,它不会。您必须在运行checkout
命令之前或之后手动删除本地文件系统中的这些文件。--soft
or--hard
git reset by path参考文献:
1.[我的答案,我一直在引用!]“关于在git中 checkout 文件或目录的所有内容”:如何从另一个分支只获取一个文件?
1.[my answer]为什么git不能通过路径进行硬/软重置?--〉请特别参考**“背景知识”**部分中的 *git术语和定义 *!
1.[我自己对
git revert
期间“them”和“us”的含义的回答]在"git revert“中,”them“和”us“是谁?1.[@LeGEC的回答指出“ours/us”总是
HEAD
,“they/their”总是另一个分支或提交]在"git revert“中,”they“和”us“是谁?1.[关于我对
git merge
和git rebase
的理解,我交叉检查了先前存在的答案]根据Git,谁是“我们”,谁是“他们”?1.从
man git checkout
开始:“注意在git rebase和git pull --rebase中,我们的和他们的可能会出现交换":1.[回答我的问题]:你也可以把目录路径传递给git来 checkout 整个目录中的所有文件,而不必单独指定文件:如何只在某个目录下接受来自“their”分支的git合并冲突?
1.[我的答案] git checkout --当文件规范包含已删除文件时是我们的
1.用于绘制漂亮的ASCII图片或图表以放置在代码中:https://asciiflow.com/ .
附加研究:
1.[我自己还需要研究这个答案!--完成;我已经弄明白了,并在这里更新了我的答案,截至2020年1月7日]在一个'git revert'中,'them'和'us'是谁?
1.[我需要学习和阅读这仍然] git checkout --我们的不删除未合并文件列表中的文件
另请参见:
1.快速链接到我经常引用并认为是“git基本原理”的答案: