GIT(分布式版本控制系统)
关于版本控制
什么是“版本控制”?我为什么要关心它呢?版本控制是一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。在本书所展示的例子中,我们对保存着软件源代码的文件作版本控制,但实际上,你可以对任何类型的文件进行版本控制。
如果你是位图形或网页设计师,可能会需要保存某一幅图片或页面布局文件的所有修订版本(这或许是你非常渴望拥有的功能),采用版本控制系统(VCS)是个明智的选择。有了它你就可以将某个文件回溯到之前的状态,甚至将整个项目都回退到过去某个时间点的状态,你可以比较文件的变化细节,查出最后是谁修改了哪个地方,从而找出导致怪异问题出现的原因,又是谁在何时报告了某个功能缺陷等等。使用版本控制系统通常还意味着,就算你乱来一气把整个项目中的文件改的改删的删,你也照样可以轻松恢复到原先的样子。但额外增加的工作量却微乎其微。
本地版本控制系统
许多人习惯用复制整个项目目录的方式来保存不同的版本,或许还会改名加上备份时间以示区别。这么做唯一的好处就是简单。不过坏处也不少:有时候会混淆所在的工作目录,一旦弄错文件丢了数据就没法撤销恢复。
为了解决这个问题,人们很久以前就开发了许多种本地版本控制系统,大多都是采用某种简单的数据库来记录文件的历次更新差异。
本地版本控制图解
其中最流行的一种叫做 rcs,现今许多计算机系统上都还看得到它的踪影。甚至在流行的 Mac OS X 系统上安装了开发者工具包之后,也可以使用 rcs 命令。它的工作原理基本上就是保存并管理文件补丁(patch)。文件补丁是一种特定格式的文本文件,记录着对应文件修订前后的内容变化。所以,根据每次 修订后的补丁,rcs 可以通过不断打补丁,计算出各个版本的文件内容。
集中化的版本控制系统
接下来人们又遇到一个问题,如何让在不同系统上的开发者协同工作?于是,集中化的版本控制系统( Centralized Version Control Systems,简称 CVCS )应运而生。这类系统,诸如 CVS,Subversion 以及 Perforce 等,都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新。多年以来,这 已成为版本控制系统的标准做法。
集中化的版本控制图解
这种做法带来了许多好处,特别是相较于老式的本地 VCS 来说。现在,每个人都可以在一定程度上看到项目中的其他人正在做些什么。而管理员也可以轻松掌控每个开发者的权限,并且管理一个 CVCS 要远比在各个客户端上维护本地数据库来得轻松容易。
事分两面,有好有坏。这么做最显而易见的缺点是中央服务器的单点故障。如果宕机一小时,那么在这一小时内,谁都无法提交更新,也就无法协同工作。要 是中央服务器的磁盘发生故障,碰巧没做备份,或者备份不够及时,就还是会有丢失数据的风险。最坏的情况是彻底丢失整个项目的所有历史更改记录,而被客户端 提取出来的某些快照数据除外,但这样的话依然是个问题,你不能保证所有的数据都已经有人事先完整提取出来过。本地版本控制系统也存在类似问题,只要整个项 目的历史记录被保存在单一位置,就有丢失所有历史更新记录的风险。
分布式版本控制系统
于是分布式版本控制系统( Distributed Version Control System,简称 DVCS )面世了。在这类系统中,像 Git,Mercurial,Bazaar 以及 Darcs 等,客户端并不只提取最新版本的文件快照,而是把原始的代码仓库完整地镜像下来。这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜 像出来的本地仓库恢复。因为每一次的提取操作,实际上都是一次对代码仓库的完整备份
分布式版本控制图解
更进一步,许多这类系统都可以指定和若干不同的远端代码仓库进行交互。籍此,你就可以在同一个项目中,分别和不同工作小组的人相互协作。你可以根据需要设定不同的协作流程,比如层次模型式的工作流,而这在以前的集中式系统中是无法实现的。
Git 简史
同生活中的许多伟大事物一样,Git 诞生于一个极富纷争大举创新的年代。
Git — The stupid content tracker, 傻瓜内容跟踪器。Linus Torvalds 是这样给我们介绍 Git 的。
Git 是用于 Linux内核开发的版本控制工具。与常用的版本控制工具 CVS, Subversion 等不同,它采用了分布式版本库的方式,不必服务器端软件支持(wingeddevil注:这得分是用什么样的服务端,使用http协议或者git协议等不太一样。并且在push和pull的时候和服务器端还是有交互的。),使源代码的发布和交流极其方便。 Git 的速度很快,这对于诸如 Linux kernel 这样的大项目来说自然很重要。 Git 最为出色的是它的合并跟踪(merge tracing)能力。
实际上内核开发团队决定开始开发和使用 Git 来作为内核开发的版本控制系统的时候,世界开源社群的反对声音不少,最大的理由是 Git 太艰涩难懂,从 Git 的内部工作机制来说,的确是这样。但是随着开发的深入,Git 的正常使用都由一些友好的脚本命令来执行,使 Git 变得非常好用,即使是用来管理我们自己的开发项目,Git 都是一个友好,有力的工具。现在,越来越多的著名项目采用 Git 来管理项目开发.
作为开源自由原教旨主义项目,Git 没有对版本库的浏览和修改做任何的权限限制。
Linux 内核开源项目有着为数众广的参与者。绝大多数的 Linux 内核维护工作都花在了提交补丁和保存归档的繁琐事务上(1991-2002年间)。到 2002 年,整个项目组开始启用一个专有的分布式版本控制系统 BitKeeper 来管理和维护代码。
到了 2005 年,开发 BitKeeper 的商业公司同 Linux 内核开源社区的合作关系结束,他们收回了 Linux 内核社区免费使用 BitKeeper 的权力。这就迫使 Linux 开源社区(特别是 Linux 的缔造者 Linux Torvalds)基于使用 BitKcheper 时的经验教训,开发出自己的版本系统。他们对新的系统制订了若干目标:
速度
简单的设计
对非线性开发模式的强力支持(允许成千上万个并行开发的分支)
完全分布式
有能力高效管理类似 Linux 内核一样的超大规模项目(速度和数据量)
自诞生于 2005 年以来,Git 日臻成熟完善,在高度易用的同时,仍然保留着初期设定的目标。它的速度飞快,极其适合管理大项目,有着令人难以置信的非线性分支管理系统(参见 Git 分支)。
起步 – Git 基础
Git 基础
那么,简单地说,Git 究竟是怎样的一个系统呢?请注意接下来的内容非常重要,若你理解了 Git 的思想和基本工作原理,用起来就会知其所以然,游刃有余。在开始学习 Git 的时候,请努力分清你对其它版本管理系统的已有认识,如 Subversion 和 Perforce 等;这么做能帮助你使用工具时避免发生混淆。 Git 在保存和对待各种信息的时候与其它版本控制系统有很大差异,尽管操作起来的命令形式非常相近,理解这些差异将有助于防止你使用中的困惑。
直接记录快照,而非差异比较
Git 和其它版本控制系统(包括 Subversion 和近似工具)的主要差别在于 Git 对待数据的方法。概念上来区分,其它大部分系统以文件变更列表的方式存储信息。这类系统(CVS、Subversion、Perforce、Bazaar 等等)将它们保存的信息看作是一组基本文件和每个文件随时间逐步累积的差异。
存储每个文件与初始版本的差异。
Git 不按照以上方式对待或保存数据。反之,Git 更像是把数据看作是对小型文件系统的一组快照。每次你提交更新,或在 Git 中保存项目状态时,它主要对当时的全部文件制作一个快照并保存这个快照的索引。为了高效,如果文件没有修改,Git 不再重新存储该文件,而是只保留一个链接指向之前存储的文件。 Git 对待数据更像是一个 快照流。
Git 存储项目随时间改变的快照。
这是 Git 与几乎所有其它版本控制系统的重要区别。因此 Git 重新考虑了以前每一代版本控制系统延续下来的诸多方面。 Git 更像是一个小型的文件系统,提供了许多以此为基础构建的超强工具,而不只是一个简单的 VCS。稍后我们在Git 分支讨论 Git 分支管理时,将探究这种方式对待数据所能获得的益处。
更多详情见请继续阅读下一页的精彩内容: http://www.linuxidc.com/Linux/2017-06/144961p2.htm
近乎所有操作都是本地执行
在 Git 中的绝大多数操作都只需要访问本地文件和资源,一般不需要来自网络上其它计算机的信息。如果你习惯于所有操作都有网络延时开销的集中式版本控制系统,Git 在这方面会让你感到速度之神赐给了 Git 超凡的能量。因为你在本地磁盘上就有项目的完整历史,所以大部分操作看起来瞬间完成。
举个例子,要浏览项目的历史,Git 不需外连到服务器去获取历史,然后再显示出来——它只需直接从本地数据库中读取。你能立即看到项目历史。如果你想查看当前版本与一个月前的版本之间引入的修改,Git 会查找到一个月前的文件做一次本地的差异计算,而不是由远程服务器处理或从远程服务器拉回旧版本文件再来本地处理。
这也意味着你离线或者没有 VPN 时,几乎可以进行任何操作。如你在飞机或火车上想做些工作,你能愉快地提交,直到有网络连接时再上传。如你回家后 VPN 客户端不正常,你仍能工作。使用其它系统,做到如此是不可能或很费力的。比如,用 Perforce,你没有连接服务器时几乎不能做什么事;用 Subversion 和 CVS,你能修改文件,但不能向数据库提交修改(因为你的本地数据库离线了)。这看起来不是大问题,但是你可能会惊喜地发现它带来的巨大的不同。
Git 保证完整性
Git 中所有数据在存储前都计算校验和,然后以校验和来引用。这意味着不可能在 Git 不知情时更改任何文件内容或目录内容。这个功能建构在 Git 底层,是构成 Git 哲学不可或缺的部分。若你在传送过程中丢失信息或损坏文件,Git 就能发现。
Git 用以计算校验和的机制叫做 SHA-1 散列(hash,哈希)。这是一个由 40 个十六进制字符(0-9 和 a-f)组成字符串,基于 Git 中文件的内容或目录结构计算出来。 SHA-1 哈希看起来是这样:
24b9da6552252987aa493b52f8696cd6d3b00373
Git 中使用这种哈希值的情况很多,你将经常看到这种哈希值。实际上,Git 数据库中保存的信息都是以文件内容的哈希值来索引,而不是文件名。
Git 一般只添加数据
你执行的 Git 操作,几乎只往 Git 数据库中增加数据。很难让 Git 执行任何不可逆操作,或者让它以任何方式清除数据。同别的 VCS 一样,未提交更新时有可能丢失或弄乱修改的内容;但是一旦你提交快照到 Git 中,就难以再丢失数据,特别是如果你定期的推送数据库到其它仓库的话。
这使得我们使用 Git 成为一个安心愉悦的过程,因为我们深知可以尽情做各种尝试,而没有把事情弄糟的危险。更深度探讨 Git 如何保存数据及恢复丢失数据的话题,请参考撤消操作。
三种状态
好,请注意。如果你希望后面的学习更顺利,记住下面这些关于 Git 的概念。 Git 有三种状态,你的文件可能处于其中之一:已提交(committed)、已修改(modified)和已暂存(staged)。已提交表示数据已经安全的保存在本地数据库中。已修改表示修改了文件,但还没保存到数据库中。已暂存表示对一个已修改文件的当前版本做了标记,使之包含在下次提交的快照中。
由此引入 Git 项目的三个工作区域的概念:Git 仓库、工作目录以及暂存区域。
工作目录、暂存区域以及 Git 仓库。
Git 仓库目录是 Git 用来保存项目的元数据和对象数据库的地方。这是 Git 中最重要的部分,从其它计算机克隆仓库时,拷贝的就是这里的数据。
工作目录是对项目的某个版本独立提取出来的内容。这些从 Git 仓库的压缩数据库中提取出来的文件,放在磁盘上供你使用或修改。
暂存区域是一个文件,保存了下次将提交的文件列表信息,一般在 Git 仓库目录中。有时候也被称作“索引”,不过一般说法还是叫暂存区域。
对于任何一个文件,在 Git 内都只有三种状态:
中文 | 英文 | 含义 |
已提交 | committed | 已提交表示该文件已经被安全地保存在本地数据库中了 |
已修改 | modified | 已修改表示修改了某个文件,但还没有提交保存 |
已暂存 | staged | 已暂存表示把已修改的文件放在下次提交时要保存的清单中 |
git中还有三类常用对象(实际不止三种),理解这三类对象也很重要。分别为:
blob,用于表示一个文件
tree,用于表示一个目录,索引到若干文件或子目录
commit,用于表示一次提交(commit)
所有对象都会以文件的形式保存在.git/objects目录,一个对象一个文件。
目录 用法
git 目录 它是 Git 用来保存元数据和对象数据库的地方。该目录非常重要,每次克隆镜像仓库的时候,实际拷贝的就是这个目录里面的数据。
工作目录 从项目中取出某个版本的所有文件和目录,用以开始后续工作的叫做工作目录。这些文件实际上都是从 git 目录中的压缩对象数据库中提取出来的,接下来就可以在工作目录中对这些文件进行编辑
暂存区域 所谓的暂存区域只不过是个简单的文件,一般都放在 git 目录中。有时候人们会把这个文件叫做索引文件,不过标准说法还是叫暂存区域。
基本的 Git 工作流程如下:
1.在工作目录中修改文件。
2.暂存文件,将文件的快照放入暂存区域。
3.提交更新,找到暂存区域的文件,将快照永久性存储到 Git 仓库目录。
如果 Git 目录中保存着的特定版本文件,就属于已提交状态。如果作了修改并已放入暂存区域,就属于已暂存状态。如果自上次取出后,作了修改但还没有放到暂存区域,就是已修改状态。在Git 基础一章,你会进一步了解这些状态的细节,并学会如何根据文件状态实施后续操作,以及怎样跳过暂存直接提交。
Git 使用规范
特别提醒:
使用Git过程中,必须通过创建分支进行开发,坚决禁止在主干分支上直接开发。review的同事有责任检查其他同事是否遵循分支规范。
在Git中,默认是不会提交空目录的,如果想提交某个空目录到版本库中,需要在该目录下新建一个 .gitignore 的空白文件,就可以提交了
【代码回溯注意】把外部文件纳入到自己的 Git 分支来的时候一定要记得是先比对,确认所有修改都是自己修改的,然后再纳入。不然,容易出现代码回溯
【代码回溯注意】多人协作时,不要各自在自己的 Git 分支开发,然后发文件合并。正确的方法应该是开一个远程分支,然后一起在远程分支里协作。不然,容易出现代码回溯(即别人的代码被覆盖的情况)
【代码回溯注意】每个人提交代码是一定要 git diff 看提交的东西是不是都是自己修改的。如果有不是自己修改的内容,很可能就是代码回溯
【代码回溯注意】review 代码的时候如果看到有被删除掉的代码,一定要确实是否是写代码的同事自己删除的。如果不是,很可能就是代码回溯
分支合并及上线
步骤 | Git 操作 |
克隆代码 | git clone 远程代码 |
创建分支 | git checkout -b branch_name |
在分支中开发 | 无 |
review代码 | 无 |
第一轮测试 | 无 |
添加代码到分支的暂存区 | git add somefile |
提交代码到分支 | git commit -m “本次提交的注释” |
切换到主版本 | git checkout master |
获取远程最新代码 | git pull origin master |
合并某分支到master分支 | git merge branch_name |
解决合并时产生的冲突 请参考分支合并时冲突的解决 | 无 |
第二轮测试 | 无 |
准备上线文档 | 无 |
获取远程最新代码 | git pull origin master |
推送master分支 | git push origin master |
通知上线 | 无 |
没有问题了删除本地分支 | git branch -d branch_name |
配置 Git
以下命令为配置 Git 相关信息,以下两项必须要配置��会出现在每次提交的信息里。
git config –global user.name “John” #规定为姓名全拼
git config –global user.email “John@linuxidc.com” #规定为公司邮箱
git config –global merge.tool “meld” #可视化的合并工具
git config –global color.ui true # 使用git默认的配色方案,推荐
git config –global –list # 查看配置信息
git config –global user.name # 查看 user.name 的配置信息Git的功能特性:
从一般开发者的角度来看,git有以下功能:
12345678 1、从服务器上克隆完整的Git仓库(包括代码和版本信息)到单机上。
2、在自己的机器上根据不同的开发目的,创建分支,修改代码。
3、在单机上自己创建的分支上提交代码。
4、在单机上合并分支。
5、把服务器上最新版的代码fetch下来,然后跟自己的主分支合并。
6、生成补丁(patch),把补丁发送给主开发者。
7、看主开发者的反馈,如果主开发者发现两个一般开发者之间有冲突(他们之间可以合作解决的冲突),就会要求他们先解决冲突,然后再由其中一个人提交。如果主开发者可以自己解决,或者没有冲突,就通过。
8、一般开发者之间解决冲突的方法,开发者之间可以使用pull 命令解决冲突,解决完冲突之后再向主开发者提交补丁。
从主开发者的角度(假设主开发者不用开发代码)看,git有以下功能:
123 1、查看邮件或者通过其它方式查看一般开发者的提交状态。
2、打上补丁,解决冲突(可以自己解决,也可以要求开发者之间解决以后再重新提交,如果是开源项目,还要决定哪些补丁有用,哪些不用)。
3、向公共服务器提交结果,然后通知所有开发人员。
优点:
适合分布式开发,强调个体。
公共服务器压力和数据量都不会太大。
速度快、灵活。
任意两个开发者之间可以很容易的解决冲突。
离线工作。
缺点:
资料少(起码中文资料很少)。
学习周期相对而言比较长。
不符合常规思维。
代码保密性差,一旦开发者把整个库克隆下来就可以完全公开所有代码和版本信息。
部署 Git
依赖库:
yum install -y curl-devel expat-devel gettext-devel openssl-devel zlib-devel asciidoc xmlto docbook2x perl-ExtUtils-Embed texinfo
yum install -y tk zlib-devel openssl-devel perl cpio expat-devel gettext-devel asciidoc xmlto openjade perl-XML-SAX
docbook2x 这个库需要额外安装123 wget ftp://ftp.is.co.za/mirror/Fedora.RedHat.com/epel/6/x86_64/docbook2X-0.8.8-1.el6.x86_64.rpm
rpm -ivh docbook2X-0.8.8-1.el6.x86_64.rpm
ln -s /usr/bin/db2x_docbook2texi /usr/bin/docbook2x-texi
当你安装好所有的必要依赖,你可以继续从几个地方来取得最新发布版本的 tar 包。
你可以从
Kernel.org 网站获取,网址为 https://www.kernel.org/pub/software/scm/git,
GitHub 网站上的镜像来获得,网址为 https://github.com/git/git/releases。
通常在 GitHub 上的是最新版本,但 kernel.org 上包含有文件下载签名,如果你想验证下载正确性的话会用到。编译安装:wget https://www.kernel.org/pub/software/scm/git/git-2.10.2.tar.gz –no-check-certificate
tar -zxvf git-2.10.2.tar.gz
cd git-2.10.2
make clean 如果时间长,出现warning,可能是系统时间问题
[root@John git-2.10.2]# make configure
GIT_VERSION = 2.10.2
GEN configure
./configure –prefix=/usr
make all doc info
make install install-doc install-html install-info
创建新仓库
获取与创建项目
你得先有一个 Git 仓库,才能用它进行操作。仓库是 Git 存放你要保存的快照的数据的地方。
拥有一个 Git 仓库的途径有两种。在已有的目录中,初始化一个新的,
其一。 比如一个新的项目,或者一个已存在的项目,但该项目尚未有版本控制。
如果你想要复制一份别人的项目, 或者与别人合作某个项目,也可以从一个公开的 Git 仓库克隆,
其二。本章将对两者都做介绍。
创建新文件夹,打开,然后执行
git init
以创建新的 git 仓库。
[root@John /]# ls -a
. .autofsck bin cgroup etc home lib64 media mnt opt root selinux sys usr www
.. .autorelabel boot dev .git lib lost+found misc net proc sbin srv tmp var
[root@John /]# cd .git/
[root@John .git]# ls
branches config description HEAD hooks info objects refs
该目录下可能还会包含其他文件,不过对于一个全新的 git init 版本库,这将是你看到的默认结构。
description 文件仅供 GitWeb 程序使用,我们无需关心。
config 文件包含项目特有的配置选项。
info 目录包含一个全局性排除(global exclude)文件,
用以放置那些不希望被记录在 .gitignore 文件中的忽略模式(ignored patterns)。
hooks 目录包含客户端或服务端的钩子脚本(hook scripts),
在 Git 钩子 中这部分话题已被详细探讨过。
剩下的四个条目很重要:
HEAD 文件、(尚待创建的)index 文件,和 objects 目录、refs 目录。
这些条目是 Git 的核心组成部分。
objects 目录存储所有数据内容;
refs 目录存储指向数据(分支)的提交对象的指针;
HEAD 文件指示目前被检出的分支;
index 文件保存暂存区信息。
我们将详细地逐一检视这四部分,以期理解 Git 是如何运转的。
恭喜,现在你就有了一个 Git 仓库的架子,可以开始快照你的项目了。
简而言之,用 git init 来在目录中创建新的 Git 仓库。
你可以在任何时候、任何目录中这么做,完全是本地化的。
用户信息:
当安装完 Git 应该做的第一件事就是设置你的用户名称与邮件地址。
这样做很重要,因为每一个 Git 的提交都会使用这些信息,并且它会写入到你的每一次提交中,不可更改:
[root@John git-2.10.2]# git config –global user.name ‘John Doe’
[root@John git-2.10.2]# git config –global user.email XXOO@126.com
再次强调,如果使用了 –global 选项,那么该命令只需要运行一次,因为之后无论你在该系统上做任何事情, Git 都会使用那些信息。 当你想针对特定项目使用不同的用户名称与邮件地址时,可以在那个项目目录下运行没有 –global 选项的命令来配置。
很多 GUI 工具都会在第一次运行时帮助你配置这些信息。
获取帮助
若你使用 Git 时需要获取帮助,有三种方法可以找到 Git 命令的使用手册:
$ git help <verb>
$ git <verb> –help
$ man git-<verb>
例如,要想获得 config 命令的手册,执行
$ git help config
这些命令很棒,因为你随时随地可以使用而无需联网。
如果你觉得手册或者本书的内容还不够用,你可以尝试在 Freenode IRC 服务器( irc.freenode.net )的 #git 或 #github 频道寻求帮助。
这些频道经常有上百人在线,他们都精通 Git 并且乐于助人。
总结
你应该已经对 Git 是什么、Git 与你可能正在使用的集中式版本控制系统有何区别等问题有了基本的了解。
现在,在你的个人系统中应该也有了一份能够工作的 Git 版本。
是时候开始学习有关 Git 的基础知识了。
基本用法
上面的四条命令在工作目录、暂存目录(也叫做索引)和仓库之间复制文件。
git add files 把当前文件放入暂存区域。
git commit 给暂存区域生成快照并提交。
git reset — files 用来撤销最后一次git add files,你也可以用git reset 撤销所有暂存区域文件。
git checkout — files 把文件从暂存区域复制到工作目录,用来丢弃本地修改。
你可以用 git reset -p, git checkout -p, or git add -p进入交互模式。
也可以跳过暂存区域直接从仓库取出文件或者直接提交代码。
git commit -a 相当于运行 git add 把所有当前目录下的文件加入暂存区域再运行。git commit.
git commit files 进行一次包含最后一次提交加上工作目录中文件快照的提交。并且文件被添加到暂存区域。
git checkout HEAD — files 回滚到复制最后一次提交。
git基本命令:
设置用户名:git config –global user.name”your name”
设置Email:git config –global user.email”email name”
创建目录:mkdir name
退回目录:cd name
显示当前目录:pwd
把目录变成git可管理的仓库:git init
添加文件到仓库:git add 文件名
提交文件到仓库:git commit
掌握仓库当前的状态:git status
查看具体修改内容:git diff
查看历史记录:git log
回退版本:git reset –hard HEAD^
恢复回退的版本:git reset –hard 版本号
记录每一次记录:git reflog
把readme.txt文件在工作区的修改全部撤销:git checkout –文件名
删除一个文件:git rm 文件名
版本库里的版本替换工作区的版本:git checkout — 文件名
创建SSH key:ssh-keygen -t rsa -C “邮箱名称”
本地关联远程数据库:git remote add origin git@git.linuxidc.net:John.com/testGit.git
git@git.linuxidc.net:John/newFile.git
git@git.linuxidc.net:John/helloWord.git
将本地数据推送到远程仓库中:git push -u origin master(注意本地仓库名称一定跟远程仓库名称相同)
将远程仓库中的数据下载到本地仓库:git clone git@git.linuxidc.net:John.com/gitSkills.git
创建并切换分支:git checkout -b 分支名称
查询所有分支:git branch
切换分支:git checkout 分支名称
合并指定分支到当前分支:git merge dev
删除分支:git branch -d 分支名称
合并分支:git merge 分支名称
合并分支禁用Fast forward:git merger –no-ff -m “注释” dev
把当前现场“储存起来”:git stash
查看工作现场列表:git stash list
恢复现场:1 git stash apply stash内容并不删除,你需要用git stash drop来删除;
2 git stash pop 恢复的同时把stash内容也删了
强行删除分支:git branch -D 分支名
将分支推送到远程仓库的对应分支上:git push origin 分支名称
抓取最新的提交从远程仓库中:git pull
建立本地分支和远程分支的关联:git branch –set-upstream dev origin/dev
在本地创建和远程分支对应的分支:git checkout -b branch-名字 origin/branch-名字
创建标签:git tag 标签名
查看标签:git tag
对指定的commit打标签:git tag commitId
创建带说明的标签:git tag -a 标签名 -m “说明” commit的ID
删除标签:git tag -d 标签名
推送标签名到远程:git push origin 标签名
一次性推送所有的标签名:git push origin –tags
从远程仓库删除标签:gitpush origin :refs/tags/标签名
为命令配置别名:git config –global alias.st status
git总结:
注意事项:
#1. 多提交(相当于多保存,多^S):
在Git中任何已提交的东西几乎总是可以恢复的。 甚至那些被删除的分支中的提交或使用 –amend 选项覆盖的提交也可以恢复。 然而,任何你未提交的东西丢失后很可能再也找不到了。
#2. 拉取别人数据之前要提交。减少工作区,暂存区数据冲突的可能。
#3. 推送之前先拉取。即将自已的版本做为最新之前,要先合并别人的修改。
#4. 切换分支前要提交,否则有可能数据丢失。即保存在此分支的修改。
#5. 合并分支之前要提交。拉取视情况而定(建议拉取)。
#6. 慎用 git checkout — <file>
撤消对文件的修改(拷贝了另一个文件来覆盖它), 除非你确实清楚不想要那个文件了,否则不要使用这个命令。
#7. 尝试让每一个提交成为一个逻辑���的独立变更集。一个问题一个提交,这个提交包含了这个问题的全部修改。
#8. 最后一件要牢记的事是提交信息。 有一个创建优质提交信息的习惯会使 Git 的使用与协作容易的多。
#9. 变基:只对尚未推送或分享给别人的本地修改执行变基操作清理历史,从不对已推送至别处的提交执行变基操作。
#10. SourceTree GitFlow快捷键(mac):alt + 花 + F 异常惊艳
环境搭建
#1:安装
Mac: Mac苹果系统: git2.10.1 + sourceTree2.3.2
Win: Windows 64 bit: git2.5.1 + TortoiseGit + zh
#2:配置用户信息
Win: 设置 —> Git
Mac: sourcetree —> 偏好设置 —-> 通用
#3: 配置密钥
Win: ????
Mac: $ ssh -keygen -t rsa -C youremail@example.com (一路yes或null就可以)
会存储在:/Users/mac/.ssh/*.pub
#4: 避免每次输入密码(未验证)
git config –global credential.helper cache
#5:仓库划分和SVN相同。
Art:加工 3dMax, ps的资源成为unity使用的 *.ab
Design: 提供服务器和客户端用的表
Public.ResPackage: 存Art生成的资源
Public.PackedVersionConfig: 制做和存储version.txt文件及热更新文件
Public.TTDS_apk:存安装包
Server:服务器
Client:客户端
#6: Clone工程
a. 创建目标目录
b. Win: —-
Mac: git clone client@10.1.10.100:tiantiandiansha.git
c. 显示隐藏文件 + 把(.git + 其它调整到合适位置)
d. 用工具打开:菜单—> 仓库—> git flow —> 初使化仓库
确认本工程在 Develop分支
分支
#1: 分支的划分和目的(git flow)
a. 线上问题
master[主干]: 备份线上版本。
hotfix[临时]: 修复线上bug。
b. 日常开发
develop[主干]: 功能集成。
features[临时]: 日常开发。
c. 发布
master[主干]: 根据Tag发布版本。
release[临时]: develop某些功能达到可发布程度,创建此分支,把功能集成到master,加Tag以备发版打包。
#2:在哪个分支上High
a. 具体操做都在分支中完成,主干只负责数据集成。减少冲突方便并行。
把主干当做单纯的数据源,分支是一个独立的空间。一个操做可以表述为,为达到一个目的从某个数据源取得数据、创建空间;然后在这个独立的空间里对其进行处理;处理完成之后再更新到某些数据源。一个操做结束。此分支也完成了他的使命。
b. features:日常功能开发 + develop上的bug修复。
c. hotfix: 线上bug修复。
在master主干根据Tag创建,完成之后是否合并到develop或master需要根据develp或master的后续版本是否已修改来定。未修改的需要合并到develop+master。已修改的可以直接在hotfix分支打包发布。另外是否合并还要考虑是否是临时性暴力修复。
注意:hotfix分支是从master旧的版本创建来的,合并时请注意莫回档。
d. release:目的是把develop成熟的功能合并到master并打Tag。供后面按需发布。不在release分支做bug修复。
e. bug修复:
develop主干bug:由features分支修复。
master线上bug:由hotfix分支修复。
master未上线bug:由release 或 features + release修复(只果只用features需要手动将分支合并到master)。
#3:分支切换
a. 未跟踪的文件:显示在了工作区。因未纳入任何分支,所以所有分支都可填加。
在目标分区提交了原分区未暂存的文件,切换回原分支,原未暂存文件丢失。
b. 已暂存未提交(新增加的文件,开始跟踪):
不放弃本地变更,到目标分支时还在已暂存未提交状态(sourceTree功能,将原分支的暂存状态copy过来了)。
放弃本地变更,目标分支不存在已暂存未提交文件。再切回原分支原已暂存未提交文件丢失。(放弃本地变更==放弃填加文件)
已暂存未提交(提交后再次修改):
不放弃本地变更, sourTree不允许。提示,先提交或隐藏(stash),之后再切换。
放弃本地变更,目标分支不存在已暂存未提交文件。再切回原分支,原已暂存的修改丢失。(放弃本地变更==放弃已暂存文件修改)
说明:切换分支时,将丢弃原分支已暂存的修改。
c. 已提交的文件:原分支已提交的文件不会带入新的分支。
请牢记:当你切换分支的时候,Git 会重置你的工作目录,使其看起来像回到了你在那个分支上最后一次提交的样子。 Git 会自动添加、删除、修改文件以确保此时你的工作目录和这个分支最后一次提交时的样子一模一样。
总结:切换分支时,应保存(提交或隐藏)本分支的操作。否则切换回来后,未保存的内容将丢失。 因为当前工作区和暂存区只有一份,切换分支时要清除属于原分支的内容。未跟踪或首次暂存内容可进入新分支是工具的优化。
#4: 分支合并
何时会合并:
a. 用git pull从远端拉取时会合并(git pull = git fetch + git merge)。
b. 主动合并分支时:如git flow的 features release 完成时。
合并方法:
在 Git 中整合来自不同分支的修改主要有两种方法:merge 以及 rebase(变基)
a. 未分叉情况:快进方式(fast-forward)
由于当前 master 分支所指向的提交是你当前提交(有关 hotfix 的提交)的直接上游,所以 Git 只是简单的将指针向前移动。 换句话说,当你试图合并两个分支时,如果顺着一个分支走下去能够到达另一个分支,那么 Git 在合并两者的时候,只会简单的将指针向前推进(指针右移),因为这种情况下的合并操作没有需要解决的分歧——这就叫做 “快进(fast-forward)”
b. 分叉情况:
在这种情况下,你的开发历史从一个更早的地方开始分叉开来(diverged)。 因为,master 分支所在提交并不是 iss53 分支所在提交的直接祖先,Git 不得不做一些额外的工作。 出现这种情况的时候,Git 会使用两个分支的末端所指的快照(C4 和 C5)以及这两个分支的工作祖先(C2),做一个简单的三方合并。
和之间将分支指针向前推进所不同的是,Git 将此次三方合并的结果做了一个新的快照并且自动创建一个新的提交指向它。 这个被称作一次合并提交,它的特别之处在于他有不止一个父提交。
冲突:
a. 只有合并时才会有冲突。
b. 文件冲突时,Git已经完成了合并(有冲突标记,此时冲突的文件应是已修改未暂存状态),但是没有自动地创建一个新的合并提交。此时Git会暂停下来,等待你去解决合并产生的冲突。
在你解决了所有文件里的冲突之后,对每个文件使用 git add 命令来将其标记为冲突已解决。 一旦暂存这些原本有冲突的文件,Git 就会将它们标记为冲突已解决。
变基:
a. 原理:
它的原理是首先找到这两个分支(即当前分支 experiment、变基操作的目标基底分支 master)的最近共同祖先 C2,然后对比当前分支相对于该祖先的历次提交,提取相应的修改并存为临时文件,然后将当前分支指向目标基底 C3, 最后以此将之前另存为临时文件的修改依序应用。
这两种整合方法(merge和rebase)的最终结果没有任何区别,但是变基使得提交历史更加整洁。
b. 风险:
变基也并非完美无缺,要用它得遵守一条准则:不要对在你的仓库外有副本的分支执行变基。
总的原则是,只对尚未推送或分享给别人的本地修改执行变基操作清理历史,从不对已推送至别处的提交执行变基操作,这样,你才能享受到两种方式带来的便利。
发版相关
a. 使用 master分支发版(旧版本 + 新版本),所以需将开发分支(develop)的修改合并到master。
b. 哪个仓库需合并到master:
Art: ??(负责生成 AssesBundle的人?)
Design: ??
Server: 客户端发版的人
Client:服务器发版的人
Public: 客户端发版的人 或 制做热更新包的人
c. 合并到master分支的时间:
Art: 成功发版后和加Tag一起
Design: 成功发版后和加Tag一起
Public: 成功发版后和加Tag一起
Server: 发版结点,开发分支测试通过后。
Client:发版结点,开发分支测试通过后。
d. 加Tag:
成功发版后给master分支加Tag. 以备以后切换到此Tag当时的版本改bug或发版本。
由合并到master的人加Tag.
注意:为保证根据Tag找到所有数据,tag不能漏加,tag格式一致(至少含相同版本号,精确到资源版本号 1.0.38.0.0 )。
基础:
详情请参考:https://git-scm.com/book/zh/v2
#1. 文件状态及对应的工作区
Git状态:已修改(modified),已暂存(staged),已提交(committed)—– 只管理已跟踪文件
工作区: 工作目录, 暂存区, Git仓库
文件状态:未跟踪 已跟踪(未修改,已修改,已暂存)
未跟踪:新加入当前分支,从未暂存(git add)过的文件。
已跟踪:已提交到Git仓库或暂存过的文件
已跟踪已暂存:暂存区中的文件(git add过)。
已跟踪已修改:工作区中的文件。
已跟踪未修改:已提交到Git仓库的文件(git commit过)。
#2. 暂存:
1. 暂存操作会为每一个文件计算校验和(使用SHA-1哈希算法)
2. 然后会把当前版本的文件快照保存到Git仓库中(Git使用blob对象来保存它们)
3. 最终将校验和加入到暂存区域等待提交。
#3. 提交:
1. 创建树对象并保存到Git仓库:
Git会先计算当前分支的每一个子目录的校验和,然后在Git仓库中将这些校验和保存为树对象。
2. 创建提交对象并保存到Git仓库:
随后,Git便会创建一个提交对象(commit object)。提交对象保存的内容:
a. 包含了作者的姓名和邮箱、提交时输入的信息。
b. 指向它的父对象的指针。(首次提交产生的提交对象没有父对象,普通提交有一个父对象,而由多个分支合并产生的提交对象有多个父对象)
c. 还包含指向1创建的树对象(项目根目录)的指针。
3. 注意:提交只提交暂存区中的文件(修改过但未暂存的文件不会被提交)。
暂存提交之后:Git 仓库中有五个对象:
a. 三个 blob 对象(暂存时保存的文件快照)
b. 一个树对象(记录着目录结构和 blob 对象索引)
c. 以及一个提交对象(包含着指向前述树对象的指针和所有提交信息)。
#4. 分支
a. Git的分支,其实本质上仅仅是指向提交对象的可变指针(Tag是不变的指针)。
b. HEAD: 在 Git 中,它是一个指针,指向当前所在的本地分支(译注:将 HEAD 想象为当前分支的别名)。
https://git-scm.com/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E5%88%86%E6%94%AF%E7%AE%80%E4%BB%8B
[个人理解]: 拉取,抓取,推送,都是对提交对象的下载上传,分支进度就是改变分支指针指向不同的对象。
#5. 抓取:git fetch [remote-name]
git fetch: 命令与一个远程的仓库交互,并且将远程仓库中有但是在当前仓库的没有的所有信息拉取下来然后存储在你本地数据库中。
#6. 拉取:
a. git pull: 命令基本上就是 git fetch 和 git merge 命令的组合体。
b. 完整格式:git pull <远程主机名> <远程分支名>:<本地分支名>
b. git pull : Git从你指定的(当前分支所跟踪的)远程仓库中抓取内容,然后马上尝试将其合并进你所在的分支中。
#7. 推送:git push [remote-name] [branch-name]
a. git push: 计算你本地数据库与远程仓库的差异,然后将差异推送到另一个仓库中。 它需要有另一个仓库的写权限
#8. .git 目录
这些条目是 Git 的核心组成部分。
objects: 目录存储所有数据内容;
refs: 目录存储指向数据(分支)的提交对象的指针;
HEAD: 文件指示目前被检出的分支;
index: 文件保存暂存区信息。
命令
git help
git init
git status
a. Untracked files: 未跟踪的文件
b. Changes to be committed: (说明是已暂存状态)
(use “git reset HEAD <file>…” to unstage)
c. Changes not staged for commit: (说明已跟踪文件的内容发生了变化,但还没有放到暂存区)
(use “git add <file>…” to update what will be committed)
(use “git checkout — <file>…” to discard changes in working directory)
d. You have unmerged paths.(合并冲突)
(fix conflicts and run “git commit”)
e. All conflicts fixed but you are still merging.
(use “git commit” to conclude merge)
git status -s
??: 新添加的未跟踪文件前面有 ?? 标记
A : 新添加到暂存区中的文件前面有 A 标记
MM: 修改过的文件前面有 M 标记。 左M: 修改了并放入了暂存区 右M: 修改了还没放入暂存区.
两位:左表暂存区,右表工作区
说明:-s 不显示需推送的内容,只涉及到工作区和暂存区,Git仓库的状态不显示。
git add <文件|目录> : (添加内容到下一次提交中)
a. 开始跟踪新文件
b. 把已跟踪(已修改)的文件放到暂存区
c. 合并时把有冲突的文件标记为已解决状态等
运行了 git add 之后又作了修订的文件,需要重新运行 git add 把最新版本重新暂存起来:
git reset HEAD <file>
说明:取消暂存的文件
NOTE: 虽然在调用时加上 –hard 选项可以令 git reset 成为一个危险的命令(译注:可能导致工作目录中所有当前进度丢失!),但本例中工作目录内的文件并不会被修改。 不加选项地调用 git reset 并不危险 — 它只会修改暂存区域。
git checkout — <file>
说明:撤消对文件的修改(拷贝了另一个文件来覆盖它)
IMPORTANT:这是一个危险的命令。 你对那个文件做的任何修改都会消失。除非你确实清楚不想要那个文件了,否则不要使用这个命令。
git diff
此命令比较的是工作目录中当前文件和暂存区域快照之间的差异, 也就是修改之后还没有暂存起来的变化内容。
请注意,git diff 本身只显示尚未暂存的改动,而���是自上次提交以来所做的所有改动。 所以有时候你一下子暂存了所有更新过的文件后,运行 git diff 后却什么也没有,就是这个原因。
git diff –staged(等同于 git diff –cached)
查看已暂存的将要添加到下次提交里的内容。是暂存文件和已提交文件的比较。
git commit (这种方式会启动文本编辑器以便输入本次提交的说明)
git commit -m “Story 182: Fix benchmarks for speed”
请记住,提交时记录的是放在暂存区域的快照。 任何还未暂存的仍然保持已修改状态,可以在下次提交时纳入版本管理。 每一次运行提交操作,都是对你项目作一次快照,以后可以回到这个状态,或者进行比较。
git commit -a -m ‘added new benchmarks’ (git acm: git add + git commit -m)
git commit –amend
commit之后:版本修改将添加到历史记录
git rm PROJECTS.md(等于 git rm -f PROJECTS.md)
git rm –cached PROJECTS.md
git rm -f PROJECTS.md
目标文件未被跟踪:操做无效
目标文件在暂存区:
a. 无参:提示加参数 –cached 或 -f
b. -f: 从暂存区中删除,也从本地文件系统中删除。
c. –cached: 从暂存区中删除,但不从本地文件系统删除。
目标文件已提交在Git仓库中:
a. 无参或加-f参数:即从仓库中删除,也从本地文件系统中删除。
b. –cached: 从仓库中删除,但不从本地文件系统删除。
git mv file_from file_to
运行 git mv 就相当于运行了下面三条命令:
$ mv README.md README
$ git rm README.md
$ git add README
git log -p -2
git log –pretty=oneline
git log –pretty=format:”%h – %an, %ar : %s”
git log –pretty=format:”%h %s” –graph
git log –since=2.weeks
git log -Sfunction_name
git log –pretty=”%h – %s” –graph — 1.txt
git log –oneline –decorate 查看各个分支当前所指的对象
git clone https://github.com/schacon/ticgit
git clone git@github.com:mojombo/grit.git NewName
git remote
git remote -v
git remote show [remote-name] 查看远程仓库
git remote add <shortname> <url> 添加远程仓库
git remote rm paul 远程仓库的移除
git remote rename pb paul 远程仓库的重命名
git fetch [remote-name] 从远程仓库中抓取与拉取
a. 这个命令查找 “origin” 是哪一个服务器(在本例中,它是 git.ourcompany.com), 从中抓取本地没有的数据,
b. 并且更新本地数据库.
c. 移动 origin/master 指针指向新的、更新后的位置。
当 git fetch 命令从服务器上抓取本地没有的数据时,它并不会修改工作目录中的内容。 它只会获取数据然后让你自己合并。
要特别注意的一点是当抓取到新的远程跟踪分支时,本地不会自动生成一份可编辑的副本(拷贝)。 换一句话说,这种情况下,不会有一个新的 serverfix 分支 – 只有一个不可以修改的 origin/serverfix 指针。
可以运行 git merge origin/serverfix 将这些工作合并到当前所在的分支。 如果想要在自己的 serverfix 分支上工作,可以将其建立在远程跟踪分支之上
git push [remote-name] [branch-name]
当你和其他人在同一时间克隆,他们先推送到上游然后你再推送到上游,你的推送就会毫无疑问地被拒绝。 你必须先将他们的工作拉取下来并将其合并进你的工作后才能推送。 阅读 Git 分支 了解如何推送到远程仓库服务器的详细信息。
git tag
git tag -l ‘v1.8.5*’
git tag -a v1.4 -m ‘my version 1.4’ 附注标签
git tag -a v1.2 9fceb02 后期打标签
git tag v1.4 轻量标签
git show v1.4
git push origin v1.5 共享标签
git push origin –tags
git push origin –delete serverfix 删除一个远程分支
基本上这个命令做的只是从服务器上移除这个指针。 Git 服务器通常会保留数据一段时间直到垃圾回收运行,所以如果不小心删除掉了,通常是很容易恢复的。
git checkout -b version2 v2.0.0
在 Git 中你并不能真的检出一个标签,因为它们并不能像分支一样来回移动。 如果你想要工作目录与仓库中特定的标签版本完全一样,可以使用 git checkout -b [branchname] [tagname] 在特定的标签上创建一个新分支:
git checkout -b [branch] [remotename]/[branch]
git checkout –track origin/serverfix
git checkout -b sf origin/serverfix
git branch -u origin/serverfix
设置已有的本地分支跟踪一个刚刚拉取下来的远程分支,或者想要修改正在跟踪的上游分支,你可以在任意时间使用 -u 或 –set-upstream-to 选项运行 git branch 来显式地设置。
git branch
git branch -v 查看每一个分支的最后一次提交
git branch -vv 查看设置的所有跟踪分支,可以使用 git branch 的 -vv 选项
需要重点注意的一点是这些数字的值来自于你从每个服务器上最后一次抓取的数据。 这个命令并没有连接服务器,它只会告诉你关于本地缓存的服务器数据。 如果想要统计最新的领先与落后数字,需要在运行此命令前抓取所有的远程仓库。 可以像这样做:$ git fetch –all; git branch -vv
git branch –merged
git branch –no-merged
git branch testing
git branch -d iss53
git checkout testing
git checkout -b iss53
git pull
a. 在大多数情况下它的含义是一个 git fetch 紧接着一个 git merge 命令。
b. 不管它是显式地设置还是通过 clone 或 checkout 命令为你创建的,git pull 都会查找当前分支所跟踪的服务器与分支,从服务器上抓取数据然后尝试合并入那个远程分支。
git pull <远程主机名> <远程分支名>:<本地分支名>
git merge iss53
git rebase master
git gc
最妙之处是你可以随时重新打包。 Git 时常会自动对仓库进行重新打包以节省空间。当然你也可以随时手动执行 git gc 命令来这么做。
当版本库中有太多的松散对象,或者你手动执行 git gc 命令,或者你向远程服务器执行推送时,Git 都会这样做。
Git 常见变量
HEAD: 表示最近一次的 commit。
MERGE_HEAD: 如果是 merge 产生的 commit,那么它表示除 HEAD 之外的另一个父母分支。
FETCH_HEAD: 使用 git-fetch 获得的 object 和 ref 的信息都存储在这里,这些信息是为日后 git-merge 准备的。
HEAD^: 表示 HEAD 父母的信息
HEAD^^: 表示 HEAD 父母的父母的信息
HEAD~4: 表示 HEAD 上溯四代的信息
HEAD^1: 表示 HEAD 的第一个父母的信息
HEAD^2: 表示 HEAD 的第二个父母的信息
COMMIT_EDITMSG: 最后一次 commit 时的提交信息。