Git & GitHub ——3:Git 分支及其操作

文章目录

在前面,我们简单地介绍了如何在本地使用 Git 完成不涉及到分支概念的一些基础操作。接下来马上介绍关于分支的概念及其操作,毕竟关于分支和提交的操作就是 Git 中最重要的操作。

Git 最初设计背后的驱动因素之一是支持大型、快速项目的非线性的开发,其特点就是特定开发与主开发线分离。使用分支意味着你可以把你的工作从开发主线(即前面提到的master分支)上分离出来,比如创建一个分支来开发特定的功能或者修改 bug,完成后再合并回去。
多条分支的存在就实现了项目的非线性的开发,因为随着项目规模和复杂性的增长,分支结构模型将极大地帮助管理开发过程。

所有 Git 操作无非就是对 Git对象的操作,Git 对象包括 Blob对象、Tree对象、Commit对象和 Tag对象。

一,什么是分支以及 Git 如何存储数据

所有 Git 操作无非就是对 Git对象的操作,Git 对象包括 Blob对象、Tree对象、Commit对象和 Tag对象。

Blob 对象是一个二进制大对象,代表各个文件,存储所有文件除元数据之外的内容数据。 Gıt 仓库中以 SHA-1 值来标识 Blob,所以不按文件名寻址而是按内容寻址。

  • Bolb当然是在 git add filename之后创建的。

Tree 对象是一个二进制文件,代表各个目录,存储对 Blob对象和其它 Tree对象的引用(SHA-1 值),解决文件名的保存问题。Gıt 仓库同样以 SHA-1 值来标识 Tree。

  • 实际上,只需要 Tree 和 Blob 就能够保存 Git 仓库的当前状态,也就是 Git 快照。
  • 如果 Blob对象代表的文件没有变更,则所有 Tree对象始终通过同一个SHA-1 值保持对它的引用,否则变更导致 SHA-1变化时将更新 Tree对象所引用的SHA-1 值。效果就是,如果该文件没变更,后面所有版本的快照中都将引用该文件。
    在这里插入图片描述

Commit 对象是一个二进制文件,代表各个版本,存储对 Tree对象的引用(SHA-1 值)之外,还存储谁在什么时间以及为什么保存了某些快照等说明信息,解决快照的保存问题。Gıt 仓库同样以 SHA-1 值来标识 Commit。

  • 可以将 Commit对象视为链表的各个节点。 除第一个 Commit对象外,每个 Commit对象都有一个指向父 Commit对象的指针,一个 Commit可以有多个父 Commit,通过这些指针就能完成对提交的切换。
    在这里插入图片描述

Tag 对象是一个二进制文件,代表各个版本号,存储对 Commit对象的引用(SHA-1 值)以及指定给它的标签信息,解决 Commit对象的标注问题。 Gıt 仓库同样以 SHA-1 值来标识 Tag。

分支本质上是一个可变指针,是一个有名字的对 Commit对象的引用(SHA-1 值),分支始终指向最新提交,解决的是提交的分离与合并问题。

  • Git 默认分支名是 master。
  • 分支看起来与标签 Tag 对象很像,只不过分支随着最新提交不断移动,而标签始终固定。
  • 合并的分支就是提交的提交,会产生合并点的提交拥有多个父提交的现象。

所以,Git 存储数据的过程大概是这样的:

  1. git add暂存文件后,创建 Blob对象和 Tree对象:
    在这里插入图片描述

  2. git commit提交暂存区后,创建Commit对象:
    在这里插入图片描述

3,多次提交后:
在这里插入图片描述
4,再打上标签v1.0:
在这里插入图片描述
5,分支的产生与切换:
在这里插入图片描述
6,在分支中工作:
在这里插入图片描述
在这里插入图片描述

7,合并分支:
在这里插入图片描述

Why GitHub renamed its master branch to main
You can select repository default branch

一个使用分支的实际例子:

  1. 开发某个网站。
  2. 为实现某个新的用户需求,创建一个分支。
  3. 在这个分支上开展工作,比如继续开发或者正常修复BUG。

正在此时,有个很严重的线上问题需要紧急修补。 你将按照如下方式来处理:

  1. 切换到你的线上分支(production branch)。
  2. 为这个紧急任务新建一个分支,并在其中修复它。
  3. 在测试通过之后,切换回线上分支,然后合并这个修补分支,最后将改动推送到线上分支。
  4. 切换回你最初工作的分支上,继续工作。

做准备:

dang@DFLubuntu:~$ mkdir git-tutorial
dang@DFLubuntu:~$ cd git-tutorial
dang@DFLubuntu:~/git-tutorial$ git init
已初始化空的 Git 仓库于 /home/dang/git-tutorial/.git/
dang@DFLubuntu:~/git-tutorial$ echo "111" >> a.txt
dang@DFLubuntu:~/git-tutorial$ echo "111" >> b.txt
dang@DFLubuntu:~/git-tutorial$ echo "<h1>test</h1>" >> index.html
dang@DFLubuntu:~/git-tutorial$ git add .
dang@DFLubuntu:~/git-tutorial$ git commit -m "第一次提交"
[master (根提交) 0932815] 第一次提交
 3 files changed, 3 insertions(+)
 create mode 100644 a.txt
 create mode 100644 b.txt
 create mode 100644 index.html
dang@DFLubuntu:~/git-tutorial$ echo "222" >> a.txt
dang@DFLubuntu:~/git-tutorial$ echo "222" >> b.txt
dang@DFLubuntu:~/git-tutorial$ git add .
dang@DFLubuntu:~/git-tutorial$ git commit -m "第二次提交"
[master f9021ee] 第二次提交
 2 files changed, 2 insertions(+)
dang@DFLubuntu:~/git-tutorial$ echo "333" >> a.txt
dang@DFLubuntu:~/git-tutorial$ echo "333" >> b.txt
dang@DFLubuntu:~/git-tutorial$ git add .
dang@DFLubuntu:~/git-tutorial$ git commit -m "第三次提交"
[master 8441143] 第三次提交
 2 files changed, 2 insertions(+)
dang@DFLubuntu:~/git-tutorial$ git tag -a v0.0.1 -m "完成v0.0.1功能开发" 8441143

目前:
在这里插入图片描述

二,创建分支:checkout -b

在分支中正常工作:假设根据问题追踪系统的 #53 问题提示,我们现在要对 c.txt 文件进行bug修复。

