Git 是分布式版本控制系统。Git 记录一组文件在特定时刻的完整状态、提交之间的关系,以及每次修改的作者与说明。每位开发者的本机仓库都可以保存完整历史,因此即使暂时无法连接服务器,仍能查看差异、创建提交和切换分支。

初学 Git 最容易混淆的不是命令,而是修改目前位于工作区、暂存区、本机历史还是远程仓库。先建立数据流模型,再学习命令,便能根据操作目的选择工具。

工作区 ──git add──> 暂存区 ──git commit──> 本机提交历史 ──git push──> 远程仓库

1. 安装与初始配置

1.1 安装 Git

Ubuntu 或 Debian 可以运行 sudo apt update && sudo apt install git,Fedora、Rocky Linux 等系统可以运行 sudo dnf install git,macOS 搭配 Homebrew 时可以运行 brew install git。安装完成后,用 git --version 检查可执行文件与版本。

每个提交都会记录作者姓名与电子邮件。下列全局设置应用到当前用户的所有仓库:

git config --global user.name "Your Name"
git config --global user.email "[email protected]"

使用 git config --global user.namegit config --global user.email 可以读取配置。特定仓库需要另一组身份时,在仓库内省略 --global;仓库配置会覆盖同名的全局配置。

1.2 理解配置层级

Git 配置有三个常见层级:

层级常见位置配置选项作用范围
系统/etc/gitconfig--system整台电脑
用户~/.gitconfig--global当前用户
仓库.git/config不加层级选项当前仓库

越接近仓库的配置优先级越高。git config --list --show-origin 会同时显示配置值与来源文件。常用初始配置如下:

git config --global init.defaultBranch main
git config --global core.editor "vim"
git config --global core.autocrlf input

Git 会在撰写合并信息、交互式 rebase 指令或标签说明时打开指定编辑器。需要熟悉编辑器基本操作时,可以参考站内的 Vim 使用指南;偏好图形界面的读者可以参考 Visual Studio Code 安装与扩展

2. 创建仓库并完成第一次提交

2.1 初始化或克隆仓库

从本机创建项目时,可以运行以下命令:

mkdir git-practice
cd git-practice
git init

git init 会创建 .git 目录,用来保存对象数据库、分支引用与仓库配置。现有项目也能运行 git init,Git 不会删除原有文件。创建第一个文件后运行 git status,Git 会把尚未纳入跟踪的文件列为 untracked。

远程已经存在项目时,通常使用 git clone [email protected]:team/project.git。Clone 会下载提交历史、创建工作区、加入名为 origin 的远程,并检出默认分支。git init 适合从本机开始新项目,git clone 适合加入现有项目。

2.2 分清四个数据区域

Git 日常操作涉及四个区域:

  1. 工作区(working tree):编辑器与文件系统目前显示的内容;
  2. 暂存区(index 或 staging area):下一次提交准备收录的内容;
  3. 本机仓库(local repository):已创建的提交、分支与标签;
  4. 远程仓库(remote repository):团队共享的提交与分支。

git add 的主要作用是把文件目前的内容加入下一次提交。已跟踪文件每次修改后也需要重新 add,才能更新暂存区中的版本。

2.3 检查、暂存与提交

git status --short 适合快速查看文件状态。git diff 显示工作区相对暂存区的差异,git diff --staged 显示暂存区相对上一个提交的差异。提交前依次查看两种 diff,可以确认哪些修改仍未暂存,以及下一次提交实际包含哪些内容。

创建文件、选择修改并提交的完整流程如下:

printf '# Git practice\n' > README.md
git status --short
git diff
git add README.md
git diff --staged
git commit -m "Add project README"

git add src/ 暂存指定目录,git add . 暂存目前路径内的新增、修改与删除,git add --patch 则能逐段选择修改。逐段暂存适合拆分大型修改,让每个提交只表达一个连贯变更。提交信息应说明修改带来的结果,例如 Fix empty search results on mobileupdate files 更容易审查。

.gitignore 用来排除构建产物、临时文件与本机配置:

node_modules/
dist/
.env
*.log

.gitignore 不会停止跟踪已经提交的文件。若 .env 已被跟踪,使用 git rm --cached .env 从暂存区移除,再提交变更。密码、API token 与私钥不应进入提交历史;稍后删除文件不会清除旧提交中的秘密。

