探索一下git的运行机制

Table of Contents

git 初探

一、Git 是什么?

![[38d3af14b09ad128476da8349b7e46df_MD5.png]]

Git 是一个开源的分布式版本控制系统,由 Linus Torvalds 在 2005 年为助力 Linux 内核开发而匠心打造。当时,传统版本控制系统在面对 Linux 内核这般庞大复杂的项目时,渐显乏力,频繁的代码合并冲突、缓慢的操作速度,都成了开发路上的绊脚石。于是,Git 应运而生,它打破常规,开启了分布式版本控制的新纪元。

与传统集中式版本控制系统(如 SVN)截然不同,Git 赋予每位开发者在本地拥有项目完整副本的能力。想象一下,你无需时刻联网,即便在飞机上、偏远山区,只要灵感来袭,就能尽情修改代码、记录版本,如同拥有一个随身携带的代码宝库。待网络恢复,再轻松同步至远程仓库,与团队伙伴共享成果。这种离线工作的特性,不仅让开发更自由随性,还极大提高了效率,即便中央服务器 “闹脾气”,大家依旧能稳步前行。

二、Git 基础概念大起底

![[17adbd61cb00d01c96f1b81f690c5145_MD5.png]]

先熟悉几个 Git 关键概念。

版本库(Repository),也就是常说的 “仓库”,如同一个藏满宝贝的魔法箱,收纳着项目所有的文件、代码以及详尽的修改历史。在本地初始化一个 Git 仓库后,系统会悄悄生成一个隐藏的.git 文件夹,这里面装着 Git 追踪项目的 “秘密配方”,千万别乱动里面的文件,以免 “魔法” 失灵。有了版本库,项目就像有了时光回溯机,随时能查看过去的模样。

提交(Commit),这可是 Git 里的重大时刻,相当于给项目在某个瞬间拍了张超清晰 “快照”,把代码、文件的状态永久定格。每次提交都附上一段精炼的提交信息,就像给照片加个备注,让队友一眼看穿你的改动意图,未来回溯时也能迅速 get 重点。一个好的提交,能让项目历史脉络清晰,开发效率蹭蹭涨。

分支(Branch)堪称 Git 的 “魔法分身术”。在项目开发中,一条主分支(常是 master 或 main)就像主干道,稳定承载可发布版本;而新功能开发、问题修复时,从主干道分出新分支,大家各忙各的,互不干扰。开发完轻松合并回主干道,项目稳步推进。比如开发电商 APP,有人在 “支付优化” 分支埋头苦干,有人在 “界面美化” 分支精雕细琢,主分支始终保持稳定运行,最后百川归海,融合成更强大的版本。

合并(Merge)是团队协作的 “会师” 环节,将不同分支的成果融合。但这过程有时像性格迥异的两人磨合,会出现冲突,比如同一行代码被不同分支改得 “南辕北辙”。别慌,Git 会精准标记冲突点,开发者手动调解,达成代码和谐统一,让项目继续阔步向前。

远程仓库(Remote Repository)则是团队的 “云端基地”,常见如 GitHub、GitLab 等平台上的仓库。本地仓库与远程仓库联动,大家能共享代码、协同作战。本地开发告一段落,轻轻一推(git push),代码 “飞” 上云端;想获取队友最新成果,顺手一拉(git pull),同步轻松达成,真正实现 “天涯若比邻” 的高效开发。

三、Git 实战技巧大放送

(一)别名巧设,效率飙升

日常频繁输入长长的 Git 命令,是不是感觉手指都在 “抗议”?别怕,别名设置来救场。打开.gitconfig 文件(在用户主目录下),在 alias 段落里尽情施展拳脚。比如把 git status 简化为 git st,git commit -m “信息” 缩写成 git cm “信息”,以后操作,手指少 “奔波”,效率直线飞。像这样:

[alias]
st = status
cm = commit -m
lg = log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<\%an>%Creset' --abbrev-commit

设置完后,git st 瞬间知晓仓库状态,git cm “修复小 bug” 轻快提交,开发节奏一路 “狂飙”。要是只想在当前仓库用别名,去掉 –global,在项目目录下的.git/config 文件里设置,个性化与便捷性完美融合。

(二)可视化提交图,一目了然

面对复杂项目,分支纵横交错,提交历史宛如一团乱麻?Git 自带 “可视化神器” 来帮忙。在查看提交记录时,加上 –graph 参数,如 git log –graph,瞬间,一幅精美的提交图在眼前展开。分支走向、合并节点清晰明了,就像拥有项目的 “导航地图”,理解项目演进易如反掌。结合 –pretty=oneline 等参数,让信息更精炼,一眼看穿每个提交的核心变化,开发决策有了坚实依据,团队协作也能少些 “磕碰”,沿着清晰轨迹稳步向前。

