分布式版本控制工具—Git

整体架构

Git整体架构图

  • 上层命令(Porcelain Commands)
  • 底层命令(Plumbing Commands)
  • 对象数据库(Object Database)

上层命令

日常使用的命令基本都是上层命令,如:commit、add、checkout、branch、remote 等。上层命令通过组合底层命令或直接操作底层数据对象,使 Git 底层实现细节对用户透明,从而为用户提供了一系列简单易用的命令集合。

底层命令

在日常开发中,我们基本接触不到 Git 的底层命令,如果要想使用这些底层命令,我们必须要对 Git 的设计原理有一定的认知。

对象数据库

Git 最核心、最底层 的部分则是其所实现的一套 对象数据库(Object Database),其本质是一个基于 Key-Value 的内容寻址文件系统(Content-addressable File System)。

常用命令

git init# 初始化本地git仓库(创建新仓库)
git config --global user.name “xxx”# 配置用户名
git config --global user.email “xxx@xxx.com”# 配置邮件
git config --global color.ui true# git status等命令自动着色
git config --global color.status auto# 配置 Git 在显示状态信息时自动使用颜色
git config --global color.diff auto#配置 Git 在显示差异信息时自动使用颜色
git config --global color.branch auto#配置Git在显示分支信息时自动使用颜色
git config --global color.interactive auto#配置 Git 在交互式界面中自动使用颜色
git config --global --unset http.proxy# remove proxy configuration on git
git clone git+ssh://git@192.168.53.168/VT.git# clone远程仓库
git status# 查看当前版本状态(是否修改)
git add xyz# 添加xyz文件至index
git add .# 增加当前子目录下所有更改过的文件至index
git commit -m ‘xxx’# 提交
git commit --amend -m ‘xxx’# 合并上一次提交(用于反复修改)
git commit -am ‘xxx’# 将add和commit合为一步
git rm xxx# 删除index中的文件
git rm -r *# 递归删除
git log# 显示提交日志
git log -1# 显示1行日志 -n为n行
git log --stat# 显示提交日志及相关变动文件
git log -p -m#-p 或 --patch 选项用于显示每次提交引入的差异的完整补丁格式,-m 或 --max-count 选项用于限制输出的提交数量。
git show dfb02e6e4f2f7b573337763e5c0013802e392818# 显示某个提交的详细内容
git show dfb02# 可只用commitid的前几位
git show HEAD# 显示HEAD提交日志
git show HEAD^# 显示HEAD的父(上一个版本)的提交日志 ^^为上两个版本 ^5为上5个版本
git tag# 显示已存在的tag
git tag -a v2.0 -m ‘xxx’# 增加v2.0的tag
git show v2.0# 显示v2.0的日志及详细内容
git log v2.0# 显示v2.0的日志
git diff# 显示所有未添加至index的变更
git diff --cached# 显示所有已添加index但还未commit的变更
git diff HEAD^# 比较与上一个版本的差异
git diff HEAD – ./lib# 比较与HEAD版本lib目录的差异
git diff origin/master…master# 比较远程分支master上有本地分支master上没有的
git diff origin/master…master --stat# 只显示差异的文件,不显示具体内容
git remote add origin git+ssh://git@192.168.53.168/VT.git# 增加远程定义(用于push/pull/fetch)
git branch# 显示本地分支
git branch --contains 50089# 显示包含提交50089的分支
git branch -a# 显示所有分支
git branch -r# 显示所有原创分支
git branch --merged# 显示所有已合并到当前分支的分支
git branch --no-merged# 显示所有未合并到当前分支的分支
git branch -m master master_copy# 本地分支改名
git checkout -b master_copy# 从当前分支创建新分支master_copy并检出
git checkout -b master master_copy# 上面的完整版
git checkout features/performance# 检出已存在的features/performance分支
git checkout --track hotfixes/BJVEP933# 检出远程分支hotfixes/BJVEP933并创建本地跟踪分支
git checkout v2.0# 检出版本v2.0
git checkout -b devel origin/develop# 从远程分支develop创建新本地分支devel并检出
git checkout – README# 检出head版本的README文件(可用于修改错误回退)
git merge origin/master# 合并远程master分支至当前分支
git cherry-pick ff44785404a8e# 合并提交ff44785404a8e的修改
git push origin master# 将当前分支push到远程master分支
git push origin :hotfixes/BJVEP933# 删除远程仓库的hotfixes/BJVEP933分支
git push --tags# 把所有tag推送到远程仓库
git fetch# 获取所有远程分支(不更新本地分支,另需merge)
git fetch --prune# 获取所有原创分支并清除服务器上已删掉的分支
git pull origin master# 获取远程分支master并merge到当前分支
git mv README README2# 重命名文件README为README2
git reset --hard HEAD# 将当前版本重置为HEAD(通常用于merge失败回退)
git rebase.#用于将一个分支的提交合并到另一个分支上。将一系列提交整合为一个整体,使版本控制历史更加清晰和简洁。
git branch -d hotfixes/BJVEP933# 删除分支hotfixes/BJVEP933(本分支修改已合并到其他分支)
git branch -D hotfixes/BJVEP933# 强制删除分支hotfixes/BJVEP933
git ls-files# 列出git index包含的文件
git show-branch# 图示当前分支历史
git show-branch --all# 图示所有分支历史
git whatchanged# 显示提交历史对应的文件修改
git revert dfb02e6e4f2f7b573337763e5c0013802e392818# 撤销提交dfb02e6e4f2f7b573337763e5c0013802e392818
git ls-tree HEAD# 内部命令:显示某个git对象
git rev-parse v2.0# 内部命令:显示某个ref对于的SHA1 HASH
git reflog# 显示所有提交,包括孤立节点
git show HEAD@{5}.#显示当前分支的引用日志(reflog)中第五个位置上的提交信息。这里的 HEAD@{5} 表示的是 HEAD 指针在引用日志中的第五个历史位置
git show master@{yesterday}# 显示master分支昨天的状态
git log --pretty=format:‘%h %s’ --graph# 图示提交日志
git show HEAD~3.#显示当前分支上倒数第三个提交的详细信息。在这个命令中,HEAD~3 是一个引用,指向当前分支上第三个最近的提交。
git show -s --pretty=raw 2be7fcb476#以原始格式(raw format)显示指定提交的详细信息,并且只显示提交的摘要信息(summary)
git stash# 暂存当前修改,将所有至为HEAD状态
git stash list# 查看所有暂存
git stash show -p stash@{0}# 参考第一次暂存
git stash apply stash@{0}# 应用第一次暂存
git grep “delete from”# 文件中搜索文本“delete from”
git grep -e ‘#define’ --and -e SORT_DIRENT#Git 仓库中搜索文本的命令。这个命令允许在提交历史记录中搜索指定的文本模式,包括文件内容、提交消息等。-e 选项后面跟着的是要搜索的模式。
git gc#执行垃圾回收(garbage collection)操作。这个命令会清理 Git 仓库中的无用对象,优化存储性能,并保持良好的磁盘空间利用率。
git fsck#验证 Git 仓库中对象一致性和完整性的命令

