# learnGit
**Repository Path**: hanxiaofeng03218/learn-git
## Basic Information
- **Project Name**: learnGit
- **Description**: 学习gitee操作
- **Primary Language**: Unknown
- **License**: MulanPSL-2.0
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2023-06-06
- **Last Updated**: 2023-06-09
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# git基本介绍
*注:这一部分主要针对git进行版本控制的原理进行一下介绍,重点关注分支的实现,对git比较熟悉的同学可以直接跳过这一部分*
## 什么是Git?
**Git**是一款免费、开源的**分布式**版本控制系统,用于敏捷高效地处理任何或小或大的项目。
最初是 Linus Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。
## 什么是分布式版本控制系统?
分布式版本控制系统没有“中央服务器”,每个人的电脑上都是一个完整的版本库,这样工作的时候,无需联网,因为版本库就在你自己的电脑上。
多人协作只需要各自的修改推送给对方,就能互相看到对方的修改。
但是通常会有一个服务器用来方便彼此之间的信息传递。
我们可以自己搭建这个充当中间人的服务器,但是成本较高。
同时也有提供这种服务器的网站,我们可以直接使用这些网站为我们提供的服务器,
这些网站就是github或者gitee。
集中式和分布式的真正区别是:
**你的本地是否有完整的版本库历史!**
集中式的版本库历史完全储存在中央服务器中,每次个人电脑只能从中央服务器中提取某一版本对应的代码,没有这个代码对应的历史修改信息。
假设集中式管理系统的服务器没了,那你丢掉了所有历史信息,因为你的本地只有当前版本以及部分历史信息。
假设GitHub服务器没了,你不会丢掉任何git历史信息,因为你的本地有完整的版本库信息。你可以把本地的git库重新上传到另外的git服务商。
## Git的基本架构
1. 工作区:电脑里能看到的目录,平时存放、修改项目代码的地方。
2. 暂存区:事实上只是一个文件,保存即将提交到文件列表信息。
3. 仓库区(或版本库):就是安全存放数据的位置,这里面有你提交到所有版本的数据。用一个HEAD指针指向最新放入仓库的版本。
4. 远程仓库:托管代码的服务器,用于与他人交换数据。
Git工作的简单流程图如下

图1
## Git的更新操作、删除操作、分支操作、分支的合并
简单介绍一下Git进行一些基本操作的原理,Git的基本操作基本都是通过指针来进行实现的,这也决定了Git的操作速度很快
Git在进行版本管理时将我们每一次提交的修改进行记录(注意记录的仅仅是修改),包装成一个版本节点,并将这些节点按照时间顺序进行串联。

图2
版本库结构如上图所示,当我们进行一次更新后相当于在后面接上一个新的节点

图3
### 版本的更新和回退
Git使用了一个HEAD指针来指向当前节点,也就是当前使用的版本,非HEAD指针指向的节点我们就称之为“历史记录”。当进行更新时,只需要将HEAD指针从旧节点转为指向新节点。
同理如果我们想要找回旧版本,将HEAD指针重新指向对应的旧版本节点即可。
### 版本的分支
有时我们的版本流程图可能并非一条直线,它可能产生分叉,我们将产生的每一个分叉称为 **“分支”**
最初的那个分支,或者说主干分支,我们通常称之为master
为了区分每一个分支,当创建一个新的分支时,只需使用一个指针指向此分支的开始节点,之后对此分支每次进行修改时,就将此指针此分支中的最新节点。比如我们可以创建master指针指向主干分支。
上面所提到的HEAD指针,我们提到它是指向当前版本的,在多个分支的项目中也被用来指明当前所在的分支,此时提交的任何修改节点均会被连接到这个分支的后面。
其实HEAD严格来说不是指向提交,而是指向分支指针(如master),而master才是指向提交的。
**举个例子**
一开始的时候,master分支是一条线,Git用master指向最新的提交,再用HEAD指向master,就能确定当前分支,以及当前分支的提交点:

图4
当我们当前在master所指向的提交版本,我们此时想要创建新的分支,比如叫做branch时,Git新建了一个指针叫 **branch**,指向master相同的提交,再把HEAD指向dev,就表示当前分支在branch上。

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

图6
此时如果我们把当前分支切换回master,在master上进行提交,就会产生分叉:

