跳到主要内容

Git 基础操作

本章将介绍 Git 的基本操作,包括创建仓库、添加文件、提交修改等核心知识。掌握这些基础操作是高效使用 Git 的前提。

Git 工作流程

理解 Git 的工作区、暂存区和版本库三个区域是掌握 Git 的基础。很多人一开始觉得 Git 操作复杂,其实只要理解了这三个区域的关系,一切都会变得清晰。

三个核心区域

  • 工作区(Working Directory):你当前正在编辑的文件所在目录,就是你在电脑上看到的项目文件
  • 暂存区(Staging Area / Index):一个临时区域,标记哪些文件变更将在下次提交时被记录。它相当于一个"购物车",你决定哪些修改要提交
  • 版本库(Repository / .git):Git 存储项目历史的地方,包含所有提交记录

工作流程说明

  1. 在工作区修改文件(编辑代码)
  2. 使用 git add 将需要的文件添加到暂存区(选择要提交的修改)
  3. 使用 git commit 将暂存区的内容提交到版本库(创建一个永久快照)

实际场景:假设你修改了 5 个文件,但只想提交其中 3 个作为一次提交。你可以用 git add 选择这 3 个文件放入暂存区,然后用 git commit 提交。另外 2 个文件的修改会保留在工作区,不会被这次提交包含。

这种设计的好处在于,你可以精确控制每次提交包含哪些修改,而不是一股脑全部提交。这对于编写有意义的提交历史非常有帮助。

创建仓库

初始化新仓库

# 创建新目录
mkdir my-project
cd my-project

# 初始化 Git 仓库
git init

初始化后会创建一个 .git 目录,这就是 Git 存储版本信息的地方。这个目录包含所有 Git 需要的数据,包括对象数据库、引用、配置等。

# 查看 .git 目录结构
ls .git/

# 输出示例
HEAD # 指向当前分支
config # 仓库配置
objects/ # 存储所有数据对象
refs/ # 存储分支和标签引用
hooks/ # 钩子脚本

克隆现有仓库

# 克隆远程仓库
git clone https://github.com/username/repository.git

# 克隆到指定目录
git clone https://github.com/username/repository.git my-folder

# 克隆特定分支
git clone -b develop https://github.com/username/repository.git

# 浅克隆(只克隆最近一次提交,适合大型仓库)
git clone --depth 1 https://github.com/username/repository.git

克隆操作会自动完成以下工作:

  1. 下载仓库的所有数据
  2. 创建远程跟踪分支(origin/main 等)
  3. 创建本地 main 分支并检出

查看状态

# 查看工作区状态
git status

# 简短输出(更直观)
git status -s

输出说明:

  • ?? - 未跟踪的新文件
  • A - 新添加到暂存区的文件
  • M - 已修改的文件
  • D - 已删除的文件
  • MM - 文件被修改并已暂存,但工作区又有新修改

理解状态信息的重要性:养成频繁使用 git status 的习惯。在每次提交前查看状态,确保你提交的内容符合预期。这能避免很多意外,比如不小心提交了不需要的文件。

添加文件

添加单个文件

# 添加特定文件
git add filename.txt

# 添加多个文件
git add file1.txt file2.txt

添加目录

# 添加当前目录下的所有修改
git add .

# 添加特定目录
git add src/

添加所有文件

# 添加所有修改和新增的文件(不包括删除)
git add .

# 添加所有文件(包括删除操作)
git add -A
# 或
git add --all

., -A 的区别:

  • git add .:添加当前目录及子目录的所有变更(不包括删除)
  • git add -A:添加整个仓库的所有变更(包括删除)

交互式添加

# 交互式选择要添加的内容
git add -p

# 会逐段显示差异,让你选择是否添加
# y - 添加这个块
# n - 不添加
# s - 分割成更小的块
# e - 手动编辑

git add -p 是一个非常实用的功能,它让你可以逐个代码块地选择要暂存的内容。当你在一个文件中做了多处修改,但想把它们分成多个提交时,这个命令特别有用。

检查文件差异

# 查看工作区与暂存区的差异(未暂存的修改)
git diff

# 查看暂存区与上次提交的差异(已暂存的修改)
git diff --staged

# 查看特定文件的差异
git diff filename.txt

# 查看两次提交之间的差异
git diff commit1 commit2

# 查看统计信息(不显示具体差异)
git diff --stat

提交修改

基本提交

提交是将暂存区的内容保存到版本库的操作,每次提交都会创建一个永久的快照。

git commit -m "提交说明"

例如:

git commit -m "添加用户登录功能"

提交后的版本库状态:

编写好的提交信息

提交信息应该清晰地说明这次修改的目的。推荐的格式:

# 简短标题(不超过 50 个字符)
git commit -m "修复用户注册时的邮箱验证问题"

