强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

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 [email protected]:user/repo.git

# 指定目录名
$ git clone [email protected]:user/repo.git my-local-name

# 浅克隆(只获取最近 N 次提交,节省时间)
$ git clone --depth 1 [email protected]:user/repo.git

# 克隆指定分支
$ git clone -b develop [email protected]:user/repo.git

# 克隆包含子模块
$ git clone --recursive [email protected]: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="[email protected]"

# 按日期过滤
$ 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 -Agit commit -m
部分提交 git add -p 选择性暂存
代码审查前 git status -sb 确认变更
查找特定修改 git log -S "keyword"git log --grep
忽略本地文件 .git/info/exclude(不提交到仓库)

扩展阅读


🔗 上一章02 - 安装配置 | 下一章04 - 分支管理