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

显示模式

登录
ARCHIVE DOCUMENTGIT

我在工作中是如何使用 Git 的

所属馆藏
Git
文件格式
Markdown
原始路径
git/05-我在工作中是如何使用 git 的
本文目录24 个章节
  1. 入职后如何配置 Git 并拉取公司代码?
  2. 常见工作流程
  3. git add
  4. git commit
  5. git pull
  6. git fetch
  7. git branch、switch 和远程分支
  8. git rebase:让提交记录更清晰
  9. git merge 和 git rebase 的区别
  10. git rebase 交互模式
  11. cherry-pick 多个提交
  12. cherry-pick 冲突处理
  13. revert 普通提交
  14. revert 合并提交
  15. stash 的应用场景
  16. VS Code 中使用 stash
  17. 丢弃工作区未暂存改动
  18. 取消暂存但保留工作区改动
  19. stash:保存未完成工作
  20. reset --soft:重新整理本地提交
  21. cherry-pick:复制指定提交
  22. revert:公开撤销提交
  23. 参考资料
  24. 附件

我在工作中是如何使用 Git 的

Category(分类): git Status: 未知

本文尽量保留原文关于 Git 环境配置、工作区、rebase、merge、cherry-pick、revert、stash、reset、reflog 和 alias 的内容,并结合 Git 官方文档、GitHub 当前认证文档补充现代实践。

文中的历史截图仍然保留,但截图里的默认分支、命令输出和界面可能已经过时。示例中的 master 可以替换为当前项目实际使用的 main 或其他默认分支。

入职后如何配置 Git 并拉取公司代码?

案例引申出一个问题:入职一家新公司,leader 给你分配了仓库权限后,如何配置本地 Git 环境并拉取代码?可以按下面的步骤操作。公司可能使用 GitHub、GitLab、Gitea、内部代码平台或自建 SSH 服务,页面名称和权限策略会略有不同。

1. 安装 Git

Git 官方下载页面 选择对应操作系统的安装包。安装完成后检查版本:

git --version

Windows 用户可以使用 Git Bash、PowerShell 或 Windows Terminal;macOS/Linux 用户可以使用系统终端。公司电脑通常还需要配置代理、证书、单点登录或内部 CA,这些应按照公司 IT 文档处理,不要为了绕过证书错误而关闭 TLS 校验。

2. 生成 SSH 密钥

原文使用 RSA:

ssh-keygen -t rsa -C "你公司内部邮箱地址"

RSA 仍然可以使用,但新建密钥时更推荐 Ed25519:

ssh-keygen -t ed25519 -C "你的公司邮箱"

按提示选择默认保存路径(通常是 ~/.ssh/id_ed25519),并设置一个安全的 passphrase。不要把私钥上传、粘贴到网页或提交进代码仓库;通常只需要复制 .pub 结尾的公钥文件。

# 查看公钥,复制整行内容
cat ~/.ssh/id_ed25519.pub

# Windows PowerShell 也可以使用
Get-Content $HOME\.ssh\id_ed25519.pub

如果公司安全策略要求使用 RSA、硬件密钥或证书认证,应遵循公司要求。生成密钥后可以使用 ssh-agent 缓存解锁后的私钥:

eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

Windows、macOS 和 Linux 对 ssh-agent 的启动方式可能不同,Git for Windows 也可以配合系统 OpenSSH 使用。

SSH 密钥文件示例

3. 把公钥添加到代码托管平台

以 GitHub 为例,进入 Settings → SSH and GPG keys,添加刚才复制的公钥。GitLab、Gitea 或企业平台通常也有类似的 SSH Keys 页面。

公钥:可以上传到代码托管平台
私钥:只能保存在自己的设备或安全的密钥管理系统中

在代码托管平台添加 SSH 公钥

测试 GitHub SSH 连接:

ssh -T git@github.com

第一次连接时,SSH 可能会提示确认服务器主机指纹。确认指纹来源可信后再接受,避免无条件输入 yes。公司内部服务器的用户名、端口和主机名以公司文档为准。

