git常见场景食用指南

这里不从0开始介绍什么是git、怎么使用git,而是来关注一些日后使用git时可能遇到的常见场景需要怎么进行处理

Rebase

我所参与的社区项目的工作流里明确指出了将新的修改提交pr前需要rebase主分支,我们先来看一下什么是rebase,官方给的解释是这样的:当执行rebase操作时,git会从两个分支的共同祖先开始提取待变基分支上的修改,然后将待变基分支指向基分支的最新提交,最后将刚才提取的修改应用到基分支的最新提交的后面

看着有点抽象,结合例子来解释一下:

css 复制代码
A---B---M      master
    |
    ----C---D  local

在这个例子里,你基于主分支的 commit B 建立了一个新分支local并在local分支上创建了commit C D,你的同事张三向主分支提交了commit M ,现在你需要把你的local分支的修改提交到主分支,但你不能直接提交pr,因为你的local分支缺少主分支的M commit修改,直接提交会发生conflict,在local分支进行操作:先 git fetch upstream拉取主仓库最新提交之后,再使用 git rebase upstream/master(假设主分支所在仓库在你本地仓库的remote关联名叫upstream)后,你的local分支的commit历史会变成:

css 复制代码
A---B---M---C---D       local

这样你的local分支就同步了主分支的最新提交,并在此基础上合并了你的新修改(值得注意的是,在rebase master分支的时候,如果M commit对代码的修改和你的C D commit对代码的修改存在交集,那么rebase会发生conflict,需要你对commit历史进行重写提交,处理冲突),这个时候你就可以把你的local分支向主仓库master分支提交pr了

(如果在rebase时发生conflict需要解决冲突重写commit历史时,出于某些原因,你需要放弃这次rebase,使用命令 git rebase --abort,注意这只有在rebase发生冲突未完成时可以使用,如果rebase成功将无法丢弃)

总结起来就是两个字:变基,即改变你的本地分支创建时的基底,你的commit都会被接到这个新基底上

【这是rebase的主要常见用法,还有些用法(例如git rebase HEAD~1之类的)感兴趣可以自行了解】

rebase的好处是显而易见的:主分支的commit历史是一条非常干净的直线,每个commit节点都是有意义的修改,可读性高,例如这是我参与的一个社区项目的commit历史记录:

​编辑

缺点也是有的:rebase之后,你的本地分支是基于主分支的哪一个commit节点创建出来的新分支在commit历史上就不可见了,但是在多人协作开发项目中,更加注重多人的开发成果能否稳定的合并进同一个分支,个人提交顺序先后反而不是特别重要

Merge

同样作为合并代码的命令,merge对commit历史的修改是这样的:

css 复制代码
A---B---C---D   master
    |
    ----E---F   local

在local分支下执行命令git merge master后:

css 复制代码
A---B---C---D      master
    |
    ----E---F---G   local

Merge之后,master分支的commit C D会合并为一个提交G,拼接到local分支的历史上(注意在merge的时候同样有可能需要处理冲突)

这样做的好处是,对于个人开发者而言,他的commit历史是足够清楚的,缺点是commit历史上会多出许多不必要的commit,当上游主分支的更新足够频繁时,这种commit历史会变得极其冗杂,可读性极低

这只是merge的简单用法,详细的Fast - forward、non - Fast - forward和squash三种合并模式,还有许多其他参数可以自行了解

Reset

reset的作用是进行版本回退,原理是通过reset命令将HEAD指针指向指定的commit节点,指定节点可以是形如aabbcc的节点哈希值,也可以形如HEAD~2(将HEAD指针指向当前节点的前两个,即倒数第三个)

一共有三个模式:soft,mixed,hard,分别来介绍

  • hard:git reset --hard HEAD~1

    css 复制代码
    A---B (HEAD)
    • 该命令会将HEAD指针从B指向前一个的A,并且完全抹除A之后的所有内容修改(无法找回),这意味着你会完全丢失commit B的所有修改内容,慎用!!
  • mixed:git reset (--mixed) HEAD~1:加括号是因为reset不加参数时默认的模式就是mixed

    css 复制代码
    A---B (HEAD)
    • 该命令会将HEAD从指向B改为指向A,但是会保留从A到B的修改在工作目录,这意味着你可以重新对从A到B的修改进行add 和commit,总结:撤销指定节点之后的所有提交,但保留修改
    • 对于保留了的修改,可能的场景就比较多了,你可能想把一个大的commit拆分成几个小的commit来提交,提升commit的原子性;也可能是原本的修改存在一些问题,需要加以一定的修正后重新提交等
  • soft:git reset --soft HEAD~1

    css 复制代码
    A---B (HEAD)
    • 该命令会将HEAD从指向B改为指向A,但是会保留从A到B的修改在暂存区(add之后commit之前),这意味着你可以对这个commit进行补充

    • 由于soft将回退的修改内容保存在暂存区,这意味着原本的修改内容不会变动,因此在如下场景你可能会用到这个模式:

      • 1.合并commit,例如git reset --soft HEAD~2,这会将最近的两个commit的修改内容都保存在暂存区,然后重新commit就可以把这两个commit合并为一个commit
      • 2.旧的commit有缺漏,在不改变原本的commit修改内容的前提下,你可以添加新的修改文件到这个commit
        1. 等等

Cherry-pick

功能是将一个分支的部分修改代码合并到另一个分支,而不必把整个分支的修改合并进去

css 复制代码
A---B---C---D       master
    |
    ----E---F---G   feature

在master分支执行git cherry-pick F后:

css 复制代码
A---B---C---D---F       master
    |
    ----E---F---G       feature

以上是基本用法,用于转移一个commit的内容;cherry-pick也支持多个commit的批量转移(示例在master分支下执行):

git cherry-pick E..G 将(E,G]的commit合并到master 效果:

css 复制代码
A---B---C---D---F---G       master
    |
    ----E---F---G           feature

git cherry-pick E^..G 将[E,G]的commit合并到master 效果:

css 复制代码
A---B---C---D---E---F---G       master
    |
    ----E---F---G           feature

(注意以上的字母都是对应commit的hash值)

其他参数可以自行了解

Stash

考虑这么一个场景:你在A分支开发时由于某些原因,需要暂停,你想切换到B分支,但是git版本管理不支持在有未提交的修改内容的情况下进行分支切换,你无法切换到B分支,这时候可以使用git stash来暂存A分支的开发内容,然后切换到B分支进行开发,等B分支任务结束后切换回A分支后,使用git stash pop来弹出stash栈顶的上一次存储的暂存内容,继续A分支上的开发

这是stash的基础用法,详细用法和原理可以自行了解

相关推荐
狂炫一碗大米饭1 小时前
如何在 Git 中检出远程分支
前端·git·github
上邪o_O4 小时前
Git 的基本使用指南(1)
linux·git
飏旎21 小时前
git pull和git fetch的区别
git
z涛.1 天前
git的使用
git
大卫小东(Sheldon)1 天前
智能生成git提交消息工具 GIM 发布 1.7 版本了
git·ai·rust
慧都小项1 天前
UI测试平台TestComplete如何实现从Git到Jenkins的持续测试
git·ui·jenkins·代码质量·testcomplete·zephyr for jira
可曾去过倒悬山2 天前
Mac上优雅简单地使用Git:从入门到高效工作流
git·elasticsearch·macos
穗 禾2 天前
github与git新手教程(快速访问github)
网络·git·github
我不是程序猿儿2 天前
【git】在 GitLab 上如何把 A 分支(如 feature/xxx)合并到 B 分支(如 trunk)
服务器·git·gitlab