Emacs возвращает буфер в странное предыдущее состояние с помощью Git-Rebase - PullRequest
3 голосов
/ 28 июня 2011

Я использую Emacs (23.3.1) на OS X. Я выдаю команды 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 каким-то образом вернулось к версии файла до изменений, которые я только что совершил. Я всегда могу решить проблему, закрыв буфер и загрузив файл снова, но каждый раз это довольно сложно.

Кто-нибудь знает, в чем проблема?

Ответы [ 5 ]

3 голосов
/ 29 июня 2011

Я - первоначальный автор autorevert, однако мне нужно довольно глубоко копаться в моей памяти, поскольку я не работал с ним в течение достаточно долгого времени.

Я полагаю, что это сводится к проблеме с основной функцией Emacs verify-visited-file-modtime, которую автоматически используют для проверки необходимости обновления буферов.

Это может быть вызвано рядом причин:

  • Это может быть условие гонки, когда Emacs читает файл до того, как он будет полностью записан, но устанавливает время модификации буфера в конечное время. (Когда это происходит, Emacs автоматически восстанавливает буфер на полпути, но новый автоматический возврат не приводит к возврату окончательной версии файла.)

  • Если два варианта файла имеют одинаковую отметку времени, Emacs не увидит разницы.

Я не думаю, что вы мало что можете сделать со стороны LISP, чтобы заставить это работать должным образом (без разрешения грубых решений). Emacs действительно нужно атомарно получить отпечаток файла, который был открыт и считан в буфер, и это можно сделать только из ядра (что-то вне моей области). Кроме того, некоторые файловые системы не имеют более высокого разрешения временной метки, чем одна секунда, что затрудняет использование временной метки для обнаружения изменения файла.

2 голосов
/ 29 мая 2012

Это состояние гонки, как упоминалось ранее.«git pull --rebase» может изменять один и тот же файл несколько раз, так как он воспроизводит ваши коммиты.Если происходит следующее, игра окончена:

  1. git меняет файл "foo"
  2. автоматически запускает и перезагружает "foo"
  3. git change file "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)))))
1 голос
/ 29 июня 2011

auto-revert-mode решает, следует ли вернуться, основываясь исключительно на дате изменения файла.Я предполагаю, что git манипулирует датой изменения перебазированных файлов во время процесса перебазирования, мешая auto-revert-mode видеть каждое изменение.

Возможные решения включают touch IN файлы после перебазировки (чёрт!)сменная версия функции auto-revert-handler, которая проверяет изменения размера файла или перебазирует в magit , который является довольно замечательным интерфейсом emacs git.

1 голос
/ 28 июня 2011

это может быть проблема синхронизации с несколькими модификациями файла в быстрой последовательности.всегда ли это происходит, или это случайно?

также, может быть, установка auto-revert-check-vc-info в t может помочь (если вы используете git-режим, совместимый с vc в emacs).

как уже упоминалось во всех ответах, вероятно, это проблема времени.это может показаться противоречащим интуиции, но, возможно, установка интервала автоматического возврата на что-то более длинное может решить проблему (таким образом, увеличивается вероятность того, что при возврате буфера он вернется в свое окончательное состояние).

0 голосов
/ 30 ноября 2018

Другим решением было бы закрыть Emacs при выполнении операций Git и открыть после. Я почти готов прибегнуть к этому, если смогу найти способ сохранить открытые буферы из одной сессии Emacs в другую.

...