git使用进阶——stash及cherry-pick的理解和实例
Posted 涂宗勋
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了git使用进阶——stash及cherry-pick的理解和实例相关的知识,希望对你有一定的参考价值。
俗话说,条条大路通罗马,很多时候要解决一个问题的办法都不止一个,但是不同的办法效率可能就有所区别。在git使用的时候,stash
和pick
就是其中两个可以一定程度上提升效率的功能。
stash
场景需求分析
假如有这样一个需求场景:
我有一个master分支,基于master
分支拉取了dev
分支,并完成了一个用户管理的功能开发,现在已经正式发布使用。
那么现在我有了一个新的工作任务,需要继续进行一个商品管理的功能开发,于是我增加了一个商品信息实体类和一个商品管理的业务接口类,分别是Goods
和GoodsController
。
但是呢,这个整体功能并没有开发完成,而是只开发了新增商品和查询,并且还没有完成测试。例如当前的业务接口代码如下:
/**
* @Author tuzongxun
* @Date 2021/9/19
*/
@RestController
@RequestMapping("/goods")
public class GoodsController {
@PostMapping
public String addGoods(@RequestBody Goods goods){
System.out.println(goods);
return "add success";
}
@GetMapping
public List<Goods> findGoodsList(){
List<Goods> list=new ArrayList<>();
list.add(new Goods("apple",5.5));
return list;
}
}
就在这个时候,发现之前的用户管理功能有问题,需要立即修改。
怎么办呢,自然是需要基于master
分支拉取一个新的修复分支来紧急修复这个问题。
于是,我执行了git checkout master
,准备切换到master
分支之后再创建一个fix_user
分支。
但是当我再来看我的代码的时候,就会发现关于商品管理的代码也都还在,例如使用git status
会看到如下结果:
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
src/main/java/com/example/gitdemo/controller/GoodsController.java
src/main/java/com/example/gitdemo/model/Goods.java
nothing added to commit but untracked files present (use "git add" to track)
这个时候,如果我直接创建新的fix_user
分支,实际上还是会把这些没有提交的代码带过去,但是我这个分支就是为了紧急处理用户管理的问题,把商品管理的也带过去显然是不合适的。
怎么办呢,一般可能会有这些可选的操作:
- 直接删除掉商品管理相关的内容,然后在
master
上创建fix_user
,然后进行问题修复。但是由于商品管理的代码并没有在dev上提交,所以一旦现在删除,那么实际上就相当于之前的功夫都白费了,于是可能就会有第二种操作。 - 基于上边所说,可能会有人选择先把商品管理的这些内容手动找个地方备份起来,然后再删除,删完了再创建
fix_user
分支,然后进行用户管理的功能修复。那么这样操作确实可以保证在不影响问题修复开发的同时,还保证了之前的功夫不会白费。但是问题是,如果现在这个功能涉及到的文件并不止一两个,而是十几个,或者几十个,那么这种手动备份和后边的手动恢复就又会花费大量的时间,且还会增加很多的犯错率。那么既然手动备份和恢复也不好,就可能又有如下的第三种选择。 - 既然手动备份可能麻烦,而直接删除又会丢失之前的工作内容,那么索性就直接在
dev
上把商品管理功能的代码提交了算了。然后再切换到master
,再基于master创建fix_user
分支进行问题修复。这样的操作理论上是可以的,并且可能也有不少人就是这样干的。只不过在实际开发的时候也会有一些问题,比如从规范性上讲,没有开发完成和测试完成的功能一般是不建议提交的,这样就会增加多余的提交记录。那么之所以这样干,实际上就是为了一方面不丢失之前的功夫,另一方面又不想手动的管理这种备份,那么对于这种需求,git刚好就提供了可用的解决方案。 - git有一个功能叫
stash
,翻译过来是贮藏,有些教程上把这个操作叫做保存现场。利用这个功能,就可能即备份了当前修改的内容,又不需要手动备份,同时还不会增加新的提交记录。
注:实际上还有一种情况,当master和dev当前commit版本不一致时,没有commit之前是无法checkout的,上边可以checkout就是因为当前master和dev的版本一致。
stash操作
stash保存
这个功能,其实很久前我就听过,当时的同事还曾给我推荐过,但是由于那时候的git使用没有太多要求,所以一直就没有去了解和使用,但是既然是能提高效率的,自然还是了解了学会了更好。
在上边,我已经从dev
切换到了master
,现在就再切回dev
,之后执行git stash
保存之前增加的文件,然后输出如下:
No local changes to save
看到上边的输出可能会很奇怪,明明有两个新的文件,为什么说没有改变呢?实际上,是因为这两个文件都是全新的文件,现在只是存在于本地文件系统中,还没有被git管理,git stash
操作的是被git管理了的内容。
基于之前的知识可以知道,这里需要使用add
操作把新增的文件交给git管理,例如我使用了git add .
之后,使用git status
输出结果如下:
On branch dev
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: src/main/java/com/example/gitdemo/controller/GoodsController.java
new file: src/main/java/com/example/gitdemo/model/Goods.java
这时候再执行git stash
操作后就有了新的输出:
Saved working directory and index state WIP on dev: ce1c82e add user manage
stash查询
从上边的提示来看应该是stash
保存成功了,可以使用git stash list
查询,结果如下:
stash@{0}: WIP on dev: ce1c82e add user manage
有查询结果,证明确实保存成功了,这时候再使用git status
就会看到和使用commit
后的结果一样:
On branch dev
nothing to commit, working tree clean
同时,在开发工具或者文件目录查看,会发现之前为了商品管理而增加的两个类文件都已经不见了,好像是回到了商品管理开发之前的状态。那么这个时候,就可以愉快的切换到master
然后创建fix_user
进行问题修改了。
stash恢复
当前用户管理的功能修复完成后,自然是需要再切换回dev
重新开发商品管理未完成的功能,切换回去之后就需要把之前stash
存下来的内容恢复回来,例如git stash pop
,输出如下:
On branch dev
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: src/main/java/com/example/gitdemo/controller/GoodsController.java
new file: src/main/java/com/example/gitdemo/model/Goods.java
Dropped refs/stash@{0} (b8194bcd302c48b9044e4ec9ec10e9105b0e4e72)
上边的输出,可以看到在最后一行进行了一个删除操作,再使用git stash list
会看到已经查不到之前存的那个记录了。
如果需要恢复的同时,保留stash的内容,可以使用git stash apply
操作。
指定名称保存
上边的操作确实是可以的,但是实际上也有些小问题,首先就是之前查看记录时输出如下:
stash@{0}: WIP on dev: ce1c82e add user manage
实际上,我这里保存的是商品管理的功能,但是从上边的输出完全看不出来。那么当stash的记录很多之后,或者间隔时间长了之后,很可能就难以分辨到底这个记录有些什么内容,所以一般在stash的时候可以指定一个名称,以方便后续的使用,例如这里再执行git stash save "goods add and find"
,然后再执行git stash list
查询,输出结果如下:
stash@{0}: On dev: goods add and find
指定记录恢复
除了git stash
有些小问题外,git stash pop
或者git stash apply
也有些问题,那就是他们只会恢复最近的一次记录,如果有需求需要恢复其他记录,就需要其他的操作,例如git stash pop stash@{0}
。
注:stash恢复也可能会有冲突。
cherry-pick
除了stash,pick也是一个很好用的功能,可以用于代码的转移,例如有这样一个场景:
需求场景分析
上边讲stash
时说,先在dev
中stash
,然后切换到master
创建新的分支fix_user
修复用户管理的bug,之后再切换回dev
进行商品管理的后续开发。
假如这里在fix_user
分支修复完用户管理的bug后,我忘记了切换回dev
,而是直接在fix_user
分支中恢复了商品管理stash
的代码,并且进行了功能完善和后续测试,然后进行了代码的提交。
这时候使用git log --pretty=oneline --abbrev-commit
查看日志时输出如下:
d2ce68d (HEAD -> fix_user) add goods and find
ce1c82e (master, dev) add user manage
02087dc init
然后从日志中发现分支用错了,我需要切换回dev
,这时候怎么办呢,就可以使用pick
功能进行代码的转移,准确的说应该是提交的复制。
cherry-pick操作
针对上边所说,完整cherry-pick
操作如下:
- 使用
git checkout dev
切换到dev
分支; - 使用
git cherry-pick d2ce68d
把fix_user
中的提交复制过来。
这里需要注意的,这个复制只是复制内容,而不会复制id,例如再使用git log --pretty=oneline --abbrev-commit
输出结果如下:
69be4a4 (HEAD -> dev) add goods and find
ce1c82e (master) add user manage
02087dc init
可以看到当前已经在dev
分支了,并且最新的提交已经包含了add goods and find
,但是前边的id是69be4a4
而不是d2ce68d
。
注:上边的代码转移需求,使用cherry-pick
并不是唯一的办法,但是是相对更简单快捷的方式。同时,cherry-pick
也可能造成代码冲突。另外,如果需要转移多次提交,需要从前往后一次次的操作而不能直接到最后一次提交。
以上是关于git使用进阶——stash及cherry-pick的理解和实例的主要内容,如果未能解决你的问题,请参考以下文章