阅读完内容,你会很快的解决下面问题,并了解其底层原理。
问题1:如何丢弃本地工作区修改的内容?
代码语言:javascript复制$ git checkout -- <filename>
问题2:如何丢弃本地工作区和暂存区修改的内容?
代码语言:javascript复制$ git checkout HEAD <filename>
问题3:误删了文件且已经提交推送到远程仓库,如何恢复?
代码语言:javascript复制# 查看远程前一次提交的文件树 支持管道过滤 | 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的类型:
代码语言:javascript复制$ git cat-file -t b93afd2cce7e065dd4e7c33d1c6a4b3a7a75b259
commit
$ git cat-file -t 1e0c5cb85e1d2b4ff6875a5bbaa9183389ace668
tree
$ git cat-file -t 3365948518aad171336a52674cbdf0450679b4dc
commit
查看对象的内容:
代码语言:javascript复制$ 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分支内容替换暂存区及工作区的文件
代码语言:javascript复制重点来了! 如何还原本地工作区某文件
$ 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
。
恢复工作进度,可以通过下述命令:
代码语言:javascript复制$ git stash apply [–index] [<stash>]
$ git stash drop [<stash>]
# 等价于上述两条命令
$ git stash pop [–index] [<stash>]
远程(remote)
代码语言:javascript复制$ 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