封面来源:本文封面来源于 Git 官网,如有侵权,请联系删除。

学习链接:Git教程 - 廖雪峰

A successful Git branching model

1. 版本控制

版本控制含义

版本控制(Revision Control)是一种在开发过程中对文件、目录或工程等内容的修改历史进行管理的软件工程技术,使用版本控制后可以通过查看历史修改记录,更方便地恢复到先前的版本。

版本控制可以:

  • 实现跨区域多人协同开发
  • 追踪和记载一个或者多个文件的历史记录
  • 组织和保护你的源代码和文档
  • 统计工作量
  • 并行开发、提高开发效率
  • 跟踪记录整个软件的开发过程
  • 减轻开发人员的负担,节省时间,同时降低人为错误

简单来说就是 用于管理多人协同开发项目的技术

版本控制工具

主流的版本控制软件有:

  • Git
  • SVN(Subversion)
  • CVS(Concurrent Versions System)
  • VSS(Micorosoft Visual SourceSafe)
  • TFS(Team Foundation Server)
  • Visual Studio Online

使用最广泛的是 GitSVN

版本控制分类

1、本地版本控制系统

记录文件每次的更新,对每个版本做一个快照,或是记录补丁文件,适合个人用,如 RCS。它是在一台机器上,记录版本的不同变化,保证内容不会丢失。但在多人开发的情况下,每个人都在不同的系统和电脑上开发,没办法协同工作。

2、集中版本控制系统

所有的版本数据都保存在中央服务器上,协同开发者从服务器上同步更新或上传自己的修改,用户只有自己以前所同步的版本,如果不连网,用户就看不到历史版本,也无法切换版本验证,或在不同分支工作。所有数据都保存在单一的服务器上,当服务器损坏时,可能会丢失所有数据,代表产品有 SVN、CVS、VSS 等。

集中式版本控制

3、分布式版本控制系统

需要一台服务器作为远程代码仓库,所有版本信息同步到每个用户的本地。用户可以本地离线提交,只需在联网时将修改推送到远程代码仓库即可。每个用户的电脑都是代码仓库,是远程代码仓库的镜像,用户修改和获取代码都是在自己的本地仓库中进行操作。

由于每个用户都保存了所有的版本数据,只要有一个用户的设备没有问题就可以恢复所有的数据,不会因为服务器损坏或者网络问题,出现不能工作的情况。

Git 与 SVN 的区别

  • SVN 是集中化的版本控制系统,必须联网使用,Git 是分布式版本控制系统,支持离线操作;

  • SVN 是按照原始文件存储的,体积较大,Git 是按照元数据方式存储的,体积很小;

  • SVN 的分支操作成本较大,会影响到其他开发人员,Git 的分支操作成本更小,不会影响到其他人;

  • SVN 常用于维护文档,Git 常用于维护代码。

2. Git 的历史

2002 年,Linux 内核开源社区开始启用分布式版本控制系统 BitKeeper 来管理和维护代码。

2005 年,开发 BitKeeper 的 BitMover 公司结束与 Linux 内核开源社区的合作关系,并收回 Linux 内核开源社区的免费使用权。而后,Linux 的缔造者 Linus Torvalds 使用两周开发出自己的版本系统,也就是如今的 Git!

李纳斯·托沃兹

Linux 和 Git 之父李纳斯·托沃兹(Linus Benedic Torvalds)1969、芬兰

3. Git 环境配置

3.1 安装与卸载 Git

打开 git官网,下载 git 对应操作系统的版本。

如果官网下载太慢,可以使用淘宝镜像下载:Git镜像下载

Git官网

进入镜像后,拖至最下方,进入稳定版的下载,然后根据自己操作系统的情况下载并进行安装。

Git镜像下载_1 Git镜像下载_2

安装时使用“傻瓜式”安装,无脑下一步即可。PS:注意按自己需求修改安装路径!😏

安装时还可以修改默认编辑器,默认是 Vim 编辑器。比如将默认编辑器修改为 NotePad3,找到 NotePad3 的 exe 文件并选择即可。

如果在安装完成后想要修改默认编辑器,可以使用命令:

1
2
3
4
# 将默认编辑器修改为 vim
git config --global core.editor vim
# 修改为 NotePad3(安装路径自行修改)
git config --global core.editor "D:\\Notepad3\\Notepad3.exe"

Git 的卸载

使用国内各种管家软件或打开控制面板进行卸载即可,最后记得删除与 Git 相关的、在 path 下的环境变量!

启动Git

安装成功后在开始菜单中会有如下信息:

开始菜单Git项

Git Bash: Unix 与 Linux 风格的命令行(使用最多,推荐使用)

Git CMD: Windows 风格的命令行

Git GUI: 图形界面的 Git,不建议初学者使用,尽量先熟悉常用命令

可以在某一目录中点击右键,点击“Git Bash Here”就可以再当前目录下打开 Git Bash。

3.2 Linux 常用命令

在 Git 的命令行界面也可以使用 Linux 的操作命令,常用的有以下命令:

1、cd:切换目录

2、cd . . :回退到上一个目录,直接 cd 进入默认目录

3、pwd:显示当前所在的目录路径

4、ls(ll):都是列出当前目录中的所有文件,只不过 ll(两个 ll)列出的内容更为详细

5、touch:新建一个文件。如 touch index.js 就会在当前目录下新建一个 index.js 文件

6、rm:删除一个文件。如 rm index.js 就会把 index.js 文件删除

7、mkdir:新建一个目录,就是新建一个文件夹

8、rm -r:删除一个文件夹,rm -r src 删除 src 目录

9、mv:移动文件

10、reset:重新初始化终端/清屏

11、clear:清屏

12、history:查看历史命令执行情况

13、help:帮助

14、exit:退出

3.3 Git 的配置

Git 的所有配置信息都保存在本地。

可以使用命令 git config -l 查看全部配置信息:

Git配置信息

还可以使用以下命令,查看不同级别的配置信息:

1
2
3
4
5
# 查看系统 config
git config --system --list
  
# 查看当前用户(global)配置
git config --global --list

Git 相关的本地配置文件位置:

  • Git 安装目录 \etc\gitconfig:Git 安装目录下的 gitconfig --system 系统级配置文件

  • C:\Users\你的用户名\ .gitconfig:只适用于当前登录用户的配置 --global 全局配置文件

当前用户配置文件信息

可以直接在这里编辑配置文件,通过命令设置后也会响应到这里。

设置用户名与邮箱 这很重要! 这很重要! 这很重要!

安装完 Git 后首先要做的事情是设置用户名和 email 地址。这 非常重要,因为每次 Git 提交都会使用该信息,它被永远的嵌入到了提交中。所以!必须进行设置!! 这很重要 * 3

如果刚安装 Git 后没有设置用户名和邮箱,执行 git config --global --list 就会出现下图中的第一种情况,此时需要按照第二个与第三个类似命令来设置用户名🏃和邮箱📧(我在此处设置的是我自己的用户名 mofan 与我的邮箱,你需要设置属于你自己的用户名和邮箱)。

配置文件编写

如果使用了 --global 选项,表示设置了全局的用户名和邮箱。如果希望在一个特定的项目中使用不同的用户名和邮箱,可以在该项目中运行该命令而不添加 --global 选项。总之 --global 为全局配置,不加为某个项目的特定配置。

设置好用户名和邮箱后,再运行 git config --global --list,就会显示设置的用户名和邮箱了,也可以在 C:\Users\你的用户名\.gitconfig 文件中看到设置的信息。

4. Git 基本理论

三大区域

Git 本地有三个工作区域:工作目录(Working Directory)、暂存区(Stage 或 Index)、资源库(历史记录区、版本库、本地仓库、History、Repository 或 Git Directory)。如果在加上远程的 Git 仓库(Remote Directory)也可以分为四个工作区域。

文件在这四个区域之间的转换关系如下:

Git三大区域

  • Working Directory:工作区,平时存放项目代码的地方。
  • Stage(Index):暂存区,用于临时存放改动,事实上它只是一个文件,保存即将提交到文件列表信息。
  • History:历史记录区,安全存放数据的位置,里面有项目所有版本的数据。
  • Remote Directory:远程仓库,托管代码的服务器,可以简单的认为是项目组中用于远程数据交换的一台电脑。

三大区域的另一种图示

本地的三个区域确切的说应该是 Git 仓库中 HEAD 指向的版本:

图解三大区域
  • Directory:使用 Git 管理的一个目录,也就是一个仓库,包含我们的工作空间和 Git 的管理空间。
  • WorkSpace:需要通过 Git 进行版本控制的目录和文件,这些目录和文件组成了工作空间。
  • .git:存放 Git 管理信息的目录,初始化仓库的时候自动创建。(一个隐藏文件夹
  • Index/Stage:暂存区,或者叫待提交更新区,在提交进入 repo 之前,可以把所有的更新放在暂存区。
  • Local Repo:本地仓库,一个存放在本地的版本库;HEAD 会只是当前的开发分支(branch)。
  • Stash:隐藏,是一个工作状态保存栈,用于保存/恢复 WorkSpace 中的临时状态。

工作流程

1、在工作目录中添加、修改文件;

2、将需要进行版本管理的文件放入暂存区域;

3、将暂存区域的文件提交到本地仓库。

Git工作流程

5. Git 项目搭建

什么是工作目录?

工作目录(WorkSpace)一般就是希望 Git 进行管理的文件夹,可以是项目的目录,也可以是一个空目录。

建议要求不能有中文。

创建全新的仓库

创建本地仓库的方法有两种:一种是创建全新的仓库,另一种是克隆远程仓库。

1、在项目的根目录执行以下命令创建全新的仓库:

1
2
# 在当前目录新建一个 Git 代码库
git init

2、执行后在当前目录下新增了一个 .git 隐藏目录,当前项目的版本信息和配置信息都会存放在这个目录里。

克隆远程仓库

可以使用以下命令将某个项目克隆(下载)到本地:

1
2
# 克隆一个项目和它的整个代码历史(版本信息)
git clone [url]

在 GitHub 上选定一个项目进行测试:

复制仓库URL

Git之clone远程仓库

删除远程仓库

GitHub 中的操作:

进入 GitHub 中需要删除的仓库,点击“Settings”,下拉网页至最后“Danger Zone”,选中并点击“Delete this Repository”,然后根据界面提示进行操作即可。

Gitee 中的操作:

进入 Gitee 中需要删除的仓库,点击“管理” — 仓库设置 — 删除仓库,根据界面提示进行操作即可。

6. Git 文件操作

文件的四种状态

版本控制就是对文件的版本控制,要对文件进行修改、提交等操作,首先要知道文件当前是什么状态。

1、Untracked:未跟踪,此文件在文件夹中,但没有加入到 Git 库,不参与版本控制。可以通过 git add 状态变为 Staged。

2、Unmodify:文件已经入库,且未修改,即版本库中的文件快照内容与文件夹中完全一致。这种类型的文件有两种去处,如果它被修改,变为 Modified;如果使用 git rm 移出版本库,则变为 Untracked。

3、Modified:文件已修改, 仅仅是修改,并没有进行其他的操作。这个文件也有两个去处,通过 git add 变为暂存 Staged 状态;使用 git checkout 丢弃修改, 返回到 Unmodify 状态。git checkout 表示从库中取出文件,覆盖当前修改,即丢弃当前修改。

4、Staged:暂存状态。通过 git commit 将修改提交到本地仓库, 文件变为 Unmodify 状态。执行 git reset HEAD filename 取消暂存, 变为 Modified 状态。

查看文件状态

可以通过以下命令查看到文件的状态:

1
2
3
4
5
# 查看指定文件状态
git status [filename]

# 查看所有文件状态
git status

add 与 commit 命令

1
2
3
4
5
6
7
8
# 添加所有文件到暂存区
git add .

# 提交暂存区中的内容到本地仓库 -m 提交信息
git commit -m "消息内容"

# 将跟踪的文件直接添加到本地仓库(如果有新增文件,还是得先执行 git add .)
git commit -am "消息内容"

忽略文件

有时不想把某些文件纳入版本控制中,比如数据库文件,临时文件,设计文件等,要求忽略这些文件的修改。

在根目录下建立 .gitignore 文件,将需要被忽略的文件名添加到此文件中。

.gitignore 文件内容解析:

1、以井号 # 开始的行表示注释;

2、可以使用 Linux 通配符。例如:星号 * 代表任意多个字符,问号 ? 代表一个字符,方括号 [] 代表可选字符范围,大括号 {} 代表可选的字符串等;

3、如果名称的最前面有一个感叹号 !,表示例外规则,满足条件的文件不会被忽略;

4、如果名称的最前面是一个路径分隔符 /,表示要忽略的文件在根目录下;

5、如果名称的最后面是一个路径分隔符 /,表示要忽略的是此目录下的文件。

1
2
3
4
5
*.txt        # 忽略所有 .txt 结尾的文件
!lib.txt # lib.txt 除外
/temp # 忽略项目根目录下的 temp 文件
build/ # 忽略 build 目录下的所有文件
doc/*.txt # 忽略 doc 目录下的所有 txt 文件,不包括子目录下

IDEA 中常用的 .gitignore 文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Compiled class file
*.class

# Log file
*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs
hs_err_pid*
.classpath
.project
.settings
target
.idea
*.iml

7. GitHub 的使用

可以使用 GitHub 作为代码托管平台,尽管 GitHub 的访问速度不佳,但也不选择 Gitee,而在公司中,还可能使用搭建的的 GitLab 服务器。

GitHub

  • 首先需要注册 GitHub;

  • 为了方便使用,强烈建议 设置本机绑定 SSH 公钥(gōng yuè),实现免密码登录;

冷知识:公钥、私钥、密钥中,钥都读 yuè。😱

1
2
3
4
5
# 进入 C:\Users(用户)\你的用户名 目录
# 如果你是第一次设置,在这个目录下看不到.ssh目录
# 在上述目录下右键打开“Git Bash Here”,运行以下命令并生成公钥
ssh-keygen -t rsa -C 邮箱或其他描述性文字
# rsa是加密方式
Git秘钥生成

对输入的命令以及执行命令后需要输出的情况进行解析

参考链接:SSH-keygen用法

1
ssh-keygen -t rsa -C "your_email@example.com"

以上代码省略了 -f 参数,因此运行上面那条命令后会要求输入一个文件名,用于保存刚才生成的 SSH key 代码,如:

1
2
Generating public/private rsa key pair.
# Enter file in which to save the key (/c/Users/you/.ssh/id_rsa): [Press enter]

也可以不输入文件名,使用默认文件名(推荐),那么就会生成 id_rsa 和 id_rsa.pub 两个秘钥文件。

接着又会提示输入两次密码(该密码是 push 文件的时候要输入的密码,而不是 GitHub 管理者的密码),也可以不输入密码,直接按回车。那么 push 的时候就不需要输入密码,直接提交到 GitHub 上了,如:(我在此直接回车,不输入密码)

1
2
Enter passphrase (empty for no passphrase): 
# Enter same passphrase again:

接下来将会显示:

1
2
3
4
Your identification has been saved in /c/Users/you/.ssh/id_rsa.
# Your public key has been saved in /c/Users/you/.ssh/id_rsa.pub.
# The key fingerprint is:
# 01:0f:f4:3b:ca:85:d6:17:a1:7d:f0:68:9d:f0:a2:db your_email@example.com

当看到上面这些信息时就说明 SSH key 已经创建成功,接下来将其添加到 GitHub 的 SSH key 上就可以了。

  • 打开 GitHub 并进行登录,点击右上角头像,选择 Settings,进入设置页面后,点击 Access 下的 SSH and GPG keys,之后点击 New SSH key;

  • 打开目录 C:\Users(用户)\你的用户名\.ssh,将 id_rsa.pub 文件内的内容复制到公钥填写处,点击“确定”,输入 GitHub 的密码即可添加成功!

公钥复制

PS:id_rsa 存放的是私钥,id_rsa.pub 存放的是公钥,需要复制的是公钥 id_rsa.pub 的内容。

  • 运行 Git Bash,输入命令 ssh -T git@github.com(如果使用 Gitee,输入 ssh -T git@gitee.com),然后输入 yes。

  • 到此,完成 SSH 免密登录的配置。

  • 如果需要新建仓库,点击主界面右上方头像旁的“+”然后选择“New repository”即可:

GitHub创建仓库

8. IDEA 中集成 Git

以下内容适用于 IDEA 2019.3 版本,其他版本仅供参考。

IDEA 配置 Git

首先需要在 IDEA 中配置 Git,打开 IDEA,settings — Version Control — Git — Path to Git executable,在此处配置 Git 安装目录下的 exe 文件。例如:D:\environment\Git\cmd\git.exe,其中 D:\environment\Git 是安装目录。(最新版不需要进行配置,IDEA 已经帮我们配置好,以防万一可以查看一下 😁)

然后可以点击“test”,查看配置是否正确。

还可以选择 settings — Version Control — GitHub,添加 GitHub 账号到 IDEA 中。

IDEA 项目绑定 Git

1、新建项目,绑定 Git。

在此给出两种绑定方式(推荐第二种):

  • 在克隆下来的空项目中使用 IDEA 创建项目。
  • 使用 IDEA 新建一个项目,将 clone 下来的项目中的全部内容(包括隐藏文件夹 .git)复制到新建的项目中(提示同名文件时,替换即可)。

为什么这么做?因为 clone 的远程仓库文件中的 .git 文件夹已经绑定了远程仓库,需要新建的项目中也有 .git 文件夹。

IDEA 绑定 Git 后可以在 IDEA 观察到 文件颜色的变化

  • 巧克力色 —— 文件尚未添加至暂存区
  • 橄榄色 —— 被忽略而不会被提交的文件
  • 绿色 —— 文件已添加至暂存区
  • 黑色 —— 文件已提交至本地库
  • 蓝色 —— 当前文件被修改,但尚未提交至本地库

2、编写代码,修改文件,使用 IDEA 操作 Git。

可以使用右上角 IDEA 集成的 Git 按钮进行操作,也可以在 IDEA 中的 Terminal 执行 Git 命令完成修改的提交。

在此推荐使用 IDEA 2020.3 及以上的版本,这些版本中的 Git 操作得到了优化。

忽略文件创建

不希望将 IDEA 的配置文件(.idea)push 到远程仓库,在 .gitignore 文件中添加 .idea/ 即可。如果当前项目中没有 .gitignore 文件,就需要创建 .gitignore 文件。

在 IDEA 中可以右键点击项目,Git — add to .gitignore — 选择 Add to .gitignore 或者 .git/info/exclude。如果选择 Add to .gitignore,后续还需要提交 .gitignore 文件;如果选择 .git/info/exclude,就不用提交 .gitignore 文件,因此推荐使用第二种方式。两种忽略文件的书写方式是一样的。

IDEA Git 切换版本

图形化界面操作:

1、右击项目 — Git — Show History(或者直接点击 IDEA 工具栏集成的 Git 操作图标,这个图标像一个钟表)

2、在 IDEA 下方的 Version Control 窗口中选中需要切换的版本,右键点击 — Copy Revision Number (建议将当前版本和想切换的版本的 commit id 都进行复制)

3、右击项目 — Git — Repository — Reset HAND… ,Reset Type 选择 Hard,To commit 粘贴你想切换版本的 commit id,最后点击 Reset 即可。如果从版本 3 切换到版本 1 后,还想回到版本 1,这个时候就只能用命令行的方式了。这也是为什么建议在复制版本号时复制当前和切换的两个版本号。

命令行操作:

1、前两步不变。

2、打开 IDEA 的 Terminal,输入以下命令:

1
git reset --hard xxxxxxx #最后的xxx是复制的版本号

3、如果需要把修改 push 到远程仓库,那么可以输入以下命令:

1
2
# 强制推送,有风险
git push -f -u origin master

IDEA Git 克隆远程仓库

1、进入 GitHub 复制项目地址

2、进入 IDEA,菜单栏中点击 VCS — Git — Clone…

3、输入复制的 URL,Directory 必须选择一个空的文件夹,等待 Clone 完成…

4、Clone 完毕后,IDEA 会提示是否创建一个项目,根据需要选择

5、然后 IDEA 又会提示在当前窗口还是新窗口打开,根据需要选择

6、完成后,需要对工程进行设置。菜单栏点击 File — Project Settings,在 Project 中选择 JDK 版本,在 Modules 选择 Clone 的项目。

7、在 Clone 的项目中进行编码,push 到远程仓库时可能需要设置文件忽略。文件忽略方式点击查看:忽略文件创建

Clone 远程仓库文件建议使用这种方式:项目绑定Git

9. Git 分支

切换分支的本质就是移动 HEAD 指针。

为什么要有分支?在 Git 中一定会有一个 master 主分支,也可以创建其他名字的分支。一般将 master 分支作为产品正式上线的分支,除此之外可以创建一个 dev 分支作为开发分支,平时编写的代码都提交到 dev 分支中,等产品上线时合并分支就可以了。如果不使用 dev 分支,平时编写的代码都提交到 master 主分支,万一新编写的代码出现了 BUG,产品又等着上线,那么上线的时间就只能延期了,而且也不方便进行排查修改。

Git项目分支

master 主分支应该非常稳定,用来发布新版本,不允许直接在 master 上进行修改。

一个项目通常有多个开发者向一个远程仓库提交代码,为了保证 master 分支的稳定性,一般每人都拉取一个个人分支并分别在自己的个人分支上进行修改,工作完成后,将个人分支合并到 dev 分支上(对应 dev 环境,用于自测),构建代码,进行简单的自测。自测没问题后,将个人分支合并到 master 分支上(对应 test 环境,用于测试),构建代码,交由测试人员进行测试。

当然在实际开发过程中肯定不止这两个分支的,一般还有 test(测试分支)、feature(特性分支)、release(预生产分支)等等。

分支之间的关系类似各个平行宇宙,如果两个平行宇宙互不干扰,那没啥影响,但如果平行宇宙之间相互“入侵”就可能产生一些问题,相当于 Git 中分支的合并。

分支的运用

多个分支如果并行存在,相当于同时存在多个版本,不会导致代码冲突。

如果同一个文件中的同一行在不同的分支都进行了修改,在合并分支时就会产生 冲突,要想解决冲突,需要手动调整要保留的内容。

Git 中与分支相关的常用指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 列出所有本地分支。-a 表示列出所有分支,包括远程分支
git branch [-a]

# 列出所有远程分支
git branch -r

# 新建一个分支,但依然停留在当前分支
git branch [branch-name]

# 切换到某个分支
git checkout [branch-name]

# 新建一个分支,并切换到该分支
git checkout -b [branch]

# 合并指定分支到当前分支
git merge [branch]

# 删除本地分支
git branch -d [branch-name]

# 删除未合并的本地分支
git branch -D [branch-name]

# 删除远程分支
git push origin --delete [branch-name]

# 删除远程分支(相当于使用一个空分支来覆盖原来的分支)
git push origin :branch-name

# 查看本地分支和追踪情况
git remote show origin

# 删除对分支的远程跟踪,但这个分支仍然在远程
git branch -dr [remote/branch]

# 更新本地 git 分支与远程分支同步(保留本地存在的分支)
git remote update origin --prune

# 删除本地多余分支(即远程已经不存在的分支)
git remote prune origin

IDEA 图形化界面分支操作

创建分支:

右击项目 — Git — Repository — Branches — New Branch

如何判断当前在哪个分支:

右击项目 — Git — Repository — Branches ,Local Branches 下最上方的就是当前分支。

切换分支:

右击项目 — Git — Repository — Branches ,选中需要切换的分支,单击 Checkout。

合并分支:

右击项目 — Git — Repository — Branches — Merge Changes…

push 到远程仓库:

右击项目 — Git — Repository — push

IDEA Git 更新本地库

如果有人在我们 push 代码之前已经将他的代码 push 到远程仓库了,这时再 push 代码到远程仓库时,要保证本地库是最新的。

如何更新本地库?依然可以直接 push 代码到远程仓库,但这时 IDEA 会弹出“Push Rejected”的对话框,点击“Merge”或“Rebase”即可更新本地仓库,建议选择 Merge。

建议每次提交之前先 pull 拉取最新的代码。

pull 命令行:Git命令之Pull从库拉取

10. 团队协作

10.1 团队内协作

Git团队内协作

假设人员 A 与人员 B 处于同一团队中,如果在同一家公司对同一个项目进行开发的两个人员。

首次由人员 A 初始化仓库,并 push 一系列代码,然后由人员 B clone 项目到本地并开发新的特性,开发完毕后提交 push 到远程仓库,这次人员 A 就可以拉取 pull 远程库的代码看到人员 B 的提交内容。

10.2 跨团队协作

Git跨团队协作

假设人员 A 与人员 B 处于同一团队中,而人员 C 并不处于这一团队,就像我们首次为开源项目提交代码一样。

从人员 C 的角度出发:对源远程库进行 fork,拉取 fork 的远程库,在本地进行修改后提交到远程库,然后可以发送 pull request,等待审核成功后即可合并到真正的远程库。

11. 其他常用命令

11.1 其他常用命令一览

命令 作用
git reflog 查看版本信息
git log 查看版本详细信息
git reset --hard 版本号(7 位) 版本穿梭
git reset --hard HEAD~3 回退到前 3 次提交之前,以此类推,回退到前 n 次提交前
git reset --hard HEAD^ 穿梭到上一个版本
git add 文件 将某一文件添加到暂存区
git rm --cached 文件 将某一文件移除暂存区
git tag 列出所有本地标签
git tag -l 通配模式文本(*) 根据符合通配模式文本,列出所有本地标签
git tag 标签名 为最新提交创建轻量标签
git tag 标签名 版本号(7位) 为对应版本号提交创建轻量标签(在后期打标签)
git tag -a 标签名 -m 备注文本 为最新提交创建附注标签
git tag -d 标签名 删除指定标签
git remote add 别名 远程仓库地址 添加远程库
git remote -v 查看添加过的远程库
git push 远程库地址或其别名 分支名 推送到远程库
git push 远程库地址或其别名 --tags 推送所有标签到远程库
git fetch 将远程库的最新内容拉到本地
git pull 远程库地址或其别名 分支名 将远程仓库某一分支最新内容拉取并合并到当前本地分支
git help 某一命令或指南 使用默认浏览器打开某一命令或指南的手册页
git show [commit id] 查看某次 commit 的具体内容

11.2 log 与 reflog 区别

git log 可以显示所有提交过的版本信息。可以加上参数 --pretty=oneline,只会显示版本号和提交时的备注信息。

git reflog 可以查看所有分支的所有操作记录(包括已经被删除的 commit 记录和 reset 的操作)。例如,执行 git reset --hard HEAD~1,退回到上一个版本,用 git log 是看不出来被删除的 commitid,用 git reflog 则可以看到被删除的 commitid。我们就可以买后悔药,恢复到被删除的那个版本。

提交记录过多时,按下 q 可结束浏览。

log 常见用法

命令 作用
git log --oneline 查看变更列表信息
git log -n2 --oneline 查看最近 2 次变更信息
git log 查看当前分支版本信息
git log --all 查看全部分支变更历史
git log --graph 以图谱的方式显示当前分支版本信息

11.3 git stash

git stash 也是工作中常用的命令,在此单独列举出来。

应用场景

1、假设你现在正在 dev 分支上开发新功能,突然项目出现一个紧急 BUG,需要你立即修复,由于新功能尚未开发完成,你并不想提交它,此时就可以使用 git stash 命令将修改的内容保存至堆栈区。保存完毕后,基于 master 分支新建 hot-fix 分支,对 BUG 进行修改。

2、假设你本应该在 dev 分支上开发新功能,在功能开发完毕时正想提交代码,不料却在 master 分支上进行了开发,为防止切换分支时出现代码冲突,可以使用 git stash 命令将修改内容保存至堆栈区后再切换分支。

命令解析

1、git stash:将所有未提交的修改(工作区和暂存区)保存至堆栈中,用于后续恢复当前工作目录。新增但未提交至暂存区的文件直接执行该命令是不会被保存到堆栈中的。如果想让新增的文件也在执行该命令后保存至堆栈区,需要将该文件交由 Git 管理,即使用 git add 文件 将新增的文件添加至暂存区再执行该命令。

2、git stash save "message":与 git stash 命令效果一样,只不过可以添加一些备注方便查阅。

3、git stash list:查看当前堆栈区所有内容。

4、git stash pop:将堆栈中第一个 stash 应用到当前工作目录下,并在堆栈中删除对应的 stash(也就是会应用并删除堆栈中最近保存的内容)。如果要应用并删除其他 stash,可以使用 git stash pop stash@{$num},即 git stash pop stash@{1} 应用并删除第二个 stash。

5、git stash apply:与 git stash pop 命令效果类似,只不过不会删除对应的 stash,进而将同一个 stash 应用到不同的分支上。

6、git stash drop stash@{$num}:删除堆栈上某一个 stash,比如 git stash drop stash@{1} 将会删除堆栈中第二个 satsh。

7、git stash clear:清空堆栈上所有内容。

8、git stash show:查看堆栈中第一个(最新保存的)stash 和当前目录的差异,可以使用 git stash show stash@{$num} 查看指定的 stash 和当前目录差异。还可以添加参数 -p 查看详细的差异,比如 git stash show -p 可以查看堆栈中第一个 stash 和当前目录的详细差异。

如何只保存部分内容到堆栈

git stash 会将所有未提交的修改(工作区和暂存区)保存至堆栈中,那么如果只保存部分内容呢?

1、将不想存储的文件保存至暂存区,比如 git add a.java, b.java

2、使用 git stash –keep-index 保存那些未被提交至暂存区的文件到堆栈;

3、使用 git reset 将暂存处的文件移动到工作区。

11.4 cherry-pick

应用场景

1、假设本应该在 mofan-master 分支上开发代码并提交,结果在 dev 分支上将代码提交了,此时可以使用 git cherry-pick 将 dev 分支上提交的代码同步到 mofan-master 上,以便自测完成后将 mofan-master 分支合并到 master 分支上。

2、使用 git merge 可以将某一分支的所有变动合并到当前分支上,如果只需要合并部分变动,则可以使用 git cherry-pick

命令解析

1、git cherry-pick 版本号:将指定的提交应用到当前分支,并在当前分支产生一个新的提交(与指定的提交不同的 commitHash)。比如我现在处于 dev 分支上,想将 dev 分支上某次提交应用到 mofan-master 分支上,那么需要先切换至 mofan-master 分支,然后使用 git cherry-pick 并使用那次提交的版本号进行应用。

2、git cherry-pick A版本号 B版本号:将 A 和 B 两次提交应用到当前分支上。

3、git cherry-pick A版本号..B版本号:将 A 到 B 之间的所有提交应用到当前分支上(左开右闭,就是说 A 不会被应用),同时提交 A 必须早于提交 B,否则命令将失败,但不会报错。

4、git cherry-pick A版本号^..B版本号:将 A 到 B 之间的所有提交应用到当前分支上,A 也会被应用。

常用参数

参数 含义
-e 打开外部编辑器,编辑提交信息
-n 只更新工作区和暂存区,不产生新的提交
-x 提交信息后追加 cherry picked from commit …
-s 提交信息后追加操作者签名

冲突的解决

在执行该命令时如果产生冲突,cherry pick 会停下来,让用户决定怎么解决冲突。

1、--continue:用户解决冲突并将修改的文件添加至暂存区后,使用 git cherry-pick --continue 命令继续进行 cherry pick。

2、--abort:放弃 cherry pick 并回到操作前的样子。

3、--quit:退出 cherry pick,但不回到操作前的样子(应用了部分提交)。

缺点

1、产生冲突。 假设在 master 分支上在某一文件的某一行新增了内容并进行提交,然后又在 master 分支上对这行内容进行了删除并提交,此时切换为 dev 分支后,先 cherry pick 第二次提交再 cherry pick 第一次提交将会产生冲突,而不是回到最开始未对这行进行修改的样子。

2、诡异的 commit 记录导致上下游分支相同的提交难以追溯。 假设现在有两个分支分别是 master 和 dev,针对文件 A 进行修改,此刻这两个分支中文件 A 的内容完全相同。先在 dev 分支上对 A 的 最后一行 添加内容“abc”,然后把“abc”修改为“abcd”,最后在 第二行 新增“123”。接下来切到 master 分支,先 cherry pick 第三次提交,一切顺利,然后在 cherry pick 第二次提交,此时会产生冲突,解决冲突后 cherry pick 第一次提交。最终两个分支的 A 文件内容相同的,但是 commit 记录呢?

dev 分支:

1
2
3
123
abcd
abc

master 分支:

1
2
abcd
123

真实的 commit 记录应该像 dev 分支的那样,而进行 cherry pick 后的 master 分支的提交记录则大不相同,过程中甚至出现了冲突。(参考链接:rebase与merge的对比及cherry-pick

3、无法 cherry pick 到新增的文件。 同样两个分支,一个文件 A,三个提交。首先在 dev 分支上修改文件 A 并增加文件 B,然后继续修改文件 A 并修改文件 B,最后只修改文件 A。接下来切换到 分支 master,连续三次修改文件 A,并 cherry pick dev 分支上最后一次提交,dev 分支上的变动会出现在 master 上,但新增的文件 B 不会出现。(参考链接:git cherry-pick 的一个陷阱!!!

4、不留下合并记录,导致上下游分支进行一系列开发再次合并时采用错误的版本并且没有冲突提示。 (参考链接:Stop cherry-picking, start merging, Part 2: The merge conflict that never happened (but should have)

基于提交 A 拉取 feature 分支,master 和 feature 分支分别产生 M1 和 F1 提交,这时发现存在重大 BUG 需要临时修复,于是将某一行的配置由 apple 修改为 berry,产生 F2 提交,测试通过后,在 master 分支上 cherry pick F2 提交,这时 master 分支上的 apple 配置也变为 berry。

临时修复并cherry-pick

然后 master 分支上产生新的提交 M3,这时开发人员找到了 BUG 的源头,原因和 apple 配置无关,因此在 feature 分支上又将 berry 改回 apple,产生 F3 提交。

溯源并改回原配置

F3 提交并测试成功后,是时候把修改同步到 master 了,于是将 feature 分支合并到 master 分支上,产生提交 M4。我们的期望是 master 分支上的临时修改能被 feature 分支上的源头修改覆盖,即 master 上的 berry 配置也改回 apple,然后遗憾的是 master 分支上的内容并没有被该改回。

合并同时出错

这还不是最糟糕的,如果又将 master 分支合并到 feature,这下就完了,配置 berry 也会同步到 feature 上。

合并到feature产生更糟糕的错误

产生这样的原因是什么呢?让我们回到第一次合并前:

溯源并改回原配置

现在我们进行合并。在合并时,Git 会寻找最近的公共基点,也就是提交 A。然后 Git 以 A 为基数,M3 作为 HEAD,F3 为入站变更进行三向合并。让我们抹除不相干的提交:

抹除不相干的提交

对比 master 的基点与头部,会发现由 apple 变为了 berry,但 feature 分支没有任何改变。由于在 feature 上没有任何改变,以为着从 feature 合并到 master 也没有任何改变,也就是 master 上的 berry 不会改变。最终导致在 feature 上是 apple,而在 master 上是 berry。

11.5 git diff

使用这个命令可以比较文件或分支之间的不同。

显示暂存区和工作区的差异:

1
git diff [file]

显示暂存区和上一次 commit 的差异:

1
2
git diff --cached [file] 
git diff --staged [file] # 显示摘要而非差异的详细信息

显示两个分支至之间的差异:

1
2
3
4
5
6
7
8
git diff branch1 branch2 --stat # 显示出 branch1 和 branch2 中差异的部分
git diff branch1 branch2 [file] # 显示指定文件的差异
git diff branch1 branch2 # 显示出所有详细差异
git log branch1 ^branch2 # 查看 branch1 有,branch2 没有的 log
git log branch1..branch2 # 查看 branch2 有,branch1 没有的 log
git log branch1...branch2 # 列出两者之间的 log 差异
git log -lefg-right branch1...branch2 # 列出两者之间的 log 差异,并显示属于哪个分支
git log origin/branch1..branch1 # 查看本地分支与远程分支的 log 差异

查找当前项目中的冲突文件:

1
git diff --check

11.6 重写历史

不建议在多人协作时去修改已经 push 到远程仓库的提交!

有时可能需要修改 (还未推送到远程仓库的) 刚提交的提交信息或者刚提交的实际提交内容,此时可以使用 git commit --amend 命令,使用这个命令可以在不增加提交次数的情况下,将新修改的代码追加到最后一次提交中。

PS:amend,读音:[əˈmend],意为修正、修订。

细节:

  • 如果当前无已暂存的修改,执行命令后,不改变最后一次提交的内容,可以修改最后一次提交的信息;
  • 如果当前已存在暂存的修改,执行命令后,将暂存的修改追加到最后一次提交的内容中,当然也可以修改最后一次提交的信息。

修改最后一次 未推送到远程仓库的 提交信息

如果仅仅只想要修改提交信息,需要保证不存在已经暂存的修改。

执行以下命令:

1
git commit --amend

该命令会将最后一次的提交信息载入到编辑器中进行修改, 当保存并关闭编辑器后,编辑器会将更新后的提交信息写入新提交中, 成为新的最后一次提交。

修改最后一次 未推送到远程仓库的 提交内容与提交信息

如果想要修改最后一次提交的实际内容,首先修改文件并 暂存(执行 git add 它们,然后执行 git commit --amend 命令将新的提交替换掉旧有的最后一次提交,当然,在此也可以修改提交信息。

如果确定在修改提交内容后会修改提交信息,可以直接执行以下命令,而不用再在编辑器中修改提交信息:

1
git commit --amend -m "新的提交信息"

修改最后一次 未推送到远程仓库的 提交内容,而不修改提交信息

1
git commit --amend --no-edit

--no-edit 表示无需修改提交信息的关键选项。

试图修改已经推送到远程仓库的提交

参考链接:git commit --amend可能带来的合并冲突

由于 git commit --amend 命令会替换最后一次提交,因此当最后一次提交已经推送到远程仓库了,就不要使用这个命令。

如果硬使用这个命令,由于原最后一次的提交被替换了,此时本地与远程的版本不一致,再次推送执行 git push 时将会失败,并提示拉取远端最新代码,而在按照提示拉取最新代码后,大概率又会产生冲突。

当然这可以使用 git push -fgit push --force-with-lease 命令 强制推送 到远程仓库来解决,但这仍可能存在问题:假设我新增提交 C1,并已经推送到远程仓库,其他同事将远程仓库的 C1 和他本地的 C2 进行合并形成 C3。接下来我执行 git commit --amend 命令修改上次的提交内容并强制推送到远程仓库形成 C4,如果此时那位同事再次合并我最新的提交,那么他又大概率会遇到冲突,因为 C1 和 C4 两次提交之间存在差异。

执行git commit --amend让他人遇到冲突

git commit --amend 的最佳适用场景:

  • 修改尚未推送到远程仓库的提交;
  • 在非多人协作或自己的特性分支上修改最后一次提交,此时进行强制推送不会有影响。

其他相关参数

--reset-author:加载提交用户的信息。重新加载新配置的本地用户信息,并更新在追加的提交中。

--author=:修改 authoremail 信息,如:git commit --amend --author='xxx <xxx@xxx.xxx>'

11.7 回滚与撤销

假设现在有两个文件,分别是 test1.txt 和 test2.txt。

test1.txt 的内容如下:

1
2
3
111
222
333

test2.txt 的内容如下:

1
2
3
000
999
888

此时 A 用户修改了 test1.txt 文件,将 333 改成了 444,test1.txt 变成了:

1
2
3
111
222
444

然后 B 用户修改了 test2.txt 文件,将 888 改成了 777,并新增了 666,test.txt 变成了:

1
2
3
4
000
999
777
666

这时环境出问题了,是用户 A 将 333 修改为 444 导致的,这是需要撤销或回滚代码,那究竟是撤销还是回滚呢?

回滚

用户 A 首先执行 git reflog 指令查看将 333 修改为 444 的那次提交的 前一次提交 的 commit id:

1
2
1a832b5 (HEAD -> master, xxx/master) HEAD@{0}: commit: 333 -> 444
35122a7 HEAD@{1}: commit: 文件初始化

前一次提交的 commit id 为 35122a7,使用此 commit id 执行以下命令:

1
git reset --hard 35122a7

然后 强制推送 到远程分支上:

1
git push -f

这时去查看远程仓库和本地代码,会发现代码已经回到了将 333 修改为 444 之前的那次提交,除此之外,B 用户的修改也不见了。

这样的操作存在一个问题,虽然服务器上的代码被还原了,但在实际开发中,往往会有很多人对一个项目进行提交,他们本地的版本依旧是比服务器的版本要高的,如果别人再提交代码,刚刚进行的回退操作又会被覆盖,上面的操作就白操作了。

当然也可以让别人把他们的本地分支删掉,然后从服务器上重新拉取分支,但这可能吗?

撤销

因此可以使用撤销,还是需要 A 用户执行 git reflog 指令查看提交记录, 只不过这次需要找将 333 修改为 444 的那次提交的 commit id, 从前面我们知道,这次提交的 commit id 为 1a832b5,因此它执行以下命令:

1
git revert -n 1a832b5

执行这个命令后,Git 的命令行界面会出现 (master|REVERTING) 的字样,并且这个命令表示撤销了指定 commit id 的提交,但是保留了那之后的提交,因此还需要推送那之后的提交(也就是说会产生一次新的提交),执行以下命令:

1
2
git commit -m "撤销 333 -> 444"
git push

此时 test1.txt 文件的内容如下:

1
2
3
111
222
333

test2.txt 文件的内容如下:

1
2
3
4
000
999
777
666

很好,撤销了错误的提交,并且还保留的之后的提交。

撤销 撤销

撤销完成后,用户 B 又对 test2.txt 进行了修改:

1
2
3
4
5
000
999
777
555
444

然后 A 用户心想,我那次提交确实有问题,应该将 333 改成 456,那应该怎么回到 444 的版本上将 444 改成 456 呢?

使用 git reflog 查看那次撤销操作的 commit id:

1
2
3
4
5
6
19d641d (HEAD -> master, xxx/master) HEAD@{0}: pull: Fast-forward
b69ab99 HEAD@{1}: commit: 撤销 333 -> 444
081f410 HEAD@{2}: pull: Fast-forward
35122a7 HEAD@{3}: reset: moving to 35122a7
1a832b5 HEAD@{4}: commit: 333 -> 444
35122a7 HEAD@{5}: commit: 文件初始化

得知那次撤销操作的 commit id 为 b69ab99,那么使用这个 commit id 撤销撤销操作:

1
2
3
4
git pull # 先同步代码
git revert -n b69ab99
git commit -m "撤销 撤销 333 -> 444"
git push

现在 test1.txt 的文件内容如下:

1
2
3
111
222
444

test2.txt 的文件内容如下:

1
2
3
4
5
000
999
777
555
444

很棒,符合预期,接下来用户 A 把 444 修改为 456 再提交就行了。

简单总结下:

1、reset 是版本回滚,回滚到某次提交时,那之后的提交都将消失;

2、revert 是撤销,撤销某次提交后,那之后的提交将存储到暂存区,可提交至本地库并推送到远程库产生新的提交。

11.8 其他后悔药

在【11.6 重写历史】中介绍了如何修改提交信息,在【11.7 回滚与撤销】中介绍了怎么回滚或撤销远程库的代码,那怎么撤销在工作区的修改、怎么撤销在暂存区的修改、怎么撤销在本地仓库的修改呢?

撤销工作区的修改(未使用 git add

放弃 某个 文件的修改:

1
2
git checkout -- filepathname
# 比如:git checkout -- test.txt

放弃 所有 文件的修改(两种方式,任选一):

1
2
git checkout . # 放弃所有文件的修改
git restore . # 放弃所有文件的修改

使用上述三个命令可以放弃对某个或所有文件的修改,执行命令后,文件回到未被修改前的样子。

谨慎使用,避免白忙活。

撤销暂存区的修改(已使用了 git add

某个 文件从暂存区移到工作区:

1
2
git reset HEAD filepathname
# 比如:git reset HEAD test.txt

所有 文件从暂存区移到工作区(两种方式,任选一):

1
2
git reset HEAD . # 将所有文件从暂存区移到工作区
git reset # 将所有文件从暂存区移到工作区

使用上述命令只是将某个或多个文件从暂存区移到工作区,如果需要放弃对文件的修改,还需要执行【撤销工作区的修改】中的命令。

撤销本地库的修改(已使用了 git commit

1、撤销(回滚)某次提交(不保留代码修改):

1
2
git reset --hard HEAD^ # 回退到上一次提交状态
git reset --hard [commit id] # 回退到某次提交状态

使用这两个命令不会保留代码的修改,但并不是说就没法再复原了,使用 git reflog 找到那次提交的 commit id,然后再使用第二个命令就能复原。

2、将 commit 和 add 的代码都拉回到工作区(两种方式,任选一)(保留代码的修改):

1
2
git reset --mixed HEAD^
git reset HEAD^

3、将 commit 的代码拉回暂存区,已 add 的代码不拉回到工作区(保留代码的修改):

1
git reset --soft HEAD^

4、撤销暂存区、本地库的修改,使其返回到远程库最新的版本(不保留代码的修改):

1
2
3
git fetch --all # 从远端获取最新到本地
# 撤销所有修改
git reset --hard origin/master # git reset --hard origin/远程分支名

谨慎使用,避免白忙活。

将某一文件还原至某次提交时,并且不产生新的提交日志

1
git checkout [commit id] -- filepathname

-- 作为分隔符,防止 commit id 恰好与 filepathname 一样。

11.9 abort 终止

当我们使用 merge 或者 cherry-pick 时可能会出现冲突,而在解决冲突时发现冲突太多,想要取消本次 merge 或者 cherry-pick,并还原到 merge 或 cherry-pick 之前所有未提交的更改。那么可以使用以下命令:

1
2
git merge --abort # Git 版本 >= 1.7.4
git reset --merge # Git 版本 >= 1.6.1

注意: 在 merge 或 cherry-pick 之前要保证没有未提交的文件,如果有未提交的文件,需要使用 commit 进行提交或者使用 stash 进行暂存,否则在某些情况下无法重现这些修改。

上述这两个命令并不是同一事物的新旧语法,使用 git help merge 命令打开手册页并查看 --abort 选项的信息:

git merge --abort is equivalent to git reset --merge when MERGE_HEAD is present unless MERGE_AUTOSTASH is also present in which case git merge --abort applies the stash entry to the worktree whereas git reset --merge will save the stashed changes in the stash list.

简单来说就是当存在 MERGE_HEAD、并且不存在 MERGE_AUTOSTASH 时,两个命令是相等的。

同样,可以使用 git help reset 命令打开手册页查看 --merge 选项的信息:

Resets the index and updates the files in the working tree that are different between <commit> and HEAD, but keeps those which are different between the index and working tree (i.e. which have changes which have not been added). If a file that is different between <commit> and the index has unstaged changes, reset is aborted.
In other words, --merge does something like a git read-tree -u -m <commit>, but carries forward unmerged index entries.

11.10 合并 commit

合并连续的提交

假设使用 git log -n4 --oneline 命令后显示的最新 4 条变更信息如下:

1
2
3
4
5d85417 (HEAD -> master) 444
66f9447 333
e462eaf 222
bee7b81 111

此时需要合并最新 3 次提交,即:e462eaf5d85417。以下命令二选一:

1
2
git rebase -i HEAD~3
git rebase -i bee7b81

其中 bee7b81最近最早无需 合并的 commit id。执行命令后,进入 Git 默认编辑器,并显示以下内容:

1
2
3
pick e462eaf 222
pick 66f9447 333
pick 5d85417 444

对上述信息修改为以下内容,然后保存并退出(按 i 进入编辑模式,按 esc 退出编辑模式,按 :wq 对内容进行保存并退出):

1
2
3
pick e462eaf 222
s 66f9447 333
s 5d85417 444

这表示将下面的两次提交合并到最上面的提交。退出后又会进入 commit message 修改页面,在这个页面可以使用 # 注释以前的 commit message,也可以添加合并后的 commit message,同样修改完保存并退出。

如果看到 Successfully rebased and updated 的字样就表示合并成功。

再使用 git log -n4 --oneline 查看最新 4 条变更信息,其中最近的两次变更信息如下:

1
2
58b2471 (HEAD -> master) 合并 222 - 444
bee7b81 111

可以看到原先 e462eaf5d85417 的提交信息已经不存在,取而代之的是 58b2471 新的 commit id。

合并过程中的冲突解决

如果合并过程中出现了冲突,需要 先解决冲突,然后再依次执行以下命令:

1
2
git add .
git rebase --continue

如果想直接放弃合并,可以执行:

1
git rebase --abort

推送到远程

要将合并后的 commit 推送到远程,需要使用 -f 选项,即:

1
git push -f

如果在自己的分支上开发,不会覆盖他人的提交记录;但如果是在主分支上使用 -f,请谨慎,这有可能会覆盖他人的代码。

rebase 提供的对提交修改的参数

参考链接:git rebase的两种用法(最全)

参数 缩写 含义
pick p 保留该 commit
reword r 保留该 commit,可以修改该 commit 的注释
edit e 保留该 commit,并停下来修改该提交(可以修改提交内容)
squash s 将该 commit 合并到前一个 commit
fixup f 将该 commit 合并到前一个 commit,但不要保留该提交的注释信息
exec x 执行 shell 命令
drop d 丢弃该 commit

执行 rebase 命令后会改变原 commit id,一旦对历史提交进行修改,从指定的提交开始往后的所有 commit id 都会发生改变,这在多人协同的场景下是毁灭性的。

假设某分支上有 100 个提交信息,使用 rebase 改了第 50 个提交,那从第 50 到 第 100 之间的所有 commit id 都会发生改变。如果又有其他分支合并到当前分支,将产生 51 个冲突,解决完冲突之后,一共产生 51 * 2 个相同的提交记录。

因此建议:

  • 在多人协作下不使用 rebase
  • 可以对尚未推送至远端的提交或有且仅有自己开发的工程下使用 rebase

11.11 指定当前工作分支与远程分支的链接关系

如果没有指定当前工作目录工作分支跟远程的仓库、分支之间的链接关系,将无法直接使用 git pullgit push 命令来拉取或推送,而是需要:

1
2
3
4
# 拉取代码
git pull 远程仓库地址别名 远程仓库分支 # 比如:git pull origin master
# 推送代码
git push 远程仓库地址别名 远程仓库分支 # 比如:git push origin master

以拉取为例,当没有指定链接关系时将会出现以下提示信息:

There is no tracking information for the current branch.
Please specify which branch you want to merge with.
See git-pull(1) for details.
	git pull <remote> <branch>
If you wish to set tracking information for this branch you can do so with:
	git branch --set-upstream-to=origin/<branch> master

根据提示信息可知,假设想要指定本地 master 分支对应远程仓库的 master 分支,可以使用:

1
git branch --set-upstream-to=远程仓库地址别名/远程仓库分支 本地分支

比如:

1
git branch --set-upstream-to=origin/master master

链接成功后就可以愉快地直接使用 git pullgit push 了。

11.12 更安全的强制推送

使用 git push -f 进行强制推送时,如果他人在当前分支推送了新的提交,那么这一命令将会“删除”他的提交。就算在推送前同步了远程仓库也是不安全的,从同步完成到真正强制推送成功之间仍存在时间差,在这个时间差之间可能又会产生新的提交。

为避免“删除”在这个时间差之间产生的新的提交,可以使用命令:

1
git push --force-with-lease

如果在这个时间差之间远程仓库增加了新的提交,使用上述命令后,推送会被拒绝,此时可以使用 git fetch 获取远程仓库后再合并远程仓库,最后使用上述命令进行强制推送。

注意: --force-with-lease 并未解决强制推送“删除”他人代码的问题,它解决的是当本地仓库不够新时的强制推送覆盖远程仓库的问题,因此当执行了 git fetch 获取远程仓库且尚未同步远程仓库时仍执意使用此命令进行强制推送,远程仓库仍会被覆盖。

12. 其他操作流程

12.1 推送到远程仓库

有时我们需要将本地未被 Git 管理的代码交由 Git 管理并推送到远程仓库,但在进行这个流程时出现了以下错误导致无法推送到远程仓库:

Updates were rejected because the remote contains work that you do

这是因为我们在新建本地库后,本地库与远程仓库的内容不一致导致的。

先看下错误的操作流程:

1
2
3
4
5
6
7
8
9
10
# 初始化仓库
git init
# 添加所有文件到本地暂存区
git add .
# 提交到本地仓库
git commit -m "commit info"
# 关联远程仓库并取别名
git remote add origin 远程仓库地址
# 推送到远程仓库
git push -u origin master

正确的操作流程需要在推送到远程仓库前同步远程仓库:

1
2
3
4
5
6
7
8
9
10
11
12
# 初始化仓库
git init
# 添加所有文件到本地暂存区
git add .
# 提交到本地仓库
git commit -m "commit info"
# 关联远程仓库并取别名
git remote add origin 远程仓库地址
# 同步远程仓库(与远程仓库的某个分支进行同步)
git pull origin master --allow-unrelated-histories
# 推送到远程仓库(推送到远程仓库的某个分支,下次推送时可直接使用 git push)
git push -u origin master

12.2 拉取远程分支到本地

本地无相关仓库代码,直接拉取即可

1
git clone -b 远程分支名 仓库地址

本地已有相关仓库代码

假设需要拉取的远程分支名为 dev

1
2
3
4
5
6
7
8
# 查看远程分支信息
git branch -r
# 创建本地分支并切换到创建的分支上
git checkout -b mydev
# 远程分支关联本地分支
git branch --set-upstream-to origin/dev mydev
# 拉取远程分支(直接使用 git pull 也行)
git pull origin dev

由于本地分支名称与关联的远程分支名称不一样(所以创建的本地分支名称最好与需要关联的远程分支名称相同),因此在 push 时得这样:

1
git push origin HEAD:dev

除此之外,第二三步创建本地分支并切换到创建的分支上,然后使创建的分支关联远程分支可以合为一步,即:

1
git checkout -b mydev origin/dev

也就是说如果我们 在创建本地分支时忘记关联远程分支,就可以使用第三部的方式进行关联。

克隆指定分支的最近一次 commit

1
git clone -b 分支名 --depth 1 远程仓库地址

使用此命令可以限制克隆的深度,不会下载 Git 历史协作记录,以大大加快克隆的速度。

在执行上述命令后想要把其他远程分支的最近一次 commit 也克隆到本地,那么可以:

1
2
3
git remote set-branches origin '目标分支名'
git fetch --depth 1 origin 目标分支名
git checkout 目标分支名

12.3 GitHub 的默认分支

虽然很久前就注册了 GitHub,但由于其访问速度不佳,一直使用的是国内的 Gitee 作为代码托管平台,但从去年开始,Gitee 变得越来越难用,先是部署 Pages 服务时时常出现内容违规的提示导致部署失败,后续又开展实名认证和仓库审核,未进行实名认证的账号无法使用 Pages 服务,仓库未经过审核不能设置为公开,只能是私有。不仅如此,其实名方式也异常严格,一般的实名只需要提交身份证正反面照片即可,而它还需要提供身份证持有人将身份证举至胸前的照片,我实在无法忍受它的一系列操作,只能放弃对它的使用,并计划将后续的代码托管都转移到 GitHub 平台上。

上周在 GitHub 创建仓库时发现仓库的默认分支竟然不是 master 了,而是变成了 main,好在 GitHub 还是挺人性化的,能够在创建仓库时修改默认分支名,一时间也没多想,于是我又将其默认分支修改回 master。

GitHub创建仓库时修改默认分支名

事后稍加思索,愈发疑惑:为什么好端端的要把默认分支名改为 main 呢?Git 的默认分支就是 master,你 GitHub 的默认分支改成了 main,这不是给用户造成了不便?

在好奇心的驱使下,对这个问题进行了简单的搜索。

这是不查不知道,原来在 20 年的 10 月 1 日就实行了,看样子是我“火星”了。那具体原因是什么呢?

回想 20 年除了疫情外还有什么呢?当年 5 月 25 日,美国街头发生一起警察暴力执法事件,致使一黑人男性乔治·弗洛伊德死亡,之后又相继爆发 BLM(Black Lives Matter)运动,简称“黑命贵”。

为了安抚愈演愈烈的民众情绪,同年 6 月,GitHub 宣布替换掉 master 等术语,避免以此联想到奴隶制。后续 MySQL 也宣布将 master、slave、blacklist(黑名单)和 whitelist(白名单)等术语分别替换为 source、replica、blocklist 和 allowlist。

对于这件事我不做评价,只不过习惯性的 git checkout master,在后续要变成 git checkout main 了。

在使用 GitHub 时,除了可以将 GitHub 默认分支又改回 master 外,也可以继续使用 main 作为默认分支。针对已经存在的仓库,如何将其主分支名修改为 main 呢?

修改主分支名为 main

首先修改本地仓库主分支名为 main:

1
git branch -m master main

同步至远端(会在远端自动创建 main 分支):

1
git push -u origin main

修改 GitHub 仓库默认分支:

切换GitHub某一仓库的默认分支为main

删除远端的 master 分支:

1
git push --delete origin master

修改 Git 本地配置,致使后续创建的仓库的默认主分支名为 main:

1
2
# 旧版本可能不支持,推荐升级至最新版 Git 后使用
git config --global init.defaultBranch main

12.4 修改远程仓库地址

书接上文,决定将 Gitee 上的代码转移到 GitHub 上,后续不再使用 Gitee。那要怎么做呢?

方式很简单,先在 GitHub 上创建一个仓库,然后将本地远程仓库地址修改成 GitHub 上远程仓库的地址后再 push 就行了。修改远程仓库地址主要涉及到以下几个命令:

1、查看本地远程仓库地址:

1
git remote -v

2、删除本地远程仓库地址:

1
git remote rm 远程仓库地址别名

3、添加远程仓库地址:

1
git remote add 远程仓库地址别名  远程仓库地址

4、修改远程仓库地址:

1
git remote set-url 远程仓库地址别名 远程仓库地址

修改本地远程仓库地址

先查看本地远程仓库地址:

1
git remote -v

假设出现以下信息:

$ git remote -v
origin  git@gitee.com:xxx/xxx.git (fetch)
origin  git@gitee.com:xxx/xxx.git (push)

这里的 origin 就是 远程仓库地址别名,那么现在有两种做法:

1、删除现有地址,然后新增一个;

2、修改现有地址。

两种都行,为了简单,选用第二种方式,假设把远程仓库地址修改为 git@github.com:xxx/xxxx.git,那么有:

1
git remote set-url origin git@github.com:xxx/xxxx.git

修改完成执行 git push 就可以把本地的代码推送到指定的远程仓库中了。

新增远程仓库地址

同样先查看本地远程仓库地址:

1
git remote -v

假设出现以下信息:

$ git remote -v
origin  git@gitee.com:xxx/xxx.git (fetch)
origin  git@gitee.com:xxx/xxx.git (push)

现在新增一个 GitHub 的远程仓库地址,在 push 时指定推送的目标远程仓库:

1
git remote add github git@github.com:xxx/xxxx.git

再次查看本地远程仓库地址信息:

$ git remote -v
github  git@github.com:xxx/xxx.git (fetch)
github  git@github.com:xxx/xxx.git (push)
origin  git@gitee.com:xxx/xxx.git (fetch)
origin  git@gitee.com:xxx/xxx.git (push)

如果想向 GitHub 的远程仓库进行推送,那么有:

1
git push github master

这表示将变更信息推送到 github 这个远程仓库地址别名对应的远程仓库的 master 分支上。

如果打算以后都往这个远程仓库进行推送,可以添加 -u 选项,表示后续默认向此远程仓库推送:

1
git push -u github master

下次只需使用 git push 就会默认往 GitHub 的远程仓库推送了。

12.5 修改历史 commit 用户名和邮箱

首先执行 git log 命令查看历史提交信息,假设有一条提交信息如下:

commit 2a51550ea114ac4b09203450c1934be22d2fa32b
Author: aaa <bbb@ccc.com>
Date:   xxxx

现需要把 Author 中的 aaa 修改为 xxx,邮箱信息 bbb@ccc.com 修改为 yyy@zzz.com,并将修改后的信息推送到远程仓库。那么可以执行以下命令(也可以编写包含以下内容的 Shell 脚本再执行)进行批量修改:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/bin/sh
git filter-branch -f --env-filter '
OLD_NAME="aaa"
OLD_EMAIL="bbb@ccc.com"
CORRECT_NAME="xxx"
CORRECT_EMAIL="yyy@zzz.com"
if [ "$GIT_COMMITTER_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_COMMITTER_EMAIL="$CORRECT_EMAIL"
fi
if [ "$GIT_AUTHOR_EMAIL" = "$OLD_EMAIL" ]
then
export GIT_AUTHOR_EMAIL="$CORRECT_EMAIL"
fi
if [ "$GIT_COMMITTER_NAME" = "$OLD_NAME" ]
then
export GIT_COMMITTER_NAME="$CORRECT_NAME"
fi
if [ "$GIT_AUTHOR_NAME" = "$OLD_NAME" ]
then
export GIT_AUTHOR_NAME="$CORRECT_NAME"
fi
' --tag-name-filter cat -- --branches --tags

如果 commit 记录比较多的话,执行时间可能比较长,耐心等待执行完毕后,再次使用 git log 命令查看是否已经修改成功。

最后执行以下命令将修改推送到远端:

1
2
git push 远程仓库地址别名 --force --all
# 例如:git push origin --force --all

批量修改命令执行失败

批量修改命令可能会执行失败,如果执行失败, 执行下面这段命令后再次执行批量修改命令:

1
git filter-branch -f --index-filter 'git rm --cached --ignore-unmatch Rakefile' HEAD

只修改最近一个 commit 的用户名和邮箱

1
git commit --amend --author="xxx <yyy@zzz.com>"

比如:

1
2
# 将最近一个 commit 的用户名修改为 mofan,邮箱修改为 yyy@zzz.com
git commit --amend --author="mofan <yyy@zzz.com>"

如果想要将最近一个 commit 的用户名和邮箱修改成当前项目 config 中的用户名和邮箱,也可以:

1
git commit --amend --reset-author --no-edit

上述命令选择执行后,使用 git push -f 强制推送到远端。

使用 rebase 修改多个 commit 的用户名和邮箱

假设有一 commit 历史为 A - B - C - D - E - F, F 为 HEAD, 现在需要修改 C 和 D 的用户名和邮箱:

  1. 先执行 git rebase -i B,如果想要修改 A,可以执行 git rebase -i -root
  2. 把 C 和 D 两个 commit 前的 pick 改为 edit,然后保存并退出;
  3. rebase 开始时,会暂停在 C,此时执行 git commit --amend --author="Author xxx <yyy@zzz.com>"
  4. 执行 git rebase --continue 继续 rebase;
  5. 接下来会暂停在 D,此时再执行 git commit --amend --author="Author xxx <yyy@zzz.com>"
  6. 执行 git rebase --continue 继续 rebase 直到结束;
  7. 最后执行 git push -f 强制推送到远端。

12.6 删除某次提交

不要在多人协作下使用!

假设使用 git log 命令可以看到如下提交信息:

commit dddddd
Author: zhangsan <aaa@bbb.com>
Date:   xxxx
	add d.txt
commit cccccc
Merge: 3d3a70e36a 8eb72887fe
Author: zhangsan <aaa@bbb.com>
Date:   xxx
	add c.txt
commit bbbbbb
Author: zhangsan <aaa@bbb.com>
Date:   xxx
	add b.txt
commit aaaaaa
Author: zhangsan <aaa@bbb.com>
Date:   xxx
	add a.txt

假设需要删除 commit id 为 cccccc 的这次提交信息,那么需要先找到这次提交的前一次 commit id,即:bbbbbb(距离当前事件最近的提交在最上面,因此前一次提交应该是 bbbbbb),然后使用这个 commit id 执行以下命令:

1
git rebase -i bbbbbb

执行命令后,会出现一个文件的编辑界面,在文件中将需要删除的 commit 之前的单词改成 drop,之后保存退出。比如:

pick dddddd add d.txt
drop cccccc add c.txt
pick bbbbbb add b.txt

比如原先 cccccc 前是 pick,现在改成 drop 后保存退出。

此时可能会产生冲突,解决冲突后,提交到本地仓库,最后执行以下命令将修改强制推送到远程仓库:

1
git push origin HEAD --force

origin 是远程仓库地址别名,根据实际情况进行替换。

12.7 修改历史提交内容

不要在多人协作下使用!

找到需要修改的目标提交的 前一个 commit id,假设为 aaaaaa,执行以下命令:

1
2
3
git rebase -i aaaaaa
# 如果需要修改第一个提交,应该使用:
git rebase -i --root

进入编辑页面,将需要修改的 commit 之前的单词改成 e,然后保存退出,比如:

e bbbbbb xxxxxx
pick cccccc xxxxxx
pick dddddd xxxxxx

接着完成想要的修改,修改完成后依次执行以下命令:

1
2
3
git add .
git commit --amend --no-edit
git rebase --continue

如果要把修改推送至远端,再执行:

1
git push -f

12.8 merge 的回退

项目新特性的开发流程一般是基于 test 分支新建特性分支 feat,在功能开发完毕后 cherry-pickdev 分支,在 dev 分支自测无误后再将 feat 分支合并到 test 分支,无论是将 dev 分支直接合并到 test 分支,还是将 test 分支直接合并到 dev 分支,这都将影响团队协作,因此如何回退 merge 而且不影响他人的提交就是一个问题。

推荐使用 revert 命令,而不是 reset

在进行 merge 操作后,会出现一条类似下方的 log 信息:

Merge remote-tracking branch 'origin/xxx' into xxx

找到这条 log 信息对应的 commit id,执行以下命令:

1
git revert [commit id] -m 1

然后再 push 即可。

12.9 修改最近一次的 commit 时间

不要在多人协作下使用!

借助 git commit --amend 实现,比如修改最近一次的 commit 时间为 2023-12-04 20:30:00

1
git commit --amend --no-edit --date "Mon 4 Dec 2023 20:30:00 Asia/Shanghai"

最后的日期时间格式必须是:

星期 日 月 年 时间 时区

在中国,时区应该是 Asia/Shanghai,而不是 CST

12.10 将其他分支完全覆盖到当前分支

不要在多人协作下使用!

在当前分支下依次执行以下命令:

1
2
git reset --hard 目标分支名
git push -f

13. 遇到的问题

13.1 IDEA Git 出现 Line Separators Warning

当我们使用 IDEA 的 Git Commit 代码时,可能会出现以下窗口:

LineSeparatorsWarning窗口

出现这种情况主要是因为不同的操作系统采用的行分隔符的方式不同。

在 Windows 下采用的是 CRLF 方式,即回车并换行。CR 是老版本 MAC 的做法,即回车,后来 MAC 系统统一成了 LF,LF 也是 Linux 下的做法,即回车。

关于这三者的关系,有兴趣的朋友可以参考这篇文章:趣谈、浅析CRLF和LF

一般操作系统上的运行库会自动决定文本文件的换行格式。 如一个程序在 Windows 上运行就生成 CRLF 换行格式的文本文件,而在 Linux 上运行就生成 LF 格式换行的文本文件。

需要注意的是 在一个平台上使用另一种换行符的文件文件可能会带来意想不到的问题。

那怎么搞?🤪

通常来说,IDE 或文本编辑器都带有换行符转换功能,使用这个功能可以将文本文件中的换行符在不同格式单互换。

场景复现

我是在 Windows 操作系统下新建了一个文件,在对这个文件编写好代码后,需要 Commit 到本地仓库,这个时候就出现了开头的窗口。

这是因为 Windows 下使用的是 CRLF 换行方式,我创建的文件的换行方式也是 CRLF,与仓库中其他文件文件的换行方式不同,所以 IDEA 就弹出了这个窗口。

解决方式

解决方式很简单,前往我们新建的文件,将 IDEA 右下角的行分隔符设置由 CRLF 修改为 LF 即可,如:

设置文件的行分隔符

注意: Windows 下是从 CRLF 改成 LF,而 MAC 下相反。

这样再 Commit 代码就不会出现这个提示框了,问题暂时解决。🤨

为什么说暂时解决,因为我们下次再创建文件时,右下角行分隔符的设置还是 CRLF,虽然也可以点一下修改,那有没有什么一劳永逸的方法?🤔

诶!有的!🧐

这主要是因为 CRLF 是跟随系统来设置的,因为当前系统是 Windows,所以采取的方式就是 CRLF 了。

主需要打开 Settings,切换至 Editor,点击 Code Style,然后将 Line separator 由 System-Dependent 修改为 Unix and MacOS(\n) 即可:

修改IDEA的默认LineSeparator

13.2 LF will be replaced by CRLF

参考链接:

关于 LF will be replaced by CRLF 问题出现的原因以及解决方式

关于git提示“warning: LF will be replaced by CRLF”终极解答

具体分析与解决

在 Windows 操作系统上使用 Git 命令行提交代码时,有时候会出现这样的警告:

warning: LF will be replaced by CRLF in xxxxx

原因很简单:Windows 中的换行符为 CRLF,而在 Linux 下的换行符为 LF,所以在将修改的文件添加至暂存区时就会出现提示。

如果你在 Windows 下想要 开启 换行符的转化,可以执行以下命令:

1
2
# 提交时转换为LF,检出时转换为CRLF
git config --global core.autocrlf true

在 Linux 或 Mac 中,行结束符是 LF,但是当 CRLF 引入文件时,我们应当使用 Git 进行修正,可以执行以下命令:

1
2
# 提交时转换为LF,检出时不转换
git config --global core.autocrlf input

如果仅在 Windows 上进行开发,且开发仅运行在 Windows 上的项目,可以设置 false 取消此功能,把回车保留在版本库中,关闭自动转换的功能即可:

1
2
# 提交检出均不转换
git config --global core.autocrlf false

还可以在文件提交时进行 safecrlf 检查:

1
2
3
4
5
6
7
8
# 拒绝提交包含混合换行符的文件
git config --global core.safecrlf true

# 允许提交包含混合换行符的文件
git config --global core.safecrlf false

# 提交包含混合换行符的文件时给出警告
git config --global core.safecrlf warn

总结

一般来说,不建议关闭这个功能,因为实际项目开发时是协同合作,两个开发者的操作系统不一致时常发生,所以建议开启这个功能。

如果是使用在 Windows 上编写自己的项目,关闭这个功能也是可以的。

而且这只是一个警告,可以直接忽略,对我们整体工作不会造成影响!

13.3 git reset --hard HEAD^ 后显示 more?

在 Windows 的 CMD(或 IDEA 的 Terminal)中执行 git reset --hard HEAD^ 命令后,显示 more?,自觉告诉我按下回车即可,结果多次按下回车后出现以下错误:

fatal: ambiguous argument 'HEAD
': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

这是因为 CMD 中默认的换行符恰好是 ^,出现的 more? 就是在询问是否要再输入,而 ^ 被当成换行符后就被 Git 命令忽略了,因此多次回车后就报错了。

处理方法:

1、加引号:git reset --hard "HEAD^"

2、多加个 ^git reset --hard HEAD^^

3、换成 ~git reset --hard HEAD~ 或者 git reset --hard HEAD~1,这表示回退 1 次提交,单独一个 ~ 默认是一次

4、不使用 CMD(或 IDEA 的 Terminal)执行 Git 命令,换用 PowerShell 或者 Git bash

13.4 git pull 提示 warning: ignoring broken ref refs/remotes/origin/HEAD

简单的两步即可解决:

1、删除项目的 .git/refs/remotes/origin 目录下的所有文件

2、执行以下命令:

1
git fetch --all

13.5 unable to get local issuer certificate

在执行 git pull 命令时遇到了以下错误信息:

fatal: unable to access 'https:xxxxxx': SSL certificate problem: unable to get local issuer certificate

这是由于通过 HTTPS 访问 Git 远程仓库时,如果服务器上的 SSL 证书未经过第三方机构的认证,这意味着可能存在很大的风险,Git 进而报错。

可以通过以下命令将 Git 中的 sslverify 关掉:

1
2
3
4
5
6
# 作用于当前系统用户
git config --global http.sslverify false
# 作用于全局所有用户
git config --system http.sslverify false
# 作用于当前仓库
git config http.sslverify false

上述命令任选其一即可,它们只是作用范围不同。

13.6 git status 中文文件名乱码

执行以下命令解决:

1
2
3
4
5
6
7
8
# 设置 git status utf-8 编码
git config --global core.quotepath false
# 设置 GUI 界面 utf-8 编码
git config --global gui.encoding utf-8
# 设置 commit 信息 utf-8 编码
git config --global i18n.commit.encoding utf-8
# 设置输出 log utf-8 编码
git config --global i18n.logoutputencoding utf-8

一般执行第一条命令即可, 余下命令仅供参考。

13.7 右键菜单没有 Git Bash here

一次 Windows 系统更新后,右键菜单中的“Git Bash here”消失了,百度上的解决方案基本都要修改注册表,但其实没这么麻烦, 重新安装 Git 即可, 甚至可以趁此机会升级下 Git 的版本。

14. 其他补充信息

14.1 Commit Type 类别

参考链接:天天用Git,分支开发你怎么弄的?

  • feat:添加新特性
  • fix:修复 BUG
  • docs:仅仅修改了文档
  • style:仅仅修改了空格、格式缩进等,未改变代码逻辑
  • refactor:代码重构,未添加新功能或修复 BUG
  • perf:增加代码进行性能测试
  • test:增加测试用例
  • chore:改变构建流程,增加依赖库、工具等

14.2 管理多个用户信息

参考链接: Organizing multiple Git identities

某些情况下,一台电脑可能会有多个 Git 用户,就像“工作一个号,生活一个号”,需要使用与默认用户名与邮箱不同的身份来提交代码。尽管可以每次都在特定的项目中使用以下命令对默认用户信息进行修改,但如果不小心忘记了,就可能遇到不必要的麻烦:

1
2
git config user.name "xxx"
git config user.email "qwe@123.com"

假设工作项目 总是 存放在 D:/Code/Work/ 目录下,这个目录下的项目需要使用公司内部的邮箱作为用户信息,除此之外其他位置的项目则是使用默认用户信息。

Git 全局的 .gitconfig 配置文件有一个很棒的特性,能够有条件地包含其他配置文件

那么可以修改全局 .gitconfig 配置文件,针对位于 D:/Code/Work/ 目录下的项目,使用 .gitconfig-work 配置文件:

1
2
3
4
5
[user]
name = mofan
email = cy.mofan@foxmail.com
[includeIf "gitdir:D:/Code/Work/"]
path = ./.gitconfig-work

.gitconfig-work 中的内容如下:

1
2
3
[user]
name = 默烦
email = example@work.com

100. 可能有用的小工具

100.1 push.sh

日常工作中提交代码时常常会频繁切换分支,具体操作步骤是:

  1. 暂存当前分支上的变更
  2. 检出 dev 分支或基于 test 分支创建的个人分支
  3. 应用暂存的变更,推送到远端
  4. 检出到原分支,将刚刚的提交 cherry-pick 到原分支并推送到远端

这些步骤中可能会产生冲突,但在没有冲突的情况下是完全相同的,因此想到编写一个脚本对上述操作完成自动化。

实现过程的参考:

脚本地址:my-tools/git/push.sh

100.2 pull.sh

通过完整的项目地址或者项目名称依次在 GitHub、Gitee 上拉取代码,适用场景较为局限,拉取代码时需要两种信息:

  1. 项目所属用户名
  2. 项目名

在知道项目名的情况下,一般并不知道项目所属用户名,常用于拉取某一特定用户名下的项目。

脚本地址:my-tools/git/pull.sh