(三)错误处理有妙招

代码江湖,难免 “失足”,错误提交、误操作时有发生,Git 贴心备有 “后悔药”。

  • 若提交信息写错,还未后续提交,git commit –amend 火速补救。修改完保存退出,错误信息 “改邪归正”,但注意,这会生成新 SHA 值,远程仓库若已有人基于此开发,需谨慎行事。

  • 刚 git add 错文件,git reset HEAD 文件名,文件就从暂存区 “退回” 工作区;想批量撤回,git reset HEAD. 轻松搞定,让错误文件 “迷途知返”。

  • 本地提交完发现问题,想撤销?视情况选 “药方”:git reset –soft HEAD1,保留更改在暂存区,给修正机会;git reset –mixed HEAD1,撤销提交与暂存区更改,文件改动仍在,可重新整理;git reset –hard HEAD~1 最 “决绝”,本地代码回退版本,提交、暂存、工作区改动全消,慎用!

  • 仓库乱成一团,想回退到前一次稳定提交?git reset –hard 提交 ID,提交 ID 从 git log 找,项目 “时光倒流”,但远程仓库若已同步,需强推(git push -f )并和团队沟通,避免 “代码冲突” 大战。

  • 合并分支后 “状况百出”?git merge –abort 紧急 “刹车”,退回合并前状态,重新规划合并策略,让项目重回正轨。

四、.git 目录下各个文件是什么意思,是在什么时候生成的

config 文件

  • 含义:存储仓库特定的配置选项。这些配置可以覆盖全局的 Git 配置,只对当前仓库生效。
  • 示例内容:包括远程仓库的信息(如远程仓库的名称、URL 等)、本地分支与远程分支的关联信息等。例如,[remote "origin"]部分会定义远程仓库的名称为origin,以及它的获取(fetch)和推送(push)的 URL,像url = https://github.com/user/repository.git这样的配置。

description 文件

  • 含义:用于为仓库提供一个简短的描述,通常用于git describe命令等场景,帮助用户快速了解仓库的主要内容。
  • 示例内容:可以是一段简短的文字,如This is a sample project for learning Git

HEAD 文件

  • 含义:指向当前分支的引用,它记录了当前所在分支的最新提交(commit)的指针。通过修改 HEAD 文件的内容,可以切换分支。
  • 示例内容:通常包含类似ref: refs/heads/master的内容,表示当前分支是master分支。

hooks 文件夹

包含一系列脚本,这些脚本可以在 Git 执行特定操作(如提交、推送等)时被触发,用于实现自定义的代码检查、自动化测试等功能。

pre - commit 文件

在执行git commit操作之前运行的脚本。可以用于检查代码格式、运行单元测试等。例如,在一个 Python 项目中,可以编写一个pre - commit脚本,在提交前运行flake8来检查代码风格是否符合规范。

post - commit 文件

git commit操作完成后运行的脚本。可以用于通知团队成员提交已经完成、更新文档等操作。

info 文件夹

  • 含义:用于存储仓库的一些辅助信息,主要用于git applygit diff等操作。
  • exclude 文件:类似于.gitignore文件,用于指定在 Git 操作中需要排除的文件模式。不过,它的优先级低于.gitignore文件。例如,可以在exclude文件中添加一些临时需要排除的文件模式,而不影响全局的.gitignore设置。

objects 文件夹

存储所有的对象数据,包括提交(commit)对象、树(tree)对象、 blob(二进制大对象,如文件内容)对象等。这些对象构成了 Git 版本控制的基础数据结构。

以哈希值命名的文件夹和文件

Git 使用 SHA - 1 哈希值来唯一标识每个对象。在objects文件夹下,对象数据存储在以哈希值前两个字符命名的文件夹中,然后文件名是哈希值的剩余部分。例如,一个提交对象的哈希值是abcdef1234567890abcdef1234567890abcdef12,那么它的存储位置可能是objects/ab/abcdef1234567890abcdef1234567890abcdef12。这些对象文件存储了实际的提交、树和 blob 等信息,是 Git 版本历史的物理存储形式。

