NotePublic/Software/Applications/Git/Git_使用说明.md

977 lines
36 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Git 使用说明
- [Git 使用说明](#git-使用说明)
- [1. 认识 Git](#1-认识-git)
- [1.1. Git 及版本管理工具所要解决的问题](#11-git-及版本管理工具所要解决的问题)
- [1.1.1. 软件版本跟踪](#111-软件版本跟踪)
- [1.1.2. 项目资源备份及集中管理](#112-项目资源备份及集中管理)
- [1.1.3. 多人协作](#113-多人协作)
- [1.1.4. 质量管理](#114-质量管理)
- [1.2. Git 及 GitHub 类服务器系统](#12-git-及-github-类服务器系统)
- [2. Git 的安装及配置](#2-git-的安装及配置)
- [2.1. Git 的安装和配置](#21-git-的安装和配置)
- [2.1.1. Git 的安装](#211-git-的安装)
- [2.1.2. Git 初始配置](#212-git-初始配置)
- [2.1.3. Git 与 SSH](#213-git-与-ssh)
- [2.1.4. Git 代理设置](#214-git-代理设置)
- [2.2. TortoiseGit 的安装和配置](#22-tortoisegit-的安装和配置)
- [3. Git 的使用](#3-git-的使用)
- [3.1. 创建仓库](#31-创建仓库)
- [3.2. 添加跟踪文件](#32-添加跟踪文件)
- [3.3. 取消跟踪文件](#33-取消跟踪文件)
- [3.4. Clone 仓库](#34-clone-仓库)
- [3.5. 提交变更](#35-提交变更)
- [3.6. 推送变更到服务器](#36-推送变更到服务器)
- [3.7. 从服务器获取变更](#37-从服务器获取变更)
- [3.8. 分支管理](#38-分支管理)
- [3.8.1. 基础命令](#381-基础命令)
- [3.8.2. 分支合并](#382-分支合并)
- [3.8.3. 远程分支管理](#383-远程分支管理)
- [3.9. 查看历史](#39-查看历史)
- [3.10. 内容比较](#310-内容比较)
- [3.11. Tag 管理](#311-tag-管理)
- [3.12. 时光穿梭](#312-时光穿梭)
- [3.13. Rebase](#313-rebase)
- [3.14. Remote 管理](#314-remote-管理)
- [3.15. 同步 fork 的仓库](#315-同步-fork-的仓库)
- [3.16 Patch](#316-patch)
- [3.16.1. 生成 Patch](#3161-生成-patch)
- [3.16.2. 应用 Patch](#3162-应用-patch)
- [3.16.3. 合并多个 Patch](#3163-合并多个-patch)
- [3.17. Submodule](#317-submodule)
- [3.17.1. 添加 Submodule](#3171-添加-submodule)
- [3.17.2. 在其他地方使用合并后的版本库](#3172-在其他地方使用合并后的版本库)
- [3.17.3. 更新合并后的版本库](#3173-更新合并后的版本库)
- [3.17.4. 批量操作](#3174-批量操作)
- [3.17.5. 如何保持 Submodule 的同步](#3175-如何保持-submodule-的同步)
- [3.17.6. 删除 Submodule](#3176-删除-submodule)
- [3.17.7. 修改 Submodule](#3177-修改-submodule)
- [4. Git 高级配置](#4-git-高级配置)
- [4.1. Git 环境变量](#41-git-环境变量)
- [4.1.1. GIT\_TERMINAL\_PROMPT](#411-git_terminal_prompt)
- [4.2. 清理本地仓库](#42-清理本地仓库)
- [5. Git 服务器](#5-git-服务器)
- [5.1. Gitea 的下载和安装](#51-gitea-的下载和安装)
- [5.2. 超级管理员](#52-超级管理员)
- [5.3. 普通用户](#53-普通用户)
- [5.4. 组织和团队](#54-组织和团队)
- [5.5. 版本仓库及分支保护](#55-版本仓库及分支保护)
- [5.10. 工单系统](#510-工单系统)
- [5.11. Gitea 综合实践](#511-gitea-综合实践)
- [6. 外部参考资料](#6-外部参考资料)
## 1. 认识 Git
### 1.1. Git 及版本管理工具所要解决的问题
黑格尔曾经说过:“凡是现实的东西都是合乎理性的”,即人们常说的“存在即合理”。
但对于很多开发人员而言,即便是不使用版本管理工具也能完成项目的开发工作。那么版本管理工具合理存在的意义是什么,使用版本管理工具又能够带来哪些好处呢?
结合本人使用版本管理工具的经验,将使用版本工具所带来的好处归纳为以下四点:
1. 软件版本跟踪
2. 项目资源备份及集中管理
3. 多人协作
4. 质量管理
#### 1.1.1. 软件版本跟踪
任何一个开发项目,都必然是一个逐步成熟的过程。从第一行代码到第一个能跑通的程序,是一个渐进的流程。当系统成熟后,依然可以继续可以得到优化和扩展,功能及性能上的改进。这就导致软件、硬件或嵌入式等项目的过程中可以建立多个版本,甚至是在第一个正式版本建立以前就需要对开发过程进行跟踪。
在没有版本管理工具的情况下,人们最常使用的方法就是当到达一个开发节点后就建立一个文件夹或者压缩包,用以保存当前的开发进度。当我们之前某个开发并验证过的功能在后续开发过程中发现功能异常后,就可以使用之前备份的版本进行对比验证,甚至是比较源码以便快速定位问题。
这样做的缺陷很明显,首先每次都要备份完整的开发进度,会导致硬盘资源的极大浪费。其次多个压缩包或者文件夹容易导致混乱,随着项目版本的增加很难判断哪个压缩包对应了哪个版本。我们不得不为每个压缩包写下一个注释来表明该阶段实现了哪些功能。
当一个项目成熟后,人们很可能需要在其基础上实现不同的功能改进。比如我们造了一架陆基飞机,然后要在其基础上改进出一架能够在航母上起飞的飞机。这时候最好的办法是生成两个项目分支,然后在每个分支上分别进行改进,并将共通的改进平移到另一个分支里,以保证相同的项目质量。使用传统的“备份式”版本管理,将会带来一个糟糕的体验。
当项目发布后,并且有多个版本进入到市场后,一旦发现了某些问题,就需要回溯到对应的版本上进行问题排查。此时需要在众多压缩包中找到对应的版本进行解压,这是一个枯燥且繁琐的过程。如果想让这个过程容易些,就需要建立一张表格,来明确不同的版本对应哪个压缩包。不是之前累一些,就是之后累一些。
因此,人们不是没有使用版本管理,只是一致以一种比较简单粗暴的方式在潜移默化的进行管理。
使用版本管理工具会优化这个过程,只需要建立一个文件夹就可以对该文件下面的工程文件进行管理和跟踪,并且每次生成的“跟踪备份”都是基于与上次备份的差异,这就极大减小硬盘空间的占用。
版本管理工具能够提供历史查看、版本比较,分支比较,以及检出功能,这样就可以快速了解每次提交版本的状态,每个项目分支的差异。当每次提交版本时,是一个自动化的过程,可以减少开发人员的工作量。
#### 1.1.2. 项目资源备份及集中管理
传统的“压缩备份”式版本管理的另一个缺陷是它极大的依赖于某台计算机,一旦计算机出现问题就意味着重要资产的损毁。因此开发人员需要定期的将众多“备份”拷贝到某台服务器中,这依然是一个人工过程。
并且,服务器管理的好坏依赖于管理人员自身的能力,想要管理得好,也需要占用管理人员的大量精力。
版本管理工具提供了服务器工具,并且基于 Git 系统的服务器有很好的 Web 管理界面和开发模型可以遵照,在本文的 Git 服务器部分将会看到目前最常用的 Git 管理模型和用法,以及它的优势。
#### 1.1.3. 多人协作
一个小的项目通常可以一个人独立完成。一个大的可以被拆分成多个独立小项目的工程也可以分配给不同的人独立完成,但另外一些项目则往往需要两个以上的人协同工作。
以较为人知的 Linux 内核项目为例,在 2002 年以前,世界各地的志愿者把源代码文件通过 Diff 的方式发给 Linus然后由 Linus 本人通过手工方式合并代码Linux 内核项目当时使用此方式有一些特定的历史原因,但从其过程中不难发现这个过程依然是一个人工过程,且容易在合并过程中出现问题。
```diff
From 7c376cdf6c20f0f0834344be06e34d108e346ca3 Mon Sep 17 00:00:00 2001
From: "rick.chan" <cy@sina.com>
Date: Mon, 25 Dec 2023 00:08:20 +0800
Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E6=B3=A8=E9=87=8A.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: rick.chan <cy@sina.com>
---
subfun.c | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/subfun.c b/subfun.c
index a9a4509..d9f0e5a 100644
--- a/subfun.c
+++ b/subfun.c
@@ -4,6 +4,12 @@ static int fun3(int a, int b)
return a+b;
}
+/**
+ * @brief 增加了一些注释.
+ *
+ * @param a
+ * @return int
+ */
int fun2(int a)
{
return fun3(a, 1);
--
2.43.0.windows.1
```
Git 类版本管理工具的另一个好处就是支持多个人向同一项目提交版本,如果版本不存在差异则自动合并,如果版本存在差异才需要人工介入选择需要正式合并的代码。
#### 1.1.4. 质量管理
前面提到,一个项目、产品的开发过程是逐步成熟的。事实上,不仅在其开发过程中,即便在产品上市后,也有可能存在改进和升级。甚至在量变引起质变后演变成新一代产品。
甚至是对产品不同的改进产生不同的子代产品,比如陆基飞机和航母飞机的例子。
这就需要在研发以及整个产品生命周期,及其子系中进行版本管理。使得产品更加稳定和成熟,具备不同的功能和优化,从而获得更高的产品质量。
在这个过程中,如果不进行版本管理,就如同每次重新造轮子,那么,此次造新轮子的过程中都有可能出现不同的问题,使得产品质量不容易稳定下来。
在一个开发团队中,有产品经理,项目主管、以及开发人员,每个角色对项目的质量都会有不同的需求,以软件开发为例在开发人员进行版本代码提交时,有可能经历一个代码评审的流程。版本管理工具能够方便的实现这个过程,使其在评审过程中及时发现问题,并更好的满足多个角度的开发需求,进而提升开发质量。
### 1.2. Git 及 GitHub 类服务器系统
实际上Git 正是 Linux 内核项目的一个派生项目,就是为了解决 Linux 内核开发管理所诞生的工具。它非常好用且开源,因此被广泛使用且变得越来越流行。
2008 年 2 月,一个名为 GitHub 的网站开始上线4 月其发布了正式版本。这个网站迅速的火了起来,之后更扩大为“全球最大的男性交友平台”,因为有数据统计,其 95% 的使用者均为男性。很快在国内就有了模仿其模式的网站,如 Gitee 和 Coding 等。之后更是诞生了很多类似 GitHub 的服务器软件,比如 Gitlab 和 Gitea。目前许多公司都在使用这类服务或者使用 Gitlab 构建自己的服务器。
GitHub 实际上就是一个 Git 服务器。Git 使一个分布式版本管理工具,这意味着即便不使用服务器也可以进行本地化的版本管理。但是当人们需要进行多人协作开发,工程文件备份和管理以及其他一些便利实用的扩展功能时就必然用到 Git 服务器了。
在 GitHub 诞生之前已经有很多 Git 服务器软件了,实际上使用 Git 自身的命令行工具就可以实现 Git 服务器的搭建。另外还有 Gerrit 基于 Web 的 Git 服务器系统。GitHub 具备很好的操作界面,简单易用,并且提供了有 Issue 系统来讨论需求和问题,最后,它提供了 Wiki 和版本发布系统。
接下来我们看看如何安装和使用 Git 及 Git 服务器。
## 2. Git 的安装及配置
我们可以到 [Git 官方网站](https://git-scm.com/) 下载最新版本的 Git 版本管理工具,它支持 Windows/Linux/Mac 等多个系统平台。
![Git 官网](./img/Git_使用说明/git-official.png)
官网下载的 Git 实际上只是一组命令行工具,但许多开发工具中已经将其进行了集成,比如 QtCreator 和 VSCodeVScode 下除自身的 Git 功能外,还具有很多优秀的插件。
另外,官网也推荐了许多优秀的 Git GUI 前端程序:
![Git GUI 前端](./img/Git_使用说明/git-gui-client.png)
推荐在 Windows 平台上通过 VSCode 来使用 Git 或者使用 [TortoiseGit](https://tortoisegit.org/download/) 工具。
### 2.1. Git 的安装和配置
#### 2.1.1. Git 的安装
![Git 的安装](./img/Git_使用说明/install-git.gif)
#### 2.1.2. Git 初始配置
在使用 Git 前需要配置好用户名和电子邮件信息Git 需要使用这些配置信息用于提交代码。在 Git 命令行下,可以使用如下命令设置用户名和电子邮件信息:
```bash
git config --global user.name "Your name"
git config --global user.email "Your email"
```
查看当前配置的用户名和电子邮件信息命令如下:
```bash
git config --global user.name
git config --global user.email
```
git 访问某些 https 连接会出现 SSL 认证错误,此时可通过全局配置关闭 SSL 认证:
```bash
git config --global http.sslVerify "false"
```
克隆比较大的项目时容易出现 RPC failed通过将 postBuffer 设置大些以解决此问题:
```bash
git config --global http.postBuffer 524288000
```
![Git 的配置](./img/Git_使用说明/config-git.gif)
#### 2.1.3. Git 与 SSH
如果想访问远程 Git 服务器,则最好通过 SSH 方式。这需要先生成 RSA 密钥,然后将公钥部署到远程服务器上即可。生成 RSA 密钥可使用 OpenSSH命令如下Windows 下该命令位于 /\<git 安装目录\>/usr/bin/ 下,可使用 Git Bash 直接访问):
```bash
ssh-keygen -t ecdsa
```
**注意:新版本 Windows10 及以上系统不再支持 RSA 算法,必须通过 ssh-keygen 的 -t 参数指定另外的算法,否则无法正常克隆/提交代码等。**
该命令将在用户目录下的 .ssh 目录下Windows 用户目录为C:\Users\\<UserName\>\, Linux /home/\<User\>生成两个密钥文件id_ecdsa id_ecdsa.pub其中 id_ecdsa 为私钥文件存储在本地计算机中以便使用id_ecdsa.pub 为公钥文件需要将其内容拷贝到 Git 服务器上使用
![生成 SSH 密钥](./img/Git_使用说明/make-ecdsa.gif)
TortoiseGit 需要额外修改 Settings->Network->SSH 为 C:\Windows\System32\OpenSSH\ssh.exe 才可以通过 OpenSSH 密钥来访问服务器。否则默认使用 PuTTY 格式密钥。
![配置 TortoiseGit 的 SSH 工具](./img/Git_使用说明/config-tortoisegit-ssh-tool.gif)
当出现类似以下错误时:
```bash
Clone: exit status 128 - fatal: unable to access 'xxxxxxxxx.git/': SSL certificate problem: certificate is not yet valid
```
可以通过关闭 http.sslVerify 来解决:
```bash
git config --global http.sslVerify false
```
#### 2.1.4. Git 代理设置
```bash
# 设置 Git 全局代理
git config --global http.proxy "socks5://<proxy addr>:<port>"
git config --global https.proxy "socks5://<proxy addr>:<port>"
# 取消全局代理
git config --global --unset http.proxy
git config --global --unset https.proxy
# 只针对谋网站进行代理
git config --global http.https://github.com.proxy "socks5://<proxy addr>:<port>"
# 取消代理谋网站的代理
git config --global --unset http.https://github.com.proxy
```
还有一种方式就是直接修改 .gitconfig 文件,一般在用户目录下:
```bash
# 全部使用代理
[http]
proxy = socks5://<proxy addr>:<port>
# 只针对谋网站进行代理
[http "https://github.com"]
proxy = socks5://<proxy addr>:<port>
```
### 2.2. TortoiseGit 的安装和配置
![TortoiseGit 的安装](./img/Git_使用说明/install-tortoisegit.gif)
![TortoiseGit 的配置](./img/Git_使用说明/config-tortoisegit.gif)
## 3. Git 的使用
本节基于 VSCode 对 Git 的使用进行讲解。TortoiseGit 的使用方法与之类似。
虽然 VSCode 本身已经支持了 Git但依然推荐安装两个插件Git History 和 GitLens。这两个插件使得 Git 功能更加易用,且使用频率很高。
![VSCode 的 Git 插件](./img/Git_使用说明/git-plugin-for-vscode.png)
### 3.1. 创建仓库
软件版本管理依赖于软件仓库的概念,一个软件仓库包容了要进行版本管理的源码和 Release 发布等内容。
在要进行版本管理的目录下键入:
```bash
git init
```
命令,可完成软件版本仓库的创建操作,在创建完软件仓库后,该目录下会生成一个名为“.git”的隐藏文件夹该文件夹下保存了软件的历史和分支等版本信息通常不需要直接操作该目录。
![创建 Git 仓库](./img/Git_使用说明/init-git-repo.gif)
在创建完 Git 仓库后,使用 Git History 插件提供的 Git History 命令,可以查看 Git 仓库的版本历史,此时内容为控,因为我们还没有提交过任何代码。
更多的情况下,我们使用 Git 服务器来创建 Git 仓库,然后克隆到本地计算机中,此时不再需要手动初始化。
![创建 Git 仓库](./img/Git_使用说明/init-git-repo-from-server.gif)
### 3.2. 添加跟踪文件
版本仓库所在文件夹下的文件并不是全部都会被 git 跟踪和管理。如果需要将该文件夹下将某个文件纳入到版本管理中,则需要通过 add 命令添加到 git 仓库中进行跟踪:
```bash
git add <file name>
```
例如:
```bash
git add main.c
```
也可以使用 VSCode 或者 TortoiseGit 来管理要跟踪的文件。
![添加跟踪文件](./img/Git_使用说明/add-file.gif)
### 3.3. 取消跟踪文件
如果对某个文件取消跟踪,但还需要保留本地文件,则使用如下命令:
```bash
git rm --cached <file name>
```
如果取消对某个文件的版本管理,并且要删除该文件,则使用如下命令:
```bash
git rm --f <file name>
```
也可以直接删除文件后将其提交:
![添加跟踪文件](./img/Git_使用说明/rm-file.gif)
### 3.4. Clone 仓库
当 Git 仓库很已经存在于远端服务器或别人的计算机中的时候,我们需要将其 clone 到本地才可以使用:
```bash
git clone <repository url>
```
例如:
```bash
git clone https://192.168.1.8/path/repo.git
```
Git Clone 支持使用 http、https、ssh 等协议:
![使用 SSH 协议进行 Clone](./img/Git_使用说明/git-clone-through-ssh.gif)
有的代码仓库比较大提交次数多Clone 仓库时不需要下载全部提交历史,此时可以使用 --depth 参数指定 Clone 深度,也就是 Shallow Clone 方式:
```bash
git clone --depth <n>
```
其中 n 是要 Clone 的深度,也就是从 HEAD 开始计算的提交次数,比如:
```bash
git clone --depth 3
```
此时 Clone 下来的仓库只包含最新的 3 次提交历史。
当使用 Shallow Clone 的仓库进行代码提交时可能会出现:
```bash
! [remote rejected] xxx -> xxx (shallow update not allowed)
```
此时需要 fetch 原仓库的全部内容,将 Shallow 转换为非 Shallow 仓库来解决问题:
```bash
# <repository url> 为原仓库地址
git fetch --unshallow <repository url>
```
### 3.5. 提交变更
在修改代码或相关文件后,需要先将变更缓存到本地,这一步被称作 Commit。带签名的提交命令如下
```bash
git add .
git commit -s -m <message>
```
如果想对上次提交进行修改,或将本次提交与上次提交合并,可为 Commit 增加 Amend 参数,如下:
```bash
git commit --amend
```
之后可通过交互界面进行编辑和修改。
每次 CommitGit 都会为本次提交创建一个带有唯一 Commit ID 的版本。如果只是在本地计算机中使用 Git 只要完成 Commit 操作就可以了。
![使用 SSH 协议进行 Clone](./img/Git_使用说明/git-commit-id.png)
### 3.6. 推送变更到服务器
如果使用了 Git 服务器,还可以将本次或者几次的 Commit 同步到服务器上:
```bash
git push
```
![推送变更到服务器](./img/Git_使用说明/git-push.gif)
### 3.7. 从服务器获取变更
当服务器端代码发生变化后,可以将变更同步到本地:
```bash
git pull
git pull --rebase
```
![从服务器获取变更](./img/Git_使用说明/git-pull.gif)
如果本地代码和服务器代码都发生了变更,要先将本地代码 Commit 然后再从服务器 Pull 代码下来。如果本地代码变更与服务器代码变更没有冲突则会自动合并。如果 Git 检测到了合并冲突,将需要解决冲突后合并,然后再提交。
![解决冲突后提交](./img/Git_使用说明/git-pull-rebase.gif)
VSCode 提供了一种 Sync 功能,可以从远程取回最新代码,合并并提交:
![VSCode Repo Sync](./img/Git_使用说明/vscode-repo-sync.gif)
### 3.8. 分支管理
版本分支是一个非常实用的功能,在很多种情况下需要使用分支功能。比如同一家族产品下的不同子代产品,他们拥有共通的 Base 代码,只有一些功能性的差异导致部分代码不同。比如我们研发了出了一款销量很好的汽车,于是想在其基础上退出一款高配车型,此时他们的基本设计和代码都是相同的,只是更换了高级的电子系统,真皮座椅,增加了天窗和副驾驶电动座椅。这是就要在现有版本基础上,为高配车型创建一个新的版本分支。
还有一种情况是基于开发需要,比如系统的滚动升级,就要保证有一个稳定的主分支用于版本发布。研发过程代码提交到 Develop 分支。当 Develop 分支开发成熟后,再将其合并到主分支中,从而避免主分支出现不稳定代码。
分支管理主要涉及:分支创建、分支切换、分支合并以及远程分支管理等功能。
#### 3.8.1. 基础命令
通过以下命令创建新的本地分支:
```bash
git branch <branch>
```
还可以通过以下命令查看分支情况:
```bash
git branch
git branch -a
```
使用下列命令切换到对应分支:
```bash
git checkout <branch> -b [branch name]
```
使用以下命令修改分支名称:
```bash
git branch -m <old name> <new name>
```
删除某本地分支的命令为:
```bash
git branch -d <branch>
```
![分支创建和切换](./img/Git_使用说明/git-branch.gif)
#### 3.8.2. 分支合并
将某个分支与当前分支合并的命令为:
```bash
git merge <from branch>
```
如果要 merge 的分支有多个 commit可以在 merge 时合并这些 commit,从而使提交变得简洁:
```bash
git merge --squash <from branch>
```
以下命令为本地分支指定一个与之对应的远程分支:
```bash
git branch --set-upstream-to=<repository>/<remote branch> <local branch>
```
也可以使用 rebase 进行分支合并,参见 [3.13. Rebase](#313-rebase) 部分。
![分支创建和切换](./img/Git_使用说明/git-branch-merge.gif)
#### 3.8.3. 远程分支管理
推送本地指定分支到远程指定分支,如果远程分支不存在则创建:
```bash
git push <repository> <local branch>:<remote branch>
```
如,推送本地的 new 分支与 \<repository\> 主机的 master 分支合并:
```bash
git push <repository> new:master
```
![推送本地分支到服务器](./img/Git_使用说明/git-branch-remote.gif)
从远程分支更新本地分支:
```bash
git pull <repository> <remote branch>:<local branch>
```
如,要取回 \<repository\> 主机的 new 分支,与本地的 master 分支合并:
```bash
git pull <repository> new:master
```
如果想获取远程仓库的最新代码,并且放弃本地的 commit可以使用 fetch 命令,该命令从远程下载最新的提交,而不尝试合并或 rebase 任何东西,操作如下:
```bash
git fetch --all
git reset --hard <repository>/<branch>
```
删除远程分支命令为:
```bash
git push <repository> --delete <branch name>
```
如果谋远程分支已经删除,需要同步到本地,可使用以下命令:
```bash
git remote prune <repository>
```
### 3.9. 查看历史
版本管理提供的最主要的功能之一便是历史追溯,可以查看每一次变更的内容,相关提交的信息等。通过下列命令实现:
```bash
# 显示提交日志概要
git log [path]
# 显示提交日志,包括变更的文件
git whatchanged [path]
# 可通过 git log 获取要查看的提交及其 commit id然后通过 git show 显示此次提交的详细信息
git show <commit id>
```
在 VSCode 环境下Git History 插件提供了很好的历史查看功能:
![查看版本历史](./img/Git_使用说明/git-history.gif)
### 3.10. 内容比较
使用
```bash
git diff <from> <to>
```
命令可比较任意两组内容,例如:
```bash
# 比较谋更改的文件/文件夹与当前分支上次提交内容的差异
git diff <file/folder>
# 比较两个分支 或 两个分支的谋文件/文件夹
git diff <branch 0> <branch 1> [file/folder]
# 比较当前分支下两次提交 或 两次提交的谋文件/文件夹
git diff <commit id 0> <commit id 1> [file/folder]
```
![分支比较](./img/Git_使用说明/git-compare-branch.gif)
### 3.11. Tag 管理
不难发现Git 会为开发过程中的每次提交时都创建一个版本管理节点。这样可以任意的调出这些节点信息,进行版本比较或代码回溯。
在实际开发过程中,有些节点会变得比其他节点更重要一些,比如在某个节点上我们发布了一个版本。这时候使用 Tag 功能对关键节点进行管理。
可以通过:
```bash
git tag
```
命令列出当前仓库中的所有标签,创建一个带附注的的标签则可以使用以下命令实现:
```bash
git tag -a <tag name> -m <message>
```
还可以使用:
```bash
git tag <tag name>
```
创建一个轻量标签。
使用 -d 参数删除标签:
```bash
git tag -d <tag name>
```
将本地 Tag 同步到远程的命令为:
```bash
git push --tags [-f]
```
将远程 Tag 同步到本地的命令为:
```bash
git pull --tags [-f]
```
如需将远程谋 Tag fetch 到本地,则命令为:
```bash
git fetch <repository> tag <tag name>
```
删除远程 Tag 的命令为:
```bash
git push <repository> --delete tag <tag name>
```
如果谋远程 Tag 已经删除,需要同步到本地,可使用以下命令:
```bash
git remote prune <repository>
```
![Tag 管理](./img/Git_使用说明/git-tag.gif)
### 3.12. 时光穿梭
在进行时光穿梭前,需要先了解下 Git 文件变更的几个状态变更Changed、缓存Stashed、提交Committed、上传Pushed
![文件变更的状态](./img/Git_使用说明/git-commit-state.gif)
如果文件尚未 Stash可使用 Checkout 命令撤回修改:
```bash
git checkout -- <file>
```
如果文件已 Stash 但是没有 Commit可使用 Reset 命令撤回修改:
```bash
git reset HEAD <file>
```
如果文件已经 Commit可使用下列命令
```bash
git reset --soft <commit id>
git reset --hard <commit id>
git reset --hard HEAD~10
git revert <commit id>
```
注意 Soft 与 Hard 的区别主要在于 Hard 不保留工作区中的内容,但是 Soft 保留工作区中的内容。而 Reset 与 Revert 的区别在于 Revert 是放弃指定提交的修改,但是会生成一次新的提交,需要填写提交注释,以前的历史记录都在,而 Reset 是指将 HEAD 指针指到指定提交,历史记录中不会出现放弃的提交记录。
![撤销变更](./img/Git_使用说明/git-reset.gif)
如果想要修改某个历史提交,可以使用 Checkout 命令:
```bash
git checkout <commit id/tag>
```
使用该命令的条件是没有待 Commit 或待 Stash 的文件。Checkout 命令会将检出的版本放到一个临时分支里,如果希望放到指定的分支中,需要添加 -b 参数:
```bash
git checkout <commit id/tag> -b <branch name>
```
![回溯代码](./img/Git_使用说明/git-checkout-commit.gif)
此时我们可以基于这个历史 Commit 和当前新分支上继续进行开发。
### 3.13. Rebase
由于合并分支,或从远程仓库中更新代码产生了额外的 merge 节点,此时可通过 rebase 命令消除这些额外的 merge 节点,使 git 历史的线性更好:
```bash
git rebase [-i] [upstream]
```
通过增加 -i 参数可进入交互模式。使用 Rebase 还可以将多次提交进行合并,或设置起止点。
```bash
git rebase [-i] <start commit id> <end commit id>
# 合并最后 n 次提交
git rebase [-i] HEAD~<n>
# 合并某个 commit id 以来的提交
git rebase [-i] <to commit id>
```
使用 -i 参数进入交互模式后,可以:
- pick保留该 commit缩写:p
- reword保留该 commit但我需要修改该 commit 的注释(缩写:r
- edit保留该 commit, 但我要停下来修改该提交(不仅仅修改注释)(缩写:e
- squash将该 commit 和前一个 commit 合并(缩写:s
- fixup将该 commit 和前一个 commit 合并,但我不要保留该提交的注释信息(缩写:f
- exec执行 shell 命令(缩写:x
- drop我要丢弃该 commit缩写:d
如果发生了冲突,可以在解决冲突并 add 后,通过:
```bash
git rebase --continue
```
也可以使用 rebase 直接从另一分支合并代码,从而避免像 merge 那样增加额外节点(相当于 merge 之后再进行一次 rebase 操作)
```bash
# 从 from branch rebase 合并到当前分支
git rebase <from branch>
# 从 from branch rebase 合并到 to branch
git rebase <from branch> <to branch>
```
命令继续 rebase。
如果在 rebase 中途需要出现异常需要停止 rebase 操作,可以使用:
```bash
git rebase --abort
```
命令来停止当前操作。
### 3.14. Remote 管理
```bash
# 查看远程 repository 设置情况
git remote -v
# 添加 repository
git remote add <repository> <utl>
# 删除 repository
git remote rm <repository>
# 修改 repository 的 url
git remote set-url <repository> <utl>
```
![回溯代码](./img/Git_使用说明/git-remote-v.png)
### 3.15. 同步 fork 的仓库
基本原理是增加上游仓库,然后进行 fetch 和 merge最后 push 到当前仓库,如果已经添加了上游仓库,无需再次添加。
```bash
git remote add upstream <upstream repository url>
git fetch upstream
git merge upstream/master
git push <repository>
```
### 3.16 Patch
#### 3.16.1. 生成 Patch
```bash
# 生成最近的 1 次 commit 的 patch
git format-patch HEAD^
# 生成最近的 3 次 commit 的 patch
git format-patch HEAD^^^
# 生成某 commit 以来的修改 patch不包含该 commit
git format-patch <commit id>
# 生成两个 commit 间的修改的 patch包含两个 commit. <commit id 1> 和 <commit id 2> 都是具体的 commit 号)
git format-patch <commit id 1>..<commit id 2>
# 生成单个 commit 的 patch
git format-patch -1 <commit id>
# 生成从根到 <commit id> 提交的所有 patch
git format-patch --root <commit id>
```
#### 3.16.2. 应用 Patch
将所有 Patch 拷贝到某一文件夹下,然后:
```bash
# 检查 patch 是否能够打上,如果没有任何输出,则说明无冲突,可以打上
git apply --check <patch dir>/*.patch
# 将谋文件夹下的全部 patch 都打上
git am <patch dir>/*.patch
# 当 git am 失败时,用以将已经在 am 过程中打上的 patch 废弃掉(比如有三个 patch打到第三个 patch 时有冲突,那么这条命令会把打上的前两个 patch 丢弃掉,返回没有打 patch 的状态)
git am --abort
# 当 git am 失败,解决完冲突后,这条命令会接着打 patch
git am --resolved
```
#### 3.16.3. 合并多个 Patch
```bash
# 先应用 patch
git am <patch>
# 然后合并提交
git rebase -i HEAD~n
# 生成新 patch
git format-patch HEAD^
```
### 3.17. Submodule
#### 3.17.1. 添加 Submodule
在主仓库中:
```bash
git clone <main-repository url>
cd <main-repository>
git submodule add <sub-repository url>
```
这时主仓库的状态会有变化,可通过 commit 和 push 提交上传:
```bash
git commit -sm <message>
git push
```
#### 3.17.2. 在其他地方使用合并后的版本库
```bash
git clone <main-repository url>
cd <main-repository>
git submodule init && git submodule update
# 或
git clone --recursive <main-repository url>
```
#### 3.17.3. 更新合并后的版本库
```bash
cd <sub-repository url>
git commit [...]
cd ..
git commit [...]
```
#### 3.17.4. 批量操作
```bash
git submodule foreach <command>
# 例如
git submodule foreach git checkout master
git submodule foreach git submodule update
```
#### 3.17.5. 如何保持 Submodule 的同步
如果对第三方 Submodule 进行了定制,但是处于某些原因这些修改并不能提交到远程去。那就要先 fork 第三方仓库,然后在 fork 的基础上添加 Submodule。之后自己的修改或第三方有更新都同步到 fork 的仓库上。
#### 3.17.6. 删除 Submodule
```bash
# deinit
git submodule deinit <path>
# remove
git rm <path>
```
#### 3.17.7. 修改 Submodule
```bash
# 修改 url
git submodule set-url <path> <newurl>
```
## 4. Git 高级配置
### 4.1. Git 环境变量
#### 4.1.1. GIT_TERMINAL_PROMPT
该变量设置是否允许 git 在终端中弹出提示,如 https 的用户密码输入提示等。如果 git clone 时出现“terminal prompts disabled”字样可在终端中进行如下设置
```bash
GIT_TERMINAL_PROMPT=1
```
### 4.2. 清理本地仓库
由于编译本地仓库等原因生成了许多临时文件,想要清除这些多余的文件可以使用 git clean 命令:
```bash
# i: 交互模式
# n: 显示将要被删除的文件
# d: 删除未被添加到 git 路径中的文件(将 .gitignore 文件标记的文件全部删除)
# f: 强制运行
# x: 删除没有被 track 的文件
git clean -ifdx
```
## 5. Git 服务器
本文推荐使用 Gitea 来搭建私有 Git 服务器,这是一个基于 Web 的开源 Git 服务器程序,比 Gerrit 更加易用,且比 GitLab 更新更快,具有更丰富的功能。
### 5.1. Gitea 的下载和安装
可以到 [Gitea 官网](https://about.gitea.com/products/gitea) 下载 Gitea 的最新版本Gitea 支持 Windows、Linux 和 Mac 系统以及 x86 和 ARM 架构。
![Gitea 的下载](img/Git_使用说明/gitea-official.png)
在 Windows 系统下运行 Gitea 非常容易只需要双击运行即可Gitea 也支持以服务的形式运行。初次运行时需要对 Gitea 服务器进行简单的配置,包括数据库系统和管理员账户。推荐使用 MySQL 类数据库系统,本文以 SQLite 为例进行讲解,因为 SQLite 的配置比较简单。
![Gitea 的安装和初始化](img/Git_使用说明/install-init-gitea.gif)
Gitea 可以将网站设置成必须登录才能探索版本仓库或组织。
### 5.2. 超级管理员
管理员可以添加或删除用户,创建组织和项目,拥有最大的操作权限。
### 5.3. 普通用户
普通用于拥有的权限由管理员进行设置,可以允许或禁止用户创建组织或项目,或者指定其能够创建项目的数量。
普通用户是 Gitea 的主要使用者。
### 5.4. 组织和团队
Gitea 支持创建组织,组织是由项目团队共通维护的多个版本仓库的集合。可以对组织的访问权限进行控制,比如设定组织对非组织成员的可见性。
一个组织下可以创建多个团队,使用团队来维护组织成员的好处是可以为每组成员设定不同的读写权限,或指定其所能访问的仓库。
### 5.5. 版本仓库及分支保护
无论是独立的版本仓库还是组织所拥有的版本仓库,都可以被设定为公开或者私有。公开的仓库可以被所有人看见,而私有的仓库只能被组织成员或特殊指定的项目参与者所见。
仓库的所有者还可以设定其他人是否可以直接 Commit 到谋代码分支,还是必须经过评审才能被合并进来,该功能由分支保护机制实现。
当仓库的参与者需要将代码合并到谋分支时,可以发起合并请求,当评审通过后,请求将被合并。
### 5.10. 工单系统
当某个问题需要被解决是,可以在工单系统发起一个请求,项目的参与者可以对工单进行讨论,其是否需要被实现以及如何实现。当项目更新解决了该问题后,可以将该工单关闭。
### 5.11. Gitea 综合实践
![Gitea 综合实践](img/Git_使用说明/gitea.gif)
## 6. 外部参考资料
1. [Pro Git](https://git-scm.com/book/zh/v2)
2. [Git Reference Manual](https://git-scm.com/docs)
3. [更优雅的 git 合并方式](https://www.yisu.com/zixun/94736.html)