执行 git checkout -b branchname命令创建并切换到新分支

dang@DFLubuntu:~/git-tutorial$ git checkout -b iss53
切换到一个新分支 'iss53'
dang@DFLubuntu:~/git-tutorial$ 
  • 创建了一个名为 iss53 的指针来表示一个分支,并切从 master 换到 iss53 上。

目前:
在这里插入图片描述
然后再 iss53 分支中解决日常的 #53 问题:

dang@DFLubuntu:~/git-tutorial$ echo "444" >> b.txt
dang@DFLubuntu:~/git-tutorial$ git add .
dang@DFLubuntu:~/git-tutorial$ git commit -m "在 c.txt 末尾新增一行测试数据 [issue 53]"
[iss53 10d78c8] 在 c.txt 末尾新增一行测试数据 [issue 53]
 1 file changed, 1 insertion(+)
dang@DFLubuntu:~/git-tutorial$
  • 在分支中的修改与提交都不会影响到其他分支。

目前:
在这里插入图片描述

三,查看分支:branch 或 log

执行 git branch 命令能列出所有分支
在这里插入图片描述

  • 星号 *表示我们正在 iss53 分支上工作。

也能执行 git branch --list 通配 命令列出满足条件的分支的信息:

dang@DFLubuntu:~/git-tutorial$ git branch --list iss*
  iss53
dang@DFLubuntu:~/git-tutorial$ 

也能执行 git branch -vv 命令查看每一个分支的最后一次提交:

dang@DFLubuntu:~/git-tutorial$ git branch -vv
* iss53  10d78c8 在 c.txt 末尾新增一行测试数据 [issue 53]
  master 5ec814b 在b.txt中添加数据以产生合并冲突
dang@DFLubuntu:~/git-tutorial$ 

也能执行 git branch -r --contains commit_SHA-1_value --all 命令查看某个commit属于哪个分支

也能执行 git log --graph --oneline --all --decorate命令形象化列出分支关系

  • 命令太长,可用别名重命名,比如命名为 lgd。
dang@DFLubuntu:~/git-tutorial$ git lgd
* 10d78c8 (HEAD -> iss53) 在 c.txt 末尾新增一行测试数据 [issue 53]
* 8441143 (tag: v0.0.1, master) 第三次提交
* f9021ee 第二次提交
* 0932815 第一次提交
dang@DFLubuntu:~/git-tutorial$ git checkout master

四,切换分支:checkout

执行 git checkout branchname 命令能切换到指定分支

dang@DFLubuntu:~/git-tutorial$ git checkout master
切换到分支 'master'
dang@DFLubuntu:~/git-tutorial$ git branch -vv
  iss53  8441143 第三次提交
* master 8441143 第三次提交
dang@DFLubuntu:~/git-tutorial$ git lgd
* 8441143 (HEAD -> master, iss53) 第三次提交
* f9021ee 第二次提交
* 0932815 第一次提交
dang@DFLubuntu:~/git-tutorial$
  • 切换回 master 分支后,b.txt 文件的内容没发生变更,因为我们只是在 iss53 分支中修改了它。
  • 如果又从 master 切换回 iss53 分支中,b.txt 文件的变更的内容会恢复。

执行 git checkout - 命令能切换回上一个分支

在切换分支前,最好保持好一个干净的工作状态,即工作目录和暂存区里的变更都已经被提交。

  • 如果不提交变更就切换分支,将导致 Git 停止执行命令甚至丢失变更。
  • 要么执行 git checkout -f branchname命令能强制切换分支并丢弃变更,要么撤销工作目录变更再切换分支。

实际上,git checkout -b branchname等价于 git branch iss53git checkout iss53两条命令,先创建分支,然后再切换到刚刚创建的这个分支上。

五,在分支中工作:在不同的文件中工作

上面我们在 iss53 分支中正常修复了一个BUG并提交了,但此时我们接到运维的通知,说是有紧急的线上问腿需要修复。所以先切换到线上分支 master 中,然后立马再创建一个分支来修复紧急问题:未同意隐私政策就能使用电子邮件注册账号:

dang@DFLubuntu:~/git-tutorial$ git checkout master
切换到分支 'master'
dang@DFLubuntu:~/git-tutorial$ git checkout -b hotfix
切换到一个新分支 'hotfix'
dang@DFLubuntu:~/git-tutorial$ git lgd
* 10d78c8 (iss53) 在 c.txt 末尾新增一行测试数据 [issue 53]
* 8441143 (HEAD -> hotfix, tag: v0.0.1, master) 第三次提交
* f9021ee 第二次提交
* 0932815 第一次提交
dang@DFLubuntu:~/git-tutorial$ vim index.html
dang@DFLubuntu:~/git-tutorial$ git add .
dang@DFLubuntu:~/git-tutorial$ git commit -m "紧急处理线上问题:注册页面隐私协议单选崩溃"
[hotfix 1f5b64b] 紧急处理线上问题:注册页面隐私协议单选崩溃
 1 file changed, 34 insertions(+), 1 deletion(-)
 rewrite index.html (100%)
dang@DFLubuntu:~/git-tutorial$

目前:
在这里插入图片描述

六,合并分支:merge

合并分支,其实就是把一个分支中的变更复制到另一个分支中,并进行一次提交。

在紧急问题修复成功后,就能将 hotfix 分支合并回的 master 分支来部署到线上。

执行 git merge branchname 命令将在当前分支中合并指定分支
在这里插入图片描述

注意 HEAD 的变化,它相当于是跟随后面的 master 追上 hotfix 的,这种用后面的 master 分支合并前面的分支的变化的过程被称为“Fast-forward”,效果相当于 master 分支直接快进到被合并的分支的状态。

目前:
在这里插入图片描述

七,删除分支:branch -d

执行 git branch -d branchname 命令删除指定的分支

  • 在每次合并完分支后,要记得删除被合并的那个分支。

由于紧急问题已经处理完了,这里就删除 hotfix 分支:

dang@DFLubuntu:~/git-tutorial$ git branch -d hotfix
已删除分支 hotfix(曾为 1f5b64b)。
dang@DFLubuntu:~/git-tutorial$ git lgd
* 1f5b64b (HEAD, master) 紧急处理线上问题:注册页面隐私协议单选崩溃
| * 10d78c8 (iss53) 在 c.txt 末尾新增一行测试数据 [issue 53]
|/  
* 8441143 (tag: v0.0.1) 第三次提交
* f9021ee 第二次提交
* 0932815 第一次提交
dang@DFLubuntu:~/git-tutorial$

