为什么要使用版本管理工具
- 备份文件:我们在日常开发中,代码备份必不可少。可以采用移动硬盘、网盘的形式来备份,但是以这种形式也有很多弊端。我们程序员一天的工作量都写在几个文件里面,如果说因为一些未知因素导致丢失了,这种情况损失还是挺大的。
- 历史记录:即使我们上面所说,通过硬盘、网盘来备份,也只能保存当前最新的文件。而版本工具可以备份每一次所提交的代码,以及可以记录详细的修改信息,比如说某一行代码是谁在什么时候进行提交的。
- 版本回退:当我们在开发过程中,也难免一些刚刚入职的同事不小心对代码所造成的伤害难以弥补的时候,这个时候我们也可以通版本管理工具,将当前的代码回退到之前提交的某个版本。
- 多端共享:提供进行团队合作使用,总不能同事A写了一个方法,同事B需要用到这个方法,总不能让同事A拿着硬盘拷贝过去吧,所以采用管理工具只需将代码提交即可。
版本控制(Version Control System)作用
- 记录文件的所有历史变化
- 错误恢复到某个历史版本
- 多人协作开发编辑同一个文件
主流的版本控制产品
名称 | 模型 | 并发模式 | 历史模式 | 变更范围 | 网络协议 | 原子提交性 |
---|---|---|---|---|---|---|
CVS | Client-server | Merge | Changeset | File | Pserver,ssh | No |
SVN | Client-server | 3-way merge, recursive merge, octopus merge | Changeset and Snapshot | Tree | custom (svn), custom (svn) over ssh, HTTP and SSL (usingWebDAV) | Yes |
Git | Distributed | Merge or lock | Snapshot | Tree | custom, custom over ssh, rsync, HTTP/HTTPS, email, bundles | Yes |
- 版本库模型(Repository model):描述了多个源码版本库副本间的关系,有客户端/服务器和分布式两种模式。在客户端/服务器模式下,每一用户通过客户端访问位于服务器的主版本库,每一客户机只需保存它所关注的文件副本,对当前工作副本(working copy)的更改只有在提交到服务器之后,其它用户才能看到对应文件的修改。而在分布式模式下,这些源码版本库副本间是对等的实体,用户的机器出了保存他们的工作副本外,还拥有本地版本库的历史信息。
- 并发模式(Concurrency model):描述了当同时对同一工作副本/文件进行更改或编辑时,如何管理这种冲突以避免产生无意义的数据,有排它锁和合并模式。在排它锁模式下,只有发出请求并获得当前文件排它锁的用户才能对对该文件进行更改。而在合并模式下,用户可以随意编辑或更改文件,但可能随时会被通知存在冲突(两个或多个用户同时编辑同一文件),于是版本控制工具或用户需要合并更改以解决这种冲突。因此,几乎所有的分布式版本控制软件采用合并方式解决并发冲突。
- 历史模式(History model):描述了如何在版本库中存贮文件的更改信息,有快照和改变集两种模式。在快照模式下,版本库会分别存储更改发生前后的工作副本;而在改变集模式下,版本库除了保存更改发生前的工作副本外,只保存更改发生后的改变信息。
- 变更范围(Scope of change):描述了版本编号是针对单个文件还是整个目录树。
- 网络协议(Network protocols):描述了多个版本库间进行同步时采用的网络协议。
- 原子提交性(Atomic commit):描述了在提交更改时,能否保证所有更改要么全部提交或合并,要么不会发生任何改变。
简而言之,各有优缺点,git要配合hub,可以避免分布式损坏。svn有权限控制,避免全被clone走。git适合纯代码,svn适合综合性文档管理,结合起来就完美。显然最大的不同在于git是分布式的。
SVN
- 优点:团队协作开发,代码集中化管理。
- 缺点:单点故障,必须联网工作,无法单机本地工作。
Git
- git的概念
1)git是世界上目前最先进的分布式版本控制系统,致力于团队、个人进行项目版本管理,完美的解决难以比较代码、难以合并代码、难以取消修改、难以在写当前代码的过程中保存未完成的修改去修改线上版本的bug等的痛点。
2)git是一个非常强大的工具,但作为一个git使用者来说,不用完全学习Git的知识点与命令,因为有的命令的使用频率非常的低甚至数年都不会用到。
- git的历史
1)Linus在1991年创建了开源的Linux,从此,Linux系统不断发展,已经成为最大的服务器系统软件了。Linus虽然创建了Linux的核心,但Linux的壮大是靠全世界热心的志愿者参与的,这么多人在世界各地为Linux编写代码,那Linux的代码是如何管理的呢?
2)事实是,在2002年以前,世界各地的志愿者把源代码文件通过diff的方式发给Linus,然后由Linus本人通过手工方式合并代码!你也许会想,为什么Linus不把Linux代码放到版本控制系统里呢?不是有CVS、SVN这些免费的版本控制系统吗?因为Linus坚定地反对CVS和SVN,这些集中式的版本控制系统不但速度慢,而且必须联网才能使用。有一些商用的版本控制系统,虽然比CVS、SVN好用,但那是付费的,和Linux的开源精神不符。不过,到了2002年,Linux系统已经发展了十年了,代码库之大让Linus很难继续通过手工方式管理了,社区的弟兄们也对这种方式表达了强烈不满,于是Linus选择了一个商业的版本控制系统BitKeeper,BitKeeper的东家BitMover公司出于人道主义精神,授权Linux社区免费使用这个版本控制系统。
3)安定团结的大好局面在2005年就被打破了,原因是Linux社区牛人聚集,不免沾染了一些梁山好汉的江湖习气。开发Samba的Andrew试图破解BitKeeper的协议(这么干的其实也不只他一个),被BitMover公司发现了(监控工作做得不错!),于是BitMover公司怒了,要收回Linux社区的免费使用权。Linus可以向BitMover公司道个歉,保证以后严格管教弟兄们,嗯,这是不可能的。实际情况是这样的:Linus花了两周时间自己用C写了一个分布式版本控制系统,这就是Git!一个月之内,Linux系统的源码已经由Git管理了!牛是怎么定义的呢?大家可以体会一下。
4)Git迅速成为最流行的分布式版本控制系统,尤其是2008年,GitHub网站上线了,它为开源项目免费提供Git存储,无数开源项目开始迁移至GitHub,包括jQuery,PHP,Ruby等等。历史就是这么偶然,如果不是当年BitMover公司威胁Linux社区,可能现在我们就没有免费而超级好用的Git了。
git是linux的创始人linus,在付费版本控制工具BitMover收回对Linux社区免费使用权利的时候,一怒之下花费两个星期的时间写出来的。(不要逼牛笔的人)
- 组成结构
1)工作区:用来保存项目的元数据和对象数据库的地方。 这是 Git 中最重要的部分,从其它计算机克隆仓库时,拷贝的就是这里的数据。
2)暂存区:保存了下次将提交的文件列表信息,一般在 Git 仓库目录中。有时候也被称作“索引”,不过一般说法还是叫暂存区域。
3)版本库:也叫本地版本库,之所以说git 快,大部分提交都是对本地仓库而言的,不依赖网络,最后一次会推送的到远程仓库。
4)远程仓库:可以看做是github,它是一个远程仓库,它提供web服务的 供大家方便下载、查看、提交、存储。文件的状态
- 文件状态
1)新建文件状态为untracked
2)add命令执行后状态变为staged
3)已存在的文件状态为unmodified
4)修改文件内容,文件状态变为modified
5)commit提交,文件状态编程unmodifed。
Git 常用基础命令
- 配置用户信息
1 | git config --global user.email "dxj1718874198@gmail.com" |
- 初始化本地 Git 仓库
1 | git init |
- 查看 Git 仓库状态
1 | git status |
1)新添加的未跟踪文件前面有 ?? 标记;
2)新添加到暂存区中的文件前面有 A 标记;
3)修改过的文件前面有 M 标记。 M 有两个可以出现的位置:
4)出现在右边的 M 表示该文件被修改了但是还没放入暂存区,
5)出现在靠左边的 M 表示该文件被修改了并放入了暂存区。
6)加到暂存区之后,又修改了一次,修改之后,并没有添加到暂存区,前面有MM标记。
7)以此类推,如果加到暂存区之后,被修改了两次,修改之后,并没有添加到暂存区,前面有MMM标记。…
- 文件添加到暂存区
1 | git add file1 |
- 将本地暂存的修改提交到版本库
1 | git commit -m "add file1" file1 |
- 查看文件的修改内容
1 | git diff file1 |
- 查看git仓库提交日志
1 | git log |
- 查看所有分支的所有操作记录
1 | git reflog # 包括已经被删除的 commit 记录和 reset 的操作 |
- 忽略文件
1 | .DS_Store |
1)创建一个名为 .gitignore 的文件,列出要忽略的文件模式
2)星号(*)匹配零个或多个任意字符;
3)[abc] 匹配任何一个列在方括号中的字符(这个例子要么匹配一个 a,要么匹配一个 b,要么匹配一个c);
4)问号(?)只匹配一个任意字符;
5)如果在方括号中使用短划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配(比如 [0-9]表示匹配所有 0 到 9 的数字)。
6)使用两个星号(*) 表示匹配任意中间目录,比如a/**/z 可以匹配 a/z, a/b/z 或a/b/c/z等
- 工作区和暂存区
1)工作区有一个隐藏目录.git,这个不算工作区,而是Git的版本库。
2)Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区,还有Git为我们自动创建的第一个分支master,以及指向master的一个指针叫HEAD。
3)第一步是用git add把文件添加进去,实际上就是把文件修改添加到暂存区;
4)第二步是用git commit提交更改,实际上就是把暂存区的所有内容提交到当前分支。
- 丢弃工作区的修改
1 | git checkout -- file1 |
- 撤销暂存区的修改(unstage),重新放回工作区
1 | git reset HEAD |
- 版本回退
1 | git reflog |
- 误删,还原
1 | git checkout file1 |
- 删除版本库中文件
1 | git rm file1 |
- 创建分支
1 | git branch <branch_name> |
- 查看分支
1 | git branch # 查看所有分支 |
- 切换分支
1 | git checkout <branch_name> # 切换到指定分支 |
- 删除分支
1 | git branch -d <branch_name> # 删除一个干净的分支(即相对当前分支而言该分支没有新的提交记录) |
注意:删除分支前都需要先切换到其他分支才能进行删除操作
- 分支恢复
1 | git reflog # 查找该分支指向的commitId |
- 重命名分支
1 | git branch -m <branch_name> newname |
- 分支合并
1 | git merge <branch_name> # 将指定分支合并到当前分支,如果两个分支没有产生分叉情况,那么会进行快速合并,即fast-forward方式 |
- 分支合并细节
1 | git merge --no-ff -m "msg" <branch_name> # 合并分支时禁用Fast forward模式 |
- 冲突解决
1)当对分叉分支进行合并时,如果两个分支都对同一文件进行了修改,那么合并时就有可能会产生冲突情况。
2)如果两个分支对同一文件的修改是有规律的,比如对不同地方的修改,那么git工具可以实现自动合并
3)如果无法自动合并,则需要对冲突文件进行手动修改,修改完成后使用git add表示冲突已经解决,然后使用git commit进行提交
- 分支暂存
1 | git stash # 将工作暂存 |
- 从暂存区之中进行恢复,有两种处理方式:
1)先恢复,而后再删除暂存
1 | git stash apply |
2)恢复的同时也将stash内容删除
1 | git stash pop |
- 本地仓库推送到远程仓库
1 | git remote add origin 远程仓库地址 # 将本地仓库和远程仓库进行关联 |
注意有坑: 新建远程仓库的时候如果你勾选了
Initialize this repository with a README
(就是创建仓库的时候自动给你创建一个README文件),那么到了第7步你将本地仓库内容推送到远程仓库的时候就会报一个failed to push some refs to 远程仓库地址
的错。原因: 由于你新创建的那个仓库里面的
README.md
文件不在本地仓库目录中,这时我们可以通过git pull --rebase origin master
命令先将内容合并,此时再push就能成功了。
- 克隆远程仓库
1 | git clone 远程仓库地址 # 从远程服务器克隆一个一模一样的版本库到本地,复制的是整个版本库 |
- 获取最新版本远程仓库
1 | git fetch |
- 清理本地仓库(以Hexo静态博客为例)
1 | 由于现在的.git文件夹里累积了太多辣鸡数据,甚至已经超过博客本身的大小了,于是打算从头开始重新部署一下博客的所有静态资源。 |