《Linux就该这么学》自学笔记_ch21_使用Git分布式版本控制系统
分布式版本控制系统
关于版本控制系统(Version Control System,VCS):随着信息科技的发展,软件开发的规模与复杂度,以及考虑到项目工程进度,软件的规模与复杂度已经不再适合一个人单打独斗地开发了,团队协作变得相当重要。如果没有VCS,假如一个团队,同事A修改了文件k,同事B也修改了文件k,当工作特别繁琐而导致有些更改细节没有相关的记录和交待时,团队开发就会变得乱七八糟。
版本控制:记录对文件、目录或工程等的修改历史,方便查看更改历史,备份以便恢复以前的版本,多人协作。
关于版本控制,有以下几种方式:
- 纯手工版本控制:最原始的版本控制手段。仅通过修改文件名,保存文件副本等方法标记文件的版本修改记录,随着时间久远而忘却文件版本和对应修改内容的可能性很大,是最笨且最容易出问题的方式。
- 本地版本控制:记录文件每次的更新,可以对每个版本做一个快照,或是记录补丁文件。比如RCS。不过,本地版本控制系统偏向于个人使用,或者多个使用的人必须要使用相同的设备(使用同一台电脑),不方便多人协作。
- 集中式版本控制:在集中化的版本控制系统( Centralized Version Control Systems,简称 CVCS )中,如Subversion,Perforce。所有的版本数据都保存在服务器上,这种特性使得团队的每个人都要从服务器上同步更新或上传自己的修改。用户的本地设备就只有自己以前所同步的版本,如果不连网,用户就看不到历史版本,也无法切换版本验证问题,无法在不同分支工作,而且有很大的风险,一旦服务器损坏,就会丢失所有的数据,必须定期对服务器进行备份。
- 分布式版本控制:针对CVCS的缺点,出现了分布式版本控制系统( Distributed Version Control System,简称 DVCS ),如Git,Mercurial。DVCS把所有的版本信息仓库全部同步到本地,这样就可以在本地查看所有版本历史,可以离线在本地提交,只需在连网时push到相应的服务器或其他用户那里。由于每个用户那里保存的都是所有的版本数据,所以,只要有一个用户的设备没有问题就可以恢复所有的数据。当然这会占用更多的本地存储空间。
分布式和集中式的版本控制的区别:
集中式版本控制系统的版本库集中存放在中央服务器,团队中用户要使用个人PC进行工作,需要先从中央服务器取得最新版本,把自己的工作成果推送给中央服务器。集中式版本控制的弊端就是必须联网才能工作,十分依赖网速和网络环境稳定性。而且一旦中央服务器出现问题,那么所有人都无法正常工作。
而分布式则没有中央服务器这一概念,每个人的PC都是一个完整的版本库,不需要联网依赖中央服务器取得最新版本库,因为版本库就在自身PC上。分布式比集中式更安全,因为每个人PC都有完整的版本库,即使某人PC的版本库损坏,也能从其他人PC复制一份。
分布式版本控制系统也可以设置一台设备充当“中央服务器”,但这个服务器的作用仅仅是用来方便“交换”团队各人的修改,没有它团队各人依然能正常工作。
目前比较常用的版本控制系统:
CVS作为最早的开源而且免费的集中式版本控制系统,直到现在还有不少人在用。由于CVS自身设计的问题,会造成提交文件不完整,版本库莫名其妙损坏的情况。同样是开源而且免费的SVN修正了CVS的一些稳定性问题,是目前用得最多的集中式版本库控制系统。
分布式版本控制系统除了Git以及促使Git诞生的BitKeeper外,还有类似Git的Mercurial和Bazaar等。这些分布式版本控制系统各有特点,但最快、最简单也最流行的依然是Git!
Git:一款开源的分布式版本控制系统,而且有其独特的功能特性。大多数的分布式版本控制系统只会记录每次文件的变化,即只会关心文件的内容变化差异,而Git则是关注于文件数据整体的变化,直接会将文件提交时的数据保存成快照,而非仅记录差异内容,并且使用SHA-1加密算法保证数据的完整性。
Git为了提高效率,对于没有被修改的文件,不会重复存储,而是创建一个链接指向之前存储过的文件。
使用Git服务程序
Git的三种重要模式:
- 已提交(committed):表示数据文件已经顺利提交到Git数据库中。
- 已修改(modified):表示数据文件已经被修改,但未被保存到Git数据库中。
- 已暂存(staged):表示数据文件已经被修改,并会在下次提交时提交到Git数据库中。
Git的工作流程:
- 在工作目录中修改数据文件。
- 将文件的快照放入暂存区域。
- 将暂存区域的文件快照提交到Git仓库中。
提交前的数据文件可能会被随意修改或丢失,但只要把文件快照顺利提交到Git数据库中,就不必担心会丢失了。
安装Git服务:yum install -y git
Git服务程序的配置文档通常会有3份,针对当前用户和指定仓库的配置文件优先级最高。
配置文件 | 作用 |
/etc/gitconfig | 保存着系统中每个用户及仓库通用配置信息。传递参数选项’--system’ 给 git config将读/写这个文件。 |
~/.gitconfig ~/.config/git/config |
针对于当前用户的配置信息。传递--global 选项将使Git 读/写这个文件。 |
工作目录/.git/config | 针对于当前仓库数据的配置信息。若当前库和/etc/gitconfig有相同参数,则以当前库的参数值为标准优先生效。 |
初始化配置:配置用户名称,电子邮件地址和默认的文本编辑器。
git config --global user.name "Wu Jia Ming" #配置个人用户名称 git config --global user.email "747631184@qq.com" #配置个人电子邮件地址 git config --global core.editor vim #设置默认文本编辑器 git config --list #查看Git工作环境信息
提交数据
Git的工作目录:本质上就是普通目录,通过进入该目录并执行git init命令,该目录就会被设置为Git的工作目录,被Git服务程序所管理。
Git会时刻的追踪目录内文件的改动(Git只能追踪类似于txt文件、网页、程序源码等文本文件的内容变化,而不能判断图片、视频、可执行命令等这些二进制文件的内容变化),在安装好Git服务程序后,默认就会创建好一个叫做master的分支,直接可以提交数据到主线。
设置Git工作目录:
mkdir /kamin #创建普通目录 cd /kamin #进入该目录 git init #将当前所在目录初始化为Git工作目录
提交数据:
按照Git的工作流程,先把文件添加到暂存区,再把暂存区文件提交到Git版本仓库。每一次修改过文件时,就应当切记要执行git add和git commit这两步,这样才能确保Git版本仓库的内容版本和当前工作目录的内容版本一致。
echo "Kamin" > test.txt #在Git工作目录里,创建文本文件 git add test.txt #把文件添加到暂存区,此时暂存区中的test.txt文件只有1行内容 echo "Kamin2" >> test.txt #修改文本文件,追加一行内容,此时工作目录内的test.txt文件内容有2行 git commit -m "1st add test.txt" #把暂存区文件提交到Git版本仓库,注意此时的暂存区文件内容只有1行,因此提交到版本仓库的文件内容自然也只有1行 git status #查看当前Git版本仓库的状态。因为当前版本仓库的文件版本和当前工作目录的不一样,因此会显示modified git diff test.txt #查看当前文件内容与Git版本数据库中的差别 git add test.txt #把修改后的文件添加到暂存区 git commit -m "2nd add test.txt,add Kamin2" #把暂存区中文件提交到Git版本库 git status #查看Git版本仓库状态,因为当前版本仓库的文件版本与当前工作目录一致,因此不会再显示modified
批量提交数据文件
如果工作目录内经常修改的文件较多,把文件一个个提交到暂存区未免太麻烦。可以这么做:
- 设置要忽略上传的文件(写入到"工作目录/.gitignore"文件中),如果工作目录总占用空间不大或没有文件可以不必上传,也可以忽略这步直接把整个工作目录的所有文件提交到暂存区;
- 使用"git add ."命令来将当前工作目录内的所有文件都一起添加到暂存区域。
关于"工作目录/.gitignore"文件,可以按如下格式设置要忽略上传的内容:
- *.a //忽略所有以.a为后缀的文件。
- !lib.a //但是lib.a这个文件除外,依然会被提交。
- build/ //忽略build目录内的所有文件。
- build/*.txt //忽略build目录内以txt为后缀的文件。
- git.c //指定忽略名字为git.c的文件。
touch git.c #在git工作目录创建一个文件 vim .gitignore #编辑./gitignore文件,把文件git.c写入当中,当提交工作目录所有文件时,自动忽略该文件 git.c git add . #把当前工作目录所有文件添加到暂存区 git commit -m "3rd" #把暂存区的文件提交到git版本仓库
git commit命令可以追加-a参数将以前所有追踪过的文件添加到暂存区后自动的提交,从而跳过上传暂存区的步骤。
git add命令可以追加-f参数强制把文件(即使文件被记录在忽略提交的配置文件中也能强制添加)添加到暂存区
echo "qwer" >> test.txt #往文件追加一行内容 git commit -a -m "4th" #文件直接被提交到git版本仓库 git status #查看git版本仓库状态,因为文件改动已经直接提交到git版本仓库,所以不会出现modified git add -f git.c #强制把忽略提交的git.c文件添加到暂存区 git commit --amend #修改上一次提交操作。会用vim的方式显示提交的描述,保存并退出即可完成修改上一次提交操作
移除数据
git rm命令:
- 追加--cached选项可以将文件从Git暂存区域的追踪列表中移除(并不会删除当前工作目录内的数据文件);
- 在不追加任何选项的情况下,该命令可以直接删除暂存区内的追踪信息及工作目录内的数据文件,可以追加-f强制删除参数忽略执行该操作的提示信息;
touch database #在git工作目录下创建名为database的文件 git add database #添加文件到git暂存区 git status #查看当前git状态,显示new file:database。暂存区新增一个尚未提交到git版本库的名为database的文件 git rm --cached database #将该文件从Git暂存区域的追踪列表中移除(并不会删除当前工作目录内的数据文件) ls #查看git工作目录的文件内容,database在 git status #再查看当前git状态,只显示database,表示工作目录存在该文件,且该文件尚未添加到git暂存区 git add . #再将工作目录所有文件添加上暂存区 git rm -f database #强行把database文件从暂存区移除和工作目录中的一起删除 ls #查看git工作目录,发现database已被删除 git status #再查看git状态,没有特别的显示信息了
移动数据
Git版本控制系统不会跟踪文件的移动操作(文件重命名)。git中修改文件名的方法:(以文件A为例)
- 使用git mv命令直接更改git版本仓库中文件A的文件名,然后再提交;
- 进入git工作目录,用mv命令对git工作目录中的文件A重命名为B,用git rm命令把git版本仓库中的文件A快照删除,再提交B文件;
git mv test.txt 777.txt #在git中对文件改名 git status #查看当前git状态,会显示关于重命名操作的说明 git commit -m "changed name 1" #提交更改 mv 777.txt test.txt #在工作目录中更改文件名 git rm 777.txt #删除git版本仓库中旧文件名的文件 git add test.txt #把改名后的文件添加到暂存区 git commit -m "changed name 2" #提交更改
实际上,方法1中git mv A B 相当于执行了方法2中的3条命令:mv A B;git rm A;git add B。
历史记录
git log命令:查看提交记录。常用参数:
- -n:n代表数字,表示仅显示最近n条记录;
- -p:显示每次提交的内容差异;
- --stat:简要显示数据修改行数,更清晰地看到提交中修改过的内容、对文件添加或移除的行数;
- --pretty:根据不同的格式展示提交的历史信息,如:oneline表示每行显示一条提交记录,fuller表示更详细地输出历史记录,还可以使用format参数指定具体输出格式:
- %s 提交说明。
- %cd 提交日期。
- %an 作者的名字。
- %cn 提交者的姓名。
- %ce 提交者的电子邮件。
- %H 提交对象的完整SHA-1哈希字串。
- %h 提交对象的简短SHA-1哈希字串。
- %T 树对象的完整SHA-1哈希字串。
- %t 树对象的简短SHA-1哈希字串。
- %P 父对象的完整SHA-1哈希字串。
- %p 父对象的简短SHA-1哈希字串。
- %ad 作者的修订时间。
git log #输出git历史记录,默认输出全部记录 git log -2 #输出最近两次历史记录 git log -p -1 #输出最近1次历史记录的同时,展开显示每次提交的内容差异 git log --stat -2 #在输出最近2次历史记录同时,简要的显示数据增改行数 git log --pretty=oneline #每行显示一条提交记录 git log --pretty=fuller -2 #以更详细的模式输出最近两次的历史记录 git log --pretty=format:"%h %cn" #按简短SHA-1哈希字串与提交者的姓名的格式输出历史记录
还原数据
Git服务程序中有一个叫做HEAD的版本指针,当用户申请还原数据时,其实就是将HEAD指针指向到某个特定的提交版本。为了避免历史记录冲突,故使用了SHA-1计算出十六进制的哈希字串来区分每个提交版本,另外默认的HEAD版本指针会指向到最近的一次提交版本记录,而上一个提交版本会叫HEAD^,上上一个版本则会叫做HEAD^^,当然一般会用HEAD~n来表示往上数第n个提交版本。
echo "nimabi" >> test.txt #进入git工作目录,修改下test.txt文件 git add test.txt #添加文件到暂存区 git commit -m "nimabi" #提交更改 cat test.txt #查看文件test.txt,追加了一行“nimabi” git log --pretty=oneline #以一行一条记录的形式查看历史记录 git reset --hard HEAD^ #还原git版本仓库到上一个提交版本 cat test.txt #再一次查看test.txt文件,追加的行“nimabi”消失 git log --pretty=oneline #查看此时的历史记录,发现最近一次提交的记录消失,这是因为当前版本还没有发生“nimabi”更新记录 git reflog #git reflog命令可以查看所有历史记录, git reset --hard 1fa4364 #还原git版本仓库到SHA-1值开头为1fa4364的版本(与我此次试验来说此SHA-1开头是最新版本) cat test.txt #在一次查看test.txt文件,“nimabi”复原。
如是只是想把某个文件内容还原,可以直接用git checkout命令。需注意git checkout命令的规则:如果暂存区中有该文件,则直接从暂存区恢复,如果暂存区没有该文件,则将还原成最近一次文件提交时的快照。
echo "heheda" >> test.txt #往文件test.txt追加内容 cat test.txt git checkout -- test.txt #把文件恢复成暂存区中的版本,若暂存区没有该文件,则还原成git仓库的版本 cat test.txt
管理标签
当版本仓库内的数据有个大的改善或者功能更新,一般会打一个类似于软件版本号的标签,更加显眼的将版本库中的某个历史版本给记录下来,方便日后随时将特定历史时期的数据取出来用。
打标签其实只是向某个历史版本做了一个指针。
git tag命令:创建,查看标签。
- -d:删除标签
- -m:为标签添加备注信息
git tag v1.0 #为当前git版本创建标签 git tag #查看所有标签 git show v1.0 #查看标签v1.0的详细信息,该标签的SHA-1值为01901bb git tag v1.1 -m "v1.1" 01901bb #为SHA-1值为01901bb的历史记录设置标签 git tag -d v1.0 #删除标签v1.0 git tag #查看所有标签,标签v1.0已删除 git show v1.1 #查看v1.1标签的详细信息
管理分支结构
分支即是平行空间,假设软件项目中负责某功能模块的人已经把其负责的功能模块代码完成了80%,如果将这不完整的代码直接提交到git仓库中,又有可能影响到其他人的工作,此时可以在该软件项目之上创建一个分支(该分支只会属于该负责人,其他人看不到),等代码编写完成后再与原来的项目主分支合并下即可,这样即能保证代码不丢失,又不影响其他人的工作。
一般在实际的项目开发中,要尽量保证master分支是非常稳定的,仅用于发布新版本,平时不要随便直接修改里面的数据文件,而工作的时候则可以新建不同的工作分支,等到工作完成后再合并到master分支上面。
git会将每次的提交操作串成一个时间线,而在之前的实验中实际都是在对master分支进行操作,Git会在创建分支后默认创建一个叫做Photograph的指针,需要将HEAD指针切换到“Photograph”的位置才能正式使用新分支。
创建分支
git branch命令:创建分支,查看分支情况。
git checkout -b 分支名:在创建新分支的同时切换到该分支。
git branch kamin #创建新分支 git checkout kamin #切换到kamin分支 git branch #查看当前分支情况(会列出仓库中所有分支,带*号为当前工作分支) echo "branch" >> test.txt #追加内容到test.txt文件 git add test.txt #添加文件到暂存区 git commit -m "new branch" #提交更改(注意此时工作分支是kamin) git checkout master #切换回master分支 cat test.txt #查看test.txt文件,没有看到新追加的内容,因为追加操作是在kamin分支中进行,不会影响到master分支
合并分支
git merge命令:将指定的的分支与当前分支合并。
git merge kamin #把kamin分支的工作成果合并到master cat test.txt #查看test.txt文件,可以看到在kamin分支中追加的内容 git branch -d kamin #删除kamin分支 git branch #查看分支,kamin分支消失
内容冲突
当遇到了内容冲突比较复杂的情况,Git无法自动地合并分支,此时必须手工将差异内容处理掉。
内容冲突通常就是文件同时在多个分支中均进行了修改并提交,git无法知晓以哪个修改为准,因而会提示内容冲突。
git checkout -b hehe #创建新分支hehe同时切换到该分支 echo "1" >> test.txt #修改文件(当前分支hehe) git add test.txt #提交 git commit -m "1" git checkout master #切换到master分支 echo "2" >> test.txt #修改test.txt文件(当前分支master) git add test.txt #提交 git commit -m "2" git merge hehe #合并分支,提示failed vim readme.txt #合并分支出现冲突,会修改当前分支的出现冲突的文件,需要把<<<、===、>>>符号删除,调整文件内容 ... #<<<<<<< HEAD ... #======= ... #>>>>>>> hehe ... git add test.txt #现在可以顺利提交 git commit -m "fixed" git log --graph --pretty=oneline --abbrev-commit #查看Git历史提交记录(可以看到分支的变化) git branch -d hehe #可以删掉hehe分支 git branch
部署Git服务器
Git是分布式的版本控制系统,只要有一个原始Git版本仓库,就可以让其他主机克隆走这个原始版本仓库,从而使得一个Git版本仓库可以被同时分布到不同的主机之上,并且每台主机的版本库都是一样的,没有主次之分(每一台安装了git的机器,既是服务器也是客户机),极大的保证了数据安全性,并使得用户能够自主选择向哪一个Git服务器推送文件。
下面实验将还原虚拟机。
服务端和客户机均需安装git:yum install -y git。
部署git服务器需要做的工作:
- 创建作为版本仓库的目录,规范一般以.git为后缀;
- 建立git用户,用于专门管理git服务;
- 修改git版本仓库的所有者和所有组为git;
- 初始化git版本仓库
mkdir /root/kamin.git #创建目录,作为git版本仓库 chown -Rf git:git /root/kamin.git/ #修改git版本仓库的所有者和所有组为git cd /root/kamin.git/ #进入kamin.git目录 git --bare init #初始化git版本仓库
客户机需要做的工作:
虽然git服务器已经搭建好,但目前客户机还不能往git服务器提交数据,也不能克隆git版本仓库,需要在服务器上开放至少一种支持Git的协议,如HTTP/HTTPS/SSH等,现在用的最多的就是HTTPS和SSH。
- 1.在客户机中生成SSH密钥,将生成的公钥传给git服务器;
- 2.克隆git服务器中的版本仓库;
ssh-keygen #生成SSH密钥 ssh-copy-id 192.168.10.10 #将客户机生成的公钥传递给Git服务器 git clone root@192.168.10.10:/root/kamin.git #从Git服务器中克隆版本仓库 cd kamin #进入kamin目录,发现明明没有实现创建,凭空出现了kamin目录,这是从git服务器克隆过来的本地git工作目录
客户机向本地git版本仓库提交文件步骤与之前实验一样。
- 1.初始化git工作环境;
- 2.进入本地git工作目录(kamin),创建文件并提交到本地git版本仓库;
git config --global user.name "Wu Jia Ming" #初始化git工作环境(用户名、用户电子邮件地址、用户默认文本编辑器) git config --global user.email "747631184@qq.com" git config --global core.editor vim echo "1st" >> test.txt #创建文件 git add test.txt #添加文件到暂存区 git status #查看git状态,显示new file: test.txt git commit -m "1st commit" #把文件提交到本地git版本仓库 git status #再查看状态,显示nothing to commit
把本地git工作仓库的文件提交给远程git服务器。
注意:git服务器中的版本仓库,即原始仓库是没有文件形式的,只保存元数据。要验证文件是否提交给远程git服务器,需要在另外的客户机或当前客户机再更换一个目录,克隆git版本仓库。
git remote add server root@192.168.10.10:/root/kamin.git #定义远程的git服务器 git push -u server master #将文件提交到git服务器
cd /root/Desktop #切换到桌面
git clone root@192.168.10.10:/root/kamin.git #克隆服务器的git版本仓库
cd /root/Desktop/kamin #进入该目录
cat test.txt #发现test文件,说明刚刚提交给远程git服务器的操作成功
Github托管服务
Github顾名思义是一个Git版本库的托管服务,是目前全球最大的软件仓库,拥有上百万的开发者用户,也是软件开发和寻找资源的最佳途径,Github不仅可以托管各种Git版本仓库,还拥有了更美观的Web界面,您的代码文件可以被任何人克隆,使得开发者为开源项贡献代码变得更加容易,当然也可以付费购买私有库,这样高性价比的私有库真的是帮助到了很多团队和企业。
大多数用户都是为了寻找资源而爱上Github的,进入网站注册等步骤略。
Github帐号用户名:RNGCtrl,密码邮箱就不在这记下了。
登录上Github进入账户配置页面,添加SSH公钥,将Linux中生成的SSH公钥复制道相应页面中。添加后如图:
在github上创建版本仓库:
克隆地址:
在linux中可以执行git clone https://github.com/RNGCtrl/kamin.git克隆上述github的版本库(前提要能上网)。