git教程及开发规范

Royal
2022-08-12 / 0 评论 / 223 阅读 / 正在检测是否收录...

Git简介
git是一种源码管理系统(source code management,缩写为SCM)。它对当前文件提供版本管理功能,核心思想是对当前文件建立一个对象数据库(object database),将历史版本信息存放在这个数据库中,git是目前最好用的版本控制系统(没有之一)。

  • 集中式和分布式
    版本控制系统又分为集中式和分布式两种,我们熟悉的CVS及SVN都是属于集中式的版本控制系统,而git则是分布式版本控制系统,集中式和分布式版本控制系统有什么区别?
    集中式版本控制系统,版本库是集中存放在中央服务器的(如图一),而干活的时候,用的都是自己的电脑,所以要先从中央服务器取得最新的版本,然后开始干活,干完活了,再把自己的活推送给中央服务器。中央服务器就好比是一个图书馆,你要改一本书,必须先从图书馆借出来,然后回到家自己改,改完了,再放回图书馆。
    l6q8xlqx.png
    分布式版本控制系统与集中式版本控制系统有何不同呢?首先,分布式版本控制系统根本没有“中央服务器”(如图二),每个人的电脑上都是一个完整的版本库,这样,你工作的时候,就不需要联网了,因为版本库就在你自己的电脑上。既然每个人电脑上都有一个完整的版本库,那多个人如何协作呢?比方说你在自己电脑上改了文件A,你的同事也在他的电脑上改了文件A,这时,你们俩之间只需把各自的修改推送给对方,就可以互相看到对方的修改了。
    在实际使用分布式版本控制系统的时候,其实很少在两人之间的电脑上推送版本库的修改,因为可能你们俩不在一个局域网内,两台电脑互相访问不了,也可能今天你的同事病了,他的电脑压根没有开机。因此,分布式版本控制系统通常也有一台充当“中央服务器”的电脑,但这个服务器的作用仅仅是用来方便“交换”大家的修改,没有它大家也一样干活,只是交换修改不方便而已。
    l6q8xvuw.png
    和集中式版本控制系统相比,分布式版本控制系统的安全性要高很多,因为每个人电脑里都有完整的版本库,某一个人的电脑坏掉了不要紧,随便从其他人那里复制一个就可以了。而集中式版本控制系统的中央服务器要是出了问题,所有人都没法干活了。本文重点讲下分布式版本控制系统git的使用。
  • Git安装
    Git可以在Linux、Unix、Mac和Windows这几大平台上正常安装运行。安装git可以直接去官网下载安装最新的安装程序:https://git-scm.com/downloads,安装步骤这里就不再详细说。
  • Git工作区、暂存区、版本库的工作流程
    我们每天使用 git ,但是很多命令记不住。一般来说,日常使用只要记住下图(如图三)6个命令,就可以了。但是熟练使用,恐怕要记住60~100个命令,首先来了解几个概念:
    l6q8zhaz.png
  • Workspace:工作区,就是你在电脑里能看到的目录,包括你在IDE中开发项目进行文件的添加、修改、删除等操作。
  • Index / Stage:暂存区,英文叫stage或index。一般存放在.git目录下的index 文件(.git/index)中(如图四),所以我们把暂存区有时也叫作索引(index)。暂存区指将工作区中的操作完成小阶段的存储,是版本库的一部分。
  • Repository:仓库区(或本地仓库),仓库区表示个人开发的一个小阶段的完成,仓库区记录的各版本是可以查看并回退的和暂存区不同的是暂存区一旦提交就再也没有了。
  • Remote:远程仓库,简单来说就是我们搭建的git服务器gitlab或者gitee或者github代码托管平台等,存储每个成员的编码记录。
    l6q90o02.png

