Git - 暂时忽略文件的微小更改

76
我正在寻找一种方法来“隐藏”对Git中几个文件进行的轻微更改,使它们在git status中不会显示,直到对这些文件进行其他更改为止。
例如:我有一个Java文件,其中唯一的更改是删除了一个未使用的import(贡献者忘记在提交之前运行organize imports)。 现在我已经删除了该import并且更改(显然)在git中显示。 由于我没有其他要对该文件进行的更改,因此我不太喜欢将该文件作为另一个(无关)更改的一部分进行提交,也不喜欢单独提交此更改。 当然,我可以恢复更改,并仅在必须更改该文件时应用它,但我可能会“冒险”忘记它。
是否存在这样的命令? 它的工作方式类似于assume-unchanged命令,但不是永久性的。
如果没有这样的命令,正确的解决方法是什么?
提前感谢。

1
为什么不为此创建一个“微小修复”分支呢? - Mat
7
为什么不提交只有微不足道的改变?这不是svn了,提交吧! - mgarciaisaia
@desert69,最终因为这会使得在日志中找到重要的更改变得更加困难。 - Zelgadis
1
一开始似乎并不是非常必要。在我的项目中,将提交保持最小化,并将此类表面更改与重大的功能更改分开被认为是一种良好的实践。 - Amir E. Aharoni
1
@Zelgadis: git log | grep,甚至是 git log -S"<code...>"。虽然有时候会出现一些你不想提交的更改,比如为了测试目的而进行的临时编辑,但这个问题仍然很有用。 - naught101
显示剩余3条评论
6个回答

118

在我的使用场景中(在我的个人计算机上使用编辑后的配置文件,然后在另一台计算机上运行未更改的配置),这对我而言是解决方案:

开始忽略对文件的更改:

git update-index --assume-unchanged path/to/file

继续跟踪:

git update-index --no-assume-unchanged path/to/file

3
谢谢您提供复制粘贴的示例。由于很少使用,我经常忘记其确切语法 :p - Rob W
1
这是最佳解决方案。最初发布于2009年2月18日,来源:http://gitready.com/intermediate/2009/02/18/temporarily-ignoring-files.html - Kalpesh Panchal
11
当你使用--assume-unchanged命令时,如果忘记提交对文件的修改,可能会导致问题。一个更好的解决方案是自动重置修改时间(mtime)或类似的东西,这样可以避免这种情况的发生。 - naught101
1
https://dev59.com/inE95IYBdhLWcg3wUMPW 上有一个很好的答案,详细说明了如何获取文件列表,以及如何从所有文件中删除标志。 - Ryota
1
感谢 @Ryota。列出被忽略的文件 tldr:git ls-files -v | grep "^[a-z]" - ford04
显示剩余5条评论

18
使用 git update-index --assume-unchanged 的缺点在于(正如Git手册中所述),你承诺不会更改文件。
当“假设未更改”位被打开时,用户保证不更改文件,并允许Git认为工作目录文件与索引记录的内容相匹配。
如果违反此承诺并更改文件,则可能会产生不良后果。例如,在某些实现中,执行git stash将丢弃您对文件所做的更改(如果您已经承诺不会更改它,为什么不应该这样做呢?)。
相反,使用--skip-worktree选项告诉Git假装工作版本与索引版本是最新的。
读取条目时,如果标记为skip-worktree,则Git会假装其工作目录版本是最新的,并读取索引版本。
这似乎是忽略更改的更可靠方法,而不会有某些Git命令可能会丢失您的更改的风险:
git update-index --skip-worktree path/to/file

重新跟踪文件的方法:

git update-index --no-skip-worktree path/to/file

列出您已设置了 skip-worktree 的文件:

git ls-files -v | grep ^S

这是最符合我的使用情况的;我想忽略文件的本地更改,但仍然在上游文件被修改时接收更新。来自 https://compiledsuccessfully.dev/git-skip-worktree/ : "如果上游文件发生更改,该文件将在拉取时更新。" - Dawngerpony

5

将未准备好的更改保留在单独的分支中。必要时使用git rebase将该分支置于主历史记录的新更改之上。

无论是正在进行的开发、临时事物,甚至永远不会包含在主项目历史记录中的内容——同样的过程同样适用。