然后就又能回到处理 #53 问题的分支中继续修复BUG并提交,
在这里插入图片描述
假设现在这个 # 53 问题已经完成维护,按照前面的做法,就是切换回 master 分支、合并 iss53 分支,最后删除 iss53 分支:
在这里插入图片描述

八,再谈 HEAD 的分离态

假设现在 iss53 还没删除,且我们已经回到了 master,如果此时我们修改 master 分支中的 b.txt 文件并提交:

dang@DFLubuntu:~/git-tutorial$ vim b.txt
dang@DFLubuntu:~/git-tutorial$ git add .
dang@DFLubuntu:~/git-tutorial$ git commit -m "在b.txt中添加数据以产生合并冲突"
[分离头指针 5ec814b] 在b.txt中添加数据以产生合并冲突
 1 file changed, 1 insertion(+), 1 deletion(-)
dang@DFLubuntu:~/git-tutorial$ git lgd
* 5ec814b (HEAD) 在b.txt中添加数据以产生合并冲突
* 1f5b64b (master) 紧急处理线上问题:注册页面隐私协议单选崩溃
| * 10d78c8 (iss53) 在 c.txt 末尾新增一行测试数据 [issue 53]
|/  
* 8441143 (tag: v0.0.1) 第三次提交
* f9021ee 第二次提交
* 0932815 第一次提交
dang@DFLubuntu:~/git-tutorial$ git branch -vv
* (头指针分离自 1f5b64b) 5ec814b 在b.txt中添加数据以产生合并冲突
  iss53                  10d78c8 在 c.txt 末尾新增一行测试数据 [issue 53]
  master                 1f5b64b 紧急处理线上问题:注册页面隐私协议单选崩溃
dang@DFLubuntu:~/git-tutorial$

此时会提示我们在是在 HEAD 分离态下产行了新提交 “头指针分离自 1f5b64b”。这是因为执行 git checkout [commit_id | branchname] 切换提交会导致 HEAD 指向该次提交,而不再指向分支,这种 HEAD 与分支的分离就会产生会 HEAD 分离态

解决 HEAD 分离态的办法:

  • 先为 HEAD 指向的提交执行 git branch branchname commit_id命令给某个提交创建一个分支
  • 再合并这个分支。
  • 最后删除这个分支。
dang@DFLubuntu:~/git-tutorial$ git branch temp 5ec814b
dang@DFLubuntu:~/git-tutorial$ git branch -vv
* (头指针分离自 1f5b64b) 5ec814b 在b.txt中添加数据以产生合并冲突
  iss53                  10d78c8 在 c.txt 末尾新增一行测试数据 [issue 53]
  master                 1f5b64b 紧急处理线上问题:注册页面隐私协议单选崩溃
  temp                   5ec814b 在b.txt中添加数据以产生合并冲突
dang@DFLubuntu:~/git-tutorial$ git checkout master
之前的 HEAD 位置是 5ec814b 在b.txt中添加数据以产生合并冲突
切换到分支 'master'
dang@DFLubuntu:~/git-tutorial$ git branch -vv
  iss53  10d78c8 在 c.txt 末尾新增一行测试数据 [issue 53]
* master 1f5b64b 紧急处理线上问题:注册页面隐私协议单选崩溃
  temp   5ec814b 在b.txt中添加数据以产生合并冲突
dang@DFLubuntu:~/git-tutorial$ git merge temp
更新 1f5b64b..5ec814b
Fast-forward
 b.txt | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)
dang@DFLubuntu:~/git-tutorial$ git branch -vv
  iss53  10d78c8 在 c.txt 末尾新增一行测试数据 [issue 53]
* master 5ec814b 在b.txt中添加数据以产生合并冲突
  temp   5ec814b 在b.txt中添加数据以产生合并冲突
dang@DFLubuntu:~/git-tutorial$ git branch -d temp
已删除分支 temp(曾为 5ec814b)。
dang@DFLubuntu:~/git-tutorial$ git branch -vv
  iss53  10d78c8 在 c.txt 末尾新增一行测试数据 [issue 53]
* master 5ec814b 在b.txt中添加数据以产生合并冲突
dang@DFLubuntu:~/git-tutorial$ git lgd
* 5ec814b (HEAD -> master) 在b.txt中添加数据以产生合并冲突
* 1f5b64b 紧急处理线上问题:注册页面隐私协议单选崩溃
| * 10d78c8 (iss53) 在 c.txt 末尾新增一行测试数据 [issue 53]
|/  
* 8441143 (tag: v0.0.1) 第三次提交
* f9021ee 第二次提交
* 0932815 第一次提交
dang@DFLubuntu:~/git-tutorial$

九,处理合并冲突:在相同的文件中工作

别忘了,Git 是一个 DVCS,尽管你能很好地规划你的计划,但你阻止不了别人做出些什么,难免别人会干扰到你的工作。

在前面,我们一边在 iss53 分支中修改了 b.txt 文件的第三行且已提交但暂未合并,同样又在 HEAD分离态下修改了 b.txt 文件的第三行并提交且已处理 HEAD分离态。

但这种在多个不同的分支中对同一个文件的同一个部分进行的不同的修改,将在合并这些分支时产生合并冲突

比如:

dang@DFLubuntu:~/git-tutorial$ git branch -vv
  iss53  10d78c8 在 c.txt 末尾新增一行测试数据 [issue 53]
* master 5ec814b 在b.txt中添加数据以产生合并冲突
dang@DFLubuntu:~/git-tutorial$ git lgd
* 5ec814b (HEAD -> master) 在b.txt中添加数据以产生合并冲突
* 1f5b64b 紧急处理线上问题:注册页面隐私协议单选崩溃
| * 10d78c8 (iss53) 在 c.txt 末尾新增一行测试数据 [issue 53]
|/  
* 8441143 (tag: v0.0.1) 第三次提交
* f9021ee 第二次提交
* 0932815 第一次提交
dang@DFLubuntu:~/git-tutorial$ git merge 10d78c8
自动合并 b.txt
冲突(内容):合并冲突于 b.txt
自动合并失败,修正冲突然后提交修正的结果。
dang@DFLubuntu:~/git-tutorial$ 
  • 此时 Git 做了合并,实际上就是在一个暂存区中暂存了产生的合并文件,但是没有自动地创建一个新的合并提交,它会暂停下来,等待你去解决合并产生的冲突。