用命令的方式解释下各个区是如何工作的:
l6q90v1u.png

  1. 工作区与暂存区
    当对工作区修改(或新增)的文件执行git add命令时,暂存区的目录树被更新,同时工作区修改(或新增)的文件内容被写入到对象库中的一个新的对象中,而该对象的ID被记录在暂存区的文件索引中。工作区到暂存区常用命令如下:
    git add [file1] [file2] ... # 添加一个或多个指定文件到暂存区  
    git add [dir] # 添加指定目录到暂存区包括子目录
    git add . # 添加当前目录的所有文件到暂存区
    git rm [file] # 同时删除工作区和暂存区的文件
    git rm --cached [file] # 删除暂存区的文件并保留工作区的文件
    git checkout . # 用暂存区全部文件替换工作区的文件
    git checkout -- # 用暂存区指定文件替换工作区的文件
    git checkout这个操作很危险,会清除工作区中未添加到暂存区中的改动。
  2. 暂存区与仓库区
    当执行提交操作(git commit)时,暂存区的目录树写到版本库(对象库)中,master 分支会做相应的更新。即 master 指向的目录树就是提交时暂存区的目录树。暂存到仓库区常用命令如下:
    git commit -m “本次提交说明” # 提交暂存区文件到本地仓库中  
    git reset HEAD # 移除添加到暂存区的文件,暂存区的目录树会被重写,被 master 分支指向的目录树所替换,但是工作区不受影响
    git reset HEAD^ # 去掉上一次的提交变成add之前的状态
    git reset --soft HEAD^ # 去掉上一次的提交变成add之后,commit之前的状态
  3. 版本库与远程仓库
    当执行提交操作(git push)时,会把本地仓库中文件提交到远程仓库中,常用命令如下:
    git pull # 取回远程仓库的变化,并与本地分支合并
    git push # 上传本地指定分支到远程仓库
  4. 创建版本库
    了解完工作区、暂存区和本地仓库的关系之后,我们知道暂存区和本地仓库都是版本库的一部分。
    什么是版本库呢?版本库又名仓库,英文名repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被Git管理起来,每个文件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以“还原”。下面我们开始git的实际操作。
    第一步:需要创建版本库,非常简单,直接创建一个空目录,进入空目录里面即可(以linux操作系统为例):
    $ mkdir testgit
    $ cd testgit
    $ pwd
    /root/gittest
    第二步:通过git init 命令把这个目录变成git可以管理的仓库;
    l6q94lyw.png

仓库建好了,当前目录下会多一个.git的目录,这个目录就是用来跟踪管理版本库的,我们也可以在其他路径使用git init <目录名称>来指定目录作为git仓库。
第三步:在工作区新建文件并把文件添加到仓库;
$ touch readme.txt
$ vi readme.txt
$ cat readme.txt
hello git
切记,新建文件一定要放到gittest目录(子目录)中,因为这是一个git仓库,放到其他地方git再厉害也找不到这个文件。
第四步:用命令git add告诉Git,把文件添加到仓库;
git add readme.txt
第五步:用命令git commit告诉Git,把文件提交到本地仓库;
$ git commit -m “测试git”
l6q95bu6.png

-m:后面输入的是本次提交的说明,建议我们在开发过程中要养成习惯,输入的内容要有意义,这样能够从历史记录中轻易找到方便我们回退版本;
1 file changed:1个文件被改动(我们新添加的readme.txt文件);
1 insertions:插入了1行内容(readme.txt有1行内容hello git);
除了以上信息之外,我们发现GIT需要我们设置名称和邮箱地址,因为Git是分布式版本控制系统,所以,每个机器都必须自报家门:你的名字和Email地址。
Git的配置文件为.git目录下的config文件,常用命令如下:
git config --list # 显示当前的Git配置  
git config [--global] user.name "[name]" # 设置提交时的用户信息
git config [--global] user.email "[email address]" # 设置提交时的邮箱地址

  • 版本回退
    我们现在已经知道了怎么修改文件,并且能够把修改后的文件提交到git版本库,然后我们不停的修改文件,不停的提交,通过git log命令我们可以查到我们都做了哪些提交。
    $ git log
    l6q967l0.png

通过以上命令我们可以看到从最近到最远提交的日志记录,一共提交了3次,最近的一次是“测试git3 修改hello git 2023”,上一次是“测试git2 修改hello git 2022”,第一次是“测试git”,可以试试加上--pretty=oneline参数打印一次:
l6q96ndn.png

