git忽略已经被提交的文件

项目中遇到这样的情况.

项目初始化时需要提交一些必不可少的代码文件,但是这些代码文件在每个人的机器上又会重新编译一次,并且结果不一样。

这样的话,如果每个人都提交这些文件,实际上是完全没有必要的。 


具体到我们项目中,使用 egret 引擎 typescript 语言开发。引擎的 ts 代码会被 "编译" 成 js ,存储在项目的 libs 目录里。

没有这些 js 游戏是跑不了的,但是每个人编译时,由于编译选项不同,每个人的电脑编译出来的 js 里 都会有一些差异。

如果每次都上传这些 js 文件,是完全没必要的。

但是如果每次提交时,都根据目录 避开这些文件的提交, 这种方法又显得很蠢。


这时候需要 .gitignore 了。但是添加了 .gitignore 忽略这些路径后, 由于这个路径是已经增加到过仓库管理中,

所以尽管已经在 ignore 列表里,依然 会被 git trace 到每个文件的变化。


所以,这个时候需要 git rm --cache 来解决这样的问题.

在此转发一篇 知乎 的问题 , 基本描述清楚了 这种问题的发生情况 和 解决办法.

另外,知乎帖子里提到了一个 "写一个脚本文件来覆盖xx文件" 的办法, 感觉有点像是 svn 的 patch 功能。 

不知道 git 里面 有没有类似 svn patch 的 功能。


git忽略已经被提交的文件

Yole_Yu  164 2014年03月11日 提问 ·  2014年03月11日 更新
  • 关注 9 关注
  • 收藏 37 收藏,10.9k 浏览
问题对人有帮助,内容完整,我也想知道答案 0 问题没有实际价值,缺少关键内容,没有改进余地

现在项目的根目录放了 .gitignore 文件,并且git远程仓库的项目根目录已经有了 logs文件夹。

由于每次本地运行项目,都会生成新的log文件,但是我并不想提交logs文件夹里面的内容,所以要在.gitignore写logs的规则。

