跳转至

Cursor 中的 Git 最佳实践

预计阅读时长 : 42 分钟

作为一个专业的文字工作者,对工作文档进行版本管理是必须的操作,毕竟谁也猜不透甲方的爸爸们究竟会喜欢第几版。

而作为一个 搞笑 高效的开发者,会熟练使用 Git 管理代码更是基本功。不论是个人项目管理,还是团队远程合作,当代软件工程的高效工作流程,无一例外都建立在 Git 的基础之上。

接下去,我们来一起探索一下如何在 Cursor 中使用 Git 来进行项目版本管理,并通过原生功能配合扩展插件,实现最高效和顺畅的使用体验。

准备工作

学习教程

关于 Git 的基础知识,可以参考廖雪峰老师的入门教程:

阮一峰老师的系列教程也非常深入浅出,适合用来作为补充阅读和随手查询材料:

如果你还有兴趣进一步研究,尤其是命令行的操作,那么可以参考以下的这本经典教材。

以上教程都是以本地操作为主,而想要把 Git 的作用发挥到最大,还是得去世界上最大的同性交友网站 Github 上溜达溜达。实验楼的这个免费教程,以互动形式让学习者可以进行远程实操练习,值得一试。

当然了,至于如何在 Cursor 中践行 Git 的最佳实践,没有比本文更详细和直观的教程了。

插件推荐

为了在 Cursor 中最高效地使用 Git,我们需要在系统自带的 Git 功能的基础上,再安装几个常用插件,配合使用来实现最佳的版本管理体验。

在以下的教程中,我不会再提哪个功能具体是由哪个插件提供的,建议大家全部安装以便能体验到最佳的版本管理体验。

基础概念

alt text
alt text

虽然我们假设本文的读者,都已经对 Git 有所了解。但我们还是先简要复习一下 Git 中的几个基础概念,重点是统一各个概念的称谓,避免出现理解上的偏差。

Git 基础概念

以下基本概念,和官方的定义的基本概念会略有不同,特别是增加了 “更改区” 这个虚拟概念,会更加方便理解,以及和 Cursor 中的操作相对应。

  • 存储库:指向当前分支的 HEAD 指针
  • 更改区:保存未提交到暂存区的本地文件更改的区域
  • 暂存区:保存已提交被本地文件更改的区域
  • 存储区:更改区 + 暂存区的集合
  • 工作区:存储库 + 存储区的集合

后面章节提到的所有内容,都是围绕这几个基础概念,以及它们之间的操作来进行的。在每一个章节中,我们都会先介绍相应的 Git 命令,然后再通过 Cursor 的 UI 界面操作来实现相同的效果。

关于 Git 的基本操作,熟知下面这张图就差不多足够了。

而这些指令和上面的几个概念相结合,我们就得到了一张如下的操作流程示意图。

graph TD
    %% 初始化
    init[init]
    init --> remote_add[remote add]
    clone[clone]

    remote_add --> branch[branch]
    clone --> branch

    %% 本地存储库
    branch --> switch[switch]
    switch --> add[add]

    %% 文件修改阶段
    add --> status[status]
    add --> diff[diff]
    add --> restore[restore]
    restore --> add
    add --> stash[stash]
    stash --> add

    add --> commit[commit]
    commit --> log[log]
    Operations --> add
    commit --> Operations

    %% 历史操作命令
    subgraph Operations[ ]
        direction LR
        rebase_i[rebase -i]
        cherry_pick[cherry-pick]
        revert[revert]
        reset[reset]
        checkout[checkout]
    end

    %% 远程存储库
    branch --> fetch[fetch]
    fetch --> merge[merge]
    fetch --> rebase[rebase]
    branch --> pull[pull]

    %% 冲突处理
    merge --> merge_conflict{{Resolve Merge Conflicts}}
    rebase --> merge_conflict
    pull --> merge_conflict
    merge_conflict --> add

    %% 远程协作
    commit --> push[push]
    commit --> tag[tag]
    tag --> push
    push --> pull_request[Pull Request]

项目初始化

让我们先从本地项目开始,迈出版本管理的第一步。

本地初始化

初始化项目
git init

首先,从源代码管理面板中,点击 打开文件夹 按钮,选择一个本地文件夹。

然后,点击 初始化仓库 按钮。OK,项目的 Git 初始化完成!

