前言: plumbing命令 和 porcelain命令

git中的命令分为plumbing命令和porcelain命令:

  • porcelain命令就是我们常用的git add,git commit等命令
  • plumbing命令可以理解为更底层的命令,实际上一个porcelain命令可以由若干个plumbing命令完成(见下文),plumbing命令可以帮助我们了解git底层的工作原理

阅读本文还需要了解.git目录的结构功能,以及git中的对象(commit对象、tree对象、blob对象等等)等概念,请自行参考其他文档。

1. git add

git add file在底层实际上完成了3个步骤:

  1. 根据文件内容计算SHA-1值
  2. 将文件内容存储到仓库的数据库中(.git/objects)
  3. 将文件内容注册到.git/index文件中

下面用plumbing命令完成git add的功能:

------------------------------------------------------------
~ » git init demo
Initialized empty Git repository in /home/lvhao/demo/.git/
------------------------------------------------------------
~ » cd demo
------------------------------------------------------------
~/demo(master) » echo "nihao" >> nihao.txt
------------------------------------------------------------
~/demo(master*) » git status
On branch master No commits yet Untracked files:
(use "git add <file>..." to include in what will be committed) nihao.txt nothing added to commit but untracked files present (use "git add" to track)
~/demo(master*) » git hash-object -w nihao.txt     # 对应于步骤1和2,-w表示写
ba9c1ad3d3b761e84cd8f9e9a18404f6f2552fcf

这时候查看.git/objects目录:

~/demo(master*) » tree .git/objects
.git/objects
├── ba
│   └── 9c1ad3d3b761e84cd8f9e9a18404f6f2552fcf
├── info
└── pack 3 directories, 1 file
~/demo(master*) » git update-index --add --info-only nihao.txt # 对应于步骤3(关于两个参数请参见git help update-index)

上面的update-index,就是更新.git/index文件,有关该文件的详细格式请参考https://stackoverflow.com/questions/4084921/what-does-the-git-index-contain-exactly。

该index文件主要包含了存在于暂存区(即index)中文件条目,可以通过git ls-files --stage来查看该文件的主要内容:

~/demo(master*) » git ls-files --stage
100644 ba9c1ad3d3b761e84cd8f9e9a18404f6f2552fcf 0 nihao.txt

这时候,nihao.txt已经存在于暂存区(即index),所以说上面的三个步骤相当于git add nihao.txt

~/demo(master*) » git status
On branch master No commits yet Changes to be committed:
(use "git rm --cached <file>..." to unstage) new file: nihao.txt

总结:

porcelain命令:

git add file

等同于plumbing命令:

git hash-object -w file
git update-index --add --info-only file

2. git commit

commit所做的工作实际上是对当前的工作树(working tree)拍一个”快照“然后保存起来,git commit在底层实际上完成的步骤:

  1. 根据当前的工作树生成一个tree对象(tree对象记录了当前工作目录的结构,保存有对当前版本的文件的引用),将tree对象保存到.git/objects下
  2. 由上述tree对象生成一个commit对象,(可选)并指明该commit对象的parent、message等信息
  3. 移动分支到新生成的commit对象
# 继续上面的代码
~/demo(master*) » git write-tree # 即步骤1
8e335a3e0ffa15ff97acc7f4d97e03d63612ec7a

让我们查看一下这个tree对象,可见它保存了对当前工作目录下文件/目录的引用:

~/demo(master*) » git cat-file -p 8e335a
100644 blob ba9c1ad3d3b761e84cd8f9e9a18404f6f2552fcf nihao.txt
~/demo(master*) » git commit-tree -m "Initial Commit" 8e335a    # 即步骤2
1f1fbcf8ff46d8d2548372c38cf3f1eabf8bfbc8 # 新生成的commit的SHA-1

这时候我们查看状态,还是待commit状态,这是因为commit-tree仅仅是生成了一个新的commit对象,并不会导致HEAD及分支的移动:

~/demo(master*) » git status
On branch master No commits yet Changes to be committed:
(use "git rm --cached <file>..." to unstage) new file: nihao.txt

为了利用当前的分支,我们需要修改分支的引用,git update-ref refs/heads/分支名 commit-hash命令来更新分支的位置到刚刚创建的commit:

~/demo(master*) » git update-ref refs/heads/master 1f1fbc  # 即步骤3

由于.git/HEAD默认就是指向master分支,所以得到如下结果:

~/demo(master) » git status
On branch master
nothing to commit, working tree clean

查看一下提交日志:

~/demo(master) » git log --graph --oneline
* 1f1fbcf (HEAD -> master) Initial Commit

总结:

porcelain命令的:

git commit -m "xxx" file

等同于plumbing命令的:

git write-tree # 得到tree的hash: tree-hash
git commit-tree -m "xxx" (-p parent-hash) tree-hash # 得到commit的hash,commit-hash
git update-ref refs/heads/分支名 commit-hash