Git 相关网络协议:

1、SSH(Secure Shell)协议:这是一种安全的远程登录协议,常用于加密和安全的通信。它提供了较强的身份验证和数据加密机制,保证了数据在传输过程中的安全性。
2、HTTPS(HyperText Transfer Protocol Secure)协议:这是基于 HTTP 并通过 SSL/TLS 加密的网络协议。它在大多数网络环境中都能良好工作,并且不需要额外的配置,使用起来相对方便。
3、Git 协议:这是一种专门为 Git 设计的只读协议,主要用于只读的访问场景,例如获取代码的只读副本。

Git 协议的缺点是缺乏授权机制,一旦放开推送操作,意味着网络上的任何人都能向项目推送代码,并且架设相对困难。同时,Git 协议在一些托管平台上可能不被直接支持,例如 GitHub 等平台主要使用 SSH 协议和智能 HTTP 协议。
在实际应用中,SSH 和 HTTPS 是比较常用的协议,具体选择哪种协议取决于多种因素,如安全性需求、服务器配置、用户权限管理等。

GIT和SVN的对比

待补充…

Git Web Hook

1.定义

Git Web Hook 是一种机制,当特定的 Git 操作(如推送代码、创建分支、创建标签等)在仓库中发生时,Git 托管平台(如 GitHub、GitLab 等)会自动向您预先指定的 URL 发送一个 HTTP 请求,以通知您相关事件的发生。

2.使用场景