此时执行 git status 就能看到是哪些文件产生了合并冲突:
在这里插入图片描述
再打开具体文件,就有特殊标记:
在这里插入图片描述

  • <<<<<<< HEAD 指示当前所在版本(当前是 master 分支)。
  • ======= 上半部分表示当前版本的内容,下半部分表示另一分支版本的内容。
  • >>>>>>> 10d78c8 指示被合并的分支。

(一)合并的情况

1,智能的合并

Git 的合并操作是很智能的,大多数情况是能自动完成合并的:合并 = 暂存 + 提交,比如说修改了不同的文件、相同文件的不同部分、文件名变更。

2,逻辑冲突

尽管 Git 能自动处理文件名的变更,但对于代码中对该文件的引用却不能变为更正后的名字;又比如一个用户修改了函数返回值而另一个用户还在用旧的返回值,但还是能合并;这就产生了逻辑冲突。

解决办法就是编写单元测试与持续集成。

3,编辑冲突

合并中更多的会遇到一般冲突,就是不同分支中对同一文件的同一位置进行了修改,这时就需要先查看冲突的内容,然后协商一致后,手动统一保留某一方的修改。

(二)查看冲突信息:diff或log

确定冲突信息非常重要

1,查看冲突文件的内容

因为 Git 自动暂存合并成功的结果,所以 diff 只会得到现在还在冲突状态的区别:

  1. 执行 git diff --ours 命令查看本分支对源文件的改动
    在这里插入图片描述

  2. 执行 git diff --theirs命令查看被合并分支对源文件的改动
    在这里插入图片描述

  3. 执行 git diff --base 命令查看双方对源文件的改动
    在这里插入图片描述

2,查看冲突提交

结合提交区间查看冲突提交,能让我们从更高的维度知道哪些提交中的修改在合并时产生了冲突。

首先看看双方都有做了哪些提交:

$ git log --oneline --left-right HEAD...MERGE_HEAD
< f1270f7 update README
< 9af9d3b add a README
< 694971d update phrase to hola world
> e3eb223 add more tests
> 7cff591 add testing script
> c3ffff1 changed text to hello mundo

然后再看看哪些提交在合并时产生了冲突:

$ git log --oneline --left-right --merge
< 694971d update phrase to hola world
> c3ffff1 changed text to hello mundo

最后用 -p 选项具体查看所有冲突文件的区别。

产生合并冲突时有下面几种处理方法:

(三)取消变更以取消合并

直接用用checkout把本地或远程分支的改动全部取消:

git checkout --ours
git checkout --theirs

(四)直接中断一次合并

如果此时你打算暂时不合并,且又想取消哪些特殊的标记内容,执行
git merge --abort 命令将简单地退出合并

  • 这条命令会尝试恢复到你执行合并前的状态,未暂存的变更将丢失。
dang@DFLubuntu:~/git-tutorial$ git status -sb
## master
UU b.txt
dang@DFLubuntu:~/git-tutorial$ cat b.txt
111
222
<<<<<<< HEAD
“用于产生合并冲突”333
=======
333
444
>>>>>>> 10d78c8
dang@DFLubuntu:~/git-tutorial$ git merge --abort
dang@DFLubuntu:~/git-tutorial$ git status -sb
## master
dang@DFLubuntu:~/git-tutorial$ cat b.txt
111
222
“用于产生合并冲突”333
dang@DFLubuntu:~/git-tutorial$

(五)忽略空白

有时可能只是简单地多了写空格或空行,Git 提供两个 merge 选项:

  • -Xignore-all-space 选项在比较行时完全忽略空白修改。
  • -Xignore-space-change 选项将一个空白符与多个连续的空白字符视作等价。

(六)初级手动处理

手动处理就是将冲突文件中冲突地点的内容变更为被合并分支中该文件该处的变更:

  1. 将产生冲突的本分支文件的变更修改为目标分支文件的变更,暂存并提交。
  2. 合并目标分支。
dang@DFLubuntu:~/git-tutorial$ git lgd
* 5ec814b (HEAD -> master) 在b.txt中添加数据以产生合并冲突
* 1f5b64b 紧急处理线上问题:注册页面隐私协议单选崩溃
| * 10d78c8 (iss53) 在 c.txt 末尾新增一行测试数据 [issue 53]
|/  
* 8441143 (tag: v0.0.1) 第三次提交
* f9021ee 第二次提交
* 0932815 第一次提交
dang@DFLubuntu:~/git-tutorial$ git merge 10d78c8
自动合并 b.txt
冲突(内容):合并冲突于 b.txt
自动合并失败,修正冲突然后提交修正的结果。
dang@DFLubuntu:~/git-tutorial$ cat b.txt
111
222
<<<<<<< HEAD
“用于产生合并冲突”333
=======
333
444
>>>>>>> 10d78c8
dang@DFLubuntu:~/git-tutorial$ vim b.txt	# 手动修改为合并后的版本,解决冲突。
dang@DFLubuntu:~/git-tutorial$ cat b.txt
111
222
333
444
dang@DFLubuntu:~/git-tutorial$ git add b.txt	
dang@DFLubuntu:~/git-tutorial$ git commit -m "手动处理第一次合并冲突"	# 手动处理冲突后要在合并前提交修改
[master c686035] 手动处理第一次合并冲突
dang@DFLubuntu:~/git-tutorial$ git merge 10d78c8
已经是最新的。
dang@DFLubuntu:~/git-tutorial$ git lgd
*   c686035 (HEAD -> master) 手动处理第一次合并冲突
|\  
| * 10d78c8 (iss53) 在 c.txt 末尾新增一行测试数据 [issue 53]
* | 5ec814b 在b.txt中添加数据以产生合并冲突
* | 1f5b64b 紧急处理线上问题:注册页面隐私协议单选崩溃
|/  
* 8441143 (tag: v0.0.1) 第三次提交
* f9021ee 第二次提交
* 0932815 第一次提交

在这里插入图片描述

(七)中级手动处理

如果产生冲突的地方实在太多,初级的手动输入将非常困难,这时可以使用 Git 提供的 git merge-file 命令将产生冲突的本分支文件、目标分支文件和产生的合并文件合并成一个文件,它会自动处理各个文件中的差异。

1,首先执行 git ls-files -u 命令获取产生冲突的本分支文件、目标分支文件和产生的合并文件的 Git blob 对象的实际 SHA-1 值