我尝试过添加以下规则
logs/*.log
logs/
/logs/

但是运行git status的时候,始终能看到modified:logs/xx.log 。

请问是我的规则编写错误,还是我某个地方有理解错误?

  • git
  • 链接 
  • 评论 
默认排序 时间排序

4 个回答

答案对人有帮助,有参考价值 13 答案没帮助,是错误的答案,答非所问
采纳
nightire 13.7k  2014年04月15日 回答 · 2014年09月03日 更新

tl;dr: 正确的做法应该是:git rm --cached logs/xx.log,然后更新 .gitignore 忽略掉目标文件,最后 git commit -m "We really don't want Git to track this anymore!"

具体的原因如下:

被采纳的答案虽然能达到(暂时的)目的,但并非最正确的做法,这样做是误解了 git update-index 的含义,而且这样做带来的最直接(不良)后果是这样的:

  1. 所有的团队成员都必须对目标文件执行:git update-index --assume-unchanged 。这是因为即使你让 Git 假装看不见目标文件的改变,但文件本身还是在 Git 的历史记录里的,所以团队的每个人在 fetch 的时候都会拉到目标文件的变更。(但实际上目标文件是根本不想被 Git 记录的,而不是假装看不见它发生了改变)

  2. 一旦有人改变目标文件之后没有 git update-index --assume-unchanged  就直接 push 了,那么接下来所有拉取了最新代码的成员必须重新执行 update-index,否则 Git 又会开始记录目标文件的变化。这一点实际上很常见的,比如说某成员换了机器或者硬盘,重新 clone 了一份代码库,由于目标文件还在 Git 的历史记录里,所以他/她很可能会忘记 update-index

为什么会这样?答案就在 Git 的 man pages 里:

首先,git update-index 的定义是:

Register file contents in the working tree to the index(把工作区下的文件内容注册到索引区)

这句话暗含的意思是:update-index 针对的是 Git 数据库里被记录的文件,而不是那些需要忽略的文件。

接着看关于 --assume-unchanged 的几句相关的描述:

When the "assume unchanged" bit is on, Git stops checking the working tree files for possible modifications, so you need to manually unset the bit to tell Git when you change the working tree file. This is sometimes helpful when working with a big project on a filesystem that has very slow lstat(2) system call (e.g. cifs).

大致意思是:

应用了该标识之后,Git 停止查看工作区文件可能发生的改变,所以你必须 手动 重置该标识以便 Git 知道你想要恢复对文件改变的追踪。当你工作在一个大型项目中,这在文件系统的 lstat 系统调用非常迟钝的时候会很有用。

我们知道 Git 不仅仅是用来做代码版本管理的,很多其他领域的项目也会使用 Git。比如说我公司曾经一个客户的项目涉及到精密零件图纸文档的版本管理,他们也用 Git。有一种使用场景是对一些体积庞大的文件进行修改,但是每一次保存 Git 都要计算文件的变化并更新工作区,这在硬盘慢的时候延迟卡顿非常明显。

git update-index --assume-unchanged 的真正用法是这样的:

  1. 你正在修改一个巨大的文件,你先对其 git update-index --assume-unchanged,这样 Git 暂时不会理睬你对文件做的修改;
  2. 当你的工作告一段落决定可以提交的时候,重置改标识:git update-index --no-assume-unchanged,于是 Git 只需要做一次更新,这是完全可以接受的了;
  3. 提交+推送。

另外,根据文档的进一步描述:

This option can be also used as a coarse file-level mechanism to ignore uncommitted changes in tracked files (akin to what .gitignore does for untracked files).

这段描述告诉我们两个事实:

  1. 虽然可以用其来达成楼主想要的结果,但这是不讲究的做法(coarse);
  2. 同样的事情更应该用 .gitignore 文件来实现(针对未追踪的文件)。

随之而来的问题是:为什么我增加了 .gitignore 里的规则却没有效果?

这是因为我们误解了 .gitignore 文件的用途,该文件只能作用于 Untracked Files,也就是那些从来没有被 Git 记录过的文件(自添加以后,从未 add 及 commit 过的文件)。

之所以你的规则不生效,是因为那些 .log 文件曾经被 Git 记录过,因此 .gitignore 对它们完全无效。这也正是开头那段简短答案所做的事情:

  1. 从 Git 的数据库中删除对于该文件的追踪;
  2. 把对应的规则写入 .gitignore,让忽略真正生效;
  3. 提交+推送。

只有这样做,所有的团队成员才会保持一致而不会有后遗症,也只有这样做,其他的团队成员根本不需要做额外的工作来维持对一个文件的改变忽略。

最后有一点需要注意的,git rm --cached 删除的是追踪状态,而不是物理文件;如果你真的是彻底不想要了,你也可以直接 rm+忽略+提交。

  • 链接 
  • 8 评论 
  •  

    感谢对update-index 用法的纠正,不过在我这个项目中,由于logs/xx.log必须要有一个初始化的文件,否则写log的时候会出错(可以算是logger的一个bug)。所以不能git rm logs/xx.log

    #1 Yole_Yu · 2014年04月15日 · 回复

  •  
    回复 Yole_Yu:

    这个问题其实也好解决,很多项目都有这种必须要有的初始化文件,但是内容可能在每个开发端都不同。针对这一问题有很多种办法,比如说放一个占位文件 logs/xx.example.log,然后告诉团队成员第一次 _clone_ 回来改一下文件名。再比如说不放任何占位文件,而是在项目根路径下写一个初始化脚本,第一次 _clone_ 回来之后运行一下初始化脚本在你的本地生成一些必须要有的文件(但是不如版本控制系统)。比较推荐方法二,因为你可以用初始化脚本做一系列事情,而不只是个别文件,这也是开发构建自动化常用的一种措施。

    #2 nightire · 2014年04月15日 · 回复

  • 1

    有时不想删除本地的文件, 只是想让git不再track, 这时可以使用 git rm --cached logs/xx.log

    #3 xhh · 2014年04月15日 · 回复

  •  

    漂亮!

    #4 Arch · 2014年11月18日 · 回复

  •  

    那么问题来了,因为工作需要我试验了一下,我把clone下来的项目修改了本地的配置文件,发现git status提醒我一大堆本地修改的文件需要git add添加,之前的处理方式是如果不是代码部分就不执行git add,这样git commit时也不会提交不想提交的配置文件,但是按照你的方法执行后,会在git status时把刚才已经git rm --cached过后的文件和git add添加的文件会列在同一个状态下边,并且提醒我要撤销的话可用git reset HEAD ~~~,我猜测如果这时git commit的话会把我刚才delete过的状态和git add的状态都提交到服务器上,这肯定是不符合预期的,期待你的回答。谢谢!

    #5 Arch · 2014年11月19日 · 回复

  •  
    回复 Arch:

    你的描述有一些细节我看不明白:为什么你修改了本地配置文件之后,git status 会提醒你“一大堆”文件需要添加?是因为你的本地配置文件太多还是?

    我所描述的方法只是一种应急措施,并不是推荐使用的常规手段。不想要添加到 Git 中的文件始终应该放进 .gitignore 里面——这才是正道。

    如果,仅仅如果是因为忘记放进 .gitignore 而导致的问题,可以用 git rm 的方式来解决,解决了之后还是不要忘记再添加 .gitignore 规则,这样才能保证以后不出一样的问题,而且也不会总是有一些文件需要你格外留意别去 git add(这是病态,不是常态)。

    当然,配置文件总是 Git 使用的一个障碍,但也不是无法可施,我最近刚回答过关于配置文件的处理策略,你可以参考一下把思路理清楚,Git 的使用不应该是如此复杂的。

    http://segmentfault.com/q/1010000000778120/a-1020000000778556

    #6 nightire · 2014年11月19日 · 回复

  •  
    回复 nightire:

    每次看到很长的回复,一看都是师傅你

    #7 JasmineOT · 2014年12月23日 · 回复

  •  

    赞,太详细了,感谢,正在按照楼主说的使用,感觉不错,关于那个初始化脚本的想法虽然第一次听说,但是就像一个窗户纸被捅破了,很痛快

    #8 海诺 · 4月6日 · 回复


你可能感兴趣的:(折腾,工作日志)