{"id":13814839,"url":"https://github.com/xjh22222228/git-manual","last_synced_at":"2025-05-15T02:07:39.047Z","repository":{"id":37707364,"uuid":"260439816","full_name":"xjh22222228/git-manual","owner":"xjh22222228","description":":octocat:  git command reference manual; Git常用命令参考手册，涵盖了在开发中用到的git命令","archived":false,"fork":false,"pushed_at":"2025-04-28T01:18:26.000Z","size":12899,"stargazers_count":948,"open_issues_count":1,"forks_count":179,"subscribers_count":20,"default_branch":"main","last_synced_at":"2025-04-28T02:26:04.559Z","etag":null,"topics":["git","git-branch","git-clone","git-commands","git-config","git-flow","git-manual","git-rebase","git-submodule","git-tutorial"],"latest_commit_sha":null,"homepage":"https://github.com/xjh22222228/git-manual","language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xjh22222228.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-05-01T11:09:19.000Z","updated_at":"2025-04-28T01:18:30.000Z","dependencies_parsed_at":"2025-04-08T19:21:12.825Z","dependency_job_id":"83772db3-d10a-4abc-937e-8783c697f334","html_url":"https://github.com/xjh22222228/git-manual","commit_stats":{"total_commits":133,"total_committers":2,"mean_commits":66.5,"dds":"0.022556390977443663","last_synced_commit":"b218b0249708151b57616cf693744efde0575bf2"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xjh22222228%2Fgit-manual","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xjh22222228%2Fgit-manual/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xjh22222228%2Fgit-manual/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xjh22222228%2Fgit-manual/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xjh22222228","download_url":"https://codeload.github.com/xjh22222228/git-manual/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254259383,"owners_count":22040820,"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-branch","git-clone","git-commands","git-config","git-flow","git-manual","git-rebase","git-submodule","git-tutorial"],"created_at":"2024-08-04T04:02:38.527Z","updated_at":"2025-05-15T02:07:39.031Z","avatar_url":"https://github.com/xjh22222228.png","language":null,"funding_links":[],"categories":["Others"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"media/poster.png\" width=\"300\" /\u003e\n  \u003cbr /\u003e\n  \u003cb\u003eGit常用命令参考手册\u003c/b\u003e\n  \u003cp align=\"center\"\u003e基本涵盖了在开发中用到的git命令，能满足日常需求\u003c/p\u003e\n  \u003cp align=\"center\"\u003e通俗易懂的例子，30分钟快速入门\u003c/p\u003e\n  \u003cp align=\"center\"\u003e\n    \u003ca href=\"https://github.com/xjh22222228/git-manual/stargazers\"\u003e\u003cimg src=\"https://img.shields.io/github/stars/xjh22222228/git-manual\" alt=\"Stars Badge\"/\u003e\u003c/a\u003e\n    \u003cimg src=\"https://img.shields.io/github/license/xjh22222228/git-manual\" /\u003e\n    \u003ca href=\"https://hits.dwyl.com/xjh22222228/git-manual\"\u003e\n      \u003cimg src=\"https://hits.dwyl.com/xjh22222228/git-manual.svg\" /\u003e\n    \u003c/a\u003e\n  \u003c/p\u003e\n\u003c/p\u003e\n\n注：2020 年 10 月 GitHub 已将默认分支 `master` 更名为 `main` 分支。\n\n---\n\n# 目录\n\n- [git config 配置](#git-config-配置)\n- [git init 初始化仓库](#git-init-初始化仓库)\n- [git clone 克隆仓库](#git-clone-克隆仓库)\n- [git remote 管理仓库](#git-remote-管理仓库)\n- [git add 暂存文件](#git-add-暂存文件)\n- [git commit 提交文件](#git-commit-提交文件)\n- [git push 推送远端](#git-push-推送远端)\n- [git branch 创建查看删除重命名分支](#git-branch-创建查看删除重命名分支)\n- [git checkout 切换创建分支恢复文件](#git-checkout-切换创建分支恢复文件)\n- [git switch 切换创建分支](#git-switch-切换创建分支)\n- [git cherry-pick 转移提交](#git-cherry-pick-转移提交)\n- [git stash 临时保存](#git-stash-临时保存)\n- [git status 文件状态](#git-status-文件状态)\n- [git log 日志](#git-log-日志)\n- [git shortlog 日志](#git-shortlog-日志)\n- [git reflog 日志](#git-reflog-日志)\n- [git blame 最后修改信息](#git-blame-最后修改信息)\n- [git merge 合并](#git-merge-合并)\n- [git rm 删除文件](#git-rm-删除文件)\n- [git restore 还原](#git-restore-还原)\n- [git pull 拉取](#git-pull-拉取)\n- [git mv 移动/重命名文件](#git-mv-移动/重命名文件)\n- [git diff 比较文件内容差异](#git-diff-比较文件内容差异)\n- [git show 查看历史提交信息](#git-show-查看历史提交信息)\n- [git reset 回滚版本](#git-reset-回滚版本)\n- [git tag 标签](#git-tag-标签)\n- [git rebase 变基](#git-rebase-变基)\n- [git flow 工作流](#git-flow-工作流)\n- [git submodule 子模块](#git-submodule-子模块)\n- [git subtree 子树](#git-subtree-子树)\n- [git bisect 二分查找](#git-bisect-二分查找)\n- [git archive 归档](#git-archive-归档)\n- [清空 commit 历史](#清空-commit-历史)\n- [帮助](#帮助)\n- [提交规范](#提交规范)\n- [解决冲突](#解决冲突)\n- [仓库迁移](#仓库迁移)\n- [奇技淫巧](#奇技淫巧)\n- [GUI 客户端](#GUI-客户端)\n- [使用 SSH 克隆仓库](#使用-SSH-克隆仓库)\n- [其他](#其他)\n- [记住密码](#记住密码)\n- [清除账号](#清除账号)\n- [加速](#加速)\n- [思维导图](#思维导图)\n\n## git config 配置\n\ngit config 是 Git 中用于配置各种参数的命令，它允许你对 Git 的行为和环境进行个性化设置。这些设置可以分为三个不同的级别：系统级、全局级和仓库级，不同级别设置的优先级不同，仓库级设置会覆盖全局级设置，而全局级设置又会覆盖系统级设置。\n\n#### 基本语法\n\n```bash\ngit config [--system | --global | --local] \u003cname\u003e \u003cvalue\u003e\n```\n\n- `--system`：指定系统级配置，对所有用户的所有仓库生效。配置文件通常位于 `/etc/gitconfig`（Linux 或 macOS）。\n- `--global`：指定全局级配置，对当前用户的所有仓库生效。配置文件通常位于 `~/.gitconfig` 或 `~/.config/git/config`（Linux 或 macOS）。\n- `--local`：指定仓库级配置，仅对当前仓库生效。配置文件位于当前仓库的 `.git/config` 目录下。\n- 如果不指定以上任何选项，默认使用 `--local`。\n- `\u003cname\u003e`：要配置的参数名称。\n- `\u003cvalue\u003e`：要为参数设置的值。\n\n```bash\n# 查看全局配置列表\ngit config --global -l\n\n# 查看当前仓库配置列表\ngit config --local -l\n\n# 查看所有的配置以及它们所在的文件\ngit config --list --show-origin\n\n# 查看已设置的全局用户名/邮箱\ngit config --global --get user.name\ngit config --global --get user.email\n\n# 设置全局用户名/邮箱\ngit config --global user.name \"xiejiahe\"\ngit config --global user.email \"example@example.com\"\n\n# 设置本地当前工作区仓库用户名/邮箱\ngit config --local user.name \"xiejiahe\"\ngit config --local user.email \"example@example.com\"\n\n# 删除配置\ngit config --unset --global user.name\ngit config --unset --global user.email\n\n# 修改默认文本编辑器，比如 nano\n# 常用编辑器：emacs / nano / vim / vi\ngit config --global core.editor nano\n\n# 将默认差异化分析工具设置为 vimdiff\ngit config --global merge.tool vimdiff\n\n# 编辑当前仓库配置文件\ngit config -e  # 等价 vi .git/config\n\n# 文件权限的变动也会视为改动, 可通过以下配置忽略文件权限变动\ngit config core.fileMode false\n\n# 文件大小写设为敏感, git默认是忽略大小写\ngit config --global core.ignorecase false\n\n# 配置 git pull 时默认拉取所有子模块内容\ngit config submodule.recurse true\n\n# 记住提交账号密码, 下次操作可免账号密码\ngit config --global credential.helper store # 永久\ngit config --global credential.helper cache # 临时，默认15分钟\n```\n\n#### 命令别名配置\n\ngit 可以使用别名来简化一些复杂命令，类似 [alias](https://github.com/xjh22222228/linux-manual#alias) 命令。\n\n```bash\n# git st 等价于 git status\ngit config --global alias.st status\n\n# 如果之前添加过，需要添加 --replace-all 进行覆盖\ngit config --global --replace-all alias.st status\n\n# 执行外部命令, 只要在前面加 ! 即可\ngit config --global alias.st '!echo hello';\n# 加 \"!\" 可以执行外部命令执行一段复杂的合并代码过程，例如：\ngit config --global alias.mg '!git checkout develop \u0026\u0026 git pull \u0026\u0026 git merge main \u0026\u0026 git checkout -';\n\n# 删除 st 别名\ngit config --global --unset alias.st\n```\n\n#### 配置代理\n\n```bash\n# 设置\ngit config --global https.proxy  http://127.0.0.1:1087\ngit config --global http.proxy  http://127.0.0.1:1087\n\n# 查看\ngit config --global --get http.proxy\ngit config --global --get https.proxy\n\n# 取消代理\ngit config --global --unset http.proxy\ngit config --global --unset https.proxy\n```\n\n## git init 初始化仓库\n\n`git init [--bare] [directory]` 用于在指定目录下创建一个新的 Git 仓库\n\n`--bare`：可选参数，用于创建一个裸仓库。裸仓库通常用于作为远程仓库，不包含工作目录，主要用于多人协作时共享代码。\n\n`directory`：可选参数，指定要初始化 Git 仓库的目录。若不指定，将在当前目录下初始化仓库。\n\n#### 使用场景\n\n- `新项目初始化`：当你开始一个新的项目时，可使用 `git init` 命令将项目目录转换为 Git 仓库，从而方便对项目进行版本控制。\n- `已有项目纳入版本控制`：如果你有一个已经存在的项目，但尚未使用版本控制，也可以使用 `git init` 命令将其纳入 Git 的管理之下。\n\n```bash\n# 会在当前目录生成.git\ngit init\n\n# 以安静模式创建，只会打印错误或警告信息\ngit init -q\n\n# 在当前目录下创建一个裸仓库，里面只有 .git 下的所有文件\ngit init --bare\n```\n\n## git clone 克隆仓库\n\n#### 基本语法\n\n```bash\ngit clone [options] \u003crepository\u003e [\u003cdirectory\u003e]\n```\n\n- `options`：可选参数，用于指定克隆操作的一些额外设置。\n- `\u003crepository\u003e`：必选参数，指定要克隆的远程仓库的地址。这个地址可以是基于 HTTP/HTTPS 协议的，也可以是基于 SSH 协议的。\n- `\u003cdirectory\u003e`：可选参数，指定克隆到本地的目标目录名称。如果不指定，Git 会使用远程仓库的名称作为本地目录名。\n\n#### 常用选项\n\n- `--depth \u003cdepth\u003e`：进行浅克隆，只克隆指定深度的提交历史，而不是完整的历史记录。这可以显著减少克隆所需的时间和磁盘空间。例如，`git clone --depth 1 \u003crepository\u003e` 只克隆最新的一次提交。\n- `--branch \u003cbranch\u003e` 或 `-b \u003cbranch\u003e`：指定要克隆的分支。默认情况下，Git 会克隆远程仓库的默认分支（通常是 `main` 或 `master`）。例如，`git clone -b develop \u003crepository\u003e` 会克隆远程仓库的 `develop` 分支。\n- `--single-branch`：只克隆指定分支的内容，不克隆其他分支的历史记录。结合 `--branch` 选项使用，例如 `git clone --single-branch --branch feature \u003crepository\u003e` 只会克隆 `feature` 分支。\n\n```bash\n# https 协议克隆\ngit clone https://github.com/xjh22222228/git-manual.git\n\n# SSH 协议克隆\ngit clone git@github.com:xjh22222228/git-manual.git\n\n# 克隆指定分支， -b 指定分支名字，实际上是克隆所有分支并切换到 develop 分支上\ngit clone -b develop https://github.com/xjh22222228/git-manual.git\n\n# --single-branch 完全只克隆指定分支\ngit clone -b develop --single-branch https://github.com/xjh22222228/git-manual.git\n\n# 指定克隆后的文件夹名称\ngit clone https://github.com/xjh22222228/git-manual.git git-study # 如果后面是 . 在当前目录创建\n\n# 递归克隆，如果项目包含子模块就非常有用\ngit clone --recursive https://github.com/xjh22222228/git-manual.git\n\n# 浅克隆, 克隆深度为1, 只克隆指定分支且历史记录只保留最后一条, 通常用于减少克隆时间和项目大小\ngit clone --depth=1 https://github.com/xjh22222228/git-manual.git\ngit clone --depth=1 --no-single-branch https://github.com/xjh22222228/git-manual.git # --no-single-branch 同时克隆其他所有分支\n\n\n# 裸克隆, 没有工作区内容，不能进行提交修改，一般用于复制仓库\ngit clone --bare https://github.com/xjh22222228/git-manual.git\n\n# 镜像克隆, 也是裸克隆, 区别于包含上游版本库注册\ngit clone --mirror https://github.com/xjh22222228/git-manual.git\n```\n\n#### 克隆指定文件夹\n\n有些仓库会包含 客户端、服务端、等多个端的代码, 但又不想完整克隆整个项目, 只想克隆某个文件夹，这个时候就需要用到 `稀疏检出`。\n\n开启稀疏检出必须满足 2 个条件：\n\n- `core.sparsecheckout` 设置为 true\n- `.git/info/sparse-checkout` 文件列出要检出的目录列表\n\n本仓库有个 `media` 文件夹，用它来演示吧。\n\n```bash\n# 1、创建一个目录并进入\nmkdir hello-git \u0026\u0026 cd hello-git\n\n# 2、初始化仓库\ngit init\n\n# 3、设置仓库地址\ngit remote add origin https://github.com/xjh22222228/git-manual.git\n\n# 4、开启稀疏检出功能\ngit config core.sparsecheckout true\n\n# 5、编辑 .git/info/sparse-checkout 文件, 默认是没有需要手动新建\n# 也可以通过命令将需要检出的目录路径写入追加进去\necho \"media\" \u003e\u003e .git/info/sparse-checkout\n\n# 6、拉取内容, 这里指定的是 mater 分支\ngit pull origin main\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003e演示克隆指定文件夹.gif\u003c/summary\u003e\n  \n  \u003cimg src=\"media/gitclone-sparsecheckout.gif\"\u003e\n\u003c/details\u003e\n\n## git remote 管理仓库\n\n`git remote` 是 Git 里用于管理远程仓库的命令，借助它可以对远程仓库进行查看、添加、删除、重命名等操作。\n\n#### 基本语法\n\n```bash\ngit remote [options] [command] [args]\n```\n\n- `options`：可选参数，用于指定命令的一些额外设置。\n- `command`：指定要执行的操作命令。\n- `args`：执行命令所需的参数。\n\n```bash\n# 查看远程仓库服务器, 一般打印 origin , 这是 Git 给你克隆的仓库服务器的默认名字\n# 一般只会显示 origin , 除非你有多个远程仓库地址\ngit remote\n\n# 指定-v, 查看当前远程仓库地址\ngit remote -v\n\n# 添加远程仓库地址 example 是自定义名字\n# 添加完后可以通过 git remote 就能看到 example\ngit remote add example https://github.com/xjh22222228/git-manual.git\n\n# 查看指定远程仓库信息\ngit remote show example\n\n# 重命名远程仓库\ngit remote rename oldName newName # git remote rename example simple\n\n# 移除远程仓库\ngit remote remove example\n\n# 修改远程仓库地址，从HTTPS更改为SSH\ngit remote set-url origin git@github.com:xjh22222228/git-manual.git\n\n# 后续的推送可以指定仓库名字\ngit push example\n\n# 更新远程仓库的信息\ngit remote update\n```\n\n## git add 暂存文件\n\n`git add` 是 Git 中一个基础且关键的命令，主要用于将工作目录中修改或新增的文件添加到暂存区。\n\n#### 基本语法\n\n```bash\ngit add [options] \u003cfile\u003e...\n```\n\n- `options`：可选参数，用于指定不同的添加行为。\n- `\u003cfile\u003e`：要添加到暂存区的文件或目录，可以指定多个文件，用空格分隔。\n\n```bash\n# 暂存所有\ngit add -A\n\n# 暂存某个文件\ngit add ./README.md\n\n# 暂存当前目录所有改动文件\ngit add .\n\n# 暂存一系列文件\ngit add 1.txt 2.txt ...\n\n# 暂存所有修改文件或删除文件，创建新的文件不会被暂存\ngit add -u\n```\n\n#### 注意事项\n\n- `.gitignore 文件`：git add 命令会忽略 `.gitignore` 文件中指定的文件和目录，即使使用 `git add .` 或 `git add -A` 也不会添加这些被忽略的文件。\n- `重复添加`：如果多次对同一个文件执行 `git add` 命令，只有最后一次添加的更改会被包含在提交中。\n\n## git commit 提交文件\n\n`git commit` 是 Git 中用于将暂存区的内容永久保存到本地仓库历史记录中的关键命令。通过提交，你可以记录项目在某个特定时间点的状态，同时添加描述性的信息，方便后续查看和理解每次更改的目的。\n\n#### 基本语法\n\n```bash\ngit commit [options] [-m \u003cmessage\u003e]\n```\n\n- `options`：可选参数，用于指定不同的提交行为。\n- `-m \u003cmessage\u003e`：用于提供本次提交的简短描述信息，`\u003cmessage\u003e` 是具体的描述内容。\n\n```bash\n# -m 提交的描述信息\ngit commit -m \"changes log\"\n\n# 当提交信息较为复杂，需要多行描述可以不适用 -m 参数，Git会默认打开文本编辑器让你输入提交信息\ngit commit\n\n# 只提交某个文件\ngit commit README.md -m \"message\"\n\n# 提交并显示diff变化\ngit commit -v\n\n# 允许提交空消息，通常必须指定 -m 参数\ngit commit --allow-empty-message\n\n# 重写上一次提交信息，确保当前工作区没有改动\ngit commit --amend -m \"新的提交信息\"\n\n# 跳过验证, 如果使用了类似 husky 工具。\ngit commit --no-verify -m \"message\"\n```\n\n#### 修改提交日期\n\n执行 `git commit` 时 `git` 会采用当前默认时间，但有时候想修改提交日期可以使用 `--date` 参数。\n\n格式：`git commit --date=\"月 日 时间 年 +0800\" -m \"init\"`\n\n例子：`git commit --date=\"Mar 7 21:05:20 2021 +0800\" -m \"init\"`\n\n**月份简写如下：**\n\n| 月份简写 | 描述   |\n| -------- | ------ |\n| Jan      | 一月   |\n| Feb      | 二月   |\n| Mar      | 三月   |\n| Apr      | 四月   |\n| May      | 五月   |\n| Jun      | 六月   |\n| Jul      | 七月   |\n| Aug      | 八月   |\n| Sep      | 九月   |\n| Oct      | 十月   |\n| Nov      | 十一月 |\n| Dec      | 十二月 |\n\n## git push 推送远端\n\n`git push` 是 Git 中用于将本地仓库的提交推送到远程仓库的命令。当你在本地完成了一系列的代码修改、添加和提交操作后，就可以使用 `git push` 把这些更改同步到远程仓库。\n\n#### 基本语法\n\n```bash\ngit push [options] [\u003crepository\u003e [\u003crefspec\u003e...]]\n```\n\n- `options`：可选参数，用于指定推送操作的一些额外设置。\n- `\u003crepository\u003e`：可选参数，指定要推送的远程仓库的名称，默认是 origin。\n- `\u003crefspec\u003e`：可选参数，用于指定本地分支和远程分支的映射关系，格式为 `[+]\u003csrc\u003e:\u003cdst\u003e`。\n\n```bash\n# 默认推送当前分支\n# 等价于 git push origin, 实际上推送到一个叫 origin 默认仓库名字\ngit push\n\n# 设置上游分支并推送\n#  使用 -u 或 --set-upstream 选项，在推送的同时将本地分支与远程分支关联起来。之后，再进行该分支的推送或拉取操作时，就可以直接使用 git push 或 git pull，无需再指定远程仓库和分支名称\ngit push -u origin main\n\n# 本地分支推送到远程分支， 本地分支:远程分支\ngit push origin \u003cbranchName\u003e:\u003cbranchName\u003e\n\n# 强制推送, --force 缩写\ngit push -f\n\n# 删除远程分支\ngit push origin :old-feature\ngit push origin --delete old-feature # 或者\n```\n\n## git branch 创建查看删除重命名分支\n\n`git branch` 是 Git 中用于管理分支的核心命令\n\n#### 基本语法\n\n```bash\ngit branch [options] [branch-name] [start-point]\n```\n\n#### 查看分支\n\n```bash\n# 查看所有分支\ngit branch -a\n\n# 查看本地分支\ngit branch\n\n# 查看远端分支\ngit branch -r\n\n# 查看本地分支所关联的远程分支\ngit branch -vv\n\n# 查看本地 main 分支创建时间\ngit reflog show --date=iso main\n\n# 搜索分支, 借助 grep 命令來搜索, 包含关键字 dev\ngit branch -a | grep dev\n\n# 查看哪些分支已经合并到当前分支\n# 该命令会列出已经将其更改合并到当前分支的所有分支。通常，这些分支可以安全地删除。\ngit branch --merged\n\n# 查看哪些分支还未合并到当前分支\ngit branch --no-merged\n```\n\n#### 创建分支\n\n```bash\n# 创建分支\ngit branch new-feature\n\n# 从指定 commit 创建分支\ngit branch new-feature commit-hash\n\n# 强制创建分支\ngit branch -f main\n\n# 创建孤立分支，没有历史记录\ngit checkout --orphan new-feature\n```\n\n#### 删除分支\n\n```bash\n# 删除本地已合并的main分支\ngit branch -d main\n\n# 强制删除本地分支\ngit branch -D branch-to-delete\n```\n\n#### 重命名分支\n\n```bash\n# 重命名分支\ngit branch -m old-branch-name new-branch-name\n```\n\n#### 给分支添加备注\n\n有时候分支过多很难通过分支名去判断这个分支做了什么。\n\n```bash\n# 命令\n$ git config branch.{branch_name}.description 备注内容\n\n# 给 hotfix/tip 分支添加备注信息\n$ git config branch.hotfix/tip.description 修复细节\n```\n\n## git checkout 切换创建分支恢复文件\n\n`git checkout` 是 Git 里一个极为常用的命令，它主要用于在不同分支间切换、恢复文件以及创建新分支并切换到该分支。\n\n#### 基本语法\n\n```bash\ngit checkout [options] \u003cbranch\u003e\ngit checkout [options] -- \u003cfile\u003e\n```\n\n- `options`：可选参数，用来指定不同的操作行为。\n- `\u003cbranch\u003e`：要切换到的分支名称。\n- `\u003cfile\u003e`：要恢复的文件名称。\n\n#### 切换分支\n\n```bash\n# 切换分支\ngit checkout \u003cbranch\u003e\n\n# 切换上一个分支\ngit checkout -\n```\n\n在克隆时使用 `--depth=1` 切换其他分支，比如切换 dev 分支：\n\n```bash\ngit clone --depth=1 https://github.com/xjh22222228/git-manual.git\n\n# 切换 dev 分支\ngit remote set-branches origin 'dev'\ngit fetch --depth=1 origin dev\ngit checkout dev\n```\n\n#### 创建分支\n\n```bash\n# 创建本地 develop 分支并切换\ngit checkout -b develop\n\n# 根据 commit hash 创建新的分支\ngit checkout -b new-branch commit-hash\n\n# 创建远程分支, 实际上创建本地分支然后推送到远端\ngit checkout -b develop\ngit push origin develop\n\n# 创建一个空的分支, 不继承父分支，历史记录是空的，一般至少需要执行4步\ngit checkout --orphan develop\ngit rm -rf .  # 这一步可选，如果你真的想创建一个没有任何文件的分支\ngit add -A \u0026\u0026 git commit -m \"提交\" # 添加并提交，否则分支是隐藏的 （执行这一步之前需要注意当前工作区必须保留一个文件，否则无法提交）\ngit push --set-upstream origin develop # 推送到远程\n```\n\n#### 恢复文件到上一次提交的状态\n\n```bash\n# -- 后跟着文件名称， 表示恢复该文件到上一次提交的状态\ngit checkout -- file.txt\n```\n\n## git switch 切换创建分支\n\n`git switch` 是 Git 2.23 版本引入的新命令，旨在简化分支切换操作，它是 `git checkout` 部分功能的替代，主要用于在不同分支之间进行切换，让操作更加清晰和安全。\n\n#### 基本语法\n\n```bash\ngit switch [options] \u003cbranch\u003e\ngit switch [options] -c \u003cnew-branch\u003e [start-point]\n```\n\n- `options`：可选参数，用于指定不同的操作行为。\n- `\u003cbranch\u003e`：要切换到的目标分支名称。\n- `-c`：创建新分支并切换到该分支。\n- `\u003cnew-branch\u003e`：要创建的新分支名称。\n- `start-point`：可选参数，指定新分支的起始提交点，默认是当前分支的最新提交。\n\n#### 切换分支\n\n```bash\n# 切换到 develop 分支\ngit switch develop\n\n# 切换到上一个分支\ngit switch -\n\n# 强制切换到 develop 分支，并抛弃本地所有修改\ngit switch -f develop\n\n# -t, 切换远端分支, 如果用了 git remote 添加一个新仓库就需要用 -t 进行切换\ngit switch -t upstream/main\n```\n\n#### 创建分支\n\n```bash\n# 创建分支并切换\ngit switch -c newBranch\n\n# 强制创建分支\ngit switch -C newBranch\n\n# 从前3次提交进行创建新的分支\ngit switch -c newBranch HEAD〜3\n\n# 也可以从某个 commit hash 创建分支\ngit switch -c new-branch commit-hash\n\n# --track 创建 dev 分支并与远程 code/dev 分支关联\ngit switch --track code/dev\n```\n\n## git cherry-pick 转移提交\n\n`git cherry-pick` 是 Git 里一个非常实用的命令，其作用是把指定的提交应用到当前分支。\n\n#### 使用场景\n\n- `同步部分提交`：当你仅需把某个分支里的部分提交同步到另一个分支时，`git cherry-pick` 就派上用场了，而无需合并整个分支。\n- `修复特定问题`：要是在某个分支发现了问题，并且已经在另一个分支修复，那么可以使用 `git cherry-pick` 把修复提交应用到有问题的分支。\n\n#### 基本用法\n\n- `--edit|-e`：在应用提交之前，允许你编辑提交信息。\n- `--no-commit`：应用提交但不自动创建新的提交，这样你可以在之后手动提交。\n- `--signoff`：在提交信息中添加你的签名，表示你对提交负责。\n\n```bash\n# 单个提交\ngit cherry-pick \u003ccommit-hash\u003e\n\n# 多个连续提交\n#  \u003cstart-commit-hash\u003e 是起始提交的哈希值，\u003cend-commit-hash\u003e 是结束提交的哈希值。注意，起始提交不会被包含在内，只会应用从起始提交之后到结束提交的所有提交。\ngit cherry-pick \u003cstart-commit-hash\u003e..\u003cend-commit-hash\u003e\n\n# 多个不连续提交\ngit cherry-pick \u003ccommit-hash-1\u003e \u003ccommit-hash-2\u003e \u003ccommit-hash-3\u003e\n```\n\n```bash\n# 可以是一个 commit_id 或者是分支名\n# 如果是分支名则是最后一次提交\ngit cherry-pick \u003ccommit_id\u003e|branch_name\n\n# 支持转移多个提交, 会产生多个提交记录\ngit cherry-pick \u003ccommit_id1\u003e \u003ccommit_id2\u003e\n\n# 保留原有作者信息进行提交\ngit cherry-pick -x \u003ccommit_id\u003e\n\n# 重新编辑提交信息, 否则会应用之前的commit消息\ngit cherry-pick -e \u003ccommit_id\u003e\n\n# 断开当前操作回到初始状态\ngit cherry-pick --abort\n\n# 当发生冲突时解决冲突后使用 git add 加入到暂存区然后执行下面命令继续执行\ngit cherry-pick --continue\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003e演示转移提交.gif: 把 `dev` 分支的第三次提交转移到当前 `main` 分支。\u003c/summary\u003e\n  \n  \u003cimg src=\"media/cherry.gif\"\u003e\n\u003c/details\u003e\n\n## git stash 临时保存\n\n`git stash`是 Git 里一个实用的命令，它可以把当前工作目录里还没提交的修改（包含暂存区和非暂存区的修改）保存起来，让工作目录回到上一次提交时的干净状态。\n\n应用场景：假设当前分支某些功能做到一半了, 突然需要切换到其他分支修改 Bug, 但是又不想提交（因为切换分支必须清理当前工作区，否则无法切换），这个时候 `git stash` 应用场景就来了。\n\n```bash\n# 保存当前修改工作区内容\ngit stash\n\n# 保存时添加注释, 推荐使用此命令\ngit stash save \"修改了#28 Bug\"\n\n# 保存包含没有被git追踪的文件\ngit stash -u\n\n# 查看当前保存列表\ngit stash list\n\n# 恢复修改工作区内容, 会从 git stash list 移除掉\ngit stash pop # 恢复最近一次保存内容到工作区, 默认会把暂存区的改动恢复到工作区\ngit stash pop stash@{1} # 恢复指定 id， 通过 git stash list 可查到\ngit stash pop --index # 恢复最近一次保存内容到工作区, 但如果是暂存区的内容同样恢复到暂存区\n\n# 与 pop 命令一致, 唯一不同的是不会移除保存列表\ngit stash apply\n\n# 清空所有保存\ngit stash clear\n\n# 清空指定 stash id, 如果 drop 后面不指定id清除最近的一次\ngit stash drop stash@{0}\ngit stash drop  # 清除最近一次\n\n# 查看已保存的修改文件内容\ngit stash show -p stash@{0}\n```\n\n## git status 文件状态\n\n`git status`是 Git 中一个非常基础且常用的命令，它用于显示工作目录和暂存区的状态。通过该命令，你可以了解到哪些文件被修改、哪些文件被添加到暂存区、哪些文件被删除等信息。\n\n```bash\n# 完整查看文件状态\ngit status\n\n# 以短格式给出输出\ngit status -s\n\n# 忽略子模块\ngit status --ignore-submodules\n\n# 显示已忽略的文件\ngit status --ignored\n```\n\n## git log 日志\n\n执行 `git log` 命令，会显示当前分支从最近到最早的所有提交记录，每条记录包含提交哈希、作者、日期以及提交说明。\n\n```bash\n# 查看完整历史提交记录\ngit log\n\n# 查看前N次提交记录 commit message\ngit log -2\n\n# 查看前N次提交记录，包括diff\ngit log -p -2\n\n# 从 commit 进行搜索, 可以指定 -i 忽略大小写\ngit log -i --grep=\"fix: #28\"\n\n# 从工作目录搜索包含 alert(1) 这段代码何时引入\ngit log -S \"alert(1)\"\n\n# 查看指定作者历史记录\ngit log --author=xjh22222228\n\n# 查看某个文件的历史提交记录\ngit log README.md\n\n# 只显示合并日志\ngit log --merges\n\n# 将每条提交记录显示为一行，仅包含提交哈希的前几位和提交说明，便于快速查看\ngit log --oneline\n\n# 以图形查看日志记录\ngit log --graph --oneline\n\n# 以倒序查看历史记录\ngit log --reverse\n\n# --since 和 --until：显示指定时间范围内的提交记录\ngit log --since=\"2025-03-01\" --until=\"2025-03-25\"\n```\n\n#### 格式化日志\n\n在使用 `git log` 命令时可以携带 `--pretty=format` 用来格式化日志。\n\n**常用格式如下：**\n\n| 参数 | 描述                                                                   |\n| ---- | ---------------------------------------------------------------------- |\n| %H   | 完整 commit hash                                                       |\n| %h   | 简写 commit hash 一般是前 7 位                                         |\n| %T   | 完整 hash 树                                                           |\n| %t   | 简写 hash 树                                                           |\n| %an  | 作者名称                                                               |\n| %ae  | 作者邮箱                                                               |\n| %ad  | 作者日期, RFC2822 风格：`Thu Jul 2 20:42:20 2020 +0800`                |\n| %ar  | 作者日期, 相对时间：`2 days ago`                                       |\n| %ai  | 作者日期, ISO 8601-like 风格： `2020-07-02 20:42:20 +0800`             |\n| %aI  | 作者日期, ISO 8601 风格： `2020-07-02T20:42:20+08:00`                  |\n| %cn  | 提交者名称                                                             |\n| %ce  | 提交者邮箱                                                             |\n| %cd  | 提交者日期，RFC2822 风格：`Thu Jul 2 20:42:20 2020 +0800`              |\n| %cr  | 提交者日期，相对时间：`2 days ago`                                     |\n| %ci  | 提交者日期，ISO 8601-like 风格： `2020-07-02 20:42:20 +0800`           |\n| %cI  | 提交者日期，ISO 8601 风格： `2020-07-02T20:42:20+08:00`                |\n| %d   | 引用名称： (HEAD -\u003e main, origin/main, origin/HEAD)                    |\n| %D   | 引用名称，不带 `()` 和 换行符： HEAD -\u003e main, origin/main, origin/HEAD |\n| %e   | 编码方式                                                               |\n| %B   | 原始提交内容                                                           |\n| %C   | 自定义颜色                                                             |\n\n例子：\n\n```bash\ngit log -n 1 --pretty=format:\"%an\" # xjh22222228\n\ngit log -n 1 --pretty=format:\"%ae\" # xjh22222228@gmail.com\n\ngit log -n 1 --pretty=format:\"%d\" #  (HEAD -\u003e main, origin/main, origin/HEAD)\n\n# 自定义输出颜色, %C后面跟着颜色名\ngit log --pretty=format:\"%Cgreen 作者：%an\"\n```\n\n## git shortlog 日志\n\n`git shortlog` 命令，它会按照作者对提交进行分组，并统计每个作者的提交数量，同时显示每个作者的最新提交信息。\n\n```bash\n# 默认以贡献者分组进行输出\ngit shortlog\n\n# 列出提交者代码贡献数量, 打印作者和贡献数量\ngit shortlog -sn\n\n# 以提交贡献数量排序并打印出message\ngit shortlog -n\n\n# 采用邮箱格式化的方式进行查看贡献度\ngit shortlog -e\n```\n\n## git reflog 日志\n\n`git reflog` 命令，它会展示本地仓库引用的更新历史，每条记录包含引用的哈希值、操作名称、提交信息以及时间等内容。\n\n- 显示 HEAD（或指定引用）在过去一段时间内的所有移动记录。\n- 包括提交、分支切换、重置、变基等操作，即使这些提交不再属于任何分支。\n- 面向引用变更，记录操作历史，适合救急和调试。\n\n如果你执行 `git reset --hard` 或删除分支，某些提交会变得 `“不可达”`，`git log` 不会显示。\n\n```bash\n# 每条提交记录以一行的形式输出日志\ngit reflog\n\n# 恢复丢失提交，通过 git reflog 找回 commit_id\ngit reflog\ngit reset --hard commit_id\n\n# 指定显示的记录数量。例如，显示最近的 5 条记录\ngit reflog -n 5\n\n# 以相对时间（如 “2 days ago”）显示记录的日期\ngit reflog --relative-date\n\n# 按照不同的日期格式显示记录。例如，以 iso 格式显示日期\ngit reflog --date=iso\n```\n\n#### 注意事项\n\n- 记录的是本地仓库的操作历史，不会随着代码一起推送到远程仓库。\n- 记录默认会保留 90 天（对于可达对象）或 30 天（对于不可达对象），但这个时间可以通过配置项 `gc.reflogExpire` 和 `gc.reflogExpireUnreachable` 进行调整。\n\n## git blame 最后修改信息\n\n`git blame` 是 Git 里一个极为实用的命令，其主要功能是逐行查看文件内容的最后修改信息，包括最后修改该内容的提交哈希、作者、日期以及提交信息。\n\n#### 基本语法\n\n```bash\ngit blame \u003c文件名\u003e\n```\n\n```bash\n# 查看 README.md 文件的修改信息，他会以每行修改信息展示\ngit blame README.md\n\n# 查看文件的指定的行数修改信息\ngit blame -L 11,12 README.md\ngit blame -L 11 README.md   # 查看第11行以后\n\n# 显示完整的 hash 值\ngit blame -l README.md\n\n# 显示修改的行数\ngit blame -n README.md\n\n# 显示作者邮箱\ngit blame -e README.md\n\n# 对参数进行一个组合查询\ngit blame -enl -L 11 README.md\n\n# -w 忽略空格更改\ngit blame -w README.md\n\n# 以更易读的格式显示时间戳\ngit blame -c README.md\n```\n\n## git merge 合并\n\n`git merge`是 Git 里用于将分支的修改合并到当前分支的命令。\n\n`feature/v1.0.0` 分支代码合并到 `develop`\n\n```bash\ngit checkout develop\ngit merge feature/v1.0.0\n```\n\n将上一个分支代码合并到当前分支\n\n```bash\ngit merge -\n```\n\n以安静模式合并, 把 develop 分支合并到当前分支并不输出任何信息\n\n```bash\ngit merge develop -q\n```\n\n合并不编辑信息, 跳过交互\n\n```bash\ngit merge develop --no-edit\n```\n\n合并分支后不进行提交\n\n```bash\ngit merge develop --no-commit\n```\n\n退出合并，恢复到合并之前的状态\n\n```bash\ngit merge --abort\n```\n\n合并某个分支指定文件或目录, 需要注意的是这会直接覆盖现有文件，而不是本质上的合并。\n\n```bash\n# 将dev分支的 src/utils/http.js src/utils/load.js 2个文件合并到当前分支下\ngit checkout dev src/utils/http.js src/utils/load.js\n```\n\n允许合并不相关的历史记录，如果在克隆使用了 `--depth` 参数会导致合并的时候会发生较大冲突，`allow-unrelated-histories` 参数可以有效的解决这个问题\n\n```bash\ngit merge develop --allow-unrelated-histories\n```\n\n合并提交指定自定义的提交信息\n\n```bash\ngit merge develop -m \"Merge develop branch into main\"\n```\n\n## git rm 删除文件\n\n`git rm \u003cfile\u003e`：此命令用于把文件从工作目录和索引中移除，并且会把这次移除操作记录到下一次提交里。\n\n```bash\n# 删除 1.txt 文件\ngit rm 1.txt\n\n# 删除当前所有文件, 与rm -rf 命令不同的是不会删除 .git 目录\ngit rm -rf .\n\n# 清除当前工作区缓存，但不会删除文件，通常用于修改文件名不生效问题\ngit rm -r --cached .\n\n# 把文件从索引中移除，工作目录里的文件会被保留。这在你想让 Git 不再追踪某个文件\ngit rm --cached \u003cfile\u003e\n```\n\n## git restore 还原\n\n`git restore`是 Git 2.23 版本引入的一个命令，主要用于恢复工作区文件和暂存区的状态。\n\n是为了分离 `git checkout` / `git reset` 职责。\n\n```bash\n# 撤销工作区文件修改, 不包括新建文件\ngit restore README.md # 一个文件\ngit restore README.md README2.md # 多个文件\ngit restore . # 当前全部文件\n\n# 从暂存区回到工作区\ngit restore --staged README.md\n```\n\n## git pull 拉取\n\n`git pull` 拉取最新内容并合并。\n\n#### 拉取远程分支最新内容\n\n默认情况下拉取当前分支\n\n```bash\n# 如果出现冲突会自动合并\ngit pull\n```\n\n#### 拉取指定分支\n\n```bash\n# 远程分支名:本地分支名\ngit pull origin main:main\n# 如果某个远程分支拉取并合并到当前分支后面可以省略\ngit pull origin main\n```\n\n#### 拉取指定工作目录\n\n```bash\n# 默认情况下拉取会在当前工作目录中，但如果想拉取指定工作目录，可以指定 `-C`\ngit -C /opt/work pull\n```\n\n#### 同步 Fork 仓库\n\n当 Fork 别人仓库后，原仓库发生变化，可以通过以下操作合并到 Fork 仓库\n\n```bash\n# 1、添加原远程仓库：git remote add 自定义名字 远程仓库地址\ngit remote add upstream https://github.com/xjh22222228/git-manual.git\n\n# 2、拉取远程最新分支内容\ngit fetch --depth=1 upstream main\n\n# 3、远程最新内容合并到当前分支(允许合并不相关历史记录)\ngit merge upstream/main --allow-unrelated-histories\n\n# 4、推送到远程\ngit push\n```\n\n## git mv 移动-重命名文件\n\n`git mv` 命令用来重命名文件或移动文件, 大部分开发者会选择手动进行移动文件, 手动和用 `git mv` 是有区别的。\n\n手动和命令两者的区别（假设`README.md`重命名为`README2.md`）：\n\n- 手动：先删除 `README.md`, 然后创建 `README2.md`, 历史记录无法正常追踪\n- `git mv`: 实际上是更新索引，把文件进行重命名, 可以通过历史记录方便检索\n\n`git mv` 和 uninx `mv` 命令很像，如果你熟悉的话。\n\n注意：新创建的文件不支持 `git mv` , 必须先提交。\n\n```bash\n# 将 1.txt 重命名为 2.txt\ngit mv 1.txt 2.txt\n\n# 强制将 1.txt 重命名为 2.txt, 不管2.txt文件存不存在\ngit mv -f 1.txt 2.txt\n\n# 移动目录也一样\ngit mv temp temp2\n```\n\n## git diff 比较文件内容差异\n\n`git diff` 命令用于查看`工作区文件`内容与暂存区或远端之间的差异。\n\n#### git diff\n\n```bash\n# 查看所有文件工作区与暂存区的差异\ngit diff\n\n# 查看指定文件工作区与暂存区差异\ngit diff README.md\n\n# 查看指定 commit 内容差异\ngit diff dce06bd\n\n# 对比2个commit之间的差异\ngit diff e3848eb dce06bd\n\n# 比较2个分支最新提交内容差异, develop分支与main分支, 如果没有差异返回空\ngit diff develop main\n\n# 比较2个分支指定文件内容差异, develop 和 main READNE.md 文件差异\ngit diff develop main README.md README.md\n\n# 查看工作区冲突文件差异\ngit diff --name-only --diff-filter=U\n\n# 查看上一次修改了哪些文件\ngit diff --name-only HEAD~\ngit diff --name-only HEAD~~ # 前2次...\n```\n\n## git show 查看历史提交信息\n\n可以通过 `git show` 命令查看历史提交信息。\n\n```bash\n# 不指定参数默认查看最新一条信息\ngit show\n\n# 指定 commit_id 查看\ngit show d68a1ef\n\n# 也可以指定 commit_id 查看指定文件提交信息\ngit show d68a1ef README.md\n\n# 只指定文件名查看最后一次提交包含此文件的提交信息\ngit show README.md\n\n# 指定分支名查看最后一次提交信息\ngit show feature/dev\n```\n\n## git reset 回滚版本\n\n回滚版本有 2 种方法：\n\n- `git reset` - 会改变提交历史。当使用 git reset 移动 HEAD 指针后，被跳过的提交在没有其他引用的情况下，最终可能会被 Git 的垃圾回收机制清理掉，从而从提交历史中消失。\n- `git revert` - 不会删除任何原有的提交，而是在提交历史中新增一个反向操作的提交。提交历史的连续性得以保留，所有原有的提交仍然存在于历史记录中。\n\n`git reset` 命令用法：\n\n```bash\n# --hard 丢弃工作区和暂存区，回到当前提交\ngit reset --hard\n\n# 回滚上一个版本\ngit reset --hard HEAD^\n\n# 回滚上两个版本\ngit reset --hard HEAD^^\n\n# 回滚到指定 commit_id ， 通过 git log 查看\ngit reset --hard 'commit id'\n\n# 回滚到前一次修改，默认--mixed，重置暂存区，工作区不变\ngit reset HEAD~1\n\n# --soft 保留之前的暂存区和工作区\ngit reset --soft HEAD^\n```\n\n`git revert` 命令用法：\n\n```bash\n# 回滚上一次提交版本\ngit revert HEAD^\n\n# 回滚指定commit\ngit revert 8efef3d37\n\n# --no-edit 回滚并跳过编辑消息\ngit revert HEAD^ --no-edit\n\n# 断开当前操作，还原初始状态\ngit revert --abort\n\n```\n\n## git tag 标签\n\n```bash\n# 列出本地所有标签\ngit tag\n\n# 列出远程所有标签\ngit ls-remote --tags origin\n\n# 按照特定模式查找标签, `*` 模板搜索\ngit tag -l \"v1.0.0*\"\n\n# 创建带有附注标签\ngit tag -a v1.1.0 -m \"标签描述\"\n\n# 创建轻量标签, 不需要带任何参数\ngit tag v1.1.0\n\n# 后期打标签, 假设之前忘记打标签了，可以通过git log查看commit id\ngit log\ngit tag -a v1.1.0 \u003ccommit_id\u003e\n\n# 推送到远程，默认只是本地创建\ngit push origin v1.1.0\n\n# 一次性推送所有标签到远程\ngit push origin --tags\n\n# 删除标签, 你需要再次运行 git push origin v1.1.0 才能删除远程标签\ngit tag -d v1.1.0\n\n# 删除远程标签\ngit push origin --delete v1.1.0\n\n# 检出标签\ngit checkout v1.1.0\n\n# 查看本地某个标签详细信息\ngit show v1.1.0\n```\n\n## git rebase 变基\n\n`git rebase` 命令有 2 个比较实用的功能：\n\n- 将多个 commit 记录合并为一条\n- 代替 `git mrege` 合并代码\n\n### 1、将多个 commit 记录合并为一条\n\n要注意保证当前工作区没内容再操作。\n\n1、指定需要操作的记录，这时候会进入交互式命令\n\n```bash\n# start起点必填， end 可选，默认当前分支 HEAD 所指向的 commit\ngit rebase -i \u003cstart\u003e \u003cend\u003e\n\ngit rebase -i HEAD~5 # 操作最近前5条提交记录\ngit rebase -i e88835de # 或者以 commit_id 进行操作\n```\n\n| 参数      | 描述                                                            |\n| --------- | --------------------------------------------------------------- |\n| p, pick   | 保留当前 commit，默认                                           |\n| r, reword | 保留当前 commit，但编辑提交消息                                 |\n| e, edit   | 保留当前 commit，但停止修改                                     |\n| s, squash | 保留当前 commit，但融入上一次提交                               |\n| b, break  | 在这里停止（稍后使用 `git rebase --continue` 继续重新设置基准） |\n| d, drop   | 删除当前 commit                                                 |\n\n这里是倒序排列，最新的记录在最后\n\n\u003cimg src=\"media/gitrebase-3.png\" width=\"700\" /\u003e\n\n2、除了第一条后面全部改成 `s` 或 `squash`:\n\n\u003cimg src=\"media/gitrebase-4.png\" width=\"700\" /\u003e\n\n3、按 `:wq` 退出交互式，接着进入另一个交互式来编辑 commit 消息, 如果不需要修改之前的 commit 消息则直接退出：\n\n\u003cimg src=\"media/gitrebase-5.png\" width=\"700\" /\u003e\n\n4、强制推送到远端\n\n```bash\n# 推送到 main 分支\ngit push -u -f origin main\n```\n\n### 2、合并分支代码\n\n都说用 `git rebase` 代替 `git merge` 进行合并，这 2 个区别在于 `git rebase` 可以使历史记录更清晰, 下面 2 张图对比一下：\n\n第一张图是 `git rebase`，第二张图是 `git merge`。\n\n可以看出 `git rebase` 是一条直线的，`git merge` 则是各种交叉，很难理解。\n\n\u003cimg src=\"media/gitrebase-1.png\" width=\"700\" /\u003e\n\u003cimg src=\"media/gitrebase-2.png\" width=\"700\" /\u003e\n\n假设有 2 个分支，main 和 dev，下面使用 `git rebase` 将 dev 分支代码合并到 main 分支上。\n\n```bash\n# 1、先切换到 main 分支\ngit switch main\n\n# 2、dev 分支合并到当前 main 分支\ngit rebase dev\n\n# 没有冲突情况, 直接推送\ngit push\n\n# 发生冲突情况，先解决完冲突 =\u003e 暂存 =\u003e 继续 =\u003e 强推\ngit add -A\ngit rebase --continue # 继续\ngit push -f # 强制推送\n```\n\n中断 `git rebase` 操作, 如果操作一半不想继续使用 `rebase` 命令则可以中断此次操作。\n\n```bash\n$ git rebase --abort\n```\n\n## git flow 工作流\n\nGit Flow 是一套基于 git 的工作流程，这个工作流程围绕着 project 的发布(release)定义了一个严格的如何建立分支的模型。\n\n`git flow` 只是简化了操作命令，不用 `git flow` 也可以，只要遵循 `git flow` 流程操作即可，手动一条一条命令执行也一样的。\n\n`git flow` 不是内置命令，需要单独安装。\n\n#### 初始化\n\n每个仓库都必须初始化一次才能使用，这是针对当前用户而言的。\n\n```bash\n# 通常直接回车以完成默认设置\ngit flow init\n```\n\n#### 开始开发一个功能\n\n假设我们要开始开发一个新的功能比如登录注册，这个时候就要打一个 `feature` 分支进行独立开发。\n\n```bash\n# 步骤一：开启新的功能, 起一个分支名叫 v1.1.0, 建立后分支名为 feature/v1.1.0\ngit flow feature start v1.1.0\n\n# 步骤二：将分支推送到远程, 在团队协作中这一步少不了\ngit flow feature publish v1.1.0\n\n# 最后：完成功能, 会将当前分支合并到 develop 分支然后删除 feature/v1.1.0 分支，回到 develop\ngit flow feature finish v1.1.0\n```\n\n#### 打补丁\n\n什么情况下需要打补丁？ 假设已经上线的功能有 BUG 需要修复就需要打补丁了。\n\nhotfix 是针对 `main` 分支进行打补丁的。\n\n```bash\n# 步骤一：开启一个补丁分支叫 fix_doc 用于修改文档错误，建立后分支名为 hotfix/fix_doc\ngit flow hotfix start fix_doc\n\n# 步骤二：推送到远程，也可以不推，如果多人同时改BUG就需要推送共享分支\ngit flow hotfix publish fix_doc\n\n# 最后：完成补丁, 将当前分支合并到 main 和 develop，然后删除分支，回到 develop\ngit flow hotfix finish fix_doc\n```\n\n#### 发布\n\n假设产品给了个新需求并完成，这个时候可以选择发布。不发布也行，但是发布后会有版本区分，以后想找到某个版本的代码就很方便。\n\n```bash\n# 步骤一：建立一个发布版本 v1.1.0 建立后分支名为 release/v1.1.0\ngit flow release start v1.1.0\n\n# 步骤二：推送到远程, 可选\ngit flow release publish v1.1.0\n\n# 最后：将当前分支合并到 main 和 develop，打上一个标签，接着删除当前分支并回到 develop 分支上\ngit flow release finish v1.1.0\n```\n\n参考：\n\n- [https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow](https://www.atlassian.com/git/tutorials/comparing-workflows/gitflow-workflow)\n- [https://www.git-tower.com/learn/git/ebook/cn/command-line/advanced-topics/git-flow](https://www.git-tower.com/learn/git/ebook/cn/command-line/advanced-topics/git-flow)\n\n#### Git flow schema\n\n![](media/git-flow.png)\n\n---\n\n## git submodule 子模块\n\n`git submodule` 子模块的作用类似于包管理，类似 `npm`, 主要是复用仓库, 但比包管理使用起来更方便。\n\n子模块可以不建立版本分支管理代码, 因为它是依赖主应用，所以建立版本分支可以从主应用去操作，那么一旦建立新的版本分支当前的所有内容都会被锁定在这个分支上，不管子模块仓库怎么修改。\n\n#### 添加子模块\n\n添加完子模块后会发现根目录下多了个 `.gitmodules` 元数据文件，主要是用于管理子模块。\n\n```bash\ngit submodule add https://github.com/xjh22222228/git-manual.git # 默认添加到当前目录下\ngit submodule add https://github.com/xjh22222228/git-manual.git submodules/git-manual  # 添加到指定目录\n\n# -b 指定需要添加仓库的某个分支\ngit submodule add -b develop https://github.com/xjh22222228/git-manual.git\n```\n\n#### 删除子模块\n\n```bash\n# 1、直接删除子模块目录\nrm -rf submodule\n\n# 2、编辑目录下的 .gitmodules 文件把需要删除的子模块删除掉\n\n# 最后直接推送\ngit add -A\ngit commit -m \"删除子模块\"\ngit push\n```\n\n#### 克隆一个包含子模块的仓库\n\n```bash\n# --recursive 用于递归克隆，否则子模块目录是空的\ngit clone --recursive https://github.com/xjh22222228/git-manual.git\n\n# 如果已经克隆了一个包含子模块的项目，但忘记了 --recursive， 可以使用此命令 初始化、抓取并检出任何嵌套的子模块\ngit submodule update --init --recursive\n```\n\n#### 修复子模块分支\n\n当把一个包含子模块的仓库克隆下来后会发现子模块分支不对，可以使用下面命令纠正：\n\n```bash\ngit submodule foreach -q --recursive 'git checkout $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo main)'\n```\n\n#### 更新子模块代码\n\n方法一：通常我们需要更新代码只需要执行 `git pull`, 这是比较笨的办法。\n\n```bash\n# 递归抓取子模块的所有更改，但不会更新子模块内容\ngit pull\n\n# 这个时候需要进入子模块目录进行更新, 这样就完成了一个子模块更新，但是如果有很多子模块就比较麻烦了\ncd git-manual \u0026\u0026 git pull\n```\n\n方法二：使用 `git submodule update` 更新子模块\n\n```bash\n# git 会尝试更新所有子模块, 如果只需要更新某个子模块只要在 --remote 后指定子模块名称\ngit submodule update --remote\n\n# --recursive 会递归所有子模块, 包括子模块里的子模块\ngit submodule update --init --recursive\n```\n\n方法三：使用 `git pull` 更新, 这是一种新的更新模式，需要 \u003e= 2.14\n\n```bash\ngit pull --recurse-submodules\n```\n\n如果嫌麻烦每次 git pull 都需要手动添加 `--recurse-submodules`，可以配置 git pull 的默认行为， 如何配置请参考 [配置](#配置)\n\n具体使用还可以看这里 [git submodule 子模块使用教程](https://www.xiejiahe.com/blog/detail/5dbceefc0bb52b1c88c30853)\n\n## git subtree 子树\n\n如果你知道 `git submodule` 那就大概知道 `git subtree` 干嘛用了， 基本上是做同一件事，复用仓库或复用代码。\n\n官方建议使用 `git subtree` 代替 `git submodule`。\n\n`git subtree` 优势：\n\n- 不会像子模块需要 `.gitmodules` 元数据文件管理\n- 子仓库会当做普通目录, 其实是没有仓库概念的\n- 支持较旧的 Git 版本（甚至比 v1.5.2 还要旧）。\n- 简单工作流程的管理很容易。\n\n`git subtree` 劣势：\n\n- 命令过于复杂, 推送拉取都很麻烦\n- 虽然用于替代子模块, 但使用率并没有子模块广泛\n- 子仓库和主仓库混合在一起, 历史记录相当于有 2 个仓库的记录\n\n`git subtree` 命令用法:\n\n```bash\ngit subtree add   --prefix=\u003cprefix\u003e \u003ccommit\u003e\ngit subtree add   --prefix=\u003cprefix\u003e \u003crepository\u003e \u003cref\u003e\ngit subtree pull  --prefix=\u003cprefix\u003e \u003crepository\u003e \u003cref\u003e\ngit subtree push  --prefix=\u003cprefix\u003e \u003crepository\u003e \u003cref\u003e\ngit subtree merge --prefix=\u003cprefix\u003e \u003ccommit\u003e\ngit subtree split --prefix=\u003cprefix\u003e [OPTIONS] [\u003ccommit\u003e]\n```\n\n在操作 `git subtree` 时当前工作区必须清空，否则无法执行。\n\n#### 添加子仓库\n\n- `--prefix` 指定将子仓库存储位置\n- `main` 是分支名称\n- `--squash` 通常做法是不将子仓库整个历史记录存储在主仓库中，如果需要的话可以忽略整个参数\n\n添加子仓库后, 会跟普通文件一样看待，可以进入 sub/common 目录执行 `git remote -v` 会发现没有仓库。\n\n```bash\ngit subtree add --prefix=sub/common https://github.com/xjh22222228/git-manual.git main --squash\n```\n\n#### 更新子仓库\n\n当远程子仓库有内容变更时，可以通过下面命令进行更新：\n\n```bash\ngit subtree pull --prefix=sub/common https://github.com/xjh22222228/git-manual.git main --squash\n```\n\n#### 推送到子仓库\n\n假如修改了子仓库里的内容，可以将修改这部分的内容推送到子仓库中\n\n```bash\n# 需要先在主仓库把子仓库的代码暂存\ngit add sub/common\ngit commit -m \"子仓库修改\"\n# 然后推送\ngit subtree push --prefix=sub/common https://github.com/xjh22222228/git-manual.git main --squash\n```\n\n#### 切割\n\n随着项目的迭代, 主仓库会提交过多, 会发现每次 `push` 时会非常慢，尤其在 `windows` 平台较为明显。\n\n每次 `push` 到子仓库里头时会花费大量的时间来重新计算子仓库的提交。并且因为每次 `push` 都是重新计算的，所以本地仓库和远端仓库的提交总是不一样的，这会导致 git 无法解决可能的冲突。\n\n当使用 `git split` 命令后，使用 `git subtree push`，git 只会计算 split 后的新提交。\n\n```bash\ngit subtree split --prefix=sub/common --branch=main\n```\n\n#### 简化命令\n\n通过以上实操，不难发现，`git subtree` 太长了，每次操作都要敲这么长的命令，谁能忍得住。\n\n将子仓库添加为远程仓库：\n\n```bash\n# common 是仓库名字，可以随意定义\ngit remote add -f common https://github.com/xjh22222228/git-manual.git\n```\n\n要做其他 `git subtree` 命令时就不需要敲仓库地址了：\n\n```bash\ngit subtree push --prefix=sub/common common main --squash\n```\n\n虽然省去了仓库地址，命令还是太长。\n\n还有另一种解决方案，就是使用别名，例如在 `mac` 或 `linux` 中使用 [`alias`](https://github.com/xjh22222228/linux-manual#alias) 命令:\n\n```bash\nalias push=\"git subtree push --prefix=sub/common https://github.com/xjh22222228/git-manual.git main --squash\"\n```\n\n也可以使用 git 自带的别名命令 =\u003e [命令别名配置](#命令别名配置)\n\n如果你写前端，可以在 `package.json` 文件中加入：\n\n```json\n{\n  \"scripts\": {\n    \"push\": \"git subtree push --prefix=sub/common https://github.com/xjh22222228/git-manual.git main --squash\"\n  }\n}\n```\n\n下次需要推送时执行:\n\n```bash\nnpm run push 或者 yarn push\n```\n\n## git bisect 二分查找\n\n`git bisect` 基于二分查找算法, 用于定位引入 Bug 的 commit，主要 4 个命令。\n\n此命令非常实用, 如果你的 Bug 不知道是哪个 commit 引起的，可以尝试此方法。\n\n```bash\n# 开始\ngit bisect start [终点] [起点] # 通过 git log 确定起点和终点\ngit bisect start HEAD 4d83cf\n\n# 记录这次的commit是好的\ngit bisect good\n\n# 记录这次的commit是坏的\ngit bisect bad\n\n# 退出\ngit bisect reset\n```\n\n参考 [https://github.com/bradleyboy/bisectercise](https://github.com/bradleyboy/bisectercise)\n\n## git archive 归档\n\n创建一个归档文件，可以理解为将当前项目压缩为一个文件。会忽略掉 `.git` 目录。\n\n但与 `zip` / `tar` 等压缩不同，`git archive` 支持将某个分支或 commit 进行归档。\n\n**参数**\n\n| 参数     | 描述                                                                                |\n| -------- | ----------------------------------------------------------------------------------- |\n| --format | 可选，指定格式，默认 tar, 支持 tar 和 zip，如果不填会根据 --output 后缀格式进行推断 |\n| --output | 输出到指定目录                                                                      |\n\n```bash\n# 归档 main 分支 并打包在当前目录下 output.tar.gz\ngit archive --output \"./output.tar.gz\" main\n\n# 归档指定commit\ngit archive --output \"./output.tar.gz\" d485a8ba9d2bcb5\n\n# 归档为 zip, 无需指定 --format， 因为会根据文件后缀进行推断\ngit archive --output \"./output.zip\" main\n\n# 归档一个或多个目录, 而不是归档整个项目\ngit archive --output \"./output.zip\" main src tests\n```\n\n## 清空 commit 历史\n\n清空 `commit` 有 2 种方法。\n\n1、第一种方法原理是通过新建新的分支，假设要清空 commit 分支是 `develop`\n\n```bash\n# 1、新建一个新分支\ngit checkout --orphan new_branch\n# 2、暂存所有文件并提交\ngit add -A \u0026\u0026 git commit -m \"First commit\"\n# 3、删除本地 develop 分支\ngit branch -D develop\n# 4、再将 new_branch 分支重命名为 develop\ngit branch -m develop\n# 5、强制将 develop 分支推送到远程\ngit push -f origin develop\n```\n\n2、第二种方法通过更新 `引用`, 假设要重设 `main` 分支\n\n```bash\n# 通过 git log 找到第一个 commit_id\ngit update-ref refs/heads/main 9c3a31e68aa63641c7377f549edc01095a44c079\n\n# 接着可以提交\ngit add .\ngit commit -m \"第一个提交\"\ngit push -f # 注意一定要强制推送\n```\n\n## 帮助\n\n```bash\n# 详细打印所有git命令\ngit help\n\n# 打印所有git命令, 此命令不会有详细信息，更清晰一些\ngit help -a\n\n# 列出所有可配置的变量\ngit help -c\n```\n\n## 提交规范\n\n| 标志     | 描述                     |\n| -------- | ------------------------ |\n| feat     | 该提交含有新的特性       |\n| style    | 通常是代码格式的修改     |\n| chore    | 构建过程或辅助工具的变动 |\n| fix      | 修复 Bug                 |\n| docs     | 文档修改                 |\n| test     | 单元测试改动             |\n| refactor | 代码重构                 |\n| perf     | 性能优化、体验           |\n| revert   | 回滚版本                 |\n| merge    | 代码合并                 |\n| typo     | 错字, 比如单词拼错       |\n\n**例子：**\n\n```bash\n# 含有新特性\ngit commit -m \"feat: 新增xx功能\"\n\n# 代码格式化\ngit commit -m \"style: 规范Eslint\"\n\n# 修改Jenkins构建流程\ngit commit -m \"chore: Update Jenkins\"\n\n# 修复Bug, 建议描述清晰, 日后方便查找, #688 是修复某个id的编号\ngit commit -m \"fix(登录闪烁): #688\"\n\n# 修改文档\ngit commit -m \"docs: git pull\"\n\n# 单元测试改动\ngit commit -m \"test: 测试登录\"\n\n# 项目代码重构\ngit commit -m \"refactor: 流程模块重构\"\n```\n\n## 解决冲突\n\n**代码合并/更新代码** 经常会遇到冲突的情况。\n\n#### 解决冲突的流程如下：\n\n1. 执行 `git pull` 把代码拉下来，git 会自动尝试合并\n2. 编辑冲突文件, 根据实际情况保留本地代码还是远端代码\n3. 暂存文件并推送到远端\n\n\u003cdetails\u003e\n  \u003csummary\u003e点击查看解决冲突.gif\u003c/summary\u003e\n  \n  \u003cimg src=\"media/git-merge-conflict.gif\"\u003e\n\u003c/details\u003e\n\n面向 GUI 的用户，推荐 3 个工具专门处理 git 冲突：\n\n- [meld](http://meld.sourceforge.net/install.html)\n- [kdiff3](http://kdiff3.sourceforge.net/)\n- 在冲突时执行 `git mergetool` 命令会启动一个默认 GUI\n\n[这篇文章专门介绍这 2 个工具如何使用](https://gitguys.com/topics/merging-with-a-gui/)\n\n## 仓库迁移\n\n仓库迁移也可以叫复制仓库。\n\n有时候需要从一个旧仓库迁移到新仓库，如果手动只能把文件进行迁移，但是如果需要把分支、标签、历史记录一起迁移就需要复制仓库。\n\n旧仓库 A: https://github.com/xjh22222228/A.git\n新仓库 B: https://github.com/xjh22222228/B.git\n\n1、克隆旧裸仓库\n\n```bash\n# 克隆裸仓库，里面没有工作区内容\ngit clone --bare https://github.com/xjh22222228/A.git\n```\n\n2、镜像推送至新仓库\n\n```bash\ncd A\ngit push --mirror https://github.com/xjh22222228/B.git\n```\n\n3、删除刚刚克隆的旧仓库\n\n```bash\nrm -rf A\n```\n\n4、拉取新仓库\n\n```bash\ngit clone https://github.com/xjh22222228/B.git\n```\n\n除了通过命令迁移之外，可以通过网页导入仓库的方式也可以。\n\n## 奇技淫巧\n\n**美化 `git log`, 直逼 GUI**\n\n```bash\n# 1、全局配置\ngit config --global alias.lg \"log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)\u003c%an\u003e%Creset' --abbrev-commit\"\n# 2、输入下面命令, 日志变得非常直观化\ngit lg\n\n# 这里另外提供几种模式, 可以选择喜欢的一种进行别名配置\ngit config --global alias.lg \"log --graph --pretty=format:'%Cred%h - %Cgreen[%an]%Creset -%C(yellow)%d%Creset %s %C(yellow)\u003c%cr\u003e%Creset' --abbrev-commit --date=relative\"\n\ngit config --global alias.his \"log --graph --decorate --oneline --pretty=format:'%Creset %s %C(magenta)in %Cred%h %C(magenta)commited by %Cgreen%cn %C(magenta)on %C(yellow) %cd %C(magenta)from %Creset %C(yellow)%d' --abbrev-commit --date=format:'%Y-%m-%d %H:%M:%S'\"\n\ngit config --global alias.hist \"log --graph --decorate --oneline --pretty=format:'%Cred%h - %C(bold white) %s %Creset %C(yellow)%d  %C(cyan) \u003c%cd\u003e %Creset %Cgreen(%cn)' --abbrev-commit --date=format:'%Y-%m-%d %H:%M:%S'\"\n```\n\n\u003cdetails\u003e\n  \u003csummary\u003e效果图.png\u003c/summary\u003e\n  \n  \u003cimg src=\"media/git-log.png\"\u003e\n\u003c/details\u003e\n\n## GUI 客户端\n\n推荐几款比较好用的 git 图形界面工具, 不分先后。\n\n- 免费 - [Github Desktop](https://desktop.github.com/)\n- 免费 - [Sourcetree](https://www.sourcetreeapp.com/)\n- 免费 - [tortoiseGit](https://tortoisegit.org/)\n- 免费 - [gitkraken](https://www.gitkraken.com/)\n- 免费 - [gitup](https://gitup.co/)\n- 免费 - [magit](https://github.com/magit/magit)\n- 收费 - [smartgit](https://www.syntevo.com/smartgit/)\n- 收费 - [git-fork](https://git-fork.com/)\n- 收费 - [tower](https://www.git-tower.com/)\n- 收费 - [lazygit](https://github.com/jesseduffield/lazygit)\n\n## 使用 SSH 克隆仓库\n\n使用 SSH 克隆仓库需要先在电脑生成 SSH 公钥和密钥，以下是生成步骤：\n\n1. 进入到 ssh\n\n```bash\ncd ~/.ssh\n```\n\n2. 替换为您的 GitHub 电子邮件地址\n\n```bash\n# -t ed25519: 使用 Ed25519 算法（更现代且安全）。\n# -C: 添加注释，通常是你的邮箱，与GitHub邮箱没有关系\nssh-keygen -t ed25519 -C \"your_email@example.com\"\n\n# 按提示操作，可以直接回车，默认会生成 ~/.ssh/id_ed25519，你可以修改名称作为管理\n```\n\n3. 查看公钥并添加到 GitHub 账号\n\n[https://github.com/settings/keys](https://github.com/settings/keys)\n\n```bash\n# 输出类似于 ssh-ed25519 AAAAC3Nza... your_email@example.com，复制整个内容。\ncat ~/.ssh/id_ed25519.pub\n```\n\n4. 添加 SSH 密钥\n\n```bash\nssh-add ~/.ssh/id_ed25519\n```\n\n5. 测试连接\n\n```bash\n# 输出以下信息说明 ssh 连接成功\n# Hi xxxxx! You've successfully authenticated, but GitHub does not provide shell access.\nssh -T git@github.com\n```\n\n#### 管理多个 GitHub 账号\n\n如果你有多个 GitHub 账号，需要额外的配置 `ssh config`\n\n修改 `~/.ssh/config`\n\n```bash\nvim ~/.ssh/config\n```\n\n```bash\n# Host 是自定义名称，通常用 GitHub 账号命名\n# 账号1\nHost user1\n  HostName github.com\n  User git\n  # 修改你的密钥文件路径\n  IdentityFile ~/.ssh/id_ed25519\n\n# 账号2\nHost user2\n  HostName github.com\n  User git\n  IdentityFile ~/.ssh/id_ed25519_2\n```\n\n使用自定义名称测试连接\n\n```bash\nssh -T git@user1\n```\n\n克隆仓库\n\n```bash\n#             Host:用户/仓库\ngit clone git@user1:admin/demo.git\n```\n\n## 其他\n\n```bash\n# 查看git版本\ngit --version\n\n# 清除本地git缓存\ngit rm -r --cached .\n\n# 列出没有被 .gitignore 忽略的文件列表\ngit ls-files\n```\n\n## 记住密码\n\n使用 https 方式会要求每次都需要输入账号和密码，如果想下次不弹出账号密码可以按以下方式：\n\n```bash\n# 临时记住密码，默认15分钟\ngit config --global credential.helper cache\n\n# 自定义记住密码时间，单位秒\ngit config credential.helper 'cache --timeout=3600'\n\n# 长期记住密码\ngit config --global credential.helper store\n```\n\n## 清除账号\n\n清除 git 已保存的用户名和密码\n\n```bash\n# windows\ngit credential-manager uninstall\n\n# mac / linux (以下任意一条命令都可)\ngit config --global credential.helper \"\"\ngit config --global --unset credential.helper\n```\n\n## 加速\n\n在国内克隆或下载版本会很慢，可以借助下面镜像站点进行加速。\n克隆：\n\n```bash\n# 公有仓库\ngit clone https://ghproxy.com/https://github.com/xjh22222228/git-manual.git\n\n# 私有仓库, 需要配合Token使用 =\u003e https://github.com/settings/tokens\ngit clone https://user:your_token@ghproxy.com/https://github.com/your_name/your_private_repo\n```\n\n资源加速：\n\n```bash\nhttps://raw.githubusercontent.com/xjh22222228/git-manual/main/media/poster.png\n# ↓ 替换为\nhttps://cdn.jsdelivr.net/gh/xjh22222228/git-manual@main/media/poster.png\n\n# 备用域名，只需要替换域名\n# testingcf.jsdelivr.net\n# img.jsdmirror.com\n# gcore.jsdelivr.net\n```\n\n###### Github 文件/GIST/RAW 加速：\n\n使用方法打开 [https://www.7ed.net/](https://www.7ed.net/)\n\n## 思维导图\n\n![](media/map.jpg)\n\n[⬆ 回顶部](#)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxjh22222228%2Fgit-manual","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxjh22222228%2Fgit-manual","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxjh22222228%2Fgit-manual/lists"}