关于Git¶
约 7938 个字 110 行代码 9 张图片 预计阅读时间 28 分钟
Git介绍¶
Git是一个版本控制器,用于记录电脑上所有文件的变化,并且可以看到修改的各个版本
对于一个文本文件来说,Git可以跟踪到文件的某一行的改动
对于一个二进制文件(例如图片、视频等),Git无法直接读取到文件修改的内容,但是可以获取到该文件属性的变化,例如大小的改变、文件是否存在等
Git安装¶
安装前可以使用下面的命令查看当前系统是否已经安装Git
Bash | |
---|---|
1 |
|
如果可以正常显示Git版本,则代表当前系统已经安装Git,否则没有安装
在CentOS下安装Git¶
使用下面的指令在CentOS系统下安装Git
Bash | |
---|---|
1 |
|
Note
需要注意,如果当前用户为非root用户,则使用sudo
命令需要确保当前用户在白名单中
安装完成后,可以再次使用版本查看命令查看是否安装成功
在Ubuntu下安装Git¶
使用下面的指令在Ubuntu系统下安装Git
Bash | |
---|---|
1 |
|
安装完成后,可以再次使用版本查看命令查看是否安装成功
在Windows下安装Git¶
在官网Git (git-scm.com)上下载对应Windows版本的Git,安装即可
初始化本地仓库¶
Git可以管理仓库,但是前提是当前目录可以被管理,为了使普通目录变为可以被Git管理的仓库,需要使用下面的命令对当前目录进行初始化,这个过程也称为初始化本地仓库
Bash | |
---|---|
1 |
|
命令执行完毕后,查看当前目录时可以发现一个隐藏文件.git
,该文件中会保存与Git相关的文件,一般情况下不要去修改,例如在CentOS下的目录结构
Text Only | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
Git中的name
和email
配置¶
为了能够正确将指定文件提交到远程仓库,需要先对Git中的信息进行配置,在Git中需要配置与远端代码仓库所属账号相同的name
和email
。具体配置方式如下:
Bash | |
---|---|
1 2 |
|
配置完成后,可以使用下面的命令查看是否已经将name
和email
信息存储到配置文件中
Bash | |
---|---|
1 |
|
如果配置成功,则会在原有配置信息的最后两行显示name
和email
,否则不显示
如果需要删除已经配置的信息,可以使用下面的命令进行删除
Bash | |
---|---|
1 2 |
|
上面的方式会在当前本地仓库下配置一个name
和email
,并且只作用于当前仓库,如果需要一个可以作用于全局的配置,则需要使用--global
选项
Bash | |
---|---|
1 2 |
|
配置完成后,可以使用下面的命令查看是否已经将name
和email
信息存储到配置文件中
Bash | |
---|---|
1 |
|
如果配置成功,则会在原有配置信息的开始两行显示name
和email
,否则不显示
需要注意,如果使用了全局配置,则直接使用前面的删除命令是无法对全局配置信息进行删除。对于全局配置删除同样需要加上--global
选项,如下:
Bash | |
---|---|
1 2 |
|
Git中的工作区、暂存区和版本库¶
工作区:当前电脑的目录(包含了.git
目录和本地文件) 暂存区:对工作区中有改变的文件(新增、修改和删除)进行存储的位置。在.git
目录中存在着对应的index
文件,所以暂存区也可以称为索引区 版本库:也称本地仓库,即.git
目录。其中包含了所有文件的修改内容和对应的版本信息
三者关系如下:
在上图中,对于版本库来说,存在一个HEAD
指针,指向当前分支的最新一次提交。默认情况下,Git会自动将HEAD
指针指向master
分支,该分支是Git默认创建的分支。当对工作区中的文件进行修改后,需要使用git add
命令将修改的文件添加到暂存区,然后使用git commit
命令将暂存区中的文件提交到版本库中
从上面的过程可以看出,如果在工作区修改了文件,该文件不会被Git管理,只有在使用git add
命令+git commit
命令之后,文件才会被Git管理
添加文件到暂存区¶
使用下面的命令可以将工作区中的文件添加到暂存区中,该命令可以添加指定文件,也可以添加所有文件:
Bash | |
---|---|
1 2 3 4 5 6 |
|
例如:
Bash | |
---|---|
1 2 |
|
默认直接使用git init
命令创建的.git
目录下是没有index
文件的,一旦第一次使用git add
命令添加文件,Git会自动创建index
文件
提交文件到版本库¶
基本使用¶
使用下面的命令可以将暂存区中的文件提交到版本库中,该命令需要添加提交信息:
Bash | |
---|---|
1 |
|
Note
注意,-m
选项不能省略,建议是给出较为精确且具体的提交信息,便于后续查看
例如:
Bash | |
---|---|
1 |
|
当然,也可以将暂存区的指定文件提交到版本库中,该命令需要添加提交信息:
Bash | |
---|---|
1 2 |
|
一旦使用git commit
命令提交文件,会看到类似下面的信息:
Bash | |
---|---|
1 2 3 |
|
如果要查看提交记录,可以使用下面的命令:
Bash | |
---|---|
1 |
|
当前情况下会看到类似下面的信息:
Bash | |
---|---|
1 2 3 4 5 |
|
如果想要查看较为简短的日志信息,可以带上--pretty=oneline
选项:
Bash | |
---|---|
1 |
|
当前情况下会看到类似下面的信息:
Bash | |
---|---|
1 |
|
日志中的长数字代表的就是日志标识符,是一个哈希值
通过.git
目录了解Git管理¶
下面进入.git
目录查看其中的内容可以看到类似下面的结构:
Text Only | |
---|---|
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 42 43 44 |
|
其中使用git add
命令会在objects
目录下创建一个对象,objects
目录是Git的对象库,在使用git log
命令查看日志信息中看到的长数字就是当前对象的具体标识,前两位表示目录名称,后面38位表示文件名称
获取到文件的哈希值后可以使用下面的命令查看其中的内容:
Bash | |
---|---|
1 |
|
例如:
Bash | |
---|---|
1 |
|
当前情况下会看到类似下面的信息:
Text Only | |
---|---|
1 2 3 4 5 |
|
在上面的信息中可以看到,author
和committer
后面的数字就是当前提交的作者和提交者的信息,另外还有一个tree
,查看该行信息可以得到类似下面的内容:
Text Only | |
---|---|
1 |
|
查看对应的test.txt
文件对应的哈希值会发现没有任何内容,因为当前test.txt
文件中没有写任何内容,如果现在向test.txt
中写入一些信息,如下:
Text Only | |
---|---|
1 |
|
再重复上面的步骤,查看test.txt
文件对于的哈希值即可查看到最新修改的内容:
Text Only | |
---|---|
1 2 |
|
再执行上面的步骤也会发现一些步骤结果发生了变化,例如查看git commit
命令结果变为如下:
Text Only | |
---|---|
1 2 |
|
此时可以看到insertion
前面的值变为了1,表示文件新增一行内容
使用git log
查看时结果变为如下:
Text Only | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 |
|
除了多一条记录外,还可以看到当前HEAD
指向了最新的提交记录,再使用git cat-file -p
查看最新提交记录的哈希值会发现内容多了一行parent
:
Text Only | |
---|---|
1 2 3 4 5 6 |
|
这个parent
表示的就是当前提交的上一个提交记录的哈希值
除了通过git log
查看外,上面还提到HEAD
会指向最新的提交记录,现在通过cat HEAD
会得到下面的结果:
Text Only | |
---|---|
1 |
|
查看其中的内容会发现也是一个哈希值,而且这个哈希值就是最新的提交记录的哈希值:
Text Only | |
---|---|
1 2 |
|
使用git cat-file -p
查看这个哈希值就可以得到与前面使用git log
+ git cat-file -p
一样的内容
Git管理文件的修改¶
Git本质上管理的是文件的内容,而不是文件本身。具体来说,Git通过一种称为内容寻址存储(Content-Addressable Storage)的机制来跟踪文件的内容。以下是关键点:
- 内容为核心:Git将文件的内容存储为“对象”(object),并对这些对象进行哈希计算(通常是SHA-1或SHA-256)。文件的内容被唯一地标识为一个哈希值
- 文件名与内容分离:在Git中,文件的名称和路径信息是存储在树对象(tree object)中的,而文件的实际内容则存储在Blob对象中。这意味着Git可以独立地追踪内容的变化,而不依赖于文件的名称
尽管Git主要管理的是文件的内容,但它也通过元数据(如树对象)来记录文件的增删操作。那么如何解释文件的新增或者删除操作呢?
当一个新文件被添加到Git仓库时,Git会执行以下操作:
- 生成Blob对象:Git会读取该文件的内容,并将其存储为一个新的Blob对象。这个Blob对象的内容会被哈希化,生成一个唯一的标识符
- 更新树对象:Git会在当前的树对象中添加一条记录,这条记录包含文件的路径、名称以及对应的Blob对象的哈希值
- 提交记录:在下一次提交时,Git会将新的树对象与之前的提交关联起来,从而记录新增文件的操作
例如:
Bash | |
---|---|
1 2 3 |
|
在这个过程中,test.txt
的内容被存储为Blob对象,而文件的路径和名称被记录在树对象中
当一个文件被删除时,Git并不会立即从存储中移除Blob对象,而是通过以下方式处理:
- 更新树对象:在当前的树对象中,Git会移除与该文件相关的记录(即文件路径和名称的映射)。
- 保留历史记录:即使文件被删除,Git仍然保留之前提交中与该文件相关的内容(Blob对象),以便在需要时可以通过历史记录恢复
例如:
Bash | |
---|---|
1 2 |
|
newfile.txt
不再出现在当前的树对象中,但其内容仍然存在于Git的对象数据库中,直到垃圾回收(GC)清理掉未引用的对象。 Git的设计理念是将文件的内容与文件的路径/名称分离管理,这种设计带来了以下优势:
- 高效的内容存储:由于Git基于内容的哈希值存储,相同内容的文件只会存储一次,节省了存储空间。
- 灵活的历史记录:通过树对象和提交对象的组合,Git能够精确地记录文件的增删改操作,同时保留完整的历史信息。
- 可追溯性:无论是新增文件还是删除文件,Git都能通过提交历史轻松还原任何状态。
如果现在修改了文件但不对文件进行git add
操作和git commit
操作,Git会指出文件的变动,这一点可以使用下面的命令查看:
Bash | |
---|---|
1 |
|
例如,向test.txt
中写入一些内容:
Text Only | |
---|---|
1 |
|
当前情况下会看到类似下面的信息:
Text Only | |
---|---|
1 2 3 4 5 6 7 |
|
当前情况下会看到Changes not staged for commit
,表示当前文件的修改没有被暂存,接着将该文件添加到暂存区,再使用git status
查看会看到类似下面的结果:
Text Only | |
---|---|
1 2 3 4 |
|
当前情况下会看到Changes to be committed
,表示当前文件的修改已经被暂存,使用git commit
提交文件后再使用git status
查看会看到类似下面的结果:
Text Only | |
---|---|
1 2 |
|
当前情况下会看到nothing to commit, working tree clean
,表示当前文件的修改已经被提交,并且工作区没有任何修改。看到这段内容就表示当前目录下的文件内容和版本库中的文件内容是一致的
但是,git status
只能查看哪些文件有变动,并不能具体变动在何处,如果想要查看这一点,可以使用下面的命令:
Bash | |
---|---|
1 2 3 4 |
|
例如,现在向test.txt
中写入下面的内容:
Text Only | |
---|---|
1 |
|
再没有使用git add
命令时,使用git diff
命令查看会看到类似下面的结果:
Text Only | |
---|---|
1 2 3 4 5 6 7 8 |
|
其中@@ -1,2 +1,3 @@
中的-1,2
表示当前旧文件从第一行开始的两行,+1,3
表示新版本从第1行开始的3行,后面的+hello Linux
就表示新增的内容,而没有任何前缀的就是没有发生修改的内容,如果内容前面存在-
那么表示删除的内容
而index 05fe86c..b3f64b9 100644
中的第一个05fe86c
表示旧文件的哈希值(前五位),第二个b3f64b9
表示新版本的哈希值(前五位),100644
表示Linux下的文件的权限
版本回退¶
Git属于版本控制工具,既然可以控制文件版本,那么必然存在回退文件版本的功能。在Git中可以使用git reset
命令回退文件,其完整形式如下:
Bash | |
---|---|
1 |
|
首先看三个选项:
选项 | 当前目录下的文件 | 暂存区 | 版本库 |
---|---|---|---|
--soft | 不影响,文件内容保持不变 | 不影响,暂存区内容保持不变 | 版本库的HEAD 指针回退到指定提交 |
--mixed | 不影响,文件内容保持不变 | 回退到指定提交的状态,暂存区被重置 | 版本库的HEAD 指针回退到指定提交 |
--hard | 回退到指定提交的状态,文件内容被重置 | 回退到指定提交的状态,暂存区被重置 | 版本库的HEAD 指针回退到指定提交 |
三种选项对于用户最直观的感受如下:
选项 | 用户看到的文件变化 | 用户需要做的操作 | 潜在风险 |
---|---|---|---|
--soft | 文件内容保持不变,工作目录和暂存区无变化 | 无需额外操作,可以直接重新提交;如果需要调整提交范围,可以手动修改暂存区内容 | 无数据丢失风险,适合安全地撤销提交 |
--mixed | 文件内容保持不变,但暂存区被清空或重置 | 需要重新将更改添加到暂存区(如使用 git add ) | 工作目录中的文件内容不受影响,风险较低,适合重新组织暂存区内容 |
--hard | 文件内容被恢复到指定提交的状态,所有未提交的更改(包括文件修改、新增或删除)都会消失 | 无需额外操作,文件和暂存区已被完全重置 | 高风险,未提交的更改会永久丢失,需谨慎使用,通常用于清理工作目录 |
Note
对于--hard
来说必须确保版本库中的文件没有被提交到远程仓库,否则版本库无法回退
接着看<commit>/HEAD
:
<commit>
:回退到指定的提交记录HEAD
:回退到最新的提交记录
对于HEAD
来说,常见的形式有:
HEAD
:回退到最新的提交记录HEAD^
:回退到上一个提交记录HEAD^^
:回退到上上个提交记录- ...
另外还有数字版本:
HEAD~0
:回退到最新的提交记录HEAD~1
:回退到上一个提交记录HEAD~2
:回退到上上个提交记录HEAD~3
:回退到上上上个提交记录- ...
如果使用了--hard
选项,因为版本库中的内容也会回退,而HEAD
指向版本库的文件树,所以此时HEAD
也会改变,例如当前HEAD
如下:
Text Only | |
---|---|
1 |
|
上一个版本值为:
Text Only | |
---|---|
1 |
|
现在回到上一个版本,使用HEAD~1
会看到下面的结果:
Text Only | |
---|---|
1 2 |
|
此时HEAD
会变为如下:
Text Only | |
---|---|
1 |
|
如果想查看最近的Git的操作记录,可以使用下面的命令:
Bash | |
---|---|
1 |
|
例如现在可以看到类似下面的结果:
Text Only | |
---|---|
1 2 3 4 |
|
整个过程中HEAD
的改变如下图所示:
如果回退时使用的是哈希值,那么可以通过git reflog
命令查看哈希值对应的操作记录从而回到上一次的最新版本,即哈希值前五位为2a086
的版本,例如:
Bash | |
---|---|
1 2 |
|
现在HEAD
会变为类似如下的内容:
Text Only | |
---|---|
1 |
|
撤销修改¶
除了可以使用git reset
命令回退文件外,还可以使用git checkout
命令撤销修改,其完整形式如下:
Bash | |
---|---|
1 |
|
Note
不要省略--
,⼀旦省略,该命令就变为其他意思了
这个命令可以对还没有进行add
操作的文件进行撤销操作,因为这个命令的本质就是将暂存区的内容恢复到当前目录中,如果已经add
但是没有commit
,那么可以使用上面的git reset --mixed
,如果已经commit
,那么可以使用git reset --hard
,但是必须保证没有提交到远程仓库
删除文件¶
删除文件有两种方式:
- 直接在当前目录使用删除命令删除,例如
rm test.txt
- 使用
git rm
命令删除,例如git rm test.txt
这两种方式的区别在于,第一种方式只是删除了当前目录下的文件而没有修改暂存区和版本库,如果要删除暂存区和版本库中的对应文件就需要将当前修改进行git add
和git commit
,而第二种方式会直接将文件从暂存区和版本库中删除,并且会生成一条删除记录,例如:
Bash | |
---|---|
1 2 |
|
分支管理¶
理解分支¶
所谓分支,可以理解为一条独立的提交记录线。在当前Git中,只有一个master
,所有的提交记录都在master
上,所以说当前提交只有一个分支,即为master
,示例图如下:
如果现在创建一个新的分支,例如分支名称为dev
,那么示例图如下:
此时当前Git下就存在着两个分支。既然存在着两个分支,那么Git如何标识当前分支呢?通过HEAD
指针,HEAD
指针所指向的就是当前分支,假设当前处于dev
分支,那么HEAD
指向的就是dev
分支,如下图所示:
查看分支¶
前面提到,默认情况下Git会创建一个master
分支,而对应的HEAD
指向的就是master
分支,如果想要查看当前分支可以使用下面的命令:
Bash | |
---|---|
1 |
|
在当前情况下会看到下面的结果:
Text Only | |
---|---|
1 |
|
所以,当前所有的提交都属于master
分支。其中的*
表示HEAD
所指向的分支
新增分支¶
在Git中想要新增分支,可以使用下面的命令:
Bash | |
---|---|
1 |
|
例如,现在想要创建一个新的分支,名称为dev
,那么可以使用下面的命令:
Bash | |
---|---|
1 |
|
在当前情况下会看到下面的结果:
Text Only | |
---|---|
1 2 |
|
此时Git下面就存在着两个分支,但是因为上面的命令只完成了创建分支并没有完成切换分支,所以HEAD
依旧指向的是master
分支
创建分支的过程是基于当前HEAD
指针的,默认情况下,直接使用git branch
命令创建的分支在切换后查看对应的HEAD
会发现该值于master
分支中查看的HEAD
是相同的,所以实际上创建新的分支示意图如下:
这一点也可以通过不同分支的HEAD
值来进行验证,每当创建一个分支就会在refs/heads
目录下形成一个新的文件,所以分别查看master
分支下的HEAD
和dev
分支下的HEAD
值如下:
Text Only | |
---|---|
1 2 3 4 |
|
切换分支¶
创建完新的分支就可以使用下面的命令切换分支:
Bash | |
---|---|
1 |
|
Note
需要注意,此时的checkout
命令后面不存在--
例如,现在想要切换到dev
分支,那么可以使用下面的命令:
Bash | |
---|---|
1 |
|
切换完成后,会看到类似下面的结果:
Text Only | |
---|---|
1 |
|
现在HEAD
指向的就是dev
分支,接下来所有的文件修改都会在dev
分支上进行
创建+切换分支¶
在Git中创建分支和切换分支是两个不同的操作,但是在实际使用中经常会同时进行这两个操作,所以Git提供了一个命令可以同时完成这两个操作:
Bash | |
---|---|
1 |
|
Note
需要注意,此时的checkout
命令后面存在-b
例如,现在想要创建一个新的分支,名称为dev1
,并且切换到dev1
分支,那么可以使用下面的命令:
Bash | |
---|---|
1 |
|
在当前情况下会看到下面的结果:
Text Only | |
---|---|
1 |
|
合并分支¶
完成分支的创建和切换后,既然是创建了新的分支,那么肯定是需要在该分支上进行开发,例如当前在dev
分支上对test.txt
文件进行修改,当前test.txt
文件内容如下:
Text Only | |
---|---|
1 2 |
|
接着在test.txt
文件中添加一行内容:
Text Only | |
---|---|
1 |
|
修改完成后进行git add
和git commit
操作再切换到master
分支。当前情况下,两个分支示意图如下:
从上图可以看到,当前dev
分支的HEAD
和master
分支的HEAD
并不指向同一个提交记录,如果此时想要二者保持一致,既需要将master
分支下的内容与dev
分支保持一致,可以用到下面的命令:
Bash | |
---|---|
1 |
|
需要注意,合并时需要确定用于合并的分支以及待合并的分支,而合并需要再被合并的分支下进行。在当前例子中,用于合并的分支为dev
分支,待合并的分支为master
分支,所以必须先确保处于master
分支,接着使用下面的命令:
Bash | |
---|---|
1 |
|
在当前情况下会看到下面的结果:
Text Only | |
---|---|
1 2 3 4 |
|
此时再看master
分支下的test.txt
中的内容会发现获取到了dev
分支新添加的内容。当前情况下,两个分支示意图如下:
默认情况下,Git选择的合并策略为Fast-forward
,这种策略下,Git会直接将master
分支的HEAD
指向dev
分支的HEAD
的值,所以这种方式的效率比较高,并且这种合并方式会自动进行git commit
操作
合并冲突¶
在实际开发中,可能会遇到多个分支同时对同一个文件进行修改,此时Git会无法自动合并,因为Git无法确定到底使用哪个分支的内容,所以此时需要手动解决冲突。当前test.txt
文件内容如下:
Text Only | |
---|---|
1 2 3 |
|
现在dev
分支和master
分支同时对test.txt
文件进行修改,修改内容如下:
Text Only | |
---|---|
1 2 |
|
Text Only | |
---|---|
1 2 |
|
接着,二者都对自己的修改进行git add
和git commit
操作,此时两个分支的示意图如下:
同样,现在用于合并的分支为dev
分支,待合并的分支为master
分支,在master
分支下执行合并命令:
Bash | |
---|---|
1 |
|
此时会发现合并失败,提示下面的内容:
Text Only | |
---|---|
1 2 3 |
|
这个提示中的CONFLICT (content): Merge conflict in test.txt
就表示合并出现了冲突,对于这个冲突问题需要手动打开文件解决,现在在master
分支下打开test.txt
文件,会发现文件内容如下:
Text Only | |
---|---|
1 2 3 4 5 6 7 8 |
|
可以看到,Git在文件中添加了一些特殊的标记,这些标记的含义如下:
<<<<<<< HEAD
:表示下面的内容为当前分支的内容>>>>>>> dev
:表示上面的内容为dev
分支的内容=======
:表示两个分支内容的分割线
现在需要手动合并冲突,即删除标记并修改内容为期望的结果,例如:
Text Only | |
---|---|
1 2 3 4 5 |
|
因为存在冲突导致Git无法将两个分支的内容进行合并,一旦处理完冲突还需要使用git add
和git commit
操作将内容提交到版本库中。如果想以图形的方式看到分支的提交记录,可以使用下面的命令:
Bash | |
---|---|
1 |
|
在当前情况下会看到下面的结果:
Text Only | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
|
但是,如果使用的是Fast-forward
合并策略会无法看到合并过程,例如下面的结果:
Text Only | |
---|---|
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 |
|
对于这种情况如果想要看到合并过程,需要使用下面的命令进行合并:
Bash | |
---|---|
1 |
|
例如现在dev
新增内容,master
进行合并,那么可以使用下面的命令:
Bash | |
---|---|
1 |
|
在当前情况下会看到下面的结果:
Text Only | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
但是,上面的命令直接使用会发现合并时会弹出一个文件编辑器,其中需要写入提交信息,可以使用默认的提交信息:
Text Only | |
---|---|
1 |
|
如果想在合并的使用不弹出这个对话框但是又可以自定义提交信息,可以使用下面的命令:
Bash | |
---|---|
1 |
|
例如:
Bash | |
---|---|
1 |
|
删除分支¶
删除分支有两种:
- 正常删除分支
- 强制删除分支
一般来说,想要正常删除分支,必须要保证该分支没有对文件进行任何修改或者修改以及提交(git add
+git commit
)到版本库,如果想要删除分支,可以使用下面的命令:
Bash | |
---|---|
1 |
|
如果当前分支对文件进行了修改并且没有进行提交,那么使用上面的命令是无法删除指定分支的,此时会提示使用下面的命令:
Bash | |
---|---|
1 |
|
需要注意的是,在删除分支时必须确保当前所处的分支不是要删除的分支
暂时保存更改¶
假如有下面的场景,dev
分支现在是开发分支,master
是稳定分支,目前master
分支没有任何问题发生,dev
分支此时开始添加新内容,例如:
Text Only | |
---|---|
1 |
|
dev
分支已经修改完成准备提交时,master
分支突然发现存在一个严重的问题,需要紧急修复,此时需要切换到master
分支对该问题进行修复,但是切换到master
分支后并不能直接在master
分支上进行修改,在实际开发中也会经常在稳定分支的最新提交上进行分支的创建来修复问题或者添加新的功能,根据这个理念,此时遇到了问题首先创建一个新的分支bug_fix
:
Bash | |
---|---|
1 |
|
接着,需要切换到bug_fix
分支并添加下面的内容表示修复问题:
Bash | |
---|---|
1 |
|
但是此时会有一个提示:
Text Only | |
---|---|
1 |
|
这个提示表示当前存在分支修改了text.txt
文件并且还没有提交,此时需要再dev
分支下可以使用下面的命令先将修改的内容暂存起来:
Bash | |
---|---|
1 |
|
例如下面的结果:
Text Only | |
---|---|
1 |
|
此时会在.git/refs
目录下添加一个stash
文件,这个文件保存的就是最新的修改信息,如何判断当前分支是最新的修改信息?通过使用git cat-file -p <stash>
查看其中parent
记录对比git log
的最新记录
一旦执行了git stash
命令,此时test.txt
文件内容就不会包含最新添加的内容。再切换到bug_fix
分支即可开始修复问题并提交到版本库
但是现在dev
分支还没有拿到最新的提交信息,所以切换到dev
分支后需要先合并再将刚才暂存的内容取出。但是实际上master
分支也没有得到最新的文件内容,所以真正的顺序是先将bug_fix
分支的信息合并到master
分支,再将master
分支的信息合并到dev
分支,最后取出刚才暂存的内容,取出暂存内容的命令如下:
Bash | |
---|---|
1 |
|
在执行完git stash pop
命令之前可以先使用git stash list
命令查看当前暂存的内容,例如下面的结果:
Text Only | |
---|---|
1 |
|
可以看到当前有一个暂存的内容,此时再使用git stash pop
命令会看到下面的结果:
Text Only | |
---|---|
1 2 3 4 5 6 7 8 9 10 |
|
接下来手动合并冲突再提交即可。回到master
分支后,再进行合并即可看到修复问题并且有新功能的文件内容:
Text Only | |
---|---|
1 2 3 4 5 6 7 8 |
|
附录:Git常用命令¶
-
初始化本地仓库:
Bash 1
git init <directory>
-
克隆一个远程仓库:
Bash 1
git clone <url>
-
添加文件到暂存区:
Bash 1
git add <file>
-
提交更改:
Bash 1
git commit -m "<message>"
-
从远程存储库中拉取更改:
Bash 1
git pull <remote name> <branch>
-
将更改推送到远程存储库:
Bash 1
git push <remote name> <branch>
-
从暂存区删除一个文件:
Bash 1
git reset <file>
-
移动或重命名文件:
Bash 1
git mv <current path> <new path>
-
从存储库中删除文件:
Bash 1
git rm <file>
-
显示分支:
Bash 1
git branch
-
创建一个分支:
Bash 1
git branch <branch>
-
切换到一个分支:
Bash 1
git checkout <branch>
-
删除一个分支:
Bash 1
git branch -d <branch>
-
合并分支:
Bash 1
git merge <branch to merge into HEAD>
-
查看之前的提交:
Bash 1
git checkout <commit id>
-
恢复提交:
Bash 1
git revert <commit id>
-
重置提交:
Bash 1
git reset <commit id>
-
查看存储库的状态:
Bash 1
git status
-
显示提交历史:
Bash 1
git log
-
显示对未暂存文件的更改:
Bash 1
git diff
-
显示两次提交之间的变化:
Bash 1
git diff <commit id> <commit id>
-
列出存储:
Bash 1
git stash list
-
删除一个藏匿处:
Bash 1
git stash drop <stash id>
-
删除所有藏匿处:
Bash 1
git stash clear
-
应用和删除存储:
Bash 1
git stash pop <stash id>
-
显示存储中的更改:
Bash 1
git stash show <stash id>
-
添加远程仓库:
Bash 1
git remote add <remote name> <url>
-
显示远程仓库:
Bash 1
git remote
-
删除远程仓库:
Bash 1
git remote remove <remote name>
-
重命名远程存储库:
Bash 1
git remote rename <old name> <new name>
-
从远程存储库中获取更改:
Bash 1
git fetch <remote name>
-
从特定分支获取更改:
Bash 1
git fetch <remote name> <branch>
-
将更改推送到特定分支:
Bash 1
git push <remote name> <branch>