我在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
(有时在交互模式下将提交压缩在一起)。现在我已经进入了远程更新,我将再次编辑该文件,并发现它看起来我已经丢失了所有最近的更改。它第一次发生时,我非常生气并诅咒rebase算法以某种方式丢失我的更改,直到我意识到文件完全完好无损并且只是Emacs在更改之前以某种方式恢复到文件的版本我我承诺。我总是可以通过关闭缓冲区并再次加载文件来解决问题,但每次都这样做很麻烦。
有谁知道问题是什么?
答案 0 :(得分:3)
我是autorevert
的原创作者,然而,我必须深入挖掘我的记忆,因为我已经很长时间没有使用它了。
我认为这归结为Emacs核心函数verify-visited-file-modtime
的一个问题,它会自动恢复使用以检查缓冲区是否需要刷新。
这可能是由多种原因造成的:
这可能是竞争条件,其中Emacs在完全写入之前读取文件,但将缓冲区modtime设置为结束时间。 (当发生这种情况时,Emacs已将缓冲区中途自动恢复,但新的自动恢复不会触发恢复文件的最终版本。)
如果文件的两个变体具有相同的时间戳,那么Emacs将看不到差异。
我不认为你在lisp方面可以做的很少,以使其正常工作(不解决暴力解决方案)。 Emacs确实需要原子地获取已打开并读入缓冲区的文件的指纹,这只能从核心(这是我所在区域之外的东西)完成。此外,某些文件系统的时间戳分辨率不高于一秒,因此使用时间戳来检测文件是否已更改本来就很困难。
答案 1 :(得分:2)
如前所述,这是一种竞争条件。 “git pull --rebase”可能会在重播您的提交时多次修改同一文件。如果发生以下情况,则游戏结束:
由于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)))))
答案 2 :(得分:1)
这可能是一个时间问题,需要快速连续多次修改文件。它总是会发生,还是随机的?
另外,将auto-revert-check-vc-info
设置为t
可能有所帮助(假设您使用的是与emacs中vc兼容的git模式)。
答案 3 :(得分:1)
auto-revert-mode
决定是否仅根据文件的修改日期进行还原。我的猜测是,git在重新定位过程中操纵了已更改文件的修改日期,阻止auto-revert-mode
看到每个更改。
可能的解决方案包括重新定位后的touch
个文件(哎呀!),编写auto-revert-handler
函数的替换版本,用于检查文件大小的变化,或者在magit内进行变基,哪个是一个相当精彩的emacs git界面。
答案 4 :(得分:0)
另一种解决方案是在执行Git操作时关闭Emacs,然后重新打开。如果我能找到一种方法来保存从一个Emacs会话到另一个Emacs会话的打开的缓冲区,我几乎准备诉诸于此。