Pro Git 阅读笔记
ProGit 阅读笔记
Git 由来
版本控制
版本控制是一种记录若干文件内容变化,以便将来查阅特定版本修订情况的系统。
集中化的版本控制系统
如 CVS, Subversion 等,都有一个单一的集中管理的服务器,保存所有文件的修订版本,客户端通过客户端连接到服务器,取出最新文件或者提交更新。
分布式版本控制系统
如 Git, Bazaar 等,客户端并不只是提取最新版本的文件快照,而是把原始的代码仓库完整地镜像下来。这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的
本地仓库恢复。因为每一次的提取操作,实际上都是一次对代码仓库的完整备份)。
Git 相比于 svn 等的特点
- 直接快照,而非比较差异,为提高性能。
近乎所有操作都可本地执行。
由于其分布式的特点,每个客户端都保存着仓库的备份,在 Git 中的绝大多数操作都只需要访问本地文件和资源,不用连网,如提交更新。
时刻保持数据完整性。
在保存到Git 之前,所有数据都要进行内容的校验和(checksum)计算,并将此结果作为数据的唯一标识和索引。如果文件在传输时变得不完整,或者磁盘损坏导致文件数据缺失,Git 都能立即察觉。Git 使用SHA-1 算法计算数据的校验和,通过对文件的内容或目录的结构计算出一个SHA-1 哈希值,作为指纹字符串。
三种状态
对于任何一个文件,在 Git 内都只有三种状态:已提交
(committed),已修改(modified)和已暂存(staged)。
Git 管理项目时,文件流转的三个工作区域:Git 的本地数据目录,工作目录以暂存区
域。
Git 基础
取得项目的 Git 仓库
从当前目录初始化:
从现有仓库克隆:
克隆某个分支:
记录每次更新到仓库
工作目录下面的所有文件都不外乎这两种状态:已跟踪或未跟踪。
已跟踪的文件是指本来就被纳入版本控制管理的文件,在上次快照中有它们的记录,工作一段时间后,它们的状态可能是未更新,已修改或者已放入暂存区。而所有其他文件都属于未跟踪文件。它们既没有上次更新时的快照,也不在当前的暂存区域。
检查当前文件状态:
跟踪新文件以及将跟踪后更新后暂存:
要暂存这次更新,需要运行git add 命令(这是个多功能命令,根据目标文件的状态不同,此命令的效果也不同:可以用它开始跟踪新文件,或者把已跟踪的文件放到暂存区,还能用于合并时
把有冲突的文件标记为已解决状态等)
忽略某些文件,编辑 .gitignore 文件:
查看已暂存和未暂存的更新:
提交更新:
移除文件,即从已跟踪文件清除:
如果删除之前修改过并且已经放到暂存区域的话,则必须
要用强制删除选项-f(译注:即force 的首字母),以防误删除文件后丢失修改的内容。
把文件从Git 仓库中删除(亦即从暂存区域移除),但仍然希望保留在当前工作目录中。用–cached 选项即可。
查看历史提交
回顾提交历史:
-2 仅显示最近的两次更新:
git log 还有其他一些选项,方便查看内容。
撤销操作
撤消刚才的提交操作,重新提交:
取消已经暂存的文件,不小心用 git add * 全加到了暂存区域。该如何撤消暂存其中的一个文件呢?取消暂存benchmarks.rb 文件:
取消对文件的修改,回到之前的状态,与上面的相比,该命令取消了未暂存的文件修改:
远程仓库的使用
查看当前的远程库:
添加远程仓库:
从远程仓库抓取数据:
有一点很重要,
需要记住,fetch 命令只是将远端的数据拉到本地仓库,并不自动合并到当前工作分支,只有当你确实准备好
了,才能手工合并,git pull 相当于 git fetch 和 git merge。
推送数据到远程仓库:
重命名远端仓库:
删除远端仓库:
Git 分支
何谓分支
在Git 中提交时,会保存一个提交(commit)对象,它包含一个指向暂存内容快照的指针,作者和相关附属信息,以一定数量(也可能没有)指向该提交对象直接祖先的指针:第一次提交是没有直接祖先的,普通提交有一个祖先,由两个或多个分支合并产生的提交则有多个祖先。
Git 中的分支,其实本质上仅仅是个指向 commit 对象的可变指针。
创建 test 分支:
会在当前 commit 对象上新建一个分支指针。
切换到新建的 testing 分支:
基本的分支与合并
在 master 上与 hotfix 分支合并:
上述方式的合并叫快进。
master 和 iss53 的合并叫合并提交:
有时合并的时候可能会遇到冲突,任何包含未解决冲突的文件都会以未合并(unmerged)状态列出。
可以看到======= 隔开的上半部分,是 HEAD (即 master 分支,在运行 merge 命令时检出的分支)中的内容,下半部分是在 iss53 分支中的内容。解决冲突的办法无非是二者选其一或者由你亲自整合到一起。
解决了所有文件里的所有冲突后,运行 git add 将把它们标记为已解决(resolved)。因为一旦暂存,就表示冲突已经解决。
分支管理
给出当前所有分支的清单:
要从该清单中筛选出你已经(或尚未)与当前分支合并的分支,可以用–merge 和–no-merged 选项,比如git branch -merge 查看哪些分支已被并入当前分支:
远程分支
远程分支(remote branch)是对远程仓库状态的索引。它们是一些无法移动的本地分支;只有在进行 Git 的网络活动时才会更新。
(远程仓库名)/(分支名) 这样的形式表示远程分支。
可以运行 git fetch origin 来进行同步。该命令首先找到origin 是哪个服务器,从上面获取你尚未拥有的数据,更新你本地的数据库,然后把 origin/master 的指针移到它最新的位置。
如果你有个叫 serverfix 的分支需要和他人一起开发,可以运行git push (远程仓库名) (分支名):
|
|
它的意思是“提取我的 serverfix 并更新到远程仓库的serverfix”。通过此语法,你可以把本地分支推送到某个命名不同的远程分支:若想把远程分支叫作 awesomebranch,可以用git push origin serverfix:awesomebranch 来推送数据。
删除远程分支,git push [远程名] :[分支名]。
衍合
把一个分支整合到另一个分支的办法有两种:merge(合并) 和 rebase(衍合)。
衍合(rebase),有了rebase 命令,就可以把在一个分支里提交的改变在另一个分支里重放一遍。
它的原理是回到两个分支(你所在的分支和你想要衍合进去的分支)的共同祖先,提取你所在分支每次提交时产生的差异(diff),把这些差异分别保存到临时文件里,然后从当前分支转换到你需要衍合入的分支,依序施用每一个差异补丁文件。
|
|
衍合的风险:永远不要衍合那些已经推送到公共仓库的更新。
服务器上的 Git
协议
Git 可以使用四种主要的协议来传输数据:本地传输,SSH 协议,Git 协议和HTTP 协议。
SSH 协议
SSH 是唯一一个同时便于读和写操作的网络协议。SSH 同
时也是一个验证授权的网络协议。
通过SSH 克隆一个Git 仓库:
Git 默认使用 SSH。
优点:
* 首先,如果你想拥有对网络仓库的写权限,基本上不可能不使用 SSH 。
* 再次,通过 SSH 进行访问是安全的——所有数据传输都是加密和授权的。
缺点:
* SSH 的限制在于你不能通过它实现仓库的匿名访问。即使仅为读取数据,人们也必须在能通过 SSH 访问主机的前提下才能访问仓库,这使得 SSH 不利于开源的项目。
Git 协议
这是一个包含在 Git 软件包中的特殊守护进程; 它会监听一个提供类似于 SSH 服务的特定端口(9418)
优点:
* Git 协议是现存最快的传输协议,因为省去了加密和授权的开销。
缺点:
* Git 协议消极的一面是缺少授权机制。用 Git 协议作为访问项目的唯一方法通常是不可取的。一般做法是,同时提供SSH 接口,让几个开发者拥有推送(写)权限,其他人通过git:// 拥有只读权限。
HTTP/S 协议
HTTP 或 HTTPS 协议的优美之处在于架设的简便性。基本上, 只需要把 Git 的纯仓库文件放在 HTTP 的文件根目录下,配置一个特定的 post-update 挂钩(hook),就搞定了。
克隆仓库:
优点:
* 使用 HTTP 协议的好处是易于架设。几条必要的命令就可以让全世界读取到仓库的内容。
缺点:
* 相对来说客户端效率更低。克隆或者下载仓库内容可能会花费更多时间,而且 HTTP 传输的体积和网络开销比其他任何一个协议都大。因为它没有按需供应的能力——传输过程中没有服务端的动态计算——因而 HTTP 协议经常会被称为傻瓜(dumb) 协议。
分布式 Git
储藏
经常有这样的事情发生,当你正在进行项目中某一部分的工作,里面的东西处于一个比较杂乱的状态,而你想转到其他分支上进行一些工作。问题是,你不想提交进行了一半的工作,否则以后你无法回到这个工作点。解决这个问题的办法就是git stash命令。
现在你想切换分支,但是你还不想提交你正在进行中的工作;所以你储藏这些变更。为了往堆栈推送一个新的储藏,只要运行git stash:
|
|
要查看现有的储藏,你可以使用git stash list:
你可以重新应用你刚刚实施的储藏:
apply 选项只尝试应用储藏的工作——储藏的内容仍然在栈上。要移除它,你可以运行git stash drop:
你也可以运行git stash pop 来重新应用储藏,同时立刻将其从堆栈中移走。
重写历史
改变最近一次提交:
修改多个提交说明:
如果你想修改或改变说明、增加文件、删除文件或任何其他事情。你可以通过给 git rebase 增加 -i 选项来以交互方式地运行 rebase。你必须通过告诉命令衍合到哪次提交,来指明你需要重写的提交的回溯深度。
修改最近三次提交,范围内的每一次提交都会被重写,无论你是否修改说明:
可以使用交互式的衍合来彻底重排或删除提交。
重排提交:
改为:
压制(Squashing)提交:
当你保存并退出编辑器,Git 会应用全部三次变更然后将你送回编辑器来归并三次提交说明。
拆分提交就是撤销一次提交,然后多次部分地暂存或提交直到结束。
将“updated README formatting and added blame”拆分成两次提交:第一次为“updated
README formatting”,第二次为“added blame”。你可以在rebase -i脚本中修改你想拆分的提交前的指令
为“edit”。
拆分提交:
|
|
衍合与挑拣
另一个引入代码的方法是挑拣。挑拣类似于针对某次特定提交的衍合。它首先提取某次提交的补丁,然后试着应用在当前分支上。如果某个特性分支上有多个 commits,但你只想引入其中之一就可以使用这种方法。
|
|