magit 实现assume-unchanged功能

有时候我们会把一些构建生成的文件放在版本库中,而这些文件一般比较大,如果放在magit status里面会导致magit卡顿,而多个人协同开发的时候,如果都上传这些文件会导致冲突。

一般对于这样的文件,我们本地希望它被assume unchanged(或者叫做skip worktree)

git提供了两个命令来处理这个需求,一个是 git update-index --skip-worktree -- file 这个功能magit已经添加了对应的实现,但是有一些瑕疵,在子目录的时候只能操作当前目录的文件。

还有一个命令是 git update-index --assume-unchanged -- file

之前一直使用的是 git alias来处理这种情况:

	hide = update-index --assume-unchanged
	unhide = update-index --no-assume-unchanged
	unhide-all = "!git ls-files -v | grep \"^[a-z]\" | awk '{print $2,$NF;}' | xargs git unhide"
	hidden = !git ls-files -v | grep \"^[a-z]\"

但是作为一个Emacs党,肯定希望少敲几个字符,一切跟版本控制相关的操作都可以用Emacs来完成。

于是,我写了下面几个函数来处理这种情况,github上面也有人遇到了同样的问题,这里分享一下。

(defun magit-skip-assume-unchanged-files ()
  (--keep (and (and (= (aref it 0) ?h)
                    (substring it 2)))
          (magit-git-items "ls-files"
                           (string-trim
                            (car (magit-git-items "rev-parse" "--show-toplevel")))
                           "-v" "--full-name" "-z")))

(defun magit-insert-assume-unchanged-files ()
  "Insert a tree of assume unchanged files.

    If the first element of `magit-buffer-diff-files' is a
    directory, then limit the list to files below that.  The value
    of that variable can be set using \"D -- DIRECTORY RET g\"."
  (when-let ((files (magit-skip-assume-unchanged-files)))
    (let* ((base (car magit-buffer-diff-files))
           (base (and base (file-directory-p base) base)))
      (magit-insert-section (assume-unchanged nil t)
        (magit-insert-heading "Assume-unchanged files:")
        (magit-insert-files files base)
        (insert ?\n)))))

(defun magit-list-all-files ()
    (magit-with-toplevel
      (with-temp-buffer
        (apply #'magit-git-insert '("ls-files" "-z" "--full-name"))
        (split-string (buffer-string) "\0" t))))

(defun magit-assume-unchanged (file)
  "Call \"git update-index --assume-unchanged FILE\"."
  (interactive (list (magit-read-file-choice "Assume unchanged for: "
                                             (cl-set-difference
                                              (magit-list-all-files)
                                              (magit-skip-assume-unchanged-files)))))
  (magit-with-toplevel
    (magit-run-git "update-index" "--assume-unchanged" "--" file)))

(defun magit-no-assume-unchanged (file)
  "Call \"git update-index --no-assume-unchanged FILE\"."
  (interactive (list (magit-read-file-choice "Do not assume unchanged for: "
                                             (magit-skip-assume-unchanged-files))))
  (magit-with-toplevel
    (magit-run-git "update-index" "--no-assume-unchanged" "--" file)))

Add assume-unchanged section to magit-status buffer:

 (magit-add-section-hook 'magit-status-sections-hook
                         'magit-insert-assume-unchanged-files nil t)

   ;; define jump 
 (magit-define-section-jumper magit-jump-to-assume-unchanged "Assume-unchanged files" assume-unchanged)
 (define-key magit-status-mode-map "ga" 'magit-jump-to-assume-unchanged)
6 个赞

可能我 out 了吧,一般我都是采用ignore。这样效果一样么?性能受影响吗?

ignore是把一个文件完全排除在版本控制之外,如果我有一个配置文件在版本库里面。我本地会去修改它,但是我并不希望这个修改被不小心提交了。就可以assume unchanged,这两个是完全不同的东西哦。

性能一般没啥问题,加了我这个东西,对于大的 .min.js 文件,性能反而会更好。因为你在magit-status buffer里面查看这个单行大文件的时候,Emacs巨卡

3 个赞

嗯,明白这个场景了。Thanks!

C-u i 不能解决吗?

1 个赞

你说的是ignore么?不一样的哈,建议了解下这个git命令

Screenshot_20190722_133625

magit-status buffer, 对某个文件 C-u i 或者 I 有这个选项。感觉 p比较符合你的需求。等于 ignore locally, 不过反向可能比较麻烦,需要自己手动删。

这篇博文提到过:

http://mbork.pl/2018-10-14_Magit_and_C-u_i

2 个赞

学习了, .git/info/exclude 这个功能确实没用过。

前几天在ruby-china也看到了类似的内容 https://ruby-china.org/topics/38807 总结了帖子里面提到过的四种方式。

我自己也用这个lol

这个有种解决办法是,在repo里面有一个config.example.json,然后要求大家cp config.example.json config.json之后才能编译运行