dang@DFLubuntu:~/git-tutorial$ git merge 10d78c8
自动合并 b.txt
冲突(内容):合并冲突于 b.txt
自动合并失败,修正冲突然后提交修正的结果。
dang@DFLubuntu:~/git-tutorial$ git ls-files -u
100644 641d57406d212612a9e89e00db302ce758e558d2 1	b.txt
100644 aae594d30ee629ed128adf2a23767236f72d59f9 2	b.txt
100644 337d41392b5b00970acb84577783563ef5796ba5 3	b.txt
  • 这三个文件都在各自的暂存区中存在,所以能查出来。
  • Stage 1 是文件共同的祖先版本。stage 2 是本分支版本,stage 3 是目标分支版本。

2,然后取出这三个文件的内容:

dang@DFLubuntu:~/git-tutorial$ git show :1:b.txt > b.common.txt
dang@DFLubuntu:~/git-tutorial$ git show :2:b.txt > b.mine.txt
dang@DFLubuntu:~/git-tutorial$ git show :3:b.txt > b.others.txt
dang@DFLubuntu:~/git-tutorial$ ls -l
总用量 24
-rw-rw-r-- 1 dang dang   12 1121 23:33 a.txt
-rw-rw-r-- 1 dang dang   12 1122 03:55 b.common.txt
-rw-rw-r-- 1 dang dang   42 1122 03:56 b.mine.txt
-rw-rw-r-- 1 dang dang   16 1122 03:56 b.others.txt
-rw-rw-r-- 1 dang dang   87 1122 03:47 b.txt
-rw-rw-r-- 1 dang dang 1019 1122 00:53 index.html
dang@DFLubuntu:~/git-tutorial$ 

3,执行git merge-file 命令合并文件:

dang@DFLubuntu:~/git-tutorial$ git merge-file -p b.mine.txt b.common.txt b.others.txt > b.txt
dang@DFLubuntu:~/git-tutorial$ ls -l
总用量 24
-rw-rw-r-- 1 dang dang   12 1121 23:33 a.txt
-rw-rw-r-- 1 dang dang   12 1122 04:06 b.common.txt
-rw-rw-r-- 1 dang dang   42 1122 04:06 b.mine.txt
-rw-rw-r-- 1 dang dang   16 1122 04:06 b.others.txt
-rw-rw-r-- 1 dang dang   98 1122 04:07 b.txt
-rw-rw-r-- 1 dang dang 1019 1122 00:53 index.html
dang@DFLubuntu:~/git-tutorial$ cat b.txt
111
222
<<<<<<< b.mine.txt
“用于产生合并冲突”333
=======
333
444
>>>>>>> b.others.txt
dang@DFLubuntu:~/git-tutorial$
  • 使用 --help 选项查看 merge-file 支持的合并模式。

4,然后执行 git clean -f 删除额外的文件:

dang@DFLubuntu:~/git-tutorial$ git clean -f
正删除 b.common.txt
正删除 b.mine.txt
正删除 b.others.txt

5,最后打开冲突文件,根据提示手动处理合并冲突。

(八)使用可视化工具

有许多可视化的冲突处理工具,能更精确地显示冲突的内容并作出处理,比如解决 Git 冲突的 14 个建议和工具

现在的 IDE 中也都集成了 Git 插件,操作起来相当方便,比如IDEA 使用Git图文详解在 Pycharm 中玩转 GitHub(图文详解)

或许是由于时间紧迫, 我们常常倾向于直接用合并工具选取这份或那份代码变更了事。这种贪图方便的行为是应该被抵制的。 如果经过difflog工具配合“其他” 版本的分析之后,你依然无法确定解决冲突的方式, 那么你就应该撤销合并。

十,撤消合并

假设现在在一个主题分支上工作,不小心将其合并到 master 中,现在提交历史看起来是这样:
在这里插入图片描述

(一)修复引用

如果这个不想要的合并提交只存在于你的本地仓库中,最简单且最好的解决方案是在爆冲突的 git merge 后运行 git reset --hard HEAD~,实际上就是重置分支指向,或者执行git reset --merge 命令重置合并操作,然后它们看起来像这样:
在这里插入图片描述
在这里插入图片描述

dang@DFLubuntu:~/git-tutorial$ git lgd
*   c686035 (HEAD -> master) 手动处理第一次合并冲突
|\  
| * 10d78c8 (iss53) 在 c.txt 末尾新增一行测试数据 [issue 53]
* | 5ec814b 在b.txt中添加数据以产生合并冲突
* | 1f5b64b 紧急处理线上问题:注册页面隐私协议单选崩溃
|/  
* 8441143 (tag: v0.0.1) 第三次提交
* f9021ee 第二次提交
* 0932815 第一次提交
dang@DFLubuntu:~/git-tutorial$ git reset --hard HEAD~
HEAD 现在位于 5ec814b 在b.txt中添加数据以产生合并冲突
dang@DFLubuntu:~/git-tutorial$ git lgd
* 5ec814b (HEAD -> master) 在b.txt中添加数据以产生合并冲突
* 1f5b64b 紧急处理线上问题:注册页面隐私协议单选崩溃
| * 10d78c8 (iss53) 在 c.txt 末尾新增一行测试数据 [issue 53]
|/  
* 8441143 (tag: v0.0.1) 第三次提交
* f9021ee 第二次提交
* 0932815 第一次提交
dang@DFLubuntu:~/git-tutorial$ 

缺点:

  • 它会重写历史,在一个共享的仓库中这会造成问题。如果其他人已经有你将合并的提交 M ,你应当避免使用 reset。
  • 如果有任何其他提交在 M 之后被创建,那么这个方法也会无效;

(二)还原提交

如果移动分支指针并不适合你,Git 给你一个生成一个新提交的选项,提交将会撤消一个已存在提交的所有修改。在这个特定的场景下,执行 git revert -m 1 HEAD 将以创建一个新提交的方式恢复当前分支在合并动作之前的内容,然后它们看起来像这样:
在这里插入图片描述
缺点:

  • 多产生了一次提交。