4. 配置 Git 提交身份

Git 的 user.nameuser.email 是写入提交对象的作者/提交者信息,不等于 GitHub 登录账号,也不负责 SSH 身份认证。可以设置全局配置,也可以在某个仓库内覆盖:

# 所有仓库默认使用
git config --global user.name "你的姓名"
git config --global user.email "你的公司邮箱"

# 只配置当前仓库(进入仓库目录后执行)
git config user.name "项目显示名"
git config user.email "项目邮箱"

# 查看配置及来源
git config --list --show-origin

上面第一行命令前不应有多余空格;这里的空格只用于 Markdown 排版时要特别注意。公司如果要求提交签名,还需要额外配置 SSH/GPG 签名或平台认可的签名方式。

5. 克隆仓库并确认远程地址

拿到仓库地址后,根据公司要求选择 SSH 或 HTTPS:

# SSH 示例
git clone git@github.com:组织名/仓库名.git

# HTTPS 示例
git clone https://github.com/组织名/仓库名.git

cd 仓库名
git remote -v
git status

如果仓库需要克隆指定分支:

git clone --branch main --single-branch <仓库地>

完成以上步骤后,就可以开始开发。SSH 通常不需要每次输入账号密码,但私钥仍应使用 passphrase 和 ssh-agent 保护。HTTPS 也不是“每次提交都输入用户名和密码”:GitHub 已经移除 HTTPS 密码认证,通常需要个人访问令牌(PAT)、Git Credential Manager 或企业统一认证;其他平台可能使用 OAuth、令牌或内部凭据助手。

认证方式的区别是:

  • SSH:本地私钥签名,服务器保存并信任对应公钥;
  • HTTPS:通过 HTTPS 连接,使用凭据助手、PAT、OAuth 或企业认证;
  • 提交身份:由 user.nameuser.email 等配置写入 commit,不等于上述传输认证。

Git 简介

Git(读音为 /ɡɪt/)是一个开源的分布式版本控制系统,适合管理从小型项目到大型项目的版本历史。它可以在本地创建提交、查看差异、切换分支和合并历史,远程仓库则用于共享、备份、代码评审和协作。

Linux 内核早期由全球贡献者通过邮件提交补丁,维护者需要手工合并大量变更。2002 年起,Linux 社区曾使用 BitKeeper 管理源码;2005 年,由于 BitKeeper 的使用条件发生变化,Linus Torvalds 开始创建 Git。Git 的早期版本在很短时间内完成了核心原型,并迅速用于 Linux 内核开发。原文“花两周时间用 C 语言写完 Git”的说法体现了历史故事的戏剧性,但不应理解为现代 Git 的全部功能只在两周内完成。

Git 历史背景示意

Git 的核心对象包括 blob、tree、commit 和 tag object;分支、标签、HEAD 等引用则为这些对象提供可读入口。理解“工作区—暂存区—提交对象—远程引用”四个层次,比死记某个托管平台的按钮更重要。

Git 的工作区域和流程

原文把工作区域画成四个部分:

  1. Workspace/Working tree:工作区,平时编辑文件的目录;
  2. Index/Staging area:暂存区/索引,下一次提交将要写入的快照;
  3. Repository:本地仓库,保存对象、引用和本地历史,普通仓库通常位于 .git
  4. Remote:远程仓库,用于共享和协作,不是本地 Git 工作区的一部分。

Git 四个工作区域

git add 并不是把文件永久“移入”另一个文件夹,也不是只标记“哪些文件由 Git 管理”。它把工作区文件在当前时刻的内容写入暂存区。之后如果再次修改文件,工作区和暂存区就会再次产生差异,需要重新 git add 才会进入下一次提交。

git commit 把暂存区的快照写成新的本地提交;git fetch 把远程对象和引用更新到本地;git mergegit rebase 才会把远程变更整合到当前分支;git push 请求远程仓库更新指定引用。