使用场景包括但不限于以下几种:
1.持续集成与持续部署(CI/CD):当代码被推送到仓库时,触发自动构建、测试和部署流程。这对于持续集成和持续部署(CI/CD)流程非常有用。
2.通知系统:Web Hook 可以用于发送通知或提醒.例如当有新的代码提交、问题被创建或合并请求被接受时,通过邮件、即时通讯工具等,告知代码库的更新情况。帮助团队成员及时了解项目动态,提高协作效率。
3.备份与同步:触发数据备份操作,或者将代码同步到其他相关的存储或服务中。
4.第三方服务集成:许多第三方服务(如 GitHub、GitLab 等)提供了与 Web Hook 集成的功能,可以用于扩展 Git 仓库的功能,例如自动化测试、文档生成等。
5.监控与审计:记录代码库的操作历史,用于监控和审计目的。
6.代码审查和质量检查:通过 Web Hook,可以在代码提交前触发自动化的代码审查和质量检查工具,确保代码质量符合团队标准。
通过使用 Git Web Hook,能够实现自动化流程,提高开发效率,并且及时响应代码库的变化。

3. Git Hook

可参考: Git——Web Hooks原理与实战

hook 举例

(1)pre-receive 只会调用一次,且返回非 0 值的时候会终止 push,stderr 的内容回返回给客户端。pre-receive 是 Git 钩子中的一种,在服务器端接收推送操作之前被触发。它会接收一系列由标准输入传入的行,每行包含旧的提交哈希值、新的提交哈希值以及引用名称(如分支名称)。
pre-receive 钩子的主要用途是在推送操作实际应用之前进行一些检查和验证。例如:
• 检查提交消息的格式是否符合规范。
• 验证推送的分支是否符合特定的命名规则。
• 确保推送的用户具有足够的权限。
如果 pre-receive 钩子以非零状态退出,整个推送操作将会被拒绝。
(2)update 对应每个 ref 更新都会调用一次,且返回非 0 值的时候会拒绝更新此 ref。其它 ref 的更新不受影响。stderr 的内容会返回给客户端。update 钩子在服务器端接收推送时,针对每个将要被更新的引用(分支或标签)都会被触发一次。它接收引用的名称、旧的对象名和新的对象名作为参数。与 post-receive 不同,update 钩子可以让您有机会拒绝特定的推送操作。如果 update 钩子脚本以非零状态退出,推送将被拒绝。
(3)post-receive 只会调用一次,无论返回任何值都不会影响 push 过程。当 git 推送操作完成后,在服务器端被触发。这个钩子会接收到所有被推送的引用的旧值和新值。它通常用于执行一些在推送完成后需要进行的全局操作,比如自动部署、通知相关人员、更新相关的缓存等。

Git Hook 通过软链接共享配置

1、统一配置一个 Git Hook 并让每个仓库都能使用:

   例如,在共享的 Hook 脚本目录下,统一配置post-receive、pre-receive、update 三个配置。将这三个文件软链接到 git-hook 文件。目的在 git-hook 中统一组织,使得对钩子脚本的修改和维护更加集中和高效,避免了在每个仓库中重复编写相同的逻辑。
   设置 core.hooksPath 配置.对于每个要应用统一 Hook 的仓库,执行以下命令:git config core.hooksPath ~/.git_hooks, 每个设置了 core.hooksPath 指向共享目录的仓库都会使用您在共享目录中定义的 Hook 脚本。

2、共享配置的好处

在 Git 钩子(Git Hooks)之间创建软链接可以起到以下一些作用:
1.共享和复用钩子脚本
如果多个 Git 仓库需要执行相同的钩子逻辑,通过创建软链接,可以避免在每个仓库中都单独维护一份相同的脚本。这样,对脚本的修改只需在一处进行,所有链接到该脚本的钩子都会受到影响。
2.统一管理和更新
方便对钩子脚本进行集中管理和更新。当需要修改钩子的行为或修复问题时,只需更新源脚本,所有通过软链接使用该脚本的仓库都能立即应用更改,确保了钩子行为的一致性。
3.简化配置和维护
减少了在多个仓库中重复配置钩子的工作量。只需在一个地方创建软链接,就可以实现多个仓库的钩子配置。
例如,假设您有一个规范的 pre-receive 钩子脚本用于执行某些代码审查和验证逻辑。通过在多个相关的 Git 仓库中创建指向该脚本的软链接,您无需为每个仓库单独编写和维护 pre-receive 脚本,节省了时间和精力,并保证了所有仓库在这个钩子上的行为一致。

存储方式

普通文件:使用Blob Object(二进制对象)存储文件内容
目录文件: 使用Tree Object(树对象)存储一系列树对象记录(tree entry)。每个树对象记录存储一个Blob Object(对应一个普通文件)或一个 Tree Object(对应一个目录文件)的元数据。