撤销合并后,应该考虑:

  • 分支重构: 最干净利落的解决方案可能就是以重构的方式对其中一个分支进行清理,并执行交互式变基,但这是一个很大的工作量。
  • 分小步合并:如果两个分支中的一个分支存在细粒度的提交,我们可以采用一次一提交的方式来处理。因为粒度越细的提交所带来的冲突往往越容易解决。但如果这种提交的数量很大,它也可能会非常耗时。无论是哪一种情况,为此创建一个本地分支都是值得推荐的做法。
  • 丢弃与捡取:在某些情况下,拒绝某个劣质分支上的某些修改是一个不错的做法,可以通过cherry-pick命令来对其采取些改进措施。
  • 评级和测试:如果受合并影响的功能可以通过测试,那么我们自然在解决冲突时候据此来推演, 并将其结果改善到能通过所有测试为止。

十一,基于分支的开发工作流

现在了解分支的基本操作,那么接下来就介绍一些常见的利用分支进行开发的工作流程。

(一)长期分支

许多使用 Git 的开发者都喜欢使用这种方式来工作,比如只在 master 分支上保留完全稳定的代码。他们还有一些名为 develop 或者 next 的平行分支,被用来做后续开发或者测试,在确保其中已完成的主题分支能够通过所有测试,并且不会引入更多 bug 之后,就可以合并入主干分支中,等待下一次的发布。

感受上是这样的一种流水线:
在这里插入图片描述
实际上是这样的:
在这里插入图片描述

稳定分支的指针总是在提交历史中落后一大截,它随着你的提交而不断右移,而前沿分支的指针往往比较靠前。

总之,就是利用稳定分支的“Fast-forward”合并实现稳定版本的快进效果。

好处就是在一个非常巨大或者复杂的项目中工作时很方便。

(二)主题分支

主题分支是一种短期分支,它被用来实现单一特性或其相关工作。前面用到的 hotfix 和 iss53 都是主题分支,在它们中提交了一些更新,并且在它们合并入主干分支之后,又删除了它们。

这种模式,让不同的流水线中每个分支都仅与其目标特性相关,因此,在做代码审查之类的工作的时候就能更加容易地看出你做了哪些改动。 你可以把做出的改动在主题分支中保留几分钟、几天甚至几个月,等它们成熟之后再合并,而不用在乎它们建立的顺序或工作进度。

主题分支的特点就是需求导向,这就让处理临时的问题变得非常容易:
在这里插入图片描述

在这里插入图片描述
然后选择性地进行分支合并:
在这里插入图片描述

(三)查看分支的合并历史与提交集合

1,查看分支的合并历史

分支的合并可能会产生两个及以上的父提交,可执行 git log --merges命令查看分支合并历史
在这里插入图片描述

2,查看分支的提交区间

在分支未合并之前,可以用提交区间来解决“这个分支还有哪些提交尚未合并到主分支?”的问题。

此时:
在这里插入图片描述

(1)双点语法

双点语法可以选出不在一个分支而在另一个分支中的提交:

$ git log master..experiment
D
C
$ git log experiment..master
F
E
$ git log origin/master..HEAD	# 在当前分支中而不在远程 origin 中的提交。如果你执行 git push 并且你的当前分支正在跟踪 origin/master,由 git log origin/master..HEAD 所输出的提交就是会被传输到远端服务器的提交。

如果你留空了其中的一边, Git 会默认为 HEAD。

(2)多点语法

Git 允许你在分支名前加上 ^ 字符或者 --not 来指明选出不在该分支中的提交:

$ git log refA..refB
等价于
$ git log ^refA refB
等价于
$ git log refB --not refA

多点语法支持更多的分支范围:

$ git log refA refB ^refC
$ git log refA refB --not refC
(3)三点语法

三点语法可以选择出被两个分支之一 包含但又不被两者同时包含的提交:

$ git log master...experiment
F
E
D
C

--left-right选项会显示每个提交到底处于哪一侧的分支。 这会让输出数据更加清晰:

$ git log --oneline --left-right master...experiment
< F
< E
> D
> C
$ git lgd
* 0a59be8 (HEAD -> master) master分支第四次提交
* 041f24a master分支第三次提交
| * eda3ebc (experiment) experiment分支第二次提交
| * 21f7242 experiment分支第一次提交
|/
* 948f989 master分支第二次提交
* 2154b36 master分支第一次提交
$ git log --oneline --left-right master...experiment
< 0a59be8 (HEAD -> master) master分支第四次提交
< 041f24a master分支第三次提交
> eda3ebc (experiment) experiment分支第二次提交
> 21f7242 experiment分支第一次提交
(4)第一父提交

如果用特性分支来开发某一个特性,在主分支中合并特性分支的结果就是主分支中多了一项特性,那么就可能存在这么一种请况:在稳定的master分支中,这次合并结果的上一次提交,就是对上一个特性的合并。这一串地特性合并提交,就是一份历史概览。
在这里插入图片描述
此时可以结合上面的提交区间,执行 git log --first-parent --oneline refA..refB命令选择第一父提交区间

在这里插入图片描述

十二,存储当前工作状态:stash

在前面,我们在一个分支中工作时突然遇到 bug 要修复,就立马创建了一个主题分支去修复问题。但是当前的工作只进行到
一半,不适合进行提交。这时候就可以使用贮藏的功能:先将
工作目录中文件的修改与暂存区中的改动状态记录下来,保存到贮藏栈上,用户就能切换到其他分支处理临时的工作。等到临时工作处理完毕之后,再切换分支,将文件恢复到原来的状态。

执行 git stash命令将贮藏当前文件状态
执行 git stash list命令能查看所有贮藏

$ mkdir test
$ cd  test
test$ git init
Initialized empty Git repository in test/.git/
test$ echo "1111" >> a.txt
test$ git add .
test$ git commit -m "第一次提交"
[master (root-commit) 5b8d68e] 第一次提交
 1 file changed, 1 insertion(+)
 create mode 100644 a.txt
test$ git checkout -b dev
Switched to a new branch 'dev'
test$ echo "111" >> dev.txt
test$ git add .
test$ git status
On branch dev
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
        new file:   dev.txt

test$ git stash
Saved working directory and index state WIP on dev: 5b8d68e 第一次提交
test$ git status
On branch dev
nothing to commit, working tree clean
test$ git stash list
stash@{0}: WIP on dev: 5b8d68e 第一次提交

默认情况下,git stash 只会贮藏已跟踪的文件。
如果指定 --include-untracked-u 选项就能同时贮藏未跟踪文件
如果指定 --all-a 选项就能同时贮藏被忽略的文件
如果指定 --patch 选项能交互式地指定需要贮藏哪些修改

