[英]How to perform git move in Visual Studio, instead of git delete and git add, when renaming, moving files in Solution Explorer?
语境
我经常在 Visual Studio 2022 中移动、重命名文件。重命名是一种标准的重构实践。 但是,当我在解决方案资源管理器中重命名文件时,不会执行git mv
操作,而是执行 git 删除和 git 添加。
这会导致丢失该特定文件/类的历史记录,这在许多情况下是一个巨大的损失。
问题
我可以离开 IDE 并使用命令行进行移动操作
git mv myoldfile.cs mynewfile.cs
这将完美地保留历史,但留下 IDE 是生产力杀手,尤其是在谈论重构和重命名多个类/文件时。
如何在 Visual Studio 中执行git mv
,而不是 git 删除和 git 添加,重命名时,在解决方案资源管理器中移动文件?
git
提交是给定时间点的整个仓库的快照。git
提交不是差异或变更集。git
提交不包含任何文件“重命名”信息。git
本身不会记录、监视、记录或以其他方式关注被移动或重命名的文件( ...在创建提交时)。以上可能是违反直觉的,甚至对某些人(包括我自己,当我第一次了解到这一点时)来说甚至令人震惊,因为它与之前所有主要的源代码控制系统(如 SVN、TFS、ZCC8D68C5501C4A9ADDZD531到 Helix)相反和其他人,因为所有这些系统都存储差异或变更集,这是他们模型的基础。
在内部, git
确实使用了各种 forms 的差异和增量压缩,但是这些都是故意对用户隐藏的,因为它们被认为是实现细节。 这是因为 git 的域 model完全建立在原子提交的概念之上,它代表了整个 repo 在特定时间点的快照 state。 此外,使用操作系统的低级文件更改检测功能来检测哪些特定文件已更改,而无需重新扫描整个工作目录:在 Linux/POSIX 上,它使用lstat
,在 Windows 上(其中lstat
不可用) 它使用fscache
。 当 git 计算您的存储库的哈希时,它使用默克尔树结构来避免必须不断地重新计算存储库中每个文件的 hash。
git
如何处理移动或重命名的文件呢?git
GUI 清楚地显示了文件重命名,而不是文件删除+添加或编辑! 虽然git
不存储有关文件重命名的信息,但它仍然能够启发式检测任何两个 git 提交之间的重命名文件,以及检测在未提交的 repo 的工作目录树和HEAD
提交之间重命名/移动的文件(又名“与未修改的比较”)。
例如:
Foo.txt
和Bar.txt
。Foo.txt
重命名为Qux.txt
(并且不进行其他更改)。git
“快照 1”和“快照 2”,那么diff
可以看到Foo.txt
已重命名为Qux.txt
(并且Bar.txt
未更改),因为内容(以及因此文件的加密哈希)是相同,因此它推断文件从Foo.txt
重命名为Qux.txt
。
git
执行相同的差异,但使用“快照 2”作为基本提交并使用“快照 1”作为后续提交,那么 git 将显示它检测到从Qux.txt
重命名回Foo.txt
。但是,如果您不只是在两次提交之间重命名或移动文件,例如同时编辑文件,那么 git可能会或可能不会将文件视为新的单独文件而不是重命名的文件。
git
可以处理常见的文件系统级重构操作(如拆分文件),比以文件为中心的源代码控制(如 TFS 和 SVN)要好得多,而且你也不会看到与重构相关的错误重命名。class
定义的MultipleClasses.cs
文件拆分为单独的.cs
文件,每个文件一个class
。 在这种情况下,不会执行真正的“重命名”,并且git
的差异会显示在添加新SingleClass1.cs
、 SingleClass2.cs
等文件的同时删除了 1 个文件( MultipleClassesw.cs
)。
SingleClass1.cs
为MultipleClasses.cs
/ TFS。 但是,正如您可以想象的那样,有时git
的启发式方法不起作用,您需要使用--follow
和/或--find-renames=<percentage>
(又名-M<percentage>
)来刺激它。
我个人的首选做法是将基于文件系统和编辑代码文件的更改保留在单独的 git 提交中(因此提交仅包含已编辑的文件,或仅添加+删除的文件,或仅拆分更改),这样您就可以git 的--follow
启发式检测重命名/移动要容易得多。
考虑这种情况:
Project/Foobar.cs
的文件,其中包含class Foobar
。 该文件只有大约 1KB 大小。class Foobar
重命名为class Barfoo
。
class Foobar
重命名为class Barfoo
并编辑项目中其他地方出现的所有Foobar
,而且还将Foobar.cs
重命名为Barfoo.cs
。Foobar
仅在 1KB 大小的Foobar.cs
文件中出现两次(首先在class Foobar
中,然后再次在构造函数定义中Foobar() {}
)因此仅更改了 12 个字节(2 * 6 个字符) . 在一个 1KB 的文件中,这是 1% 的变化( 12 / 1024 == 0.0117 --> 1.17%
)。git
(和 Visual Studio 的内置git
GUI)只看到最后一次提交Foobar.cs
,并看到当前的 HEAD (带有未提交的更改)有Barfoo.cs
与Foobar.cs
有 1% 的不同,所以它认为重命名/移动而不是删除+添加或编辑,因此 Visual Studio 的解决方案资源管理器将使用该文件旁边的“移动/重命名”git 状态图标,而不是“已编辑文件”或“新文件”状态图标。Barfoo.cs
进行了超过默认更改百分比阈值 50%的更实质性更改(尚未提交),则解决方案资源管理器将开始显示“新文件”图标而不是“重命名/移动文件”图标。
Barfoo.cs
的一些更改(同样:尚未保存任何提交),使其低于 50% 更改阈值,那么 VS 的解决方案资源管理器将再次显示重命名图标。 git
没有在提交中存储实际文件重命名/移动的一个巧妙之处在于,这意味着您可以安全地将git
与任何软件一起使用,包括任何重命名/移动文件的软件! 尤其是不支持源代码控制的软件。
因此,Visual Studio(有或没有git
支持)无需通知git
文件已重命名/移动。
事实上,git 提交不是增量,而是快照,这意味着您可以更轻松地重新排序提交,并以最小的痛苦重新调整整个分支。 这在 SVN 或 TFS 中根本不可能实现。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.