前言
Visual Studio 中深度集成了 Git 功能,可以很方便地进行源代码版本控制。大部分日常操作可以通过界面完成,省去输入 Git 命令的时间,但掌握常用的 Git 命令行仍然很有必要。
介绍 Visual Studio 2022 中 Git 功能的集成使用。内容包括 Git 安装、代码克隆、分支管理(创建/切换/删除)、文件状态(暂存/提交/撤销)、远程同步(推送/拉取/提取)、冲突解决、Pull Requests 协作流程以及提交历史管理(变基/挑拣/标签)。通过图形界面与命令行结合,实现高效的版本控制。

Visual Studio 中深度集成了 Git 功能,可以很方便地进行源代码版本控制。大部分日常操作可以通过界面完成,省去输入 Git 命令的时间,但掌握常用的 Git 命令行仍然很有必要。
本文以 Visual Studio 2022 为例进行演示。
Visual Studio 的 UI 中已集成 Git 相关功能,但需要安装 Git 后才能使用。

如果没有安装 Git,在使用相关功能时可能会看到提示。

安装方式可通过以下两种:
推荐使用这种方式,免去了单独下载和安装的环节。


输入项目地址。

git clone https://github.com/zhaotianff/ImageViewer.git
Git 中的分支是一个轻量级的、可移动的指针,指向所做的提交。默认分支的名称是 master(GitHub 现已更新为 main)。每当做出任何 commit 时,它都会自动前进。
在 Git 中,可以创建任意子分支,从主存储库创建另一条开发线,开发新功能或修复错误。一旦功能或错误修复完成,可以将它合并回主分支,然后删除子分支(也可以保留)。
如下图所示:

Git 分支只是一个小引用,用于保存准确的提交历史记录;创建分支时不会创建源代码的多个副本。
在工具栏上点击当前分支,在弹出的面板中选择创建分支。在弹出的窗口中输入新分支的名称,并选择基于的分支,再点击创建,即可创建分支。

对应的 Git 命令行:
# 方法 1:使用 checkout(兼容所有 Git 版本)
git checkout -b 新分支名称 基础分支名称
# 方法 2:使用 switch(Git 2.23+ 推荐,更直观)
git switch -c 新分支名称 基础分支名称
创建本地分支后,需要将分支推送到服务器后,才能在服务器上看到新创建的分支。
选择 Git->管理分支菜单。

找到对应的分支,在右键菜单中选择推送。

推送成功后,可以看到提示信息。

此时打开 Git 服务器上的页面,可以看到刚推送的分支。

对应的 Git 命令:
git push origin 分支名称
在工具栏上选择当前分支,在弹出的面板中选择要切换的分支。
本地分支是指存在本地电脑上的分支。远程是指存储在 Git 服务器上的分支。

对应的 Git 命令:
git checkout 分支名称
当只想删除本地分支,而不删除远程服务器上的分支时,可以通过右键菜单里的删除菜单进行删除。

对应的 Git 命令:
git branch -d 分支名称
通过这种情况删除的分支,还可以通过下面的方式恢复回来。在 remotes/origin 文件夹下,选择服务器上本地已经删除的分支,右键选择签出,即可恢复分支。

对应的 Git 命令行:
git checkout -b 删除的分支 origin/删除的分支
当想彻底删除分支时,可以通过在 remotes/origin 文件夹下面的分支上打开右键菜单,选择删除菜单,即可永久删除分支。

注意,此操作不可逆,在删除前会有确认对话框。

对应的 Git 命令:
git push origin --delete 远程分支名称
当从 Git 服务器 clone 一个项目时,所有的文件都是处于未修改的状态 (Unmodified)。对文件进行了修改,修改的文件就变成了修改状态(Modified),可以在右下角看到修改的文件数量。

单击修改数量图标,可以看到已经修改的文件。