这样是不是看得更清楚?
第一段代表commit id为版本号,和SVN不一样,Git的commit id不是1,2,3……递增的数字,而是一个SHA1计算出来的一个非常大的数字,用十六进制表示,而且你看到的commit id和我的肯定不一样,以你自己的为准。为什么commit id需要用这么一大串数字表示呢?因为Git是分布式的版本控制系统,后面我们还要研究多人在同一个版本库里工作,如果大家都用1,2,3……作为版本号,那肯定就冲突了。
第二段就很好理解了,代表我们提交说明,每次修改的内容是什么一目了然。
假如我们现在有个需求,需要把reademe.txt回退到上一个版本,也就是测试git2 输出hello git2的哪个版本,怎么做?
在git中,用HEAD表示当前版本,上一个版本是HEAD^,上上一个版本HEAD^^,往上100个版本是HEAD~100,现在回退到上一版本可以使用如下命令:
$ git reset --hard HEAD^
l6q96x98.png

可以看到readme.txt的内容已经回退到上一版本并输出hello git 2022.
假如我们现在需求变了!!需要回退到2023的版本怎么办呢?我们打印log发现2023的日志没了,这时候怎么办呢?
l6q974uz.png

我们使用git reflog命令来打印所有版本的commit id
l6q97amk.png

然后查询到git 2023的版本号为8b2cd52,再执行以下命令可以看到回退到2023的版本了。
$ git reset --hard 8b2cd52
l6q97m32.png

Git版本回退,常用命令如下:
git log --pretty=oneline # 显示历史版本  
git reset --hard HEAD^ # 回退到上一版本
git reflog # HEAD所指向的一个顺序的提交列表
git reset --hard # 回退到指定版本号

  • 撤销修改
    场景1:当你改乱了工作区某个文件的内容,想直接丢弃工作区的修改时,用命令:
    $ git checkout -- file
    场景2:当你不但改乱了工作区某个文件的内容,还添加到了暂存区时,想丢弃修改,分两步:
    $ git reset HEAD 表示把暂存区的修改撤销掉,回到场景1
    $ git checkout -- file 表示把工作区的修改直接撤销掉
    场景3:已经提交了不合适的修改到版本库时,想要撤销本次提交,参考上节版本回退。
  • 删除文件
    假如我们在工作区中删除了某个文件,会发生什么呢?
    l6q98p4d.png

现在你有两个选择,一是确实要从版本库中删除该文件,那就用命令git rm删掉,并且git commit:
l6q98v74.png

现还有一种情况是删错了,因为版本库里还有呢,所以可以很轻松地把误删的文件恢复到最新版本:
$ git checkout -- 表示用版本库里的版本替换工作区的版本
Git删除,常用命令如下:
git rm # git rm就是删文件,并且把删文件的修改提交到暂存区,相当于rm删文件后,git add 提交,保存修改  

  • 添加、推送、删除远程仓
    现在的情景是,你已经在本地创建了一个Git仓库后,你的代码只能在你的电脑上看到,不能跟其他同事协作开发,这时候就需要有一个远程仓库来同步我们的本地仓库,既可以作为备份,也可以让其他人通过远程仓进行协作,这就是我们开篇提到的分布式版本控制系统通常也有一台充当“中央服务器”,来交换大家的修改。
    第一步:添加远程仓,以gitlab为例介绍:
    $ git remote add origin git@172.30.1.24:webhex/cipher-php.git
    远程库的名字就叫origin,是可以更换的,下一步就可以把本地库的所有内容推送到远程库上了:
    $ git push -u origin master
    我们第一次推送master分支时,加上了-u参数,Git不但会把本地的master分支内容推送的远程新的master分支,还会把本地的master分支和远程的master分支关联起来,在以后的推送或者拉取时就可以简化命令。
    如果想删除远程库,可以使用如下命令(使用前,建议先用git remote -v查看远程库信息):
    $ git remote -v 查看远程库
    $ git remote rm origin 删除名字是origin的远程库
    l6q9bajh.png
  • 克隆远程仓
    现在我们知道了,先在本地建立版本库,然后添加远程仓,最后实现工作区到版本库再到远程仓的协作开发,那假如我们现在从零开发,最好的方式先在gitlab、gitee或者github等建立了远程仓,怎么实现关联呢?
    我们只需要把远程库克隆到我们本地就能在本地同步一个本地版本仓。
    $ git clone git@172.30.1.24:webhex/cipher-php.git
    然后我们就可以在生成的目录里面添加文件、修改文件、删除文件操作,然后如果有多个人协作开发,那么每个人各自从远程克隆一份就可以了。
  • 分支管理
    git版本库创建后会默认创建一个master分支,这个分支也叫主分支。当我们需要多人协作最后合并代码,或者我们需要有测试环境、生产环境、BUG修复环境等等,这时候一个主分支已经不够用了。
    一开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点:
    l6q9ewo0.png
    每次提交,master分支都会向前移动一步,这样,随着你不断提交,master分支的线也越来越长。当我们创建新的分支,例如dev时,Git新建了一个指针叫dev,指向master相同的提交,再把HEAD指向dev,就表示当前分支在dev上:
    l6q9fdg2.png