常见工作流程

工作区编辑文件
    │ git add / git add -p
    ▼
暂存区(Index)
    │ git commit
    ▼
本地提交图
    │ git fetch + merge/rebase,或 git pull
    ▼
本地分支与远程跟踪分支同步
    │ git push
    ▼
远程仓库分支

每天开始工作时,可以先检查状态并获取远程更新:

git status --short
git fetch origin
git log --oneline --graph --decorate --all -n 20

Git 基本操作

git add

把文件或补丁加入暂存区:

# 暂存指定文件
git add path/to/file.js

# 暂存多个指定文件
git add file1.js file2.js

# 交互式选择文件中的部分改动
git add -p

# 暂存当前目录下的改动(包括删除和新增,需确认范围)
git add .

# 从仓库根目录暂存所有工作区改动
git add -A

git add . 的范围与执行目录有关,且可能把不想提交的文件加入暂存区;团队应使用 .gitignore 排除构建产物、依赖目录、日志、密钥和本地配置,并在提交前运行 git statusgit diff --staged 检查。

git commit

# 提交暂存区内容,打开编辑器填写提交信息
git commit

# 使用简短提交信息
git commit -m "修复登录校验"

# 只适用于已经被 Git 跟踪的文件:暂存并提交其已修改内容
git commit -am "更新配置"

# 修改最近一次提交,通常会改变 commit ID
git commit --amend
# 不修改最近一次提交信息
git commit --amend --no-edit

git commit -am 不会自动包含未跟踪的新文件;新文件仍需先 git add。提交信息应说明“做了什么”,不要把密钥、密码、令牌和大型生成文件提交到历史中。

git pull

git pull 通常先执行 fetch,再按照配置或命令参数进行整合。不能简单断言它永远等同于 git fetch && git merge,因为它也可以采用 rebase 或只允许快进:

# 获取远程并按默认策略整合当前上游
git pull

# 明确使用 merge
git pull --no-rebase origin main

# 明确使用 rebase
git pull --rebase origin main

# 只允许 fast-forward,有分叉就停止
git pull --ff-only origin main

常见显式写法:

git pull <远程> <远程分>:<本地分>

实际工作中推荐先 git fetch,查看差异后再选择 merge 或 rebase;这样不会因为一个默认配置不清楚而意外改写当前分支历史。

git fetch

git fetch 只从远程获取对象和引用,更新 origin/main 等远程跟踪分支,不会自动修改当前本地分支的工作区:

# 获取 origin 的远程更新
git fetch origin

# 获取指定远程分支
git fetch origin main

# 获取所有远程
git fetch --all --prune

--prune 会清理远程已删除、但本地仍保留的 remote-tracking 引用。fetch 完成后可以比较:

git log --oneline HEAD..origin/main
git diff HEAD..origin/main

git branch、switch 和远程分支

# 查看本地分支
git branch

# 创建本地分支但不切换
git branch feature/login

# 推荐:切换到已有分支
git switch feature/login

# 推荐:创建并切换
git switch -c feature/login

# 查看远程跟踪分支
git branch -r

# 查看本地和远程跟踪分支
git branch -a

# 安全删除已经合并的本地分支
git branch -d feature/login

# 强制删除本地分支,可能丢失未合并提交的引用
git branch -D feature/login

# 重命名当前分支
git branch -m new-name

# 首次推送并建立上游关系
git push -u origin feature/login

# 删除服务器上的远程分支
git push origin --delete feature/login

旧版本和历史文章常使用:

git checkout feature/login
git checkout -b feature/login

checkout 仍然有效,但 Git 2.23 之后更推荐用 switch 表达分支切换,用 restore 表达文件恢复。

工作中使用 Git 解决问题的场景

git rebase:让提交记录更清晰

rebase(变基)会把一组提交重新应用到新的基础提交上。它与 merge 都能整合两个开发方向,但 rebase 会生成新的提交对象并改变提交 ID,因此一般只对个人分支、未共享历史或已取得团队同意的分支使用。