# 如果需要详细说明,使用不带 -m 的提交
git commit
# 会打开编辑器,第一行是标题,空一行后是详细说明

提交信息的约定:

  • 使用祈使语气("添加"而不是"添加了")
  • 标题简短,说明做了什么
  • 正文解释为什么做这个修改

添加并提交

# 一步完成添加和提交(只适用于已跟踪的文件)
git commit -a -m "提交说明"

# 简写
git commit -am "提交说明"

注意:-a 只会提交已跟踪的文件(已经被 add 过的文件),新增的文件不会被包含。

修改提交信息

如果发现提交信息写错了,可以修改最后一次提交:

# 修改最后一次提交的信息
git commit --amend -m "新的提交说明"

# 修改最后一次提交,同时添加遗漏的文件
git add forgotten-file.txt
git commit --amend --no-edit # --no-edit 保持原提交信息不变

修改提交信息的过程(不会创建新提交,而是替换最后一次提交):

注意:如果最后一次提交已经推送到远程,修改后需要使用 git push --force,这会改写历史,在团队协作中要谨慎使用。

查看提交历史

# 查看提交历史
git log

# 简洁输出(一行一个提交)
git log --oneline

# 显示每次提交的文件差异
git log -p

# 显示文件修改统计
git log --stat

# 查看最近 n 条记录
git log -n 5

# 图形化显示所有分支
git log --graph --oneline --all

# 查看特定文件的修改历史
git log --follow filename.txt

# 查看某个时间段的提交
git log --since="2024-01-01"
git log --until="2 weeks ago"

# 按作者筛选
git log --author="张三"

自定义输出格式:

# 美化输出
git log --pretty=format:"%h - %an, %ar : %s" --graph

# 常用占位符:
# %h - 短哈希值
# %an - 作者名
# %ar - 相对时间(如 3 天前)
# %s - 提交说明
# %d - 引用名(分支、标签)

撤销操作

撤销工作区修改

# 撤销特定文件的修改(恢复到暂存区的状态)
git checkout -- filename.txt

# 使用新命令(Git 2.23+,更直观)
git restore filename.txt

# 撤销所有修改
git checkout -- .
git restore .

警告:这些操作会丢弃工作区的修改,且无法恢复。使用前确保你真的不需要这些修改。

取消暂存

# 取消暂存特定文件
git reset filename.txt
git restore --staged filename.txt

# 取消所有暂存
git reset
git restore --staged .

撤销提交

soft 模式(保留修改到暂存区)

# 撤销最后一次提交,修改保留在暂存区
git reset --soft HEAD~1

soft 模式示意图:

执行后,提交 C 被撤销,但修改保留在暂存区,可以直接重新提交。这在你想修改最后一次提交的内容时很有用。

mixed 模式(默认,保留修改到工作区)

# 撤销最后一次提交,修改保留在工作区
git reset --mixed HEAD~1
# 或简写
git reset HEAD~1

mixed 模式示意图:

执行后,提交 C 被撤销,修改回到工作区,需要重新 add 后提交。

hard 模式(丢弃修改)

# 撤销最后一次提交,丢弃所有修改
# 警告:这会丢失所有修改,请谨慎使用!
git reset --hard HEAD~1

hard 模式示意图:

执行后,提交 C 和其中的所有修改都被永久删除,HEAD 指向 B。如果误操作了,可以通过 git reflog 找回。

撤销前 n 次提交

# 撤销前 n 次提交,保留修改在暂存区
git reset --soft HEAD~n

# 撤销前 3 次提交
git reset --soft HEAD~3

恢复删除的文件

# 恢复误删的文件(从暂存区恢复)
git checkout HEAD -- filename.txt
git restore --source=HEAD filename.txt

# 恢复到某个提交的版本
git checkout abc123 -- filename.txt

使用 reflog 恢复误操作

reflog 记录了 HEAD 的所有变化,即使使用了 reset --hard,也能通过 reflog 找回丢失的提交。

# 查看所有操作历史
git reflog

# 输出示例
abc1234 HEAD@{0}: reset: moving to HEAD~1
def5678 HEAD@{1}: commit: 添加重要功能
...

# 恢复到指定操作之前的状态
git reset --hard HEAD@{1}

# 或创建新分支
git branch recover-branch HEAD@{1}

文件操作

删除文件

# 从工作区和暂存区删除
git rm filename.txt

# 只从暂存区删除(保留工作区文件)
git rm --cached filename.txt

# 删除目录
git rm -r directory/

# 强制删除(即使文件有未提交的修改)
git rm -f filename.txt

重命名文件

git mv old-name.txt new-name.txt

这相当于执行了以下操作:

mv old-name.txt new-name.txt
git rm old-name.txt
git add new-name.txt