不过,从现在开始,对工作区的修改和提交就是针对dev分支了,比如新提交一次后,dev指针往前移动一步,而master指针不变:
l6q9fk4x.png

假如我们在dev上的工作完成了,就可以把dev合并到master上。Git怎么合并呢?最简单的方法,就是直接把master指向dev的当前提交,就完成了合并:
l6q9fx5c.png

所以Git合并分支也很快!就改改指针,工作区内容也不变!
合并完分支后,甚至可以删除dev分支。删除dev分支就是把dev指针给删掉,删掉后,我们就剩下了一条master分支:
l6q9g3qt.png

Git分支管理,常用命令如下:
git checkout -b dev #   git checkout命令加上-b参数表示创建并切换,相当于以下两条命令:
git branch dev # 创建dev分支
git checkout dev # 切换到dev分支
git branch # git branch命令会列出所有分支,当前分支前面会标一个*号。
git merge dev # 把dev分支合并到当前所在的分支
git branch -d dev # 删除dev分支
git switch -c dev # 创建切换分支同git checkout -b dev一样
git switch dev # 切换到dev分支同git checkout dev

  • 分支管理策略
    在实际开发中,我们应该按照几个基本原则进行分支管理:
    首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
    那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布时,再把dev分支合并到master上,在master分支发布1.0版本;
    你和你的小伙伴们每个人都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就可以了。
    所以,团队合作的分支看起来就像这样:
    l6q9m21s.png
    合并分支时,加上--no-ff参数就可以用普通模式合并,合并后的历史有分支,能看出来曾经做过合并,而fast forward合并就看不出来曾经做过合并。
  • BUG分支
    软件开发中,bug就像家常便饭一样。有了bug就需要修复,在Git中,由于分支是如此的强大,所以,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。
    当你接到一个修复一个代号101的bug的任务时,很自然地,你想创建一个分支issue-101来修复它,但是,等等,当前正在dev上进行的工作还没有提交:并不是你不想提交,而是工作只进行到一半,还没法提交,预计完成还需1天时间。但是,必须在两个小时内修复该bug,怎么办?
    Git还提供了一个stash功能,可以把当前工作现场“储藏”起来,等以后恢复现场后继续工作:
    git stash
    首先确定要在哪个分支上修复bug,假定需要在master分支上修复,就从master创建临时分支:
    $ git checkout master
    $ git checkout -b issue-101
    Switched to a new branch 'issue-101'
    现在修复bug,改完文件后,然后提交。修复完成后再次切换到master主分支,并完成合并,删除issue-101分支:
    $ git switch master
    Switched to branch 'master'
    $ git merge --no-ff -m "merged bug fix 101" issue-101
    现在,是时候接着回到dev分支干活了!用git stash list命令看看有哪些工作区:
    $ git stash list
    用git stash apply恢复,但是恢复后,stash内容并不删除,你需要用git stash drop来删除;另一种方式是用git stash pop,恢复的同时把stash内容也删了:
    在master分支上修复了bug后,我们要想一想,dev分支是早期从master分支分出来的,所以,这个bug其实在当前dev分支上也存在。那怎么在dev分支上修复同样的bug?重复操作一次,提交不就行了?有木有更简单的方法?有!同样的bug,要在dev上修复,我们只需要把bug分支提交的commit id找到这个提交所做的修改“复制”到dev分支。注意:我们只想复制那一个commit id这个提交所做的修改,并不是把整个master分支merge过来。Git专门提供了一个cherry-pick命令,让我们能复制一个特定的提交到当前分支:
    $ git cherry-pick
  • 多人协作
    多人协作的工作模式通常是这样:
    1.首先,可以试图用git push origin 推送自己的修改;
    2.如果推送失败,则因为远程分支比你的本地更新,需要先用git pull试图合并;
    3.如果合并有冲突,则解决冲突,并在本地提交;
    4.没有冲突或者解决掉冲突后,再用git push origin 推送就能成功!
    5.如果git pull提示no tracking information,则说明本地分支和远程分支的链接关系没有创建,用命令git branch --set-upstream-to origin/
    6.在本地创建和远程分支对应的分支,使用git checkout -b branch-name origin/branch-name,本地和远程分支的名称最好一致。
  • 标签管理
    在Git中打标签非常简单,首先,切换到需要打标签的分支上:
    $ git tag v1.0
    可以用命令git tag查看所有标签:
    $ git tag
    v1.0
    默认标签是打在最新提交的commit上的。有时候,如果忘了打标签,比如,现在已经是周五了,但应该在周一打的标签没有打,怎么办?方法是找到历史提交的commit id,然后打上就可以了:
    $ git tag v0.9
    注意,标签不是按时间顺序列出,而是按字母排序的。可以用git show 查看标签信息:
    还可以创建带有说明的标签,用-a指定标签名,-m指定说明文字:
    $ git tag -a v0.1 -m "version 0.1 released"
    如果标签打错了,也可以删除:
    $ git tag -d v0.1
    因为创建的标签都只存储在本地,不会自动推送到远程。所以,打错的标签可以在本地安全删除。
    如果要推送某个标签到远程,使用命令git push origin
    $ git push origin v1.0
    或者,一次性推送全部尚未推送到远程的本地标签:
    $ git push origin --tags
    如果标签已经推送到远程,要删除远程标签就麻烦一点,先从本地删除:
    $ git tag -d v0.9
    然后,从远程删除。删除命令也是push,但是格式如下:
    $ git push origin :refs/tags/v0.9
    Git标签管理,常用命令如下:
    git tag # 用于新建一个标签,默认为HEAD,也可以指定一个commit id
    git tag -a -m "blablabla..." # 可以指定标签信息
    git tag # 查看所有标签
    git push origin # 可以推送一个本地标签
    git push origin --tags # 可以推送全部未推送过的本地标签
    git tag -d # 可以删除一个本地标签
    git push origin :refs/tags/ # 可以删除一个远程标签
  • git产生冲突的情况汇总
    本地拉取:
    1)当远端有更新,本地库没有变化,拉取操作远端会覆盖本地库(远端版本高于本地)
    2)当远端修改,本地库也有修改,拉取操作会产生冲突文件(远端版本和本地版本冲突)
    3)当本地库修改,而远端没有变化,拉取不会产生变化(本地版本高于远端版本)
    4)当本地库版本低于远端版本,则无法推送,必须先拉取在操作,否则报错
    分支合并:
    1)当资源分支版本高于目标分支时,合并,资源分支同名文件会覆盖目标分支;
    2)当目标分支版本高于资源分支时,合并,提示Already up-to-date.(已经更新),目标分支内容不会变化;
    3)当目标分支和资源分支同名文件都有改动时,会报冲突,需手动解决; 
  • 参考资料
    git官方教程https://www.runoob.com/git/git-tutorial.html
    阮一峰git工作流程https://www.ruanyifeng.com/blog/2015/12/git-workflow.html
    廖雪峰git教程https://www.liaoxuefeng.com/wiki/896043488029600/
0

评论

博主关闭了当前页面的评论