rebase 基本原理

假设有两条分支:masterfeature/1,它们都从初始提交 add readme 创建。之后:

  • master 增加 3.js4.js,产生两次提交;
  • feature/1 增加 1.js2.js,也产生两次提交。

master 分支的历史:

master 分支提交记录

feature/1 分支的历史:

feature/1 分支提交记录

合在一起看,两个分支从共同祖先分叉:

两个分支的提交图

切换到功能分支后执行:

git switch feature/1
git fetch origin
git rebase master

如果以 origin/main 为集成分支,现代项目通常写成:

git rebase origin/main

rebase 会找到功能分支相对于共同祖先的提交,暂时移除这些提交,把当前分支基点移动到 master 最新提交,再按顺序重新应用功能分支的提交。它不是“把 master 的每个提交复制到 feature 后再保留原 feature 提交”,而是重放 feature 的独有提交。

rebase 后的提交记录

完成后,两个分支通常会呈现一条较直的历史,但提交 ID 已经发生变化。如果功能分支之前已经推送到远程,更新远程时应优先使用:

git push --force-with-lease origin feature/1

--force-with-lease 会检查远端分支仍是自己预期的旧状态,比无保护的 git push -f 安全,但仍应先确认分支和协作者影响。

rebase 冲突处理

rebase 过程中如果某个提交与新基础发生冲突,Git 会暂停:

# 编辑冲突文件后
git add <已解决的文>
git rebase --continue

# 放弃当前 rebase
git rebase --abort

# 确认当前提交的改动不需要时跳过
git rebase --skip

由于 rebase 是逐个重放提交,多个提交可能分别产生冲突;merge 则通常在一次三方合并中解决冲突,但复杂合并仍可能需要多轮处理。解决冲突后一定要运行测试,不能只看到 rebase 成功就认为功能正确。

git merge 和 git rebase 的区别

当不是 fast-forward 时,git merge 通常生成一个新的合并提交:

git switch main
git merge feature/1

merge 与 rebase 的历史差异

对比项mergerebase
历史保留真实分叉和合并关系重新生成提交,通常更线性
提交 ID原提交通常保留被重放的提交会生成新 ID
共享分支通常更安全需要谨慎,可能改写公共历史
冲突通常在合并过程中集中处理可能随每个重放提交逐步处理
适合场景公共分支整合、保留拓扑个人分支同步主线、整理未公开提交

merge 是否生成合并提交还取决于是否 fast-forward、--no-ff 和团队策略。rebase 也不是“让历史变漂亮”的无风险按钮:它会改变提交身份,公共分支通常应通过受保护分支、合并请求和明确的线性历史策略完成。

git rebase 交互模式

开发中经常会产生“修正拼写”“调试日志”“临时尝试”等不适合保留在公共历史中的提交。交互式 rebase 可以重新排序、编辑、合并或删除一段尚未共享的提交。

需要整理的多个提交

执行:

git rebase -i <base-commit>

<base-commit> 是提交范围的上游边界,Git 会编辑“该基点之后、当前分支可达”的提交。比如想整理基于 ac18084 之后的提交:

git rebase -i ac18084

在编辑器中常见操作:

pick   保留提交
reword 保留改动但修改提交信息
edit   应用提交后暂停,手动修改
squash 合并到前一个提交并重新编辑信息
fixup  合并到前一个提交并丢弃当前提交信息
drop   删除当前提交

交互式 rebase 编辑页面

例如把后面几个提交压缩成一个:

pick  第一个有效提交
squash 第二个提交
squash 第三个提交
squash 第四个提交

至少需要保留一个提交行;如果确实希望删除整个范围,要明确处理结果和工作区。ssquash 的缩写,ffixup 的缩写。

保存退出 Vim 通常是 Esc、输入 :wq、回车;也可以使用 VS Code 等编辑器,通过 git config --global core.editor 配置。之后 Git 可能打开另一个提交信息编辑器:

git add <>
git commit --amend --no-edit
git rebase --continue

完成后,历史会被整理成新的提交序列:

rebase 整理后的提交记录

特别注意:不要在别人已经基于其开发的集成分支上随意 rebase。技术上并非“任何共享分支绝对不能 rebase”,但必须先达成一致,否则会迫使协作者重新同步、解决重复提交和强制推送问题。

使用 git cherry-pick 获取指定 commit

git cherry-pick 可以理解为“挑拣”提交:它把一个或多个已有提交引入当前分支,并为每个变更创建新的提交。它不同于 merge 的地方是,不会把整个分支的提交图合并进来。

常见场景:

  • 把已经完成的线上修复从开发分支带到 release 分支;
  • 只把某个功能提交带入稳定分支,不引入同一分支的其他实验提交;
  • 在干净分支上选择性恢复一组提交。

待 cherry-pick 的功能分支

另一个功能分支的提交

目标 master 分支

切换到目标分支后,复制单个提交:

git switch main
git cherry-pick <commit-hash>

cherry-pick 会生成新的提交,因此新提交的对象 ID 通常不同;提交内容、作者时间等信息可能被保留,但提交者和父提交会是当前分支环境。

cherry-pick 后的提交记录

cherry-pick 多个提交

可以列出多个不连续提交:

git cherry-pick <commit-1> <commit-2>

连续提交可以使用范围:

git cherry-pick <first-commit>^..<last-commit>

A^..B 在常见线性历史中表示包含 A 和 B 的区间(左边界通过 A^ 排除其父提交)。遇到合并提交、多父提交或复杂图时,范围仍按提交可达性解释,应先用日志确认实际选中的提交:

git log --oneline <first-commit>^..<last-commit>

cherry-pick 冲突处理

多个提交按顺序应用时,某一步可能产生冲突:

git switch main
git cherry-pick <commit-c>^..<commit-e>

如果在提交 d 处冲突:

# 解决冲突并暂存结果
git add <>
git cherry-pick --continue

# 放弃整个 cherry-pick 序列
git cherry-pick --abort

# 停止流程但保留当前工作区和已完成的提交
git cherry-pick --quit

# 跳过当前提交(确认该提交不需要时)
git cherry-pick --skip

原文中 abort 命令的 Git 子命令拼写有误,正确形式见上面的代码。cherry-pick 前应尽量保持工作区干净,冲突解决后应检查测试和提交内容。

使用 git revert 回滚某次提交

想象这样一个场景:线上功能存在 bug,但目标分支还包含其他同事的提交。如果直接 reset,可能把整个分支指针和工作区回退,影响其他人的历史。git revert 更适合在共享分支上公开撤销一个已经存在的提交。

git revert 不删除原提交,而是根据原提交的变更创建一个新的反向提交。它要求工作区干净,冲突时需要手动处理。

revert 普通提交

git revert <commit-id>

可以添加 --no-edit 使用默认提交信息,也可以一次指定多个提交:

git revert --no-edit <commit-a> <commit-b>

多个参数表示多个明确提交,不是“前开后闭区间”。如果要撤销连续范围,常见写法是:

git revert <oldest-commit>^..<newest-commit>

Git 会按适合撤销的顺序处理范围,但复杂提交图仍可能冲突,执行前建议先查看范围和备份分支。

需要 revert 的普通提交

git revert 21dcd937fe555f58841b17466a99118deb489212

revert 生成反向变更

Git 可能打开编辑器填写提交信息。保存后会生成新的 Revert "原提交信息" 提交:

原提交仍保留在历史中,但它引入的文件变化被新的反向提交抵消。反向提交也可能与后续代码冲突,不能保证永远一键完成。

revert 合并提交

合并提交有多个父提交,Git 需要知道哪一个父提交是主线。语法是:

git revert -m <parent-number> <merge-commit>

通常 -m 1 表示把第一个父提交当作主线,撤销相对于该主线引入的另一侧变更;1 不是“保留主分支”的绝对规则,必须结合提交图确认父提交含义。