使用 git mv 的好处是 Git 能正确追踪文件的重命名历史。

查看文件历史

# 查看文件的提交历史
git log filename.txt

# 查看文件的每次修改内容
git log -p filename.txt

# 查看谁修改了文件的每一行
git blame filename.txt

# 查看文件在不同提交中的内容
git show abc123:filename.txt

.gitignore 文件

创建 .gitignore 文件来忽略不需要跟踪的文件:

# 忽略所有 .log 文件
*.log

# 忽略 node_modules 目录
node_modules/

# 忽略构建输出
build/
dist/

# 忽略敏感文件
secret.txt
.env

# 但跟踪 lib.js(即使在忽略的目录中)
!lib.js

# 忽略当前目录及子目录的所有 .tmp 文件
**/*.tmp

# 只忽略根目录下的 build 目录
/build/

创建 .gitignore 的技巧:

GitHub 提供了各种语言和框架的 .gitignore 模板,可以参考 gitignore 模板

# 如果文件已经被跟踪,需要先取消跟踪
git rm --cached filename.txt
# 然后添加到 .gitignore

储藏工作区

当你需要切换分支但不想提交当前未完成的工作时,可以使用储藏(stash)功能。

# 储藏当前工作区的修改
git stash

# 带说明的储藏
git stash push -m "正在开发新功能"

# 查看储藏列表
git stash list

# 恢复最近的储藏(不删除储藏记录)
git stash apply

# 恢复并删除最近的储藏
git stash pop

# 恢复指定的储藏
git stash apply stash@{2}

# 删除储藏
git stash drop stash@{0}

# 清空所有储藏
git stash clear

# 储藏未跟踪的文件
git stash -u

# 储藏所有文件(包括被 .gitignore 忽略的)
git stash -a

实际使用场景:

你正在开发功能 A,突然需要切换到另一个分支修复紧急 bug:

# 1. 储藏当前工作
git stash push -m "功能A开发中"

# 2. 切换到修复分支
git checkout hotfix-branch

# 3. 修复 bug 并提交
git commit -am "修复紧急 bug"

# 4. 切换回原分支
git checkout feature-branch

# 5. 恢复之前的工作
git stash pop

标签操作

创建标签

# 创建轻量标签(只是一个指向提交的引用)
git tag v1.0.0

# 创建附注标签(包含标签信息,推荐用于正式发布)
git tag -a v1.0.0 -m "版本 1.0.0 发布"

# 为历史提交添加标签
git tag -a v0.9.0 abc123 -m "0.9.0 版本"

# 签名标签(使用 GPG 签名)
git tag -s v1.0.0 -m "签名的标签"

列出标签

# 列出所有标签
git tag

# 搜索标签(支持通配符)
git tag -l "v1.*"

# 查看标签详情
git show v1.0.0

删除标签

# 删除本地标签
git tag -d v1.0.0

# 删除远程标签
git push origin --delete v1.0.0

推送标签

# 推送特定标签
git push origin v1.0.0

# 推送所有标签
git push origin --tags

配置 Git

全局配置

# 设置用户名和邮箱
git config --global user.name "张三"
git config --global user.email "[email protected]"

# 设置默认编辑器
git config --global core.editor "vim"
git config --global core.editor "code --wait" # VS Code

# 设置默认分支名
git config --global init.defaultBranch main

# 启用颜色输出
git config --global color.ui auto

查看配置

# 查看所有配置
git config --list

# 查看特定配置
git config user.name

# 查看配置来源
git config --list --show-origin

别名设置

# 设置常用别名
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status

# 更复杂的别名
git config --global alias.lg "log --oneline --graph --all"
git config --global alias.last "log -1 HEAD --stat"

设置后可以使用简写:

git st    # 等同于 git status
git co main # 等同于 git checkout main
git lg # 查看图形化历史

小结

本章我们学习了:

  1. Git 工作流程:工作区、暂存区、版本库的三区模型
  2. 创建仓库:初始化和克隆
  3. 查看状态git statusgit diff
  4. 添加和提交git addgit commit 的各种用法
  5. 撤销操作resetrestorereflog 恢复误操作
  6. 文件操作:删除、重命名、查看历史
  7. .gitignore:忽略不需要跟踪的文件
  8. 储藏git stash 临时保存工作
  9. 标签:版本标记
  10. 配置:个性化 Git 设置

练习

  1. 创建一个新仓库,添加几个文件并提交
  2. 修改已提交的文件,尝试使用 git diff 查看差异
  3. 使用 git add -p 交互式选择要暂存的内容
  4. 创建一个 .gitignore 文件
  5. 使用 git stash 储藏并恢复工作区
  6. 练习 git reset 的三种模式

参考资源