{"id":19057916,"url":"https://github.com/onesizefitsquorum/git-tips","last_synced_at":"2025-07-20T01:03:55.063Z","repository":{"id":37382698,"uuid":"311343645","full_name":"OneSizeFitsQuorum/git-tips","owner":"OneSizeFitsQuorum","description":"33 条常用 git 命令详解","archived":false,"fork":false,"pushed_at":"2022-08-19T03:34:23.000Z","size":1500,"stargazers_count":236,"open_issues_count":0,"forks_count":41,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-03T03:11:09.326Z","etag":null,"topics":["git","git-tips","tips","tips-and-tricks"],"latest_commit_sha":null,"homepage":"","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/OneSizeFitsQuorum.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-11-09T13:15:12.000Z","updated_at":"2025-04-01T04:20:02.000Z","dependencies_parsed_at":"2022-07-13T06:50:26.288Z","dependency_job_id":null,"html_url":"https://github.com/OneSizeFitsQuorum/git-tips","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/OneSizeFitsQuorum/git-tips","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OneSizeFitsQuorum%2Fgit-tips","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OneSizeFitsQuorum%2Fgit-tips/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OneSizeFitsQuorum%2Fgit-tips/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OneSizeFitsQuorum%2Fgit-tips/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/OneSizeFitsQuorum","download_url":"https://codeload.github.com/OneSizeFitsQuorum/git-tips/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/OneSizeFitsQuorum%2Fgit-tips/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266052553,"owners_count":23869474,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["git","git-tips","tips","tips-and-tricks"],"created_at":"2024-11-08T23:59:57.068Z","updated_at":"2025-07-20T01:03:55.040Z","avatar_url":"https://github.com/OneSizeFitsQuorum.png","language":null,"readme":"\u003c!-- TOC --\u003e\n\n- [概述](#概述)\n- [版本](#版本)\n- [介绍](#介绍)\n  - [init](#init)\n  - [clone](#clone)\n  - [config](#config)\n  - [remote](#remote)\n  - [add](#add)\n  - [rm](#rm)\n  - [mv](#mv)\n  - [reset](#reset)\n  - [clean](#clean)\n  - [stash](#stash)\n  - [status](#status)\n  - [log](#log)\n  - [show](#show)\n  - [shortlog](#shortlog)\n  - [diff](#diff)\n  - [branch](#branch)\n  - [checkout](#checkout)\n  - [commit](#commit)\n  - [revert](#revert)\n  - [merge](#merge)\n  - [rebase](#rebase)\n  - [cherry-pick](#cherry-pick)\n  - [tag](#tag)\n  - [push](#push)\n  - [fetch](#fetch)\n  - [pull](#pull)\n  - [bisect](#bisect)\n  - [reflog](#reflog)\n  - [fsck](#fsck)\n  - [grep](#grep)\n  - [blame](#blame)\n  - [gc](#gc)\n  - [submodule](#submodule)\n- [配置](#配置)\n- [.gitignore](#gitignore)\n- [我的 alias 设置](#我的-alias-设置)\n- [相关资料](#相关资料)\n\n\u003c!-- /TOC --\u003e\n\n## 概述\n\ngit 是一个开源的分布式版本控制系统，也是计算机专业找工作的必备技能之一，其最初由 Linux Torvalds (Linux 之父) 创造，于 2005 年发布。\n\n关于 git 的教程网上已经有很多很多，其大致工作流程如下图：\n\n![](./img/workload.png)\n\n其大致命令如下图：\n\n![](./img/cheatsheet.png)\n\n本文主要介绍常用的 33 条 git 命令，希望能够大家使用 git 带来一些帮助。\n\n由于作者能力有限，描述必然会有纰漏，欢迎提交 PR、创建 Issue 进一步交流。\n\n如果看完之后有所收获，求求给个 Star 以表支持。😊\n\n## 版本\n\ngit version 2.17.2 (Apple Git-113)\n\n## 介绍\n以下每条命令也可使用`git \u003ccommand\u003e -h`来查看对应官方文档。\n\n### init\n\n可以使用`git init`在当前目录创建 git 仓库，也可使用`git init \u003cpath\u003e`在 path 路径下创建目录并创建 git 仓库。注意有个`--bare`参数可以生成一般作为远程仓库的裸仓库，其不包含工作区，具体可参考此[博客](https://www.jianshu.com/p/5b7ff91c5338)。\n\n### clone\n\n可以直接使用`git clone \u003curl\u003e`来克隆对应仓库代码，本地默认存储目录为当前目录下的仓库同名目录，也可在 url 之后指定本地的存储目录名。例如：\n```\ngit clone \u003curl\u003e\ngit clone \u003curl\u003e \u003cdir_name\u003e\n```\n\n具体的 url 有四种协议格式，分别是 local，git，ssh，https 协议，具体区别可参考此[博客](https://juejin.im/post/6844903536749182989)和[博客](https://cloud.tencent.com/developer/article/1347791)。\n\n此外在克隆某些存在较大文件的项目时，可以使用`git clone --depth=n \u003curl\u003e`来下载该项目 master 分支最近 n 次 commit 的相关文件和引用；使用`git clone --single-branch --branch=dev \u003curl\u003e`来下载该项目 dev 分支的相关文件和引用。这样可以大大加快克隆的速度。\n\n### config\n\n通常只用来设置邮箱和用户名。注意有三个级别`local`，`global`和`system`，分别对应项目级别，用户级别和机器级别。默认不指定是`local`级别，具体区别可参考此[博客](https://www.daixiaorui.com/read/240.html)。\n```\ngit config user.email \"TXYPotato@gmail.com\"\ngit config user.name \"LebronAl\"\n```\n\n此外，在单机配置多个 git 账号时，除了在目录`~/.ssh/`下生成 config 文件外，一般也需要在对应仓库重新设置非全局的正确用户名和邮箱以避免身份混淆，具体配置方式可参考此[博客](http://www.chenyp.com/2017/08/11/multiple-git-account/)。\n\n### remote \n\n通常用来管理上游仓库，可以通过`git remote -v`来查看相关信息，其相关命令如下：\n```\n// 添加远程仓库关联\ngit remote add \u003cname\u003e \u003curl\u003e\n// 删除远程仓库关联\ngit remote remove \u003cname\u003e\n// 更名远程仓库关联\ngit remote rename \u003cold_name\u003e \u003cnew_name\u003e\n// 显示某个远程仓库的信息\ngit remote show \u003cname\u003e\n// 更新远程仓库 url\ngit remote set-url \u003cname\u003e \u003cnew_url\u003e\n```\n\n此外，remote 命令也可以为一个远程仓库设置多个 url 地址，这样一次 push 就可以同时对该远程仓库的所有 url 进行推送，比如同时往 github 和 gitee 上推送，具体可参考此[博客](https://www.cnblogs.com/hsd1727728211/p/5331651.html)，相关命令如下：\n```\ngit remote set-url --add \u003cname\u003e \u003curl\u003e\ngit remote set-url --delete \u003cname\u003e \u003curl\u003e\n```\n\n此外，remote 命令也可以跟踪远程的所有分支和清理无效的远程分支跟踪，具体可参考此[博客](https://www.jianshu.com/p/884ff6252be5)：\n```\n// 本地创建远程追踪分支\ngit remote update \u003cname\u003e\n// 本地清理无效的远程追踪分支\ngit remote prune \u003cname\u003e\n// 查看无效的远程追踪分支\ngit remote prune --dry-run \u003cname\u003e\n```\n\n### add\n\n用来从工作区向暂存区添加变更。可以使用`git add -e \u003cfile\u003e`或者`git add -p \u003cfile\u003e`来交互性的添加工作区文件的部分变更到暂存区中去，具体可查看此[博客](https://www.cnblogs.com/zqb-all/p/13020293.html)。\n\nadd 命令支持添加单个文件，带有通配符路径的所有对应文件和所有文件。添加所有文件对应三种命令，有细微的区别，介绍如下：\n```\n// 提交被修改(modified)和被删除(deleted)文件，不包括新文件(new)\ngit add -u\n// 提交当前目录下的所有变化\ngit add .\n// 提交所有变化\ngit add -A \n```\n\n### rm\n\n用于删除工作区文件，并将此次删除放入到暂存区。（注：要删除的文件没有修改过，就是说和当前版本库文件的内容相同。）\n```\ngit rm \u003cfile\u003e\n```\n\n用于删除工作区和暂存区文件，并将此次删除放入暂存区。（注：要删除的文件已经修改过，就是说和当前版本库文件的内容不同）\n```\ngit rm -f \u003cfile\u003e\n// 对所有文件进行操作\ngit rm -f -r .\n```\n\n用于删除暂存区文件，并将此次删除放入暂存区，但会保留工作区的文件。可以理解成解除 git 对这些文件的追踪，将他们转入 untracked 状态。\n```\ngit rm --cached \u003cfile\u003e\n// 对所有文件进行操作\ngit rm --cached -r .\n```\n\n更多例子可参考此[博客](https://blog.csdn.net/qq_42780289/article/details/98353792)。\n\n### mv\n\n用于移动或重命名一个文件、目录或软链接。\n```\ngit mv \u003cold_file\u003e \u003cnew_file\u003e\n```\n相当于`mv old_file new_file`，`git rm old_file`，`git add new_file`这三条命令一起运行，具体可参考此[博客](https://www.cnblogs.com/mf001/p/8663386.html)。\n\n新文件名已经存在，若想强制覆盖则可以使用 -f 参数：\n```\ngit mv -f \u003cold_file\u003e \u003cnew_file\u003e\n```\n\n### reset\n\n用于将指定 commit 和 branch 的文件替换暂存区的文件。有三个常用参数，分别是`--hard`，`--soft`，`--mixed`，默认是`--mixed`。具体细节和应用场景可参考此[博客](https://www.jianshu.com/p/c2ec5f06cf1a)。\n\n\n定义三种动作：\n1. 替换引用的指向，指向新的提交。\n2. 替换暂存区。暂存区内容将和指定的提交内容一样。\n3. 替换工作区。工作区和指定的提交内容一样。\n\n```\n// 执行 1。常用于合并多个 commit，类似于 squash\ngit reset --soft [commitId]/\u003cbranch\u003e\n// 执行 12。常用于移出暂存区的文件以作为 add 命令的反动作\ngit reset (--mixed) \u003ccommitId\u003e/\u003cbranch\u003e\n// 执行 123。常用于无条件放弃本地所有变更以向远程分支同步\ngit reset --hard \u003ccommitId\u003e/\u003cbranch\u003e\n// 将暂存区的所有改动撤销到工作区\ngit reset (--mixed) HEAD\n```\n\n### clean\n\n用来从工作目录中删除所有没有 tracked 过的文件，git clean 经常和 git reset --hard 一起结合使用。记住 reset 只影响被 track 过的文件，所以需要 clean 来删除没有 track 过的文件。结合使用这两个命令能让工作目录完全回到一个指定 \u003ccommit\u003e 的状态，具体可参考此[博客](https://blog.csdn.net/weixin_44137575/article/details/108142088)。\n\n```\n// 展示哪些文件会被删除，但不真正删除文件\ngit clean -n\n// 删除指定目录（默认当前目录）下所有没有 track 过的文件，但不会删除 .gitignore 文件里面指定的文件夹和文件\ngit clean -f (\u003cpath\u003e)\n// 删除指定目录（默认当前目录）下所有没有 track 过的文件，包括 .gitignore 文件里面指定的文件夹和文件\ngit clean -fx (\u003cpath\u003e)\n// 删除指定目录（默认当前目录）下所有没有被 track 过的文件和文件夹，但不会删除 .gitignore 文件里面指定的文件夹和文件\ngit clean -df (\u003cpath\u003e)\n// 删除指定目录（默认当前目录）下所有没有被 track 过的文件和文件夹，包括 .gitignore 文件里面指定的文件夹和文件\ngit clean -dfx (\u003cpath\u003e)\n```\n\n### stash\n\n常用来保存和恢复工作进度。注意该命令只对被 git 跟踪的文件有效。这是一个非常有用的命令，具体相关用法可查看此[博客](https://blog.csdn.net/andyzhaojianhui/article/details/80586695)。\n\n```\n// 保存当前工作进度，将工作区和暂存区恢复到修改之前。默认 message 为当前分支最后一次 commit 的 message\ngit stash\n// 作用同上，message 为此次进度保存的说明\ngit stash save message\n// 显示此次工作进度做了哪些文件行数的变化，此命令的 stash@{num} 是可选项，不带此项则默认显示最近一次的工作进度相当于 git stash show stash@{0}\ngit stash show stash@{num}\n// 显示此次工作进度做了哪些具体的代码改动，此命令的 stash@{num} 是可选项，不带此项则默认显示最近一次的工作进度相当于 git stash show stash@{0} -p\ngit stash show stash@{num} -p\n// 显示保存的工作进度列表，编号越小代表保存进度的时间越近\ngit stash list\n// 恢复工作进度到工作区，此命令的 stash@{num} 是可选项，在多个工作进度中可以选择恢复，不带此项则默认恢复最近的一次进度相当于 git stash pop stash@{0}\ngit stash pop stash@{num}\n// 恢复工作进度到工作区且该工作进度可重复恢复，此命令的 stash@{num} 是可选项，在多个工作进度中可以选择恢复，不带此项则默认恢复最近的一次进度相当于 git stash apply stash@{0}\ngit stash apply stash@{num}\n// 删除一条保存的工作进度，此命令的 stash@{num} 是可选项，在多个工作进度中可以选择删除，不带此项则默认删除最近的一次进度相当于 git stash drop stash@{0}\ngit stash drop stash@{num}\n// 删除所有保存的工作进度\ngit stash clear\n```\n此外有时我们只想保存部分文件，可以通过 add 一些不想保存的文件到暂存区去，然后使用`git stash --keep-index`来将工作区的工作进度保存，从而保留暂存区的进度并保存工作区的进度。具体可参考此[博客](https://juejin.im/post/6844903961770606605)。\n\n此外虽然 stash 命令默认不包含未跟踪的文件，但我们可以通过显示声明`-u`的方式来保存未被 git 追踪的文件。即`git stash -u`。\n\n### status\n\n用于显示文件和文件夹在工作区和暂存区的状态。\n```\ngit status\n// 展示中包含被忽略的文件\ngit status --ignored\n```\n\n### log\n\n用来回顾提交历史。可参考此[文档](https://git-scm.com/book/zh/v2/Git-%E5%9F%BA%E7%A1%80-%E6%9F%A5%E7%9C%8B%E6%8F%90%E4%BA%A4%E5%8E%86%E5%8F%B2)和此[博客](https://www.cnblogs.com/lenomirei/p/8379457.html)。以下只列出几个常用的打印格式：\n```\ngit log\ngit log --oneline\ngit log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)\u003c%an\u003e%Creset' --abbrev-commit --date=relative\n// 从 commit 信息中查找包含指定字符串的 commit\ngit log --grep \u003ckey\u003e\n// 从更改内容中查找包含指定字符串的 commit ，比如想查找哪些 commit 对代码中名为 \u003ckey\u003e 的函数进行了更改\ngit log -S \u003ckey\u003e\n```\n\n### show\n\n用来显示某个 commit 的具体提交信息和元信息。\n```\ngit show\ngit show \u003ccommitId\u003e\ngit show \u003cbranchName\u003e\n```\n\n### shortlog\n\n用于汇总 git 日志输出。非常人性化的一个命令。\n```\n// 按照用户列出其 commit 的次数以及每次 commit 的注释\ngit shortlog\n// 按照 commit 数量从多到少的顺序列出本仓库的贡献者并省略注释\ngit shortlog -sn\n```\n\n### diff\n\n用来比较文件之间的不同。具体用法可参考此[博客](https://blog.csdn.net/wq6ylg08/article/details/88798254)。diff 结果的格式可参考此[博客](http://www.ruanyifeng.com/blog/2012/08/how_to_read_diff.html)了解。\n```\n// 查看工作区与暂存区所有文件的变更\ngit diff\n// 查看暂存区与最后一次 commit 之间所有文件的变更\ngit diff --cached\n// 查看工作区与最后一次 commit 之间所有文件的变更\ngit diff HEAD\n// 查看两次 commit 之间的变动\ngit diff \u003ccommitId\u003e...\u003ccommitId\u003e\n// 查看两个分支上最后一次 commit 的内容差别\ngit diff \u003cbranch\u003e...\u003cbranch\u003e\n```\n\n### branch\n\n用于分支的操作，比如创建分支，查看分支等等。\n```\n// 可以查看本地分支对应的远程分支\ngit branch -vv \n// 查看远程版本库分支列表\ngit branch -r\n// 查看所有分支列表，包括本地和远程\ngit branch -a\n// 在当前位置新建 name 分支\ngit branch \u003cname\u003e\n// 在指定 commit 上新建 name 分支\ngit branch \u003cname\u003e \u003ccommitId\u003e\n// 强制创建 name 分支，原来的分支会被新建分支覆盖，从而迁移 name 分支指针\ngit branch -f \u003cname\u003e\n// 删除 dev 分支，如果在分支中有一些未 merge 的提交则会失败\ngit branch -d dev\n// 强制删除 dev 分支\ngit branch -D dev\n// 重命名分支\ngit branch -m \u003coldName\u003e \u003cnewName\u003e\n```\n\n### checkout\n\n用于切换分支或更新工作树文件以匹配索引或指定树中的版本，具体可参考此[博客](https://www.jianshu.com/p/cad4d2ec4da5)。\n```\n// 切换到指定分支\ngit checkout \u003cbranch\u003e\n// 在当前位置新建分支并切换\ngit checkout -b \u003cbranch\u003e\n// 在指定 commit 上新建分支并切换\ngit checkout -b \u003cbranch\u003e \u003ccommitId\u003e\n// 切换到指定 v1.2 的标签\ngit checkout tags/v1.2\n// 切换到指定 commit\ngit checkout \u003ccommitId\u003e\n// 基于当前所在分支新建一个赤裸分支，该分支没有任何的提交历史但包含当前分支的所有内容，相当于 git checkout -b \u003cnew_branch\u003e 和 git reset --soft \u003cfirstCommitId\u003e 两条命令 \ngit checkout --orphan \u003cnew_branch\u003e\n// 放弃工作区单个文件的变更，默认会从暂存区检出该文件，如果暂存区为空，则该文件会回滚到最近一次的提交状态\ngit checkout -- \u003cfilepath\u003e\n// 放弃工作区所有文件的变更（不包含未跟踪的）\ngit checkout .\n```\n\n### commit\n\n用于将暂存区里的改动给提交到本地的版本库。\n```\n// 提交一个描述为 message 的 commit\ngit commit -m \"message\"\n// 提交一个描述为 message 的 commit 并签名（部分开源社区会要求开发者提交的每个 commit 均使用 signoff 签名）\ngit commit -s -m \"message\"\n// 相当于 git add -a 和 git commit -m \"message\" 两条命令\ngit commit -am \"message\"\n// 在不增加一个新 commit 的情况下将新修改的代码追加到前一次的 commit 中，会弹出一个编辑器界面重新编辑 message 信息\ngit commit --amend\n// 在不增加一个新 commit 的情况下将新修改的代码追加到前一次的 commit 中，不需要再修改 message 信息\ngit commit --amend --no-edit\n// 提交一次没有任何改动的空提交，常用于触发远程 ci\ngit commit --allow-empty -m \"message\"\n// 修改 commit 时间\ngit commit -m \"message\" --date=\" Wed May 27 00:35:36 2020 +0800\"\n```\n\n### revert\n\n撤销某个 commit 的改动。在多人协作中如果某个 commit 已经被推到了远程，此时使用 revert 相比 reset + force-update 是更优雅的撤销变更方式。可参考此[博客](https://www.cnblogs.com/0616--ataozhijia/p/3709917.html)和[博客](https://www.jianshu.com/p/5e7ee87241e4)。\n\n```\n// 可以撤销指定的提交\ngit revert \u003ccommitId\u003e\n// 可以撤销不包括 commit1，但包括 commit2 的一串提交\ngit revert \u003ccommit1\u003e...\u003ccommit2\u003e\n// revert 命令会对每个撤销的 commit 进行一次提交，--no-commit 后可以最后一起手动提交\ngit revert --no-commit \u003ccommit1\u003e...\u003ccommit2\u003e\n// 退出 revert 过程，常在处理冲突出错时使用\ngit revert --abort\n// 继续 revert 过程，常在处理完冲突时使用\ngit revert --continue\n```\n\n### merge\n\n用于将两个或两个以上的开发历史合并在一起的操作，具体详细命令及介绍可看此[博客](https://www.jianshu.com/p/58a166f24c81)。\n\n```\n// 合并某个分支\ngit merge \u003cbranch\u003e\n// 退出 merge 过程，常在处理冲突出错时使用\ngit merge --abort\n// 继续 merge 过程，常在处理完冲突时使用\ngit merge --continue\n```\n\n此外，关于在可以 fast-forward 合并时是否再提交一个 commit 有一些争议，具体可看此[博客](https://www.jianshu.com/p/b357df6794e3)。\n```\ngit merge --no-ff \u003cbranch\u003e\n```\n\n此外可以在 merge 时也可以使用 --squash 参数，该命令能够将合并分支上的多个 commit 合并成一个 commit 放在当前分支上。相比 rebase 的 squash，其主要区别是该 commit 的 author 不一样。具体可查看此[博客](https://www.jianshu.com/p/684a8ae9dcf1)。\n```\ngit merge --squash \u003cbranch\u003e\n```\n\n### rebase\n\n相比 merge，合并分支历史的另一种管理方式，区别可查看此[博客](https://www.jianshu.com/p/6960811ac89c)。\n```\n// 变基某个分支\ngit rebase \u003cbranch\u003e\n// 退出 rebase 过程，常在处理冲突出错时使用\ngit rebase --abort\n// 继续 rebase 过程，常在处理完冲突时使用\ngit rebase --continue\n```\n\n此外也可以使用`git rebase -i HEAD~n` 来合并 n 个 commit，具体过程可参考此[博客](https://www.jianshu.com/p/6960811ac89c)。切记不要在共用的分支上进行 rebase，具体原因可查看此[博客](https://segmentfault.com/a/1190000005937408)。\n\n此外也可以使用`git rebase --onto \u003cbranch\u003e \u003cfromCommitId\u003e \u003ctoCommitId\u003e`来将 (fromCommitId,toCommitId] 上的所有 commit 合并到指定分支上，具体可参考此[博客](https://www.cnblogs.com/rickyk/p/3848768.html)。其实类似于 cherry-pick 多个提交。\n\n### cherry-pick\n\n用于将指定的 commit 应用于其他分支，具体可参考此[博客](http://www.ruanyifeng.com/blog/2020/04/git-cherry-pick.html)。\n```\n// 将指定的提交应用于当前分支\ngit cherry-pick \u003ccommitId\u003e\n// 将指定分支的最近一次提交应用于当前分支\ngit cherry-pick \u003cbranch\u003e\n// 将指定的两个提交先后应用于当前分支\ngit cherry-pick \u003ccommitId1\u003e \u003ccommitId2\u003e\n// 将指定范围内的多个提交(不包含 commitId1，包含 commitId2)先后应用于当前分支\ngit cherry-pick \u003ccommitId1\u003e..\u003ccommitId2\u003e\n// 将指定范围内的多个提交(包含 commitId1，包含 commitId2)先后应用于当前分支\ngit cherry-pick \u003ccommitId1\u003e^..\u003ccommitId2\u003e\n// 退出 cherry-pick 过程，常在处理冲突出错时使用\ngit cherry-pick --abort\n// 继续 cherry-pick 过程，常在处理完冲突时使用\ngit cherry-pick --continue\n```\n\n### tag\n\n常用于发布版本的管理，是指向某个 commit 的指针，具体可查看此[博客](https://blog.csdn.net/jdsjlzx/article/details/98654951)。\n\n```\n// 查看所有 tag\ngit tag\n// 基于本地当前分支最后一个 commit 创建 tag\ngit tag \u003ctagName\u003e \n// 基于指定 commit 创建 tag\ngit tag \u003ctagName\u003e \u003ccommitId\u003e\n// 基于指定 commit 创建 tag 并指定 message\ngit tag -a \u003ctagName\u003e -m \"message\"\n// 删除本地指定 tag\ngit tag -d \u003ctagName\u003e\n```\n\n### push\n\n用于将本地版本库的分支推送到远程服务器上对应的分支，具体用法可查看此[博客](https://www.cnblogs.com/qianqiannian/p/6008140.html)。\n```\n// 向远程推送指定分支\ngit push \u003cremote\u003e \u003cbranch\u003e\n// 向远程推送指定分支并绑定\ngit push -u \u003cremote\u003e \u003cbranch\u003e\n// 删除远程分支\ngit push \u003cremote\u003e :\u003cbranch\u003e\n// 向远程推送 tag\ngit push \u003cremote\u003e \u003ctagName\u003e\n// 删除远程 tag\ngit push \u003cremote\u003e :\u003ctagName\u003e\n// 向远程推送本地所有 tag\ngit push --tags \u003cremote\u003e\n```\n\n此外，当本地版本库的分支和远程版本库的对应分支有冲突时，如果想要强制覆盖，千万不要用`git push -f`，而是应该用`git push --force-with-lease`，具体原因可查看此[博客](https://www.cnblogs.com/walterlv/p/10236461.html)。\n\n### fetch\n\n用于从远程获取对象和引用到本地，具体可参考此[博客](https://www.yiibai.com/git/git_fetch.html)。\n\n```\n// 取回指定远程仓库的所有分支\ngit fetch \u003cremote\u003e \n// 取回指定远程仓库的指定分支\ngit fetch \u003cremote\u003e \u003cbranch\u003e\n// 取回所有远程仓库的所有分支\ngit fetch --all\n```\n\n### pull\n\n用于从远程获取代码并合并本地的版本，其实就是`git fetch`和`git merge FETCH_HEAD`的简写。有关其与 fetch 的区别，可以参考此[博客](https://www.cnblogs.com/ruiyang-/p/10764711.html)。\n\n```\n// 相当于 fetch + merge\ngit pull \u003cremote\u003e \u003cremote_branch\u003e:\u003clocal_branch\u003e\n// 相当于 fetch + rebase\ngit pull --rebase \u003cremote\u003e \u003cremote_branch\u003e:\u003clocal_branch\u003e\n```\n\n### bisect\n\n用于在 git 历史里面二分查找有 bug 的 commit。这是找 bug 时一个非常有用的命令，可以快速定位出引入 bug 的 commit。具体使用方式可查看此[博客](http://www.ruanyifeng.com/blog/2018/12/git-bisect.html)。\n```\n// 开始查找\ngit bisect start [endCommitId] [startCommitId]\n// 版本正确\ngit bisect good\n// 版本错误\ngit bisect bad\n// 退出查找\ngit bisect reset\n```\n\n### reflog\n\n常用来恢复本地错误操作。用户每次使用更新 HEAD 的 git 命令比如 commit、amend、cherry-pick、reset、revert 等都会被记录下来（不限分支），就像 shell 的 history 一样。 这样用户如果发生误操作删除了某个 commit 找不到时，调用此命令就可以查找到对应的 commit，从而回到误删前的状态了。具体使用场景可查看此[博客](https://cloud.tencent.com/developer/article/1413097)。\n\n```\ngit reflog\n```\n\n### fsck\n\n常用来恢复本地错误操作。只要是对 HEAD 进行操作的错误，一般情况下 reflog 都能够恢复，然而有些错误并不会对 HEAD 进行操作，例如将部分代码 stash 之后又不小心 drop 或 clear 掉了，那么此时 HEAD 并没有发生变化，reflog 对此类错误是无能为力的，这个时候 fsck 就可以派上用场了，该命令会更加底层，即直接检查 git 中的 blob，tree 和 commit 对象并找到悬空的对象，找到后即可以通过 `git show \u003ccommitId\u003e` 来查看该 commit 是否是被误操作删掉的 commit，如果是的话知道这个 commitId 无论如何都可以恢复了，不论用 merge 还是 cherry-pick 还是 checkout 等，可以参考一个恢复误删 stash 数据的[修复实例](https://www.jianshu.com/p/4cd5983586b2)，也可以进一步参考[官网实例](https://git-scm.com/book/zh/v2/Git-%E5%86%85%E9%83%A8%E5%8E%9F%E7%90%86-%E7%BB%B4%E6%8A%A4%E4%B8%8E%E6%95%B0%E6%8D%AE%E6%81%A2%E5%A4%8D#_data_recovery)。\n\n```\ngit fsck\n```\n\n### grep\n\n用于检索文件中的文本内容，更多用法可参考此[博客](https://www.softwhy.com/article-8652-1.html)。\n```\n// 检索所有包含指定关键字的文件\ngit grep text\n// 检索关键字出现在哪一行\ngit grep -n text\n// 统计每一个文件中检索到指定关键字的行数\ngit grep -c text\n```\n\n### blame\n\n用于查看某个文件的每一行内容由谁所写，类似于 IDEA 的 annotate 功能，具体可查看此[博客](http://www.zhai14.com/blog/git-blame-command-help-u-find-out-who-made-the-serious-mistake.html)。\n\n```\ngit blame \u003cfile\u003e\n```\n\n### gc\n\n用来清理 git 目录，删除掉无效的应用和文件，节约存储空间和传输大小。具体功能可参考此[博客](https://blog.csdn.net/lihuanshuai/article/details/37345565)。\n\n```\ngit gc\n```\n\n### submodule\n\n用于管理多个子项目。该命令允许一个 git 仓库作为另一个 git 仓库的子目录，并且保持父项目和子项目相互独立。具体用法可参考此[博客](http://www.ayqy.net/blog/%E7%90%86%E8%A7%A3git-submodules/)。\n\n## 配置\n\nGit 自带一个 git config 的工具来帮助设置控制 Git 外观和行为的配置变量。这些变量存储在三个不同的位置：\n\n/etc/gitconfig 文件: 包含系统上每一个用户及他们仓库的通用配置。如果使用带有 --system 选项的 git config 时，它会从此文件读写配置变量。\n\n~/.gitconfig 文件：只针对当前用户。可以传递 --global 选项让 Git 读写此文件。\n\n当前使用仓库的 Git 目录中的 config 文件（就是 .git/config）：针对该仓库。\n\n每一个级别覆盖上一级别的配置，所以 .git/config 的配置变量会覆盖 /etc/gitconfig 中的配置变量。\n\n## .gitignore\n\n.gitignore 文件可能从字面含义也不难猜出：这个文件里配置的文件或目录，会自动被 git 所忽略，不纳入版本控制。\n\n在日常开发中，我们的项目经常会产生一些临时文件，如编译 Java 产生的 *.class 文件，又或是 IDE 自动生成的隐藏目录（Intellij 的 .idea 目录、Eclipse 的 .settings 目录等）等等。这些文件或目录实在没必要纳入版本管理。在这种场景下，你就需要用到 .gitignore 配置来过滤这些文件或目录。\n\n配置的规则很简单，也没什么可说的，看几个例子，自然就明白了。\n\n这里推荐一下 Github 的[开源项目](https://github.com/github/gitignore)。在这里，你可以找到很多常用的模板，如：Java、Nodejs、C++ 的 .gitignore 模板等等。\n\n一般有两个级别的 .gitignore 文件：一个是全局的 ~/.gitignore 文件，另一个是仓库目录下的 .gitignore 文件。前者会覆盖后者的过滤条件，因此一个项目具体有哪些文件会被 git 忽略与两个文件都有关。我们可以将 .idea，*.class 这种常有的过滤目录添加到全局的 ~/.gitignore 中，这样子就不用在每个仓库的 .gitignore 中再次添加了。\n\n## 我的 alias 设置\n```\na = add \naa = add -A\nau = add -u\nae = add -e\nap = add -p\nbm = blame\nbr = branch -vv\nbra = branch -a\nbrd = branch -D\nbrf = branch -f\nbrm = branch -m\nbs = bisect\nbss = bisect start\nbsb = bisect bad\nbsg = bisect good\nbsr = bisect reset\ncf = config\ncfl = config --list\ncfg = config --global\ncfgl = config --global --list\ncfs = config --system\ncfsl = config --system --list\ncl = clone\nclb = clone --single-branch --branch\ncld = clone --depth\ncn = clean\ncnn = clean -n\ncnf = clean -f\ncnfx = clean -fx\ncndf = clean -df\ncndfx = clean -dfx\ncm = commit\ncmm = commit -s -m\ncmme = commit -s --allow-empty -m\ncma = commit -s --amend\ncman = commit -s --amend --no-edit\nco = checkout\ncob = checkout -b\ncod = checkout -- \ncoda = checkout .\ncoo = checkout --orphan\ncp = cherry-pick\ncpa = cherry-pick --abort\ncpc = cherry-pick --continue\ndf = diff\ndfc = diff --cached\ndfh = diff HEAD\nfh = fetch\nfha = fetch --all\nfc = fsck\nge = grep -n \ngec = grep -c \nit = init\nlg = log\nlgo = log --oneline\nlgga = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)\u003c%an\u003e%Creset' --abbrev-commit --date=relative\nlgge = log --grep\nlgs = log -S\nme = merge\nmea = merge --abort\nmec = merge --continue\nmen = merge --no-ff\nmvf = mv -f\npl = pull\nplr = pull --rebase\nps = push\npsu = push -u\npsf = push --force-with-lease\npst = push --tags\nrb = rebase\nrba = rebase --abort\nrbc = rebase --continue\nrbi = rebase -i\nrbo = rebase --onto\nrf = reflog\nrmc = rm --cached\nrmca = rm --cached -r .\nrmf = rm -f\nrmfa = rm -f -r .\nrs = reset\nrse = reset HEAD \nrsh = reset --hard\nrss = reset --soft\nrv = revert\nrva = revert --abort\nrvc = revert --continue\nrvn = revert --no-commit\nrt = remote\nrta = remote add\nrtp = remote prune\nrtpd = remote prune --dry-run\nrtrm = remote remove\nrtrn = remote rename\nrtsu = remote set-url\nrtsua = remote set-url --add\nrtsud = remote set-url --delete\nrts = remote show\nrtu = remote update\nrtv = remote -v\nsh = stash\nshu = stash -u\nshk = stash --keep-index\nsha = stash apply\nshc = stash clear\nshd = stash drop\nshl = stash list\nshp = stash pop\nshsh = stash show\nshshp = stash show -p\nshsa = stash save\nshsak = stash save --keep-index\nso = show\nsl = shortlog\nslsn = shortlog -s -n\nst = status\t--show-stash\nsti = stash ---ignored --show-stash\ntg = tag\ntga = tag -a\ntgd = tag -d\n```\n\n## 相关资料\n* [githug](https://github.com/Gazler/githug)\n* [learnGitBranching](https://github.com/pcottle/learnGitBranching)\n* [Git原理入门解析](https://mp.weixin.qq.com/s?__biz=MzI4Njc5NjM1NQ==\u0026mid=2247489338\u0026idx=2\u0026sn=6fdd8968003b8f59d43c09b72894e877\u0026chksm=ebd62816dca1a1003dcefb6dae8296dd08924426fa6f12d09b8695922007fcb9bfa342c35bd1\u0026scene=21#wechat_redirect)\n* [Git 从入门到精通，这一篇就够了](https://mp.weixin.qq.com/s/b8bQW2N5VC-qmGD4dTiwKQ)\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fonesizefitsquorum%2Fgit-tips","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fonesizefitsquorum%2Fgit-tips","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fonesizefitsquorum%2Fgit-tips/lists"}