objects/info 文件夹
  • 含义:这个文件夹主要用于存储辅助信息,以帮助 Git 更高效地处理对象。它包含了一些关于对象存储的元数据和索引信息,这些信息有助于优化 Git 对仓库中对象的访问和管理。
  • alternates 文件(如果存在):用于指定其他对象数据库的位置,这些位置可以作为备用的对象存储源。当 Git 在本地对象数据库(objects文件夹)中找不到所需的对象时,它会根据alternates文件中的记录去其他指定位置查找。这在一些复杂的仓库设置或者共享对象存储的场景下非常有用。例如,在一个大型项目的多个子仓库共享对象存储的情况下,alternates文件可以引导 Git 找到共享的对象数据库。
  • 其他索引相关文件(可能因版本或配置而异):这些文件可能包含了对象的索引信息,例如对象的哈希值范围索引等。它们可以帮助 Git 更快地定位和检索对象。例如,通过索引文件,Git 可以快速判断某个对象是否存在于当前存储中,以及如果存在,其大致的存储位置在哪里。
objects/pack 文件夹
  • 含义:当 Git 仓库中有大量的对象(如提交、树、blob 对象)时,为了节省空间和提高性能,Git 会将这些对象打包存储在objects/pack文件夹中。打包后的对象存储方式可以减少磁盘空间占用,并且在访问多个相关对象时能够提高读取效率。
  • .pack 文件和.idx 文件.pack文件是实际存储打包对象的文件,它包含了多个对象的压缩数据。而.idx文件是对应的索引文件,用于帮助 Git 快速定位.pack文件中的对象。索引文件记录了对象在打包文件中的位置、对象的哈希值等信息。例如,当 Git 需要访问一个特定的提交对象时,它会首先查询.idx文件来确定该对象在.pack文件中的位置,然后从.pack文件中提取相应的对象数据。这种打包和索引的方式使得 Git 在处理大型仓库时能够更高效地利用磁盘空间和提高访问速度。

refs 文件夹

  • 含义:存储指向各种对象(如提交对象)的引用,包括分支引用、标签引用等。它是 Git 用于跟踪不同分支和标签的提交位置的机制。
  • heads 文件夹:存储各个分支的引用。每个分支对应一个文件,文件名是分支名称,文件内容是该分支最新提交的哈希值。例如,refs/heads/master文件的内容就是master分支最新提交的哈希值,通过这个引用,Git 可以找到master分支的最新状态。
  • tags 文件夹:存储标签的引用。标签用于标记特定的提交,如版本号标签。文件内容同样是被标记提交的哈希值,用于快速定位到带有标签的特定提交。

COMMIT_EDITMSG 文件

这个文件用于在执行git commit命令时,存储用户输入的提交消息。当你使用git commit命令,Git 会打开一个文本编辑器让你输入提交消息,这些消息就存储在COMMIT_EDITMSG文件中。如果没有正确填写提交消息,这个文件的内容可能会被 Git 用于生成默认的提交消息。例如,在一个简单的文本编辑器中,你输入的“Fixed a bug in the login functionality”这样的提交消息就会存储在这个文件中。

FETCH_HEAD 文件

它记录了从远程仓库获取(fetch)操作后的结果信息。当执行git fetch命令时,Git 会更新这个文件,其中包含了从远程仓库获取到的分支的最新提交的引用。例如,如果你从远程仓库的origin/master分支获取更新,FETCH_HEAD文件会记录origin/master分支最新提交的 SHA - 1 哈希值,用于后续的合并(merge)或其他操作,如git pull命令实际上就是git fetch后跟着git merge FETCH_HEAD的组合操作。

index 文件(索引文件)

也被称为暂存区(staging area),它是一个二进制文件,用于跟踪工作区(working directory)和仓库(repository)之间的差异。当你使用git add命令将文件添加到暂存区时,实际上是在更新这个index文件。它记录了哪些文件被修改、新增或删除,以及这些文件的内容和状态信息。在提交(commit)操作时,Git 会根据index文件的内容来创建新的提交对象。例如,如果你修改了一个文件example.txt,然后执行git add example.txtindex文件就会更新这个文件的修改后的状态信息,准备将其包含在下次提交中。

ORIG_HEAD 文件

主要用于存储在执行某些可能会改变 HEAD 引用的操作之前,HEAD 的原始位置。比如,在执行git resetgit rebase等操作之前,Git 会将当前 HEAD 的位置保存到ORIG_HEAD文件中。这样,如果操作出现问题或者你想要撤销这些操作,就可以通过ORIG_HEAD文件中记录的原始位置来恢复。例如,在执行git reset --hard HEAD~1(回退到上一个提交)之前,当前分支的 HEAD 位置会被保存到ORIG_HEAD文件中,以便后续恢复。

packed - refs 文件

