在Git重基之后,在其他情况下,你可以在Git状态报告中发现一些被我们标记为删除的文件。根据Git,我们是谁?为什么?
它是指我坐在树枝上,它为我工作吗?或者它是指它自己和在我重新基于的分支上工作的人?
在Git重基之后,在其他情况下,你可以在Git状态报告中发现一些被我们标记为删除的文件。根据Git,我们是谁?为什么?
它是指我坐在树枝上,它为我工作吗?或者它是指它自己和在我重新基于的分支上工作的人?
当你合并时,us指的是你要合并的分支,而不是他们,要合并的分支。
当你重基时,us指的是上游分支,而them是你正在移动的分支。这在调整基数的情况下有点违反直觉。
原因是Git使用了相同的合并引擎来进行基重构,并且它实际上是将您的东西挑选到上游分支中。Us = into, they = from。
根据Git,谁是“我们”/“我们的”和“他们”/“他们的”?
(这也回答了这个问题:“git是如何进行重基的,它到底发生了什么?”)
在所有情况下:
"us"(或"ours") =当前签出的提交(HEAD),此时git执行导致冲突的操作(稍后详细介绍),并且: "them"(或"their ") =另一个提交,在git执行导致冲突的操作时没有被git签出(后面会详细介绍)。
重要提示:当它执行导致冲突的操作时,不一定是你输入git命令时的HEAD。理解这一点至关重要。Git可能会在运行导致冲突的操作之前做一些检出并更改HEAD(提交被检出),从而导致“我们”和“他们”在未经训练的人眼中出现互换或反向。
具体的4种情况:merge, cherry-pick, rebase, revert:
git merge (intuitive): Sample command: git checkout master git merge feature_branch # merge feature_branch into master "us"/"ours" = HEAD, which is master, because you were on branch master at the time you ran git merge feature_branch. "them"/"theirs" = feature_branch, which is the branch you're merging into master. git cherry-pick (intuitive): Sample command: git checkout feature_branch git cherry-pick some_commit # apply some_commit to feature_branch "us"/"ours" = HEAD, which is feature_branch, because you were on branch feature_branch at the time you ran git cherry-pick some_commit. "them"/"theirs" = some_commit, which is the commit you're cherry-picking onto feature_branch. git rebase (counter-intuitive, but totally makes sense once you understand the mechanics of how it works): Sample command: git checkout feature_branch git rebase master # rebase feature_branch onto latest master Diagram of this (drawn at https://asciiflow.com), with the latest or newest commits on top and/or to the right: # 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 "us"/"ours" = HEAD, which is the upstream branch: initially the last x commit on master, and then thereafter, some NEW commit, y', cherry-picked on top of that (this one's tricky!). This is because when you typed git rebase master, git first checks out master as the starting point to start cherry-picking your feature_branch commits onto, then it determines which commits from feature_branch to cherry-pick (ie: which of your feature_branch commits are not already on master). It does this by finding the merge-base (the commit which is common to both feature_branch and master and which can be found with git merge-base master feature_branch), and THEN it starts cherry-picking commits from the first one after this merge-base and onward, working one-at-a-time, towards the last commit on feature_branch, onto the tip of master, thereby "rebasing" all "new" y commits you added to feature_branch onto the latest master, as new y' commits. Therefore, "us"/"ours" = HEAD, but since git did a new checkout behind-the-scenes to perform this rebase, HEAD is NOT the branch you were on when you typed git rebase master. Instead, us, or HEAD, is either the last x commit on master if the conflict occurs during the first cherry-pick, or it is whatever NEW commit, y', was last successfully cherry-picked onto master if the conflict occurs during any later cherry-pick. Them is therefore the other commit, which is some y commit from feature_branch which is being applied to this new HEAD via a cherry-pick, in order, FROM the first y commit on feature_branch which is immediately after git merge-base master feature_branch all the way TO the last y commit on feature_branch. See explanation for "them" also, just below. "them"/"theirs" = some y commit from feature_branch which is being applied to a newly-checked-out HEAD, where HEAD is either the last x commit on master for the first cherry-pick operation during the rebase, OR one of these newly-created y' commits on top of master as feature_branch is "rebased", or cherry-picked one-commit-at-a-time (along your string of new commits from git merge-base master feature_branch to the last commit on feature_branch) onto master. See explanation for "us" also, just above. git revert (sort of intuitive): Sample command: git checkout feature_branch # create a new commit to undo the changes from some_previous_commit # within feature_branch git revert some_previous_commit For some of the detailed, low-level mechanics of this one, see my "Results and Conclusions" section at the bottom of my other answer here, as well as this very long and detailed answer by @torek here. "us"/"ours" = HEAD, which is feature_branch, because you were on branch feature_branch at the time you ran git revert some_previous_commit. More specifically, "us"/"ours" contains the changes expressed by git diff some_previous_commit..HEAD, which are the changes from the point at which the commit being reverted (some_previous_commit) was committed to the commit we are on now. "them"/"theirs" = (the inverse or opposite of) some_previous_commit, which is the commit whose changes you're reverting (undoing, by creating a new commit on top of feature_branch). In other words, "them"/"theirs" contains the changes expressed by git diff some_previous_commit..some_previous_commit~, where some_previous_commit~ is the parent commit of some_previous_commit. This means that "them"/"theirs" is the opposite of some_previous_commit, as it contains the opposite of its changes, in order to undo some_previous_commit's changes.
示例用例:
合并冲突解决示例:
# 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
处理路径没有我们的版本或路径没有他们的版本错误:
如果你运行这样的程序:
git checkout --ours -- path/to/some/dir
...但是没有用!它什么也没做。相反,它输出这些错误:
错误:path 'path/to/some/dir/file1.cpp'没有我们的版本 错误:path 'path/to/some/dir/file2.cpp'没有我们的版本 错误:path 'path/to/some/dir/file3.cpp'没有我们的版本
问题是这些错误的文件是我们这边删除的文件,所以我们必须在运行git checkout——ours——path/to/some/dir之前手动git rm每个文件。
git rm path/to/some/dir/file1.cpp path/to/some/dir/file2.cpp \
path/to/some/dir/file3.cpp
# then try again
git checkout --ours -- path/to/some/dir
你也可以这样做来自动化这个过程:
git checkout --ours -- path/to/some/dir \
|& gawk '{ print $3 }' | xargs git rm
git checkout --ours -- path/to/some/dir
查看我在这里对上面命令的详细解释:git checkout—当文件规范包括删除文件时我们的。
警告,警告,警告!
有关下面描述的问题的更详细示例,请参阅我的另一个答案。
不要在冲突解决过程中执行git checkout——path/to/some/dir,也不要在冲突解决过程中执行git checkout some_branch——path/to/some/dir,除非你打算分别从目录path/to/some/dir中的HEAD或some_branch中检出所有文件,并用这些文件覆盖本地文件,从而不仅仅接受来自一方或另一方的冲突更改。
换句话说,在解决冲突的过程中,
好:
# 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
只接受来自我们这边的改变,如果我们想这样做,这总是好的和安全的,而这个:
BAD:
# 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
will fully check out and overwrite the ENTIRE DIRECTORY or ENTIRE FILE, as specified, rather than only the conflicting changes themselves. This means you may be inadvertently deleting changes from one side or the other by doing a full checkout with git checkout some_branch rather than a conflict resolution with git checkout --ours or git checkout --theirs. FOR THIS REASON, IT IS RECOMMENDED TO USE git checkout --ours -- file_or_dir_paths or git checkout --theirs -- file_or_dir_paths, NOT git checkout some_branch -- file_or_dir_paths whenever you are in the middle of a conflict resolution such as for git merge, git cherry-pick, git rebase, or git revert.
HOWEVER, if you DO run git checkout some_branch -- file_or_dir_paths because you understand this behavior and that's what you want, then you need to be aware of this too: checking out an entire directory like that does NOT delete local files in that dir which do not exist at some_branch, like you'd expect it would. Instead, it leaves them alone in your local file system if they exist locally but not at some_branch. So, you must MANUALLY remove all of those files instead. Keep that in mind or else it will leave you very very confused in the end. Read more about this in my other answers here (this answer has a good solution to that too) and here.
进一步/额外的注意事项和提示:
The above examples of file conflict resolution can apply in the event of conflicts in any of the various types of operations (git merge, git cherry-pick, git rebase, git revert, etc.), except you need to keep in mind what theirs and ours means for each type of conflict resolution, as explained above. You can also just use branch names such as master or feature_branch in place of -X ours/-X theirs and --ours and --theirs. WARNING: Passing branch names may seem easier and clearer, but CAN BE DANGEROUS to your changes, as doing it this way does a FULL FILE OR DIRECTORY REPLACEMENT, RATHER THAN A CONFLICT RESOLUTION FOR JUST THE CONFLICTS WITHIN THE FILES. See the "WARNING WARNING WARNING" section above. If you DO want to do a full file replacement, however, rather than just accepting conflicting changes from one side or the other, here's how: # See "WARNING WARNING WARNING" section above. git checkout feature_branch -- path/to/somefile1.c path/to/somefile2.c path/to/somefile3.c You can also check out entire directories rather than specifying files individually! See this answer here and my answer here and my other answer here. I just tested it. This works too. Again, however, pay attention to the "WARNING WARNING WARNING" section above. This does a full directory replacement, rather than just accepting conflicting changes from one side or the other for an entire folder: # 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 Remember, if you do intentionally use git checkout feature_branch -- path/to/dir, expecting/hoping it will delete local files in directory path/to/dir which do NOT exist at feature_branch, it will NOT. You must remove those files manually in your local file system before or after running the checkout command. Read more in my answers here: All about checking out files or directories in git How to do a --soft or --hard git reset by path
引用:
[my answer, which I reference all the time!] "All about checking out files or directories in git": How to get just one file from another branch? [my answer] Why git can't do hard/soft resets by path? --> see especially the git terminology and definitions in the "Background knowledge" section at the end! [my own answer on what "them" and "us" mean during git revert] Who is `them` and `us` in a `git revert`? [@LeGEC's answer which points out that "ours/us" is always HEAD, and "them/theirs" is always the other branch or commit] Who is `them` and `us` in a `git revert`? [the pre-existing answer which I cross-checked against regarding my understandings of git merge and git rebase] Who is "us" and who is "them" according to Git? From man git checkout: "Note that during git rebase and git pull --rebase, ours and theirs may appear swapped": --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"). [answer to my question]: you can pass directory paths to git too to check out all files from entire directories, rather than having to specify files individually: How do I accept git merge conflicts from "their" branch for only a certain directory? [my answer] git checkout --ours when file spec includes deleted file For drawing pretty ASCII pictures or diagrams to place in code: https://asciiflow.com/.
额外的研究:
我还需要自己研究这个答案!——完成;我已经弄清楚了,并在这里更新了我的答案,截至2020年1月7日]在“git revert”中,谁是“他们”和“我们”? [我需要研究和阅读这个仍然]git签出-我们不从未合并文件列表中删除文件
参见:
快速链接到我的答案,我经常引用,并认为是“git基础”: 在git中从另一个分支创建一个分支的各种方法 所有关于在git中检出文件或目录的内容 根据Git,谁是“我们”,谁是“他们”?