📦 第01章 · Git 基础

init add commit status log diff

Git 基础

写代码难免要改、要删、要回滚。Git 就是那把帮你把每一次修改都留下记录、随时可以翻回去的"后悔药"。这一章只讲最核心又最常用的六条命令——init、add、commit、status、log、diff。学完你就能在服务器上把一个新项目纳入 Git 管理,并看懂它现在处于什么状态。

为什么需要版本控制

想象你在 ~/my-app 里写一个前端页面,改着改着发现昨天那版更好。没有 Git 时你只能手动复制 app.js.bak、app.js.bak2,一段日子后文件夹里全是备份文件,自己也分不清哪份是最新。Git 帮你解决三个问题:

要解决的问题 Git 的方案 对应的命令
记录每次改动 每次提交保存一份快照 commit
查看改动历史 按时间倒序列出提交 log
精确对比改动 逐行显示差异 diff
随时回退 依快照恢复文件 checkout/reset

Git 是分布式版本控制系统——每个克隆下来的仓库都是完整副本,即使中心服务器(Self-hosted GitLab)挂了,你本地依然有全部历史。

三个"区域":工作区、暂存区、仓库

理解 Git 的关键是区分三块区域。绝大多数新手困惑都出在这里:

区域 作用 你对此的感知
工作区 (Working Tree) 你正在编辑、看到的实际文件 编辑器里的真实内容
暂存区 (Staging / Index) 准备进入下一次提交的改动清单 git add 加入的内容
仓库 (Repository / .git) 已提交的历史快照 git commit 对象

三者的流转是单向的:

工作区文件  --git add-->  暂存区  --git commit-->  仓库历史
   (改动)                 (待提交清单)             (每次提交)

打个比方:工作区是你桌上的草稿纸,暂存区是你挑出来装进文件夹要交给秘书的那几页,仓库是秘书已经归档的文件柜。add 是把某些页挑进文件夹,commit 是把整个文件夹送进柜子归档。

首次配置:git config

让 Git 认识你是谁——这决定了每次提交记录里的作者。在一台新服务器上首次使用必须先做:

git config --global user.name  "bangdream"
git config --global user.email "1807523025@qq.com"
git config --global init.defaultBranch main
git config --list            # 查看当前生效的全部配置

注意 user.email 最好与你在 GitLab 里注册的邮箱一致,否则提交不会正确地归属到你的账号头像下(第 6 章讲用户时会更清楚)。--global 写入 ~/.gitconfig,对该用户所有仓库生效;不带 --global 则只对当前仓库生效(写入 .git/config)。

git init:把目录变成仓库

进入项目目录,执行 git init 就会在该目录下生成一个隐藏的 .git/ 文件夹——仓库本体就在这。你的源文件仍留在原地,只是从此被 Git 盯上了。

cd ~/my-app
git init                  # 初始化,生成 .git/
ls -la .git               # 能看到 repository 的对象与配置

初始化后,git status 会告诉你当前目录里所有文件都是"未跟踪 (untracked)"状态——Git 认识它们,但还没纳入版本控制。

.gitignore:把不想管的文件排出去

编译产物、依赖目录、密码文件都不该进仓库。node_modules/、dist/、.env 这类往往很大或含敏感信息。建一个 .gitignore 文件,写明规则:

node_modules/
dist/
.env
*.log
.DS_Store
git status   # 被忽略的文件不再出现在 untracked 列表里

安全 .env、密钥、数据库备份千万不要提交进仓库。一旦推送到远程(第 3 章),即使后来删除,它仍留在历史记录里,等于泄密。根密码这类敏感文件应放在 /root/.deploy/(如 gitlab_root_password)并由 Git 忽略。

git add 与 git commit:核心两条

改完文件,先 add 到暂存区,再 commit 成一次历史。快捷键是"改→暂存→提交"三步:

git add index.css        # 暂存单个文件
git add src/             # 暂存整个目录
git add .                # 暂存当前目录下所有改动(已被 gitignore 排除的除外)
git add -p               # 交互式逐块暂存,只挑一部分改动提交(进阶)
git commit -m "feat: 新增导航栏"    # 把暂存区内容提交为一次历史

git commit -am "..." 可以跳过一次 add,一步暂存并提交所有"已跟踪文件"(不含新增文件)的改动。但初学建议老老实实 add 再 commit,思路更清晰。

提交信息(commit message)格式,团队常用"类型: 描述":

类型 含义
feat: x 新功能
fix: x 修复
docs: x 文档
refactor: x 重构,不改功能
chore: x 构建、工具类杂项

git status:看现状

