Git的进阶操作
这篇主要记录 Git 使用中, 遇到的一些稍进阶, 但是却对实际使用有重要的影响的操作, 平常要用的时候需要到处去查资料但是没有一个好的归纳和体系, 所以决定单独总结一下.
而关于Git的一些棘手问题, 参考以前篇, 关于开源社区中的Git事项参考此篇
0x00. 前言
在最初的 git 篇提过, 最常见的 git 基础这里不会再单独说, 本文主要关注一些”不常见的常见”问题
比如说各种误操作导致的回退, 冲突, 迁移, 这种虽然不会常发生, 但是发生一次就很头疼的问题, 以前我是遇到的时候再去现查, 但是由于有些命令风险很大, 甚至具有不可逆性, 加上人总是要成长的, 不能一直停留在只会使用 add commit push pull
的阶段. 这是本文开启的重要缘由之一. 另外也是方便自己, 其他同学遇到这些问题, 不那么惧怕, 或者被各种不靠谱的文章误导. 下面说一下大体的几个部分:
- 更新
- 回滚 (核心)
- 修改 (核心)
- 清理
- 迁移
- 别名
有少部分在上一篇开源事项里提到过, 为了方便查阅, 还是再写一下.
0x01. 更新
A. 快速更新
上面讲的是正常的PR/更新流程, 你可能觉得更新一次怎么这么麻烦, 这还有更简单粗暴的.. 如果 fork 的项目本地是毫无修改的, 在确定我们可以直接覆盖的前提下, 可以用 reset 大法实现三板斧更新:
1 | #1.更新全部远端分支 |
当然这个做法在代码的场景下很好用, 不会存在 git pull
后还要手动解决冲突的问题, 简单快速.
B. 多远端更新
这个场景也很常见, 你需要设置多个 remote
对象, 梳理好关系, 对你后续做patch合并, 查看会有很大帮助.
比如说社区的公开仓库命名为 github
, 你本地的仓库是 origin
, 你本地对应的内网私有仓库是 gitlab
. 实际就有了至少三个不同的仓库, 而且有些官方仓库保留了许多分支, 如果你直接简单的一键关联, 会导致你的分支/仓库列表非常杂乱冗长. 所以如何正确的让这三方运作好还是需要点技巧的.
1 | git remote add # 这后面可以补上许多参数, 而不是直接一个远端地址 |
C. 小修小补
这就要隆重介绍一位大将 cherry-pick
, 它一个单独的命令, 平常用到的地方可能不多, 但是在跟进社区, 社区合作里尤为常见/重要.
简单说,它可以跨不同分支进行合并, 比如我现在使用的是稳定版的分支stable
, 我需要从最新分支dev
上合并一个最新修复的Bug, 那么merge/rebase
就不适合, 应该使用cherry-pick
命令
1 | # 基本用法, -n表示不会自动提交 (默认会自动提交) |
合并一个提交很简单, 也很舒服, 下一个关键问题是, 如果我要合并的是一个功能, 它分化成了多次提交, 而且跨度还挺大, 比如可能一年以上. 这时该怎么使用, 如果新的补丁还引用了其他的提交 (并不想合并) 该怎么办, 这部分放社区参与篇说, 因为基本只有参与社区会遇到这种问题.
D. 版本更新
git
每年需要更新一下版本, 以避免一些很棘手的bug, 它提供了内置的更新命令直接升级, 最为推荐: git update
(根据不同OS会提示输入具体命令)
0x02.Git 回滚
这个涉及到大量生产环境修改git log
/ 提交历史, 文件回滚等操作, 比较复杂且很有些危险, 觉得应该单独开一篇文章来讲, 不过因为我接触的也不全, 就先说说比较简单/常见的案例吧.
A. 错误的add提交
因为大家习惯使用git add .
来添加所有变动文件, 一不小心就容易把不该加的文件添加进去了, 这个时候如果还没有commit , 那么可以这样来操作删掉添加的错误文件
1 | #--cached只会从缓存区中移除文件,不会从磁盘移除 |
如果已经提交了, 不管是在本地还是已经push了, 最简单的做法其实都是revert commitId
了, 所以尽量别到这样…
B. 错误的邮箱和用户名
在内部和github之间, 你很可能有两套git用户, 比如gitlab & github , 在PR和本地提交之间很容易没注意写错了邮箱, 如果你发现及时, 这是你最近一次提交, 那可以这样操作:
1 | git commit --amend --author="usrname <mail>" |
然后你最好给你需要设置不同用户名的项目单独设置一个用户名+邮箱 (和全局不冲突):
1 | git config user.name "xx" |
这样就可以愉快的在多个项目间自由使用不同的邮箱+用户名了 ,也不怕搞错了.
C. 修改提交信息
先说简单的, 大家常用的是git commit -m "msg"
来记录这次提交的信息, 有时候需要填写一段内容, 其实最简单的就是直接git commit
, 它会默认打开一个文本编辑框, 你可以输入信息, 注意#代表此行是不显示的.
然后
在执行commit操作的时候, 一般至少会带上一个基本信息, 如果这个信息写错了, 但是你已经push到了server端呢? 分两种情况
- 提交信息少了, 我想补充一点 (简单) :
git commit -a --amend
,这样不会重新开启一个commit ,而是整合到一个. (如果已经提交了多个分支, 那么在本地先reset到第一个, 然后重新push -f
覆盖.) - 提交信息错误, 我希望覆盖新的信息 :
D. 修改分支名
有些时候开发就需要定一个新的分支名, 但是很可能后面觉得名字不太合适, 就需要修改, 如果分支还没有提交到远端, 那很简单 : git branch -m oldName newName
就行了, 如果和远端不一致的话呢~
1 | #1.先修改本地名字 |
E. 回滚rebase/merge等操作
这里也是比较棘手的. 首先, 推荐多用rebase
命令, 少用pull/merge
(pull自动触发merge, 所以也慎重), 下面以rebase为例, 讲一下相关的撤回操作.
如果是在rebase生效的途中, 提示你被中断, 需要解决冲突, 然后你后悔了, 你可以很简单的通过
git rebase --abort
取消回滚.如果是rebase已经完全生效, 那么目前没有直接的回滚
undo
命令, 都需要使用reset
这种危险操作. (注意备份)1
2
3
4
5
6
7
8
9# 1.通过reflog这个强大的命令, 查看所有的操作过程
git reflog
# 2.强制reset回某个时间点
# 这里使用soft的效果就是先只回滚指针,而没有变更数据.需要手动再处理一下暂存区的数据(丢弃/添加), 安全一些.
git reset --soft HEAD@{id} # 这里id是具体的数,别看错了..
# 2.1 慎重
git reset --hard HEAD@{id} # 如果熟悉确定, 可以直接reset --hard一次到位...建议提前备份:)
Git的回滚整个生效的原理, 一般也都是基于这整个的记录, 所以遇到错误提交, 先别慌张.. 只要没做一些把日志记录也一并误删的情况, 都是有救的. 不过建议养成危险操作先备份的习惯.
0x03. 初始化与迁移
A. 创建新的空分支
默认情况下, 你直接git checkout -b newBranch
, 会同时得到所有的当前分支的文件和提交记录, 而有些时候, 我们需要特定分支单独用于其他场景, 比如备份/文档等等. 这时我们就需要学会单独创建一个新的空分支, 然后清空数据了.
1 | #1.使用--orphan参数, 详情可参考help/man |
B. 仓库迁移 (mirror)
首先要明白自己的需求, 别照着任何网上命令一顿敲. 最后发现云里雾里, 这样就算歪打正着, 也一直不能理解自己到底做了什么.. 确定一下几点:
迁移的仓库是否在同一个作用域, 比如从
github --> github
, 还是从gitlab --> github
, 还是从gitlab8 --> gitlab10
- 如果是同一个作用域, 那你应该优先选用项目自带的
Tranfer
功能, 它可以把issue/pr等配置一并完整迁移, 最理想 - 如果不是, 看看是否可以用”导出(export)” 和 “导入(方式)” + 代码的迁移 (比如
gitlab8-->gitlab10
) - 如果以上都不能满足, 再考虑用纯git命令操作, 见2
- 如果是同一个作用域, 那你应该优先选用项目自带的
确认原有项目(old)的提交信息是否需要保留?
- 需要, 使用
git clone mirror
作为核心迁移, 两步走 - 不需要, 待补充
- 需要, 使用
首先, 镜像克隆旧项目
1
git clone --mirror git@../old.git old && cd old
然后在旧项目设置 —> 推送当前镜像到新项目
1
2
3
4# 确认你当前是origin
git remote set-url --push origin git@../new.git
git push --mirror最后, 新的仓库迁移完毕, 检查确认无误后, 记得把本地的远程仓库地址修改一下 (及时改)
1
2
3
4# 修改原本的old地址为new
git remote set-url origin git@../new.git
git remote -v # 确认一下修改成功
你会看到网上有很多种做法, 但是大部分说的太复杂,还有些使用init bare
的, 其实并没有, 想明白自己的需求, 选择最简单易懂的一种就行. 上述操作只有3步即可, 非常易实践. 简单则可靠 . 后续有空再对比一下不同做法的区别.
0x04. 清理
仓库里清理相关主要集中在使用过的分支上, 最常见的两个问题
- 本地仓库有大量过期分支 (远端已经删除, 但是本地不会自动删除)
- 远程仓库有大量过去分支 (早已合并, 但是未删除, 导致其他人拉取一大堆无用分支)
第一个问题很好解决, git pull
并不会删除更新本地的其它不存在的分支, 应该使用 remote 命令的 prune
参数:
1 | # 检测有哪些分支远端已经不存在了.dry-run只是告诉你, 不会实际执行 |
第二个问题, 暂时没想到太好的办法, 目前是依赖于开启 “合并分支时删除当前分支“, 然后每次PR/MR 合并后会自动清理, 至于已有的, 似乎只能确认清楚手动清理了.
0x05. 快捷命名
A. 使用别名
配置alias(别名). 跟bash一样,git有些常用命令也比较冗长,设置一些快捷名是很有裨益的.git config --global alias.cm commit
(语法) .以下是我常用的缩写
ck checkout
ps push
br "branch -a"
#注意多个参数需要用双引号括起来,否则只会读第一个rmt "remote -v"
st status
lg "log --graph -4"
#显示最近4次的log,以及gitflow图mg "merge --no-ff"
#保留合并,推荐
然后就可以直接git br显示所有分支了. 当然这每次还要输入git,能不能把git也省了呢,也可以做成一个文件存下来, 方面服务器上同步.
B. 使用Tag
打tag是很简单的事情,git tag 1.0
代表在当前分支打上了和commit对应的一个标签(因为commit好比是ip,很难记忆,而tag好比是域名,做了一层映射.) ,如果想查看tag信息 git show 1.0
即可
此文会随着使用, 持续更新
0x06. 后续
最后, 下一个 Git 的系列, 会抽空去看看学习一下 Git 的整体架构和原理分析, 不过我发现有前辈已经开放了非常好的资料, 大家可以参考:
参考资料:
- 文中引用过的所有链接
- git官方文档
- Git权威指南-online
- Git实用技巧(待看)