它是一种优化的引用存储方式。当仓库中有大量的引用(如分支和标签引用)时,Git 会将这些引用打包存储在packed - refs文件中,以节省空间并提高效率。这个文件包含了已经打包的分支和标签的引用信息,以一种更紧凑的格式存储。例如,在一个有着众多分支和标签的大型项目仓库中,packed - refs文件可以有效地管理这些引用,避免每个引用都占用独立的文件空间。

logs 文件夹

  • 含义:用于存储 Git 操作的日志信息。它包含了关于仓库中各种操作(如提交、合并、重置等)的历史记录。
  • refs 文件夹:这个refs文件夹下的子文件夹(如headstags)存储了对应分支和标签的操作日志。例如,logs/refs/heads/master文件记录了master分支上所有提交操作的详细日志,包括每次提交的时间、作者、提交消息等信息,这些日志可以用于查看分支的演变历史,追溯操作的过程。

五、git 的优点是什么

(一)分布式版本控制

  • 本地仓库优势:每个开发者都拥有完整的本地仓库,包含代码库的全部历史记录。这意味着即使在没有网络连接的情况下,开发者依然可以在本地进行代码提交、查看历史版本等操作。例如,开发人员在出差途中没有网络,仍然能够在本地记录代码的修改情况,方便后续与远程仓库同步。
  • 备份与恢复方便:分布式的特点使得代码有多个备份。如果远程仓库出现故障,如服务器崩溃或者数据丢失,任何一个拥有完整本地仓库的开发者都可以将其备份的代码重新共享,恢复整个项目。

(二)高效的分支管理

  • 并行开发支持:通过创建分支,团队成员可以同时进行多个功能的开发或者问题修复,而不会相互干扰。例如,一个团队正在开发一个大型软件项目,一部分人在开发新功能分支“new-feature-1”,另一部分人在修复漏洞分支“bug-fix-2”,这些分支的开发工作可以独立进行。
  • 灵活的分支操作:切换分支、合并分支操作简单。在需要对某个分支进行测试或者集成时,能够轻松地将其合并到主分支或者其他分支。例如,当新功能开发完成并且经过测试后,可以将其合并到主分支“master”或者“main”,实现代码的集成。

(三)记录详细的版本历史

  • 精确追溯变化:Git 能够详细记录每一次代码的提交,包括提交者、提交时间、提交说明等信息。通过查看提交历史,可以清楚地了解项目的演进过程。例如,当发现一个新的漏洞时,可以通过查看历史提交来定位可能导致问题的代码修改。
  • 方便代码回滚:如果在开发过程中发现新的代码引入了问题,可以轻松地回滚到之前的某个稳定版本。例如,使用git reset命令可以将代码库回退到指定的提交版本,迅速恢复到之前的工作状态。

六、git 的缺点是什么

(一)学习曲线较陡

  • 复杂的概念和命令:对于新手来说,Git 的一些概念(如暂存区、工作区、分支等)和命令(如git rebasegit stash等)可能比较难以理解和掌握。需要花费一定的时间和精力来学习和实践,才能熟练运用。
  • 命令行操作有门槛:Git 主要通过命令行进行操作,对于不熟悉命令行的用户,如一些纯图形化开发工具的使用者,可能会觉得使用起来不太方便。

(二)合并冲突处理复杂

  • 手动解决冲突的必要性:在团队协作过程中,当多个分支的修改同时涉及到同一部分代码时,很容易产生合并冲突。这时候需要开发者手动打开文件,对比不同分支的修改内容,然后选择合适的方式进行合并,这个过程可能会比较繁琐。
  • 冲突解决经验要求高:有效地解决合并冲突需要开发者具备一定的代码理解能力和项目背景知识,对于复杂的项目或者大型团队来说,冲突的解决可能会消耗较多的时间和精力。

(三)存储占用较大

  • 完整仓库副本占用空间:由于每个本地仓库都包含完整的项目历史记录,当项目规模较大、历史提交较多时,会占用较多的磁盘空间。特别是对于一些存储资源有限的设备,这可能会带来一定的不便。
  • 大文件处理困难:Git 在处理大文件(如大型二进制文件)时效率较低,并且会进一步增加存储成本。这些大文件的版本历史记录也会占用大量的空间。

今天这篇文章写了很长的时间,主要是 AI 雇员的 prompt 没写好,始终得不到想到的回答、文章格式和风格。我希望文章足够简洁,每句话的信息量都足够大,不要那些形容句、感叹句和类比句,而这些正是 AI 生成的内容占比最高的部分,因为这些 AI 都试图模仿人类1,后面在剥离这些语句方面还得下点功夫。

明天了解一下 git 里的 submodule,在合适的场景使用能够极大提高代码共享效率


  1. 应该说是大模型背后的商业公司试图让 AI 看上去更像人 ↩︎