如果要把文件提交到 Git 服务器上,就需要先进行暂存 (Staging)。这几种状态的含义如下:
**未跟踪 (Untracked):**指的是工作区中存在,但未被 Git 纳入版本控制的文件。正常使用场景下,我们可以不考虑这种状态。
**未修改 (Unmodified):**当我们从服务器导入代码,或进行提交后的文件状态。
**已修改:**对文件进行了编辑,就变成了已修改状态。
**暂存 (Staging):**把文件放到暂存列表中,用于下次提交 (Commit)。

例如修改了 3 个文件,但是下次提交时,只想提交两个文件,就可以对这两个文件进行暂存。

对应的 Git 命令:
git add App.xaml
git add MainWindow.xaml
如果想把文件从暂存列表中移出,在文件上单击右键,在打开的菜单里选择取消暂存即可。

对应的 Git 命令:
git restore --staged 文件名
当把文件放到暂存列表后,就可以对文件进行提交 (Commit)。在提交前,需要编辑此次提交的日志。

对应的 Git 命令:
git commit -m "提交日志 xxxxx"
当进行提交后,本地的 Git 仓库已经有了这条提交记录,但是还未同步到服务器。可以在状态栏上看到具体的数量。

此时单击传出数量,在弹出的菜单中选择推送,即可将本地的提交记录推送到服务器。

也可以在提交时直接推送,一键完成提交并推送。

当对一个代码文件进行修改后,可以在修改列表中双击这个文件,与未修改的文件进行对比。

如果需要撤销某一行的修改,可以在对比页面的修改行上点撤销行按钮。

如果需要撤销整个文件的修改,在文件右键菜单中选择撤销更改即可。

对应的 Git 命令行:
git restore <file-name>
当提交未推送到服务器上时,可以修改提交日志信息。
在工具栏点击传入/传出按钮,然后选择查看所有提交。

双击本地提交。

然后在右侧的面板中编辑提交日志消息即可。
点击查看传入/传出按钮时,可以看到提取、拉取、推送和同步这几个菜单项。下面依次介绍这些功能的区别。

推送功能的作用是把本地的修改同步到服务器,包括修改文件、创建分支等操作。
如果创建了空的分支,可以在分支的菜单里直接将这个分支推送到远程存储库。如果在本地创建了分支,并在这个分支里修改了文件,那么在推送时,它会首先创建与本地存储库同名的分支,然后向其推送本地提交,将更改发布到远程存储库。
如果在推送操作期间,Visual Studio 发现远程提交和本地提交之间存在任何冲突,它将立即中断操作。必须先解决这些冲突,然后才能推送。
当在服务器进行了修改,或者同仓库的其它协同者更新了仓库代码,而本地又并未同步这些修改时,可以通过提取 (Fetch) 功能把这些修改导入到本地,但是不自动合并这些修改。
如果服务器上有修改,可以在这里看到修改列表。

拉取的功能和提取类似,但是它会自动合并修改。如果在合并过程中发现有冲突,系统会提示你。必须在处理这些冲突之后,才能进行下次推送。
接下来演示如何处理拉取过程中的文件冲突。
假设在本地增加一行代码。

然后项目的其它协作者也修改了这行并将他的修改推送到了服务器。

此时进行 Pull 或 Sync(先 Pull 再 Push) 的操作时,就会产生冲突。

在冲突的文件上双击,可以看到如下的界面。

在这里可以勾选采用服务器上的版本(左上),也可以勾选采用本地的版本(右上),也可以两边都要。在编辑完成后,选择接受合并按钮即可。

合并完成后,需要再提交一次。

由于冲突会导致一次多余的提交记录,所以在多人协同开发时,在每次修改前,都执行一下拉取或同步命令,保证本地的文件是最新的。
这个作用就是先拉取 (Pull) 再推送 (Push)。
Pull Requests 是一种协作过程,用于讨论分支中实施的更改,获取更改的早期反馈,并在每个人都批准后将其与主分支合并。即使还没有准备好合并更改,也可以创建工作输入模块的拉取请求。
说明:因为这个功能的翻译可能会存在差异,所以尽量直接称呼为 PR 或 Pull Requests。
假设有一个计算器的项目,项目框架搭建好以后,将加、减、乘、除四个功能分别创建了四个分支,并分给四个不同的人开发。当这些功能代码完成后,需要再合并到主分支,这个时候就需要使用到 PR 功能。
例如,作者 A 要合并他的'加'功能模块到主分支,他就可以创建一个 PR,当仓库 (Repo) 管理员接收到这个 PR 以后,他可以选择合并,也可以选择忽略。
为什么不直接在主分支上进行协同开发?主要是出于以下原因考虑:
1、保护主分支的稳定性 主分支会贯穿整个项目开发,所以需要保证它的代码的稳定性。分支则相当于'隔离的开发环境',开发者在分支中完成功能后,通过 PR 审核确认无误后再合并,能确保主分支始终可运行、可发布。
2、减少代码冲突 由于分支是在自己的'隔离的开发环境'下进行开发,所以不会存在冲突问题。最后在进行 PR 时,再统一处理冲突问题,且冲突范围可控(通常是与主分支的差异)。
3、支持代码评审 如果直接在主分支进行开发,每个人都可以直接提交代码,可能会导致低质量代码进入主分支。PR 的核心作用之一是触发代码评审:其他开发者可以在 PR 中检查代码质量、逻辑漏洞、风格规范等,提出修改建议。
4、支持并行开发 大家可以同时开发自己的功能模块,在完成后,再创建 PR 进行合并。
5、功能管理与回溯
每个分支可对应一个具体任务(如'用户登录功能''修复支付 bug'),分支名(如 feature/login fix/payment)能清晰标识用途,方便团队跟踪进度。若分支开发的功能被废弃或出现问题,可直接删除分支而不影响主分支;若合并后发现问题,也可通过分支历史快速定位并回滚。
1、在 Git 服务页面上创建
一般习惯使用这种方式创建,代码对比或比较清晰,方便。这里以 GitHub 为例。
首先找到仓库的 Pull Requests Tab 页。

然后新建一个 PR。

然后选择要合并的分支。

选择分支后,可以看到与对应基础的差异。确认无误后,点击 Create pull request 按钮就可以创建 PR。

在对应的分支上打开右键菜单,选择'创建拉取请求'。

在弹出的界面中填写相应信息后,单击 Create 按钮即可创建 PR。

注意:
项目的参与者一般在创建 PR 时,需要选择 Reviewers(审核者),类似下面的界面,根据实际情况选择即可。

当创建一个 PR 后,创建 PR 时选择的审核者会收到一个通知。他们可以提供反馈或通过这个 PR 并合并代码到主分支。
这里还是以 GitHub 为例。打开项目的 Pull Requests Tab 页,可以在列表里看当前未合并的 PR。
说明:因为这里是项目的管理者,而且没有其他协作者,所以无法展示系统通知的效果。但是操作步骤是一样的。

点开这个 PR,如果合并有冲突,需要先解决这个冲突,点击 Resolve conflicts 按钮,开始解决冲突。

跟 Visual Studio 中界面类似,这里也有接受当前、接受传入、接受全部三个选项,也可以自行编辑。

编辑完成后,选择右上角的 Mark as resolved 按钮,然后再选择 Commit merge 按钮。

此时再回到前面的合并页面,可以看到已经没有冲突了。点击 Merge pull request 按钮即可合并分支。

在前面的操作中,因为是管理员,所以有部分界面会不太一样。
在多人协同开发时,在进行合并前,审核者需要对代码进行评审(Review)。当鼠标划过代码行时,左侧会有一个加号按钮,点击这个加号,可以在对应的代码行写下相应的评论。

