Skip to content

Git - 基本概念

版本控制系统(Version Control System, VCS)是跟踪和管理文件随时间变化的软件。它允许软件开发者和团队高效协作,并维护项目演变的完整历史记录。

VCS 的主要功能包括:

  • 允许多个开发者同时在一个项目上工作。
  • 防止意外覆盖不同团队成员所做的修改。
  • 记录每一次修改的详细历史,包括修改者、时间以及原因。
  • 在需要时允许恢复到项目的先前状态。

VCS 主要有两种类型:

  • 集中式版本控制系统 (CVCS): 依赖于一个中央服务器来存储项目的所有版本。例如 Subversion (SVN) 和 CVS。
  • 分布式版本控制系统 (DVCS): 每个开发者都拥有整个仓库的完整副本(即克隆),包括其历史记录。Git 是最流行的 DVCS。

本教程重点介绍 Git,一个功能强大且被广泛使用的分布式版本控制系统。

在 CVCS 中,如果中央服务器故障或无法访问,协作就会停止,并且如果备份不够健全,还存在数据丢失的风险。像 Git 这样的 DVCS 克服了这一限制。

使用 DVCS,每个协作者都在其本地机器上拥有仓库的一个完整、独立的副本。这意味着:

  • 你可以离线工作:提交(commit)修改、创建分支(branch)、查看历史记录等,无需网络连接。
  • 增强的数据安全性:如果主服务器(如果用于协作,例如 GitHub)发生故障,任何克隆(clone)都可以用来恢复它。每个克隆本质上都是一个完整的备份。
  • 性能提升:大多数操作都是本地进行的,因此速度非常快。
  • 只有当你想要与他人分享你的修改(push)或获取他们的最新更新(pull/fetch)时,才需要网络连接。

Git 根据 GNU General Public License version 2 发布,这意味着任何个人或商业项目都可以免费使用它。它的开源性质意味着任何人都可以检查、修改和贡献其源代码。

Git 的速度非常快。大多数操作都在本地执行,避免了网络延迟。Git 以项目在给定时间的快照(snapshots)来存储数据,而不仅仅是文件差异(deltas),这有助于提高其速度,特别是在分支(branching)和合并(merging)方面。它用 C 语言编写,最大限度地减少了运行时开销,并且针对性能进行了高度优化。尽管它会克隆整个仓库,但 Git 使用巧妙的压缩和存储技术来保持本地仓库体积较小。

Git 中的每个文件和提交都使用加密哈希函数(当前是 SHA-1,未来增强功能正在向 SHA-256 过渡)进行校验和。这个校验和用于标识对象。这意味着数据几乎不可能损坏或被修改而 Git 未检测到。分布式特性也意味着存在多个仓库副本,显著降低了数据丢失的可能性。

Git 中的分支(branching)轻量且快速。与一些较旧的 VCS 不同,在那些系统中分支操作可能很繁琐,Git 分支只是指向特定提交的指针。这使得轻松创建分支来开发新功能、进行实验或修复 bug,而不会影响主开发线,变得非常容易。将这些分支合并(merging)回来通常也很直接。这一功能支持强大的工作流程,如特性分支(feature branching)、Gitflow 和 GitHub Flow。

Git 能够很好地从小型个人项目扩展到拥有数千名协作者的非常大、复杂的项目(例如,最初 Git 就是为 Linux 内核开发的)。

Git 是版本控制领域的行业标准。它拥有庞大的工具生态系统(GUI、IDE 集成)、托管服务(GitHub、GitLab、Bitbucket)以及一个庞大、活跃的社区提供支持和资源。

仓库(Repository,简称 repo)是构成你的项目的所有文件和文件夹的集合,以及完整的修改历史。它本质上是项目的数据库。在 Git 中,你的计算机上有一个本地仓库(local repository),可能还有一个远程仓库(remote repository)(例如,在 GitHub 上)用于协作。

工作区 (Working Directory)、暂存区 (Staging Area/Index) 和仓库 (Repository)

Section titled “工作区 (Working Directory)、暂存区 (Staging Area/Index) 和仓库 (Repository)”

Git 有三个主要的状态或区域,你的文件可以驻留其中:

  • 工作区 (Working Directory): 这是你在本地检出(checkout)的项目文件。它是你文件系统上你正在积极修改文件的目录。
  • 暂存区 (Staging Area 或 Index): 这是一个中间区域,Git 在此存储关于哪些内容将进入你的下一个提交的信息。你可以选择性地将工作区中的修改添加到暂存区。
  • 本地仓库 (.git directory): 这是 Git 永久存储项目历史(提交、分支、标签等)的地方。当你进行提交时(commit),Git 会获取暂存区中文件的状态,并将该快照永久存储到你的本地仓库中。

