Git汇总--对象及版本库存储

阅读完内容,你会很快的解决下面问题,并了解其底层原理。

问题1:如何丢弃本地工作区修改的内容?

$ git checkout -- <filename>

问题2:如何丢弃本地工作区和暂存区修改的内容?

$ git checkout HEAD <filename>

问题3:误删了文件且已经提交推送到远程仓库,如何恢复?

# 查看远程前一次提交的文件树 支持管道过滤 | grep <filename>
$ git ls-files --with-tree=origin/HEAD^
# 查看前一次提交的指定文件内容 > welcome.txt
$ git cat-file -p origin/HEAD^:<filename>
# 当然,也可以采用reflog形式

问题4:如何忽略某文件?如何只让其本地生效?

.git/info/exclude中配置

简介

Git作者Linus Torvalds,其是一款分布式版本控制系统。

  • CVS:集中式版本控制系统。CVS采用客户端/服务器架构设计,版本库位于服务器端,实际上就是一个RCS文件容器。每一个RCS文件以“.v”作为文件名后缀,用于保存对应文件的历次更改历史。RCS文件中只保留一个版本的完全拷贝,其他历次更改仅将差异存储其中,使得存储变得更加高效。每个文件都拥有各自独立的版本号。
  • SVN:集中式版本控制系统。拥有全局版本号,每提交一次,SVN的版本号就会自动加一。利用轻量级拷贝,SVN在不同的名字空间下创建不同的目录实现里程碑和分支的创建,轻松地解决了CVS中存在的里程碑、分支创建速度慢又不可见的问题。SVN还有一个突破,就是在工作区跟踪目录(.svn目录)下为当前目录中的每一个文件都保存一份冗余的原始拷贝(工作区的根目录和每一个子目录下都有一个.svn目录)。这样做的好处一个是提高了网络的效率,在提交时仅传输变更差异,另外一个好处是部分操作不再需要网络连接,如本地修改的差异比较,以及本地更改的回退等。
  • Git:分布式版本控制系统。每个人都拥有一个完整的版本库。分布式版本控制系统的几乎所有操作包括查看提交日志、提交、创建里程碑和分支、合并分支、回退等都直接在本地完成而不需要网络连接。协同工作模型(版本库间推送、拉回,及补丁文件传送等)让开源项目的参与度有爆发式增长。

Git对象

git init 会创建一个 .git 目录。这个目录包含了几乎所有 Git 存储和操作的对象。 如若想备份或复制一个版本库,只需把这个目录拷贝至另一处即可。

$ ll .git
-rw-r--r--    1 ligang  staff     6B 11  1 10:13 COMMIT_EDITMSG
-rw-r--r--    1 ligang  staff   212B 10 31 10:02 FETCH_HEAD
-rw-r--r--    1 ligang  staff    23B  8 21 15:23 HEAD
-rw-r--r--    1 ligang  staff    41B 10 31 10:02 ORIG_HEAD
drwxr-xr-x    2 ligang  staff    64B  8 15  2017 branches
-rw-r--r--    1 ligang  staff   311B  8 15  2017 config
-rw-r--r--    1 ligang  staff    73B  8 15  2017 description
drwxr-xr-x   31 ligang  staff   992B  5 10 14:44 hooks
-rw-r--r--    1 ligang  staff   6.9M 11  1 10:13 index
drwxr-xr-x    4 ligang  staff   128B  5  9 16:11 info
drwxr-xr-x    4 ligang  staff   128B  5  9 16:11 logs
drwxr-xr-x  152 ligang  staff   4.8K 11  1 10:13 objects
-rw-r--r--    1 ligang  staff   166B  5  9 16:11 packed-refs
drwxr-xr-x    6 ligang  staff   192B  7  3 09:53 refs

-rw-r–r--:第一位用于标识文件类型,d表示目录、l表示连结文件、-表示文件;其他表示系统用户权限rw-rw-(Owner)r–(Group)r–(Other)【r可读、w可写、x可执行】

版本库位于工作区根目录下的.git目录中,且仅此一处,在工作区的子目录下则没有任何其他跟踪文件或目录。Git的这种设计,将版本库放在工作区根目录下,所有的版本控制操作(除了和其他远程版本库之间的互操作)都在本地即可完成。

$ git log -1 --pretty=raw
# 本次提交的唯一标识
commit b93afd2cce7e065dd4e7c33d1c6a4b3a7a75b259
# 本次提交所对应的目录树
tree 1e0c5cb85e1d2b4ff6875a5bbaa9183389ace668
# 本地提交的父提交(上一次提交)
parent 3365948518aad171336a52674cbdf0450679b4dc
author ligang <381510688@qq.com> 1543761152 +0800
committer ligang <381510688@qq.com> 1543761152 +0800

    feat(git): git 汇总

研究Git对象ID的一个重量级武器就是git cat-file命令。

查看一下这三个ID的类型:

$ git cat-file -t b93afd2cce7e065dd4e7c33d1c6a4b3a7a75b259
commit
$ git cat-file -t 1e0c5cb85e1d2b4ff6875a5bbaa9183389ace668
tree
$ git cat-file -t 3365948518aad171336a52674cbdf0450679b4dc
commit

查看对象的内容:

$ git cat-file -p <commitID>

Git 有一个底层命令git rev-parse 可以用于显示引用对应的提交ID

$ git rev-parse master
b93afd2cce7e065dd4e7c33d1c6a4b3a7a75b259
$ git rev-parse refs/heads/master
b93afd2cce7e065dd4e7c33d1c6a4b3a7a75b259
$ git rev-parse HEAD
b93afd2cce7e065dd4e7c33d1c6a4b3a7a75b259

可以看出它们都指向同一个对象!

  • 显示版本库.git 目录所在的位置
$ git rev-parse --git-dir
/Users/ligang/Documents/github/practice/.git
  • 显示工作区根目录
$ git rev-parse --show-toplevel
/Users/ligang/Documents/github/practice

git rev-parse 是Git的一个底层命令,其功能非常丰富(或者说杂乱),很多Git脚本或工具都会用到这条命令。

  • 显示分支 $ git rev-parse --symbolic --branches
  • 显示tags $ git rev-parse --symbolic --tags
  • 显示HEAD对应的SHA1哈希值 $ git rev-parse HEAD

版本库存储

本地(工作区、暂存区、HEAD)

说明

工作区

Git暂存区(stage,或称为index)

HEAD(当前分支,注意非远程)

  • HEAD实际是指向master分支的一个“游标”,HEAD全部可以使用master替换
  • objects为Git的对象库,位于 .git/objects 目录下;
  • 工作区 <==> 暂存区 命令 说明 git add ./<filename> 将工作区变更提交到暂存区 git checkout ./<filename> git checkout -- <filename> 暂存区内容覆盖工作区 git rm --cached <file> 直接从暂存区删除文件,工作区则不做出改变
  • 暂存区 <==> HEAD 命令 说明 git commit -s -m "" 将暂存区提交到master分支 (即master指向的目录树就是原暂存区的目录树) git reset HEAD ./<filename> 使用master指向的目录树替换缓存区 git checkout HEAD ./<filename> 用HEAD指向的master分支内容替换暂存区及工作区的文件

重点来了! 如何还原本地工作区某文件

$ git reset HEAD <filename>
$ git checkout <filename>

# 替换命令
$ git checkout HEAD <filename>

本地(stash)

git stash 保存当前工作进度,会分别对暂存区和工作区的状态进行保存。

$ git stash [save [–patch] [-k|–[no-]keep-index] [-q|–quiet] [<message>]]
  • save "message..." 保存工作进度时使用指定说明
  • --patch 会显示工作区和HEAD的差异,通过对差异文件的编辑决定在进度中最终要保存的工作区的内容
  • -k或者--keep-index参数,在保存进度后不会将暂存区重置。缺省会将暂存区和工作区强制重置!

注意: 本地没有被版本控制系统跟踪的文件并不能保存进度,即新创建文件需要 git add

恢复工作进度,可以通过下述命令:

$ git stash apply [–index] [<stash>]
$ git stash drop [<stash>]
# 等价于上述两条命令
$ git stash pop [–index] [<stash>]

远程(remote)

$ cd .git/refs/remotes
$ ll
drwxr-xr-x   5 ligang  staff   160B 11 28 10:42 origin
drwxr-xr-x  11 ligang  staff   352B 11 27 00:11 upstream
$ ll origin 
-rw-r--r--  1 ligang  staff    32B 11  8 09:43 HEAD
-rw-r--r--  1 ligang  staff    41B 11 28 10:42 feature-v2.1
-rw-r--r--  1 ligang  staff    41B 11 28 10:42 master
$ ll upstream
-rw-r--r--  1 ligang  staff    41B 11 27 00:11 develop
-rw-r--r--  1 ligang  staff    41B 11 27 00:11 master
-rw-r--r--  1 ligang  staff    41B 11 27 00:11 themes
$ cat origin/develop
eeaa2013d901bda74eaa9fe102abe1e474b7a5d6
$ git ls-tree eeaa2013d901bda74eaa9fe102abe1e474b7a5d6

Git 这样的设计是非常巧妙的,在向远程版本库执行获取操作时,不是把远程版本库的分支原封不动地复制到本地版本库的分支中,而是复制到其命名空间中。如在克隆一个版本库时,会将远程分支都复制到目录 .git/refs/remotes/origin/ 下。这样向不同的远程版本库执行获取操作,因为远程分支相互隔离,所以就避免了相互的覆盖。

  • 添加新远程版本库
$ git remote add remote-name git@x.x.x.x:project-namespace/project-name.git
$ git remote -v

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

扫码关注云+社区

领取腾讯云代金券