目录
1. SVN(Cornerstone工具)
2. Git(Sourcetree工具)
1. SVN
Apache Subversion(SVN)是一个开源的版本控制系统。
Subversion 在 2000 年由 CollabNet Inc 开发。
SVN相对于RCS、CVS,采用了分支管理系统,它的设计目标就是取代CVS。
SVN管理着随时间改变的数据, 这些数据放置在一个中央资料档案库(repository) 中。 这个档案库很像一个普通的文件服务器, 不过它会记住每一次文件的变动,这样就可以把档案恢复到旧的版本或浏览文件的变动历史。
1. repository(源代码库)
源代码统一存放的地方
2. Checkout(提取)
当手上没有源代码的时候,需要从repository checkout一份
3. Update (更新)
当已经Checkout了一份源代码, Update一下就可以和Repository上的源代码同步,手上的代码就会有最新的变更。
4. Commit(提交)
当已经修改了代码,就需要Commit到repository
5. 如果两个程序员同时修改了同一个文件, SVN 可以合并这两个程序员的改动,实际上SVN管理源代码是以行为单位的,就是说两个程序员只要不是修改了同一行程序,SVN都会自动合并两种修改。如果是同一行,SVN 会提示文件 Conflict, 冲突,需要手动确认。
1. 目录版本控制
CVS 只能跟踪单个文件的历史。
Subversion是一个 "虚拟" 的版本控管文件系统, 能够依时间跟踪整个目录的变动,目录和文件都能进行版本控制。
2. 真实的版本历史
自从CVS限制了文件的版本记录,CVS并不支持那些可能发生在文件上,但会影响所在目录内容的操作,如复制和重命名。除此之外,在CVS里不能用拥有同样名字但是没有继承老版本历史或者根本没有关系的文件替换一个已经纳入系统的文件。
在Subversion中,可以增加(add)、删除(delete)、复制(copy)和重命名(rename),无论是文件还是目录。所有的新加的文件都从一个新的、干净的版本开始。
3. 自动提交
一个提交动作,不是全部更新到了档案库中,就是不完全更新。这允许开发人员以逻辑区间建立并提交变动,以防止当部分提交成功时出现的问题。
4. 纳入版本控管的元数据
每一个文件与目录都附有一組属性关键字并和属性值相关联。可以创建并儲存任何想要的Key/Value对。 属性是随着时间来作版本控管的,就像文件內容一样。
5. 选择不同的网络层
Subversion 有抽象的档案库存取概念, 可以让人很容易地实作新的网络机制。 Subversion 可以作为一个扩展模块嵌入到Apache HTTP 服务器中。这个为Subversion提供了非常先进的稳定性和协同工作能力,除此之外还提供了许多重要功能: 举例来说, 有身份认证, 授权, 在线压缩, 以及文件库浏览等等。还有一个轻量级的独立Subversion服务器, 使用的是自定义的通信协议, 可以很容易地通过 ssh 以 tunnel 方式使用。
6. 一致的数据处理方式
Subversion 使用二进制差异算法来异表示文件的差异, 它对文字(人类可理解的)与二进制文件(人类无法理解的) 两类的文件都一视同仁。 这两类的文件都同样地以压缩形式储存在档案库中, 而且文件差异是以两个方向在网络上传输的。
7. 有效的分支(branch)与标签(tag)
在分支与标签上的消耗并不必一定要与项目大小成正比。 Subversion 建立分支与标签的方法, 就只是复制该项目, 使用的方法就类似于硬连接(hard-link)。 所以这些操作只会花费很小, 而且是固定的时间。
8. Hackability
Subversion没有任何的历史包袱; 它主要是一群共用的 C 程序库, 具有定义完善的API。这使得 Subversion 便于维护, 并且可被其它应用程序与程序语言使用。
相对CVS的优势
1. 原子提交。
一次提交不管是单个还是多个文件,都是作为一个整体提交的。在这当中发生的意外例如传输中断,不会引起数据库的不完整和数据损坏。
2. 重命名、复制、删除文件等动作都保存在版本历史记录当中。
3. 对于二进制文件,使用了节省空间的保存方法。(简单的理解,就是只保存和上一版本不同之处)。
4. 目录也有版本历史。
整个目录树可以被移动或者复制,操作很简单,而且能够保留全部版本记录。
5. 分支的开销非常小。
6. 优化过的数据库访问,使得一些操作不必访问数据库就可以做到。这样减少了很多不必要的和数据库主机之间的网络流量。
1. 创建版本库
版本库相当于一个集中的空间,用于存放开发者所有的工作成果。版本库不仅能存放文件,还包括了每次修改的历史,即每个文件的变动历史。
Create 操作:创建一个新的版本库。当创建一个新的版本库时,版本控制系统会让你提供一些信息来标识版本库,例如创建的位置和版本库的名字。大多数情况下这个操作只会执行一次。
2. 检出
Checkout操作:从版本库下载代码到工作副本。工作副本是开发者私人的工作空间,可以进行内容的修改,然后提交到版本库中。
3. 更新
Update操作:将版本库中的最新代码更新到工作副本。
4. 执行变更
当检出之后就可以做很多操作(编辑/添加/删除文件、目录)来执行变更。这些改变会被添加进待变更列表中,直到执行了 Commit 操作后才会成为版本库的一部分。
Rename操作:可以更改文件/目录的名字。
"移动"操作用来将文件/目录从一处移动到版本库中的另一处。
5. 复查变化
在 commit 操作之前复查下修改是一个很好的习惯。
Status操作 : 列出了工作副本中所进行的变动(待变更列表),但并不提供变动的详细信息。可以用 diff 操作来查看这些变动的详细信息。
6. 修复错误
Revert 操作 : 重置对工作副本的修改。
可以重置一个或多个文件/目录、整个工作副本。
7. 解决冲突
合并的时候可能会发生冲突。
Merge 操作:自动处理可以安全合并的东西。其它的会被当做冲突。例如,"hello.m" 文件在一个分支上被修改,在另一个分支上被删除了。这种情况就需要人为处理。
Resolve操作: 用来帮助用户找出冲突并告诉版本库如何处理这些冲突。
8. 提交更改
Commit 操作: 用来将更改从工作副本提交到版本库。
在提交之前,你必须将文件/目录添加到待变更列表中。列表中记录了将会被提交的改动。
当提交的时候,通常会提供一个注释来说明为什么会进行这些改动。这个注释也会成为版本库历史记录的一部分。Commit 是一个原子操作,也就是说要么完全提交成功,要么失败回滚。用户不会看到成功提交一半的情况。
SVN使用
查看是否安装SVN(mac默认已经安装)
svn --version
在服务端进行SVN版本库的相关配置
1. 创建版本库
mkdir /opt/svn
// 创建版本库
svnadmin create /opt/svn/runoob
// 启动服务
// -r: 配置方式决定了版本库访问方式。
// --listen-port: 指定SVN监听端口,不加此参数,SVN默认监听3690
svnserve -d -r 目录 --listen-port 端口号
方式一:-r直接指定到版本库(单库svnserve方式)
svnserve -d -r /opt/svn/runoob
这种情况下,一个svnserve只能为一个版本库工作。
authz配置文件中对版本库权限的配置应这样写:
[groups]
admin=user1
dev=user2
@admin=rw
user2=r
svn://192.168.0.1/ 访问runoob版本库
方式二:指定到版本库的上级目录(多库svnserve方式)
svnserve -d -r /opt/svn
这种情况下,一个svnserve可以为多个版本库工作。
authz配置文件中对版本库权限的配置应这样写:
[groups]
admin=user1
dev=user2
[runoob:/]
@admin=rw
user2=r
[runoob01:/]
@admin=rw
user2=r
svn://192.168.0.1/runoob 访问runoob版本库
2. 修改默认配置文件配置(conf目录下的svnserve.conf)
[general]
// 控制非鉴权用户访问版本库的权限,取值范围为write读写、read只读(默认)、none无访问权限。
anon-access = none
// 控制鉴权用户访问版本库的权限。默认:write。
auth-access = write
// 用户名口令文件,默认为conf目录中的passwd
password-db = /home/svn/passwd
// 指定权限配置文件名,通过该文件可以实现以路径为基础的访问控制。 除非指定绝对路径,否则文件位置为相对conf目录的相对路径。 默认为conf目录中的authz 。
authz-db = /home/svn/authz
// realm: 指定版本库的认证域,即在登录时提示的认证域名称。若两个版本库的 认证域相同,建议使用相同的用户名口令数据文件。 默认值:一个UUID(Universal Unique IDentifier,全局唯一标示)。
realm = tiku
3. 修改用户名口令文件
[users]
admin = admin
thinker = 123456
4. 修改权限配置文件
该配置文件由一个[groups]配置段和若干个版本库路径权限段组成。
[groups]
g_admin = admin,thinker
[runoob01:/]
@g_admin=rw
user2=r
[test:/home/thinker]
thinker = rw
* = r
客户端(方式1: 终端命令)
1. 检出(从远程服务器端下载项目)
svn checkout svn://192.168.0.1/runoob01 --username=user01
2. 修改文件后
// 查看更改
svn diff
// 查看工作副本的状态
// 文件的状态为?,说明它是新建的文件,还未加到版本控制中
// 文件的状态为A,说明它加到版本控制中了
// 文件的状态为M,说明它被修改了(在版本控制中)
svn status
// 将?状态的文件添加到版本控制中
svn add file1
// 更新本地代码。svn update -r6指定更新版本
svn update
// 提交代码
// 如果冲突,则先解决冲突(去除冲突代码)后再提交
svn commit -m "注释"
3. 回退
// 回退文件到未修改状态
svn revert file1
// 回退目录到未修改状态
svn revert -R 目录1
// 撤销回之前的版本(撤销旧版本里的所有更改然后提交一个新版本)。
svn merge -r 版本号2:版本号1 file1
4. 查看历史信息
// 查看svn的版本、作者、日期、路径等
svn log
// 查看版本6-8
svn log -r 6:8
// 查看某一个文件的版本修改信息
svn log file1
// 包含目录信息,且只要5条
svn log -l 5 -v
// 查看特定修改的行级详细信息
svn diff
// 比较工作副本和版本库中版本号为3的文件file1
svn diff -r 版本号3 file1
// 比较版本库中版本号为2和3的文件file1
svn diff -r 版本号2:版本号3 file1
// 取得在特定版本的某文件显示在当前屏幕
svn cat -r 版本号1 file1
// 显示一个目录或某一版本存在的文件
svn list http://192.168.0.1/runoob01
5. 分支
// 创建分支
svn copy 主干线目录/ branches/分支干线目录
// 提交新分支到版本库
svn commit -m "添加分支"
cd跳到分支目录下开发,修改、更新、提交后,再cd跳回到主分支目录下
// 合并到主分支后,然后更新、提交
svn merge ../branches/分支干线目录/
6. 标签(tag)
// 在本地工作副本创建一个 tag。会在tags目录下创建新的目录v1.0
svn copy 主分支目录/ tags/v1.0
// 提交
svn commit -m "创建tag"
客户端(方式2: Cornerstone工具)
1. Workspace:工作区
2. Index(Stage):暂存区,存放在.git/index文件
3. Repository:仓库区(或本地仓库),.git目录。HEAD是指向分支的游标,指向最后一次提交。
4. Remote:远程仓库
Git 与常用的版本控制工具 CVS, SVN 等不同,它采用了分布式版本库的方式,不必服务器端软件支持。
Git 是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。
Git 不仅仅是个版本控制系统,它也是个内容管理系统(CMS),工作管理系统等。
Git记录版本历史只关心文件数据的整体是否发生变化。Git 不保存文件内容前后变化的差异数据。
实际上,Git 更像是把变化的文件作快照后,记录在一个微型的文件系统中。每次提交更新时,它会纵览一遍所有文件的指纹信息并对文件作一快照,然后保存一个指向这次快照的索引。为提高性能,若文件没有变化,Git 不会再次保存,而只对上次保存的快照作一连接。
在分布式版本控制系统中,客户端并不只提取最新版本的文件快照,而是把原始的代码仓库完整地镜像下来。这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复。这类系统都可以指定和若干不同的远端代码仓库进行交互。借此,你就可以在同一个项目中,分别和不同工作小组的人相互协作。你可以根据需要设定不同的协作流程。
另外,因为Git在本地磁盘上就保存着所有有关当前项目的历史更新,并且Git中的绝大多数操作都只需要访问本地文件和资源,不用连网,所以处理起来速度飞快。用SVN的话,没有网络或者断开VPN你就无法做任何事情。但用Git的话,就算你在飞机或者火车上,都可以非常愉快地频繁提交更新,等到了有网络的时候再上传到远程的镜像仓库。
Git 与 SVN 区别
1、Git 和其它非分布式的版本控制系统(SVN、CVS)最核心的区别:
Git是分布式。
SVN是集中式。
集中式的版本控制系统都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。
2、Git 把内容按元数据方式存储,而 SVN 是按文件。
所有的资源控制系统都是把文件的元信息隐藏在一个类似 .svn、.cvs 等的文件夹里。
3、Git 分支和 SVN 的分支不同。
分支在 SVN 中一点都不特别,其实它就是版本库中的另外一个目录。
Git 和 Svn 的分支实现机制完全的不同,这也直接导致了 SVN 在分支合并中困难重重。尽管在 SVN 1.5 之后,通过 svn:mergeinfo 属性引入了合并追踪机制,但是在特定情况下,合并仍会出现很多困难。
1.SVN的分支合并
当你在一个分支上工作数周或几个月之后,主干的修改也同时在进行着,两条线的开发会区别巨大,当你想合并分支回主干,可能因为太多冲突,已经无法轻易合并你的分支和主干的修改。
另一个问题,SVN不会记录任何合并操作,当你提交本地修改,版本库并不能判断出你是通过svn merge还是手工修改得到这些文件。所以你必须手工记录这些信息(说明合并的特定版本号或是版本号的范围)。
要解决以上的问题只有通过有规律的将主干合并到分支来避免,制定这样一个政策:每周将上周的修改合并到分支,注意这样做时需要小心,你必须手工记录合并的过程,以避免重复的合并,你需要小心的撰写合并的日志信息,精确的描述合并包括的范围。这样做看起来有点像是胁迫。
SVN 的版本号是连续的版本号。每一次新的提交都会版本号+1 ,而无论这个提交是在哪个分支中进行的。SVN一个提交可以同时修改不同分支的不同文件,因为提交命令可以在 /trunk, /branches, /tags 的上一级目录执行。
• SVN 的提交是单线索的,每一个提交(最原始的提交0除外)都只有一个父节点(版本号小一个的提交节点)
• SVN 的提交链只有一条,仅从版本号和提交说明,我们无法获得分支图
• SVN 的分支图在某些工具(如乌龟SVN)可以提供,那是需要对提交内容进行检查,对目录拷贝动作视为分支,对 svn:mergeinfo 的改动视为合并,但这会由于目录管理的灵活性,导致千奇百怪的分支图表。
2.Git的分支合并
在 git 版本库中创建分支的成本几乎为零,所以,不必吝啬多创建几个分支。当第一次执行git-init时,系统就会创建一个名为”master”的分支。 而其它分支则通过手工创建。
常见的分支策略:
① 创建一个属于自己的个人工作分支,以避免对主分支 master 造成太多的干扰。
② 当进行高风险的工作时,最好创建一个临时分支。
③ 合并别人修改时,最好创建一个临时分支用来合并,合并完成后再“fatch”到自己的分支。
4、Git 没有一个全局的版本号,而 SVN 有。
目前为止这是跟 SVN 相比 Git 缺少的最大的一个特征。
SVN的全局版本号和CVS的每个文件都独立维护一套版本号相比,是一个非常大的进步。SVN提供对于事物处理的支持,每一个事物处理(即一次提交)都具有整个版本库全局唯一的版本号。
Git的版本号则更进一步,版本号是全球唯一的。Git 对于每一次提交,通过对文件的内容或目录的结构计算出一个SHA-1 哈希值,得到一个40位的十六进制字符串,Git将此字符串作为版本号。
• 所有保存在Git 数据库中的数据都是用此40位的哈希值作索引的,而不是靠文件名。
• 使用哈希值作版本号的好处就是对于一个分布式的版本控制系统,每个人每次提交后形成的版本号都不会出现重复。另一好处是保证数据的完整性,因为哈希值是根据内容或目录结构计算出来的,所以我们还可以据此来判断数据内容是否被篡改。
• SVN 的版本号是连续的,可以预判下一个版本号,而 Git 的版本号则不是。
因为 subversion 是集中式版本控制,很容易实现版本号的连续性。Git 是分布式的版本控制系统,而且 Git 采用 40 位长的哈希值作为版本号,每个人的提交都是各自独立完成的,没有先后之分(即使提交有先后之分,也由于PUSH/PULL的方向和时机而不同)。Git 的版本号虽然不连续,但是是有线索的,即每一个版本都有对应的父版本(一个或者两个),进而可以形成一个复杂的提交链
• Git 的版本号简化:Git 可以使用从左面开始任意长度的字串作为简化版本号,只要该简化的版本号不产生歧义。一般采用7位的短版本号(只要不会出现重复的,你也可以使用更短的版本号)。
5、Git 的内容完整性要优于 SVN。
Git 的内容存储使用的是 SHA-1 哈希算法。这能确保代码内容的完整性,确保在遇到磁盘故障和网络问题时降低对版本库的破坏。
6、版本库与工作区
SVN的版本库和工作区是分离的(不同的主机中),只能通过 https, http, svn 等协议访问,而不能直接被用户接触到。
• Subversion的工作区是一份版本库在某个历史状态下的快照,如:版本库最新的数据检出到工作区。
• Subversion的工作区中每一个目录下都包含一个名为 .svn 的控制目录(隐藏的目录),该目录的作用是:
① 标识工作区和版本库的对应关系。
② 包含一份该子目录下检出文件的原始拷贝。当文件改动的差异比较或者本地改动的回退时,可以直接参考原始拷贝而无须通过网络访问远程版本库。
• Subversion 的 .svn 控制目录会引入很多麻烦:
① .svn 下的文件原始考本,会导致在目录下按照文件内容搜索时,多出一倍的搜索时间和搜索结果。
② .svn 很容易在集成时,引入产品中,尤其是 Web 应用,将 .svn 目录带入Web服务器会导致安全隐患。因为一个不允许目录浏览的Web目录,可以通过 .svn/entries 文件查看到该目录下可能存在的文件。
Git 的版本库和工作区在同一个目录下,工作区的根目录有一个.git的子目录,这个名为 .git的目录就是版本库本身,它是Git 用来保存元数据和对象数据库的地方。该目录非常重要,每次克隆镜像仓库的时候,实际拷贝的就是这个目录里面的数据。所以千万要小心删除这个文件。
• 工作区中其他文件为工作区文件,可能是从 .git 中检出的,或者是要检入的,或者是运行产生的临时文件等。
• 版本库可以脱离工作区而存在,成为 bare(赤裸)版本库。可以用 –bare 参数来创建。但是工作区不能脱离版本库而存在,即工作区的根目录下必须有一个名为 .git 的版本库克隆文件。
• Git 的版本库因为就在工作区中,能直接被用户接触到。
① 用户可以编辑 .git/config 文件,修改配置,增添新的源
② 用户可以编辑 .git/info/exclude 文件,创建本地忽略…
• Git 的工作区中只在工作区的根目录下有一个 .git 目录,此外再无任何控制目录。Git 工作区下唯一的 .git 目录是版本库,并非 .svn 的等价物,如果删除了 .git 目录,而又没有该版本库的其他镜像(克隆)的话,你破坏了整个历史,版本库也永远的失去了。
• Git 在本地的 .git 版本库,提供了完全的改动历史。除了和其他人数据交换外,任何版本库相关的操作都在本地完成,更多的本地操作,避免了冗长的网络延迟,大大节省了时间。例如:查看 log,切换到任何历史版本等操作都无须连接网络。
• Git如何保证安全:本地创建一个Git库,因为工作区和库是在同一个目录中,如果工作区删除了,或者所在的磁盘分区格式化了,数据不是全都没有了么?其实我们可以这样做:
① 在一个磁盘分区中创建版本库(最好是用 –bare 参数创建),然后在另外的磁盘分区中克隆一个新的作为工作区。在工作区的提交要不时的PUSH到另外分区的版本库,这样就实现了本地的数据镜像。你甚至可以在本地创建更多的版本库镜像,安全性要比Subversion的一个库加上一个工作区安全。
② 另一个办法:把你的版本库共享给他人,当他人克隆了你的版本库时,你就拥有了一个异地备份。
7、权限管理
SVN通过对文件目录授权来实现权限管理,子目录默认继承父目录的权限。但是也有缺憾,即权限不能在分支中继承,不能对单个文件授权。
Git做不到类似SVN的路径授权(不符合分布式理念),授权模型只能实现非零即壹式的授权,要么拥有全部的写权限,要么没有写权限,要么拥有整个版本库的读权限,要么禁用。
Git解决授权:
1. 公司内部代码开放。
即代码在公司内部,对项目组成员一视同仁的开放。
2. 公司对代码库进行合理分解,对每个代码库分别授权。
即某个代码库对团队成员完全开放,对其它团队完全封闭。
3. 公司使用SVN做集中式的版本控制,个人和/或团队使用 Git-svn。
这样在无法改变公司版本控制策略时,程序员可以采用的变通之法。
4. Git服务器的部署实际上可以使用钩子对分支和路径进行写授权,即可以控制谁能够创建分支,能够写特定文件。
8. 部分检出
SVN可以将整个库检出到工作区,也可以将某个目录检出到工作区。
Git只能全部检出,不支持按照目录进行的部分检出。
1. SVN的部分检出
• 在SVN中,从仓库checkout的一个工作树,每个子目录下都维护着自己的.svn目录,记录着该目录中文件的修改情况以及和服务器端仓库的对应关系。所以SVN可以checkout部分路径下的内容(部分检出),而不用checkout整个版本库或分支。
• SVN 有一条命令:svn export ,可以将 SVN 版本库的一个目录下所有内容导出到指定的目录下。SVN 需要 svn export 命令是因为该命令可以导出一个干净的目录,即不包含 .svn 目录(包含配置文件和文件原始拷贝)。
2. Git的检出
• Git 没有部分检出,这并不是说只有将整个库克隆下来才能查看文件。
有很多 git 工具,提供直接浏览git库的功能,例如 gitweb, trac 的 git 版本库浏览, redmine 的 git 版本库浏览。
• Git-submodule 可以实现版本库的模块化:Git 通过子模块处理这个问题。
子模块允许你将一个Git 仓库当作另外一个Git仓库的子目录。这允许你克隆另外一个仓库到你的项目中并且保持你的提交相对独立。
• Git 为什么没有实现 svn export 的功能?由于git的本地仓库信息完全维护在project根目录的.git目录下,(不像svn一样,每个子目录下都有单独的.svn目录)。所以,只要clone,checkout然后删除.git目录就可以了。
9. 撤消操作
1.提交的撤销
SVN中一旦完成向服务器的数据提交,就没有办法再从客户端追回,只能在后续的提交中修正(回退或者修改)等。因为Subversion作为集中式的版本控制,不能允许个人对已提交的数据进行篡改。SVN具有一个非常重要的特性就是它的信息从不丢失,即使当你删除了文件或目录,它也许从最新版本中消失了 ,但这个对象依然存在于历史的早期版本中。
Git则不同,Git是分布式版本控制系统,代码库是属于个人,允许任意修改。Git通过对提交建立数字摘要来保证提交的唯一性和不可更改性,通过版本库在多人之间的多份拷贝来保障数据的安全性。Git可以丢弃最新的一个或几个提交,使用 git reset –hard命令可以永远丢弃最新的一个或者几个提交。
2.提交说明的修改
提交后如果对提交说明不满意
⑴ Git可以使用命令git commit –amend修改提交说明。
• Git可以修改最后一次提交说明,并不是说不能修改历史版本的提交说明,只是修改最后一个版本提交说明拥有最简单的命令;
• Git修改提交说明,会改变提交的commit-id。即修改提交说明后,将产生一个新的提交;
• Git可以通过git reset –hard ,git commit –amend,git rebase onto 等命令来实现对历史提交的修改;
• 使用stg工具可以更为简单的修改历史提交的提交说明,包括提交内容;
⑵ SVN也可以修改提交说明,是通过修改提交的svn:log版本属性实现的:
• 不但可以修改最后一次提交的说明,并且可以修改历史提交的提交说明;
• SVN修改提交说明是不可逆的操作,可能会造成说明被恶意修改;
• SVN缺省关闭修改提交说明的功能。管理员在设置了提交说明更改的邮件通知后,才可以打开该功能。
3.修改和重构历史提交
Git可以修改和重构历史提交:使用Git本身的reset以及 rebase 命令可以修改或者重整/重构历史提交,非常灵活。使用强大的 stg 可以使得历史提交的重构更为简洁,如果您对 stg 或者 Hg/MQ 熟悉的话。
SVN 修改历史提交,只能由管理员完成。
SVN 是集中式版本控制系统,从客户端一旦完成提交,就没有办法从客户端撤销提交。但是管理员可以在服务器端完成提交的撤销和修改,但是操作过程和代价较大。
10. 更新和提交
1.更新操作
在SVN中,因为只有一个中心仓库,所以所谓的远程更新,也就是svn update ,通过此命令来使工作区和版本库保持同步。
在git中,别人的改动是存在于远程仓库上的,所以git checkout命令尽管在某些功能上和svn中的update类似(例如取仓库特定版本的内容),但是在远程更新这一点上,还是不同的,不属于git checkout的功能涵盖范围。 Git使用git fetch和git pull来完成远程更新任务,fetch操作只是将远程数据库的object拷贝到本地,然后更新remotes head的refs,git pull 的操作则是在git fetch的基础上对当前分支外加merge操作。
2.SVN中的commit命令
对于SVN来说,由于是中心式的仓库管理形式,所以并不存在特殊的远程提交的概念,所有的commit操作都可以认为是对远程仓库的更新动作。在工作区中对文件进行添加、修改、删除操作要同步到版本库,必须使用 commit命令。
• add 命令,是将未标记为版本控制状态的文件标记为添加状态,并在下次提交时入库。
• delete命令,是通过SVN来删除文件,并在下次提交后有效。
• SVN 有提交列表功能,即将某些文件加入一个修改列表,提交可以只提交处于该列表的文件。
3.Git中的暂存区域(stage)
Git 管理项目时,文件在三个工作区域中流转:Git 的本地数据目录,工作目录以及暂存区域。暂存区域(stage)是介于 workcopy 和 版本库 HEAD 版本的一种中间状态。所谓的暂存区域只不过是个简单的文件,一般都放在git 目录中。有时候人们会把这个文件叫做索引文件,不过标准说法还是叫暂存区域。
要将一个文件纳入版本管理的范畴,首先是要用git add将文件纳入stage的监控范围,只有更新到stage中的内容才会在commit的时候被提交。另外,文件本身的改动并不会自动更新到stage中,每次的任何修改都必须重新更新到stage中去才会被提交。对于工作区直接删除的文件,需要用 git rm 命令进行标记,在下次提交时,在版本库中删除。
• 工作区的文件改动(新增文件,修改文件,删除文件),必须用 git add 或者 git rm 命令标识,使得改动进入 stage
• 提交只对加入 stage 的改动进行提交
• 如果一个文件改动加入 stage 后再次改动,则后续改动不改变 stage。即该文件的改动有两个状态,一个是标记到 stage 中并将在下次提交时入库的改动,另外的后续改动则不被提交,除非再次使用 git add 命令将改动加入到 stage 中。
• Git的stag让你在提交的时候清楚的知道git将要提交哪些改动。除非提交的时候使用 -a 参数(不建议使用)。
我们可以从文件所处的位置来判断其状态:如果是git目录中保存着的特定版本文件,就属于已提交状态;如果作了修改并已放入暂存区域,就属于已暂存状态;如果自上次取出后,作了修改但还没有放到暂存区域,就是已修改状态,如果取出后未进行修改则是未修改状态。
在git中,因为有本地仓库和remote仓库之分,所以也就区别于commit 操作,存在额外的push命令,用于将本地仓库的数据更新到远程仓库中去。git push 可以选择需要提交的、更新的分支以及制定该分支在远程仓库上的名字。
11. 分支和里程碑的实现
几乎每一种版本控制系统都以某种形式支持分支。使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续工作。在很多版本控制系统中,这是个昂贵的过程,常常需要创建一个源代码目录的完整副本,对大型项目来说会花费很长时间。
轻量级分支/里程碑的含义是,创建分支/里程碑的复杂度是o(1),不会因为版本库的愈加庞大而变得缓慢。在CVS中,创建分支的复杂度是o(n)的,导致大的版本库的的分支创建非常缓慢。
1.SVN的分支/里程碑
SVN轻量级分支和里程碑的实现是通过svn cp命令,即带历史的拷贝就是创建快速创建分支和里程碑的秘籍。SVN的版本库有特殊的设计,当你复制一个目录,你不需要担心版本库会变得十分巨大—SVN并不是拷贝所有的数据,相反,它只是建立了一个已存在目录树的入口。这种“廉价的拷贝”就是创建分支/里程碑是轻量级的原因。
由于SVN的分支和标签是来自目录拷贝,约定俗成是拷贝在 branches/和tags/目录下。所谓分支,tag等概念都只是仓库中不同路径上的一个对象或索引而已,和普通的路径并没有什么本质的区别,谁也不能阻止在一个提交中同时修改不同分支中的数据。
里程碑是对某个历史提交所起的一个别名,作为历史的标记,是不应该被更改的。svn的里程碑要建立到 tags/目录下,要求不要在tags/下的里程碑目录下进行提交。但是谁也阻止不了对未进行权限控制的里程碑的篡改。
2.Git 的轻量级分支和里程碑
Git中的分支实际上仅是一个包含所指对象校验和(40个字符长度SHA-1 哈希值)的文件,所以创建和销毁一个分支就变得非常廉价。说白了,新建一个分支就是向一个文件写入41个字节(版本号外加一个换行符)那么简单,自然速度就很快了。 Git的实现与项目复杂度无关,它永远可以在几毫秒的时间内完成分支的创建和切换。这和大多数版本控制系统形成了鲜明对比。
Git的分支是完全隔离的,而SVN则没有。分支本来就应该是相对独立的命名空间,一个提交一般只能发生在一个分支中。在Git中,其内部的对象层级依赖关系或许和SVN类似,但是其工作树的视图表现形式和SVN完全不同。工作树永远是一个完整的分支,不同的分支由不同的head索引去构建,你不可能在工作树中同时获得多个分支的内容。
Git使用的标签有两种类型:轻量级的(lightweight)和含附注的(annotated)。① 轻量级标签就像是个不会变化的分支,实际上它就是个指向特定提交对象的引用。② 而含附注标签,实际上是存储在仓库中的一个独立对象,它有自身的校验和信息,包含着标签的名字,电子邮件地址和日期,以及标签说明,标签本身也允许使用GNU Privacy Guard (GPG) 来签署或验证。
Git的里程碑是只读的,Git完全遵守历史不可更改这一时空法则。用户不能向git的里程碑中提交,否则里程碑就不是标记,而成了一个分支。当然Git允许用户删除里程碑再重新创建指定到不同历史提交。
3.多分支间的切换
SVN中提供了一个功能switch,使用switch可以在同一个工作树上,在不同的分支中进行切换。
Git在分支中进行切换使用的命令是checkout。
SVN的特点
1. 每个版本库有唯一的URL(官方地址),每个用户都从这个地址获取代码和数据;
2. 获取代码的更新,也只能连接到这个唯一的版本库,同步以取得最新数据;
3. 提交必须有网络连接(非本地版本库);
4. 提交需要授权,如果没有写权限,提交会失败;
5. 提交并非每次都能够成功。如果有其他人先于你提交,会提示“改动基于过时的版本,先更新再提交”… 诸如此类;
6. 冲突解决是一个提交速度的竞赛:手快者,先提交,平安无事;手慢者,后提交,可能遇到麻烦的冲突解决。
7. 原理上只关心文件内容的具体差异。每次记录有哪些文件作了更新,以及都更新了哪些行的什么内容。
SVN的优点
每个人都可以一定程度上看到项目中的其他人正在做些什么。而管理员也可以轻松掌控每个开发者的权限。
1. 管理方便,逻辑明确。
2. 易于管理,集中式服务器更能保证安全性。
3. 代码一致性非常高。
4. 适合开发人数不多的项目开发。
SVN的缺点
中央服务器的单点故障。若是宕机一小时,那么在这一小时内,谁都无法提交更新、还原、对比等,也就无法协同工作。如果中央服务器的磁盘发生故障,并且没做过备份或者备份得不够及时的话,还会有丢失数据的风险。最坏的情况是彻底丢失整个项目的所有历史更改记录,被客户端提取出来的某些快照数据除外,但这样的话依然是个问题,你不能保证所有的数据都已经有人提取出来。
1. 服务器压力太大,数据库容量暴增。
2. 如果不能连接到服务器上,基本上不可以工作,如果服务器不能连接上,就不能提交,还原,对比等等。
Git的特点
1. Git中每个克隆的版本库都是平等的。可以从任何一个版本库的克隆来创建属于自己的版本库,同时只要你愿意,你的版本库也可以作为源提供给他人。
2. Git的每一次提取操作,实际上都是一次对代码仓库的完整备份。
3. 提交完全在本地完成,无须别人给你授权,你的版本库你作主,并且提交总是会成功。
4. 甚至基于旧版本的改动也可以成功提交,提交会基于旧的版本创建一个新的分支。
5. Git的提交不会被打断,直到你的工作完全满意了,PUSH给他人或者他人PULL你的版本库,合并会发生在PULL和PUSH过程中,不能自动解决的冲突会提示您手工完成。
6. 冲突解决不再像是SVN一样的提交竞赛,而是在需要的时候才进行合并和冲突解决。
7. Git 也可以模拟集中式的工作模式
8. Git版本库统一放在服务器中
9. 可以为 Git 版本库进行授权:谁能创建版本库,谁能向版本库PUSH,谁能够读取(克隆)版本库
10. 团队的成员先将服务器的版本库克隆到本地;并经常的从服务器的版本库拉(PULL)最新的更新;
11. 团队的成员将自己的改动推(PUSH)到服务器的版本库中,当其他人和版本库同步(PULL)时,会自动获取改变
12. Git 的集中式工作模式非常灵活
13. 你完全可以在脱离Git服务器所在网络的情况下,如移动办公/出差时,照常使用代码库
14. 你只需要在能够接入Git服务器所在网络时,PULL和PUSH即可完成和服务器同步以及提交
15. Git提供 rebase 命令,可以让你的改动看起来是基于最新的代码实现的改动
16. Git 有更多的工作模式可以选择,远非SNV可比
Git的优点
1. 适合分布式开发,强调个体。
2. 公共服务器压力和数据量都不会太大
3. 速度快、灵活
4. 任意两个开发者之间可以很容易的解决冲突
5. 离线工作
Git的缺点
1. 代码保密性差,一旦开发者把整个库克隆下来就可以完全公开所有代码和版本信息。
Github是一个基于Git的代码托管平台(最流行的开源托管服务),付费用户可以建私人仓库,免费用户只能使用公共仓库(代码要公开)。
Github 由Chris Wanstrath, PJ Hyett 与Tom Preston-Werner三位开发者在2008年4月创办。
注册github账号。网址:https://github.com/
由于本地 Git 仓库和 GitHub 仓库之间的传输是通过SSH加密的,所以需要配置验证信息。
在本地创建ssh key。终端输入以下命令然后一路回车:
ssh-keygen -t rsa -C "github上注册的邮箱"
成功的话会在~/目录下生成.ssh文件夹,进入该目录并打开id_rsa.pub,复制里面的key,并在Github个人中心的设置中添加ssh key,粘贴。
验证是否成功,终端输入以下命令(第一次会提示是否continue,输入yes)
ssh -T git@github.com
设置username和email(每次提交时会记录谁提交的), 终端输入以下命令,
git config --global user.name "yourName"
git config --global user.email "your_email@youremail.com"
git init
关联远程仓库(origin后面的仓库地址,在Github上复制)
git remote add origin 仓库地址.git
这时在.git/config文件中会多出来,使用git config -e可查看
[remote "origin"]
url = 仓库地址.git
fetch = +refs/heads/*:refs/remotes/origin/*
git clone 仓库地址.git
Gitee(国内的 Git 托管服务,5人以下小团队免费)
国内访问Github速度比较慢,很影响使用。
Gitee 提供免费的 Git 仓库,还集成了代码质量检测、项目演示等功能。对于团队协作开发,Gitee 还提供了项目管理、代码托管、文档管理的服务。
网址:https://gitee.com
1、安装Git
yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-devel
yum install git
创建一个git用户组和用户,用来运行git服务
groupadd git
useradd git -g git
2、创建证书登录
把所有需要登录的用户的公钥导入到/home/git/.ssh/authorized_keys文件里
$ cd /home/git/
$ mkdir .ssh
$ chmod 755 .ssh
$ touch .ssh/authorized_keys
$ chmod 644 .ssh/authorized_keys
3、初始化Git仓库
首先选定一个目录作为Git仓库,假定是/home/gitrepo/runoob.git,在/home/gitrepo目录下输入命令:
$ cd /home
$ mkdir gitrepo
$ chown git:git gitrepo/
$ cd gitrepo
$ git init --bare runoob.git
以上命令Git创建一个空仓库,服务器上的Git仓库通常都以.git结尾。然后,把仓库所属用户改为git:
$ chown -R git:git runoob.git
4. 客户端克隆
git clone git@服务器IP:/home/gitrepo/runoob.git
Git使用方式1: 终端命令
git log --oneline --before={3.weeks.ago} --after={2010-04-18} --no-merges
指定日期。也可以用 --until 和 --since。--no-merges 参数:隐藏合并提交。
git log --author=Linus --oneline -5
查找指定用户的提交日志
git log --graph
查看历史中什么时候出现了分支、合并。
git log -5 --pretty --oneline
显示过去5次提交
git log --stat
显示提交日志,以及每次提交发生变更的文件
git log -S keyword1
根据关键词搜索提交历史
git log tag1 HEAD --pretty=format:%s
显示某个提交之后的所有变动,每个提交占据一行
git log tag1 HEAD --grep feature
显示某个提交之后的所有变动,其"提交说明"必须符合搜索条件
git log --follow file1
显示某个文件的版本历史,包括文件改名
git log -p file1
显示指定文件相关的每一次diff
git shortlog -sn
显示所有提交过的用户,按提交次数排序
git whatchanged file1
显示某个文件的版本历史,包括文件改名
git status
查看工作区文件状态
git diff
暂存区和工作区的不同(查看尚未暂存的更新)
git diff file1
比较文件的不同(暂存区和工作区)
git diff --cached
暂存区和本地仓库的不同
git diff --cached file1
比较文件的不同(暂存区和本地仓库)
git diff HEAD
工作区和本地仓库的不同
git diff HEAD file1
比较文件的不同(工作区和本地仓库)
git diff [first-branch]...[second-branch]
本地仓库两次提交的不同
git diff --shortstat "@{0 day ago}"
显示今天写了多少行代码
git show commit1
显示某次提交的元数据和内容变化
git show --name-only commit1
显示某次提交发生变化的文件
git show commit1:file1
显示某次提交时,某个文件的内容
git reflog
显示当前分支的最近几次提交
git ls-files
查看已经被提交的文件
git pull origin master
从远程仓库origin的master分支拉取代码和当前本地分支合并。git pull 其实就是 git fetch 和 git merge FETCH_HEAD 的简写。
git pull origin master:dev
从远程仓库origin的master分支拉取代码和本地分支dev合并。
git push origin master
将当前本地分支推送到远程仓库origin的master分支并合并(若master不存在则新建)
git push origin dev:master
将dev本地分支推送到远程仓库origin的master分支,如果本地分支名与远程分支名相同,则可以省略冒号。
git push origin --force
强行推送当前分支到远程仓库,即使有冲突
git push origin --all
推送所有分支到远程仓库
git push origin --delete master
删除远程仓库分支master
git tag tag1 commit1
新建一个tag在指定commitID,git log可获取commitID(忘记打标签了,查询过去的提交,然后打标签), 可以少输入几位,只要能确定唯一
git tag -a tag1 -m '注释'
创建带注释的版本标签tag
git tag -d tag1
删除版本标签tag
git push origin tag1
上传本地tag
git push origin --tags
上传所有本地tag到远程仓库
git push origin :refs/tags/tag1
删除远程tag
git pull origin --tags
合并远程仓库的tag到本地
git checkout -b dev tag1
新建一个本地分支dev,指向某个tag1
git init
初始化git仓库(会生成一个隐藏的 .git 目录,该目录包含了资源的所有元数据)。Git 的很多命令都需要在 Git 的仓库中运行,所以 git init 是使用 Git 的第一个命令。默认创建一个master分支。
git clone git@git路径.git
从远程仓库当前分支拉取代码到本地(当前目录下)
git clone git@git路径.git 目录名
从远程仓库当前分支拉取代码到本地(指定目录)
git archive
文件归档打包
git bisect
git cat-file
版本库对象研究工具
git citool
图形化提交,相当于 git gui 命令
git clean
清除工作区未跟踪文件
git describe
通过里程碑直观地显示提交ID
git difftool
调用图形化差异比较工具
git format-patch
创建邮件格式的补丁文件。参见 git am 命令
git grep
文件内容搜索定位工具
git gui
基于Tcl/Tk的图形化工具,侧重提交等操作
git mergetool
图形化冲突解决
git rebase
git rebase–interactive
交互式分支变基
git rev-parse
将各种引用表示法转换为哈希值等
git add–interactive
交互式添加
git apply
git am
应用邮件格式补丁
从远程仓库下载项目
初始化git仓库
git init
从远程仓库当前分支拉取代吗到本地
git clone git@git路径.git
把本地项目上传到Github
初始化git仓库(在项目根目录)
git init
把当前目录下的所有文件添加到暂存区
git add .
提交到本地仓库(-m后跟注释)
git commit -m 'first commint'
关联远程仓库和本地仓库
git remote add origin git@路径.git
查看.ssh目录下是否有id_rsa.pub文件,然后复制其内容在远程仓库配置SSH。
// 如果没有运行 ssh-keygen -t rsa -C "chenxiang698@sina.com" 生成。
cd /Users/cx/.ssh/
从远程仓库拉取
git pull origin master
推送到远程仓库
git push origin master
// git push origin dev:master 本地dev:远程master
可能遇到的错误
错误1:The authenticity of host 'e.coding.net (118.126.70.252)' can't be established.
解决:输入yes回车
错误2: fatal: refusing to merge unrelated histories
解决:git pull origin master --allow-unrelated-histories
错误3:error: src refspec master does not match any
解决:git push origin dev:master
6. 克隆:将代码从远程仓库拉取到本地仓库。
一般刚克隆下来,默认是master分支,需要从远程分支检出Developer分支或新建分支
7. 检出:本地分支检出是切换不同分支,远程分支检出是拉取远程仓库的代码到本地
8. 重置:将代码还原到指定时间轴
9. 未暂存文件:只会保留在缓存区,不会提交到本地仓库,勾选后会转到已暂存文件。已暂存文件:会提交到本地仓库。
10. 双击本地分支,可进行分支切换
11. 可以在终端进行以上相关操作
新建本地仓库
如果是导入本地项目
选择`添加已经存在的本地库`。
如果是导入github项目
选择`从URL克隆`。
1. 回滚某一次提交的部分代码
点击历史 | 点击要回滚的提交 | 点击要回滚的类 | 点击回滚区块或者选中行后点击右上方的回滚行。
2. 回滚某一次提交的全部代码
选中要回滚的提交,右键,选择提交回滚