合并提交示意

合并提交的分支关系

直接对合并提交执行普通 revert 可能报错,因为 Git 不知道应当相对于哪个父提交计算反向变更:

git revert -m 1 <merge-commit>

revert 合并提交后再次合并

revert 一个合并提交后,原功能分支的提交对象仍然在历史中,Git 可能判断该分支已经合并过,因此再次直接 merge 不会自动重新引入原来的变更。这不是“以后永远不能合并”,而是提交图的祖先关系仍然存在。

revert 合并提交后的历史

如果确认要恢复之前被 revert 的整体变更,通常可以 revert 那个 revert 提交:

git revert <revert-merge-commit>

再次反向 revert 的提交图

如果功能分支已经产生了新的修复提交,也可以创建新的提交或新分支,再通过合并请求处理。恢复前应确认哪些代码已经修复,避免把原来的 bug 一起恢复。

使用 git stash 临时保存文件

开发功能时,线上出现紧急 bug,但当前功能还没有完成,不想创建一个质量很差的临时提交,可以使用 stash 保存本地改动,然后切换到 hotfix 分支。

git stash 保存的是本地工作区/索引状态,不是远程备份,也不应代替长期提交。默认主要处理已跟踪文件;未跟踪文件需要 -u,被忽略文件需要 -a

# 使用现代写法并添加说明
git stash push -m "暂存登录功能"

# 同时保存未跟踪文件
git stash push -u -m "暂存未跟踪文件"

# 查看 stash 列表
git stash list

# 应用最近一条并删除该条 stash
git stash pop

# 应用但保留 stash
git stash apply stash@{0}

原文中的 git stash save "message" 是历史写法,目前更推荐 git stash push -m "message"save 在许多版本仍可用,但不应作为新文档的首选。

stash 的应用场景

如果当前工作区有改动,直接切换到 hotfix 可能会出现:

开发未完成时的工作区改动

error: Your local changes to the following files would be overwritten by checkout:
        1.js
Please commit your changes or stash them before you switch branches.

可以先查看状态,再保存:

git status --short
git stash push -m "WIP: 暂存未完成功能"

git switch hotfix
# 修复并提交线上问题
git switch feature/login
git stash pop

如果 pop 产生冲突,先解决冲突并检查工作区;应用冲突时 stash 通常不会自动丢失,处理前不要直接执行 clear

stash 恢复后的工作区

VS Code 中使用 stash

VS Code 的 Source Control 面板和 Git 扩展可以创建、查看和应用 stash。不同版本界面可能不同,命令行仍然是最稳定、可复现的方式。

VS Code 创建 stash

填写备注或直接确认:

VS Code 填写 stash 备注

在 STASHES 菜单中查看保存记录:

VS Code 查看 stash 列表

选择记录后可以执行 Apply 或 Pop:

VS Code 应用 stash

不同工作区域如何撤销更改

开发中要区分“工作区改动”“暂存区改动”和“已经提交的历史”。现代 Git 推荐使用 restore 表达文件恢复;reset 仍然适合移动当前分支或取消暂存。

首先检查状态:

git status --short
git diff
git diff --staged

干净工作区状态

修改文件后:

工作区存在未暂存修改

丢弃工作区未暂存改动

# 恢复指定文件到暂存区版本,丢弃该文件未暂存修改
git restore -- <filename>

# 一次恢复多个文件
git restore -- file1.js file2.js

旧命令 git checkout -- <filename> 仍然有效,但语义不如 restore 明确。以下命令会丢弃工作区改动,执行前确认没有需要保留的数据:

恢复工作区文件

取消暂存但保留工作区改动

文件已经 git add 后,如果暂时不想提交,可以:

# 推荐
git restore --staged <filename>

# 历史写法,不移动 HEAD
git reset HEAD -- <filename>

这只会把文件从暂存区取消暂存,工作区内容仍然保留:

取消文件暂存

