Day05_Git 版本控制完全指南:从入门到精通的专业实践

面向前端团队的 Git 实战博客:覆盖初始化、回滚、忽略规则、分支协作、远程仓库与 GitFlow。理论参考 Pro GitGit 官方文档GitHub Docs

目录

  • [0. 知识脉络与名词百科](#0. 知识脉络与名词百科)
    • [0.1 本章知识图谱](#0.1 本章知识图谱)
    • [0.2 核心名词解析](#0.2 核心名词解析)
  • [1. Git 基础回顾](#1. Git 基础回顾)
    • [1.1 核心概念解析](#1.1 核心概念解析)
    • [1.3 基础命令速查 · Git 配置三层优先级](#1.3 基础命令速查 · Git 配置三层优先级)
    • [1.4 差异查看与撤销操作](#1.4 差异查看与撤销操作)
    • [1.5 Git 内部对象模型](#1.5 Git 内部对象模型)
  • [2. 版本管理与回滚](#2. 版本管理与回滚)
    • [2.1 查看历史版本 · git log -S 代码考古](#2.1 查看历史版本 · git log -S 代码考古)
    • [2.2 版本回滚操作](#2.2 版本回滚操作)
    • [2.3 reset 三种模式](#2.3 reset 三种模式)
    • [2.4 git revert:安全撤销](#2.4 git revert:安全撤销)
    • [2.5 标签管理](#2.5 标签管理)
  • [3. Git 忽略文件机制](#3. Git 忽略文件机制)
  • [4. Git 分支管理策略](#4. Git 分支管理策略)
    • [4.6 git rebase:变基操作](#4.6 git rebase:变基操作)
    • [4.7 git cherry-pick:精准摘取](#4.7 git cherry-pick:精准摘取)
    • [4.8 Detached HEAD 状态解析](#4.8 Detached HEAD 状态解析)
  • [5. 远程仓库与协作开发](#5. 远程仓库与协作开发)
    • [5.3 git fetch vs git pull(重要)](#5.3 git fetch vs git pull(重要))
    • [5.4 场景化操作流程](#5.4 场景化操作流程)
    • [5.7 SSH 密钥免密登录](#5.7 SSH 密钥免密登录)
  • [6. 企业级 Git 工作流](#6. 企业级 Git 工作流)
    • [6.1 GitFlow](#6.1 GitFlow)
    • [6.2 GitHub Flow](#6.2 GitHub Flow)
    • [6.3 GitLab Flow](#6.3 GitLab Flow)
    • [6.4 Trunk-Based Development(新增)](#6.4 Trunk-Based Development(新增))
  • [7. Git 高级技巧与最佳实践](#7. Git 高级技巧与最佳实践)
    • [7.1 高级命令 · git stash 进阶](#7.1 高级命令 · git stash 进阶)
    • [7.2 Git Hooks 配置](#7.2 Git Hooks 配置)
    • [7.3 git bisect · git worktree](#7.3 git bisect · git worktree)
    • [7.4 git blame:代码考古(新增)](#7.4 git blame:代码考古(新增))
    • [7.5 git submodule:子模块(新增)](#7.5 git submodule:子模块(新增))
    • [7.6 团队协作最佳实践](#7.6 团队协作最佳实践)
  • [8. 故障排除与问题解决](#8. 故障排除与问题解决)
  • [9. 总结与展望](#9. 总结与展望)
  • [附录 A:命令与场景速查(扩充)](#附录 A:命令与场景速查(扩充))
  • [附录 B:官方与延伸阅读](#附录 B:官方与延伸阅读)

0. 知识脉络与名词百科

0.1 本章知识图谱

#mermaid-svg-K4hWQU7FWCzbkLb7{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-K4hWQU7FWCzbkLb7 .error-icon{fill:#552222;}#mermaid-svg-K4hWQU7FWCzbkLb7 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-K4hWQU7FWCzbkLb7 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-K4hWQU7FWCzbkLb7 .marker.cross{stroke:#333333;}#mermaid-svg-K4hWQU7FWCzbkLb7 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-K4hWQU7FWCzbkLb7 p{margin:0;}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge{stroke-width:3;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section--1 rect,#mermaid-svg-K4hWQU7FWCzbkLb7 .section--1 path,#mermaid-svg-K4hWQU7FWCzbkLb7 .section--1 circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .section--1 polygon,#mermaid-svg-K4hWQU7FWCzbkLb7 .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .section--1 text{fill:#ffffff;}#mermaid-svg-K4hWQU7FWCzbkLb7 .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge-depth--1{stroke-width:17;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:lightgray;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:#efefef;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-0 rect,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-0 path,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-0 circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-0 polygon,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-0 text{fill:black;}#mermaid-svg-K4hWQU7FWCzbkLb7 .node-icon-0{font-size:40px;color:black;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge-depth-0{stroke-width:14;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:lightgray;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:#efefef;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-1 rect,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-1 path,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-1 circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-1 polygon,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-1 text{fill:black;}#mermaid-svg-K4hWQU7FWCzbkLb7 .node-icon-1{font-size:40px;color:black;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge-depth-1{stroke-width:11;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:lightgray;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:#efefef;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-2 rect,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-2 path,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-2 circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-2 polygon,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-2 text{fill:#ffffff;}#mermaid-svg-K4hWQU7FWCzbkLb7 .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge-depth-2{stroke-width:8;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:lightgray;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:#efefef;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-3 rect,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-3 path,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-3 circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-3 polygon,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-3 text{fill:black;}#mermaid-svg-K4hWQU7FWCzbkLb7 .node-icon-3{font-size:40px;color:black;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge-depth-3{stroke-width:5;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:lightgray;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:#efefef;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-4 rect,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-4 path,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-4 circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-4 polygon,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-4 text{fill:black;}#mermaid-svg-K4hWQU7FWCzbkLb7 .node-icon-4{font-size:40px;color:black;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge-depth-4{stroke-width:2;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:lightgray;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:#efefef;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-5 rect,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-5 path,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-5 circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-5 polygon,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-5 text{fill:black;}#mermaid-svg-K4hWQU7FWCzbkLb7 .node-icon-5{font-size:40px;color:black;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge-depth-5{stroke-width:-1;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:lightgray;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:#efefef;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-6 rect,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-6 path,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-6 circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-6 polygon,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-6 text{fill:black;}#mermaid-svg-K4hWQU7FWCzbkLb7 .node-icon-6{font-size:40px;color:black;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge-depth-6{stroke-width:-4;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:lightgray;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:#efefef;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-7 rect,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-7 path,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-7 circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-7 polygon,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-7 text{fill:black;}#mermaid-svg-K4hWQU7FWCzbkLb7 .node-icon-7{font-size:40px;color:black;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge-depth-7{stroke-width:-7;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:lightgray;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:#efefef;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-8 rect,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-8 path,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-8 circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-8 polygon,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-8 text{fill:black;}#mermaid-svg-K4hWQU7FWCzbkLb7 .node-icon-8{font-size:40px;color:black;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge-depth-8{stroke-width:-10;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:lightgray;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:#efefef;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-9 rect,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-9 path,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-9 circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-9 polygon,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-9 text{fill:black;}#mermaid-svg-K4hWQU7FWCzbkLb7 .node-icon-9{font-size:40px;color:black;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge-depth-9{stroke-width:-13;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:lightgray;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:#efefef;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-10 rect,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-10 path,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-10 circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-10 polygon,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-10 text{fill:black;}#mermaid-svg-K4hWQU7FWCzbkLb7 .node-icon-10{font-size:40px;color:black;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge-depth-10{stroke-width:-16;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:lightgray;}#mermaid-svg-K4hWQU7FWCzbkLb7 .disabled text{fill:#efefef;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-root rect,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-root path,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-root circle,#mermaid-svg-K4hWQU7FWCzbkLb7 .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-root text{fill:#ffffff;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-root span{color:#ffffff;}#mermaid-svg-K4hWQU7FWCzbkLb7 .section-2 span{color:#ffffff;}#mermaid-svg-K4hWQU7FWCzbkLb7 .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-K4hWQU7FWCzbkLb7 .edge{fill:none;}#mermaid-svg-K4hWQU7FWCzbkLb7 .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-K4hWQU7FWCzbkLb7 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Git 完全指南
基础
init add commit status
diff restore
config 三层优先级
版本
log reflog
log -S Pickaxe 考古
reset soft mixed hard
revert 安全撤销
tag 语义化版本
忽略
gitignore 语法
rm --cached
check-ignore 调试
分支
branch switch merge
rebase 变基
cherry-pick 摘取
Detached HEAD
冲突解决
远程
fetch vs pull 区别
remote push clone
SSH 免密
submodule 子模块
工作流
GitFlow
GitHub Flow
GitLab Flow
Trunk-Based Dev
高级
stash 储藏
bisect 二分调试
worktree 多工作区
blame 代码考古
Hooks 质量门禁

0.2 核心名词解析

名词 英文 解析
Commit 提交 将暂存区快照写入本地对象库,生成唯一 SHA-1 哈希
HEAD HEAD 当前检出分支/提交的指针
Index 暂存区 git add 后的中间层,介于工作区与版本库之间
Reflog 引用日志 记录 HEAD 移动历史,误操作恢复的「黑匣子」
Fast-forward 快进合并 目标分支直接移动到源分支顶端,无分叉
Upstream 上游分支 git push -u 建立的本地与远程跟踪关系
Origin 默认远程名 git clone 自动创建的远程别名
Pull Request 合并请求 GitHub/GitLab 上请求将功能分支并入主线的协作方式
Blob 对象 Git 存储文件内容的最小单元,不含文件名
Tree 树对象 记录目录结构与 Blob 对应关系的对象
Tag 标签 指向某次提交的永久性引用,常用于版本发布
Stash 储藏 临时保存未完成的工作区改动,方便切换任务
Rebase 变基 将提交序列移植到另一个基点,产生线性历史
Cherry-pick 摘取 将指定提交的改动应用到当前分支
Detached HEAD 游离 HEAD HEAD 直接指向某个提交而非分支时的状态

1. Git基础回顾

Git是目前世界上最先进的分布式版本控制系统,由Linux之父Linus Torvalds于2005年创建。它不仅用于代码管理,还广泛应用于各种需要版本控制的领域。

1.1 核心概念解析

【名词解释】

  • 版本控制系统 (VCS):记录文件内容变化,以便将来查阅特定版本修订情况的系统
  • 分布式版本控制:每个客户端都拥有完整的代码仓库副本,可以离线工作
  • 工作区:当前看到的文件内容
  • 暂存区:准备下次提交的文件索引
  • 版本库:Git保存项目元数据和对象数据库的地方

1.2 Git工作流程图

#mermaid-svg-NH6NJ0b31oSZo1BN{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NH6NJ0b31oSZo1BN .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NH6NJ0b31oSZo1BN .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NH6NJ0b31oSZo1BN .error-icon{fill:#552222;}#mermaid-svg-NH6NJ0b31oSZo1BN .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NH6NJ0b31oSZo1BN .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NH6NJ0b31oSZo1BN .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NH6NJ0b31oSZo1BN .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NH6NJ0b31oSZo1BN .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NH6NJ0b31oSZo1BN .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NH6NJ0b31oSZo1BN .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NH6NJ0b31oSZo1BN .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NH6NJ0b31oSZo1BN .marker.cross{stroke:#333333;}#mermaid-svg-NH6NJ0b31oSZo1BN svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NH6NJ0b31oSZo1BN p{margin:0;}#mermaid-svg-NH6NJ0b31oSZo1BN .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NH6NJ0b31oSZo1BN .cluster-label text{fill:#333;}#mermaid-svg-NH6NJ0b31oSZo1BN .cluster-label span{color:#333;}#mermaid-svg-NH6NJ0b31oSZo1BN .cluster-label span p{background-color:transparent;}#mermaid-svg-NH6NJ0b31oSZo1BN .label text,#mermaid-svg-NH6NJ0b31oSZo1BN span{fill:#333;color:#333;}#mermaid-svg-NH6NJ0b31oSZo1BN .node rect,#mermaid-svg-NH6NJ0b31oSZo1BN .node circle,#mermaid-svg-NH6NJ0b31oSZo1BN .node ellipse,#mermaid-svg-NH6NJ0b31oSZo1BN .node polygon,#mermaid-svg-NH6NJ0b31oSZo1BN .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NH6NJ0b31oSZo1BN .rough-node .label text,#mermaid-svg-NH6NJ0b31oSZo1BN .node .label text,#mermaid-svg-NH6NJ0b31oSZo1BN .image-shape .label,#mermaid-svg-NH6NJ0b31oSZo1BN .icon-shape .label{text-anchor:middle;}#mermaid-svg-NH6NJ0b31oSZo1BN .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NH6NJ0b31oSZo1BN .rough-node .label,#mermaid-svg-NH6NJ0b31oSZo1BN .node .label,#mermaid-svg-NH6NJ0b31oSZo1BN .image-shape .label,#mermaid-svg-NH6NJ0b31oSZo1BN .icon-shape .label{text-align:center;}#mermaid-svg-NH6NJ0b31oSZo1BN .node.clickable{cursor:pointer;}#mermaid-svg-NH6NJ0b31oSZo1BN .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NH6NJ0b31oSZo1BN .arrowheadPath{fill:#333333;}#mermaid-svg-NH6NJ0b31oSZo1BN .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NH6NJ0b31oSZo1BN .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NH6NJ0b31oSZo1BN .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NH6NJ0b31oSZo1BN .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NH6NJ0b31oSZo1BN .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NH6NJ0b31oSZo1BN .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NH6NJ0b31oSZo1BN .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NH6NJ0b31oSZo1BN .cluster text{fill:#333;}#mermaid-svg-NH6NJ0b31oSZo1BN .cluster span{color:#333;}#mermaid-svg-NH6NJ0b31oSZo1BN div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NH6NJ0b31oSZo1BN .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NH6NJ0b31oSZo1BN rect.text{fill:none;stroke-width:0;}#mermaid-svg-NH6NJ0b31oSZo1BN .icon-shape,#mermaid-svg-NH6NJ0b31oSZo1BN .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NH6NJ0b31oSZo1BN .icon-shape p,#mermaid-svg-NH6NJ0b31oSZo1BN .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NH6NJ0b31oSZo1BN .icon-shape .label rect,#mermaid-svg-NH6NJ0b31oSZo1BN .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NH6NJ0b31oSZo1BN .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NH6NJ0b31oSZo1BN .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NH6NJ0b31oSZo1BN :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} git add
git commit
git push
git pull/clone
git checkout/reset
git restore --staged
工作区
暂存区
本地版本库
远程仓库

1.3 基础命令速查

bash 复制代码
# ── 用户信息配置(每台机器首次使用 Git 时执行一次即可)──
# --global:写入 ~/.gitconfig,对本机所有仓库生效;单仓库可用 --local 覆盖
git config --global user.name '开发者用户名'   # 显示在 git log / GitHub 贡献图
git config --global user.email 'user@example.com'  # 须与托管平台账号邮箱一致,否则贡献不计入

# ── 仓库生命周期:在工作区 → 暂存区 → 本地版本库 之间移动文件状态 ──
git init                    # 当前目录生成 .git/,建立空仓库;已有仓库勿重复 init
git status                  # 列出未跟踪/已修改/已暂存文件;操作前先看,避免误提交
git add <file>              # 将指定文件快照写入暂存区(Index),可多次 add 再一次性 commit
git add -A                  # 暂存工作区全部变更(含删除);等价于 git add --all
git commit -m '描述信息'    # 将暂存区内容固化为不可变提交对象,生成 40 位 SHA-1 哈希

【代码注释】git config --global 写入 ~/.gitconfig,对本机所有仓库生效;user.name / user.email 会写入每次 commit 的 author 字段,须与 GitHub 账号邮箱一致贡献图才统计。git init 仅在空目录或新项目执行一次;git status 是操作前「仪表盘」;git add 只影响暂存区,未 add 的改动不会进入 commit;git commit 生成不可变快照,message 建议用约定式提交(如 feat:fix:)。

Git 配置三层优先级(理论精讲)

Git 配置分三个作用域,优先级由高到低

层级 存储位置 作用范围 命令参数
local(仓库级) .git/config 仅当前仓库 git config --local / 默认
global(用户级) ~/.gitconfig 本机所有仓库 git config --global
system(系统级) /etc/gitconfig 机器上所有用户 git config --system(需 root)
bash 复制代码
# ── 配置层级查看与管理 ──

# 查看某个配置的最终生效值(以及来自哪个文件)
git config --show-origin user.email

# 查看某层的全部配置(三选一)
git config --global --list
git config --local  --list
git config --system --list

# 典型场景:公司机器全局用公司邮箱,开源项目单独用个人邮箱
cd ~/open-source/my-lib
git config --local user.email "personal@example.com"   # 只影响该仓库
git config --local user.name  "个人昵称"

# 常用全局配置(开箱即用推荐组合)
git config --global core.autocrlf input           # macOS/Linux:提交时统一 LF
git config --global pull.rebase true              # git pull 默认以 rebase 代替 merge
git config --global init.defaultBranch main       # 新仓库默认主分支名用 main
git config --global core.editor "code --wait"     # 用 VS Code 作为提交说明编辑器
git config --global merge.conflictstyle diff3     # 冲突标记显示三方(含共同祖先),更易判断

# 查看当前生效的完整配置(含继承)
git config --list --show-origin

【代码注释】 :local 覆盖 global 覆盖 system------每层配置文件都是 INI 格式,可直接用文本编辑器修改。pull.rebase true 使 git pull 默认行为变为 fetch + rebase,保持线性历史、减少「merge bubble」。core.autocrlf=input 确保仓库中统一存储 LF,避免 Windows/Mac 混团队时的换行符噪音。merge.conflictstyle diff3 在冲突标记中额外显示公共祖先版本,让判断该保留哪侧逻辑更直观。

1.4 差异查看与撤销操作

addcommit 之间,常用 diff 查看改动、用 restore 撤销误操作(Git 2.23+ 推荐,语义比 checkout 更清晰)。

bash 复制代码
# ── 差异对比:弄清「改在哪一层」再决定撤销策略 ──
# 对比:工作区 vs 暂存区(有修改但尚未 git add 时才有输出)
git diff

# 对比:暂存区 vs 最后一次 commit(已 add、未 commit;Code Review 提交前必看)
git diff --cached
# 同义写法:git diff --staged

# ── 撤销工作区改动(仅影响 Working Tree,暂存区与历史不变)──
git restore 文件名          # 单文件:丢弃未 add 的修改,恢复为暂存区/HEAD 版本
git restore .               # 批量:当前目录下所有已跟踪文件的未暂存改动一并丢弃

# ── 撤销暂存(把文件从 Index 撤出,工作区编辑内容保留)──
git restore --staged 文件名  # 单文件:取消暂存,可重新 git add -p 挑选块
git restore --staged .       # 全部撤出暂存区,相当于「误点 add .」后的急救

# ── 快捷提交:对已跟踪文件的「修改/删除」一步完成 add + commit ──
# 注意:新建文件(Untracked)不在此列,必须先 git add
git commit -am 'fix: update tracked files only'

【代码注释】git diff 无参数时比的是工作区 ↔ 暂存区--cached 比的是暂存区 ↔ 最新 commit ,提交前用它做最后一遍自查。git restore(Git 2.23+)只撤销未提交状态,不能 找回已 commit 的内容,历史回退用 reset / revert-am 中的 -a 自动暂存已跟踪 文件的修改与删除,新建 文件仍须先 git add-m 紧跟提交说明。误用 git restore . 会永久丢失未 add 的编辑,操作前可用 git stash 备份。

经典应用场景

  • 误改配置git restore .env 丢弃工作区未提交改动
  • 误暂存git restore --staged . 全部撤出暂存区再挑选 git add -p
  • Code Review 前git diff --cached 确认即将提交的内容

可运行示例 · 本地 Git 练习页 (在空目录保存为 git-practice.html,按步骤在终端执行对应命令):

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Git 基础练习清单</title>
  <style>
    body { font-family: system-ui, sans-serif; max-width: 720px; margin: 2rem auto; line-height: 1.6; }
    code { background: #f4f4f5; padding: 2px 6px; border-radius: 4px; }
    pre { background: #18181b; color: #e4e4e7; padding: 1rem; border-radius: 8px; overflow-x: auto; }
    h2 { border-bottom: 1px solid #e4e4e7; padding-bottom: 0.25rem; }
  </style>
</head>
<body>
  <h1>Git 基础命令练习</h1>
  <p>在终端进入空文件夹,按顺序执行下列命令,观察每步 <code>git status</code> 变化。</p>
  <h2>步骤 1:初始化</h2>
  <pre>git init
echo "hello" &gt; readme.txt
git status</pre>
  <h2>步骤 2:暂存与提交</h2>
  <pre>git add readme.txt
git commit -m "docs: add readme"</pre>
  <h2>步骤 3:修改与 diff</h2>
  <pre>echo "world" &gt;&gt; readme.txt
git diff
git add readme.txt
git diff --cached</pre>
  <h2>步骤 4:撤销</h2>
  <pre>echo "mistake" &gt;&gt; readme.txt
git restore readme.txt
git status</pre>
</body>
</html>

【代码注释】 :HTML 页内 <pre> 仅为命令清单与步骤导航 ,不执行 Git;须在系统终端进入空目录按序操作。每步后执行 git status 观察「未跟踪 / 已修改 / 已暂存」状态变化,建立工作区→暂存区→版本库的肌肉记忆。

1.5 Git 内部对象模型(理论精讲)

理解 Git 的存储机制,才能真正读懂每条命令背后发生了什么。

Git 本质是一个内容寻址文件系统 :每次你 git add 一个文件,Git 就把文件内容压缩后用 SHA-1 哈希命名,存入 .git/objects/

四种核心对象
复制代码
.git/objects/
├── blob   ← 存文件内容(无文件名)
├── tree   ← 存目录结构(文件名 → blob 哈希)
├── commit ← 存快照元信息(tree + parent + message)
└── tag    ← 存附注标签(指向 commit)

#mermaid-svg-j3ozYPiivi5eCWqs{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-j3ozYPiivi5eCWqs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-j3ozYPiivi5eCWqs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-j3ozYPiivi5eCWqs .error-icon{fill:#552222;}#mermaid-svg-j3ozYPiivi5eCWqs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-j3ozYPiivi5eCWqs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-j3ozYPiivi5eCWqs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-j3ozYPiivi5eCWqs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-j3ozYPiivi5eCWqs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-j3ozYPiivi5eCWqs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-j3ozYPiivi5eCWqs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-j3ozYPiivi5eCWqs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-j3ozYPiivi5eCWqs .marker.cross{stroke:#333333;}#mermaid-svg-j3ozYPiivi5eCWqs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-j3ozYPiivi5eCWqs p{margin:0;}#mermaid-svg-j3ozYPiivi5eCWqs .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-j3ozYPiivi5eCWqs .cluster-label text{fill:#333;}#mermaid-svg-j3ozYPiivi5eCWqs .cluster-label span{color:#333;}#mermaid-svg-j3ozYPiivi5eCWqs .cluster-label span p{background-color:transparent;}#mermaid-svg-j3ozYPiivi5eCWqs .label text,#mermaid-svg-j3ozYPiivi5eCWqs span{fill:#333;color:#333;}#mermaid-svg-j3ozYPiivi5eCWqs .node rect,#mermaid-svg-j3ozYPiivi5eCWqs .node circle,#mermaid-svg-j3ozYPiivi5eCWqs .node ellipse,#mermaid-svg-j3ozYPiivi5eCWqs .node polygon,#mermaid-svg-j3ozYPiivi5eCWqs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-j3ozYPiivi5eCWqs .rough-node .label text,#mermaid-svg-j3ozYPiivi5eCWqs .node .label text,#mermaid-svg-j3ozYPiivi5eCWqs .image-shape .label,#mermaid-svg-j3ozYPiivi5eCWqs .icon-shape .label{text-anchor:middle;}#mermaid-svg-j3ozYPiivi5eCWqs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-j3ozYPiivi5eCWqs .rough-node .label,#mermaid-svg-j3ozYPiivi5eCWqs .node .label,#mermaid-svg-j3ozYPiivi5eCWqs .image-shape .label,#mermaid-svg-j3ozYPiivi5eCWqs .icon-shape .label{text-align:center;}#mermaid-svg-j3ozYPiivi5eCWqs .node.clickable{cursor:pointer;}#mermaid-svg-j3ozYPiivi5eCWqs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-j3ozYPiivi5eCWqs .arrowheadPath{fill:#333333;}#mermaid-svg-j3ozYPiivi5eCWqs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-j3ozYPiivi5eCWqs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-j3ozYPiivi5eCWqs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-j3ozYPiivi5eCWqs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-j3ozYPiivi5eCWqs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-j3ozYPiivi5eCWqs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-j3ozYPiivi5eCWqs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-j3ozYPiivi5eCWqs .cluster text{fill:#333;}#mermaid-svg-j3ozYPiivi5eCWqs .cluster span{color:#333;}#mermaid-svg-j3ozYPiivi5eCWqs div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-j3ozYPiivi5eCWqs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-j3ozYPiivi5eCWqs rect.text{fill:none;stroke-width:0;}#mermaid-svg-j3ozYPiivi5eCWqs .icon-shape,#mermaid-svg-j3ozYPiivi5eCWqs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-j3ozYPiivi5eCWqs .icon-shape p,#mermaid-svg-j3ozYPiivi5eCWqs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-j3ozYPiivi5eCWqs .icon-shape .label rect,#mermaid-svg-j3ozYPiivi5eCWqs .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-j3ozYPiivi5eCWqs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-j3ozYPiivi5eCWqs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-j3ozYPiivi5eCWqs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} commit v2

tree: t2

parent: C1
tree t2

README → blob_r2

src/ → tree_s
commit v1

tree: t1
blob: README 内容 v2
tree src/

index.js → blob_i
blob: index.js 内容

关键推论

  • 同一内容的两个文件只保存一个 blob(内容相同 → 哈希相同)
  • 每次 commit 只存变化的部分,未改动文件复用旧 blob------Git 天然节省空间
  • SHA-1 哈希由内容决定,数据损坏时哈希不匹配即可发现
bash 复制代码
# ── 亲手观察 Git 对象库(在任意已有 .git 的仓库根目录执行)──
git cat-file -t HEAD          # -t:打印对象类型,HEAD 通常为 commit
git cat-file -p HEAD          # -p:pretty-print,可见 tree 哈希、parent、author、message
git cat-file -p HEAD^{tree}   # ^{tree}:从 commit 解引用到根目录 tree 对象
git ls-tree HEAD              # 以「模式 类型 哈希 路径」列出根目录条目,比 cat-file 更直观

# 按「提交:路径」语法直接查看某路径在 HEAD 处的 blob 原文(不经工作区)
git cat-file -p HEAD:src/index.js

【代码注释】 :Git 用 SHA-1 对内容 寻址:.git/objects/ab/cdef... 中前 2 位为子目录、后 38 位为文件名;相同内容只存一份 blob。HEAD 是当前检出位置的符号引用,通常指向分支名,分支再指向最新 commit。HEAD^{tree} 是「从 commit 跳到其快照根目录」的语法;HEAD:path 等价于该路径在 HEAD 提交中的 blob。松散对象过多时 git gc 会打包为 .pack 并 prune 悬空对象,减小仓库体积。

1.6 基础模块归纳

命令 作用阶段 记忆口诀
git status 任意 先看状态再操作
git add 工作区→暂存区 挑选要提交的改动
git commit 暂存区→版本库 快照落盘
git diff 对比 未 add 看工作区,已 add 看 --cached
git restore 撤销 工作区 / 暂存区分别恢复
git cat-file -p 探索对象库 理解 Git 内部存储结构

2. 版本管理与回滚

版本管理是Git的核心功能,允许开发者在任何时候回到项目的任意状态。

2.1 查看历史版本

Git为每次提交生成唯一的哈希值(版本号),通过这些标识符可以精确追踪每一次变更。

bash 复制代码
# ── 查看提交历史(只读,不改变仓库状态)──
git log                       # 完整日志:哈希、作者、日期、完整 message
git log -3                    # -n:仅显示最近 3 条,适合快速定位
git log --oneline             # 每行一条:短哈希 + 首行 subject,适合日常浏览
git log --graph --oneline --all  # --graph 画分支线;--all 含所有本地/远程跟踪分支

# ── 引用日志:HEAD 与分支指针的移动轨迹(含 reset 后「消失」的提交)──
git reflog                    # 默认 reflog show HEAD;误操作恢复时优先查这里

【代码注释】git log 遍历当前分支 可达的 commit 链;--all 才展示所有分支拓扑,合并排查时配合 --graph。提交哈希全局唯一,日志里常只显示前 7 位。git reflog 记录的是引用移动 (checkout、merge、reset 等),不是完整项目史;默认约 90 天过期。reset --hard 后 log 里看不到的提交,往往仍能通过 git reflog 找到哈希,再 git reset --hard <hash> 救回。

实际应用场景

bash 复制代码
# 单文件演进史:--follow 跨 rename 追踪;--patch 附带每次 diff
git log --follow --patch -- file.txt

# 按作者筛选(支持正则,如 --author="Zhang|Li")
git log --author="张三"

# 时间窗口:--since/--until 支持 "2 weeks ago"、"2024-12-31" 等自然语言
git log --since="2024-01-01" --until="2024-12-31"

# 每次提交改了哪些文件、增删行数汇总(不展开具体 diff)
git log --stat

【代码注释】 :路径前的 -- 用于与选项分隔,避免文件名被当成参数。--follow 在检测到 rename 时会延续历史,适合追查「这个 bug 是哪次改文件引入的」。--patch 输出量大,可配合 -S"关键字" 查「哪次提交引入/删除了某字符串」。--stat 适合 Code Review 前看影响面;分支拓扑用 git log --graph --oneline --all --decorate

git log 高级过滤:代码考古三件套

在大型项目中,「哪次提交引入了某个函数/删掉了某行代码」是最常见的调试场景,以下三种过滤器专门解决这类问题:

bash 复制代码
# ── 1. Pickaxe 搜索(-S):精准定位「某字符串出现/消失」的提交 ──
# -S 会扫描每次 commit 的 diff,仅返回「该字符串出现次数发生变化」的提交
git log -S "calculateTotal" --oneline
# 典型用途:某函数从什么时候开始被使用,或何时被删除

git log -S "api_secret_key" --all --oneline
# 安全审计:任意分支历史中是否曾经存在敏感字符串(即使已删除也能找到)

git log -S "require('lodash')" --patch -- "*.js"
# 结合 --patch:不仅找到提交,同时展示引入/删除该行的完整 diff 上下文

# ── 2. Regex 搜索(-G):正则匹配 diff 内容 ──
# 与 -S 区别:-G 匹配「任意包含该正则的 diff 行」,而非「出现次数变化」
git log -G "function\s+get[A-Z]" --oneline   # 找新增或删除了 getXxx 系列函数的提交

# ── 3. 提交说明搜索(--grep):在 message 中过滤 ──
git log --grep="fix.*login" --oneline --all     # 找所有修复登录相关的提交(正则)
git log --grep="JIRA-1234" --oneline            # 按 Issue 编号查找关联提交
git log --grep="feat" --grep="auth" --all-match # --all-match:message 同时含两词(默认是 OR)

# ── 综合场景:定位某接口何时被废弃 ──
git log -S "fetchUserProfile" --oneline --all
# 输出含两个提交:一条「新增」、一条「删除」,精准还原废弃时间线

【代码注释】-S(Pickaxe)是 Git 内置的语义级代码搜索,比 grep 历史日志更准确,因为它理解 diff 的「加/减」语义;-G 则更适合模糊匹配------两者处理正则时行为不同,优先用 -S 搜精确字符串。--all 跨所有分支搜索,安全审计必加。--grep 结合 --all-match 可实现多关键词 AND 逻辑查询。

2.2 版本回滚操作

版本回滚是Git最强大的功能之一,允许项目状态在时间线上自由移动。

bash 复制代码
# ── 将当前分支指针移动到指定提交(--hard 会同步清空工作区与暂存区)──
git reset --hard a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6  # 完整 40 位 SHA-1
git reset --hard a1b2c3d                              # 前 7 位在仓库内唯一即可定位

# ── 相对当前 HEAD 的回退语法(^ 与 ~ 可组合,如 HEAD~3^2)──
git reset --hard HEAD^       # 回退 1 个提交(^ 等价于 ~1)
git reset --hard HEAD^^      # 回退 2 个提交(连续两个父提交,仅线性历史常用)
git reset --hard HEAD~5      # 回退 5 个提交(沿第一父链)

【代码注释】git reset 移动的是当前分支指针 ,不是删除 commit 对象;--hard 同时把工作区、暂存区重置为目标提交树,未提交的修改会永久丢失 。仅适合未推送 的本地纠错;已推送共享分支应改用 git revert。回滚前可用 git branch backupgit stash 留后路;误操作后用 git reflog 查移动前的哈希再 reset --hard 恢复。

版本回滚策略
#mermaid-svg-y7aUZyZfZUgeJdQj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-y7aUZyZfZUgeJdQj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-y7aUZyZfZUgeJdQj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-y7aUZyZfZUgeJdQj .error-icon{fill:#552222;}#mermaid-svg-y7aUZyZfZUgeJdQj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-y7aUZyZfZUgeJdQj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-y7aUZyZfZUgeJdQj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-y7aUZyZfZUgeJdQj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-y7aUZyZfZUgeJdQj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-y7aUZyZfZUgeJdQj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-y7aUZyZfZUgeJdQj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-y7aUZyZfZUgeJdQj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-y7aUZyZfZUgeJdQj .marker.cross{stroke:#333333;}#mermaid-svg-y7aUZyZfZUgeJdQj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-y7aUZyZfZUgeJdQj p{margin:0;}#mermaid-svg-y7aUZyZfZUgeJdQj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-y7aUZyZfZUgeJdQj .cluster-label text{fill:#333;}#mermaid-svg-y7aUZyZfZUgeJdQj .cluster-label span{color:#333;}#mermaid-svg-y7aUZyZfZUgeJdQj .cluster-label span p{background-color:transparent;}#mermaid-svg-y7aUZyZfZUgeJdQj .label text,#mermaid-svg-y7aUZyZfZUgeJdQj span{fill:#333;color:#333;}#mermaid-svg-y7aUZyZfZUgeJdQj .node rect,#mermaid-svg-y7aUZyZfZUgeJdQj .node circle,#mermaid-svg-y7aUZyZfZUgeJdQj .node ellipse,#mermaid-svg-y7aUZyZfZUgeJdQj .node polygon,#mermaid-svg-y7aUZyZfZUgeJdQj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-y7aUZyZfZUgeJdQj .rough-node .label text,#mermaid-svg-y7aUZyZfZUgeJdQj .node .label text,#mermaid-svg-y7aUZyZfZUgeJdQj .image-shape .label,#mermaid-svg-y7aUZyZfZUgeJdQj .icon-shape .label{text-anchor:middle;}#mermaid-svg-y7aUZyZfZUgeJdQj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-y7aUZyZfZUgeJdQj .rough-node .label,#mermaid-svg-y7aUZyZfZUgeJdQj .node .label,#mermaid-svg-y7aUZyZfZUgeJdQj .image-shape .label,#mermaid-svg-y7aUZyZfZUgeJdQj .icon-shape .label{text-align:center;}#mermaid-svg-y7aUZyZfZUgeJdQj .node.clickable{cursor:pointer;}#mermaid-svg-y7aUZyZfZUgeJdQj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-y7aUZyZfZUgeJdQj .arrowheadPath{fill:#333333;}#mermaid-svg-y7aUZyZfZUgeJdQj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-y7aUZyZfZUgeJdQj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-y7aUZyZfZUgeJdQj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-y7aUZyZfZUgeJdQj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-y7aUZyZfZUgeJdQj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-y7aUZyZfZUgeJdQj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-y7aUZyZfZUgeJdQj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-y7aUZyZfZUgeJdQj .cluster text{fill:#333;}#mermaid-svg-y7aUZyZfZUgeJdQj .cluster span{color:#333;}#mermaid-svg-y7aUZyZfZUgeJdQj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-y7aUZyZfZUgeJdQj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-y7aUZyZfZUgeJdQj rect.text{fill:none;stroke-width:0;}#mermaid-svg-y7aUZyZfZUgeJdQj .icon-shape,#mermaid-svg-y7aUZyZfZUgeJdQj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-y7aUZyZfZUgeJdQj .icon-shape p,#mermaid-svg-y7aUZyZfZUgeJdQj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-y7aUZyZfZUgeJdQj .icon-shape .label rect,#mermaid-svg-y7aUZyZfZUgeJdQj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-y7aUZyZfZUgeJdQj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-y7aUZyZfZUgeJdQj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-y7aUZyZfZUgeJdQj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是

版本回滚决策
是否需要保留当前修改
使用 --soft 或 --mixed
使用 --hard
git reset --soft HEAD^
git reset --mixed HEAD^
git reset --hard HEAD^
保留工作区和暂存区修改
保留工作区修改,清空暂存区
完全回滚到指定版本

2.3 reset命令的三种模式

bash 复制代码
# --soft:只回退 commit,暂存区与工作区保持「已 add 待提交」状态
git reset --soft HEAD~1       # 典型:改 commit message 或合并多次 commit

# --mixed(默认):回退 commit 并清空暂存区,工作区文件改动仍保留
git reset --mixed HEAD~1
git reset HEAD~1              # 省略 --mixed 时行为相同;改动回到「已修改未 add」

# --hard:三处全部对齐到目标提交,工作区未提交内容一并丢弃
git reset --hard HEAD~1       # 仅本地个人分支使用;共享分支禁止对已推送历史使用

实际应用示例

bash 复制代码
# 场景1:提交说明写错(尚未 push)------ soft 撤销提交,改动仍在暂存区
git commit -m "typo: fix buug"
git reset --soft HEAD~1         # HEAD 指向上一个提交,本次改动留在 Index
git commit -m "typo: fix bug"   # 也可用 git commit --amend -m "..." 一步改说明

# 场景2:误把敏感文件或 node_modules 打进提交------ soft 后重新挑选暂存
git reset --soft HEAD~1
git restore --staged unwanted.txt   # 从暂存区移除,文件仍留在磁盘
git commit -m "feat: add new feature"

# 场景3:本地实验失败,整仓回到远程稳定线(会丢失未提交改动!)
git fetch origin
git reset --hard origin/main    # 或 reset 到本地 production 分支的 tip

【代码注释】 :三种模式差异在暂存区与工作区是否保留 :soft → 全保留;mixed(默认)→ 只保留工作区;hard → 全丢弃。改最后一次提交说明更推荐 git commit --amend(未 push 时)。reset --hard 后若 commit 已从当前分支 log 消失,对象仍在 reflog 中,用 git refloggit reset --hard <旧哈希> 可恢复。

可运行示例 · 版本回滚练习

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Git log 与 reset 练习</title>
  <style>
    body { font-family: ui-monospace, monospace; max-width: 800px; margin: 2rem auto; }
    pre { background: #0f172a; color: #cbd5e1; padding: 1rem; border-radius: 8px; }
    .tip { background: #fef3c7; padding: 0.75rem; border-radius: 6px; margin: 1rem 0; }
  </style>
</head>
<body>
  <h1>log / reflog / reset 三步练</h1>
  <div class="tip">先连续提交 3 次,再按下列命令观察 HEAD 变化。</div>
  <pre>git log --oneline
git reset --hard HEAD^
git reflog
git reset --hard &lt;reflog中的旧哈希&gt;</pre>
  <p>前 7 位 Commit ID 即可定位版本,无需完整 40 位 SHA。</p>
</body>
</html>

【代码注释】reflog 一行形如 abc1234 HEAD@{1}: reset: moving to HEAD^,左侧为可检出哈希。reset --hard 后用 git reset --hard abc1234 回到 reflog 中 HEAD@{n} 对应状态。HEAD@{0} 为当前,@{1} 为上一步操作后位置。

2.4 git revert:安全撤销已推送的提交

reset --hard重写历史 ,在团队共享分支上极其危险。git revert 通过新增一个反向提交来撤销效果,历史线不变。

bash 复制代码
# 撤销最近一次提交:新建一个「反向 diff」的 commit,历史线不断开
git revert HEAD                 # 默认打开编辑器填写 revert 说明;加 --no-edit 沿用默认文案

# 撤销历史中任意一次提交(中间提交也可,可能产生冲突需手工解决)
git revert abc1234

# 批量撤销最近 3 次:先不自动 commit,便于一次性检查合并结果
git revert --no-commit HEAD~3..HEAD   # 范围不含左端点,含右端 HEAD
git commit -m "revert: roll back last 3 commits"

# 撤销合并提交:merge commit 有两个 parent,必须指定保留哪条主线
git revert -m 1 <merge-commit-hash>   # -m 1 通常指被合并进去的目标分支(如 main)

【代码注释】revert 通过新增提交 抵消某次改动,不改写已有哈希,适合已 push 的共享分支 。与 reset 对比:reset 移动指针「抹掉」后续提交(仅本地未共享时安全);revert 保留完整审计轨迹。合并提交有两个 parent,-m 1 选第一 parent 作为「主线」基准生成反向补丁;选错会导致 revert 内容异常。若 revert 了合并提交后又想再次合并同一功能分支,需先 revert 掉那次 revert commit(「revert the revert」),否则 Git 认为改动已存在而跳过。

reset vs revert 决策矩阵
场景 推荐操作 理由
本地未推送的提交 git reset 无副作用,历史更干净
已推送到共享分支 git revert 不改写历史,团队安全
撤销某一个特定提交 git revert <hash> 精准,不影响之后的提交
回到某个历史状态 git reset --hard <hash> 仅在本地个人分支使用

经典场景:线上紧急回滚

bash 复制代码
# 1. 定位问题提交(共享分支上禁止 reset --hard,须用 revert)
git log --oneline -5
# e4f5a6b feat: new checkout flow  ← 引入 bug 的 commit
# c3d4e5f fix: typo in header       ← revert 后仍保留(在问题提交之后)
# a1b2c3d release: v2.3.0

# 2. 生成「反向补丁」新提交,历史不断开,队友 pull 即可同步回滚
git revert e4f5a6b                  # 默认打开编辑器;加 --no-edit 用自动说明
# 若 revert 冲突:解决 → git add → git revert --continue

# 3. 推送 revert 提交,CI 通常监听 main 的 push 触发回滚部署
git push origin main

2.5 标签管理(Tag)

标签是指向某次提交的永久快照,通常用于标记版本发布节点(v1.0.0、v2.3.1 等)。

bash 复制代码
# 【轻量标签】仅是指向某 commit 的固定指针,无 tagger/message 元数据
git tag v1.0.0                  # 默认打在 HEAD 当前提交上
git tag v1.0.0 abc1234          # 为历史某次提交补打标签(如漏标 release)

# 【附注标签 -a】独立 Git 对象,含打标者、时间、说明;正式发布、CHANGELOG 溯源首选
git tag -a v1.0.0 -m "Release version 1.0.0: initial stable release"
git tag -a v1.0.1 abc1234 -m "Hotfix: fix critical payment bug"

# 列出与查看
git tag                         # 按字母序列出本地所有标签
git tag -l "v1.*"               # -l 支持通配,如 v1.2.x 系列
git show v1.0.0                 # 显示 tag 对象 + 对应 commit 与 diff 摘要

# 与远程同步(标签默认不随 git push 分支一起推送)
git push origin v1.0.0          # 推送单个标签
git push origin --tags          # 推送全部本地尚未在远程的标签

# 删除(本地 / 远程需分别操作)
git tag -d v1.0.0               # 删除本地标签引用
git push origin --delete v1.0.0 # 或:git push origin :refs/tags/v1.0.0

# 检出标签快照(HEAD 脱离分支,见 4.8 Detached HEAD)
git switch --detach v1.0.0      # Git 2.23+ 推荐;旧写法 git checkout v1.0.0

【代码注释】 :轻量标签是 .git/refs/tags/<name> 里存的一行 commit 哈希;附注标签(-a)是完整对象,可 GPG 签名(-s)。git push 默认只推分支,标签需显式 git push origin <tag>--tags。SemVer(v主.次.修订)便于 CI 解析版本;npm version patch/minor/major 可自动改 package.json 并打 tag。生产热修应在 tag 对应 commit 上打 v1.0.1 而非移动已有 tag(git tag -f 会改指向,破坏他人 clone)。

语义化版本(SemVer)规范

复制代码
v主版本.次版本.修订号
v2.1.3
│ │ └─ 修订号:向后兼容的 bug 修复
│ └─── 次版本:新增向后兼容的功能
└───── 主版本:不兼容的 API 变更

经典场景:前端项目版本发布流程

bash 复制代码
# 发版前:确保 main 与远程一致,且 CI 已通过
git switch main
git pull origin main

# minor:1.1.0 → 1.2.0;会改 package.json、自动 commit + 打 tag v1.2.0
npm version minor              # patch/minor/major 对应修订/次版本/主版本
# 手写流程:改 version → git commit → git tag -a v1.2.0 -m "..."

# 分支与标签一并推送(标签不会随普通 git push 自动上去)
git push origin main --tags

# GitHub Releases 页面选 v1.2.0 写 changelog;流水线监听 tag push 打生产包

2.6 版本管理模块归纳

模式 工作区 暂存区 提交历史 典型用途
--soft 保留 保留 回退 改 commit message
--mixed 保留 清空 回退 重新挑选 add
--hard 清空 清空 回退 彻底回到某版本(仅本地)
git revert 新增反向提交 --- 不变 安全撤销已推送提交
git tag -a --- --- --- 标记发布版本

3. Git忽略文件机制

.gitignore文件是Git项目的重要组成部分,它定义了哪些文件不应该被版本控制跟踪。

3.1 需要忽略的文件类型

**【专业建议】**以下文件类型应该被Git忽略:

  1. 系统自动生成的文件

    • macOS: .DS_Store, .AppleDouble, .LSOverride
    • Windows: Thumbs.db, ehthumbs.db, Desktop.ini
    • Linux: *~, .nfs*
  2. 编译和构建产物

    • JavaScript: node_modules/, dist/, build/, .next/
    • CSS: .sass-cache/
    • 通用: *.min.js, *.min.css
  3. IDE和编辑器配置

    • VSCode: .vscode/
    • WebStorm/IntelliJ: .idea/
    • Vim: *.swp, *.swo, *.un~
    • Emacs: *~, \#*\#, .\#*
  4. 环境配置和敏感信息

    • .env, .env.local, .env.*.local
    • config/secrets.js, credentials.json
  5. 日志和临时文件

    • *.log, npm-debug.log*, yarn-debug.log*
    • *.tmp, *.temp, *.cache

3.2 .gitignore语法详解

bash 复制代码
# .gitignore 语法速查(规则相对于「该 .gitignore 文件所在目录」解析)

# 1. 空行:仅作可读性分隔;行尾 \ 可续行或转义特殊字符

# 2. # 开头:注释;若需匹配字面量 # 文件名,用 \# 转义

# 3. ! 否定:在已被上层规则排除后,重新纳入版本控制(顺序敏感,须写在排除规则之后)
*.a               # 忽略所有 .a 静态库
!lib.a            # 例外:保留 lib.a(常用于「忽略 build/*.a 但保留 vendor/lib.a」)

# 4. 末尾 /:只匹配目录(及其下所有内容),不匹配同名文件
build/            # 忽略 build/ 目录;无尾斜杠的 build 可能同时匹配文件

# 5. 开头 /:锚定到当前 .gitignore 所在目录(常为仓库根)
/TODO             # 仅忽略根目录 TODO,不忽略 src/TODO

# 6. **:跨多级目录通配(Git 2.13+ 对 ** 支持更完整)
doc/**/*.pdf      # doc 任意子目录下的 .pdf

# 7. *:单层路径内任意字符(不含 /)
*.txt             # 任意目录下的 .txt,但不匹配 a/b.txt 中的跨层需用 **

# 8. ?:精确一个任意字符
file?.txt         # file1.txt、fileA.txt,不匹配 file10.txt

# 9. []:字符类或范围
file[0-9].txt     # file0.txt ... file9.txt
file[a-z].txt     # 单个小写字母一位

【代码注释】.gitignore 只影响未跟踪 文件是否被 git add 发现;已跟踪 文件须先 git rm --cached。规则从仓库根向下继承,子目录可放独立 .gitignore 追加或覆盖。git check-ignore -v <path> 显示命中规则行号,排查「为何仍被跟踪」必备。否定 ! 若父目录整条被忽略,子路径 !子/file 可能无效,需先 !父目录/!父目录/子/file

名词解析 · 否定规则 ! :先被 * 排除的文件,可用 !filename 重新纳入;若父目录已被排除,子路径的 ! 可能无效。

3.3 .gitignore配置示例

bash 复制代码
# 【通用 Web 前端 .gitignore 模板】复制到仓库根目录,按项目删减

# 依赖目录(应通过 lock 文件 + CI 安装,勿提交二进制树)
node_modules/                   # npm/pnpm/yarn 安装产物,体积巨大
bower_components/               # 旧版 Bower 依赖
vendor/                         # PHP/混合项目的第三方包

# 构建输出(可由 CI 从源码生成,提交会导致 diff 噪音与冲突)
dist/                           # Vite/Webpack 默认输出
build/                          # CRA 等默认目录
out/                            # Next.js 静态导出等
*.min.js                        # 压缩产物(若未统一放在 dist/)
*.min.css

# 环境配置(含 API Key、数据库密码,泄露须轮换密钥)
.env                            # 所有环境变量入口
.env.local                      # 本地覆盖,最高敏感
.env.development.local
.env.test.local
.env.production.local

# 日志与调试产物
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*

# 编辑器/IDE(个人偏好,不应强加给队友)
.vscode/                        # 除共享的 extensions.json 外通常忽略
.idea/
*.swp                           # Vim 交换文件
*.swo
*~
.DS_Store                       # macOS 目录元数据

# 测试覆盖率报告(CI 生成即可)
coverage/
.nyc_output/

# 临时与缓存
*.tmp
*.temp
.cache/                         # 各类工具本地缓存

# Windows 系统文件
Thumbs.db
Desktop.ini

【代码注释】 :模板按类别分组,复制后删除与本项目无关的行即可。node_modules/dist/.env* 是前端仓库必忽略 三项;.vscode/ 若团队共享 settings.json 可改为只忽略 !.vscode/settings.json。提交前用 git status 确认无巨型目录处于 Untracked 再首次 push。

3.4 忽略已提交文件的正确流程

当文件已经被提交到版本库,需要从版本控制中移除但仍保留本地文件:

bash 复制代码
# 1. 从 Git 索引(暂存区/版本库跟踪列表)移除,磁盘文件原样保留
git rm --cached filename        # 之后该文件变为 Untracked,须配合 .gitignore

# 2. 递归移除目录下所有已跟踪项(如误提交的 node_modules)
git rm -r --cached directory/   # -r:递归;目录本身可仍在本地

# 3. 写入忽略规则,防止下次 git add . 再次纳入
echo "filename" >> .gitignore   # 建议用编辑器追加并 review,避免重复规则

# 4. 提交「停止跟踪」这一变更(团队 pull 后对方工作区会删该文件,需事先沟通)
git add .gitignore
git commit -m "chore: stop tracking filename and update gitignore"

【代码注释】--cached 只动索引 ,不动工作区文件;提交后远程仓库不再包含该路径,但本地文件还在。队友 git pull 后 Git 会删除 他们工作区里的该文件(因跟踪记录已移除),敏感文件误提交场景要先轮换密钥再执行。完整流程:.gitignoregit rm --cachedcommit → 通知团队。可用 git ls-files 查看当前仍被跟踪的文件列表。

实际应用场景

bash 复制代码
# 场景1:敏感配置已进仓库 ------ 停止跟踪 + 忽略 + 若已 push 须轮换密钥
git rm --cached config/database.yml   # 本地 yml 仍在,远程下次 push 后移除
echo "config/database.yml" >> .gitignore
git add .gitignore
git commit -m "security: remove database config from version control"
# 已泄露的密码在 Git 历史中仍存在,需 filter-repo/BFG + 强制改密

# 场景2:误提交 node_modules(体积大、跨平台路径易冲突)
git rm -r --cached node_modules/      # 不删磁盘,只移出索引
echo "node_modules/" >> .gitignore
git add .gitignore
git commit -m "chore: ignore node_modules directory"

【代码注释】 :误提交 node_modules.env 时,除 rm --cached 外,若已 push 且含密钥须轮换密钥 并考虑 git filter-repo 清历史。队友 pull 后本地该路径会被删除(因不再跟踪),应提前通知「请备份本地配置后 pull」。

实战 .gitignore 规则示例(前端项目常见写法,可直接放入项目根目录试用):

gitignore 复制代码
# 锚定根目录:只忽略仓库根下的 config.json,不忽略 src/config.json
/config.json

# 先广后窄:先忽略全部 .js,再用 ! 白名单拉回入口(顺序不可颠倒)
*.js
!01.js

# 目录规则:dist/ 只匹配目录名,避免误伤名为 dist 的单文件
dist/

# 构建链生成物:若 .css 由 Sass/Less 编译,源文件在 src/ 可单独跟踪
*.css

【代码注释】 :规则自上而下 匹配,后写的可覆盖先写的;!01.js 必须写在 *.js 之后才能生效。/config.json 只忽略仓库根目录文件;dist/ 尾斜杠只匹配目录。提交前对可疑路径执行 git check-ignore -v src/index.js 确认命中行。

业界实践

  • GitHub :新建仓库可勾选 Node/Python 等模板自动生成 .gitignore
  • create-react-app / Vite :脚手架自带忽略 node_modulesdist
  • npmnpx gitignore node 按技术栈生成规则

可运行示例 · 冲突标记识别页(帮助理解合并冲突 UI):

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Git 合并冲突标记说明</title>
  <style>
    body { font-family: system-ui, sans-serif; max-width: 640px; margin: 2rem auto; }
    .ours { background: #dbeafe; padding: 1rem; margin: 0.5rem 0; }
    .theirs { background: #fce7f3; padding: 1rem; margin: 0.5rem 0; }
    mark { background: #fde047; }
  </style>
</head>
<body>
  <h1>冲突文件中的标记</h1>
  <p>合并后编辑器中会出现如下结构,删除标记行并保留需要的内容,再 <code>git add</code> + <code>git commit</code>。</p>
  <pre class="ours">&lt;&lt;&lt;&lt;&lt;&lt;&lt; HEAD
当前分支的代码
=======</pre>
  <pre class="theirs">要合并进来的代码
&gt;&gt;&gt;&gt;&gt;&gt;&gt; feature/branch</pre>
  <p>VS Code、WebStorm、Cursor 均提供「接受当前 / 传入 / 两者」的可视化按钮。</p>
</body>
</html>

【代码注释】 :冲突标记 <<<<<<< / ======= / >>>>>>> 由 Git 插入,合并后必须全部删除。仅当两分支修改了同一文件相邻或重叠行才冲突;不同文件或相距较远的同行 Git 常自动合并。IDE 三向对比可减轻手工负担。

3.5 忽略机制模块归纳

场景 命令 / 操作
新文件应忽略 写入 .gitignore 后勿 add
已跟踪应忽略 git rm --cached + 提交
规则是否生效 git check-ignore -v <path>

4. Git分支管理策略

分支是Git最强大的功能之一,它允许开发者在独立的代码线上工作,避免相互干扰。

4.1 分支的核心概念

【名词解释】

  • 分支 (Branch):指向某个提交对象的可变指针
  • HEAD:指向当前分支的指针
  • 合并 (Merge):将分支历史整合到一起
  • 快进合并 (Fast-forward):当分支可以直接向前移动时的合并方式
  • 三方合并 (3-way Merge):当两个分支有分歧时的合并方式

分支与主线的关系(等价于课堂示意图中的「从主线分支出功能线再合并」):
#mermaid-svg-7wwKs2zhAcK9wvQA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-7wwKs2zhAcK9wvQA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-7wwKs2zhAcK9wvQA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-7wwKs2zhAcK9wvQA .error-icon{fill:#552222;}#mermaid-svg-7wwKs2zhAcK9wvQA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-7wwKs2zhAcK9wvQA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-7wwKs2zhAcK9wvQA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-7wwKs2zhAcK9wvQA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-7wwKs2zhAcK9wvQA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-7wwKs2zhAcK9wvQA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-7wwKs2zhAcK9wvQA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-7wwKs2zhAcK9wvQA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-7wwKs2zhAcK9wvQA .marker.cross{stroke:#333333;}#mermaid-svg-7wwKs2zhAcK9wvQA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-7wwKs2zhAcK9wvQA p{margin:0;}#mermaid-svg-7wwKs2zhAcK9wvQA .commit-id,#mermaid-svg-7wwKs2zhAcK9wvQA .commit-msg,#mermaid-svg-7wwKs2zhAcK9wvQA .branch-label{fill:lightgrey;color:lightgrey;font-family:'trebuchet ms',verdana,arial,sans-serif;font-family:var(--mermaid-font-family);}#mermaid-svg-7wwKs2zhAcK9wvQA .branch-label0{fill:#ffffff;}#mermaid-svg-7wwKs2zhAcK9wvQA .commit0{stroke:hsl(240, 100%, 46.2745098039%);fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .commit-highlight0{stroke:hsl(60, 100%, 3.7254901961%);fill:hsl(60, 100%, 3.7254901961%);}#mermaid-svg-7wwKs2zhAcK9wvQA .label0{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .arrow0{stroke:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .branch-label1{fill:black;}#mermaid-svg-7wwKs2zhAcK9wvQA .commit1{stroke:hsl(60, 100%, 43.5294117647%);fill:hsl(60, 100%, 43.5294117647%);}#mermaid-svg-7wwKs2zhAcK9wvQA .commit-highlight1{stroke:rgb(0, 0, 160.5);fill:rgb(0, 0, 160.5);}#mermaid-svg-7wwKs2zhAcK9wvQA .label1{fill:hsl(60, 100%, 43.5294117647%);}#mermaid-svg-7wwKs2zhAcK9wvQA .arrow1{stroke:hsl(60, 100%, 43.5294117647%);}#mermaid-svg-7wwKs2zhAcK9wvQA .branch-label2{fill:black;}#mermaid-svg-7wwKs2zhAcK9wvQA .commit2{stroke:hsl(80, 100%, 46.2745098039%);fill:hsl(80, 100%, 46.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .commit-highlight2{stroke:rgb(48.8333333334, 0, 146.5000000001);fill:rgb(48.8333333334, 0, 146.5000000001);}#mermaid-svg-7wwKs2zhAcK9wvQA .label2{fill:hsl(80, 100%, 46.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .arrow2{stroke:hsl(80, 100%, 46.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .branch-label3{fill:#ffffff;}#mermaid-svg-7wwKs2zhAcK9wvQA .commit3{stroke:hsl(210, 100%, 46.2745098039%);fill:hsl(210, 100%, 46.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .commit-highlight3{stroke:rgb(146.5000000001, 73.2500000001, 0);fill:rgb(146.5000000001, 73.2500000001, 0);}#mermaid-svg-7wwKs2zhAcK9wvQA .label3{fill:hsl(210, 100%, 46.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .arrow3{stroke:hsl(210, 100%, 46.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .branch-label4{fill:black;}#mermaid-svg-7wwKs2zhAcK9wvQA .commit4{stroke:hsl(180, 100%, 46.2745098039%);fill:hsl(180, 100%, 46.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .commit-highlight4{stroke:rgb(146.5000000001, 0, 0);fill:rgb(146.5000000001, 0, 0);}#mermaid-svg-7wwKs2zhAcK9wvQA .label4{fill:hsl(180, 100%, 46.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .arrow4{stroke:hsl(180, 100%, 46.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .branch-label5{fill:black;}#mermaid-svg-7wwKs2zhAcK9wvQA .commit5{stroke:hsl(150, 100%, 46.2745098039%);fill:hsl(150, 100%, 46.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .commit-highlight5{stroke:rgb(146.5000000001, 0, 73.2500000001);fill:rgb(146.5000000001, 0, 73.2500000001);}#mermaid-svg-7wwKs2zhAcK9wvQA .label5{fill:hsl(150, 100%, 46.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .arrow5{stroke:hsl(150, 100%, 46.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .branch-label6{fill:black;}#mermaid-svg-7wwKs2zhAcK9wvQA .commit6{stroke:hsl(300, 100%, 46.2745098039%);fill:hsl(300, 100%, 46.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .commit-highlight6{stroke:rgb(0, 146.5000000001, 0);fill:rgb(0, 146.5000000001, 0);}#mermaid-svg-7wwKs2zhAcK9wvQA .label6{fill:hsl(300, 100%, 46.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .arrow6{stroke:hsl(300, 100%, 46.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .branch-label7{fill:black;}#mermaid-svg-7wwKs2zhAcK9wvQA .commit7{stroke:hsl(0, 100%, 46.2745098039%);fill:hsl(0, 100%, 46.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .commit-highlight7{stroke:rgb(0, 146.5000000001, 146.5000000001);fill:rgb(0, 146.5000000001, 146.5000000001);}#mermaid-svg-7wwKs2zhAcK9wvQA .label7{fill:hsl(0, 100%, 46.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .arrow7{stroke:hsl(0, 100%, 46.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .branch{stroke-width:1;stroke:#333333;stroke-dasharray:2;}#mermaid-svg-7wwKs2zhAcK9wvQA .commit-label{font-size:10px;fill:#000021;}#mermaid-svg-7wwKs2zhAcK9wvQA .commit-label-bkg{font-size:10px;fill:#ffffde;opacity:0.5;}#mermaid-svg-7wwKs2zhAcK9wvQA .tag-label{font-size:10px;fill:#131300;}#mermaid-svg-7wwKs2zhAcK9wvQA .tag-label-bkg{fill:#ECECFF;stroke:hsl(240, 60%, 86.2745098039%);}#mermaid-svg-7wwKs2zhAcK9wvQA .tag-hole{fill:#333;}#mermaid-svg-7wwKs2zhAcK9wvQA .commit-merge{stroke:#ECECFF;fill:#ECECFF;}#mermaid-svg-7wwKs2zhAcK9wvQA .commit-reverse{stroke:#ECECFF;fill:#ECECFF;stroke-width:3;}#mermaid-svg-7wwKs2zhAcK9wvQA .commit-highlight-inner{stroke:#ECECFF;fill:#ECECFF;}#mermaid-svg-7wwKs2zhAcK9wvQA .arrow{stroke-width:8;stroke-linecap:round;fill:none;}#mermaid-svg-7wwKs2zhAcK9wvQA .gitTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-7wwKs2zhAcK9wvQA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} main develop feature/shopcart feature/order m1 d1 s1 o1 release

若本地有 images/分支.jpg 示意图,可与上图对照理解;无图时以上 mermaid 即可表达分支拓扑。

4.2 分支操作完整指南

bash 复制代码
# ── 查看分支(* 号为当前检出分支)──
git branch                    # 仅本地分支列表
git branch -r                 # 远程跟踪分支,形如 origin/main
git branch -a                 # 本地 + 远程跟踪分支一并列出
git branch -vv                # 附带上游跟踪关系及领先/落后提交数

# ── 创建与切换(新分支从当前 HEAD 分出)──
git branch <branch-name>      # 只创建指针,不切换;基于当前 commit
git switch <branch-name>      # 切换分支;工作区有冲突改动时可能拒绝
git switch -c <new-branch>    # 创建并切换,等价于 checkout -b

# ── 删除与重命名 ──
git branch -d <branch-name>   # 安全删除:仅当该分支已合并到当前分支
git branch -D <branch-name>   # 强制删除:丢弃未合并提交(慎用)
git branch -m <old> <new>     # 重命名当前或指定本地分支

【代码注释】git switch(2.23+)专用于切换分支;git restore 专用于恢复文件;旧版 git checkout 兼管二者易混淆。切换前 git status 应干净,否则未提交改动会随分支带走 或阻塞切换。git branch -d 删除的是指针 ,commit 仍在 reflog 中短期可查。远程分支删除用 git push origin --delete <branch>,与本地 -d 无关。

4.3 分支操作工作流

#mermaid-svg-FjhQTagyz8IOdjbj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-FjhQTagyz8IOdjbj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-FjhQTagyz8IOdjbj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-FjhQTagyz8IOdjbj .error-icon{fill:#552222;}#mermaid-svg-FjhQTagyz8IOdjbj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-FjhQTagyz8IOdjbj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-FjhQTagyz8IOdjbj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-FjhQTagyz8IOdjbj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-FjhQTagyz8IOdjbj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-FjhQTagyz8IOdjbj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-FjhQTagyz8IOdjbj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-FjhQTagyz8IOdjbj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-FjhQTagyz8IOdjbj .marker.cross{stroke:#333333;}#mermaid-svg-FjhQTagyz8IOdjbj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-FjhQTagyz8IOdjbj p{margin:0;}#mermaid-svg-FjhQTagyz8IOdjbj .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-FjhQTagyz8IOdjbj .cluster-label text{fill:#333;}#mermaid-svg-FjhQTagyz8IOdjbj .cluster-label span{color:#333;}#mermaid-svg-FjhQTagyz8IOdjbj .cluster-label span p{background-color:transparent;}#mermaid-svg-FjhQTagyz8IOdjbj .label text,#mermaid-svg-FjhQTagyz8IOdjbj span{fill:#333;color:#333;}#mermaid-svg-FjhQTagyz8IOdjbj .node rect,#mermaid-svg-FjhQTagyz8IOdjbj .node circle,#mermaid-svg-FjhQTagyz8IOdjbj .node ellipse,#mermaid-svg-FjhQTagyz8IOdjbj .node polygon,#mermaid-svg-FjhQTagyz8IOdjbj .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-FjhQTagyz8IOdjbj .rough-node .label text,#mermaid-svg-FjhQTagyz8IOdjbj .node .label text,#mermaid-svg-FjhQTagyz8IOdjbj .image-shape .label,#mermaid-svg-FjhQTagyz8IOdjbj .icon-shape .label{text-anchor:middle;}#mermaid-svg-FjhQTagyz8IOdjbj .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-FjhQTagyz8IOdjbj .rough-node .label,#mermaid-svg-FjhQTagyz8IOdjbj .node .label,#mermaid-svg-FjhQTagyz8IOdjbj .image-shape .label,#mermaid-svg-FjhQTagyz8IOdjbj .icon-shape .label{text-align:center;}#mermaid-svg-FjhQTagyz8IOdjbj .node.clickable{cursor:pointer;}#mermaid-svg-FjhQTagyz8IOdjbj .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-FjhQTagyz8IOdjbj .arrowheadPath{fill:#333333;}#mermaid-svg-FjhQTagyz8IOdjbj .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-FjhQTagyz8IOdjbj .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-FjhQTagyz8IOdjbj .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FjhQTagyz8IOdjbj .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-FjhQTagyz8IOdjbj .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FjhQTagyz8IOdjbj .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-FjhQTagyz8IOdjbj .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-FjhQTagyz8IOdjbj .cluster text{fill:#333;}#mermaid-svg-FjhQTagyz8IOdjbj .cluster span{color:#333;}#mermaid-svg-FjhQTagyz8IOdjbj div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-FjhQTagyz8IOdjbj .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-FjhQTagyz8IOdjbj rect.text{fill:none;stroke-width:0;}#mermaid-svg-FjhQTagyz8IOdjbj .icon-shape,#mermaid-svg-FjhQTagyz8IOdjbj .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-FjhQTagyz8IOdjbj .icon-shape p,#mermaid-svg-FjhQTagyz8IOdjbj .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-FjhQTagyz8IOdjbj .icon-shape .label rect,#mermaid-svg-FjhQTagyz8IOdjbj .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-FjhQTagyz8IOdjbj .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-FjhQTagyz8IOdjbj .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-FjhQTagyz8IOdjbj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 创建功能分支
开发登录功能
功能完成
合并功能分支
删除功能分支
主分支 master
feature/login
提交变更
切换回主分支
分支被删除

4.4 分支合并与冲突解决

4.4.1 合并分支基础
bash 复制代码
# 将 <branch-name> 的历史并入「当前所在分支」(先 switch 到接收方,再 merge 来源方)
git merge <branch-name>

# 示例:把 feature/login 合进 master(须在 master 上执行 merge)
git switch master               # 接收合并结果的目标分支
git merge feature/login         # 无分叉时可能 fast-forward,有分叉则三方合并

# --no-ff:即使可快进也强制生成 merge commit,保留「曾有过 feature 分支」的拓扑
git merge --no-ff feature/login # 团队规范常用,便于 revert 整次功能

# --squash:把 feature 上所有改动压成一次 staged 变更,需再手动 commit
git merge --squash feature/login
git commit -m "feat: login module"   # squash 不会自动提交

【代码注释】 :合并方向口诀:站在要并入的分支上,merge 来源分支 。Fast-forward 时无 merge commit,历史呈直线;--no-ff 多一个合并节点,方便按功能整体回滚。--squash 适合 PR 前整理提交,但会丢失 feature 上的逐条 commit 作者与时间。冲突时 Git 暂停合并,解决后 git add + git commit(或 git merge --abort 放弃)。

4.4.2 合并冲突解决

当Git无法自动合并时,需要手动解决冲突。

bash 复制代码
# 1. merge/rebase 中断后,冲突文件会标为 both modified
git status                      # Unmerged paths 下列出待解决文件

# 2. 在编辑器中处理冲突标记(三者必须全部删除,不能只留标记):
# <<<<<<< HEAD          ← 当前检出分支(合并接收方)的内容
# 当前分支的代码
# =======               ← 分隔线
# 要合并进来的分支的代码
# >>>>>>> feature/login ← 被 merge 进来的分支名

# 3. 保存人工合并结果后,加入暂存区表示「此文件冲突已解决」
git add <conflicted-file>       # 每个冲突文件都要 add;可用 git add .

# 4. 完成合并(若 merge 已启动,一般无需 -m,Git 自带默认合并说明)
git commit                      # 或 merge 过程中已自动进入编辑器
# 放弃本次合并:git merge --abort

【代码注释】 :冲突只发生在同一文件相邻或重叠行 ;不同文件 Git 常自动合并。HEAD 在 merge 时指当前分支 (你 switch 过去的那条)。可用 git checkout --ours/--theirs <file> 快速选一侧,但复杂逻辑仍建议手改。VS Code / Cursor 的「Accept Current / Incoming / Both」对应上述三区。解决后务必跑测试再 commit;git merge --abort 可回到合并前状态。

实际冲突解决示例

javascript 复制代码
// 冲突前:两分支从同一祖先各自演进,Git 无法自动决定保留哪种业务逻辑
function calculateTotal(items) {
    return items.reduce((sum, item) => sum + item.price, 0);
}

// <<<<<<< HEAD ------ 当前分支(master)在合并时引入的「折扣」实现
function calculateTotal(items, discount = 0) {
    const total = items.reduce((sum, item) => sum + item.price, 0);
    return total * (1 - discount);
}
// ======= ------ 分隔符,不是注释,合并时必须整段删除
// >>>>>>> feature/tax-calculation 的另一侧:「税费」实现
function calculateTotal(items, tax = 0.1) {
    const subtotal = items.reduce((sum, item) => sum + item.price, 0);
    return subtotal * (1 + tax);
}
// >>>>>>> feature/tax-calculation

// 解决后:手工合并两特性------先折扣后税费(顺序需与产品规则一致)
function calculateTotal(items, discount = 0, tax = 0.1) {
    const subtotal = items.reduce((sum, item) => sum + item.price, 0);
    return subtotal * (1 - discount) * (1 + tax);
}

【代码注释】 :冲突标记是 Git 插入的文本 ,留在源码中会导致语法错误与安全风险。解决原则:理解两侧业务意图后合成 合理实现,而非简单二选一。合并后执行 git diff --check 可扫描是否残留 <<<<<<<;跑单元测试确认折扣与税费计算顺序符合需求,再 git add + git commit

4.5 实际项目分支策略

bash 复制代码
# 【电商项目分支管理示例】master 稳定 + develop 集成 + feature 并行

# 1. 同步主线,确保 feature 从最新稳定点分出
git switch master
git pull origin master          # 拉远程 master,减少合并时大范围冲突

# 2. 购物车功能:从 master 或 develop 创建独立分支(课堂常从 master 分)
git switch -c feature/shopcart  # -c:创建并切换,分支 tip = 当前 HEAD

# 3. 在 feature 上小步提交,便于 Code Review 与回滚
git add .
git commit -m "feat: add shopping cart functionality"

# 4. 模拟另一开发者:回到 master 再开订单分支(与 shopcart 并行)
git switch master
git switch -c feature/order

# 5. 订单模块独立提交,不与 shopcart 混在同一分支
git add .
git commit -m "feat: add order management"

# 6. 集成分支:先切到 develop,再 merge 各 feature(接收方是 develop)
git switch develop
git merge feature/shopcart      # 若有冲突在此解决
git merge feature/order

# 7. 已合并的 feature 可删本地指针,远程可选 push --delete
git branch -d feature/shopcart
git branch -d feature/order

【代码注释】git switch 时未提交改动会随你带到新分支 (若文件在两分支都存在),易造成「在 feature 上改却在 main 上提交」的混乱。切换前 git status 应干净,或 git stash push -m "..."。删除分支前确认已 merge 或已 push 备份。

4.6 git rebase:变基操作详解

rebase 将当前分支的提交"重新播放"到目标分支顶端,产生线性历史,视觉上更整洁。

rebase vs merge 对比

#mermaid-svg-oniCYZOqmievzZqs{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-oniCYZOqmievzZqs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oniCYZOqmievzZqs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oniCYZOqmievzZqs .error-icon{fill:#552222;}#mermaid-svg-oniCYZOqmievzZqs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oniCYZOqmievzZqs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oniCYZOqmievzZqs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oniCYZOqmievzZqs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oniCYZOqmievzZqs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oniCYZOqmievzZqs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oniCYZOqmievzZqs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oniCYZOqmievzZqs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oniCYZOqmievzZqs .marker.cross{stroke:#333333;}#mermaid-svg-oniCYZOqmievzZqs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oniCYZOqmievzZqs p{margin:0;}#mermaid-svg-oniCYZOqmievzZqs .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-oniCYZOqmievzZqs .cluster-label text{fill:#333;}#mermaid-svg-oniCYZOqmievzZqs .cluster-label span{color:#333;}#mermaid-svg-oniCYZOqmievzZqs .cluster-label span p{background-color:transparent;}#mermaid-svg-oniCYZOqmievzZqs .label text,#mermaid-svg-oniCYZOqmievzZqs span{fill:#333;color:#333;}#mermaid-svg-oniCYZOqmievzZqs .node rect,#mermaid-svg-oniCYZOqmievzZqs .node circle,#mermaid-svg-oniCYZOqmievzZqs .node ellipse,#mermaid-svg-oniCYZOqmievzZqs .node polygon,#mermaid-svg-oniCYZOqmievzZqs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oniCYZOqmievzZqs .rough-node .label text,#mermaid-svg-oniCYZOqmievzZqs .node .label text,#mermaid-svg-oniCYZOqmievzZqs .image-shape .label,#mermaid-svg-oniCYZOqmievzZqs .icon-shape .label{text-anchor:middle;}#mermaid-svg-oniCYZOqmievzZqs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-oniCYZOqmievzZqs .rough-node .label,#mermaid-svg-oniCYZOqmievzZqs .node .label,#mermaid-svg-oniCYZOqmievzZqs .image-shape .label,#mermaid-svg-oniCYZOqmievzZqs .icon-shape .label{text-align:center;}#mermaid-svg-oniCYZOqmievzZqs .node.clickable{cursor:pointer;}#mermaid-svg-oniCYZOqmievzZqs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-oniCYZOqmievzZqs .arrowheadPath{fill:#333333;}#mermaid-svg-oniCYZOqmievzZqs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-oniCYZOqmievzZqs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-oniCYZOqmievzZqs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oniCYZOqmievzZqs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oniCYZOqmievzZqs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oniCYZOqmievzZqs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-oniCYZOqmievzZqs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-oniCYZOqmievzZqs .cluster text{fill:#333;}#mermaid-svg-oniCYZOqmievzZqs .cluster span{color:#333;}#mermaid-svg-oniCYZOqmievzZqs div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-oniCYZOqmievzZqs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oniCYZOqmievzZqs rect.text{fill:none;stroke-width:0;}#mermaid-svg-oniCYZOqmievzZqs .icon-shape,#mermaid-svg-oniCYZOqmievzZqs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oniCYZOqmievzZqs .icon-shape p,#mermaid-svg-oniCYZOqmievzZqs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-oniCYZOqmievzZqs .icon-shape .label rect,#mermaid-svg-oniCYZOqmievzZqs .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oniCYZOqmievzZqs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-oniCYZOqmievzZqs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-oniCYZOqmievzZqs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} rebase(线性历史)
main: A→B→C
feat rebase 后: A→B→C→D'→E'
merge(保留分叉历史)
merge
main: A→B→C
main: A→B→C→M(合并提交)
feat: A→D→E

bash 复制代码
# 【基础变基】把 feature 上的提交「挪到」main 最新 tip 之后,形成线性历史
git switch feature/login      # 在要被变基的分支上操作
git fetch origin main         # 先更新参照分支(若 main 在远程)
git rebase main               # 等价于:摘下 feature 的 commit,接到 main 顶端重放
# Git 按提交顺序逐个应用;某步冲突 → 改文件 → git add → git rebase --continue

# 变基冲突处理循环
git add <resolved-file>
git rebase --continue         # 当前提交解决后,继续下一个
git rebase --skip             # 跳过当前提交(慎用,会丢该 commit 的改动)
git rebase --abort            # 放弃整个 rebase,回到 rebase 开始前状态

# 【交互式变基 -i】重写最近 N 条提交:压缩、改 message、调序、删除
git rebase -i HEAD~4          # 打开编辑器,对 4 条提交使用 pick/squash/reword 等

交互式变基操作指令

指令 作用
pick 保留该提交(默认)
reword 保留提交但修改 message
squash 合并到上一个提交(合并 message)
fixup 合并到上一个提交(丢弃本 message)
drop 删除该提交
edit 暂停以修改提交内容

经典场景:PR 前清理本地提交历史

bash 复制代码
# 开发过程中产生了很多琐碎提交
git log --oneline feature/payment
# abc1234 fix typo
# def5678 fix lint error
# ghi9012 wip: payment api
# jkl3456 feat: payment module init

# 合并到 PR 前,将 4 个提交压缩为 1 个
git rebase -i HEAD~4
# 将前 3 个改为 fixup,只保留最后一个 pick
# 结果:一个干净的 "feat: payment module" 提交

⚠️ rebase 黄金法则绝不对已推送到共享分支的提交做 rebase。Rebase 会改写提交哈希,强推后会让团队其他人的本地仓库历史与远程产生分歧,造成严重混乱。

bash 复制代码
# ✅ 个人 feature:变基到最新 main,再安全强推(改写的是自己的提交哈希)
git switch feature/my-feature
git fetch origin
git rebase origin/main        # 将 feature 上的 commit 接到 main 最新 tip 之后
git push --force-with-lease origin feature/my-feature
# --force-with-lease:若远程已有他人新提交则拒绝强推,比 --force 更安全

# ❌ 禁止:在共享分支 main 上 rebase 功能分支 → 改写全队历史
git switch main
git rebase feature              # 会导致所有人 pull 困难,甚至丢提交

4.7 git cherry-pick:精准摘取提交

cherry-pick 将指定提交的改动应用到当前分支,适合跨分支"搬运"特定修复。

bash 复制代码
# 将指定提交的 patch 应用到当前分支顶端(生成新 commit,哈希与源提交不同)
git cherry-pick abc1234

# 按顺序摘取多个离散提交
git cherry-pick abc1234 def5678

# 范围:左开右闭 (abc1234, def5678],不含 abc1234 本身
git cherry-pick abc1234..def5678

# 只应用改动到工作区/暂存区,不自动 commit(便于批量调整后一次提交)
git cherry-pick --no-commit abc1234
git status                    # 确认 diff 无误后再 git commit

# 冲突后
git cherry-pick --continue      # 同 merge:解决 → add → continue
git cherry-pick --abort         # 放弃本次 cherry-pick,回到操作前

经典场景一:将 bug 修复同步到多个维护版本

bash 复制代码
# 多版本线维护:同一 bugfix 需落到 v2、v3 两条 release 分支
git switch release/v3
git log --oneline -3
# abc1234 fix: null pointer in checkout flow  ← 在 v3 上已验证的修复

git switch release/v2           # 切到旧版本维护分支
git cherry-pick abc1234         # 应用相同 diff;冲突则说明 v2 代码结构不同,需手改
git push origin release/v2      # 新 commit 哈希 ≠ abc1234,但 patch 内容一致

经典场景二:从废弃分支中救回有用的提交

bash 复制代码
# 废弃分支中「单 commit」有价值,不必整分支 merge 进来
git log --oneline feature/experiment
# xyz9876 refactor: extract date formatter utility  ← 仅摘这一条
# abc1111 feat: experimental feature (整段实验不要)

git switch main
git cherry-pick xyz9876         # 只引入该提交的改动;与 merge 整分支相比历史更干净
# 若 xyz9876 依赖同分支其它提交,cherry-pick 可能冲突,需补依赖或手改

4.8 Detached HEAD 状态解析

切换到标签或直接检出某个 commit 哈希时,HEAD 不再指向任何分支,而是直接指向一个提交------这就是 **Detached HEAD(游离 HEAD)**状态。

bash 复制代码
# 以下操作使 HEAD 直接指向 commit/tag,而非分支名 → Detached HEAD
git switch --detach v1.0.0      # 检出标签快照(Git 2.23+ 推荐写法)
git switch --detach abc1234     # 检出指定哈希,用于审查历史
git switch --detach HEAD~3      # 检出祖先,临时对比旧版代码

# 终端警告含义:当前不在任何分支上,在此状态下的新提交无分支引用保护
# HEAD is now at abc1234...
# You are in 'detached HEAD' state.

Detached HEAD 下的操作规则

bash 复制代码
# ✅ 只读:查看历史文件、编译旧版、跑对比测试,不产生新 commit 则无风险
git switch --detach v1.2.0
cat src/utils.js              # 读的是 v1.2.0 树中的内容,非工作区最新编辑

# ⚠️ 危险:在游离态 git commit → 新提交只被 HEAD 引用,一切走分支即「悬空」
# git gc 后可能被回收,相当于丢提交

# ✅ 若必须在旧版本上改代码:第一时间建分支,把 HEAD 钉在分支上
git switch -c hotfix/from-v1.2.0   # 从当前游离 commit 拉出分支
git add .
git commit -m "fix: patch for v1.2.0"

从 Detached HEAD 恢复

bash 复制代码
# 方法1:丢弃游离态,回到已有分支(未在游离态提交时)
git switch main

# 方法2:游离态已有有用提交,用当前 HEAD 创建分支保存
git switch -c temp-branch     # 当前 commit 成为 temp-branch 的 tip

# 方法3:已切走导致「丢失」的游离提交,用 reflog 按哈希恢复
git reflog
# abc1234 HEAD@{1}: commit: my work in detached state
git switch -c rescued-branch abc1234

4.9 电商项目多分支协作流程

典型前端电商演练(与课堂 shopcart / order 场景一致):
#mermaid-svg-6KQwSsw2aRGLihHS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-6KQwSsw2aRGLihHS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-6KQwSsw2aRGLihHS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-6KQwSsw2aRGLihHS .error-icon{fill:#552222;}#mermaid-svg-6KQwSsw2aRGLihHS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-6KQwSsw2aRGLihHS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-6KQwSsw2aRGLihHS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-6KQwSsw2aRGLihHS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-6KQwSsw2aRGLihHS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-6KQwSsw2aRGLihHS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-6KQwSsw2aRGLihHS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-6KQwSsw2aRGLihHS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-6KQwSsw2aRGLihHS .marker.cross{stroke:#333333;}#mermaid-svg-6KQwSsw2aRGLihHS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-6KQwSsw2aRGLihHS p{margin:0;}#mermaid-svg-6KQwSsw2aRGLihHS .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-6KQwSsw2aRGLihHS .cluster-label text{fill:#333;}#mermaid-svg-6KQwSsw2aRGLihHS .cluster-label span{color:#333;}#mermaid-svg-6KQwSsw2aRGLihHS .cluster-label span p{background-color:transparent;}#mermaid-svg-6KQwSsw2aRGLihHS .label text,#mermaid-svg-6KQwSsw2aRGLihHS span{fill:#333;color:#333;}#mermaid-svg-6KQwSsw2aRGLihHS .node rect,#mermaid-svg-6KQwSsw2aRGLihHS .node circle,#mermaid-svg-6KQwSsw2aRGLihHS .node ellipse,#mermaid-svg-6KQwSsw2aRGLihHS .node polygon,#mermaid-svg-6KQwSsw2aRGLihHS .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-6KQwSsw2aRGLihHS .rough-node .label text,#mermaid-svg-6KQwSsw2aRGLihHS .node .label text,#mermaid-svg-6KQwSsw2aRGLihHS .image-shape .label,#mermaid-svg-6KQwSsw2aRGLihHS .icon-shape .label{text-anchor:middle;}#mermaid-svg-6KQwSsw2aRGLihHS .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-6KQwSsw2aRGLihHS .rough-node .label,#mermaid-svg-6KQwSsw2aRGLihHS .node .label,#mermaid-svg-6KQwSsw2aRGLihHS .image-shape .label,#mermaid-svg-6KQwSsw2aRGLihHS .icon-shape .label{text-align:center;}#mermaid-svg-6KQwSsw2aRGLihHS .node.clickable{cursor:pointer;}#mermaid-svg-6KQwSsw2aRGLihHS .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-6KQwSsw2aRGLihHS .arrowheadPath{fill:#333333;}#mermaid-svg-6KQwSsw2aRGLihHS .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-6KQwSsw2aRGLihHS .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-6KQwSsw2aRGLihHS .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6KQwSsw2aRGLihHS .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-6KQwSsw2aRGLihHS .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6KQwSsw2aRGLihHS .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-6KQwSsw2aRGLihHS .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-6KQwSsw2aRGLihHS .cluster text{fill:#333;}#mermaid-svg-6KQwSsw2aRGLihHS .cluster span{color:#333;}#mermaid-svg-6KQwSsw2aRGLihHS div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-6KQwSsw2aRGLihHS .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-6KQwSsw2aRGLihHS rect.text{fill:none;stroke-width:0;}#mermaid-svg-6KQwSsw2aRGLihHS .icon-shape,#mermaid-svg-6KQwSsw2aRGLihHS .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-6KQwSsw2aRGLihHS .icon-shape p,#mermaid-svg-6KQwSsw2aRGLihHS .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-6KQwSsw2aRGLihHS .icon-shape .label rect,#mermaid-svg-6KQwSsw2aRGLihHS .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-6KQwSsw2aRGLihHS .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-6KQwSsw2aRGLihHS .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-6KQwSsw2aRGLihHS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} merge
merge
测试通过 merge
master 稳定
develop 集成分支
feature/shopcart 购物车
feature/order 订单

bash 复制代码
# 1. 集成分支先与远程同步,再基于最新 develop 开 feature
git switch develop
git pull origin develop

# 2. 购物车:从 develop 分出 → 开发 → 合回 develop → 删本地 feature
git switch -c feature/shopcart   # 基于当前 develop tip
git add .
git commit -m "feat: shopping cart module"
git switch develop             # 合并接收方必须是 develop
git merge feature/shopcart
git branch -d feature/shopcart

# 3. 订单:同上,与 shopcart 并行、互不影响直到 merge 进 develop
git switch -c feature/order
git add .
git commit -m "feat: order module"
git switch develop
git merge feature/order
git branch -d feature/order

# 4. 测试通过后,develop → master 发布;push 触发部署
git switch master
git merge develop              # 生产线只收已测过的集成结果
git push origin master

【代码注释】 :课堂 02-project 等多含 master/develop 与 shopcart、order 等 feature 历史。用 git log --graph --oneline --all --decorate 对照本节流程,可直观看到 merge 节点与分支指针移动;配合 git branch -a 查看本地与 origin/* 跟踪分支。

4.10 分支模块归纳

操作 命令 注意
创建 git branch name / git switch -c name 基于当前 HEAD
切换 git switch name 工作区须干净
合并 git merge name 先切到被并入分支
删除 git branch -d name 未合并用 -D
变基 git rebase main 仅限个人分支,勿对共享分支使用
摘取 git cherry-pick <hash> 跨分支搬运特定提交
游离恢复 git switch -c new-branch Detached HEAD 下立刻创建分支保存工作

5. 远程仓库与协作开发

远程仓库是团队协作的基础,GitHub作为全球最大的代码托管平台,为开发者提供了强大的协作工具。

5.1 GitHub平台详解

**【专业背景】**GitHub于2008年上线,2018年被微软以75亿美元收购。截至2024年,GitHub拥有超过1亿注册用户,托管着超过3.3亿个代码仓库,是全球最大的开源社区。

核心功能

  • 代码托管:Git 仓库的远程存储
  • 协作开发:Pull Request、Code Review
  • 项目管理:Issues、Projects、Actions
  • 社区建设:Stars、Forks、Watchers

业界平台对照

平台 特点 典型场景
GitHub 开源生态最大、Actions CI 开源库、国际化协作
GitLab 内置 CI/CD、私有化部署 企业内网、DevOps 一体
Gitee 国内访问快 国内团队、教学演示
Bitbucket 与 Jira 集成 Atlassian 技术栈团队

探索高星仓库 :在 GitHub 搜索框输入 stars:>100000 可按 Star 数筛选热门项目,学习 .gitignore、CI 与分支策略。

5.2 远程仓库配置操作

bash 复制代码
# ── 远程仓库(remote)配置:本地分支与托管平台之间的命名与 URL 映射 ──

git remote                    # 列出远程短名,clone 后默认有 origin
git remote -v                 # -v:同时显示 fetch/push 两套 URL(HTTPS 或 SSH)

git remote add origin https://github.com/username/project.git
# 首次关联;origin 为约定俗成默认名,也可 add upstream 指向 fork 源仓库

git remote remove origin      # 仅删除本地 remote 配置,不删除 GitHub 上的仓库
git remote rename origin upstream   # 例如 fork 后把原仓库改为 upstream
git remote set-url origin git@github.com:username/new-project.git
# 在 HTTPS ↔ SSH 之间切换,或更换仓库地址

git remote show origin        # 显示默认分支、跟踪分支、push/pull 行为摘要

【代码注释】remote 是本地 .git/config 里的别名 + URL ,不是远程仓库本身。Fork 工作流常见:origin 指向自己的 fork,upstream 指向官方原仓,git fetch upstream 同步主线。git remote show 可查看 origin/main 等跟踪分支的 ahead/behind。团队应统一 HTTPS 或 SSH,避免有人用 token、有人用密钥导致权限混乱。

5.3 git fetch vs git pull:核心区别详解

这是团队协作中最容易混淆的概念之一,理解二者差异是写出安全协作流程的前提。

5.3.0 原理对比
复制代码
git fetch origin
  ↓
只下载远程数据到本地「远程跟踪分支」(origin/main 等)
不修改任何本地工作分支
工作区/暂存区完全不变

git pull origin main
  ↓ 等价于
git fetch origin          # 先下载
git merge origin/main     # 再自动合并到当前分支(或 rebase,取决于配置)
bash 复制代码
# ── git fetch:只同步数据,不修改当前分支(最安全的同步方式)──

git fetch origin                  # 下载所有远程分支的最新提交
git fetch origin main             # 只下载 main 分支(减少带宽)

# 查看「别人做了什么」而不影响自己的工作
git log HEAD..origin/main --oneline  # 列出远程比本地多的提交(即将被 merge 的内容)
git diff HEAD origin/main            # 对比本地与远程的代码差异

# 决定好后,再手动整合(merge 或 rebase)
git merge origin/main             # 把远程更新合并进当前本地分支
# 或:git rebase origin/main      # 以变基方式整合,线性历史

# ── git pull:fetch + 自动整合(默认 merge,推荐改为 rebase)──

git pull origin main              # 默认:fetch + merge,可能产生 merge bubble
git pull --rebase origin main     # fetch + rebase,保持线性历史(推荐)
git pull                          # 已设置 upstream 时,省略 remote 与分支名

# 全局设为 rebase 模式(推荐)
git config --global pull.rebase true
# 之后 git pull 默认行为等同于 git pull --rebase
何时用 fetch,何时用 pull?

#mermaid-svg-s4W8otSmjjar7cXR{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-s4W8otSmjjar7cXR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-s4W8otSmjjar7cXR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-s4W8otSmjjar7cXR .error-icon{fill:#552222;}#mermaid-svg-s4W8otSmjjar7cXR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-s4W8otSmjjar7cXR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-s4W8otSmjjar7cXR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-s4W8otSmjjar7cXR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-s4W8otSmjjar7cXR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-s4W8otSmjjar7cXR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-s4W8otSmjjar7cXR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-s4W8otSmjjar7cXR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-s4W8otSmjjar7cXR .marker.cross{stroke:#333333;}#mermaid-svg-s4W8otSmjjar7cXR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-s4W8otSmjjar7cXR p{margin:0;}#mermaid-svg-s4W8otSmjjar7cXR .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-s4W8otSmjjar7cXR .cluster-label text{fill:#333;}#mermaid-svg-s4W8otSmjjar7cXR .cluster-label span{color:#333;}#mermaid-svg-s4W8otSmjjar7cXR .cluster-label span p{background-color:transparent;}#mermaid-svg-s4W8otSmjjar7cXR .label text,#mermaid-svg-s4W8otSmjjar7cXR span{fill:#333;color:#333;}#mermaid-svg-s4W8otSmjjar7cXR .node rect,#mermaid-svg-s4W8otSmjjar7cXR .node circle,#mermaid-svg-s4W8otSmjjar7cXR .node ellipse,#mermaid-svg-s4W8otSmjjar7cXR .node polygon,#mermaid-svg-s4W8otSmjjar7cXR .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-s4W8otSmjjar7cXR .rough-node .label text,#mermaid-svg-s4W8otSmjjar7cXR .node .label text,#mermaid-svg-s4W8otSmjjar7cXR .image-shape .label,#mermaid-svg-s4W8otSmjjar7cXR .icon-shape .label{text-anchor:middle;}#mermaid-svg-s4W8otSmjjar7cXR .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-s4W8otSmjjar7cXR .rough-node .label,#mermaid-svg-s4W8otSmjjar7cXR .node .label,#mermaid-svg-s4W8otSmjjar7cXR .image-shape .label,#mermaid-svg-s4W8otSmjjar7cXR .icon-shape .label{text-align:center;}#mermaid-svg-s4W8otSmjjar7cXR .node.clickable{cursor:pointer;}#mermaid-svg-s4W8otSmjjar7cXR .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-s4W8otSmjjar7cXR .arrowheadPath{fill:#333333;}#mermaid-svg-s4W8otSmjjar7cXR .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-s4W8otSmjjar7cXR .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-s4W8otSmjjar7cXR .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-s4W8otSmjjar7cXR .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-s4W8otSmjjar7cXR .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-s4W8otSmjjar7cXR .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-s4W8otSmjjar7cXR .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-s4W8otSmjjar7cXR .cluster text{fill:#333;}#mermaid-svg-s4W8otSmjjar7cXR .cluster span{color:#333;}#mermaid-svg-s4W8otSmjjar7cXR div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-s4W8otSmjjar7cXR .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-s4W8otSmjjar7cXR rect.text{fill:none;stroke-width:0;}#mermaid-svg-s4W8otSmjjar7cXR .icon-shape,#mermaid-svg-s4W8otSmjjar7cXR .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-s4W8otSmjjar7cXR .icon-shape p,#mermaid-svg-s4W8otSmjjar7cXR .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-s4W8otSmjjar7cXR .icon-shape .label rect,#mermaid-svg-s4W8otSmjjar7cXR .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-s4W8otSmjjar7cXR .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-s4W8otSmjjar7cXR .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-s4W8otSmjjar7cXR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 是
否,直接同步
有改动
干净
需要同步远程更新
是否想先看看别人做了什么?
git fetch → 查看 diff/log → 再手动 merge/rebase
当前分支是否有未提交改动?
git stash → git pull --rebase → git stash pop
git pull --rebase
更安全,Code Review 友好
快捷,适合个人功能分支日常同步

经典场景一:同步主线前预览变更

bash 复制代码
# 早上上班,想知道队友昨天合并了什么,再决定是否同步
git fetch origin

# 查看远程 main 比本地多的提交列表
git log main..origin/main --oneline --decorate
# abc1234 feat: add dark mode toggle (张三, 3 hours ago)
# def5678 fix: cart quantity overflow bug (李四, 5 hours ago)

# 查看具体 diff(确认没有与自己工作冲突的改动)
git diff main origin/main -- src/components/

# 确认无冲突后,安全整合
git rebase origin/main

经典场景二:pull 冲突恢复

bash 复制代码
# git pull 遭遇冲突时(rebase 模式下)
git pull --rebase origin main
# CONFLICT (content): Merge conflict in src/api/user.js
# error: could not apply abc1234... feat: add user profile API

# 解决冲突
# 编辑 src/api/user.js,删除冲突标记,保留正确代码
git add src/api/user.js
git rebase --continue     # 继续应用下一个 commit

# 若冲突过多想放弃
git rebase --abort        # 回到 pull --rebase 之前的状态

【代码注释】git pull 的本质是「fetch + 整合」,整合方式由 pull.rebase 配置决定;git fetch 只是下载,整合权在开发者手中。推荐工作流git fetch → git log HEAD..origin/main → git rebase origin/main,比直接 pull 多一步审查,避免意外覆盖本地提交。在 CI/CD 场景中,流水线通常用 git fetch --all + git checkout 而不用 pull,确保精确控制检出版本。

5.4 场景化操作流程

5.4.1 场景一:本地已有项目,推送到GitHub
bash 复制代码
# 1. 在 GitHub 网页创建空仓库(不要勾选 README,避免与本地首次 push 冲突)

# 2. 进入已有本地仓库目录,关联远程
cd /path/to/your/project
git remote add origin https://github.com/username/project.git
# 若已存在 origin:用 git remote set-url origin <新地址>

# 3. 确保工作区已提交(未提交文件不会出现在远程)
git add .
git commit -m "Initial commit"

# 4. 首次推送并建立上游跟踪(upstream)
git push -u origin main       # -u 写入 branch.main.remote / merge,之后可简写 git push
# 旧仓库可能仍用 master:git push -u origin master

# 5. 同一分支后续推送(已设置 upstream 时)
git push                      # 等价于 git push origin main

【代码注释】-u--set-upstream)在 .git/config 记录 branch.<name>.mergegit status 会显示 ahead/behind。GitHub 2020 起默认主分支名为 main;本地若叫 master 可在推送时用 git push -u origin master:main 对齐。首次 push 若远程已有 README,需先 git pull origin main --rebase 再 push,或强制(仅个人空项目)--force

可运行示例 · 远程仓库配置清单

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>远程仓库两种场景</title>
  <style>body{font-family:system-ui;max-width:700px;margin:2rem auto} pre{background:#1e293b;color:#e2e8f0;padding:1rem;border-radius:8px}</style>
</head>
<body>
  <h1>场景 A:本地已有 → 推远程</h1>
  <pre>git remote add origin &lt;HTTPS或SSH地址&gt;
git push -u origin main</pre>
  <h1>场景 B:远程已有 → 克隆</h1>
  <pre>git clone &lt;地址&gt;
cd 项目目录
git switch -c dev origin/dev</pre>
  <p>克隆后仅默认检出主分支;其他分支用 <code>git branch -a</code> 查看,<code>git switch -c 本地名 origin/远程名</code> 跟踪。</p>
</body>
</html>

【代码注释】git remote rename 只改本地别名,远程仓库不变。set-url 可在 HTTPS(需 PAT/token)与 SSH 间切换;企业代理环境有时仅 HTTPS 可用。克隆后 git branch -a 查看 remotes/origin/*,用 git switch -c 本地名 origin/远程名 建立跟踪分支。

5.4.2 场景二:克隆GitHub项目到本地
bash 复制代码
# 1. 克隆远程仓库
git clone https://github.com/username/project.git
cd project

# 2. 查看所有分支(包括远程分支)
git branch -a

# 3. 创建并切换到远程开发分支
git switch -c develop origin/develop

# 4. 进行本地开发
# ... 代码编辑 ...
git add .
git commit -m "feat: add user authentication"

# 5. 推送到远程仓库
git push origin feature/auth

【代码注释】clone 默认只检出远程 default branch (多为 main),其它分支仅作 origin/xxx 远程跟踪引用。检出远程分支:git switch -c develop origin/develop。推送映射:git push origin 本地名:远程名;删除远程分支:git push origin --delete 分支名

5.5 多人协作完整工作流

#mermaid-svg-f7OOYJyMpDPxIGHH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-f7OOYJyMpDPxIGHH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-f7OOYJyMpDPxIGHH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-f7OOYJyMpDPxIGHH .error-icon{fill:#552222;}#mermaid-svg-f7OOYJyMpDPxIGHH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-f7OOYJyMpDPxIGHH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-f7OOYJyMpDPxIGHH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-f7OOYJyMpDPxIGHH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-f7OOYJyMpDPxIGHH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-f7OOYJyMpDPxIGHH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-f7OOYJyMpDPxIGHH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-f7OOYJyMpDPxIGHH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-f7OOYJyMpDPxIGHH .marker.cross{stroke:#333333;}#mermaid-svg-f7OOYJyMpDPxIGHH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-f7OOYJyMpDPxIGHH p{margin:0;}#mermaid-svg-f7OOYJyMpDPxIGHH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-f7OOYJyMpDPxIGHH .cluster-label text{fill:#333;}#mermaid-svg-f7OOYJyMpDPxIGHH .cluster-label span{color:#333;}#mermaid-svg-f7OOYJyMpDPxIGHH .cluster-label span p{background-color:transparent;}#mermaid-svg-f7OOYJyMpDPxIGHH .label text,#mermaid-svg-f7OOYJyMpDPxIGHH span{fill:#333;color:#333;}#mermaid-svg-f7OOYJyMpDPxIGHH .node rect,#mermaid-svg-f7OOYJyMpDPxIGHH .node circle,#mermaid-svg-f7OOYJyMpDPxIGHH .node ellipse,#mermaid-svg-f7OOYJyMpDPxIGHH .node polygon,#mermaid-svg-f7OOYJyMpDPxIGHH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-f7OOYJyMpDPxIGHH .rough-node .label text,#mermaid-svg-f7OOYJyMpDPxIGHH .node .label text,#mermaid-svg-f7OOYJyMpDPxIGHH .image-shape .label,#mermaid-svg-f7OOYJyMpDPxIGHH .icon-shape .label{text-anchor:middle;}#mermaid-svg-f7OOYJyMpDPxIGHH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-f7OOYJyMpDPxIGHH .rough-node .label,#mermaid-svg-f7OOYJyMpDPxIGHH .node .label,#mermaid-svg-f7OOYJyMpDPxIGHH .image-shape .label,#mermaid-svg-f7OOYJyMpDPxIGHH .icon-shape .label{text-align:center;}#mermaid-svg-f7OOYJyMpDPxIGHH .node.clickable{cursor:pointer;}#mermaid-svg-f7OOYJyMpDPxIGHH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-f7OOYJyMpDPxIGHH .arrowheadPath{fill:#333333;}#mermaid-svg-f7OOYJyMpDPxIGHH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-f7OOYJyMpDPxIGHH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-f7OOYJyMpDPxIGHH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-f7OOYJyMpDPxIGHH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-f7OOYJyMpDPxIGHH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-f7OOYJyMpDPxIGHH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-f7OOYJyMpDPxIGHH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-f7OOYJyMpDPxIGHH .cluster text{fill:#333;}#mermaid-svg-f7OOYJyMpDPxIGHH .cluster span{color:#333;}#mermaid-svg-f7OOYJyMpDPxIGHH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-f7OOYJyMpDPxIGHH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-f7OOYJyMpDPxIGHH rect.text{fill:none;stroke-width:0;}#mermaid-svg-f7OOYJyMpDPxIGHH .icon-shape,#mermaid-svg-f7OOYJyMpDPxIGHH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-f7OOYJyMpDPxIGHH .icon-shape p,#mermaid-svg-f7OOYJyMpDPxIGHH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-f7OOYJyMpDPxIGHH .icon-shape .label rect,#mermaid-svg-f7OOYJyMpDPxIGHH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-f7OOYJyMpDPxIGHH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-f7OOYJyMpDPxIGHH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-f7OOYJyMpDPxIGHH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 有冲突
无冲突
早上上班
git pull拉取最新代码
本地开发修改代码
git add添加到暂存区
git commit提交到本地仓库
下班前推送到远程
git pull再次检查冲突
是否有冲突
解决冲突
git push推送

详细协作流程

bash 复制代码
# 【第一天】克隆 + 个人 feature 分支(勿直接在 main 上开发)

git clone https://github.com/team/project.git
cd project

git switch -c feature/user-profile   # 从默认分支(main/master)分出

# 小步提交:每个逻辑块一次 commit,便于 review 与 bisect
git add src/components/UserProfile.js
git commit -m "feat: add user profile component"

git add src/styles/profile.css
git commit -m "style: profile component styling"

# 推送前先同步主线,减少 PR 时的大冲突
git pull origin main                  # 或 master;把主线合并进当前分支
git push -u origin feature/user-profile   # -u 建立上游,之后可 git push

# 【日常】早 pull、晚 push;功能分支上开发,合并经 PR

git pull origin main                  # 每日开始先更新(或 git fetch + rebase origin/main)

git add .
git commit -m "feat: complete profile editing"

git pull origin main                  # 推送前再拉一次
git push                              # 已 -u 时可省略远程与分支名

【代码注释】 :协作铁律------先 pull(或 pull --rebase)再 push ,减少 non-fast-forward 拒绝。功能分支开发用 git pull origin maingit rebase origin/main 同步主线;推送前 git status 确认无未提交改动。冲突在本地解决后再 push,勿把带 <<<<<<< 的文件推上远程。

5.6 团队协作权限管理

5.6.1 GitHub组织管理
bash 复制代码
# 【创建组织步骤】

# 1. GitHub页面操作
# 首页 -> 右上角+号 -> New Organization
# 选择免费计划(Free)
# 填写组织名称(避免使用中文)
# 填写联系邮箱

# 2. 邀请团队成员
# 组织首页 -> People -> Invite member
# 输入成员邮箱或GitHub用户名

# 3. 配置成员权限
# 组织首页 -> Settings -> Member privileges
# 权限级别:
# - Read: 只能读取,不能推送
# - Write: 可以推送代码
# - Admin: 完全管理权限

# 4. 创建团队仓库
# 组织首页 -> Repositories -> New repository
# 设置仓库可见性:Public/Private

【代码注释】 :组织、团队、仓库权限、分支保护规则均在 GitHub 网页 配置,Git CLI 无法替代。Read/Write/Admin 决定能否 push;保护分支可强制 PR + CI 通过才能合并,是生产主线的安全阀。

5.6.2 协作冲突解决
bash 复制代码
# 【两人先后改同一文件并 push 到 main】理想情况:团队用 feature + PR,少直接推 main

# 开发者 A(先 push 成功)
git pull origin main
# 编辑 src/utils/helpers.js
git add src/utils/helpers.js
git commit -m "fix: update helper function"
git push origin main

# 开发者 B(本地基于旧 main 提交,push 时被拒)
git pull origin main              # 本地已有 B 的 commit 时,pull 可能产生 merge commit
git add src/utils/helpers.js
git commit -m "feat: add new helper function"
git push origin main              # ! [rejected] non-fast-forward → 须先整合远程

git pull origin main              # 拉下 A 的提交并尝试自动合并
# 若同一区域都改了 → 冲突,文件中会出现 <<<<<<< / ======= / >>>>>>>

# 解决:删标记、合并逻辑、测试通过后
git add src/utils/helpers.js
git commit -m "merge: resolve conflicts in helpers.js with A's fix"
git push origin main

【代码注释】 :两人先后 push 同一分支时,后 push 者常需先 pull 合并。冲突时先沟通「保留哪侧业务逻辑」,避免盲目 --ours/--theirs 覆盖。合并提交 message 建议写明 merge: resolve helpers.js with @teammate 便于日后追溯。

5.7 SSH密钥免密登录配置

SSH密钥认证比HTTPS更安全且更便捷,适合频繁操作的开发者。

bash 复制代码
# 【SSH 密钥配置完整流程】用密钥对替代 HTTPS 密码/Token,适合日常 push/pull

# 1. 生成密钥对(GitHub 现也推荐 ed25519;RSA 需 -b 4096)
ssh-keygen -t ed25519 -C "your_email@example.com"
# 或:ssh-keygen -t rsa -b 4096 -C "your_email@example.com"
# -t:算法;-b:RSA 位长;-C:注释,常填邮箱便于在 GitHub 上辨认

# 2. 交互提示:保存路径(默认 ~/.ssh/id_ed25519)、私钥口令(建议设置)

# 3. 确认权限:私钥应为 600,.ssh 目录 700
ls -la ~/.ssh/
# id_ed25519      私钥 ------ 禁止上传、禁止进仓库、禁止发给他人
# id_ed25519.pub  公钥 ------ 整行粘贴到 GitHub → Settings → SSH keys

# 4. 复制公钥(必须完整一行,含开头的 ssh-ed25519 / ssh-rsa)
cat ~/.ssh/id_ed25519.pub
pbcopy < ~/.ssh/id_ed25519.pub    # macOS;Linux 可用 xclip -sel clip < ...

# 5. GitHub 网页:Settings → SSH and GPG keys → New SSH key → Title + Key

# 6. 测试(成功:Hi <username>! You've successfully authenticated...)
ssh -T git@github.com
# 首次出现 host authenticity 提示时输入 yes,写入 known_hosts

# 7. 用 SSH URL 克隆(格式:git@github.com:用户/仓库.git)
git clone git@github.com:username/project.git

# 8. 已有 HTTPS 远程改为 SSH
git remote set-url origin git@github.com:username/project.git
git remote -v

【代码注释】 :认证原理是服务器用公钥 验签、本机用私钥 签名,私钥永不传上网。泄露私钥须立刻在 GitHub 删除对应 key 并重新 ssh-keygen。多账号可在 ~/.ssh/config 里为 github.com 指定 IdentityFilessh -T 只测登录身份,不代替仓库的 read/write 权限检查。

可运行示例 · SSH 配置步骤页

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>SSH 免密登录 GitHub</title>
  <style>body{font-family:system-ui;max-width:640px;margin:2rem auto;line-height:1.7} ol{padding-left:1.2rem} code{background:#f1f5f9;padding:2px 6px}</style>
</head>
<body>
  <h1>SSH 密钥配置(5 步)</h1>
  <ol>
    <li>生成:<code>ssh-keygen -t rsa -C "your@email.com"</code></li>
    <li>查看公钥:<code>cat ~/.ssh/id_rsa.pub</code></li>
    <li>GitHub → Settings → SSH keys → New SSH key → 粘贴</li>
    <li>测试:<code>ssh -T git@github.com</code></li>
    <li>克隆:<code>git clone git@github.com:user/repo.git</code></li>
  </ol>
</body>
</html>

【代码注释】 :macOS 用 pbcopy < ~/.ssh/id_ed25519.pub;Windows 路径 %USERPROFILE%\.ssh\。HTML 示例中的 id_rsa 可替换为 id_ed25519。WSL 与 Windows 双环境时注意 ~/.ssh/config 指定 IdentityFile,避免推代码时用错密钥。

SSH密钥工作原理
GitHub服务器 本地Git 开发者 GitHub服务器 本地Git 开发者 #mermaid-svg-EveUIlJXryJFCn0A{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-EveUIlJXryJFCn0A .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-EveUIlJXryJFCn0A .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-EveUIlJXryJFCn0A .error-icon{fill:#552222;}#mermaid-svg-EveUIlJXryJFCn0A .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-EveUIlJXryJFCn0A .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-EveUIlJXryJFCn0A .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-EveUIlJXryJFCn0A .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-EveUIlJXryJFCn0A .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-EveUIlJXryJFCn0A .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-EveUIlJXryJFCn0A .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-EveUIlJXryJFCn0A .marker{fill:#333333;stroke:#333333;}#mermaid-svg-EveUIlJXryJFCn0A .marker.cross{stroke:#333333;}#mermaid-svg-EveUIlJXryJFCn0A svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-EveUIlJXryJFCn0A p{margin:0;}#mermaid-svg-EveUIlJXryJFCn0A .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-EveUIlJXryJFCn0A text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-EveUIlJXryJFCn0A .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-EveUIlJXryJFCn0A .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-EveUIlJXryJFCn0A .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-EveUIlJXryJFCn0A .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-EveUIlJXryJFCn0A #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-EveUIlJXryJFCn0A .sequenceNumber{fill:white;}#mermaid-svg-EveUIlJXryJFCn0A #sequencenumber{fill:#333;}#mermaid-svg-EveUIlJXryJFCn0A #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-EveUIlJXryJFCn0A .messageText{fill:#333;stroke:none;}#mermaid-svg-EveUIlJXryJFCn0A .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-EveUIlJXryJFCn0A .labelText,#mermaid-svg-EveUIlJXryJFCn0A .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-EveUIlJXryJFCn0A .loopText,#mermaid-svg-EveUIlJXryJFCn0A .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-EveUIlJXryJFCn0A .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-EveUIlJXryJFCn0A .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-EveUIlJXryJFCn0A .noteText,#mermaid-svg-EveUIlJXryJFCn0A .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-EveUIlJXryJFCn0A .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-EveUIlJXryJFCn0A .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-EveUIlJXryJFCn0A .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-EveUIlJXryJFCn0A .actorPopupMenu{position:absolute;}#mermaid-svg-EveUIlJXryJFCn0A .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-EveUIlJXryJFCn0A .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-EveUIlJXryJFCn0A .actor-man circle,#mermaid-svg-EveUIlJXryJFCn0A line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-EveUIlJXryJFCn0A :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} git push命令 发送连接请求 发送随机挑战字符串 使用私钥签名挑战 发送签名的挑战 使用存储的公钥验证签名 认证成功,允许推送 显示推送成功


6. 企业级Git工作流

专业的Git工作流是团队高效协作的基础,不同的团队规模和项目类型适合不同的工作流。

6.1 GitFlow工作流详解

**【专业背景】**GitFlow由Vincent Driessen于2010年提出,是史上最成功的分支模型之一,特别适合有明确发布周期的项目。

6.1.1 核心分支说明
bash 复制代码
# 【GitFlow分支类型详解】

# 1. master/main分支
# - 用途:生产环境代码,始终保持可发布状态
# - 约束:禁止直接在此分支开发
# - 保护:设置分支保护规则

# 2. develop分支
# - 用途:开发集成分支,包含最新开发功能
# - 来源:从master分支创建
# - 合并:功能分支合并到此,测试完成后合并到master

# 3. feature分支
# - 命名:feature/功能名称(如feature/user-auth)
# - 来源:从develop分支创建
# - 合并:完成后合并回develop
# - 生命周期:短期存在,功能完成后删除

# 4. release分支
# - 命名:release/版本号(如release/1.0.0)
# - 来源:从develop分支创建
# - 用途:发布准备,修复bug,更新版本号
# - 合并:完成后合并到develop和master

# 5. hotfix分支
# - 命名:hotfix/问题描述(如hotfix/critical-bug)
# - 来源:从master分支创建
# - 用途:紧急修复生产环境问题
# - 合并:完成后合并到master和develop

【代码注释】 :GitFlow 用分支类型 表达发布阶段:main 永远可部署,develop 集成日常功能,feature/* 短期,release/* 冻结版本只修 bug,hotfix/*main 分支持紧急修复再双向合并。禁止在 main 直接开发可避免未测代码进入生产。

6.1.2 GitFlow 分支职责一览
分支 来源 合并目标 职责
master/main --- --- 生产可发布代码
develop master master(经 release) 日常集成
feature/* develop develop 功能开发
release/* develop master + develop 预发布测试
hotfix/* master master + develop 线上紧急修复

课堂资料中的 GitFlow 示意图(如 images/GitFlow.png)可与下表及流程图对照;无本地图片时以下 mermaid 即可。

6.1.3 GitFlow工作流程图

#mermaid-svg-W56Od9rSIF6V1ZAq{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-W56Od9rSIF6V1ZAq .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-W56Od9rSIF6V1ZAq .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-W56Od9rSIF6V1ZAq .error-icon{fill:#552222;}#mermaid-svg-W56Od9rSIF6V1ZAq .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-W56Od9rSIF6V1ZAq .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-W56Od9rSIF6V1ZAq .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-W56Od9rSIF6V1ZAq .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-W56Od9rSIF6V1ZAq .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-W56Od9rSIF6V1ZAq .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-W56Od9rSIF6V1ZAq .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-W56Od9rSIF6V1ZAq .marker{fill:#333333;stroke:#333333;}#mermaid-svg-W56Od9rSIF6V1ZAq .marker.cross{stroke:#333333;}#mermaid-svg-W56Od9rSIF6V1ZAq svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-W56Od9rSIF6V1ZAq p{margin:0;}#mermaid-svg-W56Od9rSIF6V1ZAq .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-W56Od9rSIF6V1ZAq .cluster-label text{fill:#333;}#mermaid-svg-W56Od9rSIF6V1ZAq .cluster-label span{color:#333;}#mermaid-svg-W56Od9rSIF6V1ZAq .cluster-label span p{background-color:transparent;}#mermaid-svg-W56Od9rSIF6V1ZAq .label text,#mermaid-svg-W56Od9rSIF6V1ZAq span{fill:#333;color:#333;}#mermaid-svg-W56Od9rSIF6V1ZAq .node rect,#mermaid-svg-W56Od9rSIF6V1ZAq .node circle,#mermaid-svg-W56Od9rSIF6V1ZAq .node ellipse,#mermaid-svg-W56Od9rSIF6V1ZAq .node polygon,#mermaid-svg-W56Od9rSIF6V1ZAq .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-W56Od9rSIF6V1ZAq .rough-node .label text,#mermaid-svg-W56Od9rSIF6V1ZAq .node .label text,#mermaid-svg-W56Od9rSIF6V1ZAq .image-shape .label,#mermaid-svg-W56Od9rSIF6V1ZAq .icon-shape .label{text-anchor:middle;}#mermaid-svg-W56Od9rSIF6V1ZAq .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-W56Od9rSIF6V1ZAq .rough-node .label,#mermaid-svg-W56Od9rSIF6V1ZAq .node .label,#mermaid-svg-W56Od9rSIF6V1ZAq .image-shape .label,#mermaid-svg-W56Od9rSIF6V1ZAq .icon-shape .label{text-align:center;}#mermaid-svg-W56Od9rSIF6V1ZAq .node.clickable{cursor:pointer;}#mermaid-svg-W56Od9rSIF6V1ZAq .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-W56Od9rSIF6V1ZAq .arrowheadPath{fill:#333333;}#mermaid-svg-W56Od9rSIF6V1ZAq .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-W56Od9rSIF6V1ZAq .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-W56Od9rSIF6V1ZAq .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-W56Od9rSIF6V1ZAq .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-W56Od9rSIF6V1ZAq .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-W56Od9rSIF6V1ZAq .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-W56Od9rSIF6V1ZAq .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-W56Od9rSIF6V1ZAq .cluster text{fill:#333;}#mermaid-svg-W56Od9rSIF6V1ZAq .cluster span{color:#333;}#mermaid-svg-W56Od9rSIF6V1ZAq div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-W56Od9rSIF6V1ZAq .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-W56Od9rSIF6V1ZAq rect.text{fill:none;stroke-width:0;}#mermaid-svg-W56Od9rSIF6V1ZAq .icon-shape,#mermaid-svg-W56Od9rSIF6V1ZAq .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-W56Od9rSIF6V1ZAq .icon-shape p,#mermaid-svg-W56Od9rSIF6V1ZAq .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-W56Od9rSIF6V1ZAq .icon-shape .label rect,#mermaid-svg-W56Od9rSIF6V1ZAq .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-W56Od9rSIF6V1ZAq .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-W56Od9rSIF6V1ZAq .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-W56Od9rSIF6V1ZAq :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 创建
创建功能分支
功能完成
发布准备
修复发布bug
发布完成
同步代码
紧急修复
修复完成
同步修复
master 生产分支
develop 开发分支
feature 新功能
合并到develop
release 发布分支
合并到master
hotfix 修复分支

6.1.4 GitFlow实际操作示例
bash 复制代码
# 【GitFlow完整操作示例】

# ========== 1. 项目初始化 ==========
git init
git commit --allow-empty -m "Initial commit"
git switch -c develop

# ========== 2. 开发新功能 ==========
git switch -c feature/user-auth
# ... 开发用户认证功能 ...
git add .
git commit -m "feat: implement user authentication"

# 功能完成,合并到develop
git switch develop
git merge --no-ff feature/user-auth
git branch -d feature/user-auth

# ========== 3. 准备发布 ==========
git switch -c release/1.0.0

# 修复发现的bug,更新版本号
# ... 测试和修复 ...
vim package.json  # 更新版本号为1.0.0
git add .
git commit -m "release: bump version to 1.0.0"

# 发布完成,合并到master和develop
git switch master
git merge --no-ff release/1.0.0
git tag -a v1.0.0 -m "Release version 1.0.0"

git switch develop
git merge --no-ff release/1.0.0
git branch -d release/1.0.0

# ========== 4. 生产环境紧急修复 ==========
git switch master
git switch -c hotfix/critical-security-fix

# ... 修复安全漏洞 ...
git add .
git commit -m "hotfix: fix critical security vulnerability"

# 修复完成,合并到master和develop
git switch master
git merge --no-ff hotfix/critical-security-fix
git tag -a v1.0.1 -m "Hotfix version 1.0.1"

git switch develop
git merge --no-ff hotfix/critical-security-fix
git branch -d hotfix/critical-security-fix

【代码注释】 :示例中 merge --no-ff 保留功能/发布/热修的合并节点,便于按分支整体 revert。release 合并到 main 时打 tag -a 标记版本;hotfix 须同时合并 maindevelop,避免修复只存在于生产线。小团队若发布频繁,可简化为 GitHub Flow 降低分支管理成本。

6.2 GitHub Flow工作流

**【专业背景】**GitHub Flow是2011年提出的简化版工作流,特别适合持续部署的云服务项目。

bash 复制代码
# 【GitHub Flow核心特点】

# 1. 始终保持master分支可部署
# 2. 新功能从master分支创建feature分支
# 3. 功能完成后提交Pull Request
# 4. 代码Review通过后合并到master
# 5. 合并后立即部署

# ========== 操作示例 ==========

# 1. 从master创建功能分支
git switch master
git pull origin master
git switch -c feature/payment-integration

# 2. 开发功能
# ... 代码编辑 ...
git add .
git commit -m "feat: add payment gateway integration"

# 3. 推送到远程,创建Pull Request
git push origin feature/payment-integration

# 4. 在GitHub网页上创建PR,请求合并到master
# 5. 团队成员进行代码Review
# 6. 根据反馈修改代码
git add .
git commit -m "fix: address review comments"
git push origin feature/payment-integration

# 7. PR被批准后,通过GitHub网页合并到master
# 8. 删除功能分支
git switch master
git pull origin master
git branch -d feature/payment-integration

【代码注释】 :GitHub Flow 只有 main + 短期 feature/*,合并必经 PR + Review + CI 绿main 须随时可部署;步骤 3--7 在网页完成 PR,避免本地直接 merge 到 main。适合 Web/SaaS;不适合需长期并行维护 v1/v2 的客户端发版节奏。

6.3 GitLab Flow工作流

**【专业背景】**GitLab Flow结合了GitFlow的分支管理和GitHub Flow的简洁性,增加了环境分支的概念。

bash 复制代码
# 【GitLab Flow工作流】

# 1. master分支:主开发分支
# 2. 环境分支:staging、production等
# 3. 功能分支:从master创建,合并回master

# ========== 操作示例 ==========

# 1. 开发新功能
git switch master
git switch -c feature/new-dashboard

# 2. 完成功能,合并到master
git switch master
git merge feature/new-dashboard
git push origin master

# 3. 部署到staging环境
git switch -c staging master
git push origin staging

# 4. staging测试通过后,部署到生产环境
git switch -c production staging
git push origin production

【代码注释】 :GitLab Flow 在 main 之上用 环境分支stagingproduction)表达部署阶段:代码沿 main → staging → production 晋升,每步对应一次部署与验收。与 GitFlow 比分支更少,但需规范谁可推进环境分支,防止配置漂移。示例中 git switch -c staging master 表示从 main 创建/更新预发分支。

6.4 Trunk-Based Development(主干开发)

【专业背景】Trunk-Based Development(TBD)是 Google、Facebook、Netflix 等大型科技公司广泛采用的开发模式,被《DevOps 加速》(DORA 研究报告)认定为高效能团队的核心实践之一

核心理念

TBD 的核心是:所有开发者直接向主干(trunk/main)提交代码,或通过极短生命周期(≤1天)的功能分支集成,彻底避免长期分支带来的集成噩梦。

复制代码
GitFlow(传统):
  main ─────────────────────────────── 发布
  develop ──────────────────────────── 集成
  feature/A ──────── (2周) ─────────── 合并
  feature/B ───── (3周) ──────────── 合并
  ↑ 越来越长的分支 = 越来越大的集成风险

Trunk-Based Development:
  main ──●──●──●──●──●──●──●──●────── 持续部署
         ↑  ↑  ↑  ↑  ↑  ↑
    小功能 小功能 小功能 小功能(每次提交均可部署)
关键实践与命令
bash 复制代码
# ── 核心:功能分支生命周期 ≤ 1天,早分早合 ──

# 1. 每日从最新 main 开出 feature 分支
git switch main
git pull --rebase origin main        # 始终从最新 main 分出
git switch -c feature/add-dark-mode  # 命名简短,当天完成

# 2. 小步提交:每个逻辑单元一次 commit(目标:PR ≤ 400 行 diff)
git add src/theme/dark.css
git commit -m "feat: add dark mode CSS variables"

git add src/hooks/useTheme.js
git commit -m "feat: theme switch hook with localStorage persistence"

# 3. 提交 PR 前同步主干(避免 CI 失败)
git fetch origin
git rebase origin/main               # 将本次改动「接」到最新 main 之后

# 4. PR 合并后立即删除分支(不留「积压」分支)
git push origin feature/add-dark-mode
# → PR Review → CI 绿灯 → Squash Merge 到 main → 自动部署
git push origin --delete feature/add-dark-mode

# ── Feature Flag(功能开关):未完成的大功能仍能持续集成 ──
# 大功能分多次提交,用开关隐藏「未就绪」的 UI
javascript 复制代码
// Feature Flag 示例:功能代码已在 main,但默认关闭
// 不影响其他队友每天集成,CI 也不会因「半成品」页面失败
const FEATURE_FLAGS = {
  darkMode: process.env.REACT_APP_DARK_MODE === 'true',   // 环境变量控制
  newCheckout: false,    // 硬编码关闭,待 QA 通过后改为 true 再合并
};

function App() {
  return (
    <Router>
      {FEATURE_FLAGS.darkMode && <ThemeProvider />}
      {FEATURE_FLAGS.newCheckout ? <NewCheckout /> : <LegacyCheckout />}
    </Router>
  );
}
三大工作流横向对比
维度 GitFlow GitHub Flow Trunk-Based Dev
分支数量 多(5类) 少(main + feature) 最少(main + 短期 feature)
集成频率 低(周/月级) 中(天级) 高(小时/天级)
适合团队规模 大型,多版本并行 中型,SaaS 大型,高频发布
CI/CD 要求 一般 较高 极高(测试覆盖率高)
学习曲线 较高 中(需配套 Feature Flag)
代表企业 传统企业软件 GitHub 自身 Google、Netflix、Facebook

【代码注释】 :TBD 的成功前提是高测试覆盖率 + 完善 CI ------主干随时可部署意味着每次提交都须通过自动化质量门禁。Feature Flag 解决了「代码已合并但功能未就绪」的矛盾,常与 LaunchDarkly、GrowthBook 等工具集成,实现灰度发布与 A/B 测试。小团队或需严格版本管理的客户端产品(App Store 审核周期长)更适合 GitHub Flow 或 GitFlow;大型后端服务/SaaS 选 TBD 可将平均 PR 合并时间从天缩短到小时。


7. Git高级技巧与最佳实践

7.1 高级命令技巧

bash 复制代码
# 【Git 高级操作技巧】

# 1. 交互式暂存:按 hunk 审查,y/n/s/e 决定是否纳入本次 commit
git add -p                      # 适合「同一文件只提交部分改动」的精细提交

# 2. 交互式变基:整理最近 3 条提交(squash、reword、drop、调序)
git rebase -i HEAD~3            # 仅用于未推送或团队允许改写历史的分支

# 3. 储藏:把工作区+暂存区改动压入栈,换分支处理紧急任务
git stash                       # 等价于 stash push(默认不含 untracked)
git stash push -m "wip: login form half done"

git stash list                  # stash@{0} 为栈顶,越新数字越小
git stash pop                   # 应用栈顶并删除该条 stash
git stash apply stash@{1}       # 应用但不删除,可重复套用同一储藏

# 4. 单文件全历史 diff(含 rename 追踪)
git log --follow --patch -- filename.txt

# 5. 在当前 HEAD 树中全文搜索(比 rg 快,只搜已提交版本)
git grep "function_name"

# 6. 二分定位引入 bug 的提交(见 7.3.1 详述)
git bisect start
git bisect bad                  # 当前检出版本有问题
git bisect good v1.0.0          # 已知正常的 tag 或 commit
# 循环:测试 → git bisect good|bad,直到打印 first bad commit
git bisect reset                # 结束并回到 bisect 开始前分支

# 7. 撤销最后一次提交,改动保留在暂存区(可改 message 再提交)
git reset --soft HEAD~1

# 8. 修改最后一次提交(未 push 时);追加文件用 git add 后 --amend
git commit --amend -m "fix: correct message"

# 9. 空提交:不改变树,仅新增 commit 节点(触发 CI、占位)
git commit --allow-empty -m "ci: trigger pipeline"

【代码注释】add -p 是提交粒度控制的核心技能;rebase -ireset 会改写历史,禁止 对已推送共享分支随意使用。stash 默认不储藏未跟踪新文件,需 --include-untrackedbisect 复杂度 O(log n),适合「不知何时引入」的回归。--amend 会改变最后一次 commit 的哈希,若已 push 需 push --force-with-lease(仅个人分支)。

7.1.1 git stash 进阶用法

stash 是开发过程中最常用的"救急"工具,把未完成的工作临时存起来,腾出干净的工作区处理紧急任务。

bash 复制代码
# 基础储藏:保存已跟踪文件的修改 + 已暂存状态,工作区恢复为干净
git stash                       # 栈顶记为 stash@{0},可多次 stash 形成栈

# 带说明的储藏(多条 stash 时必备,否则 list 难以辨认)
git stash push -m "feat: half-done user profile form"

# 连同未跟踪(Untracked)新文件一起储藏
git stash push --include-untracked -m "wip: new feature files"

# 含被 .gitignore 忽略的文件(体积大、慎用,一般用于临时全量备份)
git stash push --all -m "full backup"

git stash list
# stash@{0}: On feature/login: feat: half-done user profile form  ← 最新
# stash@{1}: On main: wip: quick experiment

git stash pop                   # apply 栈顶 + drop;冲突时 stash 仍保留
git stash apply stash@{1}       # 只应用不删除,可对多分支重复套用同一储藏

git stash show -p stash@{0}     # -p:查看该条 stash 相对 HEAD 的完整 patch

# 基于 stash 创建分支并应用(避免 pop 到错误分支;成功后自动 drop 该 stash)
git stash branch feature/saved-work stash@{0}

git stash drop stash@{1}        # 删除单条
git stash clear                 # 清空整个 stash 栈,不可恢复

经典场景一:紧急 Bug 打断开发

bash 复制代码
# 正在开发新功能,突然收到线上 P0 Bug 通知
git stash push -m "wip: add user avatar upload (50% done)"

# 切到 main,创建 hotfix 分支
git switch main
git switch -c hotfix/login-crash
# ... 修复 bug ...
git commit -m "fix: prevent null pointer on login"
git switch main
git merge hotfix/login-crash
git push origin main

# Bug 修完,恢复原来的工作
git switch feature/user-profile
git stash pop
# 继续开发头像上传功能

经典场景二:发现 stash 在错误分支上,转移到正确分支

bash 复制代码
# 发现 stash@{0} 的代码应该在 feature/order 分支上工作
git stash branch feature/order stash@{0}
# Git 自动:切换到 feature/order → apply stash → drop stash

【代码注释】stash 存的是工作区+暂存区 快照,不是 commit;切换分支前 git status 不干净时优先 stash。pop = apply + drop;若 apply/pop 产生冲突,stash 条目仍保留 ,解决冲突后需手动 dropstash branch 能在新分支上应用储藏,避免污染当前分支。储藏过久可能因底层文件变更导致冲突,宜尽早 pop。

7.2 Git钩子(Hooks)配置

Git钩子是在特定事件发生时自动执行的脚本,可以用于代码质量检查、自动部署等。

bash 复制代码
# 【Git Hooks配置示例】

# 1. 客户端钩子目录
cd .git/hooks/

# 2. 创建pre-commit钩子(提交前检查)
cat > pre-commit << 'EOF'
#!/bin/bash
# 运行代码检查
npm run lint
# 如果检查失败,阻止提交
if [ $? -ne 0 ]; then
    echo "代码检查失败,请修复后再提交"
    exit 1
fi
EOF
chmod +x pre-commit

# 3. 创建commit-msg钩子(检查提交信息)
cat > commit-msg << 'EOF'
#!/bin/bash
# 检查提交信息格式
commit_regex='^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .{1,50}'
if ! grep -qE "$commit_regex" $1; then
    echo "提交信息格式错误,请使用约定式提交格式"
    echo "示例: feat: add user login"
    exit 1
fi
EOF
chmod +x commit-msg

# 4. 创建post-merge钩子(合并后操作)
cat > post-merge << 'EOF'
#!/bin/bash
# 合并后自动安装依赖
if [ -f package.json ]; then
    npm install
fi
EOF
chmod +x post-merge

【代码注释】 :Hooks 位于 .git/hooks/,文件名固定(如 pre-commit),chmod +x 后生效;exit 1 阻断后续 Git 操作。pre-commit 跑 lint、commit-msg 校验 Conventional Commits、post-merge 在 pull 后装依赖。Hooks 默认不随仓库共享 ,团队常用 Husky + lint-staged 将钩子配置写入项目并提交。

7.3 Git别名配置

bash 复制代码
# 【Git别名配置】

# 1. 常用别名设置
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.unstage 'reset HEAD --'
git config --global alias.last 'log -1 HEAD'
git config --global alias.visual 'log --graph --oneline --all'

# 2. 复杂别名
git config --global alias.lg "log --graph --oneline --all --decorate"
git config --global alias.amend "commit --amend --no-edit"
git config --global alias.undo "reset --soft HEAD~1"

# 3. 使用别名
git co main          # 等同于 git checkout main
git br               # 等同于 git branch
git ci -m "message"  # 等同于 git commit -m "message"

# 4. 查看所有别名
git config --global --get-regexp alias

【代码注释】 :别名存于 ~/.gitconfig[alias],整条命令用引号包裹(如 lg)。unstage 映射 reset HEAD -- 在 Git 2.23+ 可改为 restore --staged。勿用与内置子命令冲突的短名;团队文档应列出别名表。undo = reset --soft HEAD~1 仅适用于未推送的最后一次提交。

7.3.1 git bisect:二分法快速定位引入 Bug 的提交

当你不知道哪次提交引入了 Bug,bisect 用二分查找帮你在 O(log n) 次内锁定"罪犯提交"。

bash 复制代码
# 1. 进入二分模式(会 detached 或移动 HEAD 到中间提交,结束前勿在 bisect 中开发)
git bisect start

# 2. 标记当前检出为「有问题」
git bisect bad

# 3. 标记已知正常的锚点(tag、release 分支或旧 commit)
git bisect good v1.0.0
# Git 检出 good 与 bad 之间的中间 commit,提示约剩 N 步

# 4. 在当前中间版本上跑测试/复现 bug,再标记结果
npm test                        # 或手动验证
git bisect bad                  # 仍有 bug → 问题在更早 half
# 或 git bisect good            # 正常 → 问题在更晚 half

# 5. 重复测试与 good/bad,直到:
# abc1234 is the first bad commit
# Author / Date / Summary ...

# 6. 结束并回到 bisect start 前的分支位置
git bisect reset

# 全自动:由脚本判定 good(0) / bad(非0),无需人工反复 checkout
git bisect start
git bisect bad HEAD
git bisect good v1.0.0
git bisect run npm test         # package.json 中 test 须能反映该 bug 是否存在

【代码注释】bisectgood, bad 闭区间内二分,复杂度约 log₂(N) 次测试。good 必须确实无 bug,bad 必须有 bug,否则结果无意义。过程中 HEAD 会反复跳转,应用 git bisect reset 归位。git bisect run 要求测试脚本 exit 0/非0 稳定对应 good/bad。找到 commit 后可用 git show abc1234 看引入改动的文件列表。

7.3.2 git worktree:同时检出多个分支

worktree 让你无需切换分支就能在另一个目录打开同一仓库的不同分支,适合同时处理多个版本。

bash 复制代码
# 在同一仓库再挂一个工作目录,检出另一分支(共享 .git,对象库不重复)
git worktree add ../project-hotfix hotfix/login-crash
# 路径 ../project-hotfix 为新建目录;第二参数为分支名(不存在时可 -b 新建)

cd ../project-hotfix
git log --oneline -3
# 在此目录修复、commit;主目录仍停留在原分支,互不切换

cd ../project-main              # 主 worktree 目录

git worktree list               # 显示各 worktree 路径、HEAD、分支

git worktree remove ../project-hotfix   # 解除关联;目录若仍有文件需先清空或加 --force

【代码注释】worktree 解决「同一仓库不能同时 checkout 两个分支」的限制,适合 v1 热修 + v2 并行开发、或同时跑两个 npm start(不同端口)。与 stash 区别:stash 隐藏改动;worktree 是真实第二工作区 。注意同一分支不能被两个 worktree 同时检出。删除用 git worktree remove;遗留目录可 git worktree prune 清理失效注册。

7.4 git blame:代码考古与责任追溯

git blame 是代码审查与 Bug 调试的利器------它为文件的每一行标注「最后是谁、在哪次提交中修改了它」。

bash 复制代码
# 基础用法:为每一行打上 commit 哈希、作者、时间
git blame src/utils/helpers.js

# 输出格式示例:
# a1b2c3d4 (张三    2024-03-15 10:23:41 +0800  42) export function formatDate(date) {
# e5f6g7h8 (李四    2024-05-22 14:07:12 +0800  43)   return date.toLocaleDateString('zh-CN');
# a1b2c3d4 (张三    2024-03-15 10:23:41 +0800  44) }

# 仅查看指定行范围(大文件时避免刷屏)
git blame -L 40,60 src/utils/helpers.js   # -L:行号范围
git blame -L /formatDate/,+10 src/utils/helpers.js  # 从匹配行开始的 10 行

# 忽略空白字符改动(格式化工具引起的行改动不影响追溯真实改动者)
git blame -w src/utils/helpers.js

# 跨文件追踪:-C 检测从其他文件复制过来的代码(代码移动后仍显示原始作者)
git blame -C -C src/utils/helpers.js

# 查看某次历史提交时该文件的状态(时光机:「这行 2023 年是谁写的」)
git blame abc1234 -- src/utils/helpers.js

经典场景一:发现 Bug,定位责任提交

bash 复制代码
# 步骤1:找到 bug 所在行
# 假设 calculateDiscount 函数第 58 行有问题
git blame -L 55,62 src/pricing/discount.js

# 输出:
# 3f4e5d6c (王五    2024-12-01 09:15:33 +0800  58)   if (user.vip && discount > 0.5) {

# 步骤2:查看这次提交的完整改动
git show 3f4e5d6c

# 步骤3:理解业务背景(查看提交关联的 PR/Issue)
git log 3f4e5d6c -1 --format="%H %s %b"
# 通常提交说明里包含 "closes #123" 或 "JIRA-456" 等关联信息

经典场景二:Code Review 中快速了解代码历史

bash 复制代码
# 为什么这里有个魔法数字 0.85?blame 一下
git blame -L 30,35 src/api/payment.js
# abc1234 (赵六  2024-09-10 ...) const TAX_RATE = 0.85;

# 查看提交说明,找到业务原因
git show abc1234 --stat
# "fix: apply correct VAT rate for EU customers (closes #892)"
# 原来是增值税率,不是魔法数字 ------ 加个注释说明

【代码注释】git blame 显示的是「最后一次修改该行 的提交」,不是「写出 bug 的人」------可能某次格式化整文件导致所有行都指向同一提交。-w 忽略空白可避开这类噪音;-C -C 可跨文件追踪代码移动。VS Code 安装 GitLens 插件后,鼠标悬浮即可看到 blame 信息,无需命令行。

git blame 与 git log 组合使用(最强代码考古流程)

bash 复制代码
# 1. blame 找到「最后改过此行」的 commit
git blame -L 100,105 src/checkout/cart.js
# xyz9876 (某人 2025-01-15) const MAX_ITEMS = 99;

# 2. 查看那次 commit 改了哪些文件
git show --stat xyz9876

# 3. 再往前追:这行最初是哪次 commit 引入的?(按 pickaxe 搜索)
git log -S "MAX_ITEMS = 99" --oneline -- src/

# 4. 查看完整变更轨迹(含 rename)
git log --follow --patch -S "MAX_ITEMS" -- src/checkout/cart.js

7.5 git submodule:管理项目中的子模块

git submodule 允许一个 Git 仓库引用另一个仓库(子模块)的特定提交,常用于管理共享组件库、第三方依赖或微前端架构中的子项目。

bash 复制代码
# ── 添加子模块 ──
# 将外部仓库引入为当前项目的子模块(锁定到某个具体 commit)
git submodule add https://github.com/org/ui-library.git packages/ui
# 此操作:
#   1. 克隆 ui-library 到 packages/ui/
#   2. 创建 .gitmodules 记录子模块配置
#   3. 在父仓库中记录「引用 ui-library 的哪个 commit」

# 查看 .gitmodules 内容(子模块配置清单)
cat .gitmodules
# [submodule "packages/ui"]
#     path = packages/ui
#     url = https://github.com/org/ui-library.git

# ── 克隆含子模块的仓库(两步走)──

# 方式1:克隆时自动初始化(推荐)
git clone --recurse-submodules https://github.com/org/main-project.git

# 方式2:已克隆但未初始化(常见问题:packages/ui 目录为空)
git submodule init       # 读取 .gitmodules,注册子模块
git submodule update     # 实际克隆子模块并 checkout 到记录的 commit

# 合并一步:
git submodule update --init --recursive   # --recursive 处理嵌套子模块

# ── 更新子模块到最新版本 ──

# 进入子模块目录,像普通仓库一样操作
cd packages/ui
git fetch origin
git switch main
git pull

# 回到父仓库,提交「引用了子模块的新 commit」这一变更
cd ../..
git add packages/ui      # 父仓库的改动是「子模块指针从旧 hash → 新 hash」
git commit -m "chore: upgrade ui-library to v2.1.0"

# 或者批量更新所有子模块到各自远程最新
git submodule update --remote --merge

# ── 查看子模块状态 ──
git submodule status
# abc1234 packages/ui (v2.0.0-5-gabc1234)
# ↑ 哈希前无标记=正常;+ 表示子模块有新改动待提交;- 表示未初始化

# ── 删除子模块(三步操作)──
git submodule deinit -f packages/ui         # 从 .git/config 中注销
git rm -f packages/ui                       # 从工作区和 .gitmodules 移除
rm -rf .git/modules/packages/ui             # 清理残留的对象库
git commit -m "chore: remove ui submodule"

经典场景:monorepo 与子模块选型

复制代码
场景           推荐方案        理由
─────────────────────────────────────────────────────────────
共享 UI 组件库   git submodule   组件库有独立版本、独立 CI、独立发布
第三方只读依赖   npm/yarn        包管理器更适合,submodule 过重
微前端子应用    git submodule   各子应用团队独立仓库,父仓库锁定版本
同一团队多包    monorepo (pnpm) 无需版本锁定,统一构建更高效

【代码注释】 :子模块的核心是「父仓库只存子模块的 commit 哈希 」,不存子模块的代码------这意味着 git clone 父仓库后子目录默认为空,须 submodule update --init 填充。子模块版本升级须在父仓库提交一次「更新子模块指针」的 commit,这让版本管理非常清晰但也是子模块最容易让新手困惑的地方。与 npm 包相比,submodule 适合「需要访问源码、可能需要 hotfix 子模块再同步回来」的场景;纯依赖首选包管理器。

7.6 团队协作最佳实践

bash 复制代码
# 【团队Git协作规范】

# 1. 提交信息规范(约定式提交)
feat: 新功能
fix: 修复bug
docs: 文档修改
style: 代码格式调整
refactor: 重构代码
test: 测试相关
chore: 构建/工具链相关

# 示例:
git commit -m "feat: add user authentication"
git commit -m "fix: resolve login timeout issue"
git commit -m "docs: update API documentation"

# 2. 分支命名规范
feature/功能描述
bugfix/问题描述
hotfix/紧急修复
release/版本号
dev/开发者姓名

# 示例:
git switch -c feature/user-profile
git switch -c bugfix/login-error
git switch -c hotfix/security-patch

# 3. 代码审查流程
# - 创建Pull Request
# - 填写PR模板(功能描述、测试情况、相关Issue)
# - 至少一人审查通过
# - 通过CI检查
# - 合并到主分支

# 4. 分支保护规则
# - master/main分支设为保护分支
# - 要求Pull Request审查
# - 要求状态检查通过
# - 限制推送权限

【代码注释】 :约定式提交(feat:/fix:/chore:)便于自动生成 CHANGELOG 与语义化版本。分支名与 Issue 编号对应(如 feature/123-login)可追溯需求。保护 main + 必过 CI + 至少一人 Approve,比线上 revert 成本低。PR 模板应要求:测了什么、影响范围、回滚方案。


8. 故障排除与问题解决

8.1 常见问题解决方案

bash 复制代码
# 【Git常见问题及解决方案】

# 1. 忘记提交敏感信息
# 问题:不小心提交了密码、API密钥等敏感信息
# 解决:
git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch config/secrets.js" \
  --prune-empty --tag-name-filter cat -- --all

# 2. 误删分支恢复
# 问题:不小心删除了重要分支
# 解决:
git reflog           # 查找分支的最后一个提交
git switch -c recovered-branch abc123def  # 重建分支

# 3. 合并错误回滚
# 问题:错误的合并操作
# 解决:
git reset --hard HEAD~1     # 回滚合并提交
git reset --hard ORIG_HEAD  # 回滚到合并前状态

# 4. 文件冲突标记残留
# 问题:解决冲突后仍有冲突标记
# 解决:
git checkout --ours filename    # 使用当前分支版本
git checkout --theirs filename  # 使用合并分支版本
git checkout --merge filename   # 尝试自动合并

# 5. 推送被拒绝
# 问题:本地版本落后于远程
# 解决:
git pull --rebase origin master
git push origin master

# 6. 忽略规则不生效
# 问题:.gitignore规则无法忽略已跟踪文件
# 解决:
git rm --cached filename
git add .
git commit -m "fix: update gitignore"

【代码注释】 :排错顺序:git statusgit log --oneline -5git reflog。敏感信息进库须轮换密钥 + git filter-repo/BFG,不只 rm --cachedORIG_HEAD 在 merge/pull 后保存操作前 HEAD,误合并可 reset --hard ORIG_HEAD(未再移动 HEAD 时)。filter-branch 已过时,新项目优先 filter-repo。推送被拒先 pull --rebase,忌盲目 --force 到共享分支。

8.1.1 高频事故场景速查

场景 A:commit message 写错了(尚未推送)

bash 复制代码
# 修改最后一次提交的 message
git commit --amend -m "fix: correct typo in login validation"

# 修改最后一次提交的内容(追加文件)
git add forgotten-file.js
git commit --amend --no-edit     # 保留原 message,追加文件进上次提交

场景 B:git add . 误暂存了不该提交的文件

bash 复制代码
# 取消暂存某个文件(保留工作区改动)
git restore --staged .env
git restore --staged dist/

# 查看暂存区内容再决定
git diff --cached --name-only

场景 C:git push 后发现推错了分支

bash 复制代码
# 情况1:推到了远程 feature 分支(可以强制撤回)
git push origin feature/wrong-branch --delete   # 删除远程分支
# 或者
git revert <last-commit>
git push

# 情况2:不小心推到了 main(高风险!)
# 先联系团队,告知大家暂停 pull
git revert HEAD                           # 撤销那次提交
git push origin main                      # 推送撤销提交
# 通知团队重新 pull

场景 D:本地和远程分支历史分叉(非线性)

bash 复制代码
# 错误信息:! [rejected] main -> main (non-fast-forward)
# 原因:本地有远程没有的提交,同时远程也有本地没有的提交

# 方案1:合并(保留两边历史)
git pull origin main                      # 产生合并提交

# 方案2:变基(线性历史,推荐个人分支使用)
git pull --rebase origin main             # 相当于 fetch + rebase
git push origin main

场景 E:误执行 git reset --hard 丢失了未提交改动

bash 复制代码
# 已提交的内容可用 reflog 找回
git reflog
# 找到丢失前的 commit hash

# 未提交且未 stash 的工作区改动:通常无法恢复(Git 没有记录过)
# 预防措施:频繁 commit 或使用 git stash

场景 F:git merge 后悔了,想撤销合并

bash 复制代码
# 合并后尚未推送
git reset --hard ORIG_HEAD           # ORIG_HEAD 保存了合并前的状态

# 合并后已推送(安全方式)
git revert -m 1 <merge-commit-hash>
git push origin main

场景 G:大文件被误提交,仓库体积暴增

bash 复制代码
# 找出大文件
git rev-list --objects --all | \
  git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | \
  sort -k3 -rn | head -20

# 从历史中彻底删除(会重写所有历史,谨慎!)
# 推荐使用 BFG Repo Cleaner(比 filter-branch 快 10-720 倍)
# brew install bfg
bfg --delete-files "*.zip" my-repo.git
git reflog expire --expire=now --all
git gc --prune=now --aggressive
git push --force

8.2 性能优化建议

bash 复制代码
# 【Git性能优化技巧】

# 1. 大文件处理
# 问题:仓库中存在大文件导致克隆缓慢
# 解决:使用Git LFS(Large File Storage)
git lfs install
git lfs track "*.psd"
git lfs track "*.zip"
git add .gitattributes
git commit -m "feat: enable git lfs"

# 2. 浅克隆
# 问题:只需要最新代码,不需要完整历史
# 解决:
git clone --depth 1 https://github.com/user/repo.git
git clone --depth 1 --no-single-branch https://github.com/user/repo.git

# 3. 稀疏检出
# 问题:只需要仓库的部分文件
# 解决:
git clone --no-checkout https://github.com/user/repo.git
cd repo
git sparse-checkout init
git sparse-checkout set src/lib
git checkout

# 4. 垃圾回收
# 问题:仓库体积过大
# 解决:
git gc --prune=now
git gc --aggressive --prune=now

# 5. 清理未跟踪文件
git clean -fd          # 删除未跟踪文件和目录
git clean -fdX         # 包括通常被忽略的文件

【代码注释】 :大二进制用 Git LFS 或勿入库;node_modules/dist 必须进 .gitignore。克隆慢用 --depth 1 浅克隆;monorepo 用 sparse-checkout 只检出子目录。git gc --aggressive 在维护窗口执行,会占 CPU。git clean -fd 删除未跟踪 文件,执行前确认无未备份草稿;-fdX 会删被 ignore 的文件,更危险。


9. 总结与展望

Git作为现代软件开发的基石工具,其重要性不言而喻。通过本文的系统学习,我们从基础命令到高级技巧,从个人开发到团队协作,全面掌握了Git的使用方法。

9.1 核心知识点回顾

  • 基础与配置:init / add / commit / status / diff / restore;config 三层优先级(system/global/local)
  • 版本管理 :git log(含 -S Pickaxe 考古、--grep 过滤)、reflog、reset 三模式、revert 安全撤销、tag 语义化版本
  • 忽略机制 :.gitignore 语法精讲、git rm --cachedcheck-ignore 调试
  • 分支策略:branch / switch / merge;rebase 变基;cherry-pick 摘取;Detached HEAD 处理;冲突解决
  • 远程协作git fetch vs git pull 核心区别;remote 配置;SSH 免密;submodule 子模块管理
  • 工作流程 :GitFlow(多版本发布)、GitHub Flow(SaaS 持续部署)、GitLab Flow(环境分支)、Trunk-Based Development(高频集成,Google/Netflix 实践)
  • 高级技巧 :stash 储藏、bisect 二分调试、worktree 多工作区、blame 代码考古、Hooks 质量门禁、性能优化

9.2 实践建议

  1. 建立团队规范:制定明确的分支管理、提交信息、代码审查规范
  2. 保护重要分支:设置master/main分支保护规则
  3. 定期备份:重要项目定期推送到多个远程仓库
  4. 持续学习:关注Git新特性和工具改进

9.3 学习资源

【专业建议】:掌握Git需要理论学习和大量实践相结合,建议在实际项目中逐步应用这些知识,遇到问题时查阅官方文档或社区资源。

Git不仅仅是一个工具,更是一种协作思维。良好的Git使用习惯将极大地提升团队开发效率和代码质量。继续学习和实践,你一定能成为Git高手!



附录 A:命令与场景速查

A.1 日常高频操作

我想... 命令
看改了什么(未 add) git diff
看改了什么(已 add) git diff --cached
撤销未 add 的改动 git restore <file>
撤销已 add(退出暂存区) git restore --staged <file>
快速提交所有已跟踪文件 git commit -am "message"
改最后一次提交说明 git commit --amend -m "new msg"
追加文件到上次提交 git add <file> && git commit --amend --no-edit
看简洁历史 git log --oneline
看分支拓扑 git log --graph --oneline --all
找丢掉的提交 git reflog
回退版本(本地) git reset --hard <id>
安全撤销已推送提交 git revert <hash>

A.2 分支操作

我想... 命令
新建并切分支 git switch -c <name>
切换分支 git switch <name>
查看所有分支 git branch -a
合并分支 git merge <name>(先切到目标分支)
变基(线性历史) git rebase main(在 feature 上执行)
摘取指定提交 git cherry-pick <hash>
删除已合并分支 git branch -d <name>
删除远程分支 git push origin --delete <name>
检出标签快照 git switch --detach v1.0.0

A.3 远程协作

我想... 命令
第一次推送建立跟踪 git push -u origin main
安全拉取(推荐) git fetch origin && git rebase origin/main
快速拉取 git pull --rebase
克隆仓库(含子模块) git clone --recurse-submodules <url>
查看远程信息 git remote -v / git remote show origin
切换远程 URL git remote set-url origin <new-url>

A.4 文件与历史管理

我想... 命令
忽略已跟踪文件 git rm --cached <file> + .gitignore
临时储藏工作 git stash push -m "说明"
恢复储藏 git stash pop
查看某行是谁写的 git blame -L <start>,<end> <file>
找引入某字符串的提交 git log -S "关键字" --oneline
二分找 bug 提交 git bisect start && git bisect bad && git bisect good <tag>
同时检出两分支 git worktree add <path> <branch>
打发布标签 git tag -a v1.0.0 -m "Release"
推送标签 git push origin --tags
初始化子模块 git submodule update --init --recursive
规则是否生效 git check-ignore -v <path>
查看 Git 对象内容 git cat-file -p HEAD

附录 B:官方与延伸阅读

资源 链接 说明
Pro Git 中文版 https://git-scm.com/book/zh/v2 系统学习首选
Git 命令大全 https://git-scm.com/docs 官方命令参考
GitHub Docs https://docs.github.com/zh PR、SSH、组织权限
Learn Git Branching https://learngitbranching.js.org/?locale=zh_CN 可视化分支练习
D3 Git 说明 https://onlywei.github.io/explain-git-with-d3/ 图形化理解内部结构

可视化工具(业界常用)

  • SourceTree / Fork:桌面 GUI
  • VS Code Git 面板:日常 diff、冲突解决
  • GitKraken:图形化历史与合并

本文涵盖 Git 核心概念、课堂全流程(初始化 → 回滚 → 忽略 → 分支 → 远程 → GitFlow)及企业协作实践;文中 HTML 为命令练习清单,请在终端配合空仓库实操。所有 md 与课堂案例仓库均保留,可直接在案例目录执行 git log --graph 观察真实提交历史。

相关推荐
Python私教6 小时前
Git 撤销与回退避坑指南:reset / revert / restore 到底用哪个(2026 实战)
git
流浪0016 小时前
Linux篇(九):一文搞懂 Git:版本控制的原理与实操指南
git
Ws_8 小时前
Git + Gerrit 第九课:cherry-pick 挑选提交
git
之歆9 小时前
Day04_Git完全指南:从入门到精通的版本控制精通
git
码出财富9 小时前
InsForge 后端分支管理实战指南
git
2601_9611940210 小时前
2026六级词汇PDF下载|大学英语六级单词表+音频PDF
windows·git·eclipse·pdf·github
幽冥三王爷12 小时前
Git 操作常见问题与处理办法
git
独挽离人13 小时前
git标准推送流程
git
无人生还别怕14 小时前
搭建gitlab服务并接入openldap认证
git·gitlab·github·openldap·ldap·统一认证