基本的 Git 工作流程如下:

  1. 你在工作区修改文件。
  2. 你选择性地选择想要包含在下一个提交中的修改,并将它们添加到暂存区(使用 git add)。
  3. 你执行一次提交(commit),这将把暂存区的文件状态保存为快照,并永久存储到你的本地仓库中(使用 git commit)。

例如,如果你修改了 feature_a.py 和 bug_fix_b.py,但只想先提交与 feature_a.py 相关的修改:

$ git add feature_a.py # 暂存 feature_a.py 的修改
$ git commit -m "Implement new aspect of feature A"
# 现在你可以暂存并提交 bug_fix_b.py 的修改了
$ git add bug_fix_b.py
$ git commit -m "Fix critical bug B"

提交(Commit)是项目已暂存修改在特定时间点的一个快照。每个提交都有一个唯一的标识符(一个 SHA-1 哈希值)、作者、时间戳以及描述修改的提交消息。提交按时间顺序链接,构成了项目的历史记录。一个提交可以有一个或多个父提交;具有多个父提交的提交表示一次合并(merge)。

分支(Branch)是一条独立的开发线。可以将其视为指向特定提交的可移动指针。当你创建一个分支时,你实际上是在为你的工作创建一个新的路径,允许你在隔离的环境中开发功能或修复 bug,而不会影响其他分支(例如主开发线)。新仓库中的默认分支通常命名为 main(历史上曾为 master)。

HEAD 是一个特殊指针,通常指向当前检出分支的最新提交(tip)。当你进行一个新的提交时,HEAD 会随着该提交向前移动。如果你检出的是一个特定的提交,而不是某个分支的最新提交,那么 HEAD 将直接指向该提交(这被称为“分离的 HEAD”状态,‘detached HEAD’ state)。

# 查看 HEAD 指向哪个分支(如果存在)以及提交哈希:
$ cat .git/HEAD
# 示例输出: ref: refs/heads/main
# 查看 HEAD 指向的提交哈希:
$ git rev-parse HEAD
# 示例输出: a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0

标签(Tag)是指向特定提交的指针,用于标记历史中的重要点,例如发布版本(如 v1.0、v2.1-beta)。标签一旦创建,通常不会移动。主要有两种类型:轻量标签(lightweight tag)(只是一个指针)和附注标签(annotated tag)(存储额外的元数据,如标签创建者、日期、消息,并且可以进行 GPG 签名)。

克隆(Cloning)会在你的本地机器上创建一个远程仓库的完整副本。这个副本包含所有文件、分支、标签以及完整的项目历史记录。这个本地副本是一个功能齐全的 Git 仓库。

git fetch 从远程仓库下载提交、文件和引用(refs)到你的本地仓库。它会更新你的远程跟踪分支(remote-tracking branches)(例如 origin/main),但不会修改你的本地工作区或本地分支。这允许你在合并之前查看修改。

git pull 本质上是 git fetch 之后紧跟着 git merge(如果配置为 git rebase 则执行 rebase)的组合。它从远程仓库抓取修改并立即尝试将它们集成到你当前的本地分支。

git push 将你的本地提交(来自本地分支)上传到远程仓库,与他人分享你的修改。你通常会将本地分支推送到远程仓库中对应的远程分支。

远程仓库(Remote)是对另一个 Git 仓库的引用,这个仓库通常托管在服务器上(例如,在 GitHub、GitLab 上)。当你克隆一个仓库时,通常会自动创建一个名为 origin 的远程仓库,指向你从中克隆的 URL。一个本地仓库可以有多个远程仓库。

# 查看当前远程仓库
$ git remote -v
# 示例输出:
# origin https://github.com/user/project.git (fetch)
# origin https://github.com/user/project.git (push)

虽然你通常不会直接与它们交互,但 Git 使用几种关键对象类型来存储其数据:

  • Blob (Binary Large Object): 存储文件的内容。它不包含文件名或路径等元数据。
  • Tree: 表示一个目录。它包含指向 Blob(文件)和其他 Tree(子目录)的指针,以及它们的名称和模式。

提交(Commits)、Blob 和 Tree 在 Git 的对象数据库中都由它们的 SHA-1 哈希值唯一标识。

要更深入地了解 Git 概念,请参阅 Git 官方文档: