Git 的使用:从第一次提交到分支与 Worktree
Git 是一个分布式版本控制系统。简单来说,它会记录项目中每一次有意义的修改,让我们能够查看历史、比较差异、恢复旧版本,也能通过分支同时尝试多种方案。
Git 的版本记录保存在项目根目录下的 .git 目录中。这个目录通常是隐藏的,不要手动修改或删除它;一旦删除,当前项目就不再保留原有的 Git 历史。
本文会同时介绍 VS Code 中的图形化操作,以及对应的 Git 命令行语法。命令均在项目根目录的终端中执行。
一、开始之前:安装并配置 Git
安装完成后,可以先检查 Git 是否可用:
1 | git --version |
第一次使用 Git 时,需要设置提交者姓名和邮箱。它们会出现在提交记录中:
1 | git config --global user.name "你的名字" |
查看当前配置:
1 | git config --global --list |
二、初始化 Git 仓库
在 VS Code 中打开项目后,进入左侧的“源代码管理”,点击“初始化仓库”。
对应的命令行操作是:
1 | git init |
初始化后,项目根目录中会多出一个隐藏的 .git 目录,这表示当前项目已经由 Git 管理。
可以用下面的命令检查仓库状态:
1 | git status |
三、完成第一次提交
我们先新建一个名为 购物清单.md 的文件,内容如下:
1 | 1. 一个西瓜 |
VS Code 会在“源代码管理”中显示这个新文件。一次完整的提交通常分为两步:
- 将准备提交的修改放入暂存区;
- 为暂存区中的修改创建一次提交。
对应的命令是:
1 | # 暂存指定文件 |
如果希望暂存当前目录下的所有修改,也可以使用:
1 | git add . |
git add .很方便,但提交前最好先运行git status,确认没有把临时文件、密码或无关内容一起加入提交。
工作区、暂存区和版本库
理解 Git 时,可以把文件的流转看成三个阶段:
1 | 工作区中的修改 -- git add --> 暂存区 -- git commit --> 本地版本库 |
- 工作区:当前实际看到和编辑的文件;
- 暂存区:已经选中、准备放进下一次提交的修改;
- 版本库:已经提交、可以长期追溯的历史记录。
四、继续修改并查看历史
接下来给购物清单添加一个榴莲:
1 | 1. 一个西瓜 |
完成第二次提交:
1 | git add "购物清单.md" |
查看简洁的提交历史:
1 | git log --oneline --graph --decorate --all |
输出大致如下:
1 | * a1b2c3d (HEAD -> main) 添加榴莲 |
其中:
- 每行开头的字符是提交 ID 的缩写;
main是当前分支名;HEAD -> main表示当前正位于main分支的最新提交上。
五、查看文件差异
在 VS Code 的源代码管理面板中点击文件,或者在时间线中选择“打开更改”,可以对比修改前后的内容。
命令行中可以使用:
1 | # 查看工作区中尚未暂存的修改 |
六、撤销修改
假设我们又添加了“一个苹果”,但还没有提交,现在想恢复到上一次提交时的状态。在 VS Code 中可以点击源代码管理面板右上角的 …,然后选择“放弃所有更改”;也可以只对某个文件选择“放弃更改”。
对应的命令是:
1 | # 放弃指定文件中尚未暂存的修改 |
如果文件已经放入暂存区,需要先取消暂存,再决定是否放弃工作区修改:
1 | # 取消暂存,但保留文件内容 |
如果错误已经提交,并且提交已经推送到远程仓库,推荐用 revert 创建一个“反向提交”:
1 | git revert <提交ID> |
git revert 不会改写已有历史,因此比 git reset 更适合处理已经共享出去的提交。
git restore和git reset --hard都可能丢失尚未提交的内容。执行前一定要先用git status和git diff确认范围。
七、创建和切换分支
分支可以理解为一条独立的开发路线。我们可以在新分支中试验或开发功能,而不影响稳定的 main 分支。
先创建并切换到 shopping-update 分支:
1 | git switch -c shopping-update |
这条命令相当于下面两条命令:
1 | git branch shopping-update |
然后在购物清单中添加苹果和薯片:
1 | 1. 一个西瓜 |
在新分支中提交:
1 | git add "购物清单.md" |
查看所有本地分支:
1 | git branch |
分支名前的 * 表示当前所在分支。切换回 main:
1 | git switch main |
这时,工作区会恢复为 main 分支对应的内容;刚才的苹果和薯片仍然安全地保存在 shopping-update 分支中。
也可以点击 VS Code 左下角的分支名称进行切换:
什么是“签出(已分离)”
如果直接签出某个历史提交,而不是切换到某个分支,就会进入 Detached HEAD(分离头指针) 状态:
1 | git switch --detach <提交ID> |
此时可以查看、编译和运行旧版本,但新提交不属于任何现有分支。若确实需要保留此时的修改,应及时创建分支:
1 | git switch -c save-detached-work |
只想查看旧版本、不做修改时,可以直接切回主分支:
1 | git switch main |
八、合并分支
当 shopping-update 分支上的内容完成后,可以将它合并回 main:
1 | # 先切换到接收修改的目标分支 |
确认合并完成后,可以删除已经不再需要的本地分支:
1 | git branch -d shopping-update |
如果两个分支修改了同一文件的同一位置,Git 可能无法自动判断应该保留哪一份内容,这就是合并冲突。文件中会出现类似标记:
1 | <<<<<<< HEAD |
处理冲突的步骤如下:
1 | # 1. 查看冲突文件 |
如果不想继续本次合并,可以在提交前取消:
1 | git merge --abort |
九、使用 Git Worktree 同时处理多个分支
创建分支只是“划出一条新的平行线”,而 Worktree(工作树)则是“在电脑上同时打开另一个平行世界”。二者解决的是不同层面的问题,但经常结合使用。
分支和 Worktree 的区别
| 维度 | 普通分支 | Git Worktree |
|---|---|---|
| 本质 | 指向某次提交的引用 | 硬盘上的另一个工作目录 |
| 占用空间 | 几乎不占空间 | 需要额外存放一份工作区文件,但共享 Git 对象库 |
| 使用方式 | 在同一目录中切换分支 | 直接打开不同目录,无须来回切换 |
| 并行处理 | 一个工作目录同一时间只能检出一个分支 | 可以同时编辑、运行和测试多个分支 |
创建 Worktree
假设主项目位于 /prjs/learn-git,我们想在相邻目录中创建一个用于修复 Bug 的 Worktree:
1 | cd /prjs/learn-git |
这条命令会:
- 基于
main创建hotfix-branch分支; - 在
../learn-git-hotfix创建新的工作目录; - 在新目录中检出
hotfix-branch。
如果分支已经存在,则不需要 -b:
1 | git worktree add ../learn-git-hotfix hotfix-branch |
查看所有 Worktree:
1 | git worktree list |
Worktree 的注意事项
同一个分支通常不能同时被两个 Worktree 检出。例如主工作目录已经位于 main,新 Worktree 就不应再次选择 main,否则 Git 会提示该分支已经被检出。
另外,删除 Worktree 时,git worktree remove 后面接的是工作树路径,不是分支名:
1 | git worktree remove ../learn-git-hotfix |
在 Worktree 中提交,并在主目录中合并
进入 Worktree,修复 Bug 并提交:
1 | cd /prjs/learn-git-hotfix |
主目录与 Worktree 共享同一个 Git 版本库,因此 Worktree 一旦完成提交,主目录立刻就能看到该提交,无须执行 git pull,也不需要通过文件路径同步。
回到主目录后,将 hotfix-branch 合并到 main:
1 | cd /prjs/learn-git |
如果已经配置远程仓库,可以推送合并后的 main:
1 | git push origin main |
清理 Worktree 和分支
确认 Worktree 中的修改已经提交或不再需要后,回到主目录执行:
1 | cd /prjs/learn-git |
如果 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 | # 系统文件 |
如果某个文件已经被 Git 跟踪,后来才加入 .gitignore,还需要将它从 Git 索引中移除,但保留本地文件:
1 | git rm --cached .env |
如果密码或令牌已经提交并推送,仅删除文件并不能让秘密从历史中消失。应立即让旧凭据失效并重新生成。
十二、临时保存尚未完成的修改
切换分支时,如果当前修改会与目标分支冲突,Git 会阻止切换。此时可以先提交,也可以用 stash 临时保存:
1 | # 临时保存当前修改 |
若只想恢复但暂时保留这条 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 最重要的不是记住所有命令,而是理解几个核心概念:
- 修改先发生在工作区;
git add将选中的修改放入暂存区;git commit把暂存区保存为一个可追溯的版本;- 分支用于维护不同的开发路线;
- Worktree 让多个分支能够在不同目录中同时工作;
- 远程仓库用于备份、分享和协作。
平时养成“小步修改、及时检查、说明清楚再提交”的习惯,Git 就会从看似复杂的命令集合,变成一个非常可靠的版本管理工具。