图7
### 分支的合并
如果我们想要将如图6所示的分支branch合并至master中,注意此时需要被合并的分支位于branch的某个历史节点中,此时只需简单地就是直接把master指针指向branch所指节点即可。

Fast forward
这种方法称为Fast forward模式,但这种模式下,删除分支branch指针后,会丢掉分支信息,它和直接更新master分支就毫无区别了。
如果要强制禁用Fast forward模式,Git就会在merge时生成一个新的commit节点,这样,从分支历史上就可以看出分支信息。

非Faster Forward
但是如果master和branch的位置图7所示,此时就不能如此简单的进行合并,否则会丢失修改信息,此时我们称分支master和分支branch产生了 **“冲突”**
此时就不能通过简单的调整指针来合并分支了,必须要手动解决冲突,将master分支中的修改更新至branch分支之后再进行合并。
# git的基本命令
接下来介绍一下git的基本指令
## 创建类
· 创建本地git空白仓库:在要创建仓库的电脑目录下输入命令
```
git init
```
· 从远程克隆git项目,此方法通常在远程库已存在的时候,也是通常项目的创建方式,即先在远程创建仓库,然后将仓库克隆下来。
```
git clone [git项目地址]
```
## 提交
· 将文件提交进暂存区
```
git add [文件名]
```
· 将某个文件删除,删除操作也是一种修改
```
git rm [文件名]
```
· 将暂存区内的修改提交进版本库,这个命令对应生成一个版本节点,因此往往需要添加对此次提交的说明,这样你就能从历史记录里方便地找到改动记录。
```
git commit -m "提交说明"
```
## 版本管理
· 显示仓库当前的状态,主要显示哪些工作区的修改没有被add进暂存区,暂存区中内容哪些没被commit
```
git status
```
· 如果想要查看工作区相对于版本库中某个版本所做的具体修改,括号中为某个版本的commit id活HEAD指针等信息标记一个具体的版本。
```
git diff (HEAD --) [文件名]
```
· 显示从最近到最远的提交日志,如下显示了近两次的提交
```
$ git log
commit ba7be79fbbebe9f64837cb90c615f495653a2212 (HEAD -> dev, origin/dev)
Author: hanxiaofeng03218
Date: Tue Jun 6 19:52:26 2023 +0800
add to branch dev
commit 70a9b46bfee210f7e7858261f802b75dc4d325b5 (origin/master, master)
Author: hanxiaofeng03218
:...skipping...
```
进行版本回退
其中commit id 就是上面log中commit后面的一串数字,此命令使用其前6个字母即可(如ba7be7)。
还可以使用HEAD^表示当前版本的上一个版本
· 版本回退后工作区的内容同时会被更新为对应版本的内容
```
git reset --hard [commit id]/HEAD^
```
· 撤销修改
1. 撤销在工作区的修改,让这个文件回到最近一次git commit或git add时的状态。
```
git checkout -- [文件名]
```
2. 撤销暂存区的修改
```
git reset HEAD [文件名]
```
3. 撤销版本库中的修改,直接进行版本回退即可,但只能改变当前版本的状态,当前版本记录仍保存在版本库中。
## 远程仓库
### SSH链接远程仓库
本地Git仓库和Gitee仓库之间的传输是通过SSH加密的,因此要链接gitee仓库首先要配置一下。
第1步:创建SSH Key,后面的邮箱最好是你在gitee上注册邮箱。
```
$ ssh-keygen -t rsa -C "youremail@example.com"
```
之后一路回车即可,在用户主目录里找到.ssh目录,里面有id_rsa和id_rsa.pub两个文件。
登陆gitee,在设置界面找到 “SSH公钥” 界面,填上任意Title,在Key文本框里粘贴id_rsa.pub文件的内容