3. 理解提交历史与分支

3.1 Commit、分支与 HEAD

每个 commit 记录项目快照、作者、时间、提交信息,以及父提交。多个提交通过父子关系形成有向图。HEAD 通常指向当前分支,分支名称则指向该分支的最新提交。创建提交 D 后,mainHEAD 都会向前移动:

A──B──C──D  main, HEAD

分支不是项目的完整副本,而是一个可移动的提交引用。创建分支的成本很低,因此短期功能分支是 Git 协作的常见单位。

Commit ID 是 Git 根据对象内容计算出的哈希值。修改提交内容、父提交或提交信息会产生新的 ID,因此 rebase 和 amend 会重写提交。命令通常只需使用不会混淆的 ID 前缀。HEAD~1 表示第一父链的上一个提交,HEAD~3 表示沿第一父链向前三个提交。

3.2 创建、切换与合并分支

新功能应在独立分支开发。git switch -c feature/search 会创建并切换分支,git switch main 会回到主分支,git branch -vv 可以查看本机分支及其上游状态。旧版 Git 常使用 git checkout 同时切换分支与还原文件;现代 Git 提供 git switchgit restore,让两类操作更容易区分。

功能完成后,先切换到接收变更的分支,再合并功能分支:

git switch main
git merge feature/search

如果 main 没有新提交,Git 只需向前移动分支引用,称为 fast-forward merge。如果两个分支都有新提交,Git 通常会创建具有两个父提交的 merge commit,保留两条开发线汇合的关系。

3.3 使用 Rebase 整理个人分支

Rebase 会把一组提交重新套用到新的基底。运行 git switch feature/search 后再运行 git rebase main,原本建立在旧基底上的功能提交会重建到最新 main 之后。

Rebase 可以产生线性历史,但会改变 commit ID。尚未共享的个人分支适合整理;多人共同使用的分支若被任意重写,其他开发者的历史会产生分歧。功能合并后,git branch -d feature/search 会安全删除已合并的本机分支;大写 -D 会跳过合并检查,使用前应确认提交仍可从其他引用找到。

4. 远程协作与冲突处理

4.1 Fetch、Pull 与 Push

git remote -v 显示远程名称与 URL。origin 是 clone 时创建的惯用名称,不是 Git 保留字。origin/main 是远程跟踪分支,代表最近一次 fetch 后,本机所知道的远程 main 状态。

git fetch origin 下载远程的新提交与分支引用,但不修改工作区或本机分支。Fetch 后可以用 git log --oneline main..origin/main 查看远程新增提交,并用 git diff main...origin/main 比较两条开发线。

git pull 通常等于 fetch 后再 merge。git pull --ff-only 只允许快进,遇到分歧时停止,让开发者明确选择 merge 或 rebase。偏好 rebase 的团队可以使用 git pull --rebase。团队应统一 pull 策略,避免每位成员产生不同的历史形状。

第一次发布功能分支时运行 git push --set-upstream origin feature/search,之后可以直接运行 git push。如果远程分支包含本机没有的提交,普通 push 会被拒绝;应先 fetch 并整合远程提交。重写个人远程分支后若确实需要强制更新,git push --force-with-lease 会检查远程是否仍处于预期状态,比不检查状态的 --force 更能避免覆盖他人的新提交。

4.2 解决 Merge 与 Rebase 冲突

两个分支修改同一段文字且结果不同时,Git 无法判断需要保留哪一版,便会加入冲突标记:

标记 <<<<<<< HEAD 表示当前分支内容的起点,======= 分隔两个版本,>>>>>>> feature/search 表示传入分支内容的终点。

Merge 发生冲突后,先用 git status 查看冲突清单。编辑每个冲突文件,产生符合两边意图的最终内容,删除冲突标记并完成测试,再运行 git add path/to/resolved-filegit commitgit merge --abort 可以取消整次合并。

Rebase 发生冲突时,修正并暂存文件后运行 git rebase --continuegit rebase --abort 可以取消整次 rebase。冲突解决的完成条件是程序符合预期并通过测试,不只是冲突标记已经消失。

