如何检查一个 Git 提交是否是另一个提交的祖先

GitGitBeginner
立即练习

💡 本教程由 AI 辅助翻译自英文原版。如需查看原文,您可以 切换至英文原版

简介

在本次实验中,你将学习如何判断一个 Git 提交是否是另一个提交的祖先。理解提交之间的祖先关系对于浏览和理解项目历史至关重要。

我们将探索 git merge-base --is-ancestor 命令,这是实现此目的的强大工具。此外,我们将使用 git log 来可视化提交历史并追溯祖先关系,还会使用非祖先提交来测试该命令,以巩固你的理解。在本次实验结束时,你将能够熟练识别 Git 仓库中的祖先关系。


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL git(("Git")) -.-> git/SetupandConfigGroup(["Setup and Config"]) git(("Git")) -.-> git/BasicOperationsGroup(["Basic Operations"]) git(("Git")) -.-> git/BranchManagementGroup(["Branch Management"]) git/SetupandConfigGroup -.-> git/init("Initialize Repo") git/BasicOperationsGroup -.-> git/add("Stage Files") git/BasicOperationsGroup -.-> git/commit("Create Commit") git/BranchManagementGroup -.-> git/branch("Handle Branches") git/BranchManagementGroup -.-> git/checkout("Switch Branches") git/BranchManagementGroup -.-> git/log("Show Commits") subgraph Lab Skills git/init -.-> lab-560057{{"如何检查一个 Git 提交是否是另一个提交的祖先"}} git/add -.-> lab-560057{{"如何检查一个 Git 提交是否是另一个提交的祖先"}} git/commit -.-> lab-560057{{"如何检查一个 Git 提交是否是另一个提交的祖先"}} git/branch -.-> lab-560057{{"如何检查一个 Git 提交是否是另一个提交的祖先"}} git/checkout -.-> lab-560057{{"如何检查一个 Git 提交是否是另一个提交的祖先"}} git/log -.-> lab-560057{{"如何检查一个 Git 提交是否是另一个提交的祖先"}} end

使用 git merge-base --is-ancestor

在这一步中,你将学习如何使用 git merge-base --is-ancestor 命令来判断一个提交是否是另一个提交的祖先。这是理解 Git 项目不同版本之间的历史和关系的一个基本概念。

首先,让我们创建一个简单的 Git 仓库,并进行几次提交以建立一些历史记录。如果你还没有在项目目录中,请导航到该目录:

cd ~/project

现在,为本次实验创建一个新目录,并在其中初始化一个 Git 仓库:

mkdir git-ancestor-lab
cd git-ancestor-lab
git init

你应该会看到类似以下的输出:

Initialized empty Git repository in /home/labex/project/git-ancestor-lab/.git/

接下来,让我们创建第一个文件并提交它:

echo "Initial content" > file1.txt
git add file1.txt
git commit -m "Add file1 with initial content"

你会看到确认提交的输出:

[master (root-commit) <commit-hash>] Add file1 with initial content
 1 file changed, 1 insertion(+)
 create mode 100644 file1.txt

现在,让我们进行另一次提交。修改文件并提交更改:

echo "More content" >> file1.txt
git add file1.txt
git commit -m "Add more content to file1"

你会看到确认第二次提交的输出:

[master <commit-hash>] Add more content to file1
 1 file changed, 1 insertion(+)

现在我们有了一个包含两次提交的简单历史记录。让我们查看日志以查看提交哈希:

git log --oneline

输出将类似于以下内容(你的提交哈希会不同):

<commit-hash-2> (HEAD -> master) Add more content to file1
<commit-hash-1> Add file1 with initial content

在这个输出中,<commit-hash-1> 是第一次提交的哈希,<commit-hash-2> 是第二次提交的哈希。第二次提交是第一次提交的直接后代。这意味着第一次提交是第二次提交的祖先。

git merge-base --is-ancestor <ancestor-commit> <descendant-commit> 命令用于检查第一个提交是否是第二个提交的祖先。如果是,该命令将以状态码 0(成功)退出。如果不是,则以状态码 1(失败)退出。

让我们来测试一下。将 <commit-hash-1><commit-hash-2> 替换为你 git log --oneline 输出中的实际哈希。

git merge-base --is-ancestor <commit-hash-1> <commit-hash-2>
echo $?

echo $? 命令会打印上一个命令的退出状态。由于 <commit-hash-1><commit-hash-2> 的祖先,git merge-base 命令应该会成功,echo $? 的输出应该是 0

理解提交的祖先关系对于许多 Git 操作(如合并和变基)至关重要,因为它有助于 Git 确定不同分支或提交之间的共同历史。

运行 git log 追溯祖先关系

在这一步中,我们将使用 git log 命令来可视化提交历史,从而更清晰地理解祖先关系的概念。git log 命令是探索仓库历史的强大工具。

如果你还没在仓库目录中,请导航到该目录:

cd ~/project/git-ancestor-lab

我们的仓库中已经有两次提交了。让我们再次查看日志,这次使用默认格式:

git log

输出将显示每次提交的详细信息,包括提交哈希、作者、日期和提交信息。提交按逆时间顺序列出(最新的在前)。

commit <commit-hash-2> (HEAD -> master)
Author: Jane Doe <[email protected]>
Date:   <Date and Time>

    Add more content to file1

