技术知识文章集合TECHNICAL ARCHIVE · 457 DOCUMENTS

显示模式

登录
ARCHIVE DOCUMENTGIT

多年 Git 使用心得 & 常见问题整理

所属馆藏
Git
文件格式
Markdown
原始路径
git/04-多年 Git 使用心得 & 常见问题整理
本文目录10 个章节
  1. Git 流程图
  2. 配置 Git
  3. 查看 Git 信息
  4. Git 常用命令
  5. Git 常用命令详解
  6. 新建一个 Git 项目的两种方式
  7. Git 分支管理规范
  8. Git 钩子
  9. 常见问题
  10. 官方文档与当前规则

多年 Git 使用心得 & 常见问题整理

Category(分类): git Status: 已整理

更新说明(2026)

本文尽量保留早期 Git 使用经验和旧命令,同时补充当前 Git 的推荐写法。旧版 checkoutresetstash save 等命令仍然有效,但涉及恢复文件、切换分支时,优先使用 git restoregit switch。示例中的 master 也可以替换为当前项目实际使用的 main 或其他默认分支。

本文根据 Git 官方文档及 GitHub 当前认证规则整理。涉及 reset --hard、强制推送、删除文件、明文凭据的命令,执行前请确认目标仓库和工作区状态。

Git 流程图

Git 工作区、暂存区、本地仓库和远程仓库流程图

  • Workspace:工作区
  • Index / Stage:暂存区
  • Repository:仓库区(或本地仓库)
  • Remote:远程仓库

配置 Git


# 配置全局用户
$ git config --global user.name "用户名"
$ git config --global user.email "your-email@example.com"
# 设置新建仓库的默认分支名(Git 2.28 及以上)
$ git config --global init.defaultBranch main
# 配置别名
$ git config --global alias.co checkout
$ git config --global alias.ss status
$ git config --global alias.cm commit
$ git config --global alias.br branch
$ git config --global alias.rg reflog
# 这里只是美化 log 的输出,实际使用时可以在 git lg 后面加命令参数,如: git lg -10 显示最近10条提交
$ git config --global alias.lg "log --color --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit"
# 删除全局配置
$ git config --global --unset alias.xxx
$ git config --global --unset user.xxx

查看 Git 信息


# 查看当前生效的全部配置,并显示配置来源
$ git config --list --show-origin
# 查看系统级配置
$ git config --system --list --show-origin
# 查看全局用户配置
$ git config --global --list --show-origin
$ cat ~/.gitconfig
# 查看当前项目的 Git 配置
$ git config --local --list --show-origin
$ cat .git/config
# 查看暂存区中已经被 Git 追踪的文件
$ git ls-files
# 查看当前 HEAD 的 reflog
$ git reflog
# 查看所有 Git 命令
$ git --help -a
# 查看当前 HEAD 指向
$ cat .git/HEAD

# git 中 D 向下翻一行,F 向下翻页,B 向上翻页,Q 退出
# 查看提交历史;以下选项可以组合使用
$ git log --oneline --graph --all
$ git log --grep="关键字"
$ git log --author="username"
$ git log --reverse
$ git log -10
$ git log -p
$ git log --before="1 week ago"
$ git log --after="2019-06-06"
$ git log --stat
$ git log --abbrev-commit
$ git log --pretty=format:"xxx"

# oneline -> 将日志记录一行一行地显示
# grep -> 查找提交说明中与关键字有关的记录
# graph -> 以图形化方式显示分支关系
# all -> 显示所有 refs(分支、标签等)可达的提交,不是“所有命令记录”
# author -> 查找指定作者提交的记录
# reverse -> 将提交记录顺序翻转
# before/after -> 查找指定时间之前/之后的记录,时间表达式需要加引号
# -10 -> 显示最近 10 次提交,也可以写成 -n 10
# stat -> 显示每次提交的文件修改统计信息
# abbrev-commit -> 仅显示 SHA-1 的缩写,而非完整对象名
# pretty=format -> 自定义提交记录格式
# p -> 显示每次提交引入的差异(补丁)

git reflog

  • 默认的 git reflog 显示 HEAD 的引用更新记录,例如切换分支、提交、reset、rebase 等;它不是所有 Git 命令的操作日志。分支也有自己的 reflog,可用 git reflog <branch> 查看。
  • 工作区中从未提交的修改通常不会进入 HEAD reflog;但 git stash 会创建 refs/stash,可以用 git reflog stash 查看 stash 的引用变化。
  • reflog 不会永久保留,Git 会定期清理过期的 reflog 和不可达对象,不要把它当作长期备份。

git log 点线图

  • Git 中一条分支就是一个指向提交的可移动指针,新建分支通常只是创建一个新的指针
  • 在正常的附着状态下,HEAD 是指向当前分支的符号引用,当前分支再指向某个提交
  • 切换至某个 commit 时,HEAD 会直接指向该提交,进入 detached HEAD(游离 HEAD)状态

符号解释:


*   表示一个 commit
|   表示分支前进
/   表示分叉
\   表示合入
|/  表示新分支

Git 常用命令


# 查看工作区和暂存区的状态
$ git status
# 将工作区的文件提交到暂存区
$ git add .
# 提交到本地仓库
$ git commit -m "本次提交说明"
# add和commit的合并,便捷写法(未追踪的文件无法直接提交到暂存区/本地仓库)
$ git commit -am "本次提交说明"
# 将本地分支和远程分支进行关联
$ git push -u origin branchName
# 将本地仓库的文件推送到远程分支
$ git push
# 拉取远程分支的代码
$ git pull origin branchName
# 合并分支
$ git merge branchName
# 查看本地拥有哪些分支
$ git branch
# 查看所有分支(包括远程分支和本地分支)
$ git branch -a
# 切换分支(旧写法;当前也可使用 git switch)
$ git checkout branchName
$ git switch branchName
# 临时将工作区文件的修改保存至堆栈中
$ git stash
# 将之前保存至堆栈中的文件取出来
$ git stash pop

当前 Git 更推荐的分支和文件恢复命令

# 创建并切换到新分支
$ git switch -c feature/name
# 从远程跟踪分支创建本地分支
$ git switch -c feature/name --track origin/feature/name
# 切换到某个提交进行查看,不修改任何分支指针
$ git switch --detach <commit>

# 丢弃工作区中某个文件的未暂存修改
$ git restore -- <file>
# 取消暂存,但保留工作区修改
$ git restore --staged -- <file>
# 将文件的暂存区和工作区都恢复到 HEAD
$ git restore --source=HEAD --staged --worktree -- <file>

checkout 仍然可以使用,但它同时承担“切换分支”和“恢复文件”两类职责,容易误操作;新项目中可以优先使用职责更清晰的 switchrestore

Git 常用命令详解

add

将工作区的文件添加到暂存区

# 添加指定文件到暂存区(也可以追踪新增文件)
$ git add [file1] [file2] ...
# 添加指定目录到暂存区,包括子目录
$ git add [dir]
# 添加当前目录及子目录中的变更
$ git add .
# 删除工作区/暂存区的文件
$ git rm [file1] [file2] ...
# 停止追踪指定文件,但保留工作区文件
$ git rm --cached -- [file]
# 改名工作区/暂存区的文件
$ git mv [file-original] [file-renamed]

# 历史行为(Git 2.0 以前)
# 只作用于当前路径下文件的新增和修改
$ git add .
# 只作用于已追踪文件的修改和删除
$ git add -u
# 作用于文件的增、删、改
$ git add -A

# Git 2.0 及以上:git add <pathspec> 会记录该路径下的删除。
# 但从子目录执行 git add . 时,范围仍然是当前目录及子目录,不能简单认为等价于 git add -A。
  • git add .:操作对象是“当前路径”及其子目录,会把该范围内的新增、修改和删除加入暂存区;从仓库根目录执行时通常覆盖整个工作区。
  • git add -u:只更新已经被 Git 追踪的文件,包括修改和删除,不会添加未追踪文件;不带路径参数时,当前 Git 会更新整个工作区的已追踪文件。
  • git add -A:添加整个工作区的新增、修改和删除;如果带路径参数,则只作用于指定路径。
  • git add -p:按代码块交互式选择要暂存的修改,适合拆分提交。

status


# 查看工作区和暂存区的状态
$ git status

commit


# 将暂存区的文件提交到本地仓库并添加提交说明
$ git commit -m "本次提交的说明"

# add 和 commit 的合并,便捷写法
# 和 git add -u 命令一样,未跟踪的文件是无法提交上去的
$ git commit -am "本次提交的说明"

# 跳过验证继续提交
$ git commit --no-verify
$ git commit -n

# 编辑器会弹出上一次提交的信息,可以在这里修改提交信息
$ git commit --amend
# 修复提交,同时修改提交信息
$ git commit --amend -m "本次提交的说明"
# 加入 --no-edit 标记会修复提交但不修改提交信息,编辑器不会弹出上一次提交的信息
$ git commit --amend --no-edit

  • git commit --amend 既可以修改上次提交的文件内容,也可以修改上次提交的说明。它会创建一个新的 commit 来替换最近一次 commit;如果暂存区有内容,新的 commit 会把这些内容和上一个 commit 的内容合并。如果暂存区没有内容,则主要是重写提交说明。已推送到公共仓库的提交不要随意 amend;普通 push 通常会被拒绝,强制推送又可能覆盖他人的提交,公共分支通常应使用 git revert

push & pull

  • 分支推送顺序的写法是 <来源地>:<目的地>
# 将本地分支推送到远程分支
# 如果省略远程分支名,则表示本地和远程同名
$ git push <远程主机> <本地分支>:<远程分支>
$ git push origin branchname

# 删除远程分支(等价写法)
$ git push origin :master
$ git push origin --delete master

# 建立当前分支和远程分支的追踪关系
$ git push -u origin master
# 当前分支已有 upstream 时,可以省略远程和分支名
$ git push

# 不管是否存在对应的远程分支,将本地所有分支推送到远程主机
$ git push --all origin

# 获取 origin 的远程跟踪信息;不会把所有远程分支合并到当前分支
$ git fetch origin
# 清理远程已经删除的远程跟踪分支
$ git fetch --prune origin
# 拉取并整合当前分支的 upstream
$ git pull origin branchname
# 当 pull.rebase=false 时,大致相当于 fetch + merge;配置为 rebase 时会执行 rebase
$ git pull --no-rebase origin branchname
$ git fetch origin branchName
$ git merge origin/branchName

# 远程分支比本地更新时,优先先 fetch,再检查差异并合并或 rebase。
# 只有明确要改写远程历史时才考虑强制推送;优先使用更安全的 force-with-lease。
$ git push --force-with-lease origin branchname
# --force 可能覆盖其他人的提交,只在确认无人基于旧历史开发时使用
$ git push --force origin branchname

git pull 的具体整合方式受 pull.rebasebranch.<name>.rebase 等配置影响。团队可以明确选择 git pull --ff-only(只允许快进)或 git pull --rebase,避免每个人的默认配置不同。

branch

# 查看本地分支
$ git branch | git branch -l
# 查看远程跟踪分支(不是远程服务器的实时查询结果)
$ git branch -r
# 查看所有分支(本地分支 + 远程跟踪分支)
$ git branch -a
# 查看所有分支并带上最新提交信息
$ git branch -av
# 查看本地分支对应的远程跟踪分支
$ git branch -vv

# 新建分支;分支只是指向当前提交的新指针
$ git branch branchname
# 切换分支(旧写法)
$ git checkout branchname
# 当前推荐写法
$ git switch branchname
# 创建并切换到 aaa 分支
$ git checkout -b aaa
$ git switch -c aaa
# 基于 master 创建并切换到 aaa
$ git checkout -b aaa master
$ git switch -c aaa master

# 新建一条没有历史的分支(详情请看问题列表)
$ git checkout --orphan emptyBranchName
# 当前推荐写法
$ git switch --orphan emptyBranchName

# 删除本地分支,会阻止删除包含未合并更改的分支
$ git branch -d branchname
# 强制删除本地分支;确认不需要其中的提交后再使用
$ git branch -D branchname
# 删除远程分支
$ git push origin :远程分支名
$ git push origin --delete 远程分支名

# 修改当前分支名
$ git branch -m branchname
# 将当前分支改名为 main
$ git branch -M main

merge 三种常用合并方法

# 默认允许 fast-forward;<branchname> 是要合并的目标分支
$ git merge <branchname>

# 禁止快进式合并,保留一个合并提交
$ git merge --no-ff <branchname>

# 只应用合并结果到工作区和暂存区,不自动创建提交
$ git merge --squash <branchname>
# 只允许快进,不能快进时直接失败
$ git merge --ff-only <branchname>

Git merge 合并方式示意图

  • fast-forward:当当前分支没有产生分叉时,Git 只移动当前分支指针,不创建新的合并提交;被合并分支的提交会成为当前分支历史的一部分。并不是每次 merge 都会发生快速合并。
  • --no-ff:即使可以快进,也会生成一个新的合并提交,便于保留一次 feature/PR 合并边界;如果目标已经被包含或发生特殊情况,也不一定会凭空生成提交。
  • --squash:不会生成合并提交,也不会记录 MERGE_HEAD,只把合并结果放入工作区和暂存区,由开发者手动提交。最终是否只有一条提交,取决于后续操作。

rebase

www.liaoxuefeng.com/wiki/896043…

git-scm.com/book/zh/v2/…

www.jianshu.com/p/4a8f4af4e…

juejin.im/post/684490…

rebase 会把当前分支的提交重新应用到新的基线之上,通常会改写这些提交的 SHA。它可以让个人分支历史更直线,但不应随意改写已经被多人使用的公共分支。

# 更新远程跟踪分支
$ git fetch origin
# 将当前 feature 分支变基到远程 main
$ git switch feature/name
$ git rebase origin/main
# 发生冲突后:解决文件、暂存解决结果,再继续
$ git add <file>
$ git rebase --continue
# 放弃本次变基并恢复到开始前的状态
$ git rebase --abort
# 交互式整理最近 N 个提交
$ git rebase -i HEAD~N

merge 保留分叉和合并提交,适合公共协作历史;rebase 重写当前分支提交,适合在提交到公共分支前整理个人分支。选择哪一种应以团队规范为准。

stash

  • 默认会保存已追踪文件的工作区修改和暂存区状态,用于后续恢复当前工作区内容。
  • 默认不会保存未追踪文件和被忽略的文件;未追踪文件使用 git stash -u,连被忽略文件也需要保存时使用 git stash -a
  • 如果没有任何可保存的已追踪修改,才会提示 No local changes to save;未暂存的已追踪修改不需要先执行 git add

使用场景: 当你接到一个修复紧急 bug 的任务时,一般可以先创建一个新的 bug 分支来修复它,然后合并,最后删除。但是,如果当前正在开发功能,短时间还无法完成、又不想直接提交到仓库,可以先把当前工作区的内容 git stash 一下,去修复 bug,修复后再 git stash pop 恢复之前的工作内容。

# 保存已追踪文件的未提交修改
$ git stash
# 当前推荐的带说明写法
$ git stash push -m "存储"
# 旧写法,仍可使用
$ git stash save "存储"
# 同时保存未追踪文件
$ git stash -u
# 同时保存未追踪和被忽略的文件
$ git stash -a

# 查看存储记录
$ git stash list
# 恢复但不删除 stash 记录
$ git stash apply "stash@{index}"
# 恢复成功后删除 stash 记录;发生冲突时通常会保留记录
$ git stash pop "stash@{index}"
# 删除一个 stash 记录
$ git stash drop "stash@{index}"
# 删除所有 stash(不可逆操作,执行前请确认)
$ git stash clear
# 查看当前记录中修改了哪些文件
$ git stash show "stash@{index}"
# 查看当前记录中修改了哪些文件的内容
$ git stash show -p "stash@{index}"
# 从 stash 创建并切换到新分支,适合处理冲突或长期保存
$ git stash branch bug-fix "stash@{index}"

在 PowerShell 中,stash@{index} 可能被解析为哈希表表达式,建议使用引号;在其他 shell 中加引号同样更稳妥。

diff

# 查看工作区和暂存区单个文件的对比
$ git diff -- filename
# 查看工作区和暂存区所有已追踪文件的对比
$ git diff
# 查看差异统计
$ git diff --stat
# 查看暂存区与 HEAD 的对比(二选一,不能写成 --cached/--staged)
$ git diff --cached
$ git diff --staged
# 查看工作区与某个提交的对比
$ git diff branchname
# 查看工作区与 HEAD 的对比
$ git diff HEAD
# 查看暂存区与 HEAD 的文件名列表
$ git diff --cached --name-only
# 检查空白字符错误
$ git diff --check

# 新增文件未被追踪时,普通 git diff 默认没有输出;先 git add 后可用 git diff --cached 查看。
# 查看两个本地分支中某个文件的对比
$ git diff branchA..branchB -- filename
# 查看两个本地分支的对比
$ git diff branchA..branchB
# 查看远程跟踪分支和本地分支的对比
$ git diff origin/branchname..branchname
# 查看两个远程跟踪分支的对比
$ git diff origin/branchA..origin/branchB
# 查看两个 commit 的对比
$ git diff commit1..commit2

remote


# 查看所有远程主机
$ git remote
# 查看关联的远程仓库的详细信息
$ git remote -v
# 删除远程仓库关联;projectname 是 remote 名称,例如 origin
$ git remote remove projectname
# 旧写法仍然有效
$ git remote rm projectname
# 设置远程仓库 URL
$ git remote set-url origin <newurl>
# 查看远程仓库的详细配置
$ git remote show origin

tag

常用于发布版本

www.liaoxuefeng.com/wiki/896043…


# 默认在 HEAD 上创建一个标签
$ git tag v1.0
# 指定一个 commit id 创建一个标签
$ git tag v0.9 f52c633
# 创建带有说明的标签,用 -a 指定标签名,-m 指定说明文字
$ git tag -a v0.1 -m "version 0.1 released"

# 查看所有标签
# 默认通常按字典序;也可能受 tag.sort 配置影响
$ git tag
# 按版本号倒序查看(v2.10 会排在 v2.9 前面)
$ git tag --sort=-version:refname

# 查看单个标签具体信息
$ git show <tagname>

# 推送一个本地标签
$ git push origin <tagname>
# 推送全部未推送过的本地标签
$ git push origin --tags

# 删除本地标签
# 因为创建的标签都只存储在本地,不会自动推送到远程。
# 所以,打错的标签可以在本地安全删除。
$ git tag -d v0.1
# 删除一个远程标签(先删除本地 tag,然后删除远程 tag)
$ git push origin :refs/tags/<tagname>
# 当前更直观的写法
$ git push origin --delete <tagname>

删除文件

# 删除暂存区和工作区的文件
$ git rm -- filename
# 只从暂存区移除文件,保留工作区文件;提交并推送后,远程分支上的当前版本也会删除它
$ git rm --cached -- filename

如果在配置 .gitignore 之前就把某个文件提交到远程仓库,之后仅配置 .gitignore 不会自动停止追踪。可以使用 git rm --cached -- filename,然后提交并推送;这只会从后续提交和当前分支中移除文件,历史提交中的内容仍然存在。

如果文件包含密码、私钥或 token,不能只依赖 git rm --cached:应立即撤销/轮换凭据,再根据需要使用 git filter-repo 或托管平台提供的敏感数据清理方案重写历史,并通知协作者重新同步。

版本切换 & 重设 & 撤销

  • git restore 主要用于恢复工作区或暂存区文件;git reset 主要用于移动当前分支指针、重置暂存区,也可以通过 --hard 同时覆盖工作区。
  • git switch / git checkout 用于切换分支或查看提交;git revert 针对已提交的 commit 新增一个反向提交,保留原有历史。
  • 需要谨慎区分:resetrestore 可能丢弃本地修改,revert 更适合公共分支。

checkout 详解

# 从暂存区恢复指定文件到工作区(旧写法)
$ git checkout -- <filename>
# 当前推荐写法
$ git restore -- <filename>
# 恢复当前目录下所有工作区文件(会丢弃未暂存修改)
$ git checkout -- .
$ git restore .

# 将文件的暂存区和工作区恢复到最近一次提交
$ git checkout HEAD -- <filename>
$ git restore --source=HEAD --staged --worktree -- <filename>
# 注意:git checkout HEAD(不带 -- 和路径)不会按这里的描述清除文件修改。

# 切换到最近一次提交的上一个版本;这会进入 detached HEAD
$ git checkout HEAD^
$ git switch --detach HEAD^
# 切换到最近一次提交的上两个版本
$ git checkout HEAD^^
$ git switch --detach HEAD~2

# 切换分支(旧写法)
$ git checkout <当前你正在使用的分>
# 当前推荐写法
$ git switch <branchname>
# 切换到某个指定的 commit 或 tag,通常会进入 detached HEAD
$ git checkout <commit_id>
$ git switch --detach <commit_id>
$ git checkout <tag>
$ git switch --detach <tag>
  • 在开发的正常阶段,HEAD 通常通过符号引用指向当前分支;使用 git checkout <commit>git switch --detach <commit> 后,HEAD 会直接指向提交,进入 detached HEAD 状态。
  • detached HEAD 很适合查看历史代码、编译项目和运行测试。未提交的修改不会自动进入任何分支;如果切回其他分支,未冲突的工作区修改可能被带过去,也可能因为冲突而无法切换,不要假设 Git 一定会提示创建新分支。
  • 如果在 detached HEAD 状态下解决了 bug 并提交,应立即执行 git switch -c bug-fix 为该提交创建分支,再把它合并或提交到目标分支;否则这个提交可能只剩下 reflog 可找。
  • 一般可以用 checkout 查看历史代码,但新项目中更推荐 switchrestore,并先用 git status 确认工作区状态。

Git checkout 与提交历史示意图

detached HEAD 状态示意图

detached HEAD 提交恢复示意图

reset 详解

git reset [--soft|--mixed|--hard|--merge|--keep] [<commit>]:将当前分支重设到指定的 <commit>(省略时默认为 HEAD),并根据模式更新 HEAD、索引和工作目录。mixed 是默认模式,正确的模式名称是 --merge,不是 merged

# 从暂存区撤销特定文件,但不改变工作区(当前也可用 git restore --staged)
$ git reset <fileName>
$ git restore --staged -- <fileName>
# 重置暂存区到 HEAD,工作区文件不变
$ git reset
# 等价于
$ git reset HEAD
# 重置暂存区与工作区到最近一次提交;会丢弃已追踪文件的本地修改
$ git reset --hard
# 重置暂存区与工作区到最近一次提交的上一个版本
$ git reset --hard HEAD^

# 当前分支指针移动到指定 commit,同时重置暂存区,工作区保持不变(mixed 默认)
$ git reset <commit>
$ git reset --mixed <commit>

# 当前分支指针移动到指定 commit,但保持暂存区和工作区不变
$ git reset --soft <commit>
# 当前分支指针移动到指定 commit,同时重置暂存区和工作区
$ git reset --hard <commit>

git reset --hard 不会自动删除普通未追踪文件,但会覆盖已追踪文件,并可能删除阻碍写入的未追踪文件。执行前先确认 git status,重要修改应先备份或 stash。

  • git reset 会移动当前分支指针,可能重置索引和工作区。它适合整理本地、尚未共享的提交;对已推送且他人可能基于其开发的分支,不要随意 reset 后强推。
  • reset 后,原提交通常只是暂时变成不可达,并不会在下一次提交时立即被删除;在 reflog 保留期内通常仍可恢复,之后可能被垃圾回收。
  • 当回退到旧版本后,普通 git log 可能看不到原来的新提交。可以用 git reflog 找到旧提交的 commit ID,再决定是否使用 git reset --hard <commit_id> 恢复。
  • reflog 是本地记录,不能替代远程备份;电脑损坏或 reflog 过期后,可能无法恢复。重要历史应通过分支、标签或远程仓库保留。
  • reset --hard 只应在确认工作区内容可以丢弃时使用。

revert 详解

# 生成一个撤销最近一次提交的新提交
$ git revert HEAD
# 撤销 HEAD 的第一个父提交(即更早的一个提交)
$ git revert HEAD^
# 撤销更早的提交;num 需要替换成数字,例如 HEAD~3
$ git revert HEAD~num

# 生成一个撤销指定提交版本的新提交
$ git revert <commit_id>
# 执行时不打开默认编辑器,直接使用 Git 自动生成的提交信息
$ git revert --no-edit <commit_id>

# 撤销一个 merge commit 时必须选择主线父提交(1 或 2),并提供 merge commit
$ git revert -m 1 <merge-commit>
# 发生冲突后,解决冲突并继续;也可以放弃本次 revert
$ git add <file>
$ git revert --continue
$ git revert --abort

git revert 命令用来撤销某个已经提交的快照。它会在提交记录最后面增加一个反向提交,而不是从项目历史中移除原提交,因此更适合公共分支。

当你希望撤销某个提交带来的代码效果、同时保留完整历史时,应使用 revert。 revert 不会删除原提交,也不保证一定无冲突;如果后续提交依赖了该修改,仍可能需要手动解决冲突。

revert 被设计为撤销公共提交的安全方式,reset 更适合重设本地、尚未共享的提交。 如果公共分支出了问题,通常新增 git revert 提交,而不是 reset 后强制推送。

发布一个提交之后,应假设其他开发者可能已经依赖它。突然移除公共历史会导致协作者分叉、丢失提交或产生难以理解的合并结果。

cherry-pick

将指定的提交 commit 应用于当前分支。它适合把另一个分支的独立修复带到当前分支;如果要撤销一次 revert,通常优先对“revert 提交”再次执行 git revert,而不是盲目 cherry-pick 原提交。


$ git cherry-pick <commit_id>
$ git cherry-pick <commit_id> <commit_id>
$ git cherry-pick <commit_id>^..<commit_id>

git bisect

  • 快速找出有 bug 的 commit
  • 它的原理很简单,就是将代码提交的历史,按照两分法不断缩小定位。所谓"两分法",就是将代码历史一分为二,确定问题出在前半部分,还是后半部分,不断执行这个过程,直到范围缩小到某一次代码提交。
# 第一个参数标记为 bad,第二个参数标记为 good;不一定必须是时间上的终点/起点
$ git bisect start [bad-commit] [good-commit]
$ git bisect start HEAD <known-good-commit>

# 标识当前检出的提交没有问题
$ git bisect good
# 标识当前检出的提交有问题
$ git bisect bad
# 按提示不断测试并标记 good/bad,直到定位到引入问题的提交

# 退出查错,回到开始 bisect 前的分支
$ git bisect reset
# 如果项目可以自动测试,也可以让 Git 自动执行
$ git bisect run <test-command>

git submodule 子模块

有种情况我们经常会遇到:某个工作中的项目需要包含并使用另一个项目。也许是第三方库,或者你独立开发的,用于多个父项目的库。 现在问题来了:你想要把它们当做两个独立的项目,同时又想在一个项目中使用另一个。如果将另外一个项目中的代码复制到自己的项目中,那么你做的任何自定义修改都会使合并上游的改动变得困难。Git 通过子模块来解决这个问题,允许你将一个 Git 仓库作为另一个 Git 仓库的子目录。 它能让你将另一个仓库克隆到自己的项目中,同时还保持提交的独立。

# 在主项目中添加子项目;URL 是子模块仓库地址,Path 是存储目录
git submodule add [URL] [Path]

# 克隆含有子模块的主项目
git clone [URL]
# 初始化本地配置并检出父项目记录的子模块提交
git submodule init
git submodule update
# 等价于
git submodule update --init
# 如果包含嵌套子模块,递归初始化和更新
git submodule update --init --recursive

# 克隆时自动初始化并更新所有子模块
git clone --recurse-submodules [URL]

# 查看子模块状态
git submodule status
# 进入每个子模块执行命令
git submodule foreach 'git status --short'

父项目提交的不是子模块当前分支名,而是子模块的一个确定提交 SHA。更新子模块后,必须在父项目中提交子模块指针变化;协作者拉取父项目后还需要执行 git submodule update --init --recursive

新建一个 Git 项目的两种方式

1.本地新建好 Git 项目,然后关联远程仓库

# 初始化一个 Git 仓库
$ git init
# (可选)设置新仓库默认分支名
$ git branch -M main
# 关联远程仓库
$ git remote add origin <git-repo-url>
# 例如
$ git remote add origin https://github.com/xxxxxx/example.git
# 首次推送当前分支,并建立 upstream;HEAD 可避免写死 master/main
$ git push -u origin HEAD

2.clone 远程仓库

# 新建好远程仓库,然后 clone 到本地
$ git clone <git-repo-url>

# 将远程仓库下载到指定目录;目录不存在时会自动创建
$ git clone <git-repo-url> <project-name>

Git 分支管理规范

blog.csdn.net/LitongZero/…

juejin.im/post/684490…

下面的 developtestreleasemaster 属于 Git Flow 风格的个人经验,不是 Git 强制要求,也不是所有团队的最佳实践。当前常见做法还包括 trunk-based development、短生命周期 feature 分支、Pull Request 和 CI/CD;默认分支名称也可能是 main

  • 实际开发的时候,一人一条分支(个人见解:除非是大项目,参与的开发人员很多时,可以采用 feature 分支,否则一般的项目中,一个开发者一条分支够用了)。除此之外还要有一条 develop 开发分支,一条 test 测试分支,一条 release 预发布分支。
    • develop开发分支,开发人员每天都需要拉取/提交最新代码的分支;
    • test测试分支,开发人员开发完并自测通过后,发布到测试环境的分支;
    • release预发布分支,测试环境测试通过后,将测试分支的代码发布到预发环境的分支(这个得看公司支不支持预发环境,没有的话就可以不采用这条分支);
    • master线上分支,预发环境测试通过后,运营/测试会将此分支代码发布到线上环境;
  • 大致流程:
    • 开发人员每天都需要拉取/提交最新的代码到 develop 分支
    • 开发人员开发完毕,开始 集成测试,测试无误后提交到 test 分支并发布到测试环境,交由测试人员测试;
    • 测试环境通过后,发布到 release 分支 上,进行预发环境测试;
    • 预发环境通过后,发布到 master 分支上并打上标签(tag);
    • 如果线上分支出了 bug ,这时候相关开发者应该基于预发布分支(没有预发环境,就使用 master 分支),新建一个 bug 分支用来临时解决 bug ,处理完后申请合并到 预发布 分支。这样做的好处就是:不会影响正在开发中的功能。

预发布环境的作用: 预发布环境是正式发布前最后一次测试。因为在少数情况下即使预发布通过了,都不能保证正式生产环境可以100%不出问题;预发布环境的配置,数据库等都是跟线上一样;有些公司的预发布环境数据库是连接线上环境,有些公司预发布环境是单独的数据库;如果不设预发布环境,如果开发合并代码有问题,会直接将问题发布到线上,增加维护的成本。

Git 钩子

  • Git Hooks 可以在提交、合并、推送接收等阶段执行代码检查、格式化或其他自动化任务。
  • 钩子默认存储在 $GIT_DIR/hooks,普通仓库通常是 .git/hooks;如果配置了 core.hooksPath,实际目录会被重定向。注意,客户端钩子默认不会随仓库提交给其他协作者。
  • 钩子分为两大类,客户端的和服务器端的
    • 客户端钩子主要被提交、合并等本地操作调用,例如 pre-commitcommit-msgpre-push
    • 服务器端钩子作用于接收被推送提交等联网操作,例如 pre-receiveupdatepost-receive;服务器端检查更适合用来强制执行团队规则。

4.1 pre-commit

  • pre-commit 会在创建 commit 之前执行,例如代码检查、格式化或测试;脚本返回非零状态时,commit 通常会被阻止。
  • pre-commit 只检查本地提交,不能替代 CI 或服务器端保护规则;git commit --no-verify 可以绕过客户端的部分检查。
  • .git/hooks/ 下面通常有 pre-commit.sample 等示例脚本。

4.2 安装 pre-commit

# 旧的 npm pre-commit 方案,仍可用于维护旧项目
npm install pre-commit --save-dev

当前项目也可以使用 core.hooksPath 管理可提交到仓库的钩子目录,例如:

mkdir -p .githooks
# 将可执行的 pre-commit 脚本放入 .githooks/
git config core.hooksPath .githooks

也可以使用项目现有的 Husky、lint-staged 或其他钩子管理工具,但应避免重复安装多个工具。

4.3 配置脚本

如果旧版 pre-commit 包没有在 .git/hooks 目录下生成 pre-commit 文件,可以在项目根目录执行:

node ./node_modules/pre-commit/install.js

package.json 中的配置应是合法 JSON;pre-commit 配置位于根级别,而不是 scripts 内:

{
  "scripts": {
    "build": "tsc",
    "eslint": "eslint src --ext .ts",
    "eslint:fix": "eslint src --ext .ts --fix"
  },
  "pre-commit": [
    "eslint"
  ]
}

上面的 pre-commit npm 包属于旧方案,维护新项目时还要结合当前 ESLint、Node.js 版本确认配置是否兼容。

4.4 跳过 pre-commit 继续提交代码

# 跳过客户端钩子检查;应只在明确了解风险时使用
$ git commit --no-verify
# 等价短写法
$ git commit -n

更多钩子:git-scm.com/book/zh/v2/…

常见问题

1、拉取远程分支后提示 Already up to date.,但本地文件内容不符合预期

Already up to date. 只表示当前分支与本次 pull 要整合的 upstream 没有新的提交,并不表示某个被删除的文件会自动恢复。可以先查看状态和分支关系:

$ git status
$ git log --oneline --decorate --graph --all -20
$ git fetch origin

如果想把某个文件恢复为远程跟踪分支中的版本,可以执行:

$ git restore --source=origin/branchName -- path/to/file
$ git add path/to/file
$ git commit -m "restore file"

如果删除操作已经提交,想撤销那次提交,可以对删除提交执行 git revert <commit>,不要把“重新 pull”当成通用恢复方法。

2、一个仓库中放多个项目,是否可以在子目录中分别使用 Git

如果根目录本身不是 Git 仓库,可以在前后端、客户端等项目目录中分别 git init,它们就是互相独立的仓库,各自维护 .gitignore

如果根目录已经是 Git 仓库,再在子目录中创建嵌套仓库,外层 Git 通常会把该目录视为嵌套仓库/gitlink;外层和内层的提交、忽略规则并不会完全“互不影响”。此时应明确选择:使用一个根仓库管理多个目录,或使用子模块/独立仓库,不要无意中形成嵌套仓库。

3、fatal:refusing to merge unrelated histories 拒绝合并不相关的历史

从 Git 2.9.0 开始,Git 默认拒绝合并没有共同祖先的两段历史(例如两个独立初始化的仓库)。如果确认这两段历史确实应该合并,可以显式允许:

$ git pull origin branchName --allow-unrelated-histories
# 或者先 fetch,再合并对应的远程跟踪分支
$ git fetch origin
$ git merge origin/branchName --allow-unrelated-histories

这个选项是为了防止误把两个独立项目合并,不是普通分支协作的常规步骤。使用前应确认远程 URL、当前分支和目标分支确实正确;正常情况下应从同一个仓库创建分支,并先拉取主分支的最新内容。

4、合并分支时出现问题,想要解除合并状态


error: merge is not possible because you have unmerged files.
hint: Fix them up in the work tree, and then use 'git add/rm <file>'
hint: as appropriate to mark resolution and make a commit.
fatal: Exiting because of an unresolved conflict.

当远程分支和本地分支发生冲突后,Git 会保持正在合并的状态。可以选择解决冲突后继续,也可以放弃本次合并;不是只有解除合并状态才能继续提交。执行 abort 前最好先备份未提交修改,因为它会尝试恢复合并开始前的状态,复杂情况下仍可能需要手工检查。


# 解决冲突后继续合并:git add/rm 冲突文件,再提交
$ git add <file>
$ git merge --continue
# 放弃本次合并并尽量恢复到合并前
$ git merge --abort
# 仅退出合并状态,但保留当前索引和工作区
$ git merge --quit

5、不小心把某些文件上传到远程 git 仓库/想要删除远程仓库中的文件


# 删除暂存区和工作区的文件
$ git rm -- filename
# 只从暂存区移除文件,保留工作区文件
$ git rm --cached -- filename

如果在配置 .gitignore 文件之前就把某个文件提交到远程仓库了,单独配置 .gitignore 不会停止追踪。若要保留本地文件,可以使用 git rm --cached -- filename,随后执行 git addgit commitgit push;远程分支的当前版本会删除该文件,但历史提交仍然保留。敏感信息还必须立即撤销并清理历史,详见前面的“删除文件”章节。

6、将本地新建的项目上传到新建的远程仓库上

之前没有进行过关联,即没有通过 clone 远程项目到本地再开始做项目,而是先本地新建了一个项目,然后想传到远程仓库上。

# 将本地仓库和远程仓库关联起来
$ git remote add origin <远程仓库地>
# 可按团队规范使用 main/master;HEAD 写法不依赖具体分支名
$ git push -u origin HEAD
# 上面的命令执行后,下次再从本地库上传内容时通常只需
$ git push

如果远程仓库已经有 README 等初始提交,而本地仓库是独立初始化的,先确认是否需要合并不相关历史,避免直接覆盖远程内容。

7、HEAD、分支与 detached HEAD

HEAD 在正常状态下是指向当前分支的符号引用,当前分支再指向最新提交;使用 git switch --detach <commit> 或旧写法 git checkout <commit> 后,HEAD 会直接指向提交,此时称为 detached HEAD(游离状态)。

8、创建与合并分支的原理

www.liaoxuefeng.com/wiki/896043…

9、git push 每次都要输入凭据,或 HTTPS/SSH 如何选择

  • step 1:生成公钥
# 当前新建密钥通常推荐 Ed25519
$ ssh-keygen -t ed25519 -C "your-email@example.com"
# 如果旧服务器不支持 Ed25519,也可以继续使用 RSA
$ ssh-keygen -t rsa -b 4096 -C "your-email@example.com"
# 根据提示设置密钥保存路径和 passphrase,不建议无密码保护
  • step 2:查看已生成的公钥
# 查看 Ed25519 公钥
$ cat ~/.ssh/id_ed25519.pub
# 如果使用旧的 RSA 密钥
$ cat ~/.ssh/id_rsa.pub

只复制 .pub 公钥内容到 GitHub、GitLab、Gitee 或公司 Git 服务器,不要泄露对应的私钥文件。

  • step3:复制已生成的公钥添加到 git 服务器

添加 SSH 公钥示意图

测试 ssh 是否能够连接成功

$ ssh -T git@github.com
# 其他 Git 服务请替换为对应的主机和用户名
  • step4:使用 ssh 协议 clone 远程仓库 或者 如果已经用 https 协议 clone 到本地了,那么就重新设置远程仓库

设置 SSH 远程仓库地址示意图

使用 ssh 协议

$ git remote set-url origin git@xxx.com:xxx/xxx.git
$ git remote -v
  • step5:配置 HTTPS 凭据

GitHub 的 HTTPS Git 操作不再接受账户密码,提示输入 password 时应输入 Personal Access Token(PAT),也可以使用 Git Credential Manager。其他 Git 服务通常也使用访问令牌或平台专用密码,具体以服务商规则为准。

# Git Credential Manager(Git for Windows 通常已自带)
$ git config --global credential.helper manager
# macOS 可使用钥匙串,Linux 可按发行版配置 libsecret 等安全 helper
# $ git config --global credential.helper osxkeychain

旧的 credential.helper store 会把凭据以明文形式保存到 ~/.git-credentials,不建议在共享电脑或生产环境使用:

# 仅在了解明文风险且环境可控时使用
$ git config --global credential.helper store

不要把真实 token 写进命令、Markdown、Shell 历史或远程 URL;如果 token 已泄露,应立即撤销并重新生成。

10、git 不允许提交空文件夹

Git 只跟踪文件,不跟踪空目录。可以在当前目录下添加一个 .gitkeep 文件;.gitkeep 不是 Git 的特殊文件名,只是社区常用约定。也可以放入真实的 README、配置或占位文件。

11、git add . 无法追踪复制过来的文件

先执行 git status --short --untracked-files=all 查看文件状态。git add . 的范围受当前目录影响;如果文件在当前目录之外,可以从仓库根目录执行 git add -A,或指定准确路径。若仍看不到,重点检查 .gitignore、全局忽略规则、大小写路径、文件是否位于另一个嵌套仓库中:

$ git check-ignore -v -- path/to/file
$ git add -f -- path/to/file  # 确认应该提交且确实被忽略时再使用

12、同一台电脑配置多个 git 账号

github.com/jawil/notes…

13、Another git process seems to be running in this repository, e.g.

Git index.lock 错误示意图

原因通常是 Git 进程异常退出,留下了未释放的锁文件;也可能确实有另一个 Git 进程正在运行。

解决方案: 先确认没有 Git、IDE 或其他自动化任务正在操作该仓库。确认没有进程后,再删除明确的锁文件:

$ rm .git/index.lock

Windows 也可以在资源管理器中显示隐藏文件后删除 .git/index.lock。如果仓库使用 worktree,锁文件位置可能不同;不要在 Git 进程仍运行时删除锁文件。

14、git commit -am "xxx" 有时候会失效,无法提交所有的修改

git commit -am "xxx" 只会将已被 Git 追踪的文件的修改和删除加入暂存区并提交,不会包含新增的未追踪文件。新增文件需要先执行 git add -- path/to/file;IDEA 是否自动添加文件属于 IDE 设置,不是 Git 行为。

15、git merge --no-ff 的作用

  • 禁止快进式(fast-forward)合并,会生成一个新的提交

git merge --no-ff 示意图

从合并后的代码内容来看,结果通常一样,区别在于 --no-ff 会让 Git 生成一个新的合并提交。通常主分支(可能叫 mainmaster)上存放较稳定的代码,feature 分支上可能存在许多零碎提交;快进式合并会把 feature 的提交线直接接到主分支上,而 --no-ff 可以保留一次分支合并边界。所以是否使用 --no-ff 是团队对历史可读性、回滚方式和 PR 策略的选择,并不是必须规则。

segmentfault.com/q/101000000…

16、git merge 与 git rebase 的区别

juejin.im/post/684490…

blog.csdn.net/liuxiaoheng…

segmentfault.com/a/119000001…

segmentfault.com/a/119000001…

merge 和 rebase 的核心差异是:merge 通常新增合并提交并保留分叉;rebase 会重新生成当前分支提交,使历史看起来更直线,但会改变 commit ID。已经推送并被他人使用的分支不要随意 rebase 后强推。

# 常见协作流程
$ git fetch origin
$ git switch feature/name
$ git rebase origin/main
# 或保留合并提交
$ git merge --no-ff origin/main

17、git log 无法正常显示中文

# 先确认是否只是分页器导致显示异常
$ git -c core.pager=cat log --oneline
# Windows 终端可以尝试 UTF-8 编码;具体命令取决于终端
$ chcp 65001
# 旧方案:使用 more 作为分页器(不一定能解决编码问题)
$ git -c core.pager=more log
# 确认有效后再设置全局配置
$ git config --global core.pager more

中文乱码还可能来自终端字体、系统编码、locale 或 Git 客户端本身;不要把 core.pager=more 当成通用修复方案。

www.zhihu.com/question/57…

www.playpi.org/2019031901.…

18、git merge -m "xxx" 可以附加合并提交说明

$ git merge -m "合并 feature 分支" feature/name
  • 旧版本常见的默认说明类似 Merge branch 'branchName',实际内容受分支名、Git 版本和配置影响;快进合并没有合并提交,也就没有这条合并说明。

Git merge 提交说明示意图

19、git pullgit fetch 与合并他人分支

git fetch origin 会更新 origin/* 远程跟踪分支;git pull 还会把当前分支的 upstream 整合到当前分支,整合方式可能是 merge 或 rebase,不是把所有远程分支都合并到本地。

想要合并他人的分支时:

  • 如果本地已有该分支,可以执行 git merge branchname
  • 如果只有远程跟踪分支,可以先 git fetch origin,再执行 git merge origin/branchname
  • 如果希望建立本地跟踪分支,可以执行 git switch -c branchname --track origin/branchname

Git pull 与分支示意图

20、查看本地分支、远程跟踪分支和远程新分支

  • git branch -lgit branch 查看本地分支;
  • git branch -r 查看本地已有的远程跟踪分支;
  • git branch -a 查看两者;
  • 这些命令不会实时查询远程服务器。别人新推送分支后,应先执行 git fetch origin,再查看 git branch -r

21、git stash 存储未追踪的文件

默认 git stash 不保存未追踪文件;未追踪文件不需要先 git add,可以直接使用:

$ git stash -u
# 如果还要保存被 .gitignore 忽略的文件
$ git stash -a

恢复时要注意工作区冲突,确认 git stash list 中的记录仍然存在。

22、如何在 github 上 pr 项目

segmentfault.com/a/119000002…

当前常见 PR 流程:从最新主分支创建短生命周期 feature 分支,完成提交并推送,创建 Pull Request;通过 CI、代码评审和测试后合并。提交前可执行 git fetch origingit rebase origin/main,但不要改写已经被其他人使用的分支历史。

23、git push 无法提交代码

可能出现的报错:

  • remote: Permission to xxxxx.git denied to xxx. fatal: unable to access 'github.com/ xxxxx.git/': The requested URL returned error: 403
  • remote: You do not have permission to push to the repository via HTTPS
    fatal: Authentication failed for 'gitee.com/xxx.git/'
# 查看远程仓库地址
$ git remote -v
$ git remote get-url origin
# 查看当前分支和 upstream
$ git branch -vv
# 验证远程仓库是否可访问(不执行推送)
$ git ls-remote origin
  • 403、Permission denied 通常表示账号没有仓库写权限、认证账号错误或组织策略限制;Authentication failed 通常表示 token/凭据无效或过期。
  • git push 常见远程协议是 SSH 和 HTTPS。切换协议只能解决连接/认证方式问题,不能自动授予仓库写权限。
  • HTTPS 使用 PAT/平台令牌或 Git Credential Manager;SSH 使用已添加到服务器账号的公钥。

Git push 权限错误示意图

24、git 输错用户名和密码,后续的 git 操作一直报错

remote: Coding 提示: Authentication failed.
remote: 认证失败,请确认您输入了正确的账号或访问令牌。
fatal: Authentication failed for 'https://e.coding.net/xxx.git/'

如果确认本地缓存了错误凭据,可以在 Windows“凭据管理器”的“Windows 凭据”中删除对应 Git/Coding 条目,然后重新执行操作。重新认证时不要输入 GitHub 账户密码:GitHub HTTPS 使用 PAT;Coding、Gitee 等平台使用各自的访问令牌或推荐的凭据管理器。

Windows 凭据管理器示意图

25、lint-staged 失败

lint-staged 错误示意图

项目路径包含中文或空格可能触发某些旧工具、Shell 或脚本的兼容问题,但不是普遍原因。应先查看 lint-staged 的具体报错,再检查 Node.js、Git、Shell、任务命令和路径转义;升级依赖、给路径正确加引号通常比直接改成英文路径更合适。必要时再把项目移动到简单路径验证。

26、查看 git 安装目录

  • Mac: 在命令行中输入 which git,就会显示 git 的安装位置了
  • Windows: 打开cmd,输入 where git,就会显示 git 的安装路径了

27、Git中用vim打开、修改、保存文件

28、windows10 无法编辑 vimrc

29、Windows下Git Bash中VIM打开文件中文乱码

30、如何修改旧的 commit 的 message/如何将多个 commit 合成一个 commit/如何将多个间隔的 commit 合成一个 commit/

# 交互式整理最近 3 个提交
$ git rebase -i HEAD~3
# 从指定基线开始整理当前分支提交
$ git rebase -i <base-commit>

在编辑器中可以使用 pickrewordeditsquashfixupdrop 等操作。多个不连续提交需要先调整顺序,再进行 squash/fixup;整理已推送历史后通常需要 git push --force-with-lease,公共分支应避免这样做。

31、重命名和修改同一个文件时是否会冲突

Git 不直接记录“重命名”这一动作,而是在 diff/merge 时根据文件相似度进行重命名检测。如果一方重命名文件、另一方修改原文件内容,Git 经常可以自动识别并合并,但取决于相似度和修改位置,不能保证一定不冲突。

如果双方把同一个文件重命名到不同目标,可能出现 rename/rename 冲突;重命名到同一目标且内容兼容时也可能自动合并。发生冲突时使用 git status 查看冲突类型,手动选择最终路径和内容后 git add,再完成 merge/rebase。

32、git revert 失败:error: Commit faulty merge is a merge but no -m option was given、error: option `mainline' expects a number greater than zero

segmentfault.com/a/119000001…

www.jianshu.com/p/3719dae37…

# 1 表示选择第一个父提交作为主线;必须提供要撤销的 merge commit
$ git revert -m 1 <merge-commit>
# 发生冲突时:解决冲突、暂存,再继续
$ git add <file>
$ git revert --continue

33、git 创建一个空的分支

普通分支需要从已有提交创建,因此会继承起点历史;如果需要一个没有父提交的新历史,应使用 orphan 分支。它仍然需要至少创建一次提交后才会真正成为可用分支。

# 旧写法
$ git checkout --orphan emptyBranchName
# 当前推荐写法
$ git switch --orphan emptyBranchName

该命令会生成一个叫 emptyBranchName 的分支。工作区和索引通常会暂时保留起点内容,但新分支不会指向任何以前的提交;如果直接提交当前内容,这次提交就是该分支的首次提交。

如果想要真正的“空分支”,需要谨慎移除已追踪文件:

# 仅移除已追踪文件;此操作会删除工作区文件,执行前确认已备份
$ git rm -r -- .
# 如果还需要删除未追踪文件,应先用 git clean -n 预览,再按需执行 git clean -f
$ git clean -nd

git rm -r -- . 不会自动删除所有未追踪文件,git clean -f 也具有破坏性。

34、如何清空一个分支的所有提交

更安全的做法是先切换到其他分支,再创建 orphan 分支;不能直接删除当前检出的分支。以下操作会重写该分支历史,远程分支还需要经过权限允许后强制更新:

$ git switch main
$ git switch --orphan reset-branch
$ git rm -r -- .
$ git commit --allow-empty -m "重新开始该分支历史"
# 确认远程分支没有他人新提交后再使用
$ git push --force-with-lease origin reset-branch

如果必须沿用旧分支名,可以先在其他分支上删除旧分支,再创建同名 orphan 分支;操作前应备份旧分支或打标签。

35、如何快速找出有 bug 的 commit

官方文档与当前规则

本文更新时参考了以下官方资料;旧文章链接仍保留,若内容与官方文档冲突,以当前 Git 和平台文档为准:

  • git-add:暂存文件、add -uadd -A 和路径范围
  • git-restore:恢复工作区和暂存区文件
  • git-switch:切换和创建分支
  • git-reset:移动分支指针和重置索引
  • git-pull:fetch 后的 merge/rebase 行为
  • git-push:refspec、强制推送和 --force-with-lease
  • git-stash:已追踪、未追踪和被忽略文件的暂存
  • githooks:客户端/服务器端钩子及 core.hooksPath
  • GitHub authentication:HTTPS、PAT、SSH 和凭据管理器
457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

支持搜索文章标题、所属分类和原始文档路径。

按分类浏览

10 COLLECTIONS