Git分布式版本控制系统
企业高校持续集成平台场景介绍
何为Git
Git简单的来说,是一种分布式的版本管理工具,其具有以下优点:
1、速度快
2、简单的设计
3、对非线性开发模式的强力支持(可以同时允许几千个分支同时进行开发和切换)
4、完全分布式(防止集中式版本管理工具所出现的单点故障问题)
本地版本控制系统
许多人习惯用复制整个项目目录的方式来保存不同的版本,或许还会改名加上备份时间以示区别,这么做唯一的好处就是简单,不过坏处也步少,有时候回混淆所在的工作目录,一旦弄错文件丢了数据就没法撤销恢复
为了解决这个问题,人们很久以前就开发了许多种本地版本控制系统,大多都是采用某种简单的数据库来记录文件的历史更新差异
集中化的版本控制系统
接下来人们又遇到一个问题,如何让在不同系统上的开发者协同工作?于是,集中化的版本控制系统(Centralized Version Control Systems,简称 CVCS)应运而生.这类系统,诸如CVS,Subversion以及PerForce等,都有一个单一的集中管理的服务器,保存所有文件的修订版本,而协同工作的人们都通过客户端连到这台服务器,取出最新的文件或者提交更新,多年以来,这已成为版本控制系统的标准做法
分布式版本控制系统
于是分布式版本控制系统(Distributed Version Control System 简称 DVCS)面世了.在这类系统中,像Git,Mercural,Bazaar以及Darcs等,客户端并不只是提取最新版本的文件快照,而是把原始的代码仓库完整地镜像下来.这么一来,任何一处协同工作用的服务器发生故障,事后都可以用任何一个镜像出来的本地仓库恢复,因为每一次的提取操作,实际上都是一次对代码仓库的完整备份
实际的例子
以前我所在的小组使用SVN作为版本控制工具,当我正在试图增强一个模块,工作做到一半,由于会改变原模块的行为导致代码服务器上许多测试的失败,所以并没有提交代码.这时候上级对我说,现在有一个紧急的Bug需要处理,必须在两个小时内完成,我只好讲本地的所有修改diff,并输出成为一个patch文件,然后回滚有关当前任务的所有代码,再开始修改Bug任务,等到修改好后,再将patch应用回来,前前后后需要完成多个繁琐的步骤,这还不计中间代码发生冲突所要进行的工作量
可是如果使用Git,我们只需要开一个分支或者转回到主分支上,就可以随时开始Bug修改的任务,完成之后,只要切换原来的分支就可以优雅的继续以前的任务.只要你愿意,每一个新的任务都可以开一个分支,完成后,再将它合并到主分支上,轻松而优雅
因此,分布式的版本控制系统,再每个使用者电脑上就有一个完整的数据仓库,没有网络依旧可以使用Git,当然为了给习惯及团队写作,会将本地数据同步到Git服务器或者GitHub等远程代码仓库
公有的Git远程仓库
Git与SVN的区别
Git与SVN是目前大多数企业所选择的版本管理工具,两者也代表了版本管理的两个主要的设计模式和理念。SVN是集中式版本管理系统的代表,中央代码库存储着所有代码提交者的代码,而每个项目的参与者只负责代码的提交,中央代码库则保存着完整版的所有代码。但这也造成了一系列的问题:
1、中央代码库单点故障所带来的项目风险,并且一旦发生,代码则无法恢复。
2、提交代码时由于网络带宽问题所带来时间上的延迟,影响开发人员的工作效率。
3、对于中央代码库而言,其服务器压力过大,数据库容量暴增。
Git则是建立在本地库基础之上的分布式版本管理工具,最终可以使得任何代码的提交者都可以成为“中央代码库”。Git的根本思想和基本工作原理主要是在本地复制一个“代码库”,每次提交的代码均是推送到本地代码库中,节约了由于网络带宽所带来的限制,不至于出现提交5MB的代码而需等待20分钟的情况。另一方面,一旦中央代码库的服务器出现“崩溃”,那么任何“本地库”均可以还原中央代码库。
Linux是如何通过Git来实现这个目标的呢?
Git的核心思想在于,每次代码版本的更迭和变化,Git都会记录数据文件的整体是否发生变化,而大多数其他系统则只关心文件内容的具体差异变化,这类系统每次记录有哪些文件作了更新,以及都更新了哪些行的什么内容。
Git并不保存这些前后变化的差异数据。实际上,Git更像是把变化的文件快照后,记录在一个微型的文件系统中。每次提交更新时,它会纵览一遍所有文件的指纹信息并对文件一一作快照,然后保存一个指向这次快照的索引。为提高性能,若文件没有变化,Git不会再次保存,而只是对上一次保存的快照作一链接。
总的来说,Git是一个存储着整个代码快照或者代码快照链接的小型文件系统,所以在每次代码修改之后都能够保持整个代码库的“风貌”,而不是像其他版本管理系统一样,只记录修改的文件和具体的修改内容。要复原整个代码库的话,还需借助“原始材料”。
Git的基本设计理念
近乎所有操作都是本地执行
在Git中的绝大多数操作都只需要访问本地文件和资源,不用联网。如查看提交记录、修改内容信息、版本更新、建立分支、切换分支等等。因为Git在本地磁盘上就保存着所有当前项目的历史更新,所以处理起来速度飞快。例如,你如果想要查看现在的代码和一个月前的版本有何差异,那么Git会取出一个月前的代码快照和此时的代码做一次对比,而不用请求远程服务器来做这件事,或者是把老版本的代码拉到本地作比较。
如果是SVN的话,那么你必须在有网络的情况下才能够提交代码,而此时你现在在火车上,那么你无法做任何事情,然而如果你正在使用Git,那么你可以随时的提交已经测试通过的代码模块,等到有网络的时候再上传到远程仓库即可。
时刻保持数据的完整性
Git通过对管理起来的每个文件计算Hash值,从而得到每个数据的唯一标识和索引。在Git中也全靠这些Hash值来唯一识别文件,而不是文件名。该“指纹”Hash值是一个由40位16进制字符串组成的,任何文件的损坏或者数据缺失都会被Git通过Hash值校验轻松识别到。
Git的三个文件区域
对任何一被Git管理的文件而言,其只具有三种可能的状态,分别是“已提交”,“已修改”和“已暂存”。已提交表示该文件已经被安全保存在本地数据库中了;已修改表示此文件修改了,但没有提交保存;已暂存表示已经把修改后的文件提交到了暂存区域但还没有提交。
Git的精髓——分支
分支是Git的核心所在,犹如操作系统的内核对于操作系统而言至关重要,也是Git能够支持上千个分支并发处理的关键所在。为了要理解Git分支的本质思想,我们需要结合Git的保存数据的方式来仔细讲解,Git保存的不是文件差异或者变化量,而只是一系列文件快照,理解这点很关键。
在Git提交时,会保存一个提交(commit)对象,这个commit对象包含着以下内容:
1、指向暂存内容快照的指针。
2、本次提交的作者等相关附属信息。
3、零个或多个指向该提交对象的父对象指针(普通提交只有一个父亲,如果从多个分支合并而来则有多个父亲)。
讲完这些之后,我们再回过头来谈Git的分支。Git中的分支,其实本质上仅仅是指向commit提交对象的可变指针,它在每次提交的时候,都会自动向前移动。新建一个新的分支只是在当前commit对象上再创建一个新的可变指针;Git会保存一个名为HEAD的特别指针,指向分支的可变指针,HEAD表示当前分支。
分支合并,变基
分支合并不是简单地把分支指针右移,而是对三方合并后的结果重新做一个新的快照,并自动创建一个指向它的提交对象。这个提交对象。这个提交对象比较特殊,它有两个祖先。在分支合并的时候,可能会导致冲突的发生,需要人工解决冲突才能够重新提交代码。
分支变基指的是回到两个分支最近的共同祖先,根据当前分支后续的历次提交对象,生成一系列的文件补丁,然后在基底分支的基础之上,逐个应用之前准备好的补丁文件,最后会生成一个新的合并提交对象。切勿对已发布版本的代码进行变基操作。
Git的windows和Linux软件包下载链接
链接:https://pan.baidu.com/s/1CHZpkPlGRDuA0nPGqMYJcg
提取码:cjj6
Windows上Git安装
Windows客户端下载地址:https://www.git-scm.com/downloads
双击安装包一路下一步即可
在桌面上创建一个文件夹
在文件夹右键选择Git Bash Here 进入git命令界面
到此我们windows的git客户端就安装完成了
Linux上yum安装Git
安装环境查看
cat /etc/redhat-release
uname -r
安装git客户端
yum -y install git
which git
git --version
git全局配置
git config --global user.name "Mr.sun" --->配置git使用用户
git config --global user.email "1123400300@qq.com" --->配置git使用邮箱
git config --global color.ui true --->语法高亮
git config --list --->查看全局配置
回滚yum安装过的Git
yum -y history
yum history undo IP号
which git
Linux上源码包安装Git
yum -y install curl-devel expat-devel gettext-devel openssl-devel zlib-devel gcc perl-ExtUtils-MakeMaker
rpm -qa curl-devel expat-devel gettext-devel openssl-devel zlib-devel gcc perl-ExtUtils-MakeMaker
tar xf git-2.9.5.tar -C /usr/src
cd /usr/src/git-2.9.5
./configure --prefix=/usr/local/git
make && make install
ln -sf /usr/local/git/bin/* /usr/bin
which git
源码编译需要链接git的命令库
ln -s /usr/libexec/git-core/* /usr/bin
git --version
至此,利用源码包安装Linux操作系统的git客户端就安装好了
Git的命令入门
工作区(放代码目录)
你建立的本地git项目目录暂存区(git add)
将工作区里变更部分(与上一版本不同的部分)暂时存储起来的地方本地仓库(git commit)
在本地创建的git版本仓库,用于提交工作区的变更远程仓库(git push)
githab,gitlab或者队友机器上所建立的一个仓库
主机 | IP | 备注 |
---|---|---|
GitA | 192.168.200.70 | Git测试服务器 |
GitB | 192.168.200.90 | Git测试服务器 |
Git帮助文档
LANG=zh_CN.UTF8 --->当前界面生效中文模式
git
git init初始化git工作目录
在linux上
mkdir -p /mycode
cd /mycode
git init
ls -la
git add将变更添加进入暂存区
touch test.txt
git add test.txt
查看git工作目录的暂存区状态
git status
git commit将变更从暂存区提交到本地仓库
git commit -m "test.txt"
会提示你告诉它你是谁才让提交
注册自己的个人信息并再次提交(已经注册过个人信息了所以这里没提示)
git config --global user.name "Mr.sun"
git config --global user.email "1123400300@qq.com"
git commit -m "test.txt"
创建github账号,并创建个人github远程仓库
git remote用于管理远程仓库并添加
git remote -v --->查看远程仓库
git remote add 代号 URL --->origin是默认代号
git remote add test https://github.com/linyaonie/yunjisuan.git
git push将本地仓库的变更推送到远程仓库的某个分支
git push -u test master --->test是代号,master是分支名字
git push https://github.com/linyaonie/yunjisuan.git master --->没有设置代号也可以直接URL
需要输入github仓库账号和密码
重新回到github网站刷新一下就会看到推送的分支
git clone克隆一个现有的仓库到本地(在GitB主机测试)
我们在另一台GitB上来模拟其他客户端进行远程仓库到本地仓库的操作
首先yum安装git命令
yum -y install git
which git
创建项目目录并克隆
mkdir /mycode
cd /mycode
git clone https://github.com/linyaonie/yunjisuan.git master
cd master
ls
修改仓库里的文件内容,并提交变更到本地,推送变更到远程仓库
echo "welcome" >> test.txt
git status
将变更加入缓存区
git add test.txt
将缓存区的变更提交到本地仓库(需要先注册个人信息)
git config --global user.email "1123400300.qq.com"
git config --global user.name "Mr.sun"
git commit -m "ceshi.txt"
将本地仓库代码推送到远程仓库
git push -u origin master
输入邮箱和密码进行推送
浏览器打开github查看提交情况
git fetch将远程仓库的变更拉去到本地仓库(GitA主机)
git fetch -u test master
git status
git checkout检查工作目录代码与本地仓库中的代码的差异
git checkout
git merge将远程仓库变更,更新到本地工作目录中
git merge test/master
cat test.txt
git pull将远程仓库的变更拉取到本地仓库,并更新本地工作目录
git pull = git fetch + git merge
--->有版本冲突只有简介的方式才能修改,pull是没办法修改冲突的
在GitA上对文件进行改动,并推送到github远程仓库
echo "yunjisuan" >> test.txt
cat test.txt
git add *
git commit -m "GitA修改了test.txt"
git push -u test master
输入github邮箱和密码并进行上传
在GitB上,拉取远程仓库的变更后直接合并进本地仓库的master分支
git remote -v
git pull origin master
cat test.txt
git mv && git reset暂存区文件的修改和撤销
如果文件还没有被添加到暂存区,那么Linux命令直接改名即可
git status
touch bennet.txt
git status
mv benet.txt yunjisuan.txt
git status
ls
假如变动的文件已经添加到了暂存区(如果直接mv改名,则需要把删除的文件git rm 文件名删除掉)
ls
git add *
git mv yunjisuan.txt benet.txt
git status
ls
通过git reset来给已经添加到暂存区的文件撤销掉
git status
git reset benet.txt
git status
git diff文件对比利器
git diff命令可以将本地的工作目录中的文件与本地仓库的文件进行对比
echo "ceshi" >> test.txt
git diff test.txt
git log查看git提交历史记录
git log -2 --->查看最后2条记录
git log -p -1 --->-p显示每次提交的内容差异
git log --stat -2 --->--stat简要显示数据增改行数,这样就能看到提交中修改过的内容
git log --pretty=oneline --->一行显示提交的历史记录
追根溯源给git log
Git还原历史数据
git服务程序中有一个叫做HEAD的版本指针,当用户申请还原数据时,其实就是将HEAD指针指向某个特定的提交版本,但是因为git是分布式版本控制系统,为了避免历史记录冲突,故使用了SHA-1计算出十六进制的哈西字符串区分每个提交版本,另外默认的HEAD版本指针会指向到最近一次的提交版本记录,而上一个提交版本会叫HEAD^,上上一个版本则会叫HEAD^^,当然一般会用HEAD~5来表示往上数第五个提交版本
git reset --hard HEAD^
--->还原历史提交版本上一次
git reset --hard 3de15d4
--->找到历史还原点的SHA-1值后,就可以还原(值不写全,系统会自动匹配)
修改一个文件,并提交到本地仓库
ls
echo "xin" >> bennet.txt
git add *
git commit -m "benet.txt添加了内容"
查看历史提交记录,并回滚到上一个提交的版本
git log --pretty=oneline
回滚到前一个版本并查看之前改动的文件
git reset --hard HEAD^
git log --pretty=oneline
cat benet.txt
回滚到指定提交的版本
git log --pretty=oneline
git reset --hard 号
git log --pretty=oneline
Git还原未来数据
当我们回滚历史某个提交版本了以后
我们发现我们已经没有在那个版本之后的提交记录了
也就是说,我们一旦还原了历史版本,想要再次还原回去,那么就回不去了
如此一来,一旦错了,我们怎么办
git reflog查看未来历史更新点(所有历史数据)
git reflog
还原之前的版本
git reset --hard 号
git log --pretty=oneline
Git标签的使用
前面回滚使用的是一串字符串,又长又难记
git tag 标签 -m "描述"
每次提交都可以打一个标签
git tag v1.0
--->给当前提交的内容打一个标记(方便回滚)
git tag
--->查看当前所有标签
git show v1.0
--->查看当前1.0版本的详细信息
git tag v1.2 -m "描述"
git tag -d v1.0
--->删除之前的v1.0标签
查看所有标记
git tag
添加v1.0标签给当前提交的内容
git tag v1.0
查看标签为v1.0的提交的详细信息
git show v1.0
修改bennet文件内容,再次进行提交
cat benet.txt
echo "ceshi" >> benet.txt
git add *
git commit -m "benet.txt添加了内容并测试标签"
给本次提交添加v2.0标签
git tag v2.0
git tag
查看标签为v1.0的提交的详细变更内容
git show v1.0
查看标签为v2.0的提交的详细变更内容
git show v2.0
删除v2.0标签,重新设定附带描述信息的标签
git tag -d v2.0
git tag
git tag v2.0 -m "给文件benet.txt增加一行代码"
git tag
git show v2.0
通过标签进行历史提交回滚,回滚到v1.0的版本
cat benet.txt
git reset --hard v1.0
cat benet.txt
gitignore文件
为什么使用.gitignore文件
大量与项目无关的文件全推到远程仓库上,同步的时候会非常慢,且跟编辑器相关的一些配置推上去之后,别人更新也会收到影响.所以,我们使用该文件,对不必要的文件进行忽略,使其不被git追踪.
一般情况下,.gitignore文件,在项目一开始创建的时候就创建,并推送到远程服务器上.这样大家初次同步项目的时候,就会用到该文件,避免以后,团队成员把与项目无关的文件,传到远程服务器上
gitinore文件匹配规则
*.log 表示忽略项目中所有以.log结尾的文件
123?.log 表示忽略项目中所有以123加任意一个字符的文件
/error.log 表示忽略根目录下的error.log文件
**/java/ 匹配所有java目录下的所有文件
!/error.log 表示在前面的匹配规则中,被忽略了的文件,你不想它被忽略,那么久可以在文件前加叹号
添加.gitignore文件并上传到远程仓库(上传远程仓库是为了让克隆远程仓库的人,能直接有过滤的目录)
vim .gitignore
cat .gitignore
target
*.log
?.idea
git add .gitignore
git commit -m "提交gitignore"
git push test master
在GitB主机上重新克隆并创建测试文件和测试
rm -rf /mycode
mkdir /mycode
cd /mycode
git clone https://github.com/linyaonie/yunjisuan.git master
cd master
ls -la
touch 111.log
touch 666
git status
Git分支管理
分支的结构概述
在实际的项目开发中,尽量保证master分支稳定,仅用于发布新版本,平时不要随便直接修改里面的数据文件,那么工作都在dev分支上,每个人从dev分支创建自己个人分支,开发完合并到dev分支,最后dev分支合并到master分支
所以,团队的合作分支看起来会像下图的样子
分支管理工作流程
在工作中,为了保证master分支稳定,产品经历通常会从master分支上复制一份代码作为dev分支
然后成员开发A在从dev分支上复制一份代码叫做michael
然后成员开发B在从dev分支上复制一份代码叫做bob
当一个新功能开发完毕,或者一个bug修复完毕以后.开发人员会先将代码变更推送到自己的个人分支,然后再把个人分支的变更合并到dev分支里
当开发经历和测试人员拉取dev分支的代码进行测试以后,如果没问题,那么开发经历就会把dev分支的变更合并进master版本
最后,由于master版本新增了测试过的新功能,那么就需要进行项目发布或者代码上线了
Git本地分支管理
本地分支的创建与切换
git branch
--->查看当前分支情况,当前分支前有*号
git branch linux
--->创建分支
git checkout
--->检查本地分支与远程分支的变更差异
git checkout linux
--->切换分支
检查当前分支情况并创建分支和切换分支
git branch
git branch linux
git checkout linux
git branch
尝试在linux本地分支进行代码提交
git branch
cat benet.txt
echo "yunjisuan" >> benet.txt
git add *
git commit -m "linux分支第一次提交"
git checkout master
cat benet.txt
把代码放到暂存区,切换分支(切换分支的时候要清空暂存区,或者提交完在切换,分支公用一个暂存区)
git branch
git checkout linux
touch linux.txt
git add *
git status
git checkout master
git status
git checkout linux
git commit -m "linux分支第二次提交"
git checkout master
git status
本地分支的合并与删除
想把linux的工作成功合并到master分支上
先切换到master分支git merge linux
--->合并linux分支到mastergit branch -d linux
--->确定合并完成后,可以放心的删除linux分支
自动合并本地分支(合并的时候master不能改变,否则合并会出问题)
git branch
cat benet.txt
git merge linux
cat benet.txt
本地分支的删除
git branch -d linux --->假如分支的变更没有合并到当前分支,那么必须用-D参数强制删除分支
git branch
手动合并本地分支===>本地分支代码冲突解决方案
当本地分支之间的同名目录-同名文件-同一行内容不同时,在进行分支合并时就会产生冲突,故为了解决代码冲突,就需要人工手动合并
在工作中,为了进来避免冲突,一般开发人员之间尽可能不负责相同的功能模块,也就不至于同时修改同一个文件
在GitA上进行操作,让linux分支和master分支产生文件代码冲突
git branch
git branch linux
git checkout linux
ls
echo "linux" >> linux.txt
cat linux.txt
git add *
git commit -m "linux提交"
git status
切换到master
git checkout master
echo "master" >> linux.txt
cat linux.txt
git add *
git commit -m "master提交"
git status
GitA上master进行合并linux分支
git branch
git merge linux
vim linux
<<<<<<< HEAD --->表示当前所在的分支
master --->此行表示当前所在分支本行的master和下边的linux所在分支的linux冲突
======= --->隔离符号
linux --->和上边的master内容冲突
>>>>>>> linux --->linux分支
手动排除,选择保留后的内容,假如我们要保留linux分支的内容,然后再将工作目录中的变更提交即可人工解决代码冲突,并完成分支合并
修改完分支冲突在提交并删除分支
cat linux.txt
git add *
git commit -m "修改了一个分支冲突"
git status
git远程分支管理
将linux本地分支代码和标签推送到github远程仓库的linux分支
在GitA上,创建本地分支linux,复制master代码
git branch
git branch linux
git checkout linux
ls
将工作目录代码benet.txt文件修改后提交到本地linux分支
echo "远程分支提交测试" >> benet.txt
cat benet.txt
git add *
git commit -m "远程测试"
git status
将本地linux分支推送到远程github的linux分支
git push test linux
输入邮箱和密码
创建本地标签,并推送到githud
git tag v3.0 -m "测试"
git tag
git push test v3.0
输入邮箱和密码
浏览器访问
在GitA上简单创建一个没有web界面的git远程服务器的本地仓库
useradd git
echo "linyaonie" | passwd --stdin git
su - git
cd /home/git
pwd
mkdir repos
cd repos
mkdir dev yunjisuan
cd yunjisuan
git --bare init --->--bare加上这个参数这个目录就只能当仓库使,就不能代码提交了
pwd
/home/git/repos/yunjisuan --->仓库链接地址
在GitB上创建目录并克隆
mkdir /test
cd /test
git clone git@192.168.200.70:/home/git/repos/yunjisuan
输入密码并克隆,因为创建的目录里面没有任何东西,所以提示是一个空版本库
在测试目录下创建文件并生成git远程仓库
ls
cd yunjisuan
touch test.txt
git add *
git commit -m "仓库"
git remote -v
git push -u origin master
因为总是需要输入密码,所以最好做一个免密钥
ssh-keygen
ssh-copy-id -i ~/.shh/id_ras.pud git@192.168.200.70
把测试目录删除并重新克隆进行测试
rm -rf /test
mkdir /test
cd /test
git clone git@192.168.200.70:/home/git/repos/yunjisuan
ls
cd yunjisuan
ls
这时候一个没有web界面的,远程服务器的本地git仓库就搭建完成了