任何时候拿不准,git status 是第一诊断命令。它告诉你:哪些文件改了未暂存、哪些已暂存待提交、哪些是未跟踪的新文件。

git status             # 完整的人类可读输出
git status -s          # 简短格式(porcelain),一行一个文件,脚本友好
git status -sb         # 再加当前分支名与上游跟踪信息

-s 输出的两列字母含义:

列组合 含义
?? untracked,未跟踪新文件
M 已修改,尚未暂存(左列空白是"未暂存",右列 M 是工作区有改动)
M 已暂存(左列 M 表示改动已 add)
MM 既有已暂存改动,工作区又有新的未暂存改动

git log:看历史

git log                       # 完整提交列表,含作者、时间、说明
git log --oneline             # 每条一行,只显示短哈希 + 说明(最常用)
git log --oneline --graph     # 用 ASCII 图形展示分支分合走向
git log --oneline -5          # 只看最近 5 条
git log --stat                # 每次提交改了哪些文件
git log --author="bangdream"  # 按作者过滤

在这台服务器上的一个示例项目里,git log --oneline 大概长这样:

a1b2c3d (HEAD -> main) feat: 导航栏改成响应式
e4f5g6h fix: 修复登录页在移动端溢出
i7j8k9l chore: 初始化项目结构

HEAD 表示你当前所在的提交,-> main 表示当前分支是 main。

git diff:看改了什么

diff 是诊断"我到底改了什么"的利器,配合 status 使用。它的三个常用变体对应三个区域的对比:

命令 对比对象 场景
git diff 工作区 改动 vs 暂存区 还没 add 的改动,最常见的用法
git diff --cached (或 --staged) 暂存区 vs 上一次提交 已经 add、准备提交的内容
git diff HEAD 工作区+暂存区 vs 最后一次提交 自从上次提交后的全部改动
git diff                          # 看没暂存的改动
git diff --cached                 # 看已暂存的改动
git diff --stat                   # 只看改了多少文件、多少行,不展开内容
git diff -- app.js                # 只看某个文件的差异

diff 输出以 + 开头的行是新增,以 - 开头的是删除,带 @@ 的被 ---/+++ 标识的是文件与变更行号块。提交前养成 git diff --cached 复查一遍的习惯,能避免把调试代码或密钥带进提交。

案例场景:把 ~/my-app 纳入版本控制

在本服务器上,一个前端 Node 应用通常最终由 git 部署到 /var/www/<name>(第 7 章会讲部署)。先用 Git 把它管起来:

# 1. 初始化并配置作者
cd ~/my-app
git init
git config --global user.name  "bangdream"
git config --global user.email "1807523025@qq.com"

# 2. 排除依赖与构建产物
cat > .gitignore <<'EOF'
node_modules/
dist/
.env
*.log
EOF

# 3. 暂存并提交首个版本
git add .
git commit -m "chore: 初始化项目结构"

# 4. 改点东西,看看状态与差异
echo "console.log('hello')" >> app.js
git status -s                 # 看到 " M app.js"
git diff                      # 看到新增的那一行
git add app.js
git commit -m "feat: 加入 hello 输出"   # 提交后 status 变干净

提交完成后,git log --oneline 能回看你刚做的两次提交。

改错了怎么办(入门级)

  • 撤销工作区上次 add:git restore --staged file 把文件从暂存区退回去(不影响工作区内容)。
  • 丢弃工作区内未提交的改动:git restore file 把文件恢复到"最后一次提交"的样子,危险,会丢失改动。
  • 重写提交信息:git commit --amend -m "新信息" 会覆盖上一次提交(前提是没推送到共享远程,否则要避免)。

安全 git restore file 会直接丢弃你对这个文件的所有未提交改动,且无法恢复。执行前确认你不需要那些修改,或先在编辑器里保存副本。

易错点

  • 忘记 add 就 commit:commit 只提交暂存区内容,工作区里未 add 的改动不会包含进去,导致提交对象与预期不符。多按 git status 确认。
  • git add . 泄漏密钥:只要 node_modules/、.env、*key 等没写进 .gitignore,一个 add . 就会把敏感文件纳入暂存。先把 .gitignore 配好再 add。
  • 误以为 diff 会显示已暂存改动:默认 git diff 不含 --cached 内容,提交前请用 git diff --cached。
  • 多分支下看错历史:git log 只在当前分支上回放。要先 git log --all 或切分支才能看到别的分支(第 2 章展开)。

相关命令

git init、git add、git commit、git status、git log、git diff、git config、git restore、git rm、git mv、gitignore(手册)。