4.3 从功能分支发起审查

一套常见的协作流程如下:

git switch main
git pull --ff-only
git switch -c feature/search-filter
# 编辑并测试
git add --patch
git diff --staged
git commit -m "Add topic filter to article search"
git push --set-upstream origin feature/search-filter

推送分支后,在 GitLab 创建 merge request。审查产生的新修改可以继续提交到同一分支,merge request 会自动更新。需要把测试接到提交与 merge request 时,可以继续阅读 GitLab CI/CD 指南;希望自行管理代码平台时,可以参考 使用 Docker 部署 GitLab

5. 安全恢复与暂存工作

5.1 根据修改所在区域选择工具

不同恢复命令处理不同区域:

目的命令历史是否改写
丢弃工作区修改git restore path/to/file
取消暂存并保留工作区修改git restore --staged path/to/file
修正最新的本机提交git commit --amend
撤销已共享提交git revert <commit-id>否,创建新提交
移动当前分支git reset

Restore 可能丢弃尚未保存的工作区内容,运行前应先查看 git diff。Amend 会创建新的 commit ID,适合修正尚未共享的最新提交。Revert 创建反向提交并保留原提交,适合公开分支与多人协作。

Reset 会移动当前分支,并依模式决定是否改动暂存区与工作区:

命令暂存区工作区
git reset --soft HEAD~1保留保留
git reset --mixed HEAD~1重置保留
git reset --hard HEAD~1重置重置

--hard 会丢弃工作区与暂存区的内容。Reset 适合整理尚未共享的本机历史,撤销公开分支上的提交通常应使用 revert。

5.2 使用 Reflog 找回提交

git reflog 记录本机 HEAD 和分支引用的移动。Reset 或 rebase 后若需要找回旧提交,可以从 reflog 找到 commit ID,再运行 git branch recovery <commit-id> 创建分支保留。Reflog 是具有保留期限的本机记录,不会同步到远程,因此不能取代远程备份。

5.3 暂存尚未完成的修改

需要临时切换分支,但修改尚未适合提交时,可以运行以下流程:

git stash push -m "WIP search form"
git switch main
# 处理临时工作
git switch feature/search
git stash pop

git stash apply 会恢复修改并保留 stash 记录,git stash pop 会在成功恢复后删除记录。默认 stash 不包含 untracked 文件;需要一并收起时使用 git stash push --include-untracked -m "WIP full tree"。Stash 适合短期切换,需要长期保存或分享的工作应创建清楚命名的分支与提交。

6. 标签、密钥与版本发布

标签通常用来标记正式版本。Annotated tag 会保存创建者、日期与说明:

git tag -a v1.0.0 -m "Release 1.0.0"
git show v1.0.0
git push origin v1.0.0

分支会随新提交移动,版本标签通常固定指向特定提交。正式版本若需要修正,创建新版本标签比移动既有标签更清楚。发布前应确认目标 commit 已通过测试,版本号与变更记录一致,构建产物可由相同源代码重建。

使用 SSH 连接远程仓库时,可以运行 ssh-keygen -t ed25519 -C "[email protected]" 创建密钥。把 .pub 公钥内容加入 Git 托管平台,私钥则留在本机并设置适当权限。私钥不能贴入网站、聊天消息或 Git 仓库。密钥管理与服务器端 SSH 加固可以参考 Linux 云服务器初始化与安全加固

7. 诊断顺序与后续练习

遇到 Git 问题时,先收集状态,再运行会修改历史或工作区的命令:

git status
git branch -vv
git remote -v
git log --oneline --decorate --graph --all -20
git diff
git diff --staged

状态输出可以回答目前所在分支、分支跟踪的远程、尚未提交的修改,以及提交图如何分叉。判断修改位于工作区、暂存区、本机历史或远程后,再选择 add、commit、fetch、merge、rebase、push、restore、revert 或 reset,可以避免大部分误操作。

初学者可以在练习仓库依次完成三个任务:先创建两个小提交,再从第一个提交创建功能分支并制造一次可控冲突,最后分别使用 revert 与 reset 观察提交图。每一步都搭配 git statusgit diff 和图形化 git log,Git 的区域模型与分支模型会比单纯背诵命令更容易掌握。