使用plumbing命令来深入理解git add和git commit的工作原理的更多相关文章

  1. 为什么要先 git add 才能 git commit

    1. git 的 add ,是一个容易引起疑问的命令.在 subversion 中的 svn add 动作是将某个文件加入版本控制,而 git add的意义完全不同. 同时, git diff --c ...

  2. git add -A /git add -u/git add .的用法

    git的指令详解 在git中有好多的指令,但是今天这几个指令就很容易忘记而且还容易混淆 git add -u <==> git add –update 提交所有被删除和修改的文件到数据暂存 ...

  3. git add && git add -u && git add -A

    git add将当前工作目录中更改或者新增的文件加入到Git的索引中,加入到Git的索引中就表示记入了版本历史中,这也是提交之前所需要执行的一步.可以递归添加,即如果后面跟的是一个目录作为参数,则会递 ...

  4. git add . git add -u git add -A命令区别图解

    git版本不同会有所区别: Git Version 1.x:  Git Version 2.x:  git add .  修改(modified)以及新文件(new),但不包括被删除的文件. git ...

  5. git add和git commit

    git命令使用:提交前可指定要提交哪些文件,然后使用git commit来提交 样例: git status 输出: Changes to be committed: modified:   app/ ...

  6. git add , git commit 添加错文件 撤销

    1. git add 添加 多余文件 这样的错误是由于, 有的时候 可能 git add . (空格+ 点) 表示当前目录所有文件,不小心就会提交其他文件 git add 如果添加了错误的文件的话 撤 ...

  7. git的常用指令(二) git add -A 、git add . 和 git add -u

    git add . :他会监控工作区的状态树,使用它会把工作时的所有变化提交到暂存区,包括文件内容修改(modified)以及新文件(new),但不包括被删除的文件. git add -u :他仅监控 ...

  8. 在dev分支上修改了文件,但是并没有执行git add. 和git commit命令,然后切换到master分支,仍然能看到dev分支的改动现象

    当我们创建一个新的分支dev,并且在新分支上修改了原文件,在我们没有提交到仓库的前提下,将分支再切换到master分支上,执行git status ,可以看到dev操作的状态: (1)因为未add的内 ...

  9. Docker镜像提交命令commit的工作原理和使用方法

    在本地创建一个容器后,可以依据这个容器创建本地镜像,并可把这个镜像推送到Docker hub中,以便在网络上下载使用. 下面我们来动手实践. docker pull nginx:1.15.3 用命令行 ...

随机推荐

  1. [原创]使MySQL注释语句在后台能够输出的方法

    开启general log或slow log的时候,前端发出的sql语句中的注释都别屏蔽掉了. 本意加注释我们想通过注释来快速知道sql是由哪个业务模块发出的.这点对dba和研发很有帮助. 一种变通的 ...

  2. jQuery validate插件,自动验证无效的原因及解决方法归纳

    最近在使用validate插件进行验证的时候,出现有的控件在个别事件(比如keydown.foucs.onchange等)下不能自动验证,而有的控件却又正常,当时觉得很诡异,后来仔细测试查看,归纳原因 ...

  3. 【转】Nginx学习---深入浅出Nginx的介绍

    [原文]https://www.toutiao.com/i6595428119933354500/ Nginx是一款轻量级的Web服务器.反向代理服务器,由于它的内存占用少,启动极快,高并发能力强,在 ...

  4. PyQt5--Signal&Slot

    # -*- coding:utf-8 -*- ''' Created on Sep 14, 2018 @author: SaShuangYiBing ''' import sys from PyQt5 ...

  5. Docker技术入门与实战 第二版-学习笔记-5-容器-命令及限制内存与cpu资源

    1.启动容器 启动容器有两种方式: 基于镜像新建一个容器并启动 将在终止状态(stopped)的容器重新启动 1)新建并启动——docker run 比如在启动ubuntu:14.04容器,并输出“H ...

  6. 青岛大学开源OJ平台搭建

    源码地址为:https://github.com/QingdaoU/OnlineJudge 可参考的文档为:https://github.com/QingdaoU/OnlineJudgeDeploy/ ...

  7. Python2.7-copy

    copy 模块,python 中的‘=’是使左边的对象成为右边对象的一个引用,对不可变对象(如数字,字符串等)使用‘=’一般不会出现问题,但当对字典,列表等可变对象进行‘=’操作时,要注意修改其中一个 ...

  8. CentOS中配置CDH版本的ZooKeeper

    三台CentOS:Host0,Host1,Host2 在三台中分别安装zookeeper-server yum install zookeeper-server -y 修改zookeeper的配置文件 ...

  9. C++之强制类型转化

    在C++语言中新增了四个关键字static_cast.const_cast.reinterpret_cast和dynamic_cast.这四个关键字都是用于强制类型转换的.我们逐一来介绍这四个关键字. ...

  10. day62

    一.组件 组件都具有模板,template new Vue()创建的是根组件 组件与实例一一对应,创建一个实例就是创建了一个组件,同理创建一个组件就相当于创建了一个实例 根组件的挂载点一般就是根组件的 ...