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

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修复 bugfix: 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 - 分支管理