好像也没什么变化???

不要着急,完成项目初始化之后,让我们对项目文件进行一些修改。

注意看,这时编辑窗口左侧的行号旁,是不是多了一些不同的色条?这些色条就是 Git 根据文件的修改状态添加的标识。

  • 绿色:新增的内容
  • 蓝色:修改的内容
  • 红色:删除的内容

恭喜你,至此已经迈出了版本管理坚实的第一步!

查看存储区状态

查看存储区状态
git status

然后,让我们再次回到左侧边栏里的 源代码管理 面板,在 更改 里面会发现有内容更改的文件都在这里显示了出来,在后方还也标注了其状态。而在编辑窗口最上方的文件名旁边,也会根据当前文件的修改状态使用不同颜色添加说明文字。

  • U:代表新增从未提交过版本记录的文件
  • M:代表曾提交过版本记录,且有修改但还未提交新版本记录的文件
  • D:代表曾提交过版本记录,但被删除的文件

查看差异

接下去,我们将移动鼠标到特定的文件名上。此时,高亮的区域将有四个可点击的功能操作:

查看文件差异
git diff

首先,我们来看一下使用功能 ❶ 进行版本差异对比的效果。

在这个页面中,我们可以查看特定文件的当前工作区版本和暂存区/存储库版本之间的差异。其中左边是暂存区/存储库版本,右边是当前工作区版本。其中的颜色标识和前面提到的状态标识是一致的。

功能 ❷ 就比较简单了,会直接在文件的编辑窗口中,打开当前工作区版本。

撤销工作区修改

撤销工作区修改
git restore

功能 ❸ 则是放弃工作区版本中的修改,恢复到暂存区版本。点击之后,会弹出确认对话框。当点击确认之后,文件编辑窗口中的内容会恢复到暂存区/存储库版本。

暂存文件

暂存文件
1
2
3
4
5
# 暂存指定文件
git add <file>

# 暂存所有文件
git add .

功能 ❹ 则是将工作区的更改放入暂存区。执行了这一步操作之后,我们会在 源代码管理 面板中看到新增了一个 暂存的更改 栏。

比较暂存区和存储库

将鼠标移到文件名上,同样会浮现刚才相似的操作区域和按钮,但是对应的操作效果略有不同。

比较暂存区和存储库
git diff --cached
  • 操作 ❶ 会打开暂存区版本和存储库版本的对比界面
  • 操作 ❷ 会打开工作区版本的编辑窗口
  • 操作 ❸ 是取消暂存区更改,相当于丢弃暂存区版本,此时工作区版本会保持现有状态不变

看到这里请暂停一下,从本节的开头重新看一遍,复习一下整个操作流程,然后自己动手试试看,看看能否真正理解了这几个概念和操作的定义。

不要着急,在日常实践中,这个章节的操作流程占据了整个 Git 操作的相当大一部分,因此请务必确保自己已经完全理解了整个操作流程。

操作存储区快照

操作存储区快照
# 保存当前工作区状态到存储区
git stash

# 修改默认设置,增加保存未跟踪的文件
git stash -u

# 查看存储区列表
git stash list

# 恢复存储区状态,并删除存储区记录
git stash pop

# 恢复存储区状态,并保留存储区记录
git stash apply

快照存储功能用于临时保存和读取存储区的快照。当你需要临时做一些与当前工作无关的工作但又不想提交当前的改动时,这个功能特别有用。

例如,我们正在开发新功能的时候,又突然需要修个一个 bug。这时就需要切换到另一个分支,但是当前的工作还没有完成,也还不想提交,那么就可以使用快照存储功能。

先把当前的存储区快照临时存储起来,然后切换到目标分支,等工作完成后,再切换回当前分支,并恢复之前的快照,即可无缝继续之前的开发工作。

需要注意的是,快照功能是跨分支的。因此,即使切换了分支,快照存储的存储区内容也会直接恢复到当前分支中。

存储库管理

当我们在工作区内完成了所有的文件更改,并将其全部提交到暂存区之后,我们就可以开始在 存储库 中的冒险旅程了。

提交历史记录

提交历史记录
1
2
3
4
5
6
7
8
# 提交历史记录
git commit

# 提交历史记录并添加提交信息
git commit -m "提交信息"