索引方式

在 Git 文件系统中,使用一个 40 位的 SHA-1 值作为一个文件或目录存储内容时所占用的一个 Object 文件的唯一标识符,最终在 .git/objects/ 目录下进行匹配查找,其中前 2 位 SHA-1 值作为存储子目录,后 38 位 SHA-1 值作为文件名。
Tree Object 和 Blob Object 的 SHA-1 值是根据内容计算得到的,只要内容相同,SHA-1 值相同;而 Commit Object 会结合内容、时间、作者等数据,因此 SHA-1 值很难出现冲突。

对象模型

Git 对象模型主要包括以下 4 种对象

  • 二进制对象(Blob Object)
  • 树对象(Tree Object)
  • 提交对象(Commit Object)
  • 标签对象(Tag Object)

所有对象均存储在 .git/objects/ 目录下,并采用相同格式进行表示,其可以分为两部分:

  • 头部信息:类型 + 空格 + 内容字节数 + \0
  • 存储内容

Git 使用两部分内容的 40 位 SHA-1 值(前 2 为作为子目录,后 38 位作为文件名)作为快照文件的唯一标识,并对它们进行 zlib 压缩,然后将压缩后的结果作为快照文件的实际内容进行存储。

Blob Object

Block Object 用于存储普通文件的内容数据,其头部信息为 “blob” + 空格 + 内容字节数 + \0,存储内容为对应文件的内容快照。

Tree Object

Tree Object 用于存储目录文件的内容数据,其头部信息为 “tree” + 空格 + 内容字节数 + \0,存储内容为 一个或多个树对象记录(Tree Entry)。

其中,树对象记录的结构(Git v2.0.0)为:文件模式 + 空格 + 树对象记录的字节数 + 文件路径 + \0 + SHA-1。

如果某一时刻,Git 仓库的文件结构如下所示,那么在 Git 文件系统中,会建立一个对象关系图,如下图所示。

$ tree
.
├── bak
│   └── test.txt
├── new.txt
└── test.txt

在这里插入图片描述
注意,当我们执行 git add(进入暂存区)时,Git 会为暂存文件创建 Blob Object,为暂存目录创建 Tree Object ,结合未修改文件和目录的 Object,建立一个整体的索引关系,从而形成一个版本快照。

Commit Object

Tree Object 和 Blob Object 用于表示版本快照,Commit Object 则不同,它 用于表示版本索引和版本关系。

此外,Tree Object 和 Blob Object 的 SHA-1 值是根据内容计算得到的,只要内容相同,SHA-1 值相同;而 Commit Object 会结合内容、时间、作者等数据,因此 SHA-1 值很难出现冲突。

Commit Object 的头部信息为 “commit” + 空格 + 内容字节数 + \0,存储内容包含多个部分(Git v2.0.0),具体如下图所示。

  • 对应的根 Tree Object 对应的 SHA-1
  • 一个或多个父级 Commit Object 对应的SHA-1。当进行分支合并时就会出现多个父级 Commit Object。
  • 提交相关内容,包括:作者信息、提交者信息、编码、提交描述等

下图所示,为 Commit Object 与 Tree Object 的关系示意图。每一个 Commit Object 索引一个版本快照,每一个版本快照则是由一个 Tree Object 作为根节点进行构建。不同的版本快照之间会进行数据复用,从而最大限度地节省磁盘空间。每一个 Commit Object 记录了其父版本的索引信息,即另一个 Commit Object 的 SHA-1 值,从而构建了一个完整的版本关系图(有向无环图)。通过版本关系图,我们可以基于一个 Commit Object 回溯其任意历史版本。
在这里插入图片描述

Tag Object

Tag Object 是第 4 种 Git 对象,其头部信息为 “tag” + 空格 + 内容字节数 + \0,存储内容包含多个部分(Git v2.0.0),具体如下图所示。

  • 所引用对象的 SHA-1 值
  • 所引用对象的类型
  • 标签名称
  • 标签创建者和日期
  • 注释信息
    在这里插入图片描述
    Tag Object 通常指向一个 Commit Object,而不是 Tree Object。它像是一个永不移动的分支引用——永远指向同一个 Commit Object,只不过给这个 Commit Object 加上一个更友好的名字罢了。
    Tag Object 并非必须指向某个 Commit Object;我们可以对任意类型的 Git 对象打标签

引用

