3

我在 OS X 上使用 Emacs (23.3.1)。我从终端发出 git 命令,而不使用 Emacs 的任何 VC 功能。我将 Emacs 设置为在文件被修改时刷新,这是由我的 .emacs 文件的这些行启用的:

(custom-set-variables
 ...
 '(auto-revert-interval 1)
 ... 
)
(global-auto-revert-mode 1)

这一直按我的预期工作;如果我拉动,我的 Emacs 缓冲区会随着合并或冲突而更新,就好像我已经退出缓冲区并重新加载每个缓冲区一样。

最近我开始变基(调用 git-fetch 然后调用 git-rebase)而不是简单地拉动(使用 git-pull),我不知道问题是否真的与 git-rebase 有关,但我当然没有在那之前注意问题,现在就做。

所以这就是问题所在:我更新一个文件,使用 提交更改git commit -am "...",运行git fetch origin,然后运行git rebase origin/master(有时以交互模式将提交压缩在一起)。现在我已经提取了远程更新,我再去编辑文件,发现它看起来好像我已经丢失了我最近的所有更改。第一次发生时,我非常生气并诅咒变基算法以某种方式丢失了我的更改,直到我意识到文件完全完好无损,只是 Emacs 以某种方式恢复到我更改之前的文件版本'刚刚承诺。我总是可以通过关闭缓冲区并再次加载文件来解决问题,但每次这样做都很痛苦。

有谁知道问题是什么?

4

5 回答 5

3

我是 的原作者autorevert,但是,我必须深入挖掘我的记忆,因为我已经有一段时间没有使用它了。

我相信这归结为 Emacs 核心功能的问题verify-visited-file-modtime,它自动恢复用于检查是否需要刷新缓冲区。

这可能是由多种原因造成的:

  • 这可能是一种竞争条件,Emacs 在文件完全写入之前读取文件,但将缓冲区 modtime 设置为结束时间。(发生这种情况时,Emacs 已在中途自动恢复缓冲区,但新的自动恢复不会触发恢复文件的最终版本。)

  • 如果文件的两个变体具有相同的时间戳,那么 Emacs 将看不到差异。

我不认为你可以在 lisp 方面做些什么来让它正常工作(不解决暴力解决方案)。Emacs 确实需要原子地获取已打开并读入缓冲区的文件的指纹,而这只能从核心(这是我的领域之外的东西)完成。此外,某些文件系统的时间戳分辨率不高于一秒,因此很难使用时间戳来检测文件是否已更改。

于 2011-06-29T10:29:11.637 回答
2

如前所述,这是一种竞争条件。“git pull --rebase”可能会在重放您的提交时多次修改同一个文件。如果出现以下情况,则游戏结束:

  1. git更改文件“foo”
  2. 自动恢复启动并重新加载“foo”
  3. git 在同一秒内再次更改文件“foo”

由于 unix 文件时间具有第二个分辨率,因此 emacs 无法判断发生了第二个更改。我最近注意到我以这种方式丢失了许多更改,因此我决定尝试解决 emacs 中的问题。我已经修补了emacs代码,如下所示。如果修改文件的时间与当前系统时间相同,我将自动恢复推迟到下一个时间间隔:

;;
;; Fix the auto-revert-handler so that if the system time is the
;; same as the new modified time for a file, skip it on this
;; iteration. This should fix race conditions when a file is changed
;; multiple times within the same second.
;; 

(defun file-change-too-close-for-comfort ()
  (let* ((file-time-raw (nth 5 (file-attributes (buffer-file-name))))
         (file-time (+ (lsh (nth 0 file-time-raw) 16) (nth 1 file-time-raw)))
         (current-time (+ (lsh (nth 0 (current-time)) 16) (nth 1 (current-time)))))
    (and (eq current-time file-time)
         (message "%s: postpone revert" (buffer-name))
         t)))


(defun auto-revert-handler ()
  "Revert current buffer, if appropriate.
This is an internal function used by Auto-Revert Mode."
  (when (or auto-revert-tail-mode (not (buffer-modified-p)))
    (let* ((buffer (current-buffer)) size
           (revert
            (or (and buffer-file-name
                     (file-readable-p buffer-file-name)
                     (if auto-revert-tail-mode
                         ;; Tramp caches the file attributes.  Setting
                         ;; `tramp-cache-inhibit' forces Tramp to
                         ;; reread the values.
                         (let ((tramp-cache-inhibit-cache t))
                           (/= auto-revert-tail-pos
                               (setq size
                                     (nth 7 (file-attributes
                                             buffer-file-name)))))
                       (and (not (file-remote-p buffer-file-name))
                            (not (verify-visited-file-modtime buffer))
                            (not (file-change-too-close-for-comfort)))))
                (and (or auto-revert-mode
                         global-auto-revert-non-file-buffers)
                     revert-buffer-function
                     (boundp 'buffer-stale-function)
                     (functionp buffer-stale-function)
                     (funcall buffer-stale-function t))))
           eob eoblist)
      (when revert
        (when (and auto-revert-verbose
                   (not (eq revert 'fast)))
          (message "Reverting buffer `%s'." (buffer-name)))
        ;; If point (or a window point) is at the end of the buffer,
        ;; we want to keep it at the end after reverting.  This allows
        ;; to tail a file.
        (when buffer-file-name
          (setq eob (eobp))
          (walk-windows
           #'(lambda (window)
               (and (eq (window-buffer window) buffer)
                    (= (window-point window) (point-max))
                    (push window eoblist)))
           'no-mini t))
        (if auto-revert-tail-mode
            (auto-revert-tail-handler size)
          ;; Bind buffer-read-only in case user has done C-x C-q,
          ;; so as not to forget that.  This gives undesirable results
          ;; when the file's mode changes, but that is less common.
          (let ((buffer-read-only buffer-read-only))
            (revert-buffer 'ignore-auto 'dont-ask 'preserve-modes)))
        (when buffer-file-name
          (when eob (goto-char (point-max)))
          (dolist (window eoblist)
            (set-window-point window (point-max)))))
      ;; `preserve-modes' avoids changing the (minor) modes.  But we
      ;; do want to reset the mode for VC, so we do it manually.
      (when (or revert auto-revert-check-vc-info)
        (vc-find-file-hook)))))
于 2012-05-29T09:16:56.283 回答
1

这可能是一个时间问题,需要快速连续地对文件进行多次修改。它总是发生,还是随机的?

另外,也许设置auto-revert-check-vc-infot可能会有所帮助(假设您使用的是在 emacs 中与 vc 兼容的 git 模式)。

正如迄今为止所有答案所提到的,这可能是一个时间问题。这似乎违反直觉,但也许将自动恢复间隔设置为更长的时间可能会解决问题(从而增加缓冲区恢复时恢复到其最终状态的机会)。

于 2011-06-28T19:40:00.717 回答
1

auto-revert-mode仅根据文件的修改日期决定是否恢复。我的猜测是 git 在变基过程中操纵变基文件的修改日期,停止auto-revert-mode查看每个更改。

可能的解决方案包括在变touch基之后对文件进行 ing(糟糕!),编写auto-revert-handler检查文件大小变化的函数的替换版本,或者在magit中变基,这是一个相当出色的 emacs git 接口。

于 2011-06-29T09:52:17.743 回答
0

另一种解决方案是在执行 Git 操作时关闭 Emacs,然后重新打开。如果我能找到一种方法将打开的缓冲区从一个 Emacs 会话保存到另一个会话,我几乎已经准备好诉诸于此。

于 2018-11-30T14:22:57.883 回答