git reset <filename> 是路径形式,不要和 git reset --hard <commit> 混淆。前者只操作暂存区,后者会移动 HEAD 并可能丢弃已跟踪工作区改动。

配置 Git alias 提升工作效率

在工作中经常要使用 branchswitchfetchrebaseaddcommitpush。可以为高频、无歧义的命令配置别名:

git config --global alias.st "status --short --branch"
git config --global alias.ci commit
git config --global alias.br branch
git config --global alias.sw switch
git config --global alias.unstage "restore --staged"

原文中的 co=checkoutci=commitbr=branch 仍可用,但新项目可以使用 sw=switchunstage=restore --staged,减少 checkout 的多重语义。

配置 Git alias 命令

--global 表示写入当前用户的全局配置,通常是 ~/.gitconfig;不加 --global 则只写入当前仓库配置:

git config --list --show-origin
git config --global --edit

查看全局 Git 配置

也可以直接编辑配置文件中的 [alias] 区域:

[alias]
    st = status --short --branch
    sw = switch
    ci = commit
    br = branch
    mg = merge
    ds = diff --staged
    last = log -1 HEAD
    type = cat-file -t
    dump = cat-file -p
    lg = log --color --graph --pretty=format:"%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %Cblue<%an>%Creset" --abbrev-commit

复杂 alias 中的引号、百分号和转义符可能受到 shell、配置文件和平台差异影响,应先用 git config --get-regexp '^alias\.' 检查。带 ! 的 shell alias 可以执行任意命令,团队共享配置前要审查安全性。

以后可以使用:

git lg
git st

使用 alias 查看提交历史

Git 不要只会 pull 和 push:实用命令补充

前面介绍了常用工作流,下面保留原文的“5 条提高效率的命令”部分,并用当前 Git 语义重新整理。命令的价值不在于背诵,而在于明确它会修改工作区、暂存区、当前分支还是远程引用。

Git 实用命令示意

stash:保存未完成工作

线上修复场景可以使用:

git status --short
git stash push -u -m "WIP: 未完成的功能"
git switch hotfix
git pull --ff-only origin hotfix
# 修复、测试、提交
git switch feature/login
git stash pop

如果改动已经足够形成一个有意义的阶段性版本,创建临时提交或临时分支通常比长期 stash 更容易追踪。stash 适合短期切换上下文,不适合团队协作传递代码。

线上修复前保存 stash

reset --soft:重新整理本地提交

reset --soft 会移动 HEAD/当前分支,但保留暂存区和工作区内容。它适合整理尚未共享的提交:

# 把最近一次提交撤回,但保留其内容在暂存区
git reset --soft HEAD^

# 重新整理后提交
git commit -m "整理后的提交信息"

reset --soft 前的提交历史

例如提交历史为 a - b - c,执行:

git reset --soft a

HEAD 移到 a,b 和 c 引入的差异会进入暂存区;它们不是“只恢复某一个 commit”,而是恢复目标提交之后直到原 HEAD 的累计差异。

reset --soft 后差异进入暂存区

如果这些提交已经推送且只是自己的功能分支,可以在确认没有覆盖他人工作的前提下:

git push --force-with-lease origin feature/login

共享分支不建议这样改写,优先使用 git revert

cherry-pick:复制指定提交

使用场景包括把某个修复提交带到 release 分支,或把功能分支中的独立提交选择性带到干净分支:

git switch main
git cherry-pick <commit-hash>

待复制的单个提交

切换到目标分支

执行 cherry-pick

多个连续提交:

git cherry-pick commit1^..commit2

这里的范围在普通线性历史中包含 commit1commit2;复杂提交图中应使用 git log commit1^..commit2 检查实际范围。发生冲突时:

cherry-pick 产生冲突

cherry-pick 的提交区间

git add <已解决文>
git cherry-pick --continue

# 放弃整个序列
git cherry-pick --abort

# 退出序列但保留当前结果
git cherry-pick --quit

cherry-pick 冲突处理过程