如果我们对仓库的某一个提交及其历史版本感兴趣,那么我们可以使用该提交的 SHA-1 值进行查找。显然,直接使用 SHA-1 值来记忆是非常不便且易错的。对此,Git 提供了容易记忆的 “别名” 来代替 SHA-1 值,这就是 “引用(referrences,简称 refs)”。
Git 支持三种引用类型,不同的引用类型对应的引用文件各自存储在 .git/refs/ 下的不同子目录中。

  • HEAD 引用
  • 标签引用
  • 远程引用

HEAD 引用

当执行 git branch 新建一个分支时,Git 是如何知道最新提交的 SHA-1 值呢?答案就是 HEAD 文件。
HEAD 文件通常是一个 符号引用(symbolic reference),指向当前所在的分支。所谓符号引用,表示它是一个指向其他引用的指针,类似于符号链接。

在某些特殊情况下,HEAD 文件可能会包含一个 Git 对象的 SHA-1 值。当我们在检出一个标签、提交或远程分支时,让仓库变成 “分离 HEAD” 状态时,就会出现这种情况。
执行 git commit 时,该命令会使用 HEAD 文件中引用所指向的 SHA-1 值作为其父提交,创建一个 Commit Object。

$ git checkout master
$ cat .git/HEAD
ref: refs/heads/master

$ git checkout test
$ cat .git/HEAD
ref: refs/heads/test

标签引用

Git 可以给仓库历史中的某一个提交打上标签,以示重要。 比较有代表性的是人们会使用这个功能来标记发布结点( v1.0 、 v2.0 等等)

标签引用(Tag Reference)包含两种类型:轻量标签 和 附注标签。

轻量标签很像一个不会改变的分支——它只是某个特定提交的引用。

附注标签是存储在 Git 数据库中的一个完整对象, 它们是可以被校验的,其中包含打标签者的名字、电子邮件地址、日期时间, 此外还有一个标签信息,并且可以使用 GNU Privacy Guard (GPG)签名并验证。 通常会建议创建附注标签,这样你可以拥有以上所有信息。但是如果你只是想用一个临时的标签, 或者因为某些原因不想要保存这些信息,那么也可以用轻量标签。

轻量标签

对于轻量标签,我们可以通过如下命令进行创建。

$ git update-ref refs/tags/v1.0 a9f2652cb992f300c0a251d3607bdabfe8901bb2
$ cat .git/refs/tags/v1.0
a9f2652cb992f300c0a251d3607bdabfe8901bb2

该命令会创建一个以标签名命名的文本文件,文件内容为其所引用的 Commit Object 的 SHA-1 值。

附注标签

对于附注标签,我们可以通过如下命令进行创建。

$  git tag -a v1.1 e73b484c6e1802c36dceae4535f6c85 -m "test tag"

该命令会创建一个 标签对象(Tag Object),存储在 .git/objects/ 目录下。

远程引用

远程引用(Remote Reference)主要用于远程仓库与本地仓库进行映射和对比。如果我们添加了一个远程仓库并对其执行过推送操作,Git 会记录下最近一次推送操作时每一个分支所对应的值,并保存在 .git/refs/remotes/ 目录下。

远程引用和分支(位于 .git/refs/heads/ 目录下的引用)之间的最主要区别在于:远程引用是只读的。虽然我们可以 git checkout 到某个远程引用,但是 Git 并不会将 HEAD 引用指向该远程引用。因此,我们永远不能通过 git commit 命令来更新远程引用。Git 将这些远程引用作为记录远程服务器上各个分支最后已知位置状态的书签来管理。

包文件

过上文我们知道,如果我们对任意一个文件进行修改,Git 就会创建一个新的 Blob Object,并将该文件的所有内容存储到里面。
如果一个文件非常大,而每次只修改其中极小一部分内容,这样的话,Git 会创建很多 Blob Object,而它们的绝大部分的内容都是相同的,因此会存在严重的磁盘空间浪费问题。
Git 采用了增量存储的方式进行了优化,不定时地自动对仓库中的对象进行打包并移除,最终生成两个文件:

  • 包文件(Pack File) :采用 原始内容 + 增量内容 的形式进存储,从而节省存储空间。
  • 索引文件(Index File):存储了各个包文件中各个对象的大小、偏移、类型等数据,从而便于重建文件快照和对象关系。
    在这里插入图片描述

文件状态

工作目录下文件有两种状态:已跟踪和未跟踪。
已跟踪(Git已经知道的文件)指的是被纳入版本控制的文件,上一次快照中有它们的记录,在工作一段时间后,它们的状态可能是未修改、已修改或已放入暂存区。
未跟踪指工作目录中除已跟踪文件外的所有文件,既不存在于上次快照,也没有放入暂存区。
文件状态变化