执行 git stash apply stash@{number} 命令将重新应用指定的的贮藏到工作目录
执行 git stash apply stash@{number} --index命令将重新应用指定的的贮藏到暂存区
如果贮藏了一些内容,然后继续在该分支上工作,那么在重新应用贮藏时可能会遇到冲突问题。如果确实在修改后还想要应用之前的贮藏(比如简化多步骤地反悔操作),那么可以执行 git stash branch <new branchname> 命令能在储藏中创建一个新分支,然后手动检出贮藏工作时所在的提交,在重新在那应用贮藏,在应用成功后丢弃贮藏,最后就能用之前的贮藏覆盖贮藏之后做出的修改。

执行 git stash drop stash@{number}命令将丢弃指定的贮藏
执行 git stash pop 将应用最新的贮藏并立即从贮藏栈出栈

十三,整合分支修改的另一种方法——变基:rebase

(一)什么是变基

这里将通过一个演示来表现 rebase 变基的特点。

假设现在 git 本地仓库是这样的:
在这里插入图片描述

  • dufu 分支上有一些提交,并已经合并到 master 分支。
  • libai 分支上有一些提交,合并到 master 分支后,libai 分支上又有一些提交。
  • 在 libai 分支上创建了一个 test_rebase 分支,HEAD 指向这个分支。

然后执行 git rebase master 将 test_rebase 分支变基到 master 分支,效果就是这样:
在这里插入图片描述
在 test_rebase 分支 rebase master 的效果就是将 test_rebase 分支的起始点重放到 master 分支处。

看起来rebase变基就是重放。 但实际上,变基后的提交是拥有新 SHA-1 值的新提交,原来的会在短时间内保持存在,直到被垃圾回收机制回收。

(二)为什么要变基

1,简化分支路线

既然重放的效果就是将一个分支的起点放到另一个分支上,那么,就能使用变基来简化分支合并的路线,避免 log 出来一大堆的分支路线图。

还是回到这个状态:
在这里插入图片描述
在这里插入图片描述

假设现在要将 libai 分支合并到 master 分支,按照之前的方法,要先从 libai 检出到 master,然后执行 merge,效果就是这样的:
在这里插入图片描述
如果继续在 libai 分支中进行开发,就可能存在多次与 master 的合并,那么,可能是下面这种效果:
在这里插入图片描述
但如果是 rebase 而不是直接 merge 的话,可能是下面这种效果:
在这里插入图片描述
在这里插入图片描述

2,获取分支内容

前面基于 merge 实现的分支合并更利于处理主题工作,而对于某长期分支的开发合并不太友好。

因为长期分支需要很长的时间才能完成开发,同时 master 分支也在不断变更前进中,为了在这种并行关系中保持必要的基础程序架构的一致性,就需要定期将主分支中的变更合并到开发分支中,这就和前面 merge 操作相反,然后各个分支又独立前进,然后又定期合并,如此共同前进。同时也会出现相当复杂的分支路线图。

回到某个状态,假设 libai 分支的继续开发需要 master1 和 master3 提交中的内容:

在这里插入图片描述

在 libai 中 rebase master,获取所需内容后再继续开发,效果就是这样:
在这里插入图片描述

变基的结果就是能在 libai 分支的工作目录中看见原本只属于 master 分支的内容和属于 dufu 分支工作目录中的内容(之前合并到 master 中)。

(三)怎样变基

1,一般变基

变基很简单,在当前分支中执行 git rebase <basebranch> 命令将目标分支重放到当前分支。

  • 这条命令的效果就是将当前分支的起点重放到 basebranch 基分支上。

在这里插入图片描述

也能执行 git rebase <basebranch> <branch>命令明确指示将分支变基到基分支

2,只变基子分支的内容

但变基时要注意一个问题,即变基子分支时是否需要携带父分支中的提交。

假设:
在这里插入图片描述

如果这时我们将 libai 变基到 master,命令默认会将 libai 及其父分支 dufu 相关的的提交都重放过去:
在这里插入图片描述

而执行 git rebase --onto <basebranch> <p_topicbranch> <s_topicbranch> 命令将只在s_topicbranch子分支而不在p_topicbranch父分支的提交重放到基分支
在这里插入图片描述

3,细粒度变基

  1. 检出到待变基的分支;
  2. 确定待变基分支的父分支;
  3. 检查待变基分支的提交区间;
  4. 确定目标位置;
  5. 执行变基。

(四)变基的风险

在前面按说过,变基操作的实质是丢弃一些现有的提交,然后相应地重放一些内容一样但实际上 SHA-1 值不同的提交。

这种情况就要求我们最好不要对已经推送到远程仓库且极有可能已经被别人引用的提交或分支进行变基,否则将导致巨大的应用混乱。

到此为止,在 本地的 Git 操作已经差不多将完了,接下来就开始与他人合作吧。

十四,远程仓库与远程分支

为了能在任意 Git 项目上协作,你需要知道如何管理自己的远程仓库以及远程分支。 这里只是简单介绍,更详细的介绍和应用会在后面再说。

远程仓库是指托管在因特网或其他网络中的你的项目的版本库。 你可以有好几个远程仓库,与他人协作涉及管理远程仓库以及根据需要推送或拉取数据。

远程引用是对远程仓库的引用,包括分支、标签等等。 你可以通过 git ls-remote <remote> 命令获得远程引用的完整列表, 或者通过 git remote show <remote> 获得远程分支的更多信息。

1,远程仓库与远程分支

假设你的网络里有一个在 git.ourcompany.com 的 Git 服务器。 执行 clone 命令将自动把某个仓库添加为远程仓库并命名为 origin,然后会抓取所有数据,再创建一个指向这个远程仓库的 master 分支的指针,并且在本地将其命名为 origin/master。 Git 也会给你一个与 origin 的 master 分支指向同一个地方的本地 master 分支,这样你就有工作的基础:
在这里插入图片描述
然后你在本地的 master 分支做了一些工作。而同一段时间内如果有其他人推送提交到远程仓库并且更新了它的 master 分支,那么两处的 master 将在远程 master被更新后分道扬镳。 只要你不与 origin 通信(抓取数据或者推送数据),你的 origin/master 指针就不会移动:
在这里插入图片描述
如果此时执行 fetch 从远程仓库抓取本地没有的数据,origin/master 指针将移动到远程 master 最新的位置:
在这里插入图片描述

2,远程跟踪分支

本地仓库与远程仓库的交流是如此重要,它通过本地分支与远程的交流来实现,最方便的就是设置远程跟踪分支,它是与远程分支有直接关系的本地分支。

