9

我使用 Git 很长时间了,但最近遇到了一个有趣的技巧,它允许您在合并期间恢复文件更改历史记录。以下是重现的步骤是:

我有两个文件和一个提交的 git 存储库:

$ git branch   
  * master
$ git log --oneline 
  80c8d5a Initial commit
$ git log --oneline -- README
  80c8d5a Initial commit
$ ls
  README conflict_file

我正在创建新的实验分支并更改 README 和 conflict_file 文件:

$ checkout -b experiment 
  Switched to branch 'experiment'
$ vim README 
$ vim conflict_file 
$ git commit -am "Experimental changes"
  [experiment cdbc988] Experimental changes
  2 files changed, 2 insertions(+)
$ git log --oneline -- README
  cdbc988 Experimental changes
  80c8d5a Initial commit

返回主分支并编辑冲突文件(在合并期间发生冲突):

$ git checkout master 
  Switched to branch 'master'
$ vim conflict_file 
$ git commit -am "Master changes"
  [master ad8b68e] Master changes
  1 file changed, 1 insertion(+)

我的存储库的状态如下:

$ git log --oneline --decorate --graph --all
  * ad8b68e (HEAD, master) Master changes
  | * cdbc988 (experiment) Experimental changes
  |/  
  * 80c8d5a Initial commit

尝试与实验分支合并:

$ git merge experiment 
  Auto-merging conflict_file
  CONFLICT (content): Merge conflict in conflict_file
  Automatic merge failed; fix conflicts and then commit the result.
$ git mergetool 
  <merging conflict in conflict_file>

这是诀窍:

$ git reset HEAD README
  Unstaged changes after reset:
  M      README
$ git commit 
  [master 909139f] Merge branch 'experiment'
$ git log --oneline -- README
  80c8d5a Initial commit

我丢失了在Experimental changes commit的实验分支中引入的README文件的更改和历史记录。任何人都可以评论它与 Git 知道所有更改的想法有什么关系吗?有可能避免这种情况吗?这可能会成为我们公司的一个问题,因为允许开发人员进行合并操作,但他们可能会意外删除某人的更改。

4

2 回答 2

8

No history has been lost: The change to README made on the experimental branch is still contained in commit 210fdc1. That it is not part of the merge commit 909139f is solely because you explicitly reverted it before finalizing the merge.

You didn’t say what you expected to happen, so I can only guess what exactly surprised you here. I’ll just point out two likely candidates.

  1. A merge commit isn’t limited to modifying just the files touched since the merge base, or even only the files that have conflicting changes. In fact, it can be completely different from all its parents.

    “Completely different” of course isn’t what one would recommend, because it would break everyone’s assumptions about what a merge does. However, it can make perfect sense to extend documentation files in a merge commit, and resolving certain merge conflicts may require changing additional files.

  2. When you call git log -- some_path, a process called history simplification is invoked. As the man page describes it, git log will try to display only those commits “that are enough to explain how the files that match the specified paths came to be”. In your case, the initial commit is enough to explain the current state of README (because README has still, or again, the same content), so it is all that is shown.

    You may want to use --full-history, possibly together with --simplify-merges, or some other history simplification options. They, as well as the exact rules for including commits (which are more complicated than implied above), are described in the git log man page, with an extended example.

于 2013-06-20T08:27:25.757 回答
1

您实际上并没有丢失更改,只是尚未签入对文件的更改README,因此它们不在您的git log命令显示的任何提交中:

*   9b8ede8 (HEAD, master) Merge branch 'experiment'
|\  
| * 210fdc1 (experiment) Experimental
* | d0c1637 Master changes
|/  
* b558d42 Initial commit

(我的 SHA 当然是不同的)并且:

$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   README
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   conflict_file.orig
no changes added to commit (use "git add" and/or "git commit -a")

正如@Chronial 在评论中指出的那样,您明确告诉 git 对then-执行一个git reset --mixed--mixed默认)文件,即(在您的情况下)。READMEHEADad8b68e

如果您git status在冲突合并的中间(之前git mergetool)运行,您会看到:

$ git status
# On branch master
# You have unmerged paths.
#   (fix conflicts and run "git commit")
#
# Changes to be committed:
#
#   modified:   README
#
# Unmerged paths:
#   (use "git add <file>..." to mark resolution)
#
#   both modified:      conflict_file
#

git mergetool步骤解决了冲突,conflict_file进入与上述相同的准备提交状态README- 但在 之后reset --mixedREADME从“要提交的更改”移动到“未暂存的更改”。

于 2013-06-20T05:40:39.240 回答