为什么GitHub需要SSH Key呢?因为GitHub需要识别出你推送的提交确实是你推送的,而不是别人冒充的,而Git支持SSH协议,所以,GitHub只要知道了你的公钥,就可以确认只有你自己才能推送。
### SSH警告
当你第一次使用Git的clone或者push命令连接GitHub时,会得到一个警告:
```
The authenticity of host 'github.com (xx.xx.xx.xx)' can't be established.
RSA key fingerprint is xx.xx.xx.xx.xx.
Are you sure you want to continue connecting (yes/no)?
```
这是因为Git使用SSH连接,而SSH连接在第一次验证GitHub服务器的Key时,需要你确认GitHub的Key的指纹信息是否真的来自GitHub的服务器,输入yes回车即可。
### 远程连接基础命令
在本地仓库目录下,运行如下命令,将当前的本地仓库与远程仓库建立联系,定位之后的操作对应的远程仓库位置
origin是给远程库起的名字,这是Git默认的叫法,后面的命令可以用origin指代远程库
```
$ git remote add origin git@gitee.com:account_name/libary_name
```
· 查看当前远程库的连接状态
```
$ git remote -v
origin git@gitee.com:hanxiaofeng03218/learn-git.git (fetch)
origin git@gitee.com:hanxiaofeng03218/learn-git.git (push)
```
· 把本地库当前分支的内容推送到远程库上
```
git push [远程库名字](origin) [远程分支名](master)
```
· 从远程仓库拉取最新的代码到本地仓库,并且自动合并到当前分支,
[本地分支名]可以省略,默认合并至当前分支。
```
git pull [远程库名] [远程分支名]:[本地分支名]
```
git clone与git pull区别是:git clone使用时本地不存在版本库,是将远程仓库完整地复制一份到本地,包括所有分支和历史记录。git pull则是拉取更新信息。
· 删除远程库的某分支
```
git push --delete [远程库名字](origin) [远程库对应分支名字](master)
```
## 分支管理
· 创建分支
```
git branch [分支名]
```
· 切换分支,switch配合-C参数可以直接创建新分支并切换过去
```
git checkout [目标分支名]
或者
git switch (-C) [目标分支名]
```
· 查看当前分支, 会列出所有分支,当前分支前面会标一个*号
```
$ git branch
* dev
master
```
· 删除分支
```
git branch -b [分支名]
```
· 将指定分支合并到当前分支上
```
$ git merge [分支名]
Updating d46f35e..b17d20e
Fast-forward
readme.txt | 1 +
1 file changed, 1 insertion(+)
```
注意到上面的Fast-forward信息,Git告诉我们,这次合并是“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。
合并后再删掉被合并到当前分支的那个分支,这和直接在master分支上工作效果是一样的。
· 若要禁止使用Fast-forward方式合并,使用下述命令
因为非Fast-forward合并要创建一个新的commit(相见git基本介绍部分),因此要加上描述。
```
git merge --no-ff -m "合并描述" [分支名]
```
· 获取远程分支及从远程库中拉取分支
参照git push和git pull命令即可。
## 标签管理
标签tag就是一个让人容易记住的有意义的名字,它跟某个commit绑定在一起,便于人们定位到此commit,没有tag时commit由commit id进行唯一标记,但是commit id十分复杂,下面就是某个id。
```
ba7be79fbbebe9f64837cb90c615f495653a2212
```
在Git中打标签非常简单,首先,切换到需要打标签的分支上:
```
git checkout master
```
使用下面的命令进行打标签,省略commit_id时默认标签打在此分支的最新一次提交上(即HEAD指针所指的地方)。
```
git tag -a [标签名](v0.1) -m [提交说明] [commit_id的前几位](f52c633)
```
删除标签
```
git tag -d [标签名]
```
推送标签到远程,[tagname]推送某一个,--tags推送所有的
```
git push origin [tagname]/--tags
```
**注:标签命名规范**

