Git操作手册
Git操作手册
ZhangCurry

Git常用命令参考手册
基本涵盖了在开发中用到的git命令,能满足日常需求
通俗易懂的例子,30分钟快速入门
注:2020 年 10 月 GitHub 已将默认分支 master 更名为 main 分支。
目录
- 配置
- 初始化仓库
- 克隆仓库
- 管理仓库
- 暂存文件
- 提交文件
- 推送远端
- 查看分支
- 切换分支
- 创建分支
- 删除分支
- 重命名分支
- 转移提交
- 临时保存
- 文件状态
- 日志
- 责怪
- 合并
- 删除文件
- 还原
- 拉取
- 移动-重命名
- 比较文件内容差异
- 查看历史提交信息
- 回滚版本
- 撤销
- 标签
- 变基
- 工作流
- 子模块
- 子树
- 二分查找
- 归档
- 格式化日志
- 清空 commit 历史
- 帮助
- 提交规范
- 解决冲突
- 仓库迁移
- 奇技淫巧
- GUI 客户端
- 生成 SSH_Key
- 其他
- 记住密码
- 清除账号
- 加速
- 思维导图
配置
1 | # 查看全局配置列表 |
命令别名配置
git 可以使用别名来简化一些复杂命令,类似 alias 命令。
1 | # git st 等价于 git status |
配置代理
1 | # 设置 |
初始化仓库
git init 创建一个空的 Git 仓库或重新初始化一个现有的仓库
实际上 git init 命令用得不多,通常在 GUI 上进行操作。
1 | # 会在当前目录生成.git |
克隆仓库
1 | # https 协议克隆 |
克隆指定文件夹
有些仓库会包含 客户端、服务端、等多个端的代码, 但又不想完整克隆整个项目, 只想克隆某个文件夹,这个时候就需要用到 稀疏检出。
开启稀疏检出必须满足 2 个条件:
core.sparsecheckout设置为 true.git/info/sparse-checkout文件列出要检出的目录列表
本仓库有个 media 文件夹,用它来演示吧。
1 | # 1、创建一个目录并进入 |
演示克隆指定文件夹.gif
管理仓库
git remote 命令用来管理远程仓库。
通常一个项目对应多个仓库就需要用到 git remote, 比如要推送到 github / gitee / gitlab, 就可以用 git remote 来管理多个仓库地址。
1 | # 查看远程仓库服务器, 一般打印 origin , 这是 Git 给你克隆的仓库服务器的默认名字 |
暂存文件
1 | # 暂存所有 |
提交文件
1 | # -m 提交的描述信息 |
修改提交日期
执行 git commit 时 git 会采用当前默认时间,但有时候想修改提交日期可以使用 --date 参数。
格式:git commit --date="月 日 时间 年 +0800" -m "init"
例子:git commit --date="Mar 7 21:05:20 2021 +0800" -m "init"
月份简写如下:
| 月份简写 | 描述 |
|---|---|
| Jan | 一月 |
| Feb | 二月 |
| Mar | 三月 |
| Apr | 四月 |
| May | 五月 |
| Jun | 六月 |
| Jul | 七月 |
| Aug | 八月 |
| Sep | 九月 |
| Oct | 十月 |
| Nov | 十一月 |
| Dec | 十二月 |
推送远端
1 | # 默认推送当前分支 |
查看分支
1 | # 查看所有分支 |
给分支添加备注
有时候分支过多很难通过分支名去判断这个分支做了什么。
1 | # 命令 |
切换分支
1 | # 切换到main分支 |
在克隆时使用 --depth=1 切换其他分支,比如切换 dev 分支:
1 | git clone --depth=1 https://github.com/xjh22222228/git-manual.git |
除了使用 git checkout 还有另一种方式切换那就是 git switch, 在 Git 版本 2.23 引入, 主要用于切换和创建分支。
1 | # 切换到 develop 分支 |
创建分支
1 | # 创建一个名为 develop 本地分支 |
删除分支
注意:删除分支不能删除当前分支,先切换到其他分支再删除。
1 | # 删除本地分支 |
重命名分支
1 | # 重命名当前分支, 通常情况下需要执行3步 |
转移提交
git cherry-pick 可以用来将一个分支的某次提交转移到当前分支中。
假设有 dev 和 main 2 个分支, dev 分支中有 10 次提交记录, main 分支想把 dev 的第 5 次提交记录合并到当前分支中, 这正是此命令的使用场景。
还可以理解为将以前的某次提交再重新提交一次。
1 | # 可以是一个 commit_id 或者是分支名 |
演示转移提交.gif: 把 `dev` 分支的第三次提交转移到当前 `main` 分支。
临时保存
应用场景:假设当前分支某些功能做到一半了, 突然需要切换到其他分支修改 Bug, 但是又不想提交(因为切换分支必须清理当前工作区,否则无法切换),这个时候 git stash 应用场景就来了。
1 | # 保存当前修改工作区内容 |
文件状态
1 | # 完整查看文件状态 |
日志
查看历史日志可以通过 git log / git shortlog / git reflog。
git log 命令是 3 个最强大的命令
1 | # 查看完整历史提交记录 |
git shortlog 以简短的形式输出日志, 通常用于统计贡献者代码量。
1 | # 默认以贡献者分组进行输出 |
git reflog 通常被引用为 安全网,当 git log 没有想要的信息时可以尝试用 git reflog。
当回滚某个版本时记录是不保存在 git log 中, 想要找到这条回滚版本信息时 git reflog 就用上了。
1 | git reflog # 等价于 git log -g --abbrev-commit --pretty=oneline |
责怪
git blame 意思是责怪,你懂的。
git blame 用于查看某个文件的修改历史记录是哪个作者进行了改动。
1 | # 查看 README.md 文件的修改历史记录,包括时间、作者以及内容 |
合并
feature/v1.0.0 分支代码合并到 develop
1 | git checkout develop |
将上一个分支代码合并到当前分支
1 | git merge - |
以安静模式合并, 把 develop 分支合并到当前分支并不输出任何信息
1 | git merge develop -q |
合并不编辑信息, 跳过交互
1 | git merge develop --no-edit |
合并分支后不进行提交
1 | git merge develop --no-commit |
退出合并,恢复到合并之前的状态
1 | git merge --abort |
合并某个分支指定文件或目录, 需要注意的是这会直接覆盖现有文件,而不是本质上的合并。
1 | # 将dev分支的 src/utils/http.js src/utils/load.js 2个文件合并到当前分支下 |
允许合并不相关的历史记录,如果在克隆使用了 --depth 参数会导致合并的时候会发生较大冲突,allow-unrelated-histories 参数可以有效的解决这个问题
1 | git merge develop --allow-unrelated-histories |
删除文件
此命令使用相对较少,通常用于清除文件缓存,比如加入 .gitignore 文件不生效问题
1 | # 删除 1.txt 文件 |
还原
还原操作通过 git restore 命令。
git restore 是在 2.23 引入的, 是为了分离 git checkout / git reset 职责。
1 | # 撤销工作区文件修改, 不包括新建文件 |
拉取
git pull 拉取最新内容并合并。
拉取远程分支最新内容
默认情况下拉取当前分支
1 | # 如果出现冲突会自动合并 |
拉取指定分支
1 | # 远程分支名:本地分支名 |
拉取指定工作目录
1 | # 默认情况下拉取会在当前工作目录中,但如果想拉取指定工作目录,可以指定 `-C` |
同步 Fork 仓库
当 Fork 别人仓库后,原仓库发生变化,可以通过以下操作合并到 Fork 仓库
1 | # 1、添加原远程仓库:git remote add 自定义名字 远程仓库地址 |
移动-重命名
git mv 命令用来重命名文件或移动文件, 大部分开发者会选择手动进行移动文件, 手动和用 git mv 是有区别的。
手动和命令两者的区别(假设README.md重命名为README2.md):
- 手动:先删除
README.md, 然后创建README2.md, 历史记录无法正常追踪 git mv: 实际上是更新索引,把文件进行重命名, 可以通过历史记录方便检索
git mv 和 uninx mv 命令很像,如果你熟悉的话。
注意:新创建的文件不支持 git mv , 必须先提交。
1 | # 将 1.txt 重命名为 2.txt |
比较文件内容差异
git diff 命令用于查看工作区文件内容与暂存区或远端之间的差异。
git diff
1 | # 查看所有文件工作区与暂存区的差异 |
查看历史提交信息
可以通过 git show 命令查看历史提交信息。
1 | # 不指定参数默认查看最新一条信息 |
回滚版本
回滚版本有 2 种方法:
git reset- 回滚版本后之前的历史记录将不保存, 不保留痕迹, 基本上不存在冲突情况。git revert- 回滚版本后之前的历史记录还存在并多增加了一条Revert记录,很容易出现冲突。
git reset 命令用法:
1 | # 回滚上一个版本 |
git revert 命令用法:
1 | # 回滚上一次提交版本 |
回滚到指定分支或 Commit_id 指定文件, 命令:
git checkout [branch|commit_id] file file...
1 | $ git checkout main 1.txt 2.txt |
撤销
1 | # 撤销当前工作区所有文件的改动 |
标签
1 | # 列出本地所有标签 |
变基
git rebase 命令有 2 个比较实用的功能:
- 将多个 commit 记录合并为一条
- 代替
git mrege合并代码
1、将多个 commit 记录合并为一条
要注意保证当前工作区没内容再操作。
1、指定需要操作的记录,这时候会进入交互式命令
1 | # start起点必填, end 可选,默认当前分支 HEAD 所指向的 commit |
| 参数 | 描述 |
|---|---|
| p, pick | 保留当前 commit,默认 |
| r, reword | 保留当前 commit,但编辑提交消息 |
| e, edit | 保留当前 commit,但停止修改 |
| s, squash | 保留当前 commit,但融入上一次提交 |
| b, break | 在这里停止(稍后使用 git rebase --continue 继续重新设置基准) |
| d, drop | 删除当前 commit |
这里是倒序排列,最新的记录在最后
2、除了第一条后面全部改成 s 或 squash:
3、按 :wq 退出交互式,接着进入另一个交互式来编辑 commit 消息, 如果不需要修改之前的 commit 消息则直接退出:
4、强制推送到远端
1 | # 推送到 main 分支 |
2、合并分支代码
都说用 git rebase 代替 git merge 进行合并,这 2 个区别在于 git rebase 可以使历史记录更清晰, 下面 2 张图对比一下:
第一张图是 git rebase,第二张图是 git merge。
可以看出 git rebase 是一条直线的,git merge 则是各种交叉,很难理解。
假设有 2 个分支,main 和 dev,下面使用 git rebase 将 dev 分支代码合并到 main 分支上。
1 | # 1、先切换到 main 分支 |
中断 git rebase 操作, 如果操作一半不想继续使用 rebase 命令则可以中断此次操作。
1 | $ git rebase --abort |
工作流
Git Flow 是一套基于 git 的工作流程,这个工作流程围绕着 project 的发布(release)定义了一个严格的如何建立分支的模型。
git flow 只是简化了操作命令,不用 git flow 也可以,只要遵循 git flow 流程操作即可,手动一条一条命令执行也一样的。
git flow 不是内置命令,需要单独安装。
初始化
每个仓库都必须初始化一次才能使用,这是针对当前用户而言的。
1 | # 通常直接回车以完成默认设置 |
开始开发一个功能
假设我们要开始开发一个新的功能比如登录注册,这个时候就要打一个 feature 分支进行独立开发。
1 | # 步骤一:开启新的功能, 起一个分支名叫 v1.1.0, 建立后分支名为 feature/v1.1.0 |
打补丁
什么情况下需要打补丁? 假设已经上线的功能有 BUG 需要修复就需要打补丁了。
hotfix 是针对 main 分支进行打补丁的。
1 | # 步骤一:开启一个补丁分支叫 fix_doc 用于修改文档错误,建立后分支名为 hotfix/fix_doc |
发布
假设产品给了个新需求并完成,这个时候可以选择发布。不发布也行,但是发布后会有版本区分,以后想找到某个版本的代码就很方便。
1 | # 步骤一:建立一个发布版本 v1.1.0 建立后分支名为 release/v1.1.0 |
参考:
- https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow
- https://www.git-tower.com/learn/git/ebook/cn/command-line/advanced-topics/git-flow
Git flow schema
子模块
git submodule 子模块的作用类似于包管理,类似 npm, 主要是复用仓库, 但比包管理使用起来更方便。
子模块可以不建立版本分支管理代码, 因为它是依赖主应用,所以建立版本分支可以从主应用去操作,那么一旦建立新的版本分支当前的所有内容都会被锁定在这个分支上,不管子模块仓库怎么修改。
添加子模块
添加完子模块后会发现根目录下多了个 .gitmodules 元数据文件,主要是用于管理子模块。
1 | git submodule add https://github.com/xjh22222228/git-manual.git # 默认添加到当前目录下 |
删除子模块
1 | # 1、直接删除子模块目录 |
克隆一个包含子模块的仓库
1 | # --recursive 用于递归克隆,否则子模块目录是空的 |
修复子模块分支
当把一个包含子模块的仓库克隆下来后会发现子模块分支不对,可以使用下面命令纠正:
1 | git submodule foreach -q --recursive 'git checkout $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo main)' |
更新子模块代码
方法一:通常我们需要更新代码只需要执行 git pull, 这是比较笨的办法。
1 | # 递归抓取子模块的所有更改,但不会更新子模块内容 |
方法二:使用 git submodule update 更新子模块
1 | # git 会尝试更新所有子模块, 如果只需要更新某个子模块只要在 --remote 后指定子模块名称 |
方法三:使用 git pull 更新, 这是一种新的更新模式,需要 >= 2.14
1 | git pull --recurse-submodules |
如果嫌麻烦每次 git pull 都需要手动添加 --recurse-submodules,可以配置 git pull 的默认行为, 如何配置请参考 配置
具体使用还可以看这里 git submodule 子模块使用教程
子树
如果你知道 git submodule 那就大概知道 git subtree 干嘛用了, 基本上是做同一件事,复用仓库或复用代码。
官方建议使用 git subtree 代替 git submodule。
git subtree 优势:
- 不会像子模块需要
.gitmodules元数据文件管理 - 子仓库会当做普通目录, 其实是没有仓库概念的
- 支持较旧的 Git 版本(甚至比 v1.5.2 还要旧)。
- 简单工作流程的管理很容易。
git subtree 劣势:
- 命令过于复杂, 推送拉取都很麻烦
- 虽然用于替代子模块, 但使用率并没有子模块广泛
- 子仓库和主仓库混合在一起, 历史记录相当于有 2 个仓库的记录
git subtree 命令用法:
1 | git subtree add --prefix=<prefix> <commit> |
在操作 git subtree 时当前工作区必须清空,否则无法执行。
添加子仓库
--prefix指定将子仓库存储位置main是分支名称--squash通常做法是不将子仓库整个历史记录存储在主仓库中,如果需要的话可以忽略整个参数
添加子仓库后, 会跟普通文件一样看待,可以进入 sub/common 目录执行 git remote -v 会发现没有仓库。
1 | git subtree add --prefix=sub/common https://github.com/xjh22222228/git-manual.git main --squash |
更新子仓库
当远程子仓库有内容变更时,可以通过下面命令进行更新:
1 | git subtree pull --prefix=sub/common https://github.com/xjh22222228/git-manual.git main --squash |
推送到子仓库
假如修改了子仓库里的内容,可以将修改这部分的内容推送到子仓库中
1 | # 需要先在主仓库把子仓库的代码暂存 |
切割
随着项目的迭代, 主仓库会提交过多, 会发现每次 push 时会非常慢,尤其在 windows 平台较为明显。
每次 push 到子仓库里头时会花费大量的时间来重新计算子仓库的提交。并且因为每次 push 都是重新计算的,所以本地仓库和远端仓库的提交总是不一样的,这会导致 git 无法解决可能的冲突。
当使用 git split 命令后,使用 git subtree push,git 只会计算 split 后的新提交。
1 | git subtree split --prefix=sub/common --branch=main |
简化命令
通过以上实操,不难发现,git subtree 太长了,每次操作都要敲这么长的命令,谁能忍得住。
将子仓库添加为远程仓库:
1 | # common 是仓库名字,可以随意定义 |
要做其他 git subtree 命令时就不需要敲仓库地址了:
1 | git subtree push --prefix=sub/common common main --squash |
虽然省去了仓库地址,命令还是太长。
还有另一种解决方案,就是使用别名,例如在 mac 或 linux 中使用 alias 命令:
1 | alias push="git subtree push --prefix=sub/common https://github.com/xjh22222228/git-manual.git main --squash" |
也可以使用 git 自带的别名命令 => 命令别名配置
如果你写前端,可以在 package.json 文件中加入:
1 | { |
下次需要推送时执行:
1 | npm run push 或者 yarn push |
二分查找
git bisect 基于二分查找算法, 用于定位引入 Bug 的 commit,主要 4 个命令。
此命令非常实用, 如果你的 Bug 不知道是哪个 commit 引起的,可以尝试此方法。
1 | # 开始 |
参考 https://github.com/bradleyboy/bisectercise
归档
创建一个归档文件,可以理解为将当前项目压缩为一个文件。会忽略掉 .git 目录。
但与 zip / tar 等压缩不同,git archive 支持将某个分支或 commit 进行归档。
参数
| 参数 | 描述 |
|---|---|
| –format | 可选,指定格式,默认 tar, 支持 tar 和 zip,如果不填会根据 –output 后缀格式进行推断 |
| –output | 输出到指定目录 |
1 | # 归档 main 分支 并打包在当前目录下 output.tar.gz |
格式化日志
在使用 git log 命令时可以携带 --pretty=format 用来格式化日志。
常用格式如下:
| 参数 | 描述 |
|---|---|
| %H | 完整 commit hash |
| %h | 简写 commit hash 一般是前 7 位 |
| %T | 完整 hash 树 |
| %t | 简写 hash 树 |
| %an | 作者名称 |
| %ae | 作者邮箱 |
| %ad | 作者日期, RFC2822 风格:Thu Jul 2 20:42:20 2020 +0800 |
| %ar | 作者日期, 相对时间:2 days ago |
| %ai | 作者日期, ISO 8601-like 风格: 2020-07-02 20:42:20 +0800 |
| %aI | 作者日期, ISO 8601 风格: 2020-07-02T20:42:20+08:00 |
| %cn | 提交者名称 |
| %ce | 提交者邮箱 |
| %cd | 提交者日期,RFC2822 风格:Thu Jul 2 20:42:20 2020 +0800 |
| %cr | 提交者日期,相对时间:2 days ago |
| %ci | 提交者日期,ISO 8601-like 风格: 2020-07-02 20:42:20 +0800 |
| %cI | 提交者日期,ISO 8601 风格: 2020-07-02T20:42:20+08:00 |
| %d | 引用名称: (HEAD -> main, origin/main, origin/HEAD) |
| %D | 引用名称,不带 () 和 换行符: HEAD -> main, origin/main, origin/HEAD |
| %e | 编码方式 |
| %B | 原始提交内容 |
| %C | 自定义颜色 |
例子:
1 | git log -n 1 --pretty=format:"%an" # xjh22222228 |
清空 commit 历史
清空 commit 有 2 种方法。
1、第一种方法原理是通过新建新的分支,假设要清空 commit 分支是 develop
1 | # 1、新建一个新分支 |
2、第二种方法通过更新 引用, 假设要重设 main 分支
1 | # 通过 git log 找到第一个 commit_id |
这 2 种方法都是用于清空 commit 历史, 不会造成当前文件的丢失,所以放心。
笔者推荐使用第二种方法,更安全可靠。
帮助
1 | # 详细打印所有git命令 |
提交规范
| 标志 | 描述 |
|---|---|
| feat | 该提交含有新的特性 |
| style | 通常是代码格式的修改 |
| chore | 构建过程或辅助工具的变动 |
| fix | 修复 Bug |
| docs | 文档修改 |
| test | 单元测试改动 |
| refactor | 代码重构 |
| perf | 性能优化、体验 |
| revert | 回滚版本 |
| merge | 代码合并 |
| typo | 错字, 比如单词拼错 |
例子:
1 | # 含有新特性 |
解决冲突
代码合并/更新代码 经常会遇到冲突的情况。
解决冲突的流程如下:
- 执行
git pull把代码拉下来,git 会自动尝试合并 - 编辑冲突文件, 根据实际情况保留本地代码还是远端代码
- 暂存文件并推送到远端
点击查看解决冲突.gif
面向 GUI 的用户,推荐 3 个工具专门处理 git 冲突:
仓库迁移
仓库迁移也可以叫复制仓库。
有时候需要从一个旧仓库迁移到新仓库,如果手动只能把文件进行迁移,但是如果需要把分支、标签、历史记录一起迁移就需要复制仓库。
旧仓库 A: https://github.com/xjh22222228/A.git
新仓库 B: https://github.com/xjh22222228/B.git
1、克隆旧裸仓库
1 | # 克隆裸仓库,里面没有工作区内容 |
2、镜像推送至新仓库
1 | cd A |
3、删除刚刚克隆的旧仓库
1 | rm -rf A |
4、拉取新仓库
1 | git clone https://github.com/xjh22222228/B.git |
除了通过命令迁移之外,可以通过网页导入仓库的方式也可以。
奇技淫巧
美化 git log, 直逼 GUI
1 | # 1、全局配置 |
效果图.png
GUI 客户端
推荐几款比较好用的 git 图形界面工具, 不分先后。
- 免费 - Github Desktop
- 免费 - Sourcetree
- 免费 - tortoiseGit
- 免费 - gitkraken
- 免费 - gitup
- 免费 - magit
- 收费 - smartgit
- 收费 - git-fork
- 收费 - tower
- 收费 - lazygit
生成 SSH_Key
以下适用于 Mac / Linux。
1、进入到 ssh
1 | cd ~/.ssh |
2、替换为您的 GitHub 电子邮件地址
1 | ssh-keygen -t rsa -b 4096 -C "your_email@example.com" |
3、当提示“输入要在其中保存密钥的文件”时,按 Enter。接受默认文件位置。 (建议修改名字,防止以后被覆盖)
1 | > Enter a file in which to save the key (/Users/you/.ssh/id_rsa): [Press enter] |
4、在提示符下,键入一个安全密码, 默认回车即可
1 | > Enter passphrase (empty for no passphrase): [Type a passphrase] |
5、生成的 SSH Key 添加到 ssh config 中
1 | vim ~/.ssh/config |
最后将公钥添加到 https://github.com/settings/keys 中
1 | cat ~/.ssh/id_rsa.pub |
演示生成SSH Key.gif
其他
1 | # 查看git版本 |
记住密码
使用 https 方式会要求每次都需要输入账号和密码,如果想下次不弹出账号密码可以按以下方式:
1 | # 临时记住密码,默认15分钟 |
清除账号
清除 git 已保存的用户名和密码
1 | # windows |
加速
在国内克隆或下载版本会很慢,可以借助下面镜像站点进行加速。
克隆:
1 | # 公有仓库 |
资源加速:
1 | https://raw.githubusercontent.com/xjh22222228/git-manual/main/media/poster.png |
Github 文件/GIST/RAW 加速:
使用方法打开 https://www.7ed.net/