untracked: 未跟踪
unmodified:未修改
modified: 已修改
staged: 暂存

git status

使用git status -s 命令或 git status --short获取工作目录文件状态信息

$ git status -s
 M README
MM Rakefile
A  lib/git.rb
M  lib/simplegit.rb
?? LICENSE.txt

输出中有两栏,左栏指明了暂存区的状态,右栏指明了工作区的状态
?? :新添加的未跟踪文件
A :新添加到暂存区中的文件
M :修改过的文件
例如,上面的状态报告显示: README 文件在工作区已修改但尚未暂存,而 lib/simplegit.rb 文件已修改且已暂存。 Rakefile 文件已修改,暂存后又作了修改,因此该文件的修改中既有已暂存的部分,又有未暂存的部分。
忽略文件
有些文件无需纳入 Git 的管理,也不希望它们总出现在未跟踪文件列表。 通常都是些自动生成的文件,比如日志文件,或者编译过程中创建的临时文件等。 在这种情况下,我们可以创建一个名为 .gitignore 的文件,列出要忽略的文件的模式。
要养成一开始就为新仓库设置好 .gitignore 文件的习惯,以免将来误提交这类无用的文件。
文件 .gitignore 的格式规范如下:
(1)所有空行或者以 # 开头的行都会被 Git 忽略。
(2)可以使用标准的 glob 模式匹配,它会递归地应用在整个工作区中。
(3)匹配模式可以以(/)开头防止递归。
(4)匹配模式可以以(/)结尾指定目录。
(5)要忽略指定模式以外的文件或目录,可以在模式前加上叹号(!)取反。

# 忽略所有的 .a 文件
*.a
# 但跟踪所有的 lib.a,即便你在前面忽略了 .a 文件
!lib.a
# 只忽略当前目录下的 TODO 文件,而不忽略 subdir/TODO
/TODO
# 忽略任何目录下名为 build 的文件夹
build/
# 忽略 doc/notes.txt,但不忽略 doc/server/arch.txt
doc/*.txt
# 忽略 doc/ 目录及其所有子目录下的 .pdf 文件
doc/**/*.pdf

所谓的 glob 模式是指 shell 所使用的简化了的正则表达式。 星号(*)匹配零个或多个任意字符;[abc] 匹配任何一个列在方括号中的字符 (这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个 c); 问号(?)只匹配一个任意字符;如果在方括号中使用短划线分隔两个字符, 表示所有在这两个字符范围内的都可以匹配(比如 [0-9] 表示匹配所有 0 到 9 的数字)。 使用两个星号()表示匹配任意中间目录,比如 a//z 可以匹配 a/z 、 a/b/z 或 a/b/c/z 等。
tips
GitHub 有一个十分详细的针对数十种项目及语言的 .gitignore 文件列表, 你可以在 https://github.com/github/gitignore 找到它

git diff

想知道具体修改了什么地方,可以用 git diff 命令。通常可能会用它来回答这两个问题:当前做的哪些更新尚未暂存? 有哪些更新已暂存并准备好下次提交?

git diff 能通过文件补丁的格式更加具体地显示哪些行发生了改变。此命令比较的是工作目录中当前文件和暂存区域快照之间的差异。 也就是修改之后还没有暂存起来的变化内容。

若要查看已暂存的将要添加到下次提交里的内容,可以用 git diff --staged 命令。 这条命令将比对已暂存文件与最后一次提交的文件差异。git diff 本身只显示尚未暂存的改动,而不是自上次提交以来所做的所有改动。 所以有时候一下子暂存了所有更新过的文件,运行 git diff 后却什么也没有,就是这个原因。

git rev-list

git rev-list 命令用于列出满足特定条件的提交对象。它的基本语法是:

git rev-list [<options>] <commit>... [--] [<path>...]

tips: git rev-list 命令不接受 --stat 和 --pretty 选项同时使用,因为这两个选项的目的不同。–stat 是为了显示每次提交的差异统计,而 --pretty 是为了定制提交信息的显示格式。如果你想要同时显示提交哈希和装饰性引用,以及每次提交的差异统计,你应该分开这两个操作。

git rev-list --pretty=format:"%H" master --since "2024-01-01 00:00:00" --before "2024-01-31 23:59:59" | while read commit_hash; do  
    echo "Commit: $commit_hash"  
    git show --stat --format="%H %D" $commit_hash  
    echo "---------------------------"  