(1)主版本号:主版本号一般从0或者1开始,一般当项目进行重大修改,则版本号加1;
(2)次版本号:次版本号是相对于主版本号而言,一般对应的是主版本下局部的调整;
(3)修订版本号:一般是Bug的修复或者细小的变动而发布的一个版本;
# git开发流程:分支策略(git flow)
## git flow介绍
gitflow是git的一种工作流程规范,由Vincent Driessen最先提出来,目的是为了解决分支和commit杂乱无章的问题,在实际开发过程中,若多名程序员开发同一个项目时很容易造成代码混乱甚至代码丢失的情况,而合理的运用gitflow规范可以很好地解决这个问题.
GitFlow工作流定义了一个围绕项目发布的严格分支模型,它为不同的分支分配了明确的角色,并定义分支之间何时以及如何进行交互。
## git flow中的分支
原文链接:https://blog.csdn.net/ku_carp/article/details/119105237
- Production 分支
也就是我们经常使用的Master分支,这个分支最近发布到生产环境的代码,最近发布的Release, 这个分支只能从其他分支合并,不能在这个分支直接修改
- Develop 分支
这个分支是我们是我们的主开发分支,包含所有要发布到下一个Release的代码,这个主要合并与其他分支,比如Feature分支
- Feature 分支
这个分支主要是用来开发一个新的功能,一旦开发完成,我们合并回Develop分支进入下一个Release
- Release分支
当你需要一个发布一个新Release的时候,我们基于Develop分支创建一个Release分支,完成Release后,我们合并到Master和Develop分支
- Hotfix分支
当我们在Production发现新的Bug时候,我们需要创建一个Hotfix, 完成Hotfix后,我们合并回Master和Develop分支,所以Hotfix的改动会进入下一个Release
## gitflow流程描述

gitflow流程图
首先是两条常驻分支:master 和 develop。
这两分支永远是代码同步的状态,也就是说,当任意一条分支发生改变时,另一条分支也要跟着改变。
其中master分支部署于生产环境,要时刻保证master分支可以正常运行。

当我们想要进行开发时,新添加的功能要基于develop分支进行。
如图,假设在当前项目中,需要开发两个模块,所以基于develop分支创建两个feature分支(feature分支为功能分支)。
因为模块1功能点较多,所以程序员小王和小李两个人去开发模块1;
模块2功能点较少,所以程序员小张一个人去开发模块2。

在开发模块1时,因为两个人同步开发,所以应当基于feature/order_upgrate_cy_dev打子分支,两人在各自的子分支下进行开发,当开发完毕后,再从各自的子分支合并到feature/order_upgrate_cy_dev这个分支下,当两人都已合并完成后,先拉下develop最新代码(因为模块2可能已经开发完毕并合并到develop下了),然后就可以把分支feature/order_upgrate_cy_dev合并到develop下了。
开发模块2同理
在模块1和模块2都开发完成并合并到develop下以后,就要来到第三步了,也就是要介绍的relese分支。

如图,为release分支,由版本号命名,其作用主要为上线前的准备,包括**测试和app质量检测**.
之前在开发过程中,程序员都会进行单元测试,至少保证了功能实现,但是还要考虑别的因素就需要集成测试,系统测试,验收测试,回归测试了,如果该软件活跃用户较高,还需要压力测试.
下面说下release分支的流程,我们从develop分支下打一个release分支下来,命名跟着上一个版本号走,例如上一个版本是v0.1,那么该版本就命名就为v0.2(注意release分支为app大版本的迭代),接下来我们需要把release分支交给测试和质量检测部门去做测试相关的工作,若测试有问题,则程序员需要在release分支下进行代码修改,然后再交由测试,直至测试没问题为止,当测试通过后,就到了上线环节了,我们需要把release分支同时合并到master分支和develop分支下并打上标记(tag,tag命名就是版本名), 然后线上跑的是master,这一个版本迭代就实现了。
### 流程总结
1. 根据需求基于develop创建feature分支
2. 在feature分支上进行开发
3. 将feature合并入develop分支
4. 基于develop创建release分支
5. 在release分支上进行功能测试
6. 在release分支上打tag,定版本
7. 将release合并入master和develop
### Hotfix分支
如图为hotfix分支,其主要目的是线上bug修改,小功能的修改。

在软件开发过程中难免会有bug或者新增一个字段这种小需求,为解决这些小问题直接按照前面的流程开一个大版本更新是不必要的,而且bug需要尽快修复不可能去等下一个版本迭代再去修改,这时候我们的hotfix分支就派上用场了。
下面说下hotfix主要流程,hotfix分支直接基于master创建。我们需要先切到master分支,然后在master分支下新建一个hotfix分支,之后就是在hotfix下进行bug修复了,修改完毕后再推到master和develop并打标记,bug就修复成功了。
标记的命名规则为:在当前release版本号后面写小版本号,例如: 当前的release分支为v0.2,则hotfix分支名为v0.2.1,下个hotfix分支名为v0.2.2,
--------
目前总结的仅是进行git开发的基础知识,其他功能需要大家在开发过程中自行探索,欢迎大家对本文档进行补充修改。