Git 的使用:从第一次提交到分支与 Worktree

Git 是一个分布式版本控制系统。简单来说,它会记录项目中每一次有意义的修改,让我们能够查看历史、比较差异、恢复旧版本,也能通过分支同时尝试多种方案。

Git 的版本记录保存在项目根目录下的 .git 目录中。这个目录通常是隐藏的,不要手动修改或删除它;一旦删除,当前项目就不再保留原有的 Git 历史。

本文会同时介绍 VS Code 中的图形化操作,以及对应的 Git 命令行语法。命令均在项目根目录的终端中执行。

一、开始之前:安装并配置 Git

安装完成后,可以先检查 Git 是否可用:

1
git --version

第一次使用 Git 时,需要设置提交者姓名和邮箱。它们会出现在提交记录中:

1
2
git config --global user.name "你的名字"
git config --global user.email "你的邮箱@example.com"

查看当前配置:

1
git config --global --list

二、初始化 Git 仓库

在 VS Code 中打开项目后,进入左侧的“源代码管理”,点击“初始化仓库”。

对应的命令行操作是:

1
git init
VS Code 初始化 Git 仓库的位置
图 1:在 VS Code 中初始化 Git 仓库

初始化后,项目根目录中会多出一个隐藏的 .git 目录,这表示当前项目已经由 Git 管理。

可以用下面的命令检查仓库状态:

1
git status
Git 初始化后的项目目录
图 2:Git 初始化后的项目目录

三、完成第一次提交

我们先新建一个名为 购物清单.md 的文件,内容如下:

1
1. 一个西瓜

VS Code 会在“源代码管理”中显示这个新文件。一次完整的提交通常分为两步:

  1. 将准备提交的修改放入暂存区;
  2. 为暂存区中的修改创建一次提交。

对应的命令是:

1
2
3
4
5
# 暂存指定文件
git add "购物清单.md"

# 提交暂存区中的内容
git commit -m "添加购物清单"

如果希望暂存当前目录下的所有修改,也可以使用:

1
git add .

git add . 很方便,但提交前最好先运行 git status,确认没有把临时文件、密码或无关内容一起加入提交。

在 VS Code 中提交修改
图 3:在 VS Code 中提交修改

工作区、暂存区和版本库

理解 Git 时,可以把文件的流转看成三个阶段:

1
工作区中的修改  -- git add -->  暂存区  -- git commit -->  本地版本库
  • 工作区:当前实际看到和编辑的文件;
  • 暂存区:已经选中、准备放进下一次提交的修改;
  • 版本库:已经提交、可以长期追溯的历史记录。

四、继续修改并查看历史

接下来给购物清单添加一个榴莲:

1
2
1. 一个西瓜
2. 一个榴莲

完成第二次提交:

1
2
git add "购物清单.md"
git commit -m "添加榴莲"

查看简洁的提交历史:

1
git log --oneline --graph --decorate --all

输出大致如下:

1
2
* a1b2c3d (HEAD -> main) 添加榴莲
* e4f5g6h 添加购物清单

其中:

  • 每行开头的字符是提交 ID 的缩写;
  • main 是当前分支名;
  • HEAD -> main 表示当前正位于 main 分支的最新提交上。
在 VS Code 中查看 Git 时间线
图 4:查看提交时间线

五、查看文件差异

在 VS Code 的源代码管理面板中点击文件,或者在时间线中选择“打开更改”,可以对比修改前后的内容。

命令行中可以使用:

1
2
3
4
5
6
7
8
9
10
11
# 查看工作区中尚未暂存的修改
git diff

# 查看已经暂存、即将提交的修改
git diff --staged

# 查看最近一次提交具体改了什么
git show HEAD

# 查看某个文件的历史
git log --follow -- "购物清单.md"
在 VS Code 中查看文件差异
图 5:查看文件差异

六、撤销修改

假设我们又添加了“一个苹果”,但还没有提交,现在想恢复到上一次提交时的状态。在 VS Code 中可以点击源代码管理面板右上角的 ,然后选择“放弃所有更改”;也可以只对某个文件选择“放弃更改”。

对应的命令是:

1
2
3
4
5
# 放弃指定文件中尚未暂存的修改
git restore "购物清单.md"

# 放弃当前目录下所有尚未暂存的修改
git restore .
在 VS Code 中放弃修改
图 6:放弃尚未提交的修改

如果文件已经放入暂存区,需要先取消暂存,再决定是否放弃工作区修改:

1
2
3
4
5
# 取消暂存,但保留文件内容
git restore --staged "购物清单.md"

# 如果连文件修改也不要了,再执行
git restore "购物清单.md"

如果错误已经提交,并且提交已经推送到远程仓库,推荐用 revert 创建一个“反向提交”:

1
git revert <提交ID>

git revert 不会改写已有历史,因此比 git reset 更适合处理已经共享出去的提交。

git restoregit reset --hard 都可能丢失尚未提交的内容。执行前一定要先用 git statusgit diff 确认范围。

七、创建和切换分支

分支可以理解为一条独立的开发路线。我们可以在新分支中试验或开发功能,而不影响稳定的 main 分支。

先创建并切换到 shopping-update 分支:

1
git switch -c shopping-update

这条命令相当于下面两条命令:

1
2
git branch shopping-update
git switch shopping-update

然后在购物清单中添加苹果和薯片:

1
2
3
4
1. 一个西瓜
2. 一个榴莲
3. 一个苹果
4. 一包薯片

在新分支中提交:

1
2
git add "购物清单.md"
git commit -m "添加苹果和薯片"

查看所有本地分支:

1
git branch

分支名前的 * 表示当前所在分支。切换回 main

1
git switch main

这时,工作区会恢复为 main 分支对应的内容;刚才的苹果和薯片仍然安全地保存在 shopping-update 分支中。

main 分支内容
新分支内容
图 7:两个分支的内容对比

也可以点击 VS Code 左下角的分支名称进行切换:

在 VS Code 中切换分支
图 8:在 VS Code 中切换分支

什么是“签出(已分离)”

如果直接签出某个历史提交,而不是切换到某个分支,就会进入 Detached HEAD(分离头指针) 状态:

1
git switch --detach <提交ID>

此时可以查看、编译和运行旧版本,但新提交不属于任何现有分支。若确实需要保留此时的修改,应及时创建分支:

1
git switch -c save-detached-work

只想查看旧版本、不做修改时,可以直接切回主分支:

1
git switch main

八、合并分支

shopping-update 分支上的内容完成后,可以将它合并回 main

1
2
3
4
5
# 先切换到接收修改的目标分支
git switch main

# 合并开发分支
git merge shopping-update

确认合并完成后,可以删除已经不再需要的本地分支:

1
git branch -d shopping-update

如果两个分支修改了同一文件的同一位置,Git 可能无法自动判断应该保留哪一份内容,这就是合并冲突。文件中会出现类似标记:

1
2
3
4
5
<<<<<<< HEAD
main 分支的内容
=======
shopping-update 分支的内容
>>>>>>> shopping-update

处理冲突的步骤如下:

1
2
3
4
5
6
7
8
9
10
# 1. 查看冲突文件
git status

# 2. 手动编辑冲突文件,删除冲突标记并保留最终内容

# 3. 将解决后的文件加入暂存区
git add "购物清单.md"

# 4. 完成合并提交
git commit

如果不想继续本次合并,可以在提交前取消:

1
git merge --abort

九、使用 Git Worktree 同时处理多个分支

创建分支只是“划出一条新的平行线”,而 Worktree(工作树)则是“在电脑上同时打开另一个平行世界”。二者解决的是不同层面的问题,但经常结合使用。

在 VS Code 中创建工作树
图 9:创建工作树

分支和 Worktree 的区别

维度 普通分支 Git Worktree
本质 指向某次提交的引用 硬盘上的另一个工作目录
占用空间 几乎不占空间 需要额外存放一份工作区文件,但共享 Git 对象库
使用方式 在同一目录中切换分支 直接打开不同目录,无须来回切换
并行处理 一个工作目录同一时间只能检出一个分支 可以同时编辑、运行和测试多个分支

创建 Worktree

假设主项目位于 /prjs/learn-git,我们想在相邻目录中创建一个用于修复 Bug 的 Worktree:

1
2
cd /prjs/learn-git
git worktree add -b hotfix-branch ../learn-git-hotfix main

这条命令会:

  1. 基于 main 创建 hotfix-branch 分支;
  2. ../learn-git-hotfix 创建新的工作目录;
  3. 在新目录中检出 hotfix-branch

如果分支已经存在,则不需要 -b

1
git worktree add ../learn-git-hotfix hotfix-branch

查看所有 Worktree:

1
git worktree list
为 Worktree 选择新分支
图 10:为 Worktree 选择新分支
创建 Worktree
图 11:创建 Worktree
Worktree 对应的独立文件夹
图 12:Worktree 对应的独立文件夹

Worktree 的注意事项

同一个分支通常不能同时被两个 Worktree 检出。例如主工作目录已经位于 main,新 Worktree 就不应再次选择 main,否则 Git 会提示该分支已经被检出。

另外,删除 Worktree 时,git worktree remove 后面接的是工作树路径,不是分支名:

1
git worktree remove ../learn-git-hotfix

在 Worktree 中提交,并在主目录中合并

进入 Worktree,修复 Bug 并提交:

1
2
3
4
cd /prjs/learn-git-hotfix
git status
git add .
git commit -m "修复登录页面的 Bug"

主目录与 Worktree 共享同一个 Git 版本库,因此 Worktree 一旦完成提交,主目录立刻就能看到该提交,无须执行 git pull,也不需要通过文件路径同步。

回到主目录后,将 hotfix-branch 合并到 main

1
2
3
cd /prjs/learn-git
git switch main
git merge hotfix-branch

如果已经配置远程仓库,可以推送合并后的 main

1
git push origin main

清理 Worktree 和分支

确认 Worktree 中的修改已经提交或不再需要后,回到主目录执行:

1
2
3
4
5
6
7
8
9
10
cd /prjs/learn-git

# 删除额外的工作目录
git worktree remove ../learn-git-hotfix

# 清理已经失效的 Worktree 管理记录
git worktree prune

# 删除已经合并的本地分支
git branch -d hotfix-branch

如果 Worktree 中仍有未提交的修改,Git 会拒绝普通删除。此时应先提交、暂存或备份这些内容。只有确认可以丢弃时,才使用:

1
git worktree remove --force ../learn-git-hotfix

十、连接远程仓库并推送

本地提交只保存在自己的电脑中。若要备份到 GitHub、GitLab 或 Gitee,或者与他人协作,需要连接远程仓库。

查看当前远程仓库:

1
git remote -v

为本地仓库添加名为 origin 的远程地址:

1
git remote add origin <远程仓库地址>

第一次推送 main 分支:

1
git push -u origin main

其中 -u 会建立本地 main 与远程 origin/main 的跟踪关系。以后通常可以直接执行:

1
git push

获取并合并远程更新:

1
git pull

如果只想查看远程更新、暂时不合并:

1
git fetch origin

从远程仓库下载一个新项目:

1
git clone <远程仓库地址>

推送一个新分支:

1
git push -u origin hotfix-branch

删除远程分支:

1
git push origin --delete hotfix-branch

十一、使用 .gitignore 忽略文件

日志、缓存、依赖目录、编辑器配置和包含密钥的环境变量文件通常不应该提交。可以在项目根目录创建 .gitignore

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 系统文件
.DS_Store
Thumbs.db

# 编辑器配置
.vscode/

# 依赖和构建结果
node_modules/
dist/

# 环境变量和日志
.env
*.log

如果某个文件已经被 Git 跟踪,后来才加入 .gitignore,还需要将它从 Git 索引中移除,但保留本地文件:

1
2
git rm --cached .env
git commit -m "停止跟踪环境变量文件"

如果密码或令牌已经提交并推送,仅删除文件并不能让秘密从历史中消失。应立即让旧凭据失效并重新生成。

十二、临时保存尚未完成的修改

切换分支时,如果当前修改会与目标分支冲突,Git 会阻止切换。此时可以先提交,也可以用 stash 临时保存:

1
2
3
4
5
6
7
8
# 临时保存当前修改
git stash push -m "暂存未完成的购物清单修改"

# 查看暂存列表
git stash list

# 恢复最近一次暂存,并将其从列表中删除
git stash pop

若只想恢复但暂时保留这条 stash 记录:

1
git stash apply

十三、常用命令速查

操作 命令
初始化仓库 git init
查看状态 git status
暂存指定文件 git add <文件>
暂存所有修改 git add .
创建提交 git commit -m "说明"
查看提交历史 git log --oneline --graph --decorate --all
查看未暂存差异 git diff
查看已暂存差异 git diff --staged
放弃未暂存修改 git restore <文件>
取消暂存 git restore --staged <文件>
创建并切换分支 git switch -c <分支名>
切换分支 git switch <分支名>
合并分支 git merge <分支名>
删除已合并的本地分支 git branch -d <分支名>
查看 Worktree git worktree list
创建 Worktree 和新分支 git worktree add -b <分支名> <目录> <起点>
删除 Worktree git worktree remove <目录>
查看远程仓库 git remote -v
拉取远程更新 git pull
推送本地提交 git push

结语

Git 最重要的不是记住所有命令,而是理解几个核心概念:

  1. 修改先发生在工作区;
  2. git add 将选中的修改放入暂存区;
  3. git commit 把暂存区保存为一个可追溯的版本;
  4. 分支用于维护不同的开发路线;
  5. Worktree 让多个分支能够在不同目录中同时工作;
  6. 远程仓库用于备份、分享和协作。

平时养成“小步修改、及时检查、说明清楚再提交”的习惯,Git 就会从看似复杂的命令集合,变成一个非常可靠的版本管理工具。


本站由 楠瓜 使用 Stellar 1.33.1 主题创建。
风起于青萍之末,浪成于微澜之间。