commit <commit-hash-1>
Author: Jane Doe <[email protected]>
Date:   <Date and Time>

    Add file1 with initial content

在这个输出中,你可以看到第二次提交(<commit-hash-2>)指向第一次提交(<commit-hash-1>)。这就是 Git 跟踪历史的方式。每个提交(除了初始提交)都有一个父提交,这种父子关系定义了祖先关系。

git log 命令本质上是从当前提交(由 HEAD -> master 指示)开始,沿着这个父提交链向后遍历。

让我们再进行一次提交,使历史记录稍微长一些:

echo "Final content" >> file1.txt
git add file1.txt
git commit -m "Add final content to file1"

现在,再次运行 git log --oneline 以查看更新后的历史记录:

git log --oneline

输出将显示三次提交:

<commit-hash-3> (HEAD -> master) Add final content to file1
<commit-hash-2> Add more content to file1
<commit-hash-1> Add file1 with initial content

在这里,<commit-hash-3> 是最新的提交,<commit-hash-2> 是它的父提交,而 <commit-hash-1><commit-hash-2> 的父提交。这意味着 <commit-hash-1><commit-hash-2><commit-hash-3> 的祖先。同样,<commit-hash-2><commit-hash-3> 的祖先。

我们可以使用 git merge-base --is-ancestor 来验证这些关系。将占位符替换为你实际的提交哈希。

git merge-base --is-ancestor <commit-hash-1> <commit-hash-3>
echo $?

这应该输出 0,因为第一次提交是第三次提交的祖先。

git merge-base --is-ancestor <commit-hash-2> <commit-hash-3>
echo $?

这也应该输出 0,因为第二次提交是第三次提交的祖先。

使用 git log 有助于你可视化提交图并理解父子关系,这与 git merge-base --is-ancestor 所检查的祖先关系概念直接相关。

测试非祖先提交

在前面的步骤中,我们使用 git merge-base --is-ancestor 确认了同一分支上较早的提交是较晚提交的祖先。现在,让我们来探索当测试彼此不是祖先关系的提交时会发生什么。

导航到你的仓库目录:

cd ~/project/git-ancestor-lab

目前,我们有一个包含三次提交的单分支(master)。为了测试非祖先关系,我们需要创建一个新分支,并在该分支上进行一次提交。这将创建一个分叉的历史记录。

首先,让我们创建一个名为 feature 的新分支:

git branch feature

此命令创建了一个名为 feature 的新分支指针,它指向与 master 相同的提交(即我们的最新提交 <commit-hash-3>)。

现在,让我们切换到 feature 分支:

git checkout feature

你应该会看到表明你已切换分支的输出:

Switched to branch 'feature'

现在你处于 feature 分支上。让我们在这个分支上进行一次新的提交。创建一个新文件:

echo "Feature content" > file2.txt
git add file2.txt
git commit -m "Add file2 on feature branch"

你会看到确认在 feature 分支上提交的输出:

[feature <commit-hash-4>] Add file2 on feature branch
 1 file changed, 1 insertion(+)
 create mode 100644 file2.txt

现在,让我们使用 git log --oneline --all --graph 查看历史记录。--all 标志会显示所有分支的提交,--graph 会以文本形式绘制提交历史的图形。

git log --oneline --all --graph

输出将显示一个分支历史记录。它可能类似于以下内容(提交哈希会有所不同):

* <commit-hash-4> (HEAD -> feature) Add file2 on feature branch
* <commit-hash-3> (master) Add final content to file1
* <commit-hash-2> Add more content to file1
* <commit-hash-1> Add file1 with initial content

在这个图形中,<commit-hash-4>feature 分支上的最新提交,<commit-hash-3>master 分支上的最新提交。这两个提交彼此不是祖先关系。它们有一个共同的祖先,即 <commit-hash-3>feature 分支创建时的提交)。

让我们使用 git merge-base --is-ancestor 来测试 <commit-hash-4><commit-hash-3> 之间的关系。将占位符替换为你实际的提交哈希。

git merge-base --is-ancestor <commit-hash-4> <commit-hash-3>
echo $?

此命令检查 <commit-hash-4> 是否是 <commit-hash-3> 的祖先。根据我们的图形,它不是。因此,该命令应该以状态码 1 退出。

现在,让我们反过来测试:<commit-hash-3> 是否是 <commit-hash-4> 的祖先?

git merge-base --is-ancestor <commit-hash-3> <commit-hash-4>
echo $?

此命令检查 <commit-hash-3> 是否是 <commit-hash-4> 的祖先。从图形中可以看出,<commit-hash-4> 的父提交是 <commit-hash-3>。所以,<commit-hash-3><commit-hash-4> 的祖先。该命令应该以状态码 0 退出。

这展示了如何使用 git merge-base --is-ancestor 以编程方式检查仓库历史中任意两个提交之间的关系,即使它们跨不同的分支。

总结

在本次实验中,我们学习了如何使用 git merge-base --is-ancestor 命令来检查一个 Git 提交是否是另一个提交的祖先。我们首先创建了一个简单的 Git 仓库,并进行了几次提交以建立历史记录。然后,我们使用 git log --oneline 查看提交历史并识别提交哈希。这一基础步骤对于理解 Git 项目中不同版本之间的关系至关重要。