# 修改最近一次历史记录
git commit --amend -m "提交信息"

回到 源代码管理 面板,在 消息 栏中输入提交信息,然后点击 ✓ 提交 按钮,即可将当前的暂存区版本提交到 存储库 中保存为历史记录。

除了使用 ✓ 提交 按钮之外,我们还可以从菜单的 提交 选项中,选择更多的提交方式。

查看历史记录

查看历史记录
git log

随着向存储库中提交的历史记录越来越多,我们需要一个更加直观的方式来查看和管理这些历史记录。

为了提升版本管理的体验,我们不会使用系统自带的 时间线 面板,而是 改为使用 Git History Graph 插件来项目的历史记录。

回到左侧边栏点击 源代码管理,在面板的中点击时钟图标,就会自动打开项目的历史记录面板。

在任意的提交历史记录上点击左键,会弹出当次提交的文件列表,点击任意一个文件,就可以打开文件的相邻历史记录对比界面。

而在任意的提交历史记录上点击右键,则会弹出相应的操作菜单,可以进行下面提到的各种关于历史记录的操作。

文件历史记录

要查看文件的历史记录,只需要在文件的编辑窗口中,点击右键菜单选择 Git : View File History 即可。

同样的,点击右键菜单可以选择相应的操作。不过需要注意的是,这里的操作的效果和上面项 界面中的操作效果相同,针对的是整个提交涉及的所有文件,而不仅仅只是当前文件。

检查历史记录

检查历史记录
git checkout <commit-hash>

在提交历史上使用 checkout 命令,可以切换到某个特定的历史记录上,并进入一个称为分离 HEAD 状态(detached HEAD state) 的特殊状态。

在这个状态下,Git 不再跟踪当前分支,而是让你可以查看指定历史记录中文件的内容。一般而言,这个操作的作用是检查历史记录中文件的内容,以确定是否要重置到该版本,或者撤销到指定的版本。

如果在分离 HEAD 状态下,对文件进行了更改,那么这些更改将不会被自动保存到暂存区中。而是需要通过创建新的分支来保存这些更改。所以在最佳实践中,建议不要在分离 HEAD 状态下进行文件的修改,而是专注于进行版本内容的检查。

重置历史记录

重置历史记录
# 回到 <HEAD> 版本,默认使用 --mixed 选项
git reset

# 重置历史记录,清空暂存区,保留工作区更改
git reset --mixed<commit-hash>

# 重置历史记录,清空暂存区,丢弃工作区更改
git reset --hard <commit-hash>

# 重置历史记录,保留暂存区,保留工作区更改
git reset --soft <commit-hash>

在完成了对特定历史记录的内容检查之后,我们可以使用 reset 命令,将当前分支的 HEAD 重置到指定历史记录上,并保留工作区的更改。

使用 reset 命令,重置到的历史记录之后提交的所有历史记录都会被丢弃,不会在提交历史中产生新的提交记录。

撤销历史记录

撤销历史记录
git revert <commit-hash>

使用 revert 命令,则重置到的历史记录之后提交的所有历史记录都会被保留,并且会创建一个新的提交记录,用于记录撤销指定历史记录的更改。

变基历史记录

变基历史记录
git rebase -i

变基历史记录,也称为交互变基,主要用于当前分支整理和清理提交历史版本,以便在提交历史中产生更加简洁和线性的提交记录。

管理标签

管理标签
1
2
3
4
5
6
7
8
# 创建标签
git tag <tag>

# 查看标签
git tag

# 删除标签
git tag -d <tag>

标签是指向特定历史记录的引用,主要用于标记重要的版本节点。通过使用标签,我们可以更加直观的指定某个特定的历史记录。

尤其是在和 CI/CD 系统配合使用的时候,标签可以提供更加直观和方便的版本管理。需要注意的是,标签的创建首先是在本地进行的,也同样需要使 push 操作将标签推送到远程存储库中。

为了减少 CI/CD 触发的不确定性,一般建议单独的进行标签的推送,而不要和常规的代码推送合并在一起。

多分支管理

在上一个章节中,我们虽然没有提过分支这个概念,但事实上我们一直工作在默认的名为 main 的分支上。

接下去我们来看看如何创建更多的分支,以及对分支进行各种操作。

创建分支

创建分支
git branch <branch>