如果/当分支的更改准备好并需要包含在主历史记录中时,请将其合并。如果没有准备好,请将它们保留在单独的分支中并继续进行rebase操作。

(副注:git merge --no-ff可能有用,即使可以进行快进式合并也可以创建一个合并提交——根据您的项目规则,这可能更可取)


1
这是我发现对我来说最好的解决方案。谢谢! - Zelgadis
如果您有两组不同的更改,这将变得很烦人。 - naught101

5

不要添加琐碎的改动。

在提交之前,仔细审核您添加的内容是一种好习惯。

您甚至可以忽略文件中的某些更改,同时添加其他更改,使用

git add -p.

1

我在这里添加了所有答案,以添加一些代码,使我的生活变得更加舒适。期望使用BSD coreutils(例如在MacOS上)。

git-overlook

#!/usr/bin/env bash

path="$1"
root="$(git rev-parse --show-toplevel)"

if [ -z "$path" ]; then
  echo "Specify a path" 1>&2
  exit 1;
fi;

if [ ! -d .git/overlook ]; then
    mkdir $root/.git/overlook
fi;
record="$root/.git/overlook/$(echo $path | sed 's/\./___/g' | sed 's/\//____/g')"
touch "$record"
git update-index --skip-worktree "$path"

git-relook

#!/usr/bin/env bash

path="$1"
root="$(git rev-parse --show-toplevel)"

record="$root/.git/overlook/$(echo $path | sed 's/\./___/g' | sed 's/\//____/g')"
if [ -f "$record" ]; then
  git update-index --no-skip-worktree "$path"
  rm "$record"
fi;

最后,一个.git/hooks/pre-commit
#!/bin/bash

set -eu

root="$(git rev-parse --show-toplevel)"

get_record_name() {
  record="$root/.git/overlook/$(echo $1 | sed 's/\./___/g' | sed 's/\//____/g')"
  echo "$record"
}

for file in $(git ls-files -v | grep ^S | sed 's/S //'); do
  record="$(get_record_name $file)"
  record_mtime="$(stat -f %m $record)"
  file_mtime="$(stat -f %m $file)"

  if [ "$record_mtime" -lt "$file_mtime" ]; then
    echo "File $file is overlooked but has been modified since. Please run either git overlook $file or git relook $file" 1>&2
    exit 1
  else
    echo "File $file is overlooked but hasn't been modified"
  fi;

done;

这样做的好处是我可以运行git overlook path/to/file,然后继续进行提交,直到我改变了被忽略的文件,此时我必须再次忽略它或使用git relook path/to/file将其还原。显然,如果被忽略的文件是我所做的唯一更改,那么我在git status中就看不到它,这对我没有帮助。
它在.git/overlook中创建一些文件来跟踪信息,但据我所知,git将与它们和平共处。

我无法尝试它,因为我在使用Windows系统,但这正是我一直在寻找的! - Zelgadis

0

有多种方法可以实现(虽然可能不太干净整洁,需要您的注意)

  1. 将相关文件添加到您的存储库中的.gitignore中,以便它不会显示在提交中。请注意,在准备提交文件时,要从.gitignore中删除此文件
  2. 确保在提交其余更改时不要“暂存”该文件。您可能需要在git上编写一个包装器,以确保像git commit -agit add .这样的命令在除了问题文件之外的所有文件上运行。另一种选择是使用git guigit citool,在其中您可以直观地确保您的文件不在“暂存”区域中,因此永远不会被提交
  3. 另一种方法是提交所有可提交的更改,然后git stash save您唯一的工作文件。稍后,当您准备更改文件时,可以git stash pop并继续工作和提交。

希望这有所帮助 :)


1
很遗憾,第一种解决方案不起作用,因为这些文件已经被跟踪了。目前我正在使用第二种和第三种的混合方法,但我希望有更自动化的解决方案。无论如何,感谢您的回答! - Zelgadis
不幸的是,git 还不能读取开发者的思想来判断一个更改是否重要(需要提交)或不重要(保持不变)。最清晰的选项是:(a)为“琐碎修复”创建单独的分支,定期进行变基并在某些时间合并;(b)在提交信息中将更改标记为“琐碎”。在我看来,(b)更可取,因为它避免了没有人测试过的偏差和版本。 - vonbrand

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接