所有注释添加完后,点击'Finish review',选择审查结论并提交,整个审查流程就结束了。
Git 用于在将代码作为提交保存到本地存储库时管理更改历史,然后在 pull 请求获得批准后将这些更改与 main 分支合并。
当团队成员使用分支进行开发,它失去了提交历史的线性,使其难以遵循。这也使得当单个功能分支中存在多个提交时,很难跟踪最终的功能更改。在这种情况下,如果想在以后恢复 (revert) 一个功能,就会变得比较复杂。
为了解决这个问题,Git 提供了一个名为 rebase 的命令,可以解决所有这些问题。它接收在当前分支上进行的提交,并在另一个分支上还原它们。当前分支上的提交历史将被重写,以保留历史的粒度。
例如,将下图视为提交历史记录,从 old base 开始功能分支,并通过两次提交保存了更改。与此同时,团队的其他一些成员在 main 中提交了两项更改。因此,old base 现在将有两个分支:一个指向 main,另一个指向 feature。

现在,当将功能分支重新设置为主分支时,它将重写本地提交历史,将功能更改重放到主分支。然后在本地存储库上能看到线性提交历史记录以及其他人所做的更改。
按照上面示意图创建对应的分支:
1、创建一个 master 分支,master 分支 commit 一次
2、在 master 的基础上创建 branch_one
3、在 master 分支上进行两次提交
4、在 branch_one 上进行两次提交
现在来看一下提交记录情况。


操作步骤如下:
1、在 master 上右键菜单选择'将 branch_one 变基为 master'。

2、变基以后可以看到提交记录变成如下。

3、选择同步(先拉取后推送)功能,可以看到提交记录如下:

这个时候可以看到提交记录是线性的,branch_one 的提交在 master 的提交后面。
有时候会遇到在 rebase 时发生冲突的情况。

处理方法跟前面处理冲突方法一致,在修复冲突后,选择接受合并。解决完冲突以后,就可以继续执行后面的操作。
对应的 Git 命令行:
# 切换到你的开发分支
git checkout branch_one
# 基于 master 分支的最新提交,重新调整 branch_one 分支的提交
git rebase master
Cherry pick 是一个将提交从一个分支复制到另一个分支的过程。它只复制提交中的更改,而不是复制分支中的所有更改,它与合并和重基执行的功能完全不同。
当不小心提交到错误的分支或想在某个分支中提取一组提交到主/功能分支时,就可以使用 Cherry-pick 功能。在 Visual Studio 中,这个功能的翻译是'挑拣'。
假设有一个 master 分支和 branch_one 分支,想把 branch_one 分支中的某一次提交复制到 master 中。
操作步骤如下:
1、切换到 master 分支。

2、保持当前分支在 master,然后在 branch_one 上右键选择查看历史。

3、在需要复制的 commit 上,右键选择挑拣。

4、此时可以看到 master 分支已经有这条 commit 了,然后再推送即可。

在 GitHub 中创建 release 时,需要把 release 对应一个 Tag。

Tag 的作用是用来标识某些关键的提交。
如何创建 Tag:打开提交记录,在需要创建 Tag 的提交上,打开右键菜单,选择新建标记。

然后在弹出的窗口中填写 Tag 信息。

创建完成后,可以在提交历史上看到创建好的 Tag 信息。

对应的 Git 命令:其中 v1.0 是 tag 名称,a1b2c3d 为提交 id。
git tag -a v1.0 a1b2c3d -m "提交信息 xxxxx"
有时候本地修改的代码,或者已经提交的代码,想进行撤销,应该如何处理呢?这里可以分为多种情况。
1、将本地分支重置为以前的状态
这里的话,可以继续使用前面的方式。只是右键的位置换成了项目根节点,而不是单独的某一个文件。平常习惯使用这种方式,比较方便快捷。

也可以在分支上右键,选择重置->删除更改 (--hard)。

2、将分支重置到某一次提交
可以参考相关文档。
3、从远程分支还原更改
有时候已经将修改推送到服务器,例如进行了 3 次提交,但是现在不想要后面的 2 次提交了,这个时候就可以用到 revert 功能。

git revert 的作用是创建一个新的提交,抵消目标提交之后的所有变更,而不是直接删除历史。
所以在执行还原后,可以看到这样一条提交记录。

对应的 Git 命令:xxxx 为提交 id。
git revert xxxx

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML 转 Markdown 互为补充。 在线工具,Markdown 转 HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML 转 Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online