02【Git分支的使用、Git回退、还原】

上一篇01【Git的基本命令、底层命令、命令原理】

下一篇03【Git的协同开发、TortoiseGit、IDEA的操作Git】


文章目录

  • 02【Git分支的使用、Git回退、还原】
  • 一、分支
    • [1.1 分支概述](#1.1 分支概述)
      • [1.1.1 Git分支简介](#1.1.1 Git分支简介)
      • [1.1.2 Git分支原理](#1.1.2 Git分支原理)
    • [1.2 创建分支](#1.2 创建分支)
      • [1.2.1 创建普通分支](#1.2.1 创建普通分支)
      • [1.2.2 指定提交对象创建分支](#1.2.2 指定提交对象创建分支)
    • [1.3 切换分支](#1.3 切换分支)
      • [1.3.1 checkout分支切换](#1.3.1 checkout分支切换)
      • [1.3.2 checkout原理](#1.3.2 checkout原理)
        • [1) 影响工作空间](#1) 影响工作空间)
        • [2) 影响暂存区](#2) 影响暂存区)
      • [1.3.3 switch切换分支](#1.3.3 switch切换分支)
    • [1.4 分支合并](#1.4 分支合并)
      • [1.4.1 快进合并](#1.4.1 快进合并)
      • [1.4.2 典型合并](#1.4.2 典型合并)
    • [1.5 Git的代码冲突](#1.5 Git的代码冲突)
      • [1.5.1 Git的代码冲突的分类与特点](#1.5.1 Git的代码冲突的分类与特点)
      • [1.5.2 快进合并代码冲突](#1.5.2 快进合并代码冲突)
      • [1.5.3 典型合并代码冲突](#1.5.3 典型合并代码冲突)
    • [1.6 Git的分支状态存储](#1.6 Git的分支状态存储)
      • [1.6.1 Git存储引入](#1.6.1 Git存储引入)
      • [1.6.2 Git存储的使用](#1.6.2 Git存储的使用)
        • [1) 使用存储状态](#1) 使用存储状态)
        • [2) 读取存储状态](#2) 读取存储状态)
        • [3) 存储状态的删除](#3) 存储状态的删除)
      • [1.6.3 Git存储的原理](#1.6.3 Git存储的原理)
        • [1) 使用存储状态的原理](#1) 使用存储状态的原理)
        • [2) 读取存储状态的原理](#2) 读取存储状态的原理)
        • [3) 删除存储状态的原理](#3) 删除存储状态的原理)
    • [1.7 Git打标签](#1.7 Git打标签)
      • [1.7.1 标签的语法与介绍](#1.7.1 标签的语法与介绍)
      • [1.7.2 标签的使用](#1.7.2 标签的使用)
  • 二、Git数据恢复与还原
    • [2.1 Git的还原](#2.1 Git的还原)
      • [2.1.1 还原工作空间](#2.1.1 还原工作空间)
      • [2.1.2 还原暂存区](#2.1.2 还原暂存区)
      • [2.1.3 还原提交对象](#2.1.3 还原提交对象)
        • [1) 提交日志修正](#1) 提交日志修正)
        • [2) 提交内容修正](#2) 提交内容修正)
        • [3) 提交文件修正](#3) 提交文件修正)
    • [2.2 Git的数据回退](#2.2 Git的数据回退)
      • [2.2.1 回退HEA指针](#2.2.1 回退HEA指针)
      • [2.2.2 回退暂存区](#2.2.2 回退暂存区)
      • [2.2.3 回退工作空间](#2.2.3 回退工作空间)

02【Git分支的使用、Git回退、还原】

一、分支

1.1 分支概述

1.1.1 Git分支简介

Git的分支概念与SVN的分支概念完全不同,在SVN中,分支更倾向于是一个文件夹 ,建立分支也只是建立一个新的文件夹,利用分支管理项目其实本质上是为了使得项目的结构更加清晰。当然SVN的分支也提供合并、回退等功能,但相对于Git过于笨重。

在Git中使用分支的主要目的是为了==合并分支==,基于分支来开发项目并不会影响主线开发,当其他分支的代码确认无误需要集成到主线分支(Master分支)时,我们需要进行分支的合并即可,即将主线分支合并到其他分支中,这样一个完整的功能就集成到主线代码中了;

  • Git分支如图所示:

1.1.2 Git分支原理

在很多版本控制系统中,创建分支是一个略微低效的过程------常常需要完全创建一个源代码目录的副本。对于大项目来说,这样的过程会耗费很多时间,例如SVN正是这样的操作。显然,Git并没有采用这种笨重的方式来管理分支。

在Git中,分支其实本质上仅仅是指向Commit对象的可变指针 ;默认情况下所有仓库都会有一个默认的分支,名为master。在.git\refs\heads目录存储着当前仓库的所有分支。每一个分支都有一个以分支名命名的文件,该文件存储着当前分支指向的提交对象的hash值;

【初始化仓库】

shell 复制代码
rm -rf .git ./*
git init
echo '111' > aaa.txt
git add ./
git commit -m '111' ./

【查看分支】

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline
ceca35b (HEAD -> master) 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ cat .git/refs/heads/master
ceca35b10c0495e852cbf26205fb5a5af409b70e

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git cat-file -p ceca35b
tree 8f96f2f60c766a6a6b78591e06e6c1529c0ad9af
author xiaohui <xiaohui@aliyun.com> 1697097246 +0800
committer xiaohui <xiaohui@aliyun.com> 1697097246 +0800

111

【继续开发,查看master分支变化】

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ echo '222' > aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git commit -m '222' ./
warning: LF will be replaced by CRLF in aaa.txt.
The file will have its original line endings in your working directory
warning: LF will be replaced by CRLF in aaa.txt.
The file will have its original line endings in your working directory
[master 69372ca] 222
 1 file changed, 1 insertion(+), 1 deletion(-)

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline
69372ca (HEAD -> master) 222
ceca35b 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ cat .git/refs/heads/master
69372ca265e6a98c8a7f839b8760f98b80bbf4fe

我们可以看到,在Git中,分支仅仅是一个指向Commit对象的一个可变指针,当执行commit提交后,当前分支会指向最新的commit对象的hash值,这样通过提交对象的hash值来找到对应版本的内容,而不需要像其他版本控制工具那样将整个文件夹进行拷贝。

因此,Git的分支相对于其他版本控制工具显得极其轻量级,操作速度也是其他版本控制工具不可比拟的。

  • 在Git中,每一个分支都是保存一个提交对象的hash值:

1.2 创建分支

1.2.1 创建普通分支

  • 语法:
shell 复制代码
git branch {branchName} [commitHash]

创建一个分支,默认情况下指向最新的commit对象

【初始化项目】

shell 复制代码
rm -rf .git ./*
git init
echo '111' > aaa.txt
git add ./
git commit -m '111' ./
echo '222' > aaa.txt
git commit -m '222' ./
echo '333' > aaa.txt
git commit -m '333' ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline
6f02325 (HEAD -> master) 333
7ded3d1 222
a9e7314 111

HEAD是一个活动指针,指向的是正在活跃的分支。

【在当前HEAD指针指向的位置创建分支】

shell 复制代码
git branch b1

查看.git\refs\heads目录:

【继续使用master分支开发】

shell 复制代码
echo '444' > aaa.txt
git commit -m '444' ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline
2920850 (HEAD -> master) 444
57a8f94 (b1) 333
3ac4fa8 222
afb705d 111

1.2.2 指定提交对象创建分支

【指定提交对象来创建分支】

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git branch b2 3ac4fa8					# 根据指定的提交对象来创建分支

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline
2920850 (HEAD -> master) 444
57a8f94 (b1) 333
3ac4fa8 (b2) 222
afb705d 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ cat .git/refs/heads/b2
3ac4fa83b8c224518758b2b4ef7d2f214ac0224a

1.3 切换分支

1.3.1 checkout分支切换

创建好了分支之后,我们可以使用git checkout切换到其他分支进行开发;

  • 语法:
shell 复制代码
git checkout {branchName}

【准备环境】

shell 复制代码
rm -rf .git ./*
git init
echo '111' >> aaa.txt
git add ./
git commit -m '111' ./
echo '222' >> aaa.txt
git commit -m '222' ./
echo '333' >> aaa.txt
git commit -m '333' ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline
2130f83 (HEAD -> master) 333
86cbe2b 222
dad9d1a 111

【创建分支】

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 在当前位置创建b1分支
$ git branch b1

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 指定提交对象来创建分支
$ git branch b2 86cbe2b

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		
$ git log --oneline
2130f83 (HEAD -> master, b1) 333		# b1分支的位置
86cbe2b (b2) 222						# b2分支的位置
dad9d1a 111

【切换到b1分支进行开发】

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 切换到b1分支
$ git checkout b1
Switched to branch 'b1'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)			# HEAD指针指向了b1分支
$ git log --oneline
2130f83 (HEAD -> b1, master) 333
86cbe2b (b2) 222
dad9d1a 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)		# 编辑文件
$ echo "444" >> aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)		# 提交
$ git commit -m "b1 444" ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)		# 查看文件内容
$ cat aaa.txt
111
222
333
444

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)		# 查看日志
$ git log --oneline
51b41c0 (HEAD -> b1) b1 444				# b1分支正在开发
2130f83 (master) 333					# master和b2分支留在原地
86cbe2b (b2) 222
dad9d1a 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)		# 切换到master分支
$ git checkout master
Switched to branch 'master'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)	# 查看日志
$ git log --oneline
2130f83 (HEAD -> master) 333		# HEAD指针指向了master分支
86cbe2b (b2) 222
dad9d1a 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)	# 查看master分支工作状态的文件内容(文件内容是旧版本的)
$ cat aaa.txt
111
222
333

【查看所有版本情况】

默认情况下使用git log查询日志只能查询到之前的版本的日志,无法查询到比自身分支版本还要新的版本,通过--all可以查询所有版本的提交日志;

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline				# 只能查询master分支之前版本的日志信息
2130f83 (HEAD -> master) 333
86cbe2b (b2) 222
dad9d1a 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 查询所有日志信息
$ git log --oneline --all
51b41c0 (b1) b1 444
2130f83 (HEAD -> master) 333
86cbe2b (b2) 222
dad9d1a 111

1.3.2 checkout原理

通常情况下,当前分支如果存在未提交的操作时,则无法切换到其他分支;所以我们切换分支时最好保证当前工作空间的状态为nothing to commit;即:所有操作均已提交

在切换分支时,Git会对以下地方进行修改:

  • 1)HEAD指针:将HEAD指针指向最新的分支
  • 2)工作空间的内容:将当前工作空间的内容更新为之前的分支的内容

【代码示例】

shell 复制代码
rm -rf .git ./*
git init
echo '111' >> aaa.txt
git add ./
git commit -m '111' ./
git branch b1						
echo '222' >> aaa.txt
git add ./
git commit -m '222' ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git log --oneline --all
464d580 (HEAD -> master) 222
cc3429a (b1) 111

【操作master分支的文件但不提交,切换到b1分支发现切换失败】

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)			# 编辑文件
$ echo "333" >> aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   aaa.txt

no changes added to commit (use "git add" and/or "git commit -a")

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git checkout b1
error: Your local changes to the following files would be overwritten by checkout:
        aaa.txt
Please commit your changes or stash them before you switch branches.
Aborting

可以看出,如果当前分支存在未提交的操作时,Git不允许切换分支。但是有2种情况是例外的:

  • 1)新分支的情况:当该分支是一个新创建的分支时,当前分支存在未提交的操作依旧可以切换到新分支,并且会将未提交的操作影响到新分支;
  • 2)新文件的情况:当操作的文件是一个新的文件时,当前分支未提交依旧是可以切换到其他分支,并将操作影响到其他分支;

如果存在以上两种行为进行切换分支,则切换分支时不仅会对HEAD指针、工作空间等地方修改,还会对如下地方进行修改:

  • 1)工作空间的状态:将当前分支的工作空间状态更新为之前分支
  • 2)暂存区的内容:将当前暂存区内容更新为之前的分支

Tips:我们在切换分支时,要保证当前分支是提交的状态,否则会对其他分支造成不必要的影响;

1) 影响工作空间

新分支、新文件的操作都会影响工作空间和暂存区;

【演示新分支影响工作空间】

初始化仓库

shell 复制代码
rm -rf ./* .git
git init
echo '111' >> aaa.txt
git add ./
git commit -m '111' ./
git branch b1						
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)	# b1分支创建后整个工作空间还没有提交过任何一次文件,属于新分支
$ git log --oneline --all
4b3c6bc (HEAD -> master, b1) 111			# 指针还是指向master分支的

在master分支编辑文件,然后切换到b1分支,查看工作空间的内容,发现变更了(影响了b1分支的工作空间内容):

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)		# 编辑master分支的文件内容
$ echo "222" >> aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)		# master分支的状态
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   aaa.txt

no changes added to commit (use "git add" and/or "git commit -a")

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)		# 切换到b1分支,发现能够切换成功
$ git checkout b1
Switched to branch 'b1'
M       aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (b1)			# b1分支的文件内容
$ cat aaa.txt
111
222

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (b1)			# b1分支的状态
$ git status
On branch b1
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   aaa.txt

no changes added to commit (use "git add" and/or "git commit -a")

可以看到在遇到新分支时,当前工作空间的操作即使未提交也可以切换到其他分支,而且影响其他分支的工作空间状态及工作空间内容;注意,此时将master未提交的数据也影响到了b1分支,当分支切换到master然后提交,b1分支被影响的内容就消失了;

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (b1)			# 切换到master分支
$ git checkout master
Switched to branch 'master'
M       aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)		# 提交操作
$ git commit -m "222" ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)		# 切换到b1分支
$ git checkout b1
Switched to branch 'b1'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (b1)			# 查看内容,发现影响消失了
$ cat aaa.txt
111

重新切换到master分支,编辑文件未提交,切换到b1分支失败(此时b1分支不属于新分支了):

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (b1)
$ git checkout master
Switched to branch 'master'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ echo "333" >> aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)			# 切换到b1分支失败,此时b1分支不属于新分支了;
$ git checkout b1
error: Your local changes to the following files would be overwritten by checkout:
        aaa.txt
Please commit your changes or stash them before you switch branches.
Aborting

【演示新文件影响工作空间】

把之前编辑的内容删除,将工作空间恢复到干净状态

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)		# 把之前编辑的内容删除
$ vi aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ cat aaa.txt
111
222

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)		# 工作空间又回到了干净状态(所有的操作均已提交)
$ git status
On branch master
nothing to commit, working tree clean

使用master创建一个新的文件,切换到b1分支,查看b1分支的工作空间内容,发现也多了一个bbb.txt文件(影响了b1分钟的工作空间):

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)		# 编辑一个新的文件
$ echo "333" >> bbb.txt		# 注意,此时创建了一个新的文件bbb.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git status
On branch master
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        bbb.txt

nothing added to commit but untracked files present (use "git add" to track)

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)		# 切换到b1分支,发现切换成功
$ git checkout b1
Switched to branch 'b1'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (b1)
$ ll
total 2
-rw-r--r-- 1 Adminstrator 197121 5 Oct 23 09:23 aaa.txt
-rw-r--r-- 1 Adminstrator 197121 4 Oct 23 09:23 bbb.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (b1)			# b1的工作空间多了一个文件
$ git status
On branch b1
Untracked files:
  (use "git add <file>..." to include in what will be committed)
        bbb.txt

nothing added to commit but untracked files present (use "git add" to track)

切换到master分支,提交操作,然后切换回b1分支,发现b1分支的bbb.txt文件清除了:

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (b1)			# 切换回master分支
$ git checkout master
Switched to branch 'master'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git add ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)		# 提交操作
$ git commit -m "333-bbb" ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)		# 切换到b1分支
$ git checkout b1
Switched to branch 'b1'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (b1)
$ git status
On branch b1
nothing to commit, working tree clean

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (b1)		# b1工作空间的bbb.txt文件清除了
$ ll
total 1
-rw-r--r-- 1 Adminstrator 197121 5 Oct 23 09:26 aaa.txt
2) 影响暂存区

【演示新分支影响暂存区】

初始化仓库:

shell 复制代码
rm -rf ./* .git
git init
echo '111' >> aaa.txt
git add ./
git commit -m '111' ./
git branch b1			

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)		# b1分支创建后整个工作空间还没有提交过任何一次文件,属于新分支
$ git log --oneline --all
19fd84b (HEAD -> master, b1) 111		# 指针还是指向master分支的

在master分支编辑文件,然后添加到暂存区,切换到b1分支,查看b1分支的暂存区内容,发现变更了(影响了b1分支的暂存区);

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ echo "222" >> aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git add ./
warning: LF will be replaced by CRLF in aaa.txt.
The file will have its original line endings in your working directory

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git checkout b1
Switched to branch 'b1'
M       aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (b1)
$ git ls-files -s
100644 a30a52a3be2c12cbc448a5c9be960577d13f4755 0       aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (b1)
$ git cat-file -p a30a52a3
111
222

重新切换回master,提交之前的操作,然后再切换回b1分支,查看暂存区内容,发现影响消除了(此时b1不再属于新分支了):

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (b1)
$ git checkout master
Switched to branch 'master'
M       aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git commit -m "222" ./
warning: LF will be replaced by CRLF in aaa.txt.
The file will have its original line endings in your working directory
[master 2ae7cda] 222
 1 file changed, 1 insertion(+)

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git checkout b1
Switched to branch 'b1'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (b1)
$ git ls-files -s
100644 58c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c 0       aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (b1)
$ git cat-file -p 58c9bdf9
111

切换回master分支,编辑文件,还没有提交,切换到b1分钟发现出错(此时b1分支不属于新分支了):

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ echo "333" >> aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git add ./
warning: LF will be replaced by CRLF in aaa.txt.
The file will have its original line endings in your working directory

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git checkout b1
error: Your local changes to the following files would be overwritten by checkout:
        aaa.txt
Please commit your changes or stash them before you switch branches.
Aborting

【演示新文件影响工作空间】

提交之前的操作,将工作空间恢复到干净状态

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git commit -m "333" ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git log --oneline --all
f5d54ea (HEAD -> master) 333
2ae7cda 222
e9b98e3 (b1) 111

创建一个新文件,添加到暂存区,然后切换到b1分支,发现b1分支的暂存区也多了一个文件(影响了b1分支的暂存区):

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ echo "444-bbb" >> bbb.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git add ./
warning: LF will be replaced by CRLF in bbb.txt.
The file will have its original line endings in your working directory

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git checkout b1
Switched to branch 'b1'
A       bbb.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (b1)		# b1分支的暂存区也多了一个文件
$ git ls-files -s
100644 58c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c 0       aaa.txt
100644 a864cef2a0696aabcc85e6e55f04dc12230bab84 0       bbb.txt

切换回master,提交之前的操作,然后再切换回b1分支,查询b1分支的暂存区内容被消除了(消除影响):

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (b1)
$ git checkout master
Switched to branch 'master'
A       bbb.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git commit -m "444-bbb" ./
warning: LF will be replaced by CRLF in bbb.txt.
The file will have its original line endings in your working directory
[master d76cc6f] 444-bbb
 1 file changed, 1 insertion(+)
 create mode 100644 bbb.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git checkout b1
Switched to branch 'b1'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (b1)
$ git ls-files -s
100644 58c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c 0       aaa.txt

此时bbb.txt文件已经不属于新文件了,回到master分支,编辑bbb.txt文件,未提交,然后切换到b1分支,发现切换不了:

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (b1)
$ git checkout master
Switched to branch 'master'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ echo "555-bbb" >> bbb.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git add ./
warning: LF will be replaced by CRLF in bbb.txt.
The file will have its original line endings in your working directory

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git checkout b1
error: Your local changes to the following files would be overwritten by checkout:
        bbb.txt
Please commit your changes or stash them before you switch branches.
Aborting

1.3.3 switch切换分支

git switch是Git 2.23版本推出的一个新的命令,专门用于分支的切换,而git checkout除了做分支的切换还有一些其他的功能,如恢复文件,恢复暂存区等;

初始化仓库:

shell 复制代码
rm -rf ./* .git
git init
echo '111' >> aaa.txt
git add ./
git commit -m '111' ./

使用git checkout恢复文件:

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ echo "222" >> aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   aaa.txt

no changes added to commit (use "git add" and/or "git commit -a")

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git checkout aaa.txt
Updated 1 path from the index

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git status
On branch master
nothing to commit, working tree clean

使用git checkout恢复暂存区:

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ echo "222" >> aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)		# 添加到暂存区
$ git add ./
warning: LF will be replaced by CRLF in aaa.txt.
The file will have its original line endings in your working directory

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   aaa.txt


Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git reset aaa.txt
Unstaged changes after reset:
M       aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)
$ git checkout aaa.txt
Updated 1 path from the index

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (master)		# 成功恢复
$ git status		
On branch master
nothing to commit, working tree clean

git switch命令是专门用于切换分支的,并且git switch切换分支也会存在我们探究的新分支、新文件切换时所带来的影响问题;

使用git switch命令切换分支:

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 ((89dda87...))
$ git log --oneline --all
033f6b5 (master) 222
89dda87 (HEAD) 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 ((89dda87...))
$ git branch b1

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 ((89dda87...))
$ git switch b1
Switched to branch 'b1'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace/xiaohui/test01 (b1)
$ git log --oneline --all
033f6b5 (master) 222
89dda87 (HEAD -> b1) 111

1.4 分支合并

建立好分支后,我们可以使用分支进行开发功能,开发功能趋于成熟稳定后,我们可以将master分支与当前分支进行合并,这样其他分支的功能就集成到主分支上了,这也是当前主流的开发方式。

使用分支开发功能从角度上划分一般为同轴开发非同轴开发

  • 分支合并语法:
shell 复制代码
git merge {branchName}
  • 解释:将branchName分支的代码合并到当前分支

1.4.1 快进合并

  • 同轴开发的快进合并:

同轴开发:创建一个新分支,使用新分支开发,等待开发功能趋于成熟稳定后,我们可以将master分支与新分支进行合并,这样其他分支的功能就集成到主分支上了,这也是企业里面经常用的一种开发方式;

对于前面的版本想要合并后面的版本,我们称为快进合并

在同轴开发中,多个分支处于同一根开发轴上,后面版本的代码包含前面版本的代码,因此同轴开发一般只会存在快进合并;

  • 如图所示:
  • 首先创建一个新分支(login分支)
  • 切换到新分支,在新分支开发上功能代码

【示例代码】

初始化项目:

shell 复制代码
rm -rf .git ./*
git init
echo '用户名+密码登录' >> login.txt
git add ./
git commit -m '用户名+密码登录功能完成' ./

①创建login分支

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git branch login

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline --all
2f5d87a (HEAD -> master, login) 用户名+密码登录功能完成

②切换到login分支

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git checkout login
Switched to branch 'login'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (login)
$ git log --oneline --all
2f5d87a (HEAD -> login, master) 用户名+密码登录功能完成

③使用login分支开发:集成QQ登录

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (login)
$ echo "集成QQ登录" >> login.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (login)
$ git commit -m "集成QQ登录" ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (login)
$ git log --oneline --all
a879a6c (HEAD -> login) 集成QQ登录
2f5d87a (master) 用户名+密码登录功能完成

④使用login分支开发:集成微信登录

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (login)
$ echo "集成微信登录" >> login.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (login)
$ git commit -m "集成微信登录" ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (login)
$ git log --oneline --all
c5a2584 (HEAD -> login) 集成微信登录
a879a6c 集成QQ登录
2f5d87a (master) 用户名+密码登录功能完成

当login分支基于稳定后,将功能集成到maste分支:

  • 如图所示:

⑤切换回master分支,将login分支的代码合并到master分支:

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (login)
$ git checkout master
Switched to branch 'master'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ cat login.txt
用户名+密码登录

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git merge login
Updating 2f5d87a..c5a2584
Fast-forward
 login.txt | 2 ++
 1 file changed, 2 insertions(+)

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ cat login.txt
用户名+密码登录
集成QQ登录
集成微信登录

将login分支的代码合并到master分支,其实本质上就是让master也指向与login一样的提交对象;

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# master与login分支指向的是同一个Commit对象
$ cat .git/refs/heads/login
c5a258428f0bec398017159126f0b87b0d05a1e4
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ cat .git/refs/heads/master
c5a258428f0bec398017159126f0b87b0d05a1e4

1.4.2 典型合并

  • 非同轴开发的典型合并:

非同轴开发:创建一个新分支,使用新分支开发,与此同时master分支也在开发行功能,等到新分支开发的功能完毕后,将master分支与新分支进行合并,将新分支集成到master分支中,但是新分支在开发过程中master分支也进行了一部分开发。

==在非同轴开发中,后面的版本(v3)需要合并前面的版本(v2),这种合并方式称为典型合并;==由于典型合并存在于非同轴

  • 如图所示:

需要注意的是,如果是同轴开发,后面版本的内容肯定包含前面版本的内容,因此==同轴开发不存在典型合并==,只存在快进合并。另外,非同轴开发也存在快进合并,例如上图中的③步骤中,前面的版本(v2)想要合并后面的版本(v3),这也是一种快进合并。

总结:

  • 同轴开发:只存在快进合并
  • 非同轴开发:
    • 前面的版本合并后面的版本:快进合并
    • 后面的版本合并前面的版本:典型合并

Tips:典型合并必定是非同轴,快进合并可以是非同轴也可以是同轴。

【示例代码】

初始化项目:

shell 复制代码
rm -rf .git ./*
git init
echo '用户名+密码登录' >> project.txt
git add ./
git commit -m '用户名+密码登录功能完成' ./

①创建login分支:

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git branch login

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)			# 查看不同轴的日志需要加上--graph
$ git log --oneline --all --graph
* 7c0b2a3 (HEAD -> master, login) 用户名+密码登录功能完成

②切换到login分支,使用login分支进行开发:

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git checkout login
Switched to branch 'login'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (login)
$ echo "集成QQ登录" >> project.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (login)
$ git commit -m "集成QQ登录" ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (login)
$ git log --oneline --all --graph
* 9c968e0 (HEAD -> login) 集成QQ登录
* 7c0b2a3 (master) 用户名+密码登录功能完成

③切换回master分支添加注册功能:

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (login)
$ git checkout master
Switched to branch 'master'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 查看master分支的project.txt文件,代码并没有合并过来
$ cat project.txt
用户名+密码登录
添加头像上传功能

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 查看日志,处于不同轴开发路径
$ git log --oneline --all --graph
* fecdb41 (HEAD -> master) 添加头像上传功能
| * 9c968e0 (login) 集成QQ登录
|/
* 7c0b2a3 用户名+密码登录功能完成

④将login分支的合并到master分支:产生代码冲突

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git merge login
Auto-merging project.txt
CONFLICT (content): Merge conflict in project.txt
Automatic merge failed; fix conflicts and then commit the result.

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master|MERGING)
$ cat project.txt
用户名+密码登录
<<<<<<< HEAD
添加头像上传功能
=======
集成QQ登录
>>>>>>> login

解决代码冲突:

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master|MERGING)
$ cat project.txt
用户名+密码登录
<<<<<<< HEAD
添加头像上传功能
=======
集成QQ登录
>>>>>>> login

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master|MERGING)
$ vi project.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master|MERGING)
$ cat project.txt
用户名+密码登录
添加头像上传功能
集成QQ登录

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master|MERGING)
$ git commit -a -m "合并login分支,并解决代码冲突"
[master 9ccdde2] 合并login分支,并解决代码冲突

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline --all --graph
*   9ccdde2 (HEAD -> master) 合并login分支,并解决代码冲突			# 解决冲突后提交产生一次新的版本
|\
| * 9c968e0 (login) 集成QQ登录
* | fecdb41 添加头像上传功能
|/
* 7c0b2a3 用户名+密码登录功能完成

经过上一次合并后,master与login属于同轴了,当切换为login分支,可以使用快进合并来合并master分支;

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)			# 切换为login分支
$ git checkout login
Switched to branch 'login'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (login)			# login分支的代码
$ cat project.txt
用户名+密码登录
集成QQ登录

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (login)
$ git log --oneline --all --graph
*   9ccdde2 (master) 合并login分支,并解决代码冲突
|\
| * 9c968e0 (HEAD -> login) 集成QQ登录			
* | fecdb41 添加头像上传功能
|/
* 7c0b2a3 用户名+密码登录功能完成

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (login)			# 合并master分支
$ git merge master
Updating 9c968e0..9ccdde2
Fast-forward
 project.txt | 1 +
 1 file changed, 1 insertion(+)

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (login)			# 查看login分支的代码,发现master的代码被合并过来了
$ cat project.txt
用户名+密码登录
添加头像上传功能
集成QQ登录

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (login)
$ git log --oneline --all --graph
*   9ccdde2 (HEAD -> login, master) 合并login分支,并解决代码冲突
|\
| * 9c968e0 集成QQ登录
* | fecdb41 添加头像上传功能
|/
* 7c0b2a3 用户名+密码登录功能完成

1.5 Git的代码冲突

1.5.1 Git的代码冲突的分类与特点

Git存在代码冲突的情况分为协同开发时的代码冲突分支合并时的代码冲突 ;其中分支合并时的代码冲突又分为快进合并代码冲突典型合并代码冲突

另外还需要注意的是,Git与SVN的代码冲突逻辑不一致。

Tips:我们本章主要讨论分支合并时的代码冲突。

  • 在SVN中:只要两个用户编辑的不是同一行则不会出现代码冲突。
  • 在Git中:只要两个文件内容不一致,在合并分支时必定会产生冲突,无论两个文件是否编辑的是同一行。不过具体情况还得细分如下:
    • 快进合并:两个分支属于不同轴开发,前面版本合并后面版本时,只要两个文件不一致,合并时必定出现冲突,不管两个分支编辑的是不是同一行。
    • 典型合并:由于典型合并两个分支属于不同的开发轴,而后面的分支想要合并前面的分支,两个文件必定不一致,因此典型合并必定出现代码冲突。

Tips:同轴开发的快进合并是不会产生冲突的。

【SVN代码冲突模拟】

SVN的情况:两个用户编辑的代码是同一行则出现冲突

xiaohui xiaolan
创建abc.txt
内容为:
111
222
执行add
执行commit
执行update
修改内容为:
111aaa
222
执行commit
修改内容为:
111bbb
222
执行update(冲突)

如果"xiaolan"用户最后更改的文件内容为:

shell 复制代码
111
222bbb

然后再执行update,则不会出现代码冲突,因为SVN判断代码冲突的代码单元为行,即:两个用户编辑的不是同一行就不会出现代码冲突。

1.5.2 快进合并代码冲突

快进合并属于前面的版本合并后面的版本,需要注意的是==前面的版本并非是"旧版本",后面的版本并非是"新版本"==,这主要取决于是否是同轴开发。对于同轴开发,后面的版本自然是新版本,其内容肯定包含前面版本的内容。但对于非同轴开发,后面的版本内容大概率是不包含前面的版本内容的;

在同轴开发情况下,快进合并不会产生代码冲突,但如果在非同轴开发情况下,快进合并就会产生代码冲突了。我们观察下列操作:

master分支 test分支
创建abc.txt
内容为:
111
222
执行add
执行commit
创建分支 -- 此时test分支的abc.txt的内容也是111、222
修改内容为:
111aaa
222
执行commit
切换到test分支
修改内容为:
111
222bbb
执行commit -- 此时test分支和master分支已经不同轴了
切换回master分支,合并test分支,属于快进合并,但会出现冲突

Tips:注意,上述操作在SVN中则不会认为是冲突,因为SVN判断是否冲突的标准是操作同一个文件的同一行记录;

  • 快进合并冲突演示:

【使用Git演示代码冲突】

①初始化项目

shell 复制代码
rm -rf .git ./*
git init

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ vi aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ cat aaa.txt
111
222

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git add ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git commit -m "111 222" ./

②创建test分支

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git branch test

③继续使用master分支开发

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline --all --graph
* d40d605 (HEAD -> master, test) 111 222

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ vi aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ cat aaa.txt
111aaa
222

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git commit -m "aaa" ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline --all --graph
* 09fe9cb (HEAD -> master) aaa
* d40d605 (test) 111 222

④切换到test分支开发

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git checkout test
Switched to branch 'test'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (test)
$ vi aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (test)			# 注意,和master分支修改的文件内容不是同一行
$ cat aaa.txt
111
222bbb

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (test)
$ git commit -m "bbb" ./
[test dfe1b42] bbb
 1 file changed, 1 insertion(+), 1 deletion(-)

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (test)			# 查看日志,test与master分支已经不同轴
$ git log --oneline --all --graph	
* dfe1b42 (HEAD -> test) bbb
| * 09fe9cb (master) aaa
|/
* d40d605 111 222

⑤切换回master分支,合并test分支,出现冲突:

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (test)			# 切换回master分支
$ git checkout master
Switched to branch 'master'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 合并test分支,属于快进合并,但是会产生冲突
$ git merge test
Auto-merging aaa.txt
CONFLICT (content): Merge conflict in aaa.txt
Automatic merge failed; fix conflicts and then commit the result.

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master|MERGING)
$ cat aaa.txt
<<<<<<< HEAD
111aaa
222
=======
111
222bbb
>>>>>>> test

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master|MERGING)
$ vi aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master|MERGING)
$ cat aaa.txt
111aaa
222bbb


Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master|MERGING)			# 解决冲突
$ git commit -a -m "master合并test分支"
[master adaec78] master合并test分支

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline --all --graph
*   adaec78 (HEAD -> master) master合并test分支
|\
| * 7b14536 (test) bbb
* | eeca4c9 aaa
|/
* 5f41035 111 222

需要注意的是:此时切换回test分支,合并master分支内容不会出现冲突(属于快进合并,但是内容不一致,但不会出现冲突):因为master分支的内容是刚刚解决了冲突之后用户确定了的内容。

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git checkout test
Switched to branch 'test'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline --all --graph
*   adaec78 (master) master合并test分支
|\
| * 7b14536 (HEAD -> test) bbb
* | eeca4c9 aaa
|/
* 5f41035 111 222

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (test)
$ cat aaa.txt
111
222bbb

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (test)
$ git merge master
Updating 7b14536..adaec78
Fast-forward
 aaa.txt | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (test)
$ cat aaa.txt
111aaa
222bbb

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline --all --graph
*   adaec78 (HEAD -> test, master) master合并test分支
|\
| * 7b14536 () bbb
* | eeca4c9 aaa
|/
* 5f41035 111 222

1.5.3 典型合并代码冲突

典型合并时的代码冲突就是我们之前遇到的那种情况,典型合并属于不同轴的开发,并且后面版本需要合并前面版本的情况,由于不同轴,因此后面版本的内容不一定包含前面版本的内容,由于Git代码冲突的特点,因此典型合并必定会出现代码冲突,因为后面版本的内容不可能和前面版本内容一致。

我们观察下列操作:

master分支 test分支
创建abc.txt
内容为:
111
222
执行add
执行commit
创建分支 -- 此时test分支的abc.txt的内容也是111、222
修改内容为:
111aaa
222
执行commit
切换到test分支
修改内容为:
111
222bbb
执行commit -- 此时test分支和master分支已经不同轴了
合并master分支的代码,属于典型合并,出现代码冲突。
  • 典型合并冲突演示:

【使用Git演示代码冲突】

①初始化项目:

shell 复制代码
rm -rf .git ./*
git init

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ vi aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ cat aaa.txt
111
222

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git add ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git commit -m "111 222" ./

②创建test分支

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git branch test

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline --all --graph
* 45fa06b (HEAD -> master, test) 111 222

③继续使用master分支开发

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git checkout master
Already on 'master'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ vi aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ cat aaa.txt
111aaa
222

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git commit -m "aaa" ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline --all --graph
* c86283b (HEAD -> master) aaa
* 45fa06b (test) 111 222

④切换到test分支,继续开发

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git checkout test
Switched to branch 'test'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (test)
$ vi aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (test)
$ cat aaa.txt
111
222bbb

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (test)
$ git commit -m "bbb" ./
[test e61df5a] bbb
 1 file changed, 1 insertion(+), 1 deletion(-)

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (test)
$ git log --oneline --all --graph
* e61df5a (HEAD -> test) bbb
| * c86283b (master) aaa
|/
* 45fa06b 111 222

⑤使用test分支合并master分支(属于典型合并)

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (test)
$ git merge master
Auto-merging aaa.txt
CONFLICT (content): Merge conflict in aaa.txt
Automatic merge failed; fix conflicts and then commit the result.

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (test|MERGING)
$ cat aaa.txt
<<<<<<< HEAD
111
222bbb
=======
111aaa
222
>>>>>>> master

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (test|MERGING)
$ vi aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (test|MERGING)
$ cat aaa.txt
111aaa
222bbb

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (test|MERGING)
$ git commit -a -m "合并master分支,并解决冲突"
[test 448d619] 合并master分支,并解决冲突

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (test)
$ git log --oneline --all --graph
*   448d619 (HEAD -> test) 合并master分支,并解决冲突
|\
| * c86283b (master) aaa
* | e61df5a bbb
|/
* 45fa06b 111 222

同样的,如果此时切换回master分支,合并test分支虽然属于快进合并并且不同轴,但合并依旧不会出现冲突,因为test分支的内容是解决冲突并且经过用户确认之后的内容。

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (test)
$ git log --oneline --all --graph
*   448d619 (HEAD -> test) 合并master分支,并解决冲突
|\
| * c86283b (master) aaa
* | e61df5a bbb
|/
* 45fa06b 111 222

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (test)
$ git checkout master
Switched to branch 'master'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ cat aaa.txt
111aaa
222

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git merge test
Updating c86283b..448d619
Fast-forward
 aaa.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ cat aaa.txt
111aaa
222bbb

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline --all --graph
*   448d619 (HEAD -> master, test) 合并master分支,并解决冲突
|\
| * c86283b aaa
* | e61df5a bbb
|/
* 45fa06b 111 222

1.6 Git的分支状态存储

有时,当你在项目的一部分上已经工作一段时间后,所有东西都进入了混乱的状态,而这时你想要切换到另一个分支做一点别的事情。 但你必须将当前工作空间所做的操作提交到版本库,否则Git不允许切换分支;但是当前的操作还不足以生成一次版本快照,此时,我们就可以使用Git的存储功能,将当前工作状态存储起来,然后再切换到其他分支工作,最终工作完毕后切回当前分支,从Git存储中取出之前的工作内容;

1.6.1 Git存储引入

【初始化项目环境】

shell 复制代码
rm -rf ./* .git
git init
echo '111-master' >> aaa.txt
git add ./
git commit -m '111-master' ./
git branch b1
git checkout b1
echo '111-b1' >> aaa.txt
git add ./
git commit -m '111-b1' ./
echo '222-b1' >> aaa.txt
git commit -m '222-b1' ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)		# 查看分支详情
$ git log --oneline --graph --all
* 01ca592 (HEAD -> b1) 222-b1			# b1的位置
* 1337456 111-b1
* f828bbd (master) 111-master			# master的位置

编辑文件:

shell 复制代码
echo '333-b1' >> aaa.txt

编辑完了后还不想提交,此时接收到了新的"临时任务",想要切换到其他分支继续操作,发现切换失败:

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)				# 查看git状态
$ git status
On branch b1
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   aaa.txt

no changes added to commit (use "git add" and/or "git commit -a")			# 有修改操作还未提交

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)				# 切换到master失败
$ git checkout master
error: Your local changes to the following files would be overwritten by checkout:
        aaa.txt
Please commit your changes or stash them before you switch branches.
Aborting

1.6.2 Git存储的使用

1) 使用存储状态
  • 语法:
shell 复制代码
git stash list 		# 查看当前Git中存储的所有状态
git stath 			# 将当前状态保存
git stash apply {stashName}		# 根据存储名称读取Git存储
git stash drop {stashName}		# 根据存储名称删除Git存储

【使用Git存储将当前状态存储起来】

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)		# 查看当前Git存储列表,发现列表为空
$ git stash list

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)		# 使用Git存储
$ git stash
warning: LF will be replaced by CRLF in aaa.txt.
The file will have its original line endings in your working directory
Saved working directory and index state WIP on b1: 01ca592 222-b1

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)
$ git stash list
stash@{0}: WIP on b1: 01ca592 222-b1

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)			# 使用Git将当前状态存储起来后,文件内容变为了未更改前的内容
$ cat aaa.txt
111-master
111-b1
222-b1

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)			# 再次查看git的状态,发现工作空间正常
$ git status
On branch b1
nothing to commit, working tree clean

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)			# 查看日志,发现使用Git存储也会产生一次日志
$ git log --oneline --graph --all
*   082f406 (refs/stash) WIP on b1: 01ca592 222-b1
|\
| * c613227 index on b1: 01ca592 222-b1
|/
* 01ca592 (HEAD -> b1) 222-b1
* 1337456 111-b1
* f828bbd (master) 111-master

【当前状态被Git存储了,当前的工作空间也是正常的,因此可以切换到其他分支继续操作】

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)			# 切换分支到master
$ git checkout master
Switched to branch 'master'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 查看master分支的内容
$ cat aaa.txt
111-master

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ echo "222-master" >> aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		
$ git commit -m '222-master' ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline --graph --all
* 4974e13 (HEAD -> master) 222-master
| *   082f406 (refs/stash) WIP on b1: 01ca592 222-b1
| |\
| | * c613227 index on b1: 01ca592 222-b1
| |/
| * 01ca592 (b1) 222-b1
| * 1337456 111-b1
|/
* f828bbd 111-master
2) 读取存储状态

等到"临时任务"处理完后,我们可以切换回test分支,并将上一次使用Git存储的状态读取出来

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)			# 切换回b1分支
$ git checkout b1
Switched to branch 'b1'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)				# 查看文件内容,依旧是没有编辑前的状态
$ cat aaa.txt
111-master
111-b1
222-b1

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)				# 查看Git存储的状态
$ git stash list
stash@{0}: WIP on b1: 01ca592 222-b1

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)				# 读取状态
$ git stash apply stash@{0}
On branch b1
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   aaa.txt

no changes added to commit (use "git add" and/or "git commit -a")			# 读取成功后回到我们当初的状态(当前工作空间未提交)

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)				# 查看文件内容,将文件内容还原回来了
$ cat aaa.txt
111-master
111-b1
222-b1
333-b1

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)
$ git commit -m "333-b1" ./
[b1 1f0ebea] 333-b1
 1 file changed, 1 insertion(+)

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)
$ git log --oneline --graph --all
* 1f0ebea (HEAD -> b1) 333-b1
| * 4974e13 (master) 222-master
| | * 082f406 (refs/stash) WIP on b1: 01ca592 222-b1
| |/|
|/| |
| | * c613227 index on b1: 01ca592 222-b1
| |/
|/|
* | 01ca592 222-b1
* | 1337456 111-b1
|/
* f828bbd 111-master
3) 存储状态的删除

Git存储被读取之后状态并不会被删除,我们可以手动删除存储状态

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)			# 查看Git存储状态,发现依旧存在
$ git stash list
stash@{0}: WIP on b1: 01ca592 222-b1

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)			# 手动删除状态
$ git stash drop stash@{0}
Dropped stash@{0} (082f40626ab35cf6b1bd413e634e0a1a946824aa)

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)			# 查看Git存储的状态,发现没有了
$ git stash list

1.6.3 Git存储的原理

  • 使用Git存储:

Git存储的原理其实是在使用git stash命令后,Git直接将当前工作状态的更改添加到暂存区,然后提交,中途生成了Blob对象、Tree对象、Commit对象等三个对象;其中Commit对象会生成2次,第一次指向原来的Tree对象,第二次指向新的Tree对象,之后再将暂存区改回原来的样子(执行git stash命令之前的样子)

由于当前工作空间的操作均已提交,因此当前工作空间的状态自然为nothing to commit,然后就可以切换到其他分支了;

当使用git stash命令以后,会产生两个Commit对象,其还会再.git/refs/目录创建一个名为stash的文件,该文件保存着最新Commit对象的hash值;

  • 读取Git存储状态的原理:

当使用git stash apply {stashName}命令读取Git存储状态时,其底层其实就是读取到stash文件中的Commit对象,通过该Commit对象找到执行git stash命令后生成的Blob对象,读取该Blob对象的内容写入当前工作空间,达到还原工作空间的目的。

  • 删除Git存储状态的原理:

在Git日志中查询不到了,然后将git/refs/stash文件删除掉

【准备环境】

shell 复制代码
rm -rf ./* .git
git init
echo '111' >> aaa.txt
git add ./
git commit -m "111" ./
git branch b1
git checkout b1

.git/objects/8f/96f2f60c766a6a6b78591e06e6c1529c0ad9af
$ find .git/objects/ -type f
.git/objects/58/c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c			# Blob对象
.git/objects/7d/811c6d8fa7794fc7a0a2371a4cf197e8cfb47d			# Commit对象
.git/objects/8f/96f2f60c766a6a6b78591e06e6c1529c0ad9af			# Tree对象

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)			# 查看当前暂存区
$ git ls-files -s
100644 58c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c 0       aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)
$ git log --oneline --all --graph
* 7d811c6 (HEAD -> b1, master) 111
1) 使用存储状态的原理

【编辑文件】

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)
$ echo "222" >> aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)
$ git status
On branch b1
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   aaa.txt

no changes added to commit (use "git add" and/or "git commit -a")


Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)			# 使用Git存储
$ git stash
warning: LF will be replaced by CRLF in aaa.txt.
The file will have its original line endings in your working directory
Saved working directory and index state WIP on b1: 7d811c6 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)
$ git ls-files -s
100644 58c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c 0       aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)			# 暂存区没有变化
$ git cat-file -p 58c9bdf9
111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)
$ find .git/objects/ -type f
.git/objects/58/c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c			# Blob对象.v1
.git/objects/70/3a3923a3f4d516543ba3e6e9182467f31b328c			# Tree对象.v2
.git/objects/7d/811c6d8fa7794fc7a0a2371a4cf197e8cfb47d			# Commit对象.v1
.git/objects/8f/96f2f60c766a6a6b78591e06e6c1529c0ad9af			# Tree对象.v1
.git/objects/99/11efb0f75f3280b2e8581bd83724e9a7a10528			# Commit对象.v2
.git/objects/a3/0a52a3be2c12cbc448a5c9be960577d13f4755			# Blob对象.v2
.git/objects/b3/e1f5cd5d92a906cff3dfc4816d6e22c72afffe			# Commit对象.v3

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)		# 查看stash文件,保存的是最新Commit对象的hash值
$ cat .git/refs/stash
b3e1f5cd5d92a906cff3dfc4816d6e22c72afffe

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)		# 查看Blob对象.v2
$ git cat-file -p a30a52a3be2c12cbc448a5c9be960577d13f4755
111
222

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)		# 查看Tree对象.v2
$ git cat-file -p 703a3923a3f4d516543ba3e6e9182467f31b328c
100644 blob a30a52a3be2c12cbc448a5c9be960577d13f4755    aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)		# 查看Commit对象.v2
$ git cat-file -p 9911efb0f75f3280b2e8581bd83724e9a7a10528
tree 8f96f2f60c766a6a6b78591e06e6c1529c0ad9af		# 包裹的是原来的Tree对象(v1版本)
parent 7d811c6d8fa7794fc7a0a2371a4cf197e8cfb47d		# 指向的是Commit对象.v1
author xiaohui <xiaohui@aliyun.com> 1697278938 +0800
committer xiaohui <xiaohui@aliyun.com> 1697278938 +0800

index on b1: 7d811c6 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)		# 查看Commit对象.v3
$ git cat-file -p b3e1f5cd5d92a906cff3dfc4816d6e22c72afffe
tree 703a3923a3f4d516543ba3e6e9182467f31b328c		# 包裹的是新的Tree对象(v3)
parent 7d811c6d8fa7794fc7a0a2371a4cf197e8cfb47d		# 指向Commit对象.v1
parent 9911efb0f75f3280b2e8581bd83724e9a7a10528		# 指向Commit对象.v2
author xiaohui <xiaohui@aliyun.com> 1697278938 +0800
committer xiaohui <xiaohui@aliyun.com> 1697278938 +0800

WIP on b1: 7d811c6 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)		# 查看日志,发现生成了两个Commit对象
$ git log --oneline --all --graph
*   b3e1f5c (refs/stash) WIP on b1: 7d811c6 111		# Commit对象.v3
|\
| * 9911efb index on b1: 7d811c6 111				# Commit对象.v2
|/
* 7d811c6 (HEAD -> b1, master) 111					# HEAD指针还是指向b1
2) 读取存储状态的原理
shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)			# 由于当前是Git
$ git checkout master
Switched to branch 'master'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git checkout b1
Switched to branch 'b1'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)			# 读取Git存储
$ git stash apply stash@{0}
On branch b1
Changes not staged for commit:		
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   aaa.txt

no changes added to commit (use "git add" and/or "git commit -a")		# 工作空间状态恢复成原来的状态了
3) 删除存储状态的原理
shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)
$ git stash list
stash@{0}: WIP on b1: 7d811c6 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)		# 删除Git存储状态
$ git stash drop stash@{0}
Dropped stash@{0} (b3e1f5cd5d92a906cff3dfc4816d6e22c72afffe)

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)		# 发现stash文件已经被删除
$ ll .git/refs/
total 0
drwxr-xr-x 1 Adminstrator 197121 0 Oct 14 18:22 heads/
drwxr-xr-x 1 Adminstrator 197121 0 Oct 14 18:20 tags/

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)
$ git log --oneline --all --graph
* 7d811c6 (HEAD -> b1, master) 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)
$ find .git/objects/ -type f
.git/objects/58/c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c			# Blob对象.v1
.git/objects/70/3a3923a3f4d516543ba3e6e9182467f31b328c			# Tree对象.v2
.git/objects/7d/811c6d8fa7794fc7a0a2371a4cf197e8cfb47d			# Commit对象.v1
.git/objects/8f/96f2f60c766a6a6b78591e06e6c1529c0ad9af			# Tree对象.v1
.git/objects/99/11efb0f75f3280b2e8581bd83724e9a7a10528			# Commit对象.v2
.git/objects/a3/0a52a3be2c12cbc448a5c9be960577d13f4755			# Blob对象.v2
.git/objects/b3/e1f5cd5d92a906cff3dfc4816d6e22c72afffe			# Commit对象.v3

1.7 Git打标签

1.7.1 标签的语法与介绍

像其他版本控制系统(VCS)一样,Git 可以给仓库历史中的某一个提交打上标签,以示重要。 比较有代表性的是人们会使用这个功能来标记发布结点( v1.0v2.0 等等)。

  • 创建标签:
shell 复制代码
git tag {tagName}						# 以当前状态创建标签	(轻量标签)
git tag {tagName} {commitHash}			# 以指定的提交对象来创建标签(轻量标签)

git tag {tagName} {commitHash} -m {注释}		# 以当前状态创建标签	(附注标签)
git tag {tagName} {commitHash} -m {注释}		# 以指定的提交对象来创建标签(附注标签)
  • 查看标签:
shell 复制代码
git tag						# 查看所有标签
git show {tagName}			# 查看特定标签
  • 删除标签:
shell 复制代码
git tag -d {tagName}
  • 检出标签:
shell 复制代码
git checkout {tagName}						# 检出到指定标签位置,会出现"头指针分离"现象
git checkout -b {branchName} {tagName}		# 检出到指定标签位置,并在此位置创建分支

Git 支持两种标签:轻量标签(lightweight)与附注标签(annotated)。

  • 轻量标签:轻量标签指向某个版本的提交对象的hash值;
  • 附注标签:会在Git数据库中创建一个全新的Git对象------tag对象,该tag对象保存了这个版本的提交对象的hash值,同时还存储了一些有关于tag本身的日志信息;

创建好的标签存储在.git/refs/tags目录;

1.7.2 标签的使用

【初始化版本库】

shell 复制代码
rm -rf ./* .git
git init
echo "111" >> aaa.txt
git add ./
git commit -m '111' ./
echo "222" >> aaa.txt
git add ./
git commit -m '222' ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline
794c1a9 (HEAD -> master) 222
a25c873 111

【创建标签】

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 查看当前有多少标签
$ git tag

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 创建一个附注标签
$ git tag v1.0 -m "这是我的1.0版本"

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 查看标签
$ git tag
v1.0

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 查看日志
$ git log --oneline
794c1a9 (HEAD -> master, tag: v1.0) 222
a25c873 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 创建一个轻量标签
$ git tag v1.2

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 查看标签
$ git tag
v1.0
v1.2

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 查看日志
$ git log --oneline
794c1a9 (HEAD -> master, tag: v1.2, tag: v1.0) 222
a25c873 111

查看.git/refs/tags目录:

v1.0是一个附注标签,创建附注标签时会创建一个新的tag对象,该tag对象保存了所引用的提交对象hash值即一些其他日志信息;

v1.2是一个轻量标签,创建轻量标签时并不会创建一个tag对象,仅仅是将这个表情所指向的提交对象的hash值保存下来;

查看轻量标签和附注标签的内容:

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ ll .git/refs/tags/
total 2
-rw-r--r-- 1 Adminstrator 197121 41 Oct 16 15:38 v1.0			# 创建了两个标签文件
-rw-r--r-- 1 Adminstrator 197121 41 Oct 16 15:38 v1.2

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# v1.0(附注标签)保存的是tag对象的hash值
$ cat .git/refs/tags/v1.0
8223942a63f358c9f958d39c06f85a7837a29526

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# v1.2(轻量标签)保存的是提交对象的hash值
$ cat .git/refs/tags/v1.2
794c1a9c132d391e31dfd3c37b16aa948c46d3b1

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 是一个tag对象
$ git cat-file -t 8223942a63f358c9f958d39c06f85a7837a29526
tag

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 是一个commit对象
$ git cat-file -t 794c1a9c132d391e31dfd3c37b16aa948c46d3b1
commit

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 轻量标签的内容(就是一个提交对象)
$ git cat-file -p 8223942a63f358c9f958d39c06f85a7837a29526
object 794c1a9c132d391e31dfd3c37b16aa948c46d3b1
type commit
tag v1.0
tagger xiaohui <xiaohui@aliyun.com> 1697441891 +0800

这是我的1.0版本

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 附注标签的内容
$ git cat-file -p 794c1a9c132d391e31dfd3c37b16aa948c46d3b1
tree 703a3923a3f4d516543ba3e6e9182467f31b328c
parent a25c8731c03a7c07dc574d8809f6ad7a69c09f27
author xiaohui <xiaohui@aliyun.com> 1697441871 +0800
committer xiaohui <xiaohui@aliyun.com> 1697441871 +0800

222

【查看标签】

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)			# 查看v1.0标签(附注标签)
$ git show v1.0
tag v1.0
Tagger: xiaohui <xiaohui@aliyun.com>
Date:   Mon Oct 16 15:38:11 2023 +0800

这是我的1.0版本

commit 794c1a9c132d391e31dfd3c37b16aa948c46d3b1 (HEAD -> master, tag: v1.2, tag: v1.0)
Author: xiaohui <xiaohui@aliyun.com>
Date:   Mon Oct 16 15:37:51 2023 +0800

    222

diff --git a/aaa.txt b/aaa.txt
index 58c9bdf..a30a52a 100644
--- a/aaa.txt
+++ b/aaa.txt
@@ -1 +1,2 @@
 111
+222

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)			# 查看v1.2标签(轻量标签)
$ git show v1.2
commit 794c1a9c132d391e31dfd3c37b16aa948c46d3b1 (HEAD -> master, tag: v1.2, tag: v1.0)
Author: xiaohui <xiaohui@aliyun.com>
Date:   Mon Oct 16 15:37:51 2023 +0800

    222

diff --git a/aaa.txt b/aaa.txt
index 58c9bdf..a30a52a 100644
--- a/aaa.txt
+++ b/aaa.txt
@@ -1 +1,2 @@
 111
+222

【使用master分支继续开发】

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ echo "333" >> ccc.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git add ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git commit -m '333' ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline --all
472d1e1 (HEAD -> master) 333
794c1a9 (tag: v1.2, tag: v1.0) 222
a25c873 111

【检出标签】

如果你想查看某个标签所指向的文件版本,可以使用 git checkout 命令, 虽然这会使你的仓库处于"分离头指针(detached HEAD)"的状态------这个状态有些不好的副作用:

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline --all
472d1e1 (HEAD -> master) 333
794c1a9 (tag: v1.2, tag: v1.0) 222
a25c873 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git checkout v1.0
Note: switching to 'v1.0'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at 794c1a9 222

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace ((v1.0))		# 记得使用-all,不然不会显示HEAD之后的内容
$ git log --oneline --all
472d1e1 (master) 333
794c1a9 (HEAD, tag: v1.2, tag: v1.0) 222		# HEAD指针指向的位置并没有指针
a25c873 111

在"分离头指针"状态下,如果你做了某些更改然后提交它们,标签不会发生变化, 但你的新提交将不属于任何分支,并且将无法访问,除非通过确切的提交哈希才能访问。 因此,如果你需要进行更改,比如你要修复旧版本中的错误,那么通常需要创建一个新分支:

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace ((v1.0))
$ git log --oneline --all
472d1e1 (master) 333
794c1a9 (HEAD, tag: v1.2, tag: v1.0) 222
a25c873 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace ((v1.0))		# 查看当前HEAD指针位置的工作空间内容,发现并没有333
$ cat aaa.txt
111
222

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace ((v1.0))		# 追加内容1010
$ echo "1010" >> aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace ((v1.0))
$ git commit -m "1010" ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace ((830c1d5...))
$ git log --oneline --all --graph
* 830c1d5 (HEAD) 1010
| * 472d1e1 (master) 333
|/
* 794c1a9 (tag: v1.2, tag: v1.0) 222
* a25c873 111

【在当前位置创建分支】

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace ((830c1d5...))
$ git branch b1

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace ((830c1d5...))		# 在当前位置创建b1指针,但HEAD指针并没有执行b1
$ git log --oneline --all --graph
* 830c1d5 (HEAD, b1) 1010
| * 472d1e1 (master) 333
|/
* 794c1a9 (tag: v1.2, tag: v1.0) 222
* a25c873 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace ((830c1d5...))		# 切换到b1分支
$ git checkout b1
Switched to branch 'b1'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)
$ git log --oneline --all --graph
* 830c1d5 (HEAD -> b1) 1010
| * 472d1e1 (master) 333
|/
* 794c1a9 (tag: v1.2, tag: v1.0) 222
* a25c873 111

【检出标签创建分支】

为了避免出现"分离头指针"现象,因此在检出标签时通常会创建一个新的分支:

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)
$ git log --oneline --all --graph
* 830c1d5 (HEAD -> b1) 1010
| * 472d1e1 (master) 333
|/
* 794c1a9 (tag: v1.2, tag: v1.0) 222
* a25c873 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)		# 查看所有标签
$ git tag
v1.0
v1.2

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b1)		# 检出到v1.2标签,并且在这个位置创建一个新的分支
$ git checkout -b b2 v1.2
Switched to a new branch 'b2'

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b2)
$ git log --oneline --all --graph
* 830c1d5 (b1) 1010
| * 472d1e1 (master) 333
|/
* 794c1a9 (HEAD -> b2, tag: v1.2, tag: v1.0) 222		# HEAD指针指向b2分支了
* a25c873 111

【删除标签】

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b2)		# 查看当前所有标签
$ git tag
v1.0
v1.2

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b2)		# 删除v1.0标签
$ git tag  -d v1.0
Deleted tag 'v1.0' (was 8223942)

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b2)		# 查看当前所有标签	
$ git tag
v1.2

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b2)		# 查看日志
$ git log --oneline --all --graph
* 830c1d5 (b1) 1010
| * 472d1e1 (master) 333
|/
* 794c1a9 (HEAD -> b2, tag: v1.2) 222							# 发现v1.0标签被删除了
* a25c873 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b2)		# 查看v1.2标签
$ git tag -d v1.2
Deleted tag 'v1.2' (was 794c1a9)

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b2)		# 查看当前所有标签	
$ git tag

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (b2)		# 查看日志
$ git log --oneline --all --graph
* 830c1d5 (b1) 1010
| * 472d1e1 (master) 333
|/
* 794c1a9 (HEAD -> b2) 222		# 发现v1.2标签也被删除了
* a25c873 111

二、Git数据恢复与还原

2.1 Git的还原

在任何一个阶段,你都有可能想要撤消某些操作。 在本章,我们将会学习几个撤消你所做修改的基本工具。 注意,有些撤消操作是不可逆的。 这是在使用 Git 的过程中,会因为操作失误而导致之前的工作丢失的少有的几个地方之一。

有时候我们提交完了才发现漏掉了几个文件没有添加,或者提交信息写错了。 此时,可以运行带有 --amend 选项的提交命令来重新提交:

  • Git还原的语法:
shell 复制代码
git restore {文件|目录}					# 还原工作空间
git restore --staged {文件|目录}		# 仅还原暂存区,该命令不会还原工作空间

git commit --amend -m "注释"				# 还原(重置)提交对象信息

2.1.1 还原工作空间

【项目初始化】

shell 复制代码
rm -rf ./* .git
git init
echo '111' >> aaa.txt
git add ./
git commit -m '111' ./

【编辑文件,然后使用Git还原】

shell 复制代码
Administration@XiaoMi MINGW64 ~/Desktop/work (master)				# 给文件追加内容(改变工作空间的内容)
$ echo '222' >> aaa.txt

Administration@XiaoMi MINGW64 ~/Desktop/work (master)				# 查看被修改的文件
$ cat aaa.txt	
111
222

Administration@XiaoMi MINGW64 ~/Desktop/work (master)				# 查看当前状态
$ git status
On branch master
Changes not staged for commit:										# 有更改未提交
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   aaa.txt

no changes added to commit (use "git add" and/or "git commit -a")
	
Administration@XiaoMi MINGW64 ~/Desktop/work (master)				# 撤销对工作空间的修改
$ git restore aaa.txt

Administration@XiaoMi MINGW64 ~/Desktop/work (master)				# 再次查看文件(发现之前的更改已经被撤销)
$ cat aaa.txt
111

Administration@XiaoMi MINGW64 ~/Desktop/work (master)				# 再次查看当前状态
$ git status
On branch master
nothing to commit, working tree clean								# 工作空间状态正常

2.1.2 还原暂存区

【项目初始化】

shell 复制代码
rm -rf ./* .git
git init
echo '111' >> aaa.txt
git add ./
git commit -m '111' ./

【查看当前暂存区】

shell 复制代码
Administration@XiaoMi MINGW64 ~/Desktop/work (master)					# 查看当前暂存区
$ git ls-files -s
100644 58c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c 0       aaa.txt

Administration@XiaoMi MINGW64 ~/Desktop/work (master)					# 查看Blob对象的内容
$ git cat-file -p 58c9bdf9
111

【编辑文件】

shell 复制代码
Administration@XiaoMi MINGW64 ~/Desktop/work (master)					# 编辑文件
$ echo "222" > aaa.txt

Administration@XiaoMi MINGW64 ~/Desktop/work (master)					# 添加到暂存区
$ git add ./

Administration@XiaoMi MINGW64 ~/Desktop/work (master)					# 查看暂存区的内容(已经被更新了)
$ git ls-files -s
100644 c200906efd24ec5e783bee7f23b5d7c941b0c12c 0       aaa.txt

Administration@XiaoMi MINGW64 ~/Desktop/work (master)					# 查看Blob对象的内容,发现已经修改
$ git cat-file -p c2009
222

【还原暂存区】

shell 复制代码
Administration@XiaoMi MINGW64 ~/Desktop/work (master)				# 查看当前状态
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)					# 有更改还未提交
        modified:   aaa.txt


Administration@XiaoMi MINGW64 ~/Desktop/work (master)				# 还原暂存区
$ git restore --staged aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)	# 工作空间还没有被还原
$ cat aaa.txt
222

Administration@XiaoMi MINGW64 ~/Desktop/work (master)				# 再次查看暂存区
$ git ls-files -s
100644 58c9bdf9d017fcd178dc8c073cbfcbb7ff240d6c 0       aaa.txt

Administration@XiaoMi MINGW64 ~/Desktop/work (master)				# 查看Blob对象的内容,发现已经还原
$ git cat-file -p 58c9
111

Administration@XiaoMi MINGW64 ~/Desktop/work (master)				# 查看当前状态,已经还原成未添加到暂存区的状态了
$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   aaa.txt

no changes added to commit (use "git add" and/or "git commit -a")

【继续还原工作空间】

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git restore aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)	# 工作空间也被还原了
$ cat aaa.txt
111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git status
On branch master
nothing to commit, working tree clean

2.1.3 还原提交对象

1) 提交日志修正

【初始化项目】

shell 复制代码
rm -rf ./* .git
git init
echo "初始化文件"  >> aaa.txt
git add ./
git commit -m "初始化文件" ./

【具体操作】

shell 复制代码
Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ git log --oneline
f726afa (HEAD -> master) 初始化文件

Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ git commit --amend -m '初始化文件-001' ./
[master c3b69fa] 初始化项目
 Date: Wed Aug 2 20:03:53 2023 +0800
 1 file changed, 1 insertion(+)
 create mode 100644 aaa.txt

Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ git log --oneline
c3b69fa (HEAD -> master) 初始化文件-001							# 修改了提交日志
2) 提交内容修正

有时候提交文件之后才发现文件漏了一些内容,我们当然可以编辑文件再次提交,但这样会使得提交日志过于冗余;Git允许我们来修改提交内容并且不造成提交日志的冗余;

【初始化项目】

shell 复制代码
rm -rf ./* .git
git init
echo '1245' >> aaa.txt
git add ./
git commit -m '初始化文件' ./

没有使用还原提交对象:对项目中造成很多无用的冗余日志

shell 复制代码
Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ vi aaa.txt

Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ cat aaa.txt
12345

Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ git commit -m '第一次写错了,补上了第一次的错误' ./

Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ git log --oneline
0e7e757 (HEAD -> master) 第一次写错了,补上了第一次的错误				# 多了一条无用日志
13d329d 初始化文件

【重新初始化项目】

shell 复制代码
rm -rf ./* .git
git init
echo '1245' >> aaa.txt
git add ./
git commit -m '初始化文件' ./

使用提交修正:

shell 复制代码
Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ vi aaa.txt

Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ cat aaa.txt
12345

Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ git commit --amend -m '初始化文件' ./								# 还原提交对象

Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ git log --oneline
1c7942d (HEAD -> master) 初始化文件								# 还是一条日志
3) 提交文件修正

一次提交两个文件,结果漏提交了,只提交了一个;如果再次提交会导致两个文件不是同一个版本。Git同样允许我们对某一次的提交文件进行修正;

【初始化项目】

shell 复制代码
rm -rf ./* .git
git init
echo '111' >> aaa.txt
git add ./
git commit -m '111' ./

【提交一个文件】

shell 复制代码
Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ echo "222" >> bbb.txt

Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ git add ./

Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ git commit -m '222' ./

Administration@XiaoMi MINGW64 ~/Desktop/work (master)			# 查看提交日志发现有两个日志
$ git log --oneline
2ae2646 (HEAD -> master) 222
59c4706 111

【重新初始化项目】

shell 复制代码
rm -rf ./* .git
git init
echo '111' >> aaa.txt
git add ./
git commit -m '111' ./

【使用amend进行提交文件的修正】

shell 复制代码
Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ git log --oneline
3e7ce9d (HEAD -> master) 111

Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ echo "222" >> bbb.txt

Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ git add ./

Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ git commit --amend -m '初始化项目' ./

Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ git log --oneline
a59cc7a (HEAD -> master) 初始化项目							# 只有一个提交日志

2.2 Git的数据回退

在你使用 Git 的时候,你可能会意外丢失一次提交。 通常这是因为你强制删除了正在工作的分支,但是最后却发现你还需要这个分支, 亦或者硬重置了一个分支,放弃了你想要的提交。 如果这些事情已经发生,该如何找回你的提交呢?

下面的例子将硬重置你的测试仓库中的 master 分支到一个旧的提交,以此来恢复丢失的提交。 首先,让我们看看你的仓库现在在什么地方:

git reset可以帮助我们回退HEAD指针、暂存区、工作空间;

  • 只移动HEAD,不改变暂存区和工作空间
shell 复制代码
git reset --soft {CommitHash}		# 回退到指定的Commit对象

git reset --soft HEAD~				# 回退到上一个版本	
git reset --soft HEAD~~				# 回退到上上个版本
  • 移动HEAD、更改暂存区,不改变工作空间
shell 复制代码
git reset --mixed HEAD~				# 回退到上一个版本
git reset --mixed HEAD~~			# 回退到上上一个版本

git reset --mixed {CommitHash}		# 回退到指定的Commit对象
  • 移动HEAD、更改暂存区、更改工作空间
shell 复制代码
git reset --hard HEAD~				# 回退到上一个版本
git reset --hard HEAD~~			# 回退到上上一个版本

git reset --hard {CommitHash}		# 回退到指定的Commit对象

2.2.1 回退HEA指针

  • 语法:
shell 复制代码
git reset --soft {CommitHash}

git reset --soft HEAD~				# 还原到上一个版本	
git reset --soft HEAD~~				# 还原到上上个版本

Tips:git reset --soft只会退回HEAD指针,并不会对暂存区和工作空间造成影响。

【初始化项目】

shell 复制代码
rm -rf ./* .git
git init
echo '111' >> aaa.txt
git add ./
git commit -m '111' ./
echo "222" >> aaa.txt
git add ./
git commit -m "222" ./
echo "333" >> aaa.txt
git add ./
git commit -m "333" ./

Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ git log --oneline
a6f8911 (HEAD -> master) 333			# 指针指向的是最新的Commit对象
0ecb277 222
8b0d39e 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git reflog
a6f8911 (HEAD -> master) HEAD@{0}: commit: 333
0ecb277 HEAD@{1}: commit: 222
8b0d39e HEAD@{2}: commit (initial): 111

【将HEAD指针回退到上一个版本】

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git reset --soft HEAD~

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 使用--all也只能查询到HEAD指针指向的位置
$ git log --oneline --all
0ecb277 (HEAD -> master) 222
8b0d39e 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 使用reflog命令可以查看到所有的提交对象
$ git reflog
0ecb277 (HEAD -> master) HEAD@{0}: reset: moving to HEAD~
a6f8911 HEAD@{1}: commit: 333
0ecb277 (HEAD -> master) HEAD@{2}: commit: 222
8b0d39e HEAD@{3}: commit (initial): 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 查看暂存区
$ git ls-files -s
100644 641d57406d212612a9e89e00db302ce758e558d2 0       aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 暂存区的内容没有发生变化
$ git cat-file -p 641d57406d212612a9e89e00db302ce758e558d2
111
222
333

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)		# 工作空间的内容也没有发生变化
$ cat aaa.txt
111
222
333

**git reset --soft HEAD~**指令只会回退HEAD指针,不会对暂存区、工作空间造成影响,其原理也是生成了一次新的Commit对象;

【查看当前工作空间状态】

当HEAD指针被回退后,当前工作空间的状态变为了Changes to be committed,即:有修改的操作,但还未提交;

需要注意的是HEAD指针被回退后,暂存区、工作空间的内容均未发生改变,如果此时执行commit提交后只是生成了一个新的Commit对象,并且HEAD指针将指向这个新的Commit对象,但是暂存区和工作空间都未发生变化;

因为执行commit提交是将当前暂存区的内容生成Tree对象,然后再生成Commit对象;HEAD指针被回退后暂存区并没有发送变化,此时如果直接提交是毫无意义的;

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)			# 当前工作空间状态
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)			# 提交
$ git commit -m "222" ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)			# 查看文件内容(并没有发生变化)
$ cat aaa.txt
111
222
333

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)			# 查看日志
$ git log --oneline
2226792 (HEAD -> master) 222
0ecb277 222
8b0d39e 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git reflog
2226792 (HEAD -> master) HEAD@{0}: commit: 222
0ecb277 HEAD@{1}: reset: moving to HEAD~
a6f8911 HEAD@{2}: commit: 333
0ecb277 HEAD@{3}: commit: 222
8b0d39e HEAD@{4}: commit (initial): 111

【选择指定的Commit对象来还原】

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git reset --soft 8b0d39e

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline --all
8b0d39e (HEAD -> master) 111

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git reflog
8b0d39e (HEAD -> master) HEAD@{0}: reset: moving to 8b0d39e
2226792 HEAD@{1}: commit: 222
0ecb277 HEAD@{2}: reset: moving to HEAD~
a6f8911 HEAD@{3}: commit: 333
0ecb277 HEAD@{4}: commit: 222
8b0d39e (HEAD -> master) HEAD@{5}: commit (initial): 111

2.2.2 回退暂存区

使用git reset --soft用于还原HEAD指针指向的Commit对象,并不会对暂存区和工作空间造成影响;

如果想要还原暂存区可以使用git reset --mixed HEAD~git reset HEAD~;同样的,git reset --mixed命令也可以还原到相邻位置和任意位置;

  • 语法:
shell 复制代码
git reset --mixed {CommitHash}

Tips:git reset --mixed会更改HEAD指针和暂存区,并不会对工作空间修改;

【初始化项目】

shell 复制代码
rm -rf ./* .git
git init
echo '111' >> aaa.txt
git add ./
git commit -m '111' ./
echo "222" >> aaa.txt
git add ./
git commit -m "222" ./
echo "333" >> aaa.txt
git add ./
git commit -m "333" ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline
3c69dff (HEAD -> master) 333-ccc
4c4b75f 222-bbb
bd6f3d2 111-aaa

Administration@XiaoMi MINGW64 ~/Desktop/workspace (master)
$ cat aaa.txt
111
222
333

Administration@XiaoMi MINGW64 ~/Desktop/workspace (master)
$ git ls-files -s
100644 641d57406d212612a9e89e00db302ce758e558d2 0       aaa.txt

Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ git cat-file -p 641d57406d212612a9e89e00db302ce758e558d2
111
222
333

【还原暂存区】

shell 复制代码
Administration@XiaoMi MINGW64 ~/Desktop/workspace (master)				# 还原暂存区
$  git reset --mixed HEAD~
Unstaged changes after reset:
M       aaa.txt

Administration@XiaoMi MINGW64 ~/Desktop/workspace (master)				# 查看暂存区
$ git ls-files -s
100644 a30a52a3be2c12cbc448a5c9be960577d13f4755 0       aaa.txt

Administration@XiaoMi MINGW64 ~/Desktop/work (master)				# 查看暂存区中的Blob对象的内容(发现更改了)
$ git cat-file -p a30a52a3be2c12cbc448a5c9be960577d13f4755
111
222

Administration@XiaoMi MINGW64 ~/Desktop/workspace (master)				# 工作空间没有更改
$ cat aaa.txt
111
222
333

Administration@XiaoMi MINGW64 ~/Desktop/workspace (master)				# 查看日志
$ git log --oneline
ffe591a (HEAD -> master) 222
f1a17fe 111

Administration@XiaoMi MINGW64 ~/Desktop/work (master)				# 查看详细日志
$ git reflog
ffe591a (HEAD -> master) HEAD@{0}: reset: moving to HEAD~
4e8f0c3 HEAD@{1}: commit: 333
ffe591a (HEAD -> master) HEAD@{2}: commit: 222
f1a17fe HEAD@{3}: commit (initial): 111

**git reset --soft HEAD~**指令只会还原HEAD指针,不会对暂存区、工作空间造成影响,其原理也是生成了一次新的Commit对象;

【查看当前工作空间状态】

使用reset --soft还原之后,当前的工作状态处于Changes to be committed,即有更改的文件,但还未提交;

注意:git reset --mixed不会还原工作空间,即使当前暂存区被还原了,如果此时提交了,那么文件还是没有修改,因为对于已经执行过add的文件来说,执行commit之前会执行一次隐式的add,就相当于把工作空间的内容再重新添加到暂存区了,然后再生成Tree对象、Commit对象等。

shell 复制代码
Administration@XiaoMi MINGW64 ~/Desktop/work (master)
$ git status
On branch master
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        modified:   aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git commit -m "222" ./

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)			# 查看工作空间的文件内容(发现还是回退之前的)
$ cat aaa.txt
111
222
333

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)			# 查看状态
$ git status
On branch master
nothing to commit, working tree clean

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)			# 查看暂存区
$ git ls-files -s
100644 641d57406d212612a9e89e00db302ce758e558d2 0       aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)			# 查看暂存区的内容(发现还是回退之前的)
$ git cat-file -p 641d57406d212612a9e89e00db302ce758e558d2
111
222
333

2.2.3 回退工作空间

  • 语法:
shell 复制代码
git reset --hard HEAD~				# 回退到上一个版本
git reset --hard HEAD~~				# 回退到上上一个版本

git reset --hard {CommitHash}		# 回退到指定的Commit对象

Tips:使用git reset --hard回退会回退HEAD指针、暂存区、工作空间这三个地方;

【初始化项目】

shell 复制代码
rm -rf ./* .git
git init
echo '111' >> aaa.txt
git add ./
git commit -m '111' ./
echo "222" >> aaa.txt
git add ./
git commit -m "222" ./
echo "333" >> aaa.txt
git add ./
git commit -m "333" ./
git log --oneline

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git log --oneline
6fc1ae0 (HEAD -> master) 333
d100998 222
980a440 111

【执行操作】

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)			# 回退到上个版本
$ git reset --hard HEAD~
HEAD is now at d100998 222


Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)			# HEAD指针被回退了
$ git log --oneline
d100998 (HEAD -> master) 222
980a440 111


Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git ls-files -s
100644 a30a52a3be2c12cbc448a5c9be960577d13f4755 0       aaa.txt

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)			# 暂存区被回退了	
$ git cat-file -p a30a52a3be2c12cbc448a5c9be960577d13f4755
111
222

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)			# 工作空间被回退了
$ cat aaa.txt
111
222

Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)			# 查看回退日志
$ git reflog
d100998 (HEAD -> master) HEAD@{0}: reset: moving to HEAD~
6fc1ae0 HEAD@{1}: commit: 333
d100998 (HEAD -> master) HEAD@{2}: commit: 222
980a440 HEAD@{3}: commit (initial): 111

【查看当前工作空间状态】

由于HEAD指针、暂存区、工作空间都被回退了,因此当前工作空间为nothing to commit状态,即:所有的操作均已提交;

shell 复制代码
Adminstrator@LAPTOP-OC90J78H MINGW64 ~/Desktop/workspace (master)
$ git status
On branch master
nothing to commit, working tree clean
相关推荐
苏三有春6 分钟前
五分钟学会如何在GitHub上自动化部署个人博客(hugo框架 + stack主题)
git·go·github
high201111 小时前
【Git】-- 版本说明
git
kaixin_learn_qt_ing12 小时前
git clone
git
sin220112 小时前
git stash
git
喝鸡汤12 小时前
一起学Git【第二节:创建版本库】
git
慢慢成长的码农12 小时前
git 同步分支操作
git
sin220112 小时前
git推送本地仓库到远程(Gitee)
git·gitee
丁总学Java14 小时前
git branch -r(--remotes )显示你本地仓库知道的所有 远程分支 的列表
git
yylの博客17 小时前
Windows通过git-bash安装zsh
windows·git·bash·zsh
丁总学Java17 小时前
(Z Shell)zsh: no matches found: ? 使用单引号包裹
git·zsh