目录及文件介绍
- HEAD 指向当前分支
- config 包含了项目特有的配置选项
- description 仅供 GitWeb 程序使用
- hooks/ 保存客户端或服务端钩子脚本
- index 保存了暂存区域信息
- info/ 目录保存了一份不希望在 .gitignore 文件中管理的忽略模式 (ignored patterns) 的全局可执行文件
- objects/ 存储所有数据内容
- refs/ 存储指向数据 (分支) 的提交对象的指针
对象存储
文件类型
blob: 字符串文件
tree:目录文件
commit:提交文件
文件存储
存储步骤
- header 以对象类型为起始内容构造一个文件头,然后添加一个空格,接着是数据内容的长度,最后是一个空字节 (null byte) example:
>> header = "blob #{content.length}\0"
=> "blob 16\000"
- Git 将文件头与原始数据内容拼接起来,并计算拼接后的新内容的 SHA-1 校验和。可以在 Ruby 中使用 require 语句导入 SHA1 digest 库,然后调用 Digest::SHA1.hexdigest() 方法计算字符串的 SHA-1 值
>> store = header + content
=> "blob 16\000what is up, doc?"
>> require 'digest/sha1'
=> true
>> sha1 = Digest::SHA1.hexdigest(store)
=> "bd9dbf5aae1a3862dd1526723246b20206e5fc37""
- Git 用 zlib 对数据内容进行压缩,在 Ruby 中可以用 zlib 库来实现。首先需要导入该库,然后用 Zlib::Deflate.deflate() 对数据进行压缩
>> require 'zlib'
=> true
>> zlib_content = Zlib::Deflate.deflate(store)
=> "x\234K\312\311OR04c(\317H,Q\310,V(-\320QH\311O\266\a\000_\034\a\235"
- 最后将用 zlib 压缩后的内容写入磁盘。需要指定保存对象的路径 (SHA-1 值的头两个字符作为子目录名称,剩余 38 个字符作为文件名保存至该子目录中)。在 Ruby 中,如果子目录不存在可以用 FileUtils.mkdir_p() 函数创建它。接着用 File.open 方法打开文件,并用 write() 方法将之前压缩的内容写入该文件
>> path = '.git/objects/' + sha1[0,2] + '/' + sha1[2,38]
=> ".git/objects/bd/9dbf5aae1a3862dd1526723246b20206e5fc37"
>> require 'fileutils'
=> true
>> FileUtils.mkdir_p(File.dirname(path))
=> ".git/objects/bd"
>> File.open(path, 'w') { |f| f.write zlib_content }
=> 32
所有的 Git 对象都以这种方式存储,惟一的区别是类型不同 ── 除了字符串 blob,文件头起始内容还可以是 commit 或 tree 。不过虽然 blob 几乎可以是任意内容,commit 和 tree 的数据却是有固定格式的。
commit文件格式
<当前文件类型> <当前文件路径>
parent <前一个commit文件的路径>
author <作者> <作者邮箱> <时间戳>
committer <提交者> <提交者者邮箱> <时间戳>
<提交说明注释>
example:
tree e562d474522fc61bba990332790d847b2d350720
parent e5d491cb9ca52560355b532f9c06cdc5f245eba3
author GTMYang <289135816@qq.com> 1531797414 +0800
committer GTMYang <289135816@qq.com> 1531797414 +0800
first commit
PS: 可以看出来所有commit文件形成一个链表。 commit里的tree文件包含了本次提交所涉及的文件的快照索引。
tree文件格式
example:
040000 tree d8329fc1cc938780ffdd9f94e0d364e0ea74f579 bak
100644 blob fa49b077972391ad58037050f2a75f74e3671e92 new.txt
100644 blob 1f7a7a472abf3dd9643fd615f6da379c4acb3e3a test.txt
Git 对象打包
git中对每个文件的任何细微的修改都会产生一个新的文件(Git 往磁盘保存对象时默认使用的格式叫松散对象格式),如果大文件多次修改会很浪费存储空间。
Git 时不时地将这些对象打包至一个叫 packfile 的二进制文件以节省空间并提高效率。当仓库中有太多的松散对象,或是手工调用 git gc 命令,或推送至远程服务器时,Git 都会这样做。
打包后只有最新版本的保存文件全部内容,前面的版本只存储差异信息。
Git 中的分支
1 分支本质上仅仅是个指向 commit 对象的可变指针
Git 会使用 master 作为分支的默认名字。在若干次提交后,你其实已经有了一个指向最后一次提交对象的 master 分支,它在每次提交的时候都会自动向前移动。
2 多个分支
创建分支命令:
git branch <branch name>
3 当前所在分支
Git 保存着一个名为 HEAD 的特别指针。HEAD 指向你正在工作中的本地分支的指针
切换分支命令:
git checkout <branch name>
切换分支后
4 在工作分支commit
PS: 由于 Git 中的分支实际上仅是一个包含所指对象校验和(40 个字符长度 SHA-1 字串)的文件,所以创建和销毁一个分支就变得非常廉价。说白了,新建一个分支就是向一个文件写入 41 个字节(外加一个换行符)那么简单,当然也就很快了。
4 分支合并
命令
git checkout master // 回到主分支
git merge <branch name> // 合并分支
git branch -d <branch name> // 删除无用分支
Git 常用命令
git init // 初始化新仓库
git add <file name> // 作用1:告诉 Git 开始对文件进行跟踪
// 作用2:把已跟踪的文件放到暂存区
// 作用3:用于合并时把有冲突的文件标记为已解决状态
git add *.c // git add 通配符方式
git add . // .表示所有文件(此方式会将不需要跟踪的文件也添加,不建议使用)
git reset HEAD <file name> // 取消暂存文件
git checkout --<file name> // 取消文件修改
git clone <仓库路径URL> // 克隆仓库
git clone <仓库路径URL> <自定义项目目录名> // 克隆仓库(自定义项目目录名)
git status // 检查文件状态
git diff // 查看尚未暂存的文件更新了哪些部分(当前文件和暂存区域快照之间的差异)
git diff --cached // 查看已经暂存起来的文件和上次提交时的快照之间的差异
git diff --staged // 同 git diff --cached
git commit -m '提交注释' // 提交到本地库(本地创建commit文件)
git commit -a -m '提交注释' // 加-a 选项 Git 就会自动把所有已经跟踪过的文件暂存起来一并提交,从而跳过 git add 步骤
git rm <file name> // 移除文件
git rm --cached <file name> // 除跟踪但不删除文件
git log // 查看提交历史
git log -p -2 // -p 选项展开显示每次提交的内容差异,用 -2 则仅显示最近的两次更新
git log -U1 --word-diff // 单词层面的对比
git log --pretty=oneline // oneline 将每个提交放在一行显示
git remote -v // 显示对应的克隆地址
git remote add <shortname> <url> // 添加一个新的远程仓库
git fetch <remote-name> // 从远程仓库拉数据
git push <remote-name> <branch-name> // 本地仓库中的数据推送到远程仓库。ex: git push origin master
git remote show <remote-name> // 查看某个远程仓库的详细信息 ex: git remote show origin
git tag // 列显已有的标签
git tag -l 'v1.4.2.*' // 搜索列显已有的标签
git tag -a v1.4 -m 'my version 1.4' // 创建一个含附注类型的标签
git tag -a v1.2 9fceb02 // 打标签的时候跟上对应提交对象的校验和(或前几位字符)
// 可以为之前到提交打标签
// 需要先用git log --pretty=oneline 命令查看之前打提交记录
git push origin <tagname> // 把标签推送到远端服务器上
git push origin --tags // 一次推送所有本地新增的标签到远端服务器上
git tag -d <tag name> // 删除一个本地tag
git push origin :refs/tags/<tag name> // 删除一个远程tag