前面说到,克隆远程仓库后会创建一对配对的 origin/master 和 master,效果就是自动地创建一个跟踪 origin/master 的 master 分支,这个本地的 master 分支就是一个远程跟踪分支:

~/test $ git clone https://github.com/libgit2/libgit2
Cloning into 'libgit2'...
remote: Enumerating objects: 113722, done.
remote: Counting objects: 100% (113722/113722), done.
remote: Compressing objects: 100% (32170/32170), done.
remote: Total 113722 (delta 81040), reused 112136 (delta 79581), pack-reused 0
Receiving objects: 100% (113722/113722), 56.05 MiB | 4.61 MiB/s, done.
Resolving deltas: 100% (81040/81040), done.

~/test $ cd libgit2

~/test/libgit2 $ git remote -v
origin  https://github.com/libgit2/libgit2 (fetch)
origin  https://github.com/libgit2/libgit2 (push)

~/test/libgit2 $ git branch -vv
* master 6fdb1b2f5 [origin/master] Merge pull request #6122 from libgit2/ethomson/cleanup  

执行 git checkout -b <branch> <remote>/<branch> 命令将从远程分支检出一个本地的远程跟踪分支。这是一个十分常用的操作所以 Git 提供了 --track 快捷方式:

$ git checkout --track origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'

3,添加远程仓库

管理i远程仓库的前提当然是你拥有一个远程仓库并能够与它通信。

我们通常使用 GitHub 来创建远程仓库,当然能你也能在自己的服务器上搭建 Git,然后执行 git remote add <shortname> <url> 命令添加已有的远程仓库。

  • <shortname> 是远程仓库 URL 的简称,通常使用 origin 来表示。使用 clone 命令克隆远程仓库将其默认添加它为远程仓库并命名为 origin。
  • <url>表示连接远程仓库的方式,比如git@github.com:username/repositoryname.git、https://github.com/username/repositoryname.git等。

比如添加远程仓库first-pr

$ git remote add origin https://github.com/ituring/first-pr.git

添加成功后就能在本地管理远程仓库了,比如:
在这里插入图片描述

4,查看远程仓库

执行 git remote 命令将列出所有已添加远程仓库的简称

$ git remote
origin

执行 git remote -v 命令将列出所有已添加远程仓库的简称与其对应的 URL

$ git remote -v
origin  https://github.com/ituring/first-pr.git (fetch)
origin  https://github.com/ituring/first-pr.git (push)

还能执行 git remote show <shortname> 命令查看更多远程仓库的信息

$ git remote show origin
* remote origin		# 远程仓库简称
  Fetch URL: https://github.com/ituring/first-pr	# 抓取信息的地址
  Push  URL: https://github.com/ituring/first-pr	# 推送信息的地址
  HEAD branch: gh-pages								
  Remote branches:									
    feature/move-jquery-from-cdn-to-local tracked	# 已被本地跟踪的远程纷纷年至
    gh-pages                              tracked
  Local branch configured for 'git pull':			
    gh-pages merges with remote gh-pages			# 执行 git pull 时哪些本地分支可以与它跟踪的远程分支自动合并
  Local ref configured for 'git push':
    gh-pages pushes to gh-pages (up to date)		# 默认推送到哪个远程分支

5,从远程仓库中抓取与拉取内容

执行 git branch -a获取本地和远程的所有分支
在这里插入图片描述

执行 git fetch <shortname> 命令能抓取远程仓库中有但本地没有的内容

$ git fetch origin
remote: Counting objects: 43, done.
remote: Compressing objects: 100% (36/36), done.
remote: Total 43 (delta 10), reused 31 (delta 5)
Unpacking objects: 100% (43/43), done.
From https://github.com/ituring/first-pr.git
 * [new branch]      master     -> origin/master
 * [new branch]      ticgit     -> origin/ticgit
  • fetch 并不会修改工作目录中的内容,只会拥有那个远程仓库中所有分支的引用,需要手动合并来将远程变更应用到本地。

执行 git pull <shortname> 命令能拉取远程仓库中有但本地没有的内容并自动合并

  • 不管这个 shortname 是显式地设置还是通过 clone 或 checkout 命令创建的,git pull都会先查找当前分支所跟踪的远程分支,然后从服务器上抓取数据并尝试合并。
  • 通常单独显式地使用 fetchmerge 命令。

6,推送到远程仓库

执行 git push <remote> <local_branch>[:<remote_branch>] 命令将本地 branch 分支推送到 remote远程仓库 [的remote_branch分支],比如将本地的稳定分支 master 分支推送到 origin远程仓库:

$ git push origin master
  • 这种情况当然是推送到被跟踪的远程分支(又称上游分支)。

7,删除与恢复远程分支

假设你已经通过远程分支和协作者已经完成了一个特性开发, 并且将其合并到了远程仓库的 master 分支(或任何其他稳定代码分支)。 执行 git push <remote> --delete <remotebranch> 命令将删除远程仓库中指定的远程分支。例如:

$ git push origin --delete serverfix
To https://github.com/schacon/simplegit
 - [deleted]         serverfix

当然也能恢复被删除的远程分支:

  1. 执行 git reflog --date=iso 命令以标准时间格式展示引用日志,找到目标分支最后一次的提交的 SHA-1 值;
  2. 执行 git checkout -b <branch> <remote>/<branch> 命令从远程分支检出一个本地的远程跟踪分支,就设置了跟踪效果;
  3. 执行 git push <remote> <branch>命令推送本地分支到远程分支

8,远程仓库的重命名与移除

执行 git remote rename <oldname> <newname> 命令将重命名远程仓库简称

执行 git remote -rm <shortname> 命令将移除本地的远程仓库

十四,整合分支修改的另一种方法——捡取:cherry-pick

如果说 rebase 变基是将整条分支上的所有提交都重放另一分支上的话,cherry-pick 捡取则允许将分支上某些提交复制到另一分支上

此时的状态:
在这里插入图片描述
在这里插入图片描述

目标:将 client 分支第三条提交捡取到 dev 分支上。

切换并捡取后显示有冲突,
在这里插入图片描述
解决冲突后再提交就行:
在这里插入图片描述
目标完成:
在这里插入图片描述
执行 git cherry-pick <branchname>命令将捡取指定分支的最新提交

更多cherry-pick详情可参考 阮一峰:git cherry-pick 教程

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值