创建分支,本质上是在本地存储库中创建一个新的分支引用,并将其指向当前分支的 HEAD 位置。

源代码管理 的菜单中,我们可以选择使用 签出到 选项,或者在 分支 选项中,根据需要从当前分支或者指定分支创建新的分支。

在状态栏的最右侧,也有当前分支的信息显示,点击同样可以唤出 签出到 的选项下拉框。

切换分支

切换分支
git switch <branch>

切换分支,本质上是通过更新 HEAD 指针,将当前分支切换到指定的分支。

同样是在 签出到 功能选项中,点击分支名即可快速完成分支的切换操作。

合并分支

合并分支
git merge <branch>

合并分支可以说是 Git 中最核心的操作,尤其是在团队协作的场景下。当我们想要将一个分支的更新合并到另一个分支时,就需要使用合并操作。

在不存在冲突的情况下,合并操作非常简单,只需要在 源代码管理 面板中,点击 ··· 按钮唤出 分支 菜单,然后点击 合并 选项,然后在弹出菜单框中选择想要合并的分支,点击 合并 按钮即可完成合并操作。

但如果存在冲突,问题就变得稍微复杂一些,需要手动解决冲突,然后再进行合并操作。

Cursor 的合并冲突编辑器操作非常直观,一共有四种方式来确定采用的合并策略。

  • 接受传入:使用待合并分支的 commit
  • 接受当前:使用当前分支的 commit
  • 重置为基:使用两者共同的基 commit
  • 手动解决:手动解决冲突

变基分支

变基分支
git rebase <branch>

变基分支可以理解为是合并分支的简洁形式,本质是将一系列提交"重新基于"另一个基础点进行提交,从而让提交记录看起来更加简洁和线性。

变基分支也同样可能遇到合并冲突,同样需要使用上面的逻辑来进行处理。

分支对比

Cursor 原生功能并不支持分支之间的对比,因此我们将使用 Git Tree Compare 插件来实现类似的功能。

在右侧的 Git Tree Compare 面板中,点击标红的分支选择按钮,然后在弹出菜单中选择想要比较的分支,就会在面板中自动列出所有存在差异的文件。

这个功能常用于以下场景:

  • 查看当前工作区与远程仓库版本的差异
  • 在合并上游更新前预览变更内容
  • 在处理复杂合并冲突时进行文件对比

交互式操作

截止到这里,我们已经讲完了 Git 在本地的绝大部分操作。大多数的功能也都可以通过命令行 + UI 界面的方式来完成。

但是,有没有更加直观和便捷的操作方式呢?接下去让我们看看最近新涌现出来的一个交互式操作插件:Interactive Git Log ⧉

Interactive Git Log
Interactive Git Log

这个插件将 Cursor 中的 Git 操作体验提升到了一个新的层次,让原本枯燥无味且容易犯错的 Git 操作变成了一个非常直观和愉悦的体验,堪比当年从 DOS 升级到 Windows 的飞跃。

强烈推荐重度 Git 爱好者尝试,相信你一定会像我一样爱上这个插件的。

远程存储库

在完成了本地的版本操作演练之后,现在让我们来看看远程存储库的相关操作。如果我们的工作涉及到远程协作,那么对远程存储库的操作就必不可少了。

克隆远程存储库

克隆远程存储库
git clone <repository-url>

最简单的使用远程存储库的方式,就是使用 克隆仓库 命令,直接克隆远程存储库到本地。

例如,我们在 Github 上已经有了项目,那么我们可以使用 克隆仓库 命令直接克隆远程存储库到本地。

对于通过 Clone 创建的项目,默认已经添加了名为 origin 的远程存储库。

创建远程存储库

创建远程存储库
# 创建远程存储库
gh repo create

如果我们的项目起步于本地,那么我们可以使用 发布 命令,将本地项目发布到远程存储库中。

在 Cursor 中,本地项目如果还没有和远程存储库绑定,则会在第一次执行 发布 命令的时候,引导发布到 Github 远程存储库。

绑定远程存储库

绑定远程存储库
git remote add <remote-name> <repository-url>

如果本地项目已经有了对应的远程存储库,但还没有进行绑定,例如创建项目的时候是使用的下载的源代码,那么我们可以使用 git remote add 命令,手动绑定远程存储库。

