文章目录
1. 前言
1.1 版本控制
什么是版本控制:是一种记录若干文件内容变化,以便将来查阅特定版本修订情况的系统。
文件补丁:是一种特定格式的文本文件,记录着对应文件修订前后的内容变化。
- 本地版本控制系统:使用简单的数据库来记录为文件的历次更新差异。
- 集中化的版本控制系统(Cnetralized Version Control System,CVCS)
- 目的:让不同系统上的开发者协同工作。
- 原理:使用一个单一的集中管理的服务器,保存所有文件的修改版本,而协同工作的人们都通过客户端连接到这台服务器,取出最新的文件或者更新文件。
- 工具有:SVN
- 缺点:中央服务器的单点故障。如果中央服务器的磁盘发生故障,并且没做过备份或者备份得不够及时的话,还会有丢失数据的风险。最坏的情况是彻底丢失整个项目的所有历史更改记录,被客户端提取出来的某些快照数据除外,但这样的话依然是个问题,你不能保证所有的数据都已经有人提取出来。
- 分布式版本控制系统(Distributed Version Control System, DVCS)
- 原理:客户端不只提取最新版本的文件快照,而是把原始的代码仓库完整地镜像下来。
- 优点:当任何一处协同工作用的服务器发生故障,都可以使用任何一个镜像出来的本地仓库恢复。
1.2 Git历史
- 1991-2002年间:绝大多数的 Linux 内核维护工作都花在了提交补丁和保存归档的繁琐事务。
- 2002年起:项目组开始启用分布式版本控制系统 BitKeeper 来管理和维护代码。
- 2005年,Git开始诞生。
1.3 Git基础要点
快照:是数据存储的某一时刻的状态记录。
- 原理:快照锁定物理单元内容,并记录本次快照和上一次快照的所对应的物理地址(或者是上一层逻辑地址)的差异。
- 快照仅仅记录逻辑地址和物理地址的对应关系。
备份:是数据存储的某一时刻的备份。
快照和备份的区别:
- 备份的数据安全性更好:如果原始数据损坏,快照回滚是无法恢复出正确的数据,而备份可以。
- 快照的速度比备份快:生成快照的速度比备份快。
- 占用空间不同:备份占用双倍的存储空间,而快照所占用的存储空间则取决于快照的数量以及数据变动情况。极端情况下,快照可能会占用1%不到的存储空间,也可能占用数十倍的存储空间。(对同一份数据,同时做相同数量的快照和增量备份的话,备份还是会占用更多的空间)
1.3.1 直接快照
- Git只关系文件数据的整体是否发生变化,而其他版本控制系统则只关心文件内容的具体差异。这类系统(CVS、Subversion(SVN)、Perfore等等)每次记录有哪些文件作了更新,以及都更新了哪些行的具体内容。
- Git并不保存这些前后变化的差异数据。实际上,Git 更像是把变化的文件作快照后,记录在一个微型的文件系统中。每次提交更新时,它会纵览一遍所有文件的指纹信息并对文件作一快照,然后保存一个指向这次快照的索引。为提高性能,若文件没有变化,Git 不会再次保存,而只对上次保存的快照作一连接。
1.3.2 本地执行
在 Git 中的绝大多数操作都只需要访问本地文件和资源,不用连网。但如果用 CVCS 的话,差不多所有操作都需要连接网络。因为 Git 在本地磁盘上就保存着所有有关当前项目的历史更新,所以处理起来速度飞快。
1.3.3 数据完整
在保存到 Git 之前,所有数据都要进行内容的校验和(checksum)计算,并将此结果作为数据的唯一标识和索引。换句话说,不可能在你修改了文件或目录之后,Git 一无所知。这项特性作为 Git 的设计哲学,建在整体架构的最底层。所以如果文件在传输时变得不完整,或者磁盘损坏导致文件数据缺失,Git 都能立即察觉。
1.4 Git基本理论(核心)
-
对于任何一个文件,在Git内都只有三种状态:
- 已提交(commited):表示该文件已经被安全地保存在本地数据库中。
- 已修改(modified):表示修改了某个文件,但还没有提交保存。
- 已暂停(staged):表示把已修改的文件放在下次提交时要保存的清单中。
-
上述三种工作状态对应文件流转的三个工作区域:
- Git的本地数据目录
- 工作目录:从项目中取出某个版本的所有文件和目录,用以开始后续工作。
- 暂停区域。
-
每个项目都有一个 git 目录,它是 Git 用来保存元数据和对象数据库的地方。每次拷贝时拷贝的即是该目录里面的数据。
Git本地有三个工作区域:工作目录、暂存区和资源库。如果加上远程的git仓库(Remote directory)就分为四个区域。
基本的Git工作流程:
- 1)在工作目录中修改某些文件;
- 2)对这些修改了的文件作快照,并保存到暂停区域;
- 3)提交更新,将保存在暂停区域的文件快照转储到git目录中。
从文件所处的位置来判断状态:如果是 git 目录中保存着的特定版本文件,就属于已提交状态;如果作了修改并已放入暂存区域,就属于已暂存状态;如果自上次取出后,作了修改但还没有放到暂存区域,就是已修改状态。
1.5 安装及配置
1.5.1 安装
-
直接使用命令安装。使用如下命令在linux(CentOS)安装git。
yum install git-core
-
从github中下载源代码安装
- 1)删除已安装的git。yum -y remove git
- 2)在github(https://github.com/git/git/releases)中下载最新的git版本;
- 3)使用命令tar -zxvf xxx 解压;
- 4)安装编译源码需要的依赖。命令如下:yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel gcc perl-ExtUtils-MakeMaker
- 5)编译git源码,进入/usr/local/xxx 目录; make prefix=/usr/local/git all
- 6)安装git 至/usr/bin/git路径 make prefix=/usr/local/git install
- 7)配置环境变量 vim /etc/profile
- 8)使环境变量可用 source /etc/profile
- 9)查看Git是否安装完成 git --version
windows下各个命令的作用:
- Git Bash:Unix与Linux风格的命令行,使用最多;
- Git CMD:Windows风格的命令行
- Git GUI:图形界面的Git
1.5.2 配置
-
用户信息
git config --global user.name "wei lin" git config --global user.email "wei_lin527@163.com"
每次 Git 提交时都会引用这两条信息,说明是谁提交了更新,所以会随更新内容一起被永久纳入历史记录。
-
文本编辑器
git config --global core.editor emacs #将文本编辑器设置为emacs
Git 需要你输入一些额外消息的时候,会自动调用一个外部文本编辑器给你用。默认会使用操作系统指定的默认编辑器,一般可能会是 Vi 或者 Vim。
-
查看配置信息
git config --list
-
查看系统配置信息
git config --system --list
Git相关配置文件:
- 1)D:\Git\etc\gitconfig :Git安装目录下的gitconfig --system级别;
- 2)C:\Users\Administrator.config:只适用于当前登录用户的配置 --global级别。
这里可以直接编辑配置文件,通过命令设置后会响应到这里。
-
设置本机绑定SSH公钥,实现免密码登录
-
生成公钥;
# 生成公钥。在git bash中使用如下命令 ssh-keygen
出现的选择直接回车即可:
-
-
在C:\Users\Administrator.ssh目录下。第一个是自己的密钥,第二个是公钥。
-
将公钥信息public key添加倒码云账户中;
1.6 Linux常用命令
- cd 更改目录;
- cd … 回退到上一级目录,直接cd进入默认目录;
- pwd 显示当前所在的目录路径;
- ls(ll)列出当前目录中的所有文件,ll列出的内容更加详细;
- touch 新建一个文件,如touch index.js,就是在当前目录下创建一个node.js文件;
- rm 删除一个文件;
- mkdir 新建一个文件夹;
- rm -r 删除一个文件夹, rm -r src 删除src目录;
- mv 移动文件,mv index.html src index.html,其中src是目标文件夹;
- reset 重新初始化终端 / 清屏;
- clear 清屏;
- history 查看历史命令;
- help 帮助;
- exit 退出
- # 表示注释
2. Git基础
Git常用命令如下图所示:
- add
- commit:提交到本地仓库。
- push:本地仓库提交到远程。
- fetch/clone:从远程克隆到本地仓库。
- pull:从远程仓库中获取一个项目到工作目录。
- checkout
2.1 获取项目的Git仓库
本地仓库搭建。有两种方法:
- 1)在现存目录下,通过导入所有文件来创建新的Git仓库。
- 2)从已有的Git仓库克隆出一个新的镜像仓库。
-
本地搭建
-
创建新的仓库(文件夹),使用如下命令进行Git管理:
git init
-
初始化后,在当前目录下会出现一个名为 .git 的目录,所有 Git 需要的数据和资源都存放在这个目录中。不过目前,仅仅是按照既有的结构框架初始化好了里边所有的文件和目录,但我们还没有开始跟踪管理项目中的任何一个文件。
-
-
克隆远程仓库
使用如下命令克隆一个项目和它的整个代码历史(版本信息)
git clone [url] # 例如 git clone https://gitee.com/hms-core/hms-ecommerce-demo.git
2.2 文件操作
文件的四种状态:
版本控制就是对文件的版本控制,要对文件进行修改、提交等操作,需要知道文件处于什么状态。
-
1)Untracked:未跟踪,此文件在文件夹中,但并没有加入到git库,不参与版本控制,通过
git add
状态变为 Staged。-
使用命令 git add 开始跟踪一个新文件。
git add README #开始跟踪README文件
-
运行git status查看状态。结果显示如下。说明文件已经是暂存状态,如果此时提交,那么文件此时的版本就被留存到历史记录中。
-
-
git add 后可以接要跟踪的文件或目录的路径。
-
如果是目录的话,就说明要递归跟踪所有该目录下的文件。
-
2)Unmodify:文件已经入库,未修改。即版本库中的文件快照内容与文件夹中完全一致。如果该文件被修改,则变为Modified;如果使用
git rm
移除仓库,则变为Untracked文件。 -
3)Modified:文件已修改,仅仅是修改,并没有进行其他操作。
-
通过
git add
可进入暂存 Staged状态;# git add . 添加所有文件到暂存区 # git add [filename] 添加具体的文件到暂存区
-
使用
git checkout
则丢弃修改,返回到Unmodify状态。该命令是从库中取出文件,覆盖当前修改的文件。# git commit -m "message" 提交暂存区中的内容到本地仓库 -m表示提交信息message
-
-
4)Staged:暂存状态。
- 执行
git commit
将修改同步到库中,这时库中的文件和本地文件又变为一致,文件为Unmodify状态。 - 执行
git reset HEAD filename
取消暂存,文件状态为Modify
- 执行
查看文件的状态
# 查看指定文件状态
git status [filename]
#查看所有文件状态
git status
忽略文件 git ignore (上传不会被选中)
在项目中,需要创建一个 .gitignore
文件来存储那些不需要进行跟踪(不需要上传的文件),即列出要忽略的文件模式。
文件.gitignore
的格式规范如下:
- 所有空行或者以注释符号 # 开头的行都会被 Git 忽略。
- 可以使用标准的 glob 模式匹配。glob模式是指shell所使用的简化了的正则表达式
*
匹配 0个或多个任意字符;- [abc] 匹配任何一个列在方括号中的字符。该例要么匹配一个a,要么一个b,要么一个c;
?
表示只匹配一个任意字符;- [0-9]表示匹配到所有0到9 的数字。如果在方括号中使用段划线分隔两个字符,表示所有在这两个字符范围内的都可以匹配。
- 匹配模式最后跟反斜杠( / )说明要忽略的是目录。
- 要忽略指定模式以外的文件或目录,可以在模式前加上惊叹号( ! )取反。
# .gitignore文件例子
*.a # 忽略所有以 .a结尾的文件
!lib.a # 但除 lib.a 以外
/TODO # 仅仅忽略项目根目录下的TODO文件,不包括其他目录的TODO文件
build/ # 忽略 build/ 目录下的所有文件
doc/*.txt # 会忽略doc/notes.txt,但不包括doc/server/arch.txt
3. Git分支
3.1 分支的概念
Git保存的是一些列的文件快照,而非是文件差异或者变化量。
在Git提交时,会保存一个提交对象
,它包含一个指向暂存内容快照的指针、作者和相关附属信息,以及一定数量(可能没有)指向该提交对象直接祖先的指针:第一次提交是没有直接祖先的,普通的提交有一个祖先,由两个或多个分支合并产生的提交则有多个祖先。
当使用 git commit
新建一个提交对象前,Git 会先计算每一个子目录的校验和,然后在 Git 仓库中将这些目录保存为树tree对象。之后 Git 创建的提交对象,除了包含相关提交信息以外,还包含着指向这个树对象(项目根目录)的指针,如此它就可以在将来需要的时候,重现此次快照的内容。
现在,Git 仓库中有五个对象:三个表示文件快照内容的 blob 对象;一个记录着目录树内容及其中各个文件对应 blob 对象索引的 tree 对象;以及一个包含指向 tree 对象(根目录)的索引和其他提交信息元数据的 commit 对象。如下图所示(表示一次提交后仓库里的数据):
作些修改后再次提交,那么这次的提交对象会包含一个指向上次提交对象的指针(译注:即下图中的parent 对象)。两次提交后,仓库历史如下图所示:
Blob,Binary Large Object的缩写,代表二进制类型的大对象
3.2 Git分支中常用的命令
git branch # 列出所有本地分支
git branch -r # 列出所有远程分支
git branch [branch-name] # 新建一个分支,但仍处于当前分支,不会自动切换到新建的分支中
git chechout -b [branch-name] # 切换分支
git merge [branch] # 合并指定分支到当前分支
git branch -d [branch-name] # 删除分支
# 删除远程分支
git push origin --delete [branch-name]
git branch -dr [remote/branch]
git chechout -b [branch-name] # 切换到分支,相当于如下两条语句:
git branch [branch-name]
git checkout [branch-name]
master主分支应该非常稳定,用来发布新的版本,一般情况下不允许在上面工作,工作一般在新建的dev分支上进行。工作完后,若需要发布,或者dev分支代码稳定后可以合并到主分支master上。
git checkout master
# 该命令做了两件事情:
## 1. 将HEAD指针移动到master分支
## 2. 把工作目录中的文件换成了 master 分支所指向的快照内容。
3.3 基本的分支与合并
实际工作中的工作流程:
- 开发某个网站;
- 为实现某个新的需求,创建一个分支;
- 在这个分支上开展工作。
假设此时,需要对项目中某个问题进行解决,那么可以按照下面的方式处理:
- 1)返回到原先已经发布到生产服务器上的分支。
- 2)为此次补救建立一个新的分支。
- 3)测试通过后,将该补救分支合并,再发布到生产服务器上。
- 4)切换到之前实现需求的分支,继续工作。
3.3.1 基本分支
前提:在项目中已经提交过几次更新。
要求:需要修补问题追踪系统上的问题。顺带说明下,Git 并不同任何特定的问题追踪系统打交道。
步骤如下:
-
创建解决问题的分支
issue
,并切换到当前分支。git branch -b issue
上述命令的结果如下图所示:C0表示第一次Commit。切换到issue,则HEAD指针指向issue分支。
-
在网站上进行修改并提交。
vim update.html # 修改内容 git branch -a -m 'add a new footer [issue]' # 提交
上述命令完成的结果如下图所示:
-
提交修改后,正常转换到master分支。命令运行结果与上图一致,只是HEAD指针指向了master分支。
【注】Git 会把工作目录的内容恢复为检出某分支时它所指向的那个 commit 的快照。它会自动添加、删除和修改文件以确保目录的内容和你上次提交时完全一样。
-
进行紧急修补。创建一个修补分支hotfix来展开工作。
git branch -b hotfix # 创建并切换分支 vim checkFix.html # 修补 git commit -a - 'fixed xxx' # 提交修补
-
修改完毕之后合并到主分支。
git checkout master # 先切换到master分支 git merge hootfix # 合并修补分支
-
删除修补分支。
git branch -d hotfix
-
回到未完成工作的issue分支继续工作。
git checkout issue
3.3.2 基本合并
对于3.3.1中hotfix与master的合并来说,master是处于hotfix分支的直接上游
,Git只需要把指针横移即可到达另一个。换句话说就是顺着一个分支走下去可以到另一个分支。如上所述的分支能够直接合并并且可以删除非主分支。
而像master分支和issue分支没有上下游关系。不能够直接合并。那么如何合并issue和master分支呢???
解决方法:
由于当前 master 分支所指向的 commit (C4)并非想要并入分支issue
的直接祖先,Git 不得不进行一些处理。
- Git 会用两个分支的末端(C4 和 C3)和它们的共同祖先(C2)进行一次简单的三方合并计算( Git 为分支合并自动识别出最佳的同源合并点)。
- Git 没有简单地把分支指针右移,而是对三方合并的结果作一新的快照,并自动创建一个指向它的 commit(C5)。我们把这个特殊的 commit 称作合并提交(merge commit),因为它的祖先不止一个。
- 即Git 会自动创建了一个包含了合并结果的 commit 对象。
**然后可以直接删除issue分支。**git branch -d issue
3.3.3 冲突的合并
当修改了两个待合并分支中的同一个文件的同一部分,Git就无法干净地把两者合并到一起。这里Git作了合并操作,但是没有提交,它会停下来等你解决冲突。这里可以使用git status查看状态。
任何包含未解决冲突的文件都会以未合并(unmerged)状态列出。Git 会在有冲突的文件里加入标准的冲突解决标记,可以通过它们来手工定位并解决这些冲突。
3.4 分支管理
-
git branch 命令不仅能创建和删除分支,如果不加参数,则表示列出当前所有的分支
$ git branch * master # *表示当前分支。如果现在提交更新, master 分支将随着开发进度前移
-
git branch -v 查看每个分支最后一次
commit
信息。$ git branch -v * master 6381527 [ahead 1] new file test.text
-
git branch --merged:查看哪些分支已经被并入到当前分支。
-
git branch --no-merged:查看还未进行合并发分支。如果使用git branch -d name会导致删除失败。
-
git branch -D [branch-name] 强制删除某个分支,即使该分支尚未合并,不会报错。