本文是读《progit_v2.1.16》(提取码: vdyx )一书的学习笔记。
用复制整个项目目录的方式来保存不同版本,例如写论文;
坏处:有时会混淆工作目录,可能写错或覆盖意想外的文件。
有一个单一的集中管理的服务器,保存所有文件的修订版本。协同工作的人(客户端)取出最新的文件或提交更新。
好处:相对于本地版本控制,每个人可以一定程度上看到其他人在做什么,管理员也能轻松掌握开发者的权限。
坏处:中央服务器的单点故障。如果宕机一小时,这一小时内都无法提交更新,也无法协同工作;如果磁盘损坏,又没有备份,那将丢失所有数据,只剩各机器上的单独快照。
如Git。客户端不止提取最新文件快照,而是把代码仓库完整地镜像下来。也是一次完整备份。还可以指定和若干不同的代码仓库进行交互。
其他系统:以文件变更列表的方式存储信息,存储每个文件与初始版本的差异。
Git:把数据看作是对小型文件系统的一组快照,并保存快照的索引。
因为在本地磁盘就有项目的完整历史,所以大部分操作看起来瞬间完成,少了集中式版本控制的网络延时开销。所以离线也能工作、提交,等到有网络时再上传。
Git 中所有数据在存储前都计算校验和,然后以校验和来引用。 这意味着不可能在 Git 不知情时更改任何文件内 容或目录内容。 这个功能建构在 Git 底层,是构成 Git 哲学不可或缺的部分。 若你在传送过程中丢失信息或损坏 文件,Git 就能发现。
Git 用以计算校验和的机制叫做 SHA-1 散列(hash,哈希)。 这是一个由 40 个十六进制字符(0-9 和 a-f)组 成字符串,基于 Git 中文件的内容或目录结构计算出来。 SHA-1 哈希看起来是这样:
24b9da6552252987aa493b52f8696cd6d3b00373
实际上,Git 数据库中保存的信息都是以文件内容的哈希值来索引,而不是文件名。
你执行的 Git 操作,几乎只往 Git 数据库中增加数据。 很难让 Git 执行任何不可逆操作,或者让它以任何方式清 除数据。 同别的 VCS 一样,未提交更新时有可能丢失或弄乱修改的内容;但是一旦你提交快照到 Git 中,就难 以再丢失数据,特别是如果你定期的推送数据库到其它仓库的话。
Git 有三种状态,你的文件可能处 于其中之一:已提交(committed)、已修改(modified)和已暂存(staged)。 已提交表示数据已经安全的 保存在本地数据库中。 已修改表示修改了文件,但还没保存到数据库中。 已暂存表示对一个已修改文件的当前 版本做了标记,使之包含在下次提交的快照中。
由此引入 Git 项目的三个工作区域的概念:Git 仓库、工作目录以及暂存区域。
以示某次提交的重要,比如标记发布节点(v1.0)
Git 使用两种主要类型的标签:轻量标签(lightweight)与附注标签(annotated)。
轻量标签:很像一个不会改变的分支 - 它只是一个特定提交的引用。附注标签:是存储在 Git 数据库中的一个完整对象。通常建议创建附注标签。
> git tag // 列出标签 > git tag -l 'v1.8.5*' //查找特定标签 > git tag -a v1.4 -m 'my version 1.4' // 创建一个附注标签。-m 选项指定了一条将会存储在标签中的信息。如果没有为附注标签指定一条信息,Git会运行编辑器要求你输入信息。 > git show v1.4 // 显示标签信息与对应的提交信息 > git tag -a v1.2 9fceb02 -m "" // 为某次提交打标签 > git push origin v1.5 // 默认情况下,git push 命令并不会传送标签到远程仓库服务器上。 在创建完标签后你必须显式地推送标签到 共享服务器上 > git push origin --tags // 这将会把所有不在远程仓库 服务器上的标签全部传送到origin > git checkout -b version2 v2.0.0 // 在特定的标签上创建一个新分支,当然,如果在这之后又进行了一次提交,version2 分支会因为改动向前移动了,那么 version2 分支就会和 v2.0.0 标签稍微有些不同,这时就应该当心了。为了更加形象地说明,我们假设现在有一个工作目录,里面包含了三个将要被暂存和提交的文件。 暂存操作会 为每一个文件计算校验和(使用我们在 起步 中提到的 SHA-1 哈希算法),然后会把当前版本的文件快照保存到 Git 仓库中(Git 使用 blob 对象来保存它们),最终将校验和加入到暂存区域等待提交:
$ git add README test.rb LICENSE $ git commit -m 'The initial commit of my project'当使用git commit进行提交操作时,Git会先计算每一个子目录(本例中只有项目根目录)的校验和,然后在 Git 仓库中这些校验和保存为树对象。 随后,Git 便会创建一个提交对象,它除了包含上面提到的那些信息外, 还包含指向这个树对象(项目根目录)的指针。如此一来,Git 就可以在需要的时候重现此次保存的快照。
现在,Git 仓库中有五个对象:三个 blob 对象(保存着文件快照)、一个树对象(记录着目录结构和 blob 对象 索引)以及一个提交对象(包含着指向前述树对象的指针和所有提交信息)。
做些修改后再次提交,那么这次产生的提交对象会包含一个指向上次提交对象(父对象)的指针。
Git 的分支,其实本质上仅仅是指向提交对象的可变指针。
由于 Git 的分支实质上仅是包含所指对象校验和(长度为 40 的 SHA-1 值字符串)的文件,所以它的创建和销毁 都异常高效。 创建一个新分支就相当于往一个文件中写入 41 个字节(40 个字符和 1 个换行符),如此的简单 能不快吗?
> git branch hbl3 // 创建一个分支hbl3 > git checkout hbl3 // 切换到hbl3分支 > git checkout -b hbl3 // 上面两个操作的合并操作 > git log --oneline --decorate --graph --all // 查看项目分叉历史 > git checkout master > git merge hotfix // 合并hotfix分支到master分支。若有冲突需手动解决,然后git add .暂存后Git就会将它们标记为冲突已解决 > git pull // 相当于执行上面两个指令 > git branch -d hotfix // 删除本地分支 > git push origin --delete serverfix // 删除远程分支。这个命令做的只是从服务器上移除这个指针。 Git 服务器通常会保留数据一段时间直到垃圾回收运行,所 以如果不小心删除掉了,通常是很容易恢复的。是一种短期分支,它被用来实现单一特性或其相关工作。
origin 是执行git clone 时默认的远程仓库名字
master 是执行git init 时默认的起始分支名字
> git fetch origin // 抓取远程仓库有而本地没有的数据 > git push (remote) (branch) // 推送到远程仓库当克隆一个仓库时,它通常会自动地创建一个跟踪 origin/master 的 master 分支。
> git checkout -b hbl2 origin/hbl2 // 创建一个本地分支跟踪远程分支 > git checkout --track origin/serverfix // 与上述写法等效 > git branch --set-upstream-to origin/serverfix // 已有本地分支,跟踪远程分支或修改上游分支 > git branch -vv // 查看设置的所有跟踪分支原理是首先找到这两个分支的最近共同祖先 C2,然后对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件,然后将当前分支指向目标基底 C3, 最后以此将之前另存为临时文件的修改依序应用。
> git checkout hbl2 > git rebase master // 当前分支 experiment、变基操作的目标基底分支 master > git checkout master > git merge experiment // 合并使用共享文件系统,团队每一个成员都对其(例如一个挂载的 NFS)拥有访问权。
简单、只需把裸版本库对副本放在大家都可以访问对路径,设置好读写权限,就可以了。
共享文件系统比较难配置,并且比起基本的网络连接访问,这不方便从多个位置访问。这个协议并不保护仓库避免意外的损坏。 每一个用户都有“远程”目录的完整 shell 权限,没有方法可以 阻止他们修改或删除 Git 内部文件和损坏仓库。
智能” HTTP 协议的运行方式和 SSH 及 Git 协议类似,只是运行在标准的 HTTP/S 端口上并且可以使用各种 HTTP 验证机制,这意味着使用起来会比 SSH 协议简单的多,比如可以使用 HTTP 协议的用户名/密码的基础 授权,免去设置 SSH 公钥。
不同的访问方式只需要一个 URL 以及服务器只在需要授权时提示输入授权信息,这两个简便性让终端用户使用 Git 变得非常简单。 相比 SSH 协议,可以使用用户名/密码授权是一个很大的优势,这样用户就不必须在使用 Git 之前先在本地生成 SSH 密钥对再把公钥上传到服务器。 对非资深的使用者,或者系统上缺少 SSH 相关程序 的使用者,HTTP 协议的可用性是主要的优势。 与 SSH 协议类似,HTTP 协议也非常快和高效。
在一些服务器上,架设 HTTP/S 协议的服务端会比 SSH 协议的棘手一些。
架设 Git 服务器时常用 SSH 协议作为传输协议。SSH 协议也是一个验证授权的网络协议;并且,因为其普遍性,架设和使用都很容易。
> git clone ssh://user@server/project.git // 通过SSH协议克隆版本库,可以指定一个ssh://的URL > git clone user@server:project.git // 另一种简单的写法。也可以不指定用户,Git 会使用当前登录的用户名。SSH 架设相对简单。通过 SSH 访问是安全的, 所有传输数据都要经过授权和加密。SSH 协议很高效,在传输前也会尽量压缩数据。
不能通过他实现匿名访问。
这是包含在 Git 里的一个特殊的守护进程;它监听在一个特定的端口(9418),类似于SSH 服务,但是访问无需任何授权。
Git 协议是 Git 使用的网络传输协议里最快的。它使用与 SSH 相同的数据传输机制,但是省去了加密和授权的开销。
缺乏授权机制。最难架设。 它要求有自己的守护进程,还要求防火墙开放 9418 端,但是企业防火墙一般不会开放这个非标准端口。 而大型的企业防火墙通常会封锁这个端口。
注意:裸仓库不包含工作区,所以并不会存在在裸仓库上直接显示提交变更的情况。但实际上已经push了的,可以通过在另一个目录下克隆检验。
若干个开发者则作为节点——也就是中心仓库的消费者——并且与其进行同步。
在这里插入图片描述
这种工作流程并不常用,只有当项目极为庞杂,或者需要多级别管理时,才会体现出优势。
当你决定进行一次发布时,你可能想要留下一个标签,这样在之后的任何一个提交点都可以重新创建该发布。
下面的速查表列出了命令对树的影响。 “HEAD” 一列中的 “REF” 表示该命令移动了 HEAD 指向的分支引 用,而`‘HEAD’’ 则表示只移动了 HEAD 自身。 特别注意 WD Safe? 一列 - 如果它标记为 NO,那么运行该命 令之前请考虑一下。
Git 使用一系列配置文件来保存你自定义的行为。 它首先会查找 /etc/gitconfig 文件, 该文件含有系统里每位用户及他们所拥有的仓库的配置值。 如果你传递 --system 选项给 git config,它就会读写该文件。
接下来 Git 会查找每个用户的 ~/.gitconfig 文件(或者 ~/.config/git/config 文件)。 你可以传递 --global 选项让 Git 读写该文件。
最后 Git 会查找你正在操作的版本库所对应的 Git 目录下的配置文件(.git/config)。 这个文件中的值只对该版本库有效。
以上三个层次中每层的配置(系统、全局、本地)都会覆盖掉上一层次的配置,所以 .git/config 中的值会覆盖掉 /etc/gitconfig 中所对应的值。
执行 git init 时,Git 会创建一个 .git 目录。 这个目录包含了几乎所有 Git 存储和操作的对象。 如若想备份或复制一个版本库,只需把这个目录拷贝至另一处即可。
目录的结构:(重要的4个条目)
HEAD :指向目前被检出的分支 config* :包含项目特有的配置选项 ~~description :~~仅供GitWeb程序使用,我们无需关心 hooks/ :包含客户端或服务端端钩子脚本 info/ :包含一个全局性排除(global exclude)文件 ,用以放置那些不希望被记录在 .gitignore 文件中的忽略模式 objects/ :存储所有数据内容 refs/ :指向数据(分支)的提交对象的指针 index/ :保存暂存区信息