依然是在 源代码管理 面板中,点击 ··· 按钮唤出菜单,然后点击 添加远程存储库 选项,然后在弹出菜单框中输入远程存储库的 URL 地址,点击 添加 按钮即可完成远程存储库的添加。

上面提过,对于通过 Clone 创建的项目,默认已经添加了名为 origin 的远程存储库。

而除了 origin 之外,我们还可以为项目添加多个远程存储库,例如 upstream 存储库。

这个 upstream 存储库,通常是我们的远程存储库 fork 的上游存储库。我们可以用来同步上游的更新,并使用 pull request 的方式,提交自己的更新为项目做出贡献。

抓取远程存储库

抓取远程存储库
git fetch <remote-name>

通过 抓取 操作,我们可以从远程存储库中抓取最新的更新到本地,但暂时不将其合并到本地存储库中。

翻译错误

菜单中的官方中文翻译有误,子菜单最下方的抓取分块中出现了 抓取 获取 拉取 三种翻译。对比英文版,应该全部为 抓取

这个操作,经常和上面提到的分支对比操作配合使用,用来查看远程仓库(尤其是 upstream 仓库)的更新情况,并人工判断是否需要合并到本地存储库中。

拉取远程存储库分支

拉取远程存储库分支
git pull <remote-name> <branch>

上面我们介绍了如何进行分支的合并,如果想要合并的分支是远程存储库中的分支,那们可以使用 pull 来进行操作,相当于是 fetchmerge 的组合操作。

推送本地分支到远程存储库

推送本地分支到远程存储库
git push <remote-name> <branch>

在完成了本地分支和远程分支的合并之后,我们就可以使用 push 操作,将本地分支的更新推送到远程存储库中。

请求上游更新

请求上游更新
gh pr create

当我们参与 Github 上的开源项目时,通常需要通过 Pull Request 的方式向 upstream 仓库提交自己的更新。

通过 GitHub Pull Requests 插件,可以让我们在 Cursor 中直接管理 Github 上的 Pull Request。

FAQ

就个人经验而言, Git 是一个入门容易, 但难以精通的工具。

以下章节中,我会记录一些自己在使用过程中遇到的问题,一方面方便自己后续查阅,另一方面也希望能帮助被同样问题困扰的朋友。

配置用户名和邮箱

配置用户名和邮箱
git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"

在本地完成 Git 的安装之后,如果要推送到远程仓储库,还需要进行用户名和邮箱的配置,这样才能顺利完成提交。

让 Git 对文件名大小写变化敏感

Git 在 Mac 上默认是大小写不敏感的,如果想要让 Git 对文件名的大小写变化敏感,可以修改 Git 的配置。

git config --global core.ignorecase false

使用模板添加忽略文件列表

对于某些场景,我们不希望 Git 跟踪某些文件,例如 node_modules 目录,以及敏感文件,那么可以使用 gitignore-generator 插件,快速生成忽略文件。

想加入忽略文件配置中,但又想在文件列表中显示

使用 Explorer .gitignore Toggle 插件,可以在资源管理器中显示被 .gitignore 文件忽略(隐藏)的文件。

尤其是对于环境变量文件和数据库文件,这个插件非常有用,可以帮助我们快速定位到想要忽略的文件,并进行修改等后续操作。。

将已经追踪的文件,从追踪的列表中删除

已经追踪的文件,想要从追踪的列表中删除,可以使用 git rm --cached <file> 命令。

这样操作之后, 文件就不会继续被追踪, 也不会再出现在 更改 列表中。

而如果想从历史记录中删除文件存在的所有痕迹,那么则需要使用 git filter-branch 命令。

1
2
3
4
5
# 安装 git-filter-repo
pip install git-filter-repo

# 从历史记录中移除文件
git filter-repo --path-glob 'path/to/file' --invert-paths

注意,这些操作会改变提交历史,如果已经推送到远程存储库,还需要使用 git push --force 进行一次强制推送。

从历史记录中打补丁

从历史记录中打补丁
git cherry-pick <commit-hash>

从历史记录中打补丁,主要用于将其他分支的指定历史记录应用到当前分支,会生成仅包含指定历史记录的新历史记录。

这个操作常用于将上游分支的更新选择性的应用到当前分支,以保持当前分支的最小化更新。