done

git rebase

可以使用 rebase 命令将提交到某一分支上的所有修改都移至另一分支上,就好像“重新播放”一样。
在这里插入图片描述
      提取在 C4 中引入的补丁和修改,然后在 C3 的基础上应用一次。它的原理是首先找到这两个分支(即当前分支 experiment、变基操作的目标基底分支 master) 的最近共同祖先 C2,然后对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件, 然后将当前分支指向目标基底 C3, 最后以此将之前另存为临时文件的修改依序应用。

      变基使得提交历史更加整洁。 你在查看一个经过变基的分支的历史记录时会发现,尽管实际的开发工作是并行的, 但它们看上去就像是串行的一样,提交历史是一条直线没有分叉。

     变基是将一系列提交按照原有次序依次应用到另一分支上,而合并是把最终结果合在一起。

变基风险: 只对尚未推送或分享给别人的本地修改执行变基操作清理历史, 从不对已推送至别处的提交执行变基操作。如果提交存在于你的仓库之外,而别人可能基于这些提交进行开发,那么不要执行变基。

git reset

git reset 是 Git 版本控制系统中的一个命令,用于重置当前 HEAD 到指定的状态。此命令主要用于撤销提交或更改历史记录

git reset 与 HEAD 一起使用,以撤销最近的提交。例如,git reset HEAD^ 会撤销最近的提交,而 git reset HEAD~3 会撤销最近的三个提交。
在这里插入图片描述
Working Directory: 可以把工作目录当做 沙盒。在你将修改提交到暂存区并记录到历史之前,可以随意更改。
Index: 索引是你的 预期的下一次提交。 我们也会将这个概念引用为 Git 的“暂存区”,这就是当你运行 git commit 时 Git 看起来的样子。
HEAD: HEAD 是当前分支引用的指针,它总是指向该分支上的最后一次提交。 这表示 HEAD 将是下一次提交的父结点。 通常,理解 HEAD 的最简方式,就是将它看做 该分支上的最后一次提交 的快照。

reset :三个模式

1、Soft(默认模式): git reset --soft commit 将 HEAD 重置到指定的 commit,但保留工作目录和暂存区的更改。可以继续进行提交操作,而不会丢失任何更改
在这里插入图片描述

本质是撤销了上一次 git commit 命令。
当再次运行 git commit 时,Git 会创建一个新的提交,并移动 HEAD 所指向的分支来使其指向该提交。 当 reset 回 HEAD~(HEAD 的父结点)时,其实就是把该分支移动回原来的位置,而不会改变索引和工作目录。 可以更新索引并再次运行 git commit 来完成 git commit --amend 所要做的事情
2、 Mixed(或默认不带任何参数):git reset [–mixed] commit HEAD 重置到指定的 commit,同时保留工作目录的更改,但清除暂存区的更改。可以使用 git add 命令再次暂存更改,并进行新的提交
在这里插入图片描述

–mixed 选项,reset 将会在这时停止。 这也是默认行为,所以如果没有指定任何选项, 这就是命令将会停止的地方。
会撤销一上次 提交,还会 取消暂存 所有的东西。 回滚到了所有 git add 和 git commit 的命令执行之前。
3、Hard:git reset --hard commit: 将 HEAD 重置到指定的 commit,同时清除工作目录和暂存区的所有更改。会丢失所有未提交的更改。在执行此操作之前,请确保你确实希望丢弃这些更改
在这里插入图片描述
–hard 选项让工作目录看起来像索引。
–hard 标记是 reset 命令唯一的危险用法,它也是 Git 会真正地销毁数据的仅有的几个操作之一。 其他任何形式的 reset 调用都可以轻松撤消,但是 --hard 选项不能,因为它强制覆盖了工作目录中的文件。 在这种特殊情况下,我们的 Git 数据库中的一个提交内还留有该文件的 v3 版本, 我们可以通过 reflog 来找回它。但是若该文件还未提交,Git 仍会覆盖它从而导致无法恢复。

***reset 命令会以特定的顺序重写这三棵树,在你指定以下选项时停止:

  1. 移动 HEAD 分支的指向 (若指定了 --soft,则到此停止)
  2. 使索引看起来像 HEAD (若未指定 --hard,则到此停止)
  3. 使工作目录看起来像索引***
checkout 和 reset 之间的区别:

