Git 学习(四)操作修改和版本穿梭
Git 学习(四)操作修改和版本穿梭
之前的章节,已介绍了本地Git库创建、暂存区增、删、改,以及提交版本库;可回顾下命令操作: git add 和 git commit。
光有之前章节的操作,Git 显然不能满足版本控制的需求。所谓的版本控制,可理解为文件夹的时间机,即从创建该文件夹伊始,所有文件提交操作都将被记录版本库,且可以随意穿梭版本(回退至昨日的版本,或甚至N年前)。
本文就此具体说明Git是如何管理修改、撤销修改以及在各个版本间穿梭的。
管理修改
为什么Git比其他版本控制系统设计得优秀,因为Git跟踪并管理的是修改,而非文件。为达成这一目的,暂存区存在的意义就在于此。
此外,先依据 .git/index (暂存区文件)中记录的时间戳、长度等信息判断工作区文件是否改变。如果工作区的文件时间戳改变,说明文件的内容可能被改变了,需要要打开文件,读取文件内容,和更改前的原始文件相比较,判断文件内容是否被更改。如果文件内容没有改变,则将该文件新的时间戳记录到 .git/index 文件中。因为判断文件是否更改,使用时间戳、文件长度等信息进行比较要比通过文件内容比较要快的多,所以 Git 这样的实现方式可以让工作区状态扫描更快速的执行,这也是 Git 高效的因素之一。
git/index 实际上就是一个包含文件索引的目录树,像是一个虚拟的工作区。在这个虚拟工作区的目录树中,记录了文件名、文件的状态信息(时间戳、文件长度等),文件的内容并不存储其中,而是保存在 Git 对象库(.git/objects)中,文件索引建立了文件和对象库中对象实体之间的对应。下面这个图展示了工作区、版本库中的暂存区和版本库之间的关系。
工作区、版本库、暂存区 操作原理图
在这个图中,我们可以看到部分 Git 命令是如何影响工作区和暂存区(stage/index)的:
图中左侧为工作区,右侧为版本库。在版本库中标记为 "index" 的区域是暂存区(stage/ndex),标记为 "master" 的是 master 分支所代表的目录树
图中我们可以看出此时 "HEAD" 实际是指向 master 分支的一个“游标”。所以图示的命令中出现 HEAD 的地方可以用 master 来替换
图中的 objects 标识的区域为 Git 的对象库,实际位于 ".git/objects" 目录下
当对工作区修改(或新增)的文件执行 "git add ..." 命令时,暂存区的目录树被更新,同时工作区修改(或新增)的文件内容被写入到对象库中的一个新的对象中,而该对象的ID 被记录在暂存区的文件索引中。
当执行提交操作(git commit)时,暂存区的目录树写到版本库(对象库)中,master 分支会做相应的更新。即 master 指向的目录树就是提交时暂存区的目录树
当执行 "git reset HEAD" 命令时,暂存区的目录树会被重写,被 master 分支指向的目录树所替换,但是工作区不受影响
当执行 "git rm --cached <file>" 命令时,会直接从暂存区删除文件,工作区则不做出改变
当执行 "git checkout -- <file>" 命令时,会将文件在工作区的修改撤销
当执行 "git checkout HEAD ." 或者 "git checkout HEAD <file>" 命令时,会用 HEAD 指向的 master 分支中的全部或者部分文件替换暂存区和以及工作区中的文件。这个命令是极具危险性的,因为不但会清除工作区中未提交的改动,也会清除暂存区中未提交的改动。
上文内容其实是第三章: Git 学习(三)本地仓库操作——git add & commit 工作区与版本库的补充说明,原理图中提及的部分命令还未介绍,可先大致浏览,完全看完下文后再理解上图(该图很重要)。
需要注意:工作区其实与Git库是分离的,我们在工作区进行的修改,如果不add到暂存区,commit提交并不会提交版本(再次重申,commit 提交修改仅针对暂存区)。
这边所说的修改,包括文件本身的修改(删行、加行、改内容等),而创建新文件或删除,也算一个修改。
修改撤销
这边,介绍下暂存区撤销修改的命令:
git checkout -- <file1> <file2> ... 将文件在工作区的修改全部撤销,可多个,空格分隔
git checkout -- file命令中的 "--" 很重要,没有"--",就变成了切换分支的命令,切换分支命令将在以后章节介绍。
举几个具体示例来说明以上命令的用法,初始化空git库,若你先创建了 1.txt 并将其 git add,你在工作区修改了 1.txt 文件,但发现修改错了,需要撤销修改至当时 git add 的情况
git add 时 1.txt 内容为 111(即暂存区),工作区修改为 wrong
git status 可见如下提示
git checkout -- 1.txt 后,再次打开 1.txt,发现其中的内容被撤销修改了,即内容为 111;
若工作区删除了 1.txt,但现在又想撤销删除,也可操作 git checkout -- 1.txt ,此时查看工作区,可发现 1.txt 被恢复了。
版本穿梭
仅仅针对工作区的修改撤销是远远不够的,需要在所有版本库中任意切换;版本是针对 commit 操作而言的,每次 commit 成功后,都会自动生成版本号。
git log 显示当前分支提交版本库的日志
git log 该命令很常用,可显示提交日志信息(当前版本库,可加参数,help查询具体);上图可见茶色字体显示了一大串类似 的是commit id(版本号),和SVN不一样,Git的commit id不是1,2,3……递增的数字,而是一个SHA1计算出来的一个非常大的数字,用十六进制表示。
每提交一个新版本,实际上Git就会把它们自动串成一条时间线。如果使用可视化工具查看Git历史,就可以更清楚地看到提交历史的时间线:
git reset --hard <revision> 重置至某一版本(强制,暂存区和工作区均重置)
必须知道当前版本是哪个版本,在Git中,用HEAD表示当前版本,也就是最新的提交,上一个版本就是HEAD^,上上一个版本就是HEAD^^ ...
如上,我们要把当前版本回退到最新版本,就可以使用命令行 git reset --hard ,若使用命令行 git reset --hard HEAD^,则回滚为上一版本,此时 git log 仅显示了当前版本及其之前的信息
命令可输入版本号,前几位即可(通常前7位);显然,若回滚了昨天的版本后,又反悔了的这种情况还是时有发生的,这时,就需要输版本号了;然则,版本号忘了怎么办。。这时,需要另一个命令帮助:
git reflog 显示操作的日志
该命令可显示操作日志,且显示了对应的版本号及信息,可查询到之前的版本号并再次回滚, 如 git reset --hard 6de39b3 。
Git的版本回退速度非常快,因为Git在内部有个指向当前版本的HEAD
指针,当你回退版本的时候,Git仅仅是把HEAD指向改变
改为指向 2
:
然后顺便把工作区、暂存区文件更新了。
出处:http://www.cnblogs.com/feeland/
本博客内容大多为作者原创 如果您想转载本博客,请注明出处
如果您对本文有意见或者建议,欢迎留言
感谢您的阅读,请关注我的后续博客
如果您看了本篇博客,觉得对您有所收获,请点击右下角的 [推荐]