Git 完全指南 / 03 - 基础操作:init、add、commit、status、log
第三章:基础操作
掌握 Git 的日常基础命令,是高效版本控制的第一步。
3.1 创建仓库
3.1.1 初始化新仓库:git init
# 创建项目目录并初始化
$ mkdir my-project
$ cd my-project
$ git init
Initialized empty Git repository in /home/user/my-project/.git/
# 查看生成的 .git 目录
$ ls -la .git/
total 40
drwxr-xr-x 7 user user 4096 May 10 10:00 .
drwxr-xr-x 3 user user 4096 May 10 10:00 ..
-rw-r--r-- 1 user user 23 May 10 10:00 HEAD
drwxr-xr-x 2 user user 4096 May 10 10:00 branches
-rw-r--r-- 1 user user 92 May 10 10:00 config
-rw-r--r-- 1 user user 73 May 10 10:00 description
drwxr-xr-x 2 user user 4096 May 10 10:00 hooks
drwxr-xr-x 2 user user 4096 May 10 10:00 info
drwxr-xr-x 4 user user 4096 May 10 10:00 objects
drwxr-xr-x 4 user user 4096 May 10 10:00 refs
.git 目录结构说明:
| 文件/目录 | 作用 |
|---|---|
HEAD | 指向当前分支的引用 |
config | 仓库级配置 |
objects/ | 存储所有对象(blob、tree、commit) |
refs/ | 存储分支和标签引用 |
hooks/ | 钩子脚本 |
index | 暂存区(add 后生成) |
logs/ | 引用变更日志 |
3.1.2 指定初始分支名
# 默认分支名建议设为 main
$ git init -b main
Initialized empty Git repository in /home/user/my-project/.git/
# 或通过全局配置
$ git config --global init.defaultBranch main
3.1.3 克隆已有仓库:git clone
# HTTPS 方式克隆
$ git clone https://github.com/user/repo.git
# SSH 方式克隆(推荐)
$ git clone git@github.com:user/repo.git
# 指定目录名
$ git clone git@github.com:user/repo.git my-local-name
# 浅克隆(只获取最近 N 次提交,节省时间)
$ git clone --depth 1 git@github.com:user/repo.git
# 克隆指定分支
$ git clone -b develop git@github.com:user/repo.git
# 克隆包含子模块
$ git clone --recursive git@github.com:user/repo.git
3.2 文件状态生命周期
Git 中文件有以下几种状态:
untracked unmodified modified staged
┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐
│ │ │ │ │ │ │ │
│ 新文件 │─────►│ 已跟踪 │────►│ 已修改 │────►│ 暂存区 │
│ (未追踪) │ add │ (未修改) │ edit│ (未暂存) │ add │ (待提交) │
│ │ │ │ │ │ │ │
└───────────┘ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘
│ ▲ │
│ │ │
└──────────────────┴──────────────────┘
commit
| 状态 | 含义 | git status 显示 |
|---|---|---|
| Untracked | 新文件,未被 Git 追踪 | 红色 “Untracked files” |
| Untracked (staged) | 新文件已加入暂存区 | 绿色 “new file” |
| Modified (unstaged) | 已追踪文件被修改但未暂存 | 红色 “modified” |
| Modified (staged) | 修改已暂存 | 绿色 “modified” |
| Unmodified | 文件未变化 | 不显示 |
3.3 暂存文件:git add
git add 将工作区的变更添加到暂存区。
基本用法
# 暂存单个文件
$ git add README.md
# 暂存多个文件
$ git add file1.txt file2.txt file3.txt
# 暂存当前目录所有变更
$ git add .
# 暂存所有变更(包括删除)
$ git add -A
# 等价于
$ git add --all
# 交互式暂存(选择性暂存部分内容)
$ git add -p
# 或
$ git add --patch
git add . vs git add -A vs git add -u
| 命令 | 新文件 | 修改文件 | 删除文件 | 作用范围 |
|---|---|---|---|---|
git add . | ✅ | ✅ | ✅ | 当前目录及子目录 |
git add -A | ✅ | ✅ | ✅ | 整个工作区 |
git add -u | ❌ | ✅ | ✅ | 整个工作区 |
💡 推荐:在仓库根目录使用
git add -A,确保所有变更都被暂存。
交互式暂存详解
$ git add -p
diff --git a/src/main.py b/src/main.py
index abc1234..def5678 100644
--- a/src/main.py
+++ b/src/main.py
@@ -10,6 +10,8 @@ def main():
print("Hello")
+ print("World")
+ print("Git")
(1/1) Stage this hunk [y,n,q,a,d,s,e,?]?
交互选项:
| 选项 | 含义 |
|---|---|
y | 暂存这个 hunk |
n | 跳过这个 hunk |
q | 退出 |
a | 暂存当前文件所有 hunk |
d | 跳过当前文件所有 hunk |
s | 将当前 hunk 分割为更小的块 |
e | 手动编辑 hunk |
? | 显示帮助 |
💡 交互式暂存允许你在一次修改中只提交部分内容,保持提交的原子性。
3.4 提交变更:git commit
git commit 将暂存区的内容创建为一个新的提交对象。
基本用法
# 提交并附带提交信息
$ git commit -m "Add user authentication module"
# 提交所有已跟踪文件的变更(跳过 add)
$ git commit -a -m "Fix typo in README"
# 等价于
$ git commit -am "Fix typo in README"
# 使用编辑器编写多行提交信息
$ git commit
# 会打开默认编辑器
# 修改最近一次提交
$ git commit --amend -m "New commit message"
# 修改最近一次提交(保留原信息)
$ git commit --amend --no-edit
# 创建空提交(用于触发 CI/CD)
$ git commit --allow-empty -m "Trigger build"
提交信息规范
好的提交信息格式:
<类型>(<范围>): <简要描述>
<详细说明>
<关联信息>
示例:
# 简单提交
$ git commit -m "feat: add user login page"
# 带详细说明的提交
$ git commit -m "fix(auth): resolve token expiration issue
- Refresh token before API call
- Add retry logic for 401 responses
- Update token storage to use secure cookies
Closes #123"
常用提交类型:
| 类型 | 说明 | 示例 |
|---|---|---|
feat | 新功能 | feat: add payment module |
fix | 修复 bug | fix: resolve login timeout |
docs | 文档更新 | docs: update API reference |
style | 格式调整 | style: fix indentation |
refactor | 重构 | refactor: extract auth logic |
test | 测试 | test: add unit tests for user |
chore | 杂项 | chore: update dependencies |
3.5 查看状态:git status
# 完整状态
$ git status
On branch main
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
new file: src/app.py
modified: README.md
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
modified: src/utils.py
Untracked files:
(use "git add <file>..." to include in what will be committed)
tests/test_app.py
# 简洁模式(推荐日常使用)
$ git status -sb
## main...origin/main [ahead 1]
M src/utils.py
M README.md
A src/app.py
?? tests/test_app.py
简洁模式符号含义:
| 符号 | 含义 |
|---|---|
## | 当前分支和跟踪信息 |
M (左侧) | 暂存区有修改 |
M (右侧) | 工作区有修改 |
A | 新文件已暂存 |
D | 文件已删除 |
?? | 未追踪文件 |
R | 文件已重命名 |
UU | 合并冲突 |
3.6 查看日志:git log
基本用法
# 完整日志
$ git log
# 单行简洁格式
$ git log --oneline
abc1234 Initial commit
def5678 Add README
ghi9012 Fix typo
# 带图形的分支图
$ git log --oneline --graph --decorate --all
* abc1234 (HEAD -> main, origin/main) Latest commit
| * def5678 (feature) Feature commit
|/
* ghi9012 Initial commit
常用过滤选项
# 限制显示数量
$ git log -n 5 # 最近 5 条
# 按作者过滤
$ git log --author="John"
$ git log --author="john@email.com"
# 按日期过滤
$ git log --since="2024-01-01"
$ git log --until="2024-06-30"
$ git log --since="2 weeks ago"
# 按提交信息搜索
$ git log --grep="fix"
$ git log --grep="feat" --grep="fix" --all-match
# 按文件过滤
$ git log -- src/main.py
$ git log --follow -- src/main.py # 包含重命名历史
# 按分支差异
$ git log main..feature # feature 有但 main 没有的提交
$ git log main...feature # 两个分支独有的提交
# 显示每次提交的变更统计
$ git log --stat
# 显示每次提交的具体修改
$ git log -p
$ git log --patch
# 按内容搜索(引入/删除了特定代码的提交)
$ git log -S "function_name"
$ git log -G "regex_pattern"
自定义日志格式
# 自定义格式
$ git log --pretty=format:"%h %an %ar %s"
abc1234 John 2 hours ago Fix bug
# 常用格式占位符
# %H 完整提交哈希
# %h 简短提交哈希
# %an 作者名
# %ae 作者邮箱
# %ar 相对时间
# %s 提交信息标题
# %d 引用(分支、标签)
# %n 换行
# 设置别名
$ git config --global alias.lg "log --oneline --graph --decorate --all --date=short --format='%C(yellow)%h%C(reset) %C(green)%ad%C(reset) %C(blue)%an%C(reset) %C(red)%d%C(reset) %s'"
3.7 .gitignore 文件
.gitignore 指定 Git 应忽略的文件和目录。
# 创建 .gitignore
$ touch .gitignore
常用忽略规则
# 注释
# 忽略所有 .log 文件
*.log
# 但保留 important.log
!important.log
# 忽略 build 目录
build/
# 忽略根目录下的 TODO 文件
/TODO
# 忽略 doc 目录下所有 .pdf 文件
doc/**/*.pdf
# 忽略所有 .pyc 文件
*.pyc
# 忽略 __pycache__ 目录
__pycache__/
# 忽略 node_modules
node_modules/
# 忽略 .env 环境变量文件
.env
.env.local
.env.*.local
# 忽略 IDE 配置
.idea/
.vscode/
*.swp
*.swo
# 忽略操作系统文件
.DS_Store
Thumbs.db
匹配模式
| 模式 | 含义 | 示例 |
|---|---|---|
* | 任意字符 | *.log 匹配所有 .log 文件 |
? | 单个字符 | ?.txt 匹配 a.txt 等 |
** | 任意目录深度 | **/logs 匹配所有层级 logs 目录 |
! | 取反(不忽略) | !important.log |
/ 开头 | 仅根目录 | /build 仅匹配根目录下的 build |
/ 结尾 | 仅目录 | build/ 仅匹配目录 |
全局 .gitignore
# 设置全局忽略文件
$ git config --global core.excludesfile ~/.gitignore_global
# 编辑全局忽略文件
$ vim ~/.gitignore_global
3.8 .gitkeep 与空目录
Git 不追踪空目录。如果你需要保留某个空目录结构:
# 方法 1:使用 .gitkeep(约定俗成)
$ mkdir -p logs/uploads
$ touch logs/uploads/.gitkeep
$ git add logs/uploads/.gitkeep
# 方法 2:使用 .gitignore(保留目录但忽略内容)
$ echo "*" > logs/.gitignore
$ echo "!.gitignore" >> logs/.gitignore
3.9 完整工作流示例
# 1. 创建项目
$ mkdir web-app && cd web-app
$ git init -b main
# 2. 创建 .gitignore
$ cat > .gitignore << 'EOF'
node_modules/
.env
.DS_Store
*.log
dist/
EOF
# 3. 创建初始文件
$ echo "# Web App" > README.md
$ mkdir src
$ cat > src/index.js << 'EOF'
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.send('Hello Git!');
});
app.listen(3000, () => {
console.log('Server running on port 3000');
});
EOF
# 4. 查看状态
$ git status -sb
?? .gitignore
?? README.md
?? src/
# 5. 暂存所有文件
$ git add -A
# 6. 确认暂存内容
$ git status
Changes to be committed:
new file: .gitignore
new file: README.md
new file: src/index.js
# 7. 首次提交
$ git commit -m "feat: initial project setup"
# 8. 修改文件
$ echo "node_modules/" >> .gitignore
$ echo "console.log('App started');" >> src/index.js
# 9. 查看差异
$ git diff
diff --git a/src/index.js b/src/index.js
index abc1234..def5678 100644
--- a/src/index.js
+++ b/src/index.js
@@ -9,3 +9,4 @@ app.listen(3000, () => {
console.log('Server running on port 3000');
});
+console.log('App started');
# 10. 暂存并提交
$ git add -A
$ git commit -m "feat: add startup log message"
# 11. 查看提交历史
$ git log --oneline
def5678 feat: add startup log message
abc1234 feat: initial project setup
业务场景
| 场景 | 推荐操作 |
|---|---|
| 开始新项目 | git init -b main + 创建 .gitignore |
| 日常开发 | 编辑 → git add -A → git commit -m |
| 部分提交 | git add -p 选择性暂存 |
| 代码审查前 | git status -sb 确认变更 |
| 查找特定修改 | git log -S "keyword" 或 git log --grep |
| 忽略本地文件 | .git/info/exclude(不提交到仓库) |
扩展阅读
- git-scm.com: git-add
- git-scm.com: git-commit
- git-scm.com: gitignore
- Conventional Commits
- gitignore.io — 自动生成 .gitignore