Git全解析(2W+长文):从原理到高频操作手册

目录

[一、先把 Git 想成"游戏存档系统"](#一、先把 Git 想成“游戏存档系统”)

[二、Git 管理的是"快照",不是简单的修改记录](#二、Git 管理的是“快照”,不是简单的修改记录)

[三、Git 最重要的三个区域](#三、Git 最重要的三个区域)

[1. 工作区 Working Directory](#1. 工作区 Working Directory)

[2. 暂存区 Staging Area](#2. 暂存区 Staging Area)

[3. 本地仓库 Repository](#3. 本地仓库 Repository)

四、为什么需要暂存区?

五、一次完整操作到底发生了什么?

第一步:修改文件

[第二步:执行 git add](#第二步:执行 git add)

[第三步:执行 git commit](#第三步:执行 git commit)

[六、commit 到底是什么?](#六、commit 到底是什么?)

[七、HEAD 是什么?](#七、HEAD 是什么?)

八、分支的原理是什么?

九、切换分支为什么文件会变化?

十、合并分支是什么原理?

十一、冲突为什么会出现?

[十二、GitHub 和 Git 到底什么关系?](#十二、GitHub 和 Git 到底什么关系?)

Git

GitHub

[十三、push 的原理是什么?](#十三、push 的原理是什么?)

[十四、pull 的原理是什么?](#十四、pull 的原理是什么?)

[十五、clone 的原理是什么?](#十五、clone 的原理是什么?)

[十六、reset、restore、revert 的区别](#十六、reset、restore、revert 的区别)

[1. git restore](#1. git restore)

[2. git reset](#2. git reset)

[3. git revert](#3. git revert)

[十七、.git 目录是什么?](#十七、.git 目录是什么?)

[十八、Git 底层保存的四类对象](#十八、Git 底层保存的四类对象)

[1. blob](#1. blob)

[2. tree](#2. tree)

[3. commit](#3. commit)

[4. tag](#4. tag)

[十九、为什么 Git 中经常看到一串哈希?](#十九、为什么 Git 中经常看到一串哈希?)

二十、用你实际的项目串起来

二十一、最核心的命令与原理对应

二十二、最后只记住这张图

[后端程序员 Git 日常高频操作:按"目标"整理](#后端程序员 Git 日常高频操作:按“目标”整理)

一、第一次把远程项目下载到本地

目标

指令链

每一步的含义

[1. 克隆远程仓库](#1. 克隆远程仓库)

[2. 进入项目](#2. 进入项目)

[3. 查看当前状态](#3. 查看当前状态)

[4. 查看所有分支](#4. 查看所有分支)

[5. 查看远程仓库地址](#5. 查看远程仓库地址)

二、每天开始开发前,同步远程最新代码

目标

推荐指令链

每一步的含义

[1. 切换到主开发分支](#1. 切换到主开发分支)

[2. 确认工作区是否干净](#2. 确认工作区是否干净)

[3. 拉取远程最新提交](#3. 拉取远程最新提交)

三、从主分支创建一个功能分支

目标

指令链

含义

[1. 先进入基准分支](#1. 先进入基准分支)

[2. 同步最新代码](#2. 同步最新代码)

[3. 创建并切换新分支](#3. 创建并切换新分支)

四、完成一次规范的代码提交

目标

推荐指令链

第一步:查看修改状态

常见状态

第二步:查看尚未暂存的修改

第三步:把本次需要提交的文件加入暂存区

第四步:检查暂存区内容

第五步:生成提交

五、把本地功能分支推送到远程

目标

第一次推送

后续推送

六、开发中途临时保存修改,但暂时不想提交

目标

指令链

[stash 的含义](#stash 的含义)

推荐写法

[查看 stash](#查看 stash)

[恢复并删除 stash](#恢复并删除 stash)

[恢复但保留 stash](#恢复但保留 stash)

[删除某个 stash](#删除某个 stash)

[删除所有 stash](#删除所有 stash)

七、只暂存一个文件中的部分修改

目标

指令

八、撤销工作区中尚未暂存的修改

目标

撤销单个文件

撤销全部未暂存修改

[九、撤销已经执行的 git add](#九、撤销已经执行的 git add)

目标

撤销单个文件暂存

撤销全部暂存

十、刚提交完,发现漏了一个文件

目标

指令链

重要风险

安全场景

谨慎场景

十一、撤销最近一次本地提交,但保留代码

目标

方案一:保留在暂存区

方案二:保留在工作区,取消暂存

方案三:提交和代码一起丢弃

十二、已经推送到远程后,安全撤销某次提交

目标

查看提交历史

撤销指定提交

[reset 和 revert 的区别](#reset 和 revert 的区别)

十三、把远程最新代码同步到功能分支

目标

方案一:merge,安全直观

方案二:rebase,历史更线性

[rebase 的安全原则](#rebase 的安全原则)

[十四、解决 merge 或 rebase 冲突](#十四、解决 merge 或 rebase 冲突)

目标

第一步:查看冲突文件

第二步:打开文件解决冲突

第三步:标记冲突已经解决

[merge 冲突处理完成](#merge 冲突处理完成)

[rebase 冲突处理完成](#rebase 冲突处理完成)

[十五、rebase 后如何推送远程分支](#十五、rebase 后如何推送远程分支)

目标

推荐命令

--force

--force-with-lease

十六、只把另一个分支的某个提交拿过来

目标

找到目标提交

切换到目标分支

拿取指定提交

多个连续提交

冲突处理

十七、查看提交历史和某次代码变更

目标

简洁查看提交历史

图形化查看分支历史

查看某次提交详情

查看某个文件的提交历史

查看某一行是谁写的

十八、比较两个分支之间的差异

目标

查看代码差异

查看提交差异

只看修改文件统计

十九、删除已经合并完成的功能分支

目标

删除本地分支

删除远程分支

清理本地失效的远程分支引用

[二十、新建本地仓库并上传到 GitHub](#二十、新建本地仓库并上传到 GitHub)

目标

指令链

关键步骤解释

创建本地仓库

重命名当前分支

关联远程仓库

首次推送

二十一、修改远程仓库地址

目标

查看当前地址

修改地址

再次确认

删除远程关联

二十二、忽略配置文件和敏感信息

目标

[.gitignore 示例](#.gitignore 示例)

[已经被 Git 跟踪的文件,新增 .gitignore 不生效](#已经被 Git 跟踪的文件,新增 .gitignore 不生效)

重要提醒

[二十三、查看某个文件为什么没有被 Git 跟踪](#二十三、查看某个文件为什么没有被 Git 跟踪)

目标

检查命令

[二十四、误删分支或误 reset 后恢复代码](#二十四、误删分支或误 reset 后恢复代码)

目标

[查看 HEAD 移动记录](#查看 HEAD 移动记录)

创建恢复分支

二十五、清理未跟踪的临时文件

目标

删除未跟踪文件

同时删除未跟踪目录

[连 .gitignore 忽略的文件也删除](#连 .gitignore 忽略的文件也删除)

二十六、给正式版本打标签

目标

推荐使用附注标签

二十七、临时切换到历史提交查看旧代码

目标

查看历史

临时切换到指定提交

[二十八、二分查找定位引入 Bug 的提交](#二十八、二分查找定位引入 Bug 的提交)

目标

操作流程

二十九、提交前临时检查是否误提交敏感内容

目标

查看暂存文件列表

查看暂存内容

搜索敏感关键词

三十、后端程序员推荐的完整日常工作流

[1. 开始新功能](#1. 开始新功能)

[2. 开发过程中检查修改](#2. 开发过程中检查修改)

[3. 选择本次提交内容](#3. 选择本次提交内容)

[4. 检查待提交内容](#4. 检查待提交内容)

[5. 本地运行测试](#5. 本地运行测试)

[6. 创建提交](#6. 创建提交)

[7. 推送远程](#7. 推送远程)

[8. 开发期间同步 develop](#8. 开发期间同步 develop)

[9. 解决冲突后继续](#9. 解决冲突后继续)

[10. rebase 后更新远程分支](#10. rebase 后更新远程分支)

[11. PR 合并后清理](#11. PR 合并后清理)

[三十一、紧急线上 Bug 修复工作流](#三十一、紧急线上 Bug 修复工作流)

目标

指令链

三十二、常见错误场景速查

[1. 提交信息写错了,尚未 push](#1. 提交信息写错了,尚未 push)

[2. 漏提交一个文件,尚未 push](#2. 漏提交一个文件,尚未 push)

[3. git add 加多了](#3. git add 加多了)

[4. 文件改坏了,还没有 add](#4. 文件改坏了,还没有 add)

[5. 本地最后一次提交不想要了,但保留代码](#5. 本地最后一次提交不想要了,但保留代码)

[6. 已推送的提交需要撤销](#6. 已推送的提交需要撤销)

[7. 需要临时切换分支,代码未完成](#7. 需要临时切换分支,代码未完成)

[8. 拉取代码发生冲突](#8. 拉取代码发生冲突)

[9. 误 reset 或误删分支](#9. 误 reset 或误删分支)

[10. rebase 后 push 被拒绝](#10. rebase 后 push 被拒绝)

[三十三、建议配置的 Git 别名](#三十三、建议配置的 Git 别名)

三十四、后端开发中最需要掌握的命令优先级

第一层:必须熟练

第二层:必须会处理问题

第三层:提高开发效率

三十五、最终记住这几套最常用目标

新建功能分支

正常提交并推送

临时保存未完成代码

同步最新开发分支

撤销未提交修改

取消暂存

撤销本地提交但保留代码

撤销已经推送的提交

恢复误删提交


Git 的本质可以先压缩成一句话:

Git 是一个"给项目目录拍快照,并用指针管理这些快照"的版本管理系统。

它不是简单地"备份文件",也不只是"把代码上传到 GitHub"。


一、先把 Git 想成"游戏存档系统"

你写代码的过程类似玩游戏:

复制代码
写了一部分代码
↓
觉得当前状态没问题
↓
保存一个存档
↓
继续修改
↓
改坏了
↓
回到以前的存档

Git 中的"存档"就是:

复制代码
git commit

每执行一次 commit,Git 就记录一次项目状态。

例如:

复制代码
提交1:完成登录功能
提交2:完成图片上传功能
提交3:完成图片搜索功能

它们形成一条历史链:

复制代码
A ← B ← C

含义是:

  • A:第一次提交

  • B:基于 A 修改得到

  • C:基于 B 修改得到

  • 当前代码通常指向 C


二、Git 管理的是"快照",不是简单的修改记录

很多人会认为 Git 保存的是:

复制代码
第一行增加了什么
第二行删除了什么

从使用角度看,它确实可以展示差异,但 Git 的核心模型更接近:

每次提交,都保存当时整个项目的目录结构和文件内容快照。

例如第一次提交时:

复制代码
项目快照 A
├── UserController.java
├── UserService.java
└── application.yml

第二次提交时:

复制代码
项目快照 B
├── UserController.java
├── UserService.java
├── FileController.java
└── application.yml

不过 Git 不会粗暴地把完全相同的文件重复保存很多份。

如果两个提交里的 UserService.java 内容完全一样,Git 会复用原来的对象。

所以 Git 可以理解成:

复制代码
内容相同 → 复用
内容变化 → 保存新对象
提交对象 → 记录本次项目快照

三、Git 最重要的三个区域

这是理解 Git 的关键。

复制代码
工作区 → 暂存区 → 本地仓库

1. 工作区 Working Directory

就是你现在肉眼看到的项目文件。

例如:

java 复制代码
AIWear/
├── src/
├── pom.xml
└── README.md

你在 IDEA 中修改代码,修改的就是工作区。

例如你改了:

java 复制代码
public String login() {
    return "success";
}

此时 Git 知道文件变了,但还没有形成提交。

可以查看:

java 复制代码
git status

2. 暂存区 Staging Area

执行:

复制代码
git add .

就是把当前修改放入暂存区。

暂存区可以理解为:

准备进入下一次提交的文件清单和内容。

例如你修改了三个文件:

复制代码
UserController.java
UserService.java
README.md

但这次只想提交登录相关代码,不想提交 README。

可以执行:

复制代码
git add src/main/java/UserController.java
git add src/main/java/UserService.java

此时:

复制代码
工作区:
UserController.java 已修改
UserService.java 已修改
README.md 已修改

暂存区:
UserController.java
UserService.java

下一次 commit 只会提交暂存区里的内容。

因此:

复制代码
git add

不是"保存到 Git 历史",而是:

选择哪些内容进入下一次提交。


3. 本地仓库 Repository

执行:

复制代码
git commit -m "完成登录功能"

Git 才会真正生成一个提交记录。

这个提交保存在本机项目的:

复制代码
.git

目录中。

也就是说,即使没有网络,即使没有 GitHub,你照样可以:

复制代码
git add
git commit
git log
git reset
git checkout

因为 Git 本身就是本地版本管理工具。


四、为什么需要暂存区?

很多人第一次学 Git 时会觉得:

复制代码
为什么不能直接 commit?
为什么中间要多一个 add?

因为一次修改中,可能混杂了多件事情。

例如你今天同时做了:

复制代码
1. 修复登录 Bug
2. 修改 README
3. 调试时加入大量 println
4. 顺手改了图片上传逻辑

但你希望提交历史清晰:

复制代码
提交1:修复登录空指针问题
提交2:完善项目说明
提交3:优化图片上传异常处理

这时就可以使用暂存区分批提交。

例如:

复制代码
git add UserService.java
git commit -m "修复登录空指针问题"

然后:

复制代码
git add README.md
git commit -m "完善项目说明"

所以暂存区不是多余步骤,它承担的是:

组织提交边界。


五、一次完整操作到底发生了什么?

假设你修改了:

复制代码
UserController.java

第一步:修改文件

复制代码
工作区发生变化

此时:

复制代码
git status

可能显示:

复制代码
modified: UserController.java

状态可以理解为:

复制代码
工作区版本 ≠ 暂存区版本

第二步:执行 git add

复制代码
git add UserController.java

Git 会把该文件当前内容放进暂存区。

此时:

复制代码
工作区版本 = 暂存区版本
暂存区版本 ≠ 上一次提交版本

第三步:执行 git commit

复制代码
git commit -m "修改登录接口"

Git 根据暂存区内容生成一个新提交。

此时:

复制代码
暂存区版本 = 最新提交版本

如果提交后没有再修改文件:

复制代码
工作区 = 暂存区 = 最新提交

这时 git status 会显示:

复制代码
nothing to commit, working tree clean

六、commit 到底是什么?

一个 Git 提交并不只是提交说明。

一个 commit 大致包含:

复制代码
commit
├── 本次项目快照
├── 父提交是谁
├── 作者
├── 提交时间
└── 提交说明

例如:

复制代码
Commit C
├── 项目快照:第三版代码
├── parent:Commit B
├── author:小卢
├── time:2026-07-10
└── message:完成图片检索功能

因为每个提交都记录父提交,所以提交形成链:

复制代码
A ← B ← C ← D

其中 D 表示当前最新提交。


七、HEAD 是什么?

HEAD 是 Git 中一个非常重要的概念。

它可以理解为:

你当前所在的位置。

正常情况下:

复制代码
HEAD → main → Commit D

意思是:

  • 你当前在 main 分支

  • main 指向提交 D

  • HEAD 指向 main

图示:

复制代码
A ← B ← C ← D
              ↑
             main
              ↑
             HEAD

当你继续提交一次:

复制代码
git commit -m "新增功能"

就会变成:

复制代码
A ← B ← C ← D ← E
                  ↑
                 main
                  ↑
                 HEAD

本质上就是:

复制代码
创建新提交 E
main 指针从 D 移动到 E

八、分支的原理是什么?

很多人把分支想得很复杂。

实际上,Git 分支本质上只是:

一个指向某个提交的可移动指针。

例如当前:

复制代码
A ← B ← C
          ↑
         main

创建分支:

复制代码
git branch feature

结果:

复制代码
A ← B ← C
          ↑
         main
          ↑
        feature

此时两个分支都指向 C。

切换到 feature:

复制代码
git switch feature

然后提交两次:

复制代码
A ← B ← C ← D ← E
          ↑         ↑
         main    feature

注意:

  • main 仍然停在 C

  • feature 已经走到 E

  • 两个分支共享前面的 A、B、C

所以分支不是完整复制一份项目。

不是:

复制代码
复制一个新文件夹

而是:

复制代码
创建一个很轻量的提交指针

这也是 Git 创建分支非常快的原因。


九、切换分支为什么文件会变化?

假设:

复制代码
main → C
feature → E

执行:

复制代码
git switch main

Git 会做两件事:

复制代码
1. HEAD 指向 main
2. 工作区恢复为提交 C 对应的项目快照

所以你会发现文件内容变回 main 分支的状态。

再执行:

复制代码
git switch feature

工作区又会恢复成 E 的状态。

所以"切换分支"本质上是:

改变当前位置,并把工作区切换到对应提交的快照。


十、合并分支是什么原理?

假设:

复制代码
A ← B ← C
          ├── D ← E   feature
          └── F       main

feature 分支完成了功能,main 也有了自己的修改。

执行:

复制代码
git switch main
git merge feature

Git 会尝试把两条开发线合并:

复制代码
A ← B ← C
          ├── D ← E
          └── F ← M

M 是合并提交:

复制代码
M 的父提交有两个:
1. F
2. E

因此 merge 的本质是:

把两个分支的修改结果整合成一个新快照。


十一、冲突为什么会出现?

例如 main 分支将同一行改成:

复制代码
return "main";

feature 分支将同一行改成:

复制代码
return "feature";

Git 无法判断应该保留哪个,于是产生冲突:

复制代码
<<<<<<< HEAD
return "main";
=======
return "feature";
>>>>>>> feature

这不是 Git 出错,而是:

Git 无法代替开发者判断业务上哪个修改正确。

你手动修改成最终结果:

复制代码
return "success";

然后:

复制代码
git add .
git commit

冲突就解决了。


十二、GitHub 和 Git 到底什么关系?

这个必须分清。

Git

是安装在本机上的版本控制工具。

负责:

复制代码
记录版本
创建提交
创建分支
切换版本
合并代码
查看历史

GitHub

是互联网上的 Git 仓库托管平台。

负责:

复制代码
远程保存代码
多人协作
代码审查
Pull Request
Issues
Actions
项目展示

关系类似:

复制代码
Git = 本地版本管理工具
GitHub = 远程代码托管网站

没有 GitHub,Git 仍然能正常工作。

没有 Git,本地通常也无法完成规范的 GitHub 版本管理。


十三、push 的原理是什么?

本地有一条提交历史:

复制代码
A ← B ← C
          ↑
         main

GitHub 远程仓库可能只有:

复制代码
A ← B
      ↑
  origin/main

执行:

复制代码
git push origin main

就是把本地缺少的提交 C 上传到远程。

上传后:

复制代码
本地:
A ← B ← C
          ↑
         main

远程:
A ← B ← C
          ↑
    origin/main

这里:

  • origin:远程仓库的默认名称

  • main:分支名

  • origin/main:本地记录的远程 main 分支位置

所以 push 不是简单地"上传整个项目文件夹"。

它更准确的含义是:

将本地仓库中远程缺少的 Git 对象和提交同步到远程。


十四、pull 的原理是什么?

远程仓库比本地多了提交:

复制代码
远程:
A ← B ← C ← D

本地:
A ← B ← C

执行:

复制代码
git pull

大致相当于:

复制代码
git fetch
git merge

即:

复制代码
1. fetch:下载远程新提交
2. merge:合并到当前分支

所以:

复制代码
git pull

并不只是"下载代码",而是:

下载远程提交,并尝试合并到当前本地分支。


十五、clone 的原理是什么?

执行:

复制代码
git clone 仓库地址

Git 通常会:

复制代码
1. 下载远程仓库的提交历史
2. 下载文件内容对象
3. 创建本地 .git 仓库
4. 创建远程名称 origin
5. 检出默认分支
6. 生成工作区文件

所以 clone 不只是下载当前项目文件。

它通常还下载了项目的版本历史。

这也是为什么克隆完成后可以执行:

复制代码
git log
git branch
git reset

十六、reset、restore、revert 的区别

这三个命令经常混淆。

1. git restore

主要用于恢复文件。

例如放弃工作区修改:

复制代码
git restore UserController.java

意思是:

复制代码
不要当前工作区修改
恢复成暂存区或提交中的版本

2. git reset

主要用于移动分支指针、调整暂存区。

例如:

复制代码
git reset --soft HEAD~1

含义:

复制代码
撤销最近一次提交
但代码仍保留在暂存区

git reset --mixed HEAD~1

含义:

复制代码
撤销提交
代码保留在工作区
暂存区清空

git reset --hard HEAD~1

含义:

复制代码
提交撤销
暂存区撤销
工作区修改也丢弃

其中 --hard 风险最大。


3. git revert

不是删除旧提交,而是创建一个反向提交。

例如:

复制代码
A ← B ← C

C 有问题,执行:

复制代码
git revert C

得到:

复制代码
A ← B ← C ← D

D 的内容用于抵消 C 的修改。

所以:

复制代码
reset:改写历史、移动指针
revert:保留历史、增加反向提交

公共分支一般优先使用 revert


十七、.git 目录是什么?

执行:

复制代码
git init

项目中会出现:

复制代码
.git/

这就是 Git 仓库的核心数据库。

它里面大致保存:

复制代码
.git/
├── objects/       Git 对象
├── refs/          分支和标签指针
├── HEAD           当前所在分支
├── index          暂存区
├── config         仓库配置
└── logs/          操作记录

因此:

项目之所以是 Git 仓库,是因为存在 .git 目录。

如果删除 .git

复制代码
代码文件还在
但所有提交历史、分支、标签、仓库配置都会消失

项目就会变成普通文件夹。


十八、Git 底层保存的四类对象

从底层理解,Git 主要有四类对象。

1. blob

保存文件内容。

例如:

复制代码
public class User {}

Git 会根据内容生成哈希,并存储为 blob。


2. tree

保存目录结构。

例如:

复制代码
src/
├── User.java
└── File.java

tree 中记录:

复制代码
文件名 → blob
目录名 → tree

3. commit

保存一次提交。

它指向:

复制代码
项目根 tree
父 commit
作者
时间
提交信息

4. tag

通常用于给某个提交起一个固定名称。

例如:

复制代码
v1.0.0
v2.0.0

十九、为什么 Git 中经常看到一串哈希?

例如:

复制代码
a31f9c8

完整提交 ID 可能是:

复制代码
a31f9c87d0e9...

Git 会根据对象内容计算哈希值。

这个哈希可以理解为:

Git 对象的内容身份证。

内容改变,哈希通常也会改变。

因此:

复制代码
文件内容 → blob 哈希
目录结构 → tree 哈希
提交信息 → commit 哈希

提交中还包含父提交哈希,所以历史之间相互关联。


二十、用你实际的项目串起来

假设你的 AIWear 项目最开始是:

复制代码
提交 A:完成基础项目搭建

然后完成用户登录:

复制代码
提交 B:完成 JWT 登录鉴权

完成图片上传:

复制代码
提交 C:完成图片上传与 OSS 存储

完成图片检索:

复制代码
提交 D:完成 CLIP 图片检索

Git 历史:

复制代码
A ← B ← C ← D
              ↑
             main
              ↑
             HEAD

现在你想试验一个新功能,但怕改坏:

复制代码
git switch -c feature-image-replace

得到:

复制代码
A ← B ← C ← D
              ↑
             main
              ↑
   feature-image-replace
              ↑
             HEAD

你在新分支提交两次:

复制代码
A ← B ← C ← D
              ├── E ← F
              │       ↑
              │    feature-image-replace
              ↑
             main

测试成功后合并:

复制代码
git switch main
git merge feature-image-replace

这就是 Git 的典型工作方式:

复制代码
稳定版本留在 main
新功能在独立分支开发
完成后再合并

二十一、最核心的命令与原理对应

命令 本质
git init 创建 .git 本地仓库
git status 比较工作区、暂存区和最新提交
git add 将内容放入暂存区
git commit 基于暂存区创建新快照
git log 查看提交链
git branch 创建或查看提交指针
git switch 切换分支,并更新工作区
git merge 合并两条提交历史
git clone 复制远程仓库和历史到本地
git fetch 下载远程新提交,不自动合并
git pull 下载远程提交并合并
git push 上传本地新增提交
git restore 恢复文件内容
git reset 移动分支指针或重置暂存区
git revert 创建反向提交抵消旧提交

二十二、最后只记住这张图

复制代码
                    git push
本地仓库  ─────────────────────→  远程仓库 GitHub
   ↑                                  │
   │ git commit                       │ git fetch / pull
   │                                  ↓
暂存区
   ↑
   │ git add
   │
工作区

对应你的日常操作:

复制代码
# 1. 修改代码
工作区变化

# 2. 选择要提交的内容
git add .

# 3. 生成本地版本
git commit -m "完成某功能"

# 4. 上传到 GitHub
git push

最准确的理解是:

git add 是选择下一次快照内容,git commit 是在本地生成快照,git push 是把本地快照同步到远程。

而分支本质上只是指向某个提交的指针,Git 的整个历史就是由一个个 commit 连接起来的有向图。



后端程序员 Git 日常高频操作:按"目标"整理

日常开发中,不应该把 Git 命令孤立地记忆,而应该记成一套套工作流:

复制代码
我要完成什么目标
→ 当前处于什么状态
→ 依次执行哪些命令
→ 每个命令改变了什么

先记住 Git 的几个核心区域:

复制代码
工作区
  │ git add
  ▼
暂存区
  │ git commit
  ▼
本地仓库
  │ git push
  ▼
远程仓库

一、第一次把远程项目下载到本地

目标

公司已经有一个后端项目,例如:

复制代码
https://github.com/company/order-service.git

你要把它下载到本机,然后开始开发。

指令链

复制代码
git clone https://github.com/company/order-service.git

cd order-service

git status

git branch -a

git remote -v

每一步的含义

1. 克隆远程仓库

复制代码
git clone https://github.com/company/order-service.git

这条命令不只是下载文件,还会:

复制代码
1. 下载项目当前文件
2. 下载 Git 提交历史
3. 创建本地 .git 仓库
4. 创建远程仓库别名 origin
5. 自动检出默认分支

克隆完成后,一般会得到:

复制代码
order-service/
├── src/
├── pom.xml
├── README.md
└── .git/

2. 进入项目

复制代码
cd order-service

Git 命令通常必须在 Git 仓库目录或其子目录内执行。

3. 查看当前状态

复制代码
git status

可能输出:

复制代码
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean

含义:

复制代码
当前分支:main
本地 main 与远程 main 一致
工作区没有未提交修改

4. 查看所有分支

复制代码
git branch -a

其中:

复制代码
git branch

只看本地分支。

复制代码
git branch -r

只看远程分支。

复制代码
git branch -a

同时查看本地和远程分支。

5. 查看远程仓库地址

复制代码
git remote -v

可能输出:

复制代码
origin  https://github.com/company/order-service.git (fetch)
origin  https://github.com/company/order-service.git (push)

origin 是远程仓库的默认别名,不是固定关键字,只是约定俗成的名称。


二、每天开始开发前,同步远程最新代码

目标

上班后准备开发,但其他同事昨天可能已经向远程分支提交了新代码。

你需要先更新本地代码,避免基于旧版本继续开发。

推荐指令链

复制代码
git switch main

git status

git pull --ff-only origin main

如果团队主分支叫 develop

复制代码
git switch develop

git status

git pull --ff-only origin develop

每一步的含义

1. 切换到主开发分支

复制代码
git switch main

或者:

复制代码
git switch develop

switch 专门用于切换分支,比旧命令更清晰:

复制代码
git checkout main

旧命令仍然能用,但现在更推荐:

复制代码
git switch main

2. 确认工作区是否干净

复制代码
git status

如果存在未提交修改,直接切换或拉取代码可能产生冲突。

理想状态:

复制代码
nothing to commit, working tree clean

3. 拉取远程最新提交

复制代码
git pull --ff-only origin main

其中:

复制代码
git pull
≈
git fetch + git merge

--ff-only 表示:

只允许快进更新,不允许 Git 自动产生额外的合并提交。

假设:

复制代码
远程:A ← B ← C
本地:A ← B

拉取后:

复制代码
A ← B ← C
          ↑
         main

这叫 fast-forward,主分支历史比较干净。

如果本地和远程已经各自有新提交,--ff-only 会直接拒绝,而不是擅自合并。


三、从主分支创建一个功能分支

目标

你要开发一个新功能,例如:

复制代码
新增订单退款接口

不应该直接在 maindevelop 上乱改,而应该创建独立功能分支。

指令链

复制代码
git switch develop

git pull --ff-only origin develop

git switch -c feature/refund-api

含义

1. 先进入基准分支

复制代码
git switch develop

功能分支通常要从团队最新的开发分支创建。

2. 同步最新代码

复制代码
git pull --ff-only origin develop

保证新分支基于最新版本创建。

3. 创建并切换新分支

复制代码
git switch -c feature/refund-api

等价于:

复制代码
git branch feature/refund-api
git switch feature/refund-api

-c 表示 create。

创建前:

复制代码
A ← B ← C
          ↑
       develop

创建后:

复制代码
A ← B ← C
          ↑
       develop
          ↑
feature/refund-api
          ↑
         HEAD

分支本质上只是指向提交的指针,并不是复制一整份项目。


四、完成一次规范的代码提交

目标

你完成了退款接口相关开发,现在要提交代码。

推荐指令链

复制代码
git status

git diff

git add src/main/java/com/example/order/controller/RefundController.java
git add src/main/java/com/example/order/service/RefundService.java

git diff --cached

git commit -m "feat(order): add refund API"

git status

第一步:查看修改状态

复制代码
git status

它会告诉你:

复制代码
哪些文件被修改
哪些文件是新增文件
哪些文件已进入暂存区
哪些文件尚未暂存

例如:

复制代码
Changes not staged for commit:
  modified: RefundService.java

Untracked files:
  RefundController.java

常见状态

状态 含义
modified 已被 Git 管理的文件发生修改
untracked 新文件,Git 尚未跟踪
deleted 文件被删除
staged 已进入暂存区,准备提交

第二步:查看尚未暂存的修改

复制代码
git diff

它比较的是:

复制代码
工作区
vs
暂存区

也就是查看你改了什么,但还没有执行 git add 的内容。

如果只想查看某个文件:

复制代码
git diff src/main/java/com/example/order/service/RefundService.java

第三步:把本次需要提交的文件加入暂存区

复制代码
git add RefundController.java
git add RefundService.java

含义:

把这些文件当前版本放入暂存区,准备进入下一次提交。

不建议无脑执行:

复制代码
git add .

因为它可能把这些内容一起提交:

复制代码
调试日志
临时文件
IDE 配置
错误修改
敏感配置

不过在你确认整个工作区修改都属于同一个目标时,可以使用:

复制代码
git add .

或者:

复制代码
git add -A

区别通常不大,git add -A 会统一处理新增、修改和删除。


第四步:检查暂存区内容

复制代码
git diff --cached

或者:

复制代码
git diff --staged

两者等价。

它比较的是:

复制代码
暂存区
vs
上一次提交

这条命令非常重要,它回答的是:

我下一次 commit 到底会提交什么?


第五步:生成提交

复制代码
git commit -m "feat(order): add refund API"

提交后,Git 会基于暂存区创建一个新提交。

提交信息建议采用类似规范:

复制代码
类型(模块): 简要描述

常见类型:

类型 含义
feat 新功能
fix 修复 Bug
refactor 重构,不改变外部功能
docs 文档修改
test 测试代码
chore 构建、配置、依赖等杂项
perf 性能优化
style 格式调整,不涉及业务逻辑

示例:

复制代码
git commit -m "feat(user): add SMS login"
git commit -m "fix(order): prevent duplicate payment"
git commit -m "refactor(file): extract OSS upload service"
git commit -m "test(refund): add refund service unit tests"

五、把本地功能分支推送到远程

目标

本地已经有:

复制代码
feature/refund-api

现在要推送到远程,供其他人查看或创建 Pull Request。

第一次推送

复制代码
git push -u origin feature/refund-api

其中:

复制代码
origin

表示远程仓库。

复制代码
feature/refund-api

表示要推送的本地分支。

复制代码
-u

表示建立本地分支与远程分支的跟踪关系。

建立后,以后只需要:

复制代码
git push

不必每次都写:

复制代码
git push origin feature/refund-api

后续推送

复制代码
git push

注意:

复制代码
commit 只保存到本地
push 才会同步到远程

完整关系:

复制代码
改代码
→ git add
→ git commit
→ git push

六、开发中途临时保存修改,但暂时不想提交

目标

你正在开发退款功能,代码还没写完。

突然需要:

复制代码
切换到 main 修复线上紧急 Bug

当前代码又不适合提交。

这时使用 stash

指令链

复制代码
git status

git stash push -u -m "WIP: refund API"

git switch main

git pull --ff-only origin main

处理完其他任务后:

复制代码
git switch feature/refund-api

git stash list

git stash pop

stash 的含义

复制代码
git stash

相当于:

暂时把当前工作区修改收起来,让工作区恢复干净。

推荐写法

复制代码
git stash push -u -m "WIP: refund API"

参数含义:

复制代码
push:创建一个 stash
-u:连未跟踪的新文件一起保存
-m:添加说明
WIP:Work In Progress,表示尚未完成

如果不加 -u,新创建但未跟踪的文件可能不会进入 stash。

查看 stash

复制代码
git stash list

输出类似:

复制代码
stash@{0}: On feature/refund-api: WIP: refund API
stash@{1}: On feature/login: WIP: login validation

恢复并删除 stash

复制代码
git stash pop

含义:

复制代码
恢复最近一次 stash
并从 stash 列表中删除它

恢复但保留 stash

复制代码
git stash apply stash@{0}

apply 只恢复,不删除。

删除某个 stash

复制代码
git stash drop stash@{0}

删除所有 stash

复制代码
git stash clear

这条命令风险较高,执行后通常不容易恢复。


七、只暂存一个文件中的部分修改

目标

你在同一个文件中同时做了两件事:

复制代码
1. 修复空指针 Bug
2. 顺手重构了一段代码

你只想先提交 Bug 修复。

指令

复制代码
git add -p RefundService.java

-p 表示 patch,Git 会把修改分块展示,并询问:

复制代码
Stage this hunk [y,n,q,a,d,s,e,?]?

常见选项:

选项 含义
y 暂存这一块
n 不暂存这一块
q 退出
a 暂存当前文件后续所有修改块
d 不暂存当前文件后续所有修改块
s 把当前修改块进一步拆分
e 手工编辑修改块

然后检查:

复制代码
git diff --cached

再提交:

复制代码
git commit -m "fix(refund): handle null refund record"

这是组织高质量提交非常实用的命令。


八、撤销工作区中尚未暂存的修改

目标

你改坏了一个文件,而且还没有执行 git add

想恢复到修改之前。

撤销单个文件

复制代码
git restore RefundService.java

含义:

用暂存区中的版本覆盖工作区中的这个文件。

如果暂存区没有特殊版本,通常就会恢复到最近一次提交状态。

撤销全部未暂存修改

复制代码
git restore .

风险:

复制代码
工作区未暂存的修改会被直接丢弃

执行前先检查:

复制代码
git diff

九、撤销已经执行的 git add

目标

你误执行了:

复制代码
git add .

一些文件不应该进入下一次提交。

撤销单个文件暂存

复制代码
git restore --staged application.yml

含义:

复制代码
文件仍然保留在工作区
只是从暂存区移除

不会删除你的代码修改。

撤销全部暂存

复制代码
git restore --staged .

也可以使用旧写法:

复制代码
git reset HEAD .

推荐优先使用语义更清晰的:

复制代码
git restore --staged .

十、刚提交完,发现漏了一个文件

目标

你刚执行:

复制代码
git commit -m "feat(order): add refund API"

但发现漏提交了:

复制代码
RefundRequest.java

指令链

复制代码
git add RefundRequest.java

git commit --amend --no-edit

含义:

复制代码
把新增暂存内容补进最近一次提交
保留原提交信息不变

如果还想修改提交信息:

复制代码
git commit --amend -m "feat(order): add refund application API"

重要风险

--amend 不是修改原提交,而是创建一个新的提交替换它。

因此提交哈希会改变。

安全场景

复制代码
最近一次提交还没有 push

谨慎场景

复制代码
最近一次提交已经 push,且其他人可能已经基于它开发

公共历史尽量不要随意 amend。


十一、撤销最近一次本地提交,但保留代码

目标

刚刚提交错了,但还没有推送。

你希望:

复制代码
提交记录撤销
代码修改保留

方案一:保留在暂存区

复制代码
git reset --soft HEAD~1

结果:

复制代码
最近一次 commit 被撤销
代码仍然处于 staged 状态

适合:

复制代码
提交信息写错
需要重新组织提交
漏了一些文件

方案二:保留在工作区,取消暂存

复制代码
git reset HEAD~1

默认是 --mixed

复制代码
git reset --mixed HEAD~1

结果:

复制代码
提交撤销
暂存区清空
代码修改仍保留在工作区

方案三:提交和代码一起丢弃

复制代码
git reset --hard HEAD~1

结果:

复制代码
最近提交被撤销
暂存区内容丢弃
工作区修改也丢弃

这是危险命令。

执行前必须确认:

复制代码
git log --oneline -5
git status

十二、已经推送到远程后,安全撤销某次提交

目标

某个 Bug 提交已经推送到团队公共分支。

不能随意改写历史,需要生成一条"反向提交"。

查看提交历史

复制代码
git log --oneline

例如:

复制代码
9f21a6d fix(order): incorrect refund amount
73c0c3a feat(order): add refund API

撤销指定提交

复制代码
git revert 9f21a6d

Git 会创建一个新提交,抵消 9f21a6d 的修改。

历史由:

复制代码
A ← B ← C

变为:

复制代码
A ← B ← C ← D

其中 D 是对 C 的反向修改。

然后推送:

复制代码
git push

reset 和 revert 的区别

命令 原理 适用场景
reset 移动分支指针,可能改写历史 尚未共享的本地提交
revert 创建新提交抵消旧提交 已推送的公共提交

后端团队开发中:

已推送到公共分支的错误提交,优先使用 git revert


十三、把远程最新代码同步到功能分支

目标

你开发了几天,期间 develop 分支已经被其他人更新。

现在想把最新 develop 合入自己的功能分支。

有两种常见方案。


方案一:merge,安全直观

复制代码
git switch develop

git pull --ff-only origin develop

git switch feature/refund-api

git merge develop

如果没有冲突,可能生成合并提交:

复制代码
Merge branch 'develop' into feature/refund-api

优点:

复制代码
不改写现有历史
安全
容易理解
适合已推送、多人共同开发的分支

缺点:

复制代码
可能产生较多 merge commit
提交图相对复杂

方案二:rebase,历史更线性

复制代码
git switch develop

git pull --ff-only origin develop

git switch feature/refund-api

git rebase develop

假设原来:

复制代码
A ← B ← C ← D  develop
         \
          E ← F  feature

rebase 后:

复制代码
A ← B ← C ← D ← E' ← F'

Git 会把功能分支提交重新放到最新 develop 后面。

优点:

复制代码
提交历史线性、整洁

缺点:

复制代码
会重写 E、F,生成新的 E'、F'
提交哈希发生变化

rebase 的安全原则

适合:

复制代码
自己的私人功能分支
尚未被其他人基于其开发

谨慎:

复制代码
多人共享分支
公共主分支
已经被大量同事拉取的提交

十四、解决 merge 或 rebase 冲突

目标

同步代码时出现冲突。

例如:

复制代码
git merge develop

输出:

复制代码
CONFLICT (content): Merge conflict in RefundService.java

第一步:查看冲突文件

复制代码
git status

可能显示:

复制代码
both modified: RefundService.java

第二步:打开文件解决冲突

冲突标记类似:

复制代码
<<<<<<< HEAD
return refundRepository.findById(id);
=======
return refundRepository.findByOrderId(orderId);
>>>>>>> develop

含义:

复制代码
<<<<<<< HEAD
当前分支内容

=======
分隔线

>>>>>>> develop
被合并分支内容

你必须根据业务逻辑修改为最终结果,例如:

复制代码
return refundRepository.findByOrderId(orderId);

并删除所有冲突标记。

第三步:标记冲突已经解决

复制代码
git add RefundService.java

注意:这里的 git add 不只是"暂存",还表示:

这个冲突文件我已经人工处理完毕。


merge 冲突处理完成

复制代码
git commit

或者某些情况下:

复制代码
git merge --continue

如果不想继续合并:

复制代码
git merge --abort

它会尝试恢复到 merge 前的状态。


rebase 冲突处理完成

复制代码
git add RefundService.java

git rebase --continue

如果后续还有冲突,重复:

复制代码
修改文件
→ git add
→ git rebase --continue

放弃本次 rebase:

复制代码
git rebase --abort

跳过当前提交:

复制代码
git rebase --skip

--skip 可能丢掉当前提交的修改,不能随便使用。


十五、rebase 后如何推送远程分支

目标

你的功能分支已经推送过远程,后来又执行了:

复制代码
git rebase develop

因为提交哈希已经改变,普通 push 会被拒绝。

推荐命令

复制代码
git push --force-with-lease

不推荐:

复制代码
git push --force

区别:

--force

无条件强制覆盖远程分支。

可能把别人刚推送的提交覆盖掉。

--force-with-lease

只有远程分支仍然是你预期的状态时,才允许强制推送。

如果其他人已推送新提交,它会拒绝覆盖。

因此:

确实需要强推时,优先使用 --force-with-lease

仍然只适用于自己的功能分支,不要随便对公共分支使用。


十六、只把另一个分支的某个提交拿过来

目标

另一个分支修复了一个公共 Bug,但你不想合并整个分支,只想拿其中一个提交。

使用 cherry-pick

找到目标提交

复制代码
git log --oneline feature/payment-fix

假设目标提交是:

复制代码
a82c13f fix(payment): prevent duplicate callback

切换到目标分支

复制代码
git switch feature/refund-api

拿取指定提交

复制代码
git cherry-pick a82c13f

Git 会把该提交所包含的修改应用到当前分支,并生成一个新提交。

原提交和新提交内容相似,但哈希通常不同。

多个连续提交

复制代码
git cherry-pick a82c13f b93d128

或者一个区间:

复制代码
git cherry-pick a82c13f^..b93d128

冲突处理

复制代码
git status

解决冲突后:

复制代码
git add .
git cherry-pick --continue

放弃:

复制代码
git cherry-pick --abort

十七、查看提交历史和某次代码变更

目标

排查:

复制代码
这个接口什么时候改的?
谁改的?
哪次提交引入了 Bug?

简洁查看提交历史

复制代码
git log --oneline

图形化查看分支历史

复制代码
git log --oneline --graph --decorate --all

示例:

复制代码
* a93f2bd (HEAD -> feature/refund-api) feat: add refund API
| * 61f8a03 (develop) fix: update payment callback
|/
* 2ca31de init project

参数含义:

参数 含义
--oneline 每个提交显示一行
--graph 显示分支图
--decorate 显示分支、标签、HEAD
--all 查看所有分支

查看某次提交详情

复制代码
git show a93f2bd

它会展示:

复制代码
提交作者
提交时间
提交说明
具体代码差异

只看统计:

复制代码
git show --stat a93f2bd

查看某个文件的提交历史

复制代码
git log --oneline -- RefundService.java

查看文件每次具体变化:

复制代码
git log -p -- RefundService.java

查看某一行是谁写的

复制代码
git blame RefundService.java

输出会标记:

复制代码
每一行对应的提交
作者
时间

git blame 主要用于追踪代码来源,不应该被理解成"追责工具"。


十八、比较两个分支之间的差异

目标

你想知道:

复制代码
功能分支比 develop 多改了什么?

查看代码差异

复制代码
git diff develop...feature/refund-api

三个点 ... 通常表示:

从两个分支共同祖先开始,查看功能分支新增的变化。

如果当前就在功能分支:

复制代码
git diff develop...HEAD

查看提交差异

复制代码
git log --oneline develop..feature/refund-api

含义:

复制代码
查看 feature/refund-api 中有
但 develop 中没有的提交

只看修改文件统计

复制代码
git diff --stat develop...HEAD

输出类似:

复制代码
RefundController.java | 42 +++++++++
RefundService.java    | 81 +++++++++++++
2 files changed, 123 insertions(+)

十九、删除已经合并完成的功能分支

目标

Pull Request 已经合并,功能分支不再需要。

删除本地分支

先切换到其他分支:

复制代码
git switch develop

然后:

复制代码
git branch -d feature/refund-api

-d 是安全删除。

如果分支尚未合并,Git 会拒绝删除。

强制删除:

复制代码
git branch -D feature/refund-api

-D 会无视是否合并,风险更高。

删除远程分支

复制代码
git push origin --delete feature/refund-api

清理本地失效的远程分支引用

远程分支已经删除,但本地可能仍能看到:

复制代码
origin/feature/refund-api

执行:

复制代码
git fetch --prune

或者:

复制代码
git remote prune origin

二十、新建本地仓库并上传到 GitHub

目标

你本地已有一个 Spring Boot 项目,现在要纳入 Git 管理并推送远程。

指令链

复制代码
cd order-service

git init

git status

git add .gitignore

git add pom.xml
git add src/

git diff --cached

git commit -m "chore: initialize project"

git branch -M main

git remote add origin https://github.com/username/order-service.git

git push -u origin main

关键步骤解释

创建本地仓库

复制代码
git init

生成:

复制代码
.git/

从此该目录成为 Git 仓库。

重命名当前分支

复制代码
git branch -M main

-M 表示强制重命名当前分支为 main

关联远程仓库

复制代码
git remote add origin 远程地址

含义:

复制代码
给远程地址起别名 origin

查看:

复制代码
git remote -v

首次推送

复制代码
git push -u origin main

推送并建立跟踪关系。


二十一、修改远程仓库地址

目标

远程地址发生变化,例如:

复制代码
HTTPS 改 SSH
仓库迁移
GitHub 改 GitLab

查看当前地址

复制代码
git remote -v

修改地址

复制代码
git remote set-url origin git@github.com:username/order-service.git

再次确认

复制代码
git remote -v

删除远程关联

复制代码
git remote remove origin

注意:

复制代码
只删除本地对远程仓库的引用
不会删除远程仓库
也不会删除本地代码

二十二、忽略配置文件和敏感信息

目标

后端项目中这些内容不应该提交:

复制代码
数据库密码
Redis 密码
OSS Secret
JWT 密钥
IDE 配置
target 目录
日志文件
环境变量文件

.gitignore 示例

复制代码
# IDEA
.idea/
*.iml

# Java
target/
*.class
*.jar
*.log

# Maven
.mvn/timing.properties

# Sensitive configuration
application-local.yml
application-dev.yml
application-prod.yml
.env

# Runtime files
logs/
tmp/

# OS
.DS_Store
Thumbs.db

已经被 Git 跟踪的文件,新增 .gitignore 不生效

例如 application-dev.yml 已经提交过。

仅写:

复制代码
application-dev.yml

是不够的。

还需要:

复制代码
git rm --cached src/main/resources/application-dev.yml

含义:

复制代码
从 Git 跟踪中移除
但保留本地文件

然后提交:

复制代码
git add .gitignore

git commit -m "chore(config): stop tracking local configuration"

如果是整个目录:

复制代码
git rm -r --cached logs/

重要提醒

如果密码已经推送到远程,仅删除文件并不能真正消除泄漏风险,因为密码仍在 Git 历史中。

应立即:

复制代码
1. 作废旧密钥或修改密码
2. 更新配置
3. 必要时清理 Git 历史

首先应该轮换密钥,而不是只想着删除提交。


二十三、查看某个文件为什么没有被 Git 跟踪

目标

你执行:

复制代码
git add application-local.yml

但文件没有进入暂存区。

可能是被 .gitignore 忽略了。

检查命令

复制代码
git check-ignore -v src/main/resources/application-local.yml

它会告诉你:

复制代码
是哪一个 .gitignore
哪一条规则
忽略了该文件

例如:

复制代码
.gitignore:12:application-local.yml src/main/resources/application-local.yml

二十四、误删分支或误 reset 后恢复代码

目标

你误执行了:

复制代码
git reset --hard HEAD~2

或者误删了分支。

Git 中很多操作仍可通过 reflog 恢复。

查看 HEAD 移动记录

复制代码
git reflog

可能看到:

复制代码
c81d2a1 HEAD@{0}: reset: moving to HEAD~2
af92b31 HEAD@{1}: commit: feat: add refund API
7283ca0 HEAD@{2}: commit: feat: add refund validation

说明 reset 前的提交是:

复制代码
af92b31

创建恢复分支

复制代码
git switch -c recovery/refund af92b31

这是相对安全的做法,不直接覆盖当前分支。

确认代码正确后,再决定如何合并。

也可以直接恢复当前分支:

复制代码
git reset --hard af92b31

但应先确认没有其他需要保留的修改。

reflog 是后端开发中非常重要的"后悔药"。


二十五、清理未跟踪的临时文件

目标

编译、测试或脚本运行后产生了很多未跟踪文件。

先预览:

复制代码
git clean -n

或者:

复制代码
git clean --dry-run

只显示将被删除的文件,不真正删除。

删除未跟踪文件

复制代码
git clean -f

同时删除未跟踪目录

复制代码
git clean -fd

.gitignore 忽略的文件也删除

复制代码
git clean -fdx

这条命令极其危险,可能删除:

复制代码
本地 application-local.yml
.env
IDE 文件
构建产物
本地测试数据

因此日常开发一般不要随便使用:

复制代码
git clean -fdx

必须先执行:

复制代码
git clean -ndx

进行预览。


二十六、给正式版本打标签

目标

后端服务正式发布,例如:

复制代码
v1.2.0

需要给某个提交打标签,方便部署和回滚。

推荐使用附注标签

复制代码
git tag -a v1.2.0 -m "release v1.2.0"

查看标签:

复制代码
git tag

查看标签详情:

复制代码
git show v1.2.0

推送一个标签:

复制代码
git push origin v1.2.0

推送所有标签:

复制代码
git push origin --tags

删除本地标签:

复制代码
git tag -d v1.2.0

删除远程标签:

复制代码
git push origin --delete v1.2.0

标签和分支的区别:

复制代码
分支:通常会随着提交继续移动
标签:通常固定指向某个版本提交

二十七、临时切换到历史提交查看旧代码

目标

你想查看旧版本的实现,但不打算立即修改。

查看历史

复制代码
git log --oneline

临时切换到指定提交

复制代码
git switch --detach a82c13f

此时进入 detached HEAD 状态:

复制代码
HEAD 直接指向某个提交
而不是指向某个分支

适合:

复制代码
查看旧代码
运行旧版本
排查历史问题

查看完成后返回:

复制代码
git switch develop

如果在 detached HEAD 状态下产生了新提交,应该及时创建分支保存:

复制代码
git switch -c fix/from-old-version

否则这些提交未来可能难以找到。


二十八、二分查找定位引入 Bug 的提交

目标

你知道:

复制代码
当前版本有 Bug
某个旧版本没有 Bug

但中间有大量提交,不知道是哪一次引入的。

使用:

复制代码
git bisect

操作流程

开始:

复制代码
git bisect start

标记当前版本是坏的:

复制代码
git bisect bad

标记某个旧提交是好的:

复制代码
git bisect good a82c13f

Git 会自动切换到中间提交。

你运行测试后,如果该提交正常:

复制代码
git bisect good

如果存在 Bug:

复制代码
git bisect bad

反复几次,Git 会定位第一个坏提交。

结束:

复制代码
git bisect reset

这相当于对提交历史做二分查找。

适合排查:

复制代码
接口何时开始报错
性能回退从哪次提交开始
某个单元测试从哪次开始失败
配置兼容性问题由哪个提交引入

二十九、提交前临时检查是否误提交敏感内容

目标

提交 Spring Boot 项目前,检查是否包含密码、密钥和环境配置。

查看暂存文件列表

复制代码
git diff --cached --name-only

查看暂存内容

复制代码
git diff --cached

搜索敏感关键词

在 Git Bash 中可以使用:

复制代码
git diff --cached | grep -Ei "password|secret|token|api[_-]?key|access[_-]?key"

也可以查看当前已跟踪文件:

复制代码
git ls-files

重点检查:

复制代码
application.yml
application-dev.yml
.env
docker-compose.yml
数据库初始化脚本
私钥文件
云服务配置

三十、后端程序员推荐的完整日常工作流

假设团队使用:

复制代码
主开发分支:develop
功能分支:feature/xxx

1. 开始新功能

复制代码
git switch develop

git pull --ff-only origin develop

git switch -c feature/refund-api

2. 开发过程中检查修改

复制代码
git status

git diff

3. 选择本次提交内容

复制代码
git add src/main/java/com/example/order/controller/RefundController.java
git add src/main/java/com/example/order/service/RefundService.java
git add src/test/java/com/example/order/RefundServiceTest.java

4. 检查待提交内容

复制代码
git diff --cached

5. 本地运行测试

Maven 项目:

复制代码
mvn test

或者 Maven Wrapper:

复制代码
./mvnw test

Windows:

复制代码
mvnw.cmd test

6. 创建提交

复制代码
git commit -m "feat(order): add refund application API"

7. 推送远程

第一次:

复制代码
git push -u origin feature/refund-api

后续:

复制代码
git push

8. 开发期间同步 develop

复制代码
git fetch origin

git rebase origin/develop

或者使用 merge:

复制代码
git merge origin/develop

9. 解决冲突后继续

rebase:

复制代码
git add .
git rebase --continue

merge:

复制代码
git add .
git commit

10. rebase 后更新远程分支

复制代码
git push --force-with-lease

11. PR 合并后清理

复制代码
git switch develop

git pull --ff-only origin develop

git branch -d feature/refund-api

git fetch --prune

如果远程没有自动删除分支:

复制代码
git push origin --delete feature/refund-api

三十一、紧急线上 Bug 修复工作流

目标

线上出现支付回调重复处理问题,需要从正式分支快速修复。

假设正式分支是 main

指令链

复制代码
git switch main

git pull --ff-only origin main

git switch -c hotfix/payment-idempotency

修改代码后:

复制代码
git status

git diff

git add src/main/java/com/example/payment/PaymentCallbackService.java

git diff --cached

mvn test

git commit -m "fix(payment): make callback processing idempotent"

git push -u origin hotfix/payment-idempotency

合并上线后,需要把修复同步回开发分支:

复制代码
git switch develop

git pull --ff-only origin develop

git merge main

git push origin develop

或者把修复提交 cherry-pick 到 develop:

复制代码
git switch develop

git pull --ff-only origin develop

git cherry-pick <hotfix提交哈希>

git push

具体使用 merge 还是 cherry-pick,应遵循团队分支策略。


三十二、常见错误场景速查

1. 提交信息写错了,尚未 push

复制代码
git commit --amend -m "正确的提交信息"

2. 漏提交一个文件,尚未 push

复制代码
git add 漏掉的文件
git commit --amend --no-edit

3. git add 加多了

复制代码
git restore --staged 不该提交的文件

4. 文件改坏了,还没有 add

复制代码
git restore 文件名

5. 本地最后一次提交不想要了,但保留代码

复制代码
git reset HEAD~1

6. 已推送的提交需要撤销

复制代码
git revert 提交哈希
git push

7. 需要临时切换分支,代码未完成

复制代码
git stash push -u -m "WIP"
git switch 其他分支

回来后:

复制代码
git switch 原分支
git stash pop

8. 拉取代码发生冲突

复制代码
git status

人工处理后:

复制代码
git add 冲突文件
git commit

或者 rebase 情况:

复制代码
git add 冲突文件
git rebase --continue

9. 误 reset 或误删分支

复制代码
git reflog
git switch -c recovery 分支丢失前的提交哈希

10. rebase 后 push 被拒绝

复制代码
git push --force-with-lease

仅限明确知道自己正在重写个人功能分支历史时使用。


三十三、建议配置的 Git 别名

高频命令可以配置别名:

复制代码
git config --global alias.st status
git config --global alias.sw switch
git config --global alias.br branch
git config --global alias.co checkout
git config --global alias.cm commit
git config --global alias.lg "log --oneline --graph --decorate --all"
git config --global alias.unstage "restore --staged"

配置后:

复制代码
git st
git sw develop
git lg
git unstage application.yml

查看全局配置:

复制代码
git config --global --list

设置用户名和邮箱:

复制代码
git config --global user.name "Your Name"
git config --global user.email "your@email.com"

三十四、后端开发中最需要掌握的命令优先级

第一层:必须熟练

复制代码
git status
git diff
git add
git commit
git push
git pull
git switch
git branch
git log

这是正常开发的基础闭环。

第二层:必须会处理问题

复制代码
git restore
git reset
git revert
git stash
git merge
git rebase
git reflog

这是处理误操作、冲突和分支同步的核心。

第三层:提高开发效率

复制代码
git add -p
git cherry-pick
git blame
git bisect
git clean
git tag

这是代码审查、问题定位和发布管理能力。


三十五、最终记住这几套最常用目标

新建功能分支

复制代码
git switch develop
git pull --ff-only origin develop
git switch -c feature/xxx

正常提交并推送

复制代码
git status
git diff
git add 指定文件
git diff --cached
git commit -m "feat(module): description"
git push

临时保存未完成代码

复制代码
git stash push -u -m "WIP: xxx"
git switch 其他分支

恢复:

复制代码
git switch 原分支
git stash pop

同步最新开发分支

复制代码
git fetch origin
git rebase origin/develop

或者:

复制代码
git merge origin/develop

撤销未提交修改

复制代码
git restore 文件名

取消暂存

复制代码
git restore --staged 文件名

撤销本地提交但保留代码

复制代码
git reset HEAD~1

撤销已经推送的提交

复制代码
git revert 提交哈希
git push

恢复误删提交

复制代码
git reflog
git switch -c recovery 提交哈希

Git 日常操作的核心不是背几十条命令,而是始终先判断这三个问题:

复制代码
1. 我的修改现在位于工作区、暂存区,还是已经 commit?
2. 这个提交是否已经 push,被其他人共享?
3. 我是想保留代码、丢弃代码,还是仅修改提交历史?

只要这三个问题判断清楚,绝大多数 Git 命令就不会用错。

相关推荐
深海鱼在掘金5 小时前
深入浅出RAG——第1章:认识RAG
人工智能·搜索引擎
远光九天5 小时前
远光软件亮相2026 CIO百人会高峰论坛 分享“模数共振”新范式构建之路
大数据·人工智能
LitchiCheng6 小时前
DGX Spark部署Isaac Sim(一键安装脚本)
大数据·elasticsearch·spark
RockHopper20257 小时前
企业智能体为何应面向“活动”而非“数据”——一个架构范式的转变
大数据·人工智能·智能体·活动驱动
JustNow_Man7 小时前
【openspec】存量项目中如何使用进行开发
大数据·人工智能
Geeys7 小时前
拼多多新店快速起流量完整实操
大数据·网络·人工智能
逸Y 仙X8 小时前
Spark部署
大数据·分布式·spark
Elasticsearch9 小时前
Semantic_text 字段默认嵌入模型变为 .jina-embeddings-v5-text-small
elasticsearch
BD_Marathon9 小时前
什么是Spark
大数据·分布式·spark