了解 Git 历史记录简化

Azure DevOps Services | Azure DevOps Server 2022 - Azure DevOps Server 2019

Git 历史记录简化就像一头让人困惑的野兽。 99% 的时候你甚至不知道它的存在,但偶尔它会跳出 Git 的黑暗角落,冷不丁地咬你一口。 在本文中,我们将探讨什么是历史记录简化,以及在查看文件历史记录时它对用户带来的困惑。

我们先从一个常见场景开始:

  1. 你将更改推送到文件,然后将更改合并到 main。
  2. 一些同事也将其分支合并到 main。
  3. 过了一段时间,你回过头来,发现更改丢失了。
  4. 为了寻找罪魁祸首,你去查看文件历史记录并注意到…你的更改甚至没有列出!?

Git 提交历史记录是一棵树。 有时,按时间顺序排列的历史记录与实际的文件树历史记录不同。 当合并提交将文件还原回其原始状态时,最常发生这种情况。 在这种情况下,默认历史记录视图实际上不会显示所有更改,因为从技术上讲,文件并没有更改。 在此场景中,Git 意识到它可以简化历史记录,并从日志中删除一些更改,而你最有可能恰恰需要查找这些“更改”。

除非你以前遇到过,否则你可能会感到沮丧,想知道“我的更改到底去了哪里?”

历史记录简化:默认启用

默认情况下,对文件 git log file.txt 运行 log 命令会自动简化历史记录,可能会从其输出中隐藏某些提交。 有关详细信息,请参阅 git log 手册页

更令人困惑的是,如果你只是运行 git log,则不会发生历史记录简化,因为你将查看所有更改,没有什么可以简化。

若要关闭历史记录简化,需要使用命令行开关 --full-history

历史记录简化示例

为了更好地了解简化的工作原理,我们创建了自己的历史记录简化示例。 首先,我们来看一下将要创建的历史记录图:

Git 分支

如你所见,我们将:

  1. 创建一个文件。
  2. 在分支 (animals) 中向该文件添加一行。
  3. 在另一个分支 (fruit) 中向该文件添加不同的行。
  4. 将分支 animals 合并回 main。
  5. 将分支 fruit 合并回 main,并从 fruit 分支中选择该文件的整个副本。
  6. 检查该文件的历史记录。

Git 将为我们简化历史记录。 步骤 5 是这里的关键。 我们忽略了 animal 分支的所有更改。 Git 会注意到,我们的文件在步骤 1 和步骤 5 之间基本上没有更改,因此它只会向我们显示两个历史记录条目。

首先,我们创建文件并将其添加到存储库中:

> cd sample
> git init
> echo "some content" > test.txt
> git add test.txt
> git commit -m "Initial commit"

现在,我们决定将文本“donkeys”追加到 animal 分支中的文件中:

> git checkout -b animals
> echo "donkeys" >> test.txt
> git commit -am "We have added an animal"

在进行试验时,我们决定可能要在文件中改为使用 fruit,因此我们创建了另一个分支,并改为在文件末尾追加文本“bananas”:

> git checkout main -b fruit
> echo "bananas" >> test.txt
> git commit -am "We have added a fruit"

对更改满意后,我们决定将 animal 分支合并回 main:

> git checkout main
> git merge animals

现在,我们来看一下 test.txt 文件的日志:

> git log test.txt
    
    commit 6b33d99b996c430a60c9552b79245d1aa8320339
        Date:   Mon Feb 15 10:45:33 2016 -0500

        We have added an animal

    commit 206613ccd9a54b055b184c7b6c16f2ece8067e51
        Date:   Mon Feb 15 10:44:18 2016 -0500

        Initial commit

到目前为止一切正常,对吧? 在我们的日志输出中,没有什么看起来不寻常的。 现在,假设我们改变了主意,决定合并 fruit 分支:

>git merge fruit
    
    Auto-merging test.txt
    CONFLICT (content): Merge conflict in test.txt
    Automatic merge failed; fix conflicts and then commit the result.

噢哦,出现合并冲突。 经过一番考虑,我们决定使用 fruit 分支中的整个 test.txt 文件。 通常,你会使用某种文本编辑器或合并工具,但我们直接重新创建整个文件,因为它只有两行:

> echo "some content" > test.txt
> echo "bananas" >> test.txt
> git commit -am "Fixed merge conflict"

现在,我们来看看 test.txt 文件的历史记录:

> git log test.txt
    
    commit fdd4dfd816c4efebc5bdb240f49e934e299db581
        Date:   Mon Feb 15 10:51:06 2016 -0500

        We have added a fruit

    commit 206613ccd9a54b055b184c7b6c16f2ece8067e51
        Date:   Mon Feb 15 10:44:18 2016 -0500

        Initial commit

果然,我们在日志中没有看到与第一次实验相比的任何更改,也没有看到合并! 更改还在吗? Git 是否完全消除了这些更改?

> git log --full-history test.txt

如你所见,尽管它简化了没有 full-history 标志的日志,但 Git 保留了我们所有的更改:

> commit 5d0bb77a24e265dc154654fb3b5be331b53bf977
    Merge: 6b33d99 fdd4dfd
        Date:   Mon Feb 15 10:59:34 2016 -0500

        Fixed merge conflict

    commit fdd4dfd816c4efebc5bdb240f49e934e299db581
        Date:   Mon Feb 15 10:51:06 2016 -0500

        We have added a fruit

    commit 6b33d99b996c430a60c9552b79245d1aa8320339
        Date:   Mon Feb 15 10:45:33 2016 -0500

        We have added an animal

    commit 206613ccd9a54b055b184c7b6c16f2ece8067e51
        Date:   Mon Feb 15 10:44:18 2016 -0500

        Initial commit

Git 历史记录简化总结

历史记录简化的关键是,大多数时候你不会注意到它。 但是,当合并冲突出错并且你想知道发生了什么时,你可能会发现自己在查看 git 日志历史记录,并想知道更改去了哪里。

现在,你不会再惊慌失措,因为你知道:

  • 文件的历史记录简化默认处于启用状态
  • --full-history 标志将为你提供更全面的文件历史记录

更新:自从我写这篇文章以来,Azure DevOps Services 在 Web 上引入了许多很棒的历史记录查看选项。 这意味着,如果你不想通过命令行进行艰苦的工作,只需在我们的资源管理器中拉出要查看历史记录的文件,你将看到以下历史记录筛选器,你可以在其中指定简单或非简单的历史记录视图:

Git 筛选器

(c) 2016 Microsoft Corporation。 保留所有权利。 本文档“按原样”提供。本文档中表达的信息和观点(包括 URL 和其他 Internet 网站引用)如有更改,恕不另行通知。 您自行承担其使用风险。

本文档未向您提供任何 Microsoft 产品中任何知识产权的任何合法权利。 您可为了内部参考目的复制和使用本文档。