# git_demo **Repository Path**: jswyll_com/git_demo ## Basic Information - **Project Name**: git_demo - **Description**: No description available - **Primary Language**: Unknown - **License**: LGPL-3.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2022-01-16 - **Last Updated**: 2023-08-05 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Git 笔记 > Git是一个强大的版本控制系统,可以记录、对比、云同步、回退、撤销、还原文件的改动。 > > 本文原文链接: ## 引言 为什么要使用Git? > “项目需求有变,回到半个月前的样子!” > > “今天现场测试的版本不稳定,前天不是好好的吗,这两天改了什么?能不能用之前的版本测试?测完再研究现在这个版本的问题?” > > “之前xx版本的功能不要了,能不能只去掉那个功能而保留后来的功能?” > > “删掉的功能又需要了,能不能恢复回来?” > > “发现紧急BUG,请暂停当前的新改动,先修复BUG。” > > “改一下论文,改完同步给老师,让老师知道你改了哪里。” > > “电脑坏了,上个月的版本发给小伙伴有备份,但是忘了这个月改了什么了!” 每次改了文件把整个工程的文件复制出来作为备份?嗯,这是一个粗暴且有效的办法,但不便于管理。 > **一些教程** > > 菜鸟教程 - 《学习Git》[\[1\]](https://www.runoob.com/git/git-tutorial.html "菜鸟教程 - 学习Git") > > 廖雪峰 - 《浅显易懂的Git教程》[\[2\]](https://www.liaoxuefeng.com/wiki/896043488029600 "廖雪峰 - 浅显易懂的Git教程") > > Git官方中文教程[\[3\]](https://git-scm.com/book/zh/v2 "Git官方中文教程") > > 码云 - Git 知识大全[\[4\]](https://gitee.com/help/categories/43 "Git 知识大全") --- ## 基础 ### 基本概念 - **工作区(workspace)**: 电脑当前的目录、文件 - **暂存区(stage/index)**: git提交到版本库之前暂存的地方 - **本地仓库(repository)**: 本地版本库,记录着每个版本之间的文件差异 - **远程仓库(remote)**: 远程版本库 - **分支(branch)**: 使用分支意味着你可以从开发主线上分离开来,然后在不影响主线的同时继续工作 ### 工作流程 - `git init` 初始化。创建版本库(默认分支为`master`),生成一个隐藏的`.git`目录,版本库的记录存在里面,初始化后才可以跟踪当前目录(版本库根目录)和子路径下的文件。 - `git add 文件路径` 添加文件到暂存区。可以使用`git add .`将除[忽略跟踪](#%E5%BF%BD%E7%95%A5%E6%96%87%E4%BB%B6)以外的文件都添加到暂存区。 - `git commit -m "备注"` 将暂存区的文件变化提交到版本库。每提交一次,就生成了一个版本,记录在`.git`目录下的文件里。添加`-a`参数可以将全部已跟踪的文件的提交,省去`git add 文件路径`的步骤,**但不会提交未跟踪过的文件**。 - `git push 远程主机名 本地分支名:远程分支名` 将本地版本库推送到远程版本库。远程和本地的分支一样时可以忽略`:远程分支名`。 - `git pull` 拉取远程版本库到本地版本库,并合并文件变化到工作区。 Git工作流程[\[5\]](https://www.cnblogs.com/moris5013/p/12263582.html "踏月而来 - git版本控制") ![Git工作流程](figure/Fig.1.png "Git工作流程") > **Note** > > 可以“无视”暂存区的功能,将`git add`和`git commit`看作一个操作,只管`工作区 - 本地仓库 - 远程仓库`的关系。 ### 操作步骤 > **Note** > > 在命令行里,复制和粘贴的快捷键分别是`Ctrl+Insert`和`Shift+Insert`,按TAB键进行命令补全,按↑、↓查看历史命令。 > 输入`git help Git指令`可以查看该指令的完整说明手册。 1. 下载安装Git[\[6\]](https://git-scm.com/downloads "Git - downloads") 。 2. 进入命令行模式。打开工作区文件夹,右键选择`Git Bash Here`。 ![进入命令行模式](figure/fig_enter_bash.jpg "进入命令行模式") 3. 第一次使用Git需要配置个人信息: ```sh git config --global user.name "姓名" git config --global user.email 邮箱 ``` 4. `git init`初始化为Git仓库。 5. 编写`.gitignore`文件来忽略跟踪当前目录下不需要跟踪的文件。 在仓库根目录下新建名为`.gitignore`(没有其它后缀名)的文件,编写规则可以忽略跟踪指定的文件,这个功能可以用来指示Git忽略编译生成的中间或输出文件。以文本方式编辑,多条规则分行写,以`#`开头的行是注释。 #### 忽略文件 ```bash # 忽略所有.o文件 *.o # 忽略JLinkLog.txt JLinkLog.txt # 忽略所有名称中含.uvguix的文件 *.uvguix* ``` #### 忽略文件夹 ```bash # 忽略settings文件夹下的所有文件 settings/ # 忽略build文件夹下的所有文件(可以不加最后的斜杠/) build ``` #### 在已忽略文件中不忽略指定文件 以感叹号开头[\[7\]](https://www.cnblogs.com/e206842/p/7121927.html "git提交忽略文件或文件") ```bash # 忽略node_modules文件夹下除了layer/layer.js以外的文件 /node_modules/* !/node_modules/layer/layer.js ``` > **Note** > > 忽略规则中,以/开头表示只忽略与仓库根目录相关的文件夹或文件,否则表示忽略路径中包含该规则的文件。 > > 示例: 假设仓库下的文件结构为 > > ```sh > ├─┬ path > │ │ └── file.c (1) > │ │ └── file2.c (2) > ├─┬ path2 > │ ├── path > │ │ └── file.c (3) > │ ├── file.c (4) > │ ├── file2.c (5) > ├── file.c (6) > ``` > > 则不同忽略规则对应如下 > > ```sh > # 忽略(1)(3)(4)(6) > file.c > > # 忽略(6) > /file.c > > # 忽略(4) > path2/file.c > > # 忽略(1)(2)(3) > path > > # 忽略(1)(2) > /path > ``` #### 忽略文件的模板 > GitHub 有一个十分详细的针对数十种项目及语言的 .gitignore 文件列表, 你可以在[\[8\]](https://github.com/github/gitignore ".gitignore模板") 找到它。 ⑥ `git add .`提交除忽略文件以外的全部文件到暂存区。 ⑦ `git commit -m "备注"`提交更改,产生版本。 ### 远程仓库 远程仓库可以用于云备份和协作,比较知名的远程仓库有GitHub和码云Gitee,以下使用码云。 ⑧ 创建远程仓库[\[9\]](https://gitee.com/help/articles/4120 "码云 - 创建你的第一个仓库"),得到一个远程仓库链接`https://gitee.com/用户名/xxx.git`。 ⑨ 在本地仓库`git remote add origin 远程仓库链接`,第一次会弹出提示框输入码云的账号(用户名)和密码,可以在`控制面板 -> 用户账户 -> 凭据管理器 -> 管理Windows凭据`查看或修改远程仓库使用的账号信息。 ⑩ `git branch --set-upstream-to=origin/master master`把本地分支和远程分支关联(关联后拉取和推送可以省去远程名称和分支名称)。 ⑪ `git pull`将远程仓库的内容合并到本地仓库和工作区。 ⑫ `git push`将本地仓库的内容推送到远程仓库,可以添加`-f`参数表示强制以本地仓库覆盖远程仓库的当前内容。 每次改动了文件后,重复步骤2、6、7提交一个新的版本;如果需要把本地的提交备份到远程仓库,重复步骤11\~12。 > **Note** > > Git以行 为单位进行比较差异,请养成在文件末尾添加一个空行的习惯。 > > 对于Word文档之类的二进制文件,只能跟踪整个文件大小的变化。(但对于Latex语法编辑的论文,可以按行跟踪。) ### 自动提交 对于Windows平台,可以在仓库根目录编写`.bat`脚本文件进行批处理: ```bash @echo off set DATE=%date:~0,4%.%date:~5,2%.%date:~8,2% git add . git commit -am %DATE% git pull origin master --tags git push origin master --tags ``` 其中,第一行代码表示关闭回显,第二行表示取系统日期作为提交备注(形如`2022.01.12`),参数`--tags`表示拉取或推送远程仓库的标签 每次双击该文件就可以将工作区的文件改动一键提交到本地和远程仓库。 把该bat脚本添加到Windows任务[\[10\]](https://blog.csdn.net/u013788943/article/details/81629645 "添加Windows任务")可以让电脑定时自动运行该脚本,比如设置每天中午12:00自动备份一次。 #### 多仓库自动提交 将上面的[一键提交脚本](#push_bat)命名为`push.bat`,新建一个`PushAll.bat`脚本放在任意地方,编写如下代码 ```bash set PATHS=仓库1根目录 仓库2根目录 仓库3根目录 (for %%p in (%PATHS%) do ( cd %%p call push.bat )) ``` 然后把`PushAll.bat`添加到Windows任务即可定时批量备份仓库 ### 取消跟踪 使用过一次`git add`添加过某个文件后,文件就处于Git跟踪状态了,这时才在`.gitignore`写忽略规则是不生效的。使用`git rm --cached 文件路径`可以取消跟踪该文件。 ```bash # 移除单个文件的跟踪 git rm --cached test.txt # 移除文件夹的跟踪 git rm --cached -r MCUAssistant/bin/ ``` --- ## 版本管理 假设有三个文本文件`ming.txt`、`hong.txt`和`hua.txt`分别代表小明、小红和小华(素材: 驻颜术[\[11\]](https://blog.csdn.net/thewindkee/article/details/105804165 "thewindkee - git reset --soft驻颜术(恢复文件之前的状态)")),前后共提交了5次,每次的提交信息备注和改动为: 1. `初始化` - +`小明18岁`(ming.txt) - +`小红20岁`(hong.txt) 2. `小明23岁` - +`工作了5年,23岁了`(ming.txt) 3. `结婚` - +`23岁的小明娶了小红`(ming.txt) - +`2014年小红嫁给了小明`(hong.txt) 4. `小华出生` - +`2016年小明当爹了`(ming.txt) - +`2016年生了小华`(hong.txt) - +`小华出生`(hua.txt) 5. `2022年` - +`2022年小明31岁了,已经是大明了`(ming.txt) - +`2022年小红忘了自己的年龄,但是脸上有了岁月的痕迹`(hong.txt) - +`2022年小华8岁了`(hua.txt) 第5次提交后,各文件的内容为: ming.txt: ```plaintext 18岁的小明 工作了5年,23岁了 23岁的小明娶了小红 2016年小明当爹了 2022年小明31岁了,已经是大明了 ``` hong.txt: ```plaintext 20岁的小红 2014年小红嫁给了小明 2016年生了小华 2022年小红忘了自己的年龄,但是脸上有了岁月的痕迹 ``` hua.txt: ```plaintext 小华出生 2022年小华8岁了 ``` ### 提交日志 每提交一次,就产生了一个版本。使用`git log`查看提交日志,加`--oneline`可以使每条记录显示为一行。 ![Git提交日志](figure/fig_git_log.jpg "Git提交日志") 可以看到每次提交的commit号、操作人的名字和邮箱、提交时间、提交备注。 ### 版本编号 每个版本都会有一个对应的commit号,可以用它的前几位表示该版本。也可以用下面的方式代表版本: ```bash HEAD 当前版本 HEAD~1 上一个版本 HEAD~2 上上一个版本 HEAD~3 上上上一个版本 以此类推... ``` 还可以使用[版本标签](#%E7%89%88%E6%9C%AC%E6%A0%87%E7%AD%BE)标记,语义化版本号,打标签后可以用标签名代替commit号。 ### 版本标签 `git tag 标签 版本`对目标版本添加标签。不提供版本参数时,默认对当前版本打标签。标签名需要唯一,一个版本可以有多个标签。 ```bash # 对当前版本添加标签v5 git tag v5 # 对commit号为dbc202开头的版本打标签birth git tag birth dbc202 # 对之前的版本打标签 git tag v4 HEAD~1 git tag v3 HEAD~2 git tag v2 HEAD~3 git tag v1 HEAD~4 # 删除本地标签 git tag -d 标签名 # 删除远程标签 git push --delete origin 标签名 ``` 查看结果: ![打标签后的结果](figure/fig_git_log_oneline_tags.jpg "打标签后的结果") ### 文件状态 `git status`查看文件的状态,加`-s`参数简介显示,加`-u`参数显示工作区新增的文件所在目录和具体文件(默认只显示目录)。`??`表示文件是工作区新增的(未被跟踪,Untracked);`M`(右边,红色)表示在工作区文件改动了且未暂存(Modified);`A`(左边,绿色)表示暂存了新增的但未提交(Added);`M`(左边,绿色)表示暂存了改动的文件但未提交;`D`表示之前已跟踪的文件在工作区被删除(Deleted);`MM`表示文件暂存了还没提交又在工作区改动了。 示例: HEAD处于`结婚`时,工作区改成了`小华出生`且未暂存时、暂存后、提交后的文件状态: ![Git文件状态](figure/fig_status.jpg "Git文件状态") ### 查看改动 `git show 版本`显示某个版本相对于其前一个版本的差异 示例: 查看`小华出生`改变了什么: ![详细改动记录](figure/fig_git_show_birth.jpg "详细改动记录") 可见,Git的每次提交记录的是与前一次提交的差异。 ### 比较差异 - `git diff`比较工作区和暂存区差异。改动了工作区已跟踪的文件后,可以用这个指令查看改动了哪些文件和内容,因为继上次提交后暂存区与当前版本(HEAD)一致。 - `git diff 版本1 版本2`比较两个版本间的差异,待比较的放在后面。 - `git diff HEAD`比较工作区和版本库差异。 - `git diff --cached`比较暂存区和版本库差异。 示例: 查看全家人从`结婚`(不含)后至`2022年`发生了什么: ![全家人结婚之后的改动](figure/fig_diff.jpg "全家人结婚之后的改动") > **Note** > > 使用`git diff`(不加其它参数)比较的是已处于跟踪状态的文件,因此不能比较出工作区刚新增的(Untracked)文件的内容。要比较工作区与HEAD的差异,可以先用`git status`列出新增的和改动(已跟踪)的文件列表,再用`git diff`比较已跟踪文件的变化;或先用`git add`将新增的文件加入跟踪状态,再用`git diff --cached`查看全部改动。 ### 版本回退 `git reset 模式 目标版本`将版本回退到某个版本,该版本之后的版本的改动将被“移除”。模式是以下值之一,默认为`--mixed` - `--mixed`: 将HEAD和暂存区恢复为目标版本 - `--soft`: 将HEAD移到目标版本 - `--hard`: 将工作区、暂存区和HEAD恢复为目标版本。**工作区内未提交到仓库的改动将被丢弃!** 要回退到的版本为D,则不同参数的操作结果如下表 | 参数 | 工作区(working) | 暂存区(index) | 当前版本(HEAD) | | --- | --- | --- | --- | | 原版本 | A | B | C | | --mixed | A | D | D | | --soft | A | B | D | | --hard | D | D | D | 假设当前状态如下图(暂存区与HEAD一致): ![当前状态](figure/fig_v1_now.jpg "当前状态") 工作区相对于`v5`版本改动了一行,这就相当于删去了原来的一行再新增了一行。 #### 回退示例1 `git reset --hard v3`全家人回退到`结婚`版本: ![--hard回退](figure/fig_hard_reset.jpg "--hard回退") 可见工作区被目标版本覆盖了,包括已改动但未提交的文件;`hua.txt`文件被删除。 #### 回退示例2 目标: 全家人回退到结婚时,并保留未提交的改动`2014年小红25岁,嫁给了小明`。 1. `git reset v3`全家人回退到`结婚` ![回退示意图](figure/fig_soft_reset_graph.jpg "回退示意图") 2. `git status`+`git diff HEAD`查看回退的版本之后的各个版本+工作区总共改动了什么 ![回退后工作区与HEAD的差异](figure/fig_soft_reset_diff.jpg "回退后工作区与HEAD的差异") 相对于HEAD,当前工作区存在一个新增的文件`hua.txt`和两个有改动的文件。 3. 决定文件内容的去留。 新增的文件(相对于HEAD为未跟踪的文件)如果不要了,直接删除(hua.txt);改动了的文件,如果需要使用HEAD的版本,使用`git checkout 文件路径`(ming.txt);如果需要使用工作区的,直接暂存/提交;或根据工作区和HEAD版本的差异各留一部分。然后,使用`git add`和`git commit`发起提交。 总之,这时候就是在相对于回退的版本(`v3`)作修改,产生一个新版本。 ![决定文件内容去留](figure/fig_after_soft_reset.jpg "决定文件内容去留") > **Note** > > 如果不需要保留回退版本之后的所有内容,使用`git reset --hard 目标版本`;否则使用`git reset 目标版本`。`git reset --hard`不会删除当前工作区中未被跟踪的文件,追加命令`git clean -df`可删除全部未被跟踪过且不在忽略规则中的文件。 ### 版本撤销 `git revert 目标版本`以一次新的提交撤销掉某个历史提交的版本。 撤销示例: 当前版本为`v5`,需要撤销`v2`(小明23岁)的改动。 ![撤销版本](figure/fig_revert.jpg "撤销版本") 撤销时出现了冲突,[解决冲突](#解决冲突)的办法是把冲突的文件修改为需要的内容,然后再发起提交。 > **Note** > > 使用`git log`、`git diff`、`git reset`、`git show`、`git checkout`指令时可以添加文件路径参数,限定操作只影响指定的文件。 > > #### 限定示例1 > > 当前版本为`v5`,查看`hong.txt`在哪些提交被改动过: > > ![查看指定文件的提交](figure/fig_log_limit.jpg "查看指定文件的提交") > > #### 限定示例2 > > 当前版本为`v5`,小红要去除`2022年`产生的“岁月痕迹”、小明无所谓、小华不能丢: > > ![回退限定](figure/fig_reset_limit.jpg "回退限定") ### 任意穿梭 使用`git reset`后,回退版本之后的版本会被“移除”,使用`git log`不显示,这时要想恢复到回退版本之后的某个版本: 如果记得目标版本的commit号或标签,可以使用`git reset`可以穿梭回去;如果不记得了,使用`git reflog`可以查看引用日志,引用日志记录了每次的改动后HEAD指向的commit号。 ![任意穿梭](figure/fig_reflog.jpg "任意穿梭") > **Note** > > 再次提醒: 任意穿梭的是已提交改动产生的版本,使用`git reset --hard`应先使用`git status`确认有没有未提交的改动,或先提交全部改动(产生一个版本)再回退,或不使用`--hard`参数然后决定是否保留工作区可能存在的未提交的改动。 --- ## 可视化操作 命令行模式适用于Linux、Windows、Mac等平台,但需要记忆一些指令。掌握了Git的原理后,可以使用GUI完成大多数操作。除了`Git GUI`以外,大多数IDE平台内置了Git,例如`Visual Studio`、`Visual Studio Code`(`VS Code`)、`eclipse`和基于它开发的IDE(`RT-Thread Studio`、`STM32 Studio`、`AURIX Development Studio`等)、`IntelliJ IDEA`/`PyCharm`。以下以轻量级代码编辑工具`VS Code`为例。 ### 基本操作 1. 下载安装VS Code[\[12\]](https://code.visualstudio.com/Download "Download Visual Studio Code");安装后打开,在左侧`扩展`栏分别搜索`Chinese`(中文简体)和`GitLens`插件安装。 2. `菜单 -> 文件 -> 打开文件夹...`打开工作区文件夹。 3. 打开左侧分支图标`源代码管理`栏,初始化当前工作区目录为Git仓库。 ![初始化Git](figure/vs_init_git.jpg "初始化Git") 4. 添加码云的远程仓库。 ![添加远程仓库](figure/vs_add_remote.jpg "添加远程仓库") 然后输入远程仓库名称(一般用`origin`),按回车确认。 5. `源代码管理 -> 更改`列出了所有改动的文件和Git状态,`U`、`M`、`D`分别表示对应工作区上的文件未跟踪(新增的)、已改动、已删除。单击文件可以浏览文件的改动。 ![查看已改动的文件](figure/vs_watch_diff.jpg "查看已改动的文件") 如果需要撤销工作区的改动,可以点文件名右方的撤销图标![撤销图标](figure/vs_restore_icon.jpg "撤销图标")将文件恢复到上个版本的状态,否则进行下一步。 6. 提交全部改动到仓库。 ![提交全部改动到仓库](figure/vs_commit.jpg "提交全部改动到仓库") 7. 把本地仓库推送到远程仓库。 如果远程仓库是第一次创建并且是空的,显示的是`$(云上传)发布分支` ![发布分支](figure/publish_branch.jpg "发布分支") 之后是`$(同步)同步更改` ![同步更改](figure/vs_sync_change.jpg "同步更改") 每次改动文件后,重复步骤5~7。 ### 管理版本 假设该工作区的文件提交了5次,改动内容和备注和[前面](#版本管理 "版本管理")的一样。 #### 文动历史 打开文件时,在左侧侧边栏`时间线`可以看到文件在哪些提交中改动了这个文件,单击某个提交可以查看该提交的改动。 ![文件时间线](figure/vs_timeline.jpg "文件时间线") #### 其它操作 `源代码管理 -> COMMITS`可以查看仓库提交历史。展开某个版本(commit号或标签)可以看到改动了的文件,点击改动的文件可以查看该提交的改动内容。 ![仓库提交历史、标签等](figure/vs_commits_log.jpg "仓库提交历史、标签等") 在版本上右键,可以进行版本对比、撤销、回退、打标签等等。例如点击版本回退,选择一个回退模式 ![回退模式](figure/vs_reset.jpg "回退模式") 然后根据情况重复步骤5\~7。 这些功能由`GitLens`插件提供,更多使用说明见: 《GitLens - Git supercharged》[\[13\]](https://marketplace.visualstudio.com/items?itemName=eamodio.gitlens "GitLens - Git supercharged") --- ## 多人协作 ### 同级开发 1. 仓库管理者创建仓库,得到远程仓库链接 2. 管理者添加开发者[\[14\]](https://gitee.com/help/categories/37 "Gitee - 仓库成员管理") 3. 开发者使用`git clone 远程仓库地址`克隆远程仓库到本地 4. 各成员每次工作时,`git pull`拉取云端最新的改动并合并到本地 -> 修改 -> 提交 -> 同步(拉取+推送)。 ### 解决冲突 两个不同的地方(本地的不同分支、本地与云端、两个人通过远程仓库协作)基于同一各版本,改动了文件后各自产生了向后的版本,Git尝试进行合并,如果两个新版本改动的地方相同(同一个文件的同一处地方),则产生冲突。 示例: 基于前面的v5(commit号为`372b817...`)版本,开发者A修改了`hong.txt`为 ```plaintext 20岁的小红 2014年小红25岁,嫁给了小明 2016年生了小华 2022年小红忘了自己的年龄,但是脸上有了岁月的痕迹 ``` 提交改动后产生了版本`4ff4c9e...`; 开发者B也基于v5版本修改了`hong.txt` ```plaintext 20岁的小红 2014年小红27岁,嫁给了小明 2016年生了小华 2022年小红忘了自己的年龄,但是脸上有了岁月的痕迹 ``` 然后B发起提交,产生`5888e4a...`版本,这时B同步(拉取+推送)时产生了冲突,冲突的地方被Git修改为: ```plaintext <<<<<<< 当前版本 当前内容 ======= 对方内容 >>>>>>> 对方版本 ``` 打开该文件可以看到 ![出现冲突](figure/vs_conflict.jpg "出现冲突") VS Code还在上方提供了保留当前改动、保留对方改动、保留双方改动、对比文件、开启动态会话按钮,可以点击前三个按钮的其中一个快速解决冲突,或根据需要手动修改冲突的文件。**改完后发起暂存+提交就算解决了冲突**,使用`git log --oneline --graph`可以查看版本的走向。 ![解决冲突后的分支结构](figure/fixed_conflict_log.jpg "解决冲突后的分支结构") 其中,各行的commit号和备注对应的是带`*`号的版本。 > **Note** > > 如果开发者B先拉取A修改后的文件,然后在A的基础上修改,提交推送时就不会有冲突。这时版本的走向为 > > ![先拉取再修改](figure/pull_then_modify.jpg "先拉取再修改") ### 使用分支 前面的操作都是单分支结构(默认主分支`master`),理解了冲突之后,就可以使用多分支了[\[15\]](https://git-scm.com/book/zh/v2/Git-%E5%88%86%E6%94%AF-%E5%88%86%E6%94%AF%E7%AE%80%E4%BB%8B "Git分支"),每个分支可以有自己独立的版本走向,也可以与另一个分支合并,HEAD其实是指向当前分支的顶端版本。云端可以用`master`分支作为正式版、用`develop`作为开发版;本地新增某个功能时用`feature`开发新特性、用`bug`分支修复问题,完成后再合并到主分支。 ### 代码贡献 > 本文使用markdown编写,管理员阿伟将说明文档和示例文件在码云[\[16\]](https://gitee.com/jswyll_com/git_demo "海南大学刘伟 - Git学习笔记")开源。 > > 访客阿冬在学习时发现一个BUG: 小华出生时应该是2014年! #### 提交Issue 发现开源仓库的问题时,可以提交一个Issue/任务,提交后仓库管理员会收到通知。 #### Pull Request 如果有更好的方案,可以向开源仓库Pull Request(PR)。所谓PR,是指发送通知给目标仓库管理员,请求审核、测试后决定是否当内容合并自己的改动到目标仓库。 > **Note** > > 访客(非仓库成员)直接`克隆仓库 -> 修改提交 -> 推送`会报错`403 拒绝访问` > > ![非仓库成员push](figure/pr_directly_push_error.jpg "非仓库成员push") > > 正确的方式如下。 1. 在云端Fork仓库——打开目标仓库,点击Fork复制一个副本到自己的云空间。 ![Fork仓库](figure/fork.jpg "Fork仓库") 2. 在本地`克隆副本仓库 -> 修改 -> 提交推送`到副本仓库。 3. 在副本仓库发起PR ![发起PR](figure/add_pr.jpg "发起PR") ![填写PR信息](figure/pr.jpg "填写PR信息") 4. 原仓库审查员(默认是仓库管理员)收到通知,审查、测试并合并 ![审查、测试](figure/allow_pr.jpg "审查、测试") ![手动合并(或由系统自动合并)](figure/merge_pr.jpg "手动合并(或由系统自动合并)") 5. PR完成。 ![PR成功](figure/pr_success.jpg "PR成功") > **Note** > > PR的流程略微麻烦,码云提供了轻量级PR的办法——在网页版打开目标仓库直接编辑提交,即可自动发起PR[\[17\]](https://gitee.com/help/articles/4291 "Gitee - 轻量级 PR")。 ### 评审模式 Git本身每个人的权限是平等的,前面[同级开发](#同级开发)开发时各个成员都有权合并自己的改动,管理员可以将仓库的某些分支设置为保护分支,开启评审模式。 ![Gitee设置保护分支](figure/protect_branch.jpg "Gitee设置保护分支") - 标准模式。只有设定的可推送代码的成员和可合并的成员才能推送或合并该分支,其它人推送时返回`拒绝访问`。 示例: 假设云端设置了`master`保护分支和`dev_wei`和`dev_dong`分支,阿伟是管理员,阿冬是普通开发者。阿冬修改了文件 ![阿冬的修改内容](figure/pr_diff.jpg "阿冬的修改内容") 如果他直接向`master`分支推送: ![无权限人员向标准的保护分支推送](figure/push_standard_pb.jpg "无权限人员向标准的保护分支推送") 正确的流程是: 修改提交到本地仓库后,`git push origin master:dev_dong(推送到自己的分支) -> 在网页版登录切换到dev_dong分支发起PR -> 填写PR信息 -> ...` ![仓库成员向保护分支发起PR](figure/pb_add_pr.jpg "仓库成员向保护分支发起PR") - 评审模式。标准模式比较繁琐,Gitee支持设置保护分支为评审模式。 示例: 阿冬可以直接向`master`分支推送,仍然是无权限合并,但云端自动创建了PR。 ![向评审分支推送结果](figure/pb_pr_result.jpg "向评审分支推送结果")