和 reset 一样,checkout 也操纵三棵树,不过它有一点不同,这取决于你是否传给该命令一个文件路径。
不带路径
运行 git checkout [branch] 与运行 git reset --hard [branch] 非常相似,它会更新所有三棵树使其看起来像 [branch],不过有两点重要的区别。

首先不同于 reset --hard,checkout 对工作目录是安全的,它会通过检查来确保不会将已更改的文件弄丢。 其实它还更聪明一些。它会在工作目录中先试着简单合并一下,这样所有 还未修改过的 文件都会被更新。 而 reset --hard 则会不做检查就全面地替换所有东西。

第二个重要的区别是 checkout 如何更新 HEAD。 reset 会移动 HEAD 分支的指向,而 checkout 只会移动 HEAD 自身来指向另一个分支。
在这里插入图片描述

带路径
运行 checkout 的另一种方式就是指定一个文件路径,这会像 reset 一样不会移动 HEAD。 它就像 git reset [branch] file 那样用该次提交中的那个文件来更新索引,但是它也会覆盖工作目录中对应的文件。 它就像是 git reset --hard [branch] file(如果 reset 允许你这样运行的话), 这样对工作目录并不安全,它也不会移动 HEAD。

此外,同 git reset 和 git add 一样,checkout 也接受一个 --patch 选项,允许你根据选择一块一块地恢复文件内容。

git blame

如果你在追踪代码中的一个 bug,并且想知道是什么时候以及为何会引入,文件标注通常是最好用的工具。 它能显示任何文件中每行最后一次修改的提交记录。 所以,如果你在代码中看到一个有 bug 的方法,你可以使用 git blame 标注这个文件,查看哪一次提交引入了这行。
用 -L 选项将标注的输出限制到行
例: git blame -L from,to file.name

OAuth

OAuth 2.0 是目前最流行的授权机制,用来授权第三方应用,获取用户数据。

OAuth 就是一种授权机制。数据的所有者告诉系统,同意授权第三方应用进入系统,获取这些数据。系统从而产生一个短期的进入令牌(token),用来代替密码,供第三方应用使用。

令牌与密码:
令牌(token)与密码(password)的作用是一样的,都可以进入系统,但是有三点差异。
(1)令牌是短期的,到期会自动失效,用户自己无法修改。密码一般长期有效,用户不修改,就不会发生变化。
(2)令牌可以被数据所有者撤销,会立即失效。密码一般不允许被他人撤销。
(3)令牌有权限范围(scope)。对于网络服务来说,只读令牌就比读写令牌更安全。密码一般是完整权限。
上面这些设计,保证了令牌既可以让第三方应用获得权限,同时又随时可控,不会危及系统安全。这就是 OAuth 2.0 的优点。
注意: 只要知道了令牌,就能进入系统。系统一般不会再次确认身份,所以令牌必须保密,泄漏令牌与泄漏密码的后果是一样的。 这也是为什么令牌的有效期,一般都设置得很短的原因。

git的 clone 和 fork

fork 的仓库与原仓库独立, clone 的仓库是原仓库的副本
查看原文:https://blog.csdn.net/m0_62986746/article/details/139089479

git clone

GIT_TRACE 和 GIT_TRACE_CURL 是 Git 的环境变量,它们用于控制 Git 命令执行过程中的详细输出。
(1)设置 GIT_TRACE,Git 会在执行命令时输出详细的调试信息。这些信息包括命令执行的各个阶段,以及 Git 内部的一些详细操作。便于诊断 Git 命令的问题。

  • 当设置为 0(默认值)时,不显示任何跟踪信息。
  • 当设置为 1 时,显示基本的跟踪信息,这通常包括命令的执行和一些关键的内部调用。
  • 当设置为 2 时,显示更详细的跟踪信息,包括更多的内部状态和决策过程。
    (2)设置 GIT_TRACE_CURL,Git 会在执行涉及 HTTP/HTTPS 请求的操作时输出详细的 cURL 调试信息。便于诊断网络问题或与远程仓库交互时的问题。
    例: GIT_TRACE=2 GIT_TRACE_CURL=2 git clone xxx.git
  • 当设置为 0(默认值)时,不显示任何跟踪信息。
  • 当设置为 1 时,显示基本的跟踪信息,这通常包括 HTTP 请求和响应的基本信息。
  • 当设置为 2 时,显示更详细的跟踪信息,包括请求和响应的头信息、重定向过程、连接状态等。

参考文章:深入理解 Git 底层实现原理
参考书籍:Pro Git
参考命令:git_toturial

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值