revert:公开撤销提交

revert 适合已经进入共享分支的错误提交:

git revert <commit-id>

它新增反向提交,不删除原提交。普通提交可以直接 revert;合并提交需要指定主线父编号:

git revert -m 1 <merge-commit>

revert 普通提交前

Git 可能打开提交信息编辑器;可以保留默认的 Revert "..." 信息,也可以通过 --no-edit 跳过编辑器:

revert 提交信息编辑

revert 后的日志

对于连续提交可以使用 oldest^..newest 范围,但要先确认撤销顺序和依赖关系。revert 可能冲突,完成后要运行测试和部署检查。

reflog:错误操作后的恢复入口

reflog 是本地引用的变更记录,不是“所有 commit 操作的远程日志”。它经常能帮助找回 reset、rebase、误删分支或 detached HEAD 前的提交:

git reflog

git reflog show main

典型事故:误执行 git reset --hard,分支指针退得太远。先不要继续提交或运行会清理对象的命令,查看 reflog 找到旧位置:

git reflog
# 找到类似 HEAD@{1} 或旧 commit-id 后

git switch --detach <旧提>
git switch -c recovery/restore-work

也可以把分支直接重置回恢复点,但要先确认当前分支和工作区:

git reset --hard <recovery-commit>

reflog 中的引用记录

reset 后暂时丢失的提交

通过 reflog 找到旧提交

创建恢复分支

注意:

  • reflog 默认是本地记录,不会随 push 上传;
  • reflog 会按配置过期,不能永久保存;
  • 没有引用可达的对象最终可能被垃圾回收;
  • 误操作后应尽快建立恢复分支或复制对象 ID;
  • 远程已经被覆盖的分支还需要根据远程日志、其他克隆或托管平台记录恢复。

一套更稳健的日常工作流

# 开始工作
git status --short
git fetch origin
git switch -c feature/login   # 如果分支尚不存在

# 开发和提交
git add -p
git diff --staged
git commit -m "实现登录功能"

# 同步主线
git fetch origin
# 二选一:个人分支常用 rebase
git rebase origin/main
# 或者在团队策略要求下使用 merge:
# git merge origin/main

# 推送个人功能分支
git push -u origin feature/login

# 合并请求通过后清理
git switch main
git pull --ff-only origin main
git branch -d feature/login
git push origin --delete feature/login

提交或推送前建议检查:

  • git status 是否存在未预期的文件;
  • git diff --staged 是否只包含当前需求;
  • 是否误提交 .env、令牌、私钥、构建产物或依赖目录;
  • 是否在公共分支上使用了 reset、rebase 或 force push;
  • cherry-pick、revert、merge 后是否运行测试;
  • 远程分支是否受保护、是否必须通过 CI 和代码评审。

总结

本文主要分享了 Git 环境配置和工作中常用的命令:

  • add:把工作区快照放入暂存区;
  • commit:把暂存区写入本地提交图;
  • pull/fetch:从远程获取更新,pull 还会执行整合;
  • rebase/merge:整合分支历史;
  • cherry-pick:选择性复制提交;
  • revert:用新提交公开撤销旧提交;
  • stash:短期保存未完成工作;
  • reset --soft:整理未共享的本地提交并保留改动;
  • restore:恢复工作区或取消暂存;
  • reflog:查找本地引用曾经指向的位置;
  • alias:缩短高频命令,但复杂 alias 要注意可读性和安全性。

最重要的是理解命令会改变哪一层:工作区、暂存区、本地提交历史、当前分支,还是远程引用。个人分支可以在确认影响后整理历史;多人协作的集成分支优先通过 merge 请求、revert、分支保护和 CI 保持可追踪性。

参考资料

附件

原文附带的 Git pull/push 命令整理 PDF

457 DOCUMENTS · 10 COLLECTIONS
ARCHIVE SEARCH457 篇文章

SEARCH GUIDE

输入关键词开始搜索

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

按分类浏览

10 COLLECTIONS