我正在尝试使用代码格式化工具来格式化整个仓库。这样做时,我想保留有关谁提交了哪一行的信息,以便使git blame
之类的命令仍显示正确的信息。这样,我的意思是它应该向作者显示以前编辑过的每一行(在格式化之前)。
有一个git filter-branch命令,它允许您从时间开始对仓库的每个修订版运行命令。
git filter-branch --tree-filter '\
npx prettier --write "src/main/web/app/**/**.{js, jsx}" || \
echo "Error: no JS files found or invalid syntax"' \
-- --all
运行此操作将需要永远,而且我真的不在乎过去。我只想格式化master分支,而不更改每一行的所有权。我怎样才能做到这一点?我尝试在末尾使用rev-list
和其他过滤器类型,但仍然无法正常工作。在保留每一行的作者信息的同时,必须有一种格式化代码库的方法。
答案 0 :(得分:1)
您试图做的事是不可能的。您无法在某个时间点更改一行代码,但是git报告说对该行代码的最新更改是在该时间点之前发生的。
我想一个源代码控制工具可以支持“不重要的更改”的想法,在这里您将提交标记为修饰,然后历史分析将跳过该提交。我不确定该工具如何验证更改是否确实是表面上的,如果没有某种形式的工具强制实施,该功能肯定会被滥用,从而导致错误引入可能隐藏在“不重要”的提交中。但实际上,我认为这是个坏主意的原因在这里是学术上的-底线是,git没有这种功能。 (我也想不出有这样做的任何源代码控制工具。)
您可以更改格式。您可以保留过去更改的可见性。您可以避免编辑历史记录。但是您不能同时做这三个,所以您将不得不决定要牺牲哪个。
顺便说一下,实际上历史记录重写有两个缺点。您提到处理时间,所以让我们先来看一下:
您已经注意到,使用filter-branch
来完成此操作的简单方法将非常耗时。您可以做一些事情来加快它的速度(例如为其工作树提供一个ramdisk),但这是tree-filter
,涉及处理每个文件的每个版本。
如果进行了一些预处理,则效率可能会更高。例如,您可能能够预处理数据库中的每个BLOB
并创建一个映射(其中TREE
包含BLOB
X,将其替换为BLOB
Y),然后然后使用index-filter
执行替换。这将避免所有检出和添加操作,并且将避免重复重新格式化相同的代码文件。这样可以节省大量I / O。但这不是一件容易的事,而且仍然很耗时。
(可以基于相同的原理编写更专业的工具,但是AFAIK却没有人编写过。有先例,更专业的工具可以比filter-branch
更快...)
即使您得出的解决方案运行速度足够快,也请记住,历史记录重写会干扰您的所有引用。像任何历史记录重写一样,回购协议的所有用户都必须更新其克隆-对于这种全面的操作,我建议这样做的方法是,在开始重写之前先丢弃克隆,然后再重新克隆。
这也意味着,如果您有任何依赖于提交ID的内容,那也将被破坏。 (这可能包括构建基础结构或发布文档等;具体取决于您的项目的实践。)
因此,历史记录重写是一个非常严格的解决方案。另一方面,仅仅因为仅从第一天开始就没有完成格式化代码的假设,这似乎也太过激烈了。所以我的建议是:
在新的提交中重新格式化。如果您需要使用git blame
,并且将您指向重新格式化发生的提交,则在重新格式化提交的父级上再次运行git blame
。
是的,很烂。一阵子。但是,随着时间的流逝,一段特定的历史趋于变得不那么重要,因此从那里开始,您就可以让问题逐渐消解到过去。
答案 1 :(得分:1)
git blame -w -M
应该忽略空格和移动的代码更改,因此,您只需要重新格式化代码,并记住寻找归咎于谁就可以使用这些选项!
https://coderwall.com/p/x8xbnq/git-don-t-blame-people-for-changing-whitespaces-or-moving-code
答案 2 :(得分:0)
在保留每一行的作者信息的同时,必须有一种格式化代码库的方法。
您可以做的一件事是从一些较早的提交分支出来,重新格式化代码,然后将master
重新建立到您的分支上。
这就是这个主意,但是出于某些重要原因,您不应该这样做:
重新建立共享分支是一个坏主意。事实上,您甚至还关心保留更改的作者身份,这可能意味着有很多人在积极地编写代码。如果您去对master分支重新设置基础,那么回购的每个fork或克隆都将拥有一个具有旧历史的master分支,这势必会引起混乱和痛苦,除非您非常谨慎地管理流程并确定每个人都知道您在做什么,并适当更新其副本。更好的方法可能是不重新建立master的基础,而是将master的提交合并到您的分支中。然后,让所有人开始使用新分支而不是master
。
合并冲突。在重新格式化整个代码库时,您可能将对几乎每个文件中的大量行进行更改。合并后续提交时,无论是通过rebase
还是通过merge
进行合并,都可能会解决大量冲突。如果您采用我上面建议的方法,并将master的提交合并到新分支中而不是重新定基础,则可以有序地解决这些冲突,因为您可以一次合并几个提交,直到被抓住为止。
不完整的解决方案。。您将不得不弄清楚要在历史记录中插入重新格式化操作的位置。您走得越远,您保留的更改的著作权就越多,但是在后续更改中进行合并的工作也就越多。因此,您可能仍然会得到很多代码,其中重新格式化提交是最新的更改。
有限的利益。您实际上从未在git
中丢失丢失的作者信息-只是这些工具通常仅显示谁做了最新更改。但是,您仍然可以回头查看先前的提交,并探究任何一段代码的整个历史,包括编写者。因此,真正将重新格式化操作插入历史记录的唯一选择就是,您可以方便地看到谁更改了某些代码,而无需执行返回到较早提交的额外步骤。
这是不诚实的。。当您重写分支的历史记录时,您正在更改有关代码随时间变化的事实记录,这可能会造成真正的问题。让我们想象一下,重新格式化不会像您想的那样毫无意义,并且在进行重新格式化时实际上会产生一个错误。例如,假设您在多行字符串常量中引入了一些额外的空格。几周后,终于有人注意到了问题并开始寻找原因,并且看起来这种改变是在一年半之前做出的(因为这就是您将重新格式化输入到历史记录中的地方)。但是问题似乎很新-它并没有出现在两个月前发布的版本中,所以到底发生了什么?
收益会随着时间的流逝而减少。随着发展的不断发展,您试图掩盖的变化将被一些 other 覆盖。无论如何,您重新格式化的更改同样会被这些新更改所取代。随着时间和发展的进行,您为重新格式化格式所做的工作不会带来太大的意义。
如果您不希望自己的名字显示为项目中每一行的作者,但又不想忍受上述问题,那么您可能需要重新考虑您的做法。 更好的解决方案可能是作为一个团队来解决格式化问题:让团队中的每个人都同意在他们更改的任何文件上运行格式化程序,并在以后的所有代码审查中提出正确的格式化要求。随着时间的流逝,您的团队将覆盖大部分代码,并且作者身份信息将非常适合,因为无论如何重新格式化的每个文件都会被更改。您最终可能会得到少量因格式非常稳定且不需要更新而从未重新格式化的文件,并且您可以选择重新格式化(因为有些格式错误的文件使您发疯)或不重新格式化(因为无论如何,没有人真正在这些文件中工作。
答案 3 :(得分:0)
git filter-branch --tree-filter“找到<目录> -regex'。*。(cpp \ | h \ | c \ | <等>)'-exec <格式化程序命令> {} \;” --全部
< dir >
:相关目录,因为上述目录需要从根目录运行,但是您可能只想格式化根git目录下的某些子目录。
< etc >
:其他文件格式。
< formatter-command >
:您可以为单个文件运行的命令,它将格式化该文件。
--all
最后意味着对所有git分支(总共4个破折号)执行此操作
例如这就是我所拥有的,其中我的git包含src目录(除了测试,工具等)
git filter-branch --tree-filter“找到src -regex'。*。(cpp \ | h \ | cu \ | inl)'-exec clang-format -style = google -i {} \;” --全部
以上将重写每个git提交,但不更改git批注。由于此操作会修改git历史记录,因此一旦按下该按钮,每个人都将不得不重新克隆。
答案 4 :(得分:0)
Mercurial为此提供了(实验)选项“ --skip”:
--skip <REV[+]>
revision to not display (EXPERIMENTAL)
我认为默认git中还没有等效的功能,但是外部开发了hyper-blame command。
根据我的经验,两者都不能很好地处理格式更改,特别是当多行折叠成一行时。