all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Karl Fogel <kfogel@red-bean.com>
To: Stefan Monnier <monnier@IRO.UMontreal.CA>
Cc: emacs-devel@gnu.org
Subject: Re: Don't complain about changed file when it hasn't changed
Date: Tue, 06 Sep 2016 16:41:25 -0500	[thread overview]
Message-ID: <87oa40k8oq.fsf@red-bean.com> (raw)
In-Reply-To: <jwvy4349b0m.fsf-monnier+emacs@gnu.org> (Stefan Monnier's message of "Tue, 06 Sep 2016 13:50:09 -0400")

Stefan Monnier <monnier@IRO.UMontreal.CA> writes:
>> Stefan, what is the performance impact of this change, and how often
>> will that cost be paid?
>
>If you look at the code, you'll see that it is only called when the
>previous behavior would have been to prompt the user.
>
>I'm sure you've already bumped into those prompts, but I'd be really
>surprised if you've bumped into them frequently enough to be worried
>about their impact on battery life.

Stefan, I noticed you put a "FIXME" comment in the new code, about how it would be better to check the file size first.  That would obviously be a good optimization: most of the time -- like, 99% of the time -- if the file contents are different from the buffer contents, their sizes will differ as well, so it's an easy "early out" check that would allow us to avoid loading in the entire file contents in the vast majority of cases.

Since it's also fairly easy to do, I thought maybe there's some subtle reason you didn't do it.  In an earlier comment, you mention encrypted files (and of course perhaps compressed files are another case), where the file size on disk might differ from the buffer size even though the contents are actually "the same".

If those kinds of things were the reason you didn't code the early-out check, then do we have a canonical list anywhere of all possible "non-verbatim" buffer<->file relationships?  E.g., compressed, encrypted... what else?

It would be a shame not to have the optimization that would work fine in the majority of cases, but of course we need to make absolutely sure it's correct for all cases.  I'm not sure how difficult that would be, but thought maybe you'd considered that question, and I'd like to know how deep this rabbit hole goes.  (If it's not that deep, I'll address the FIXME.)

Your patch is below, for convenient reference.

Best regards,
-Karl

=============================================
git log --name-status 5a4bffb661^..5a4bffb661
=============================================

commit 5a4bffb6617a274ca19bc7f5c1b1ceb6345651ab
Author: Stefan Monnier <monnier@iro.umontreal.ca>
Date:   Fri Sep 2 11:44:13 2016 -0400

    Check actual contents before promting about changed file
    
    * lisp/userlock.el (userlock--check-content-unchanged)
    (userlock--ask-user-about-supersession-threat): New functions.
    * src/filelock.c (lock_file): Use them to avoid spurious prompting.
    * doc/lispref/buffers.texi (Modification Time): Update doc of
    ask-user-about-supersession-threat.

M	doc/lispref/buffers.texi
M	etc/NEWS
M	lisp/userlock.el
M	src/filelock.c

================================
git diff 5a4bffb661^..5a4bffb661
================================

diff --git doc/lispref/buffers.texi doc/lispref/buffers.texi
index 740d7cf..e4ef4d5 100644
--- doc/lispref/buffers.texi
+++ doc/lispref/buffers.texi
@@ -669,8 +669,9 @@ Modification Time
 This function is used to ask a user how to proceed after an attempt to
 modify an buffer visiting file @var{filename} when the file is newer
 than the buffer text.  Emacs detects this because the modification
-time of the file on disk is newer than the last save-time of the
-buffer.  This means some other program has probably altered the file.
+time of the file on disk is newer than the last save-time and its contents
+have changed.
+This means some other program has probably altered the file.
 
 @kindex file-supersession
 Depending on the user's answer, the function may return normally, in
diff --git etc/NEWS etc/NEWS
index 18975cb..3092e9f 100644
--- etc/NEWS
+++ etc/NEWS
@@ -213,6 +213,10 @@ In modes where form feed was treated as a whitespace character,
 It now deletes whitespace after the last form feed thus behaving the
 same as in modes where the character is not whitespace.
 
+** No more prompt about changed file when the file's content is unchanged.
+Instead of only checking the modification time, Emacs now also checks
+the file's actual content before prompting the user.
+
 \f
 * Changes in Specialized Modes and Packages in Emacs 25.2
 
diff --git lisp/userlock.el lisp/userlock.el
index a0c55fd..1cfc3b9 100644
--- lisp/userlock.el
+++ lisp/userlock.el
@@ -97,6 +97,41 @@ ask-user-about-lock-help
 
 (define-error 'file-supersession nil 'file-error)
 
+(defun userlock--check-content-unchanged (fn)
+  (with-demoted-errors "Unchanged content check: %S"
+    ;; Even tho we receive `fn', we know that `fn' refers to the current
+    ;; buffer's file.
+    (cl-assert (equal fn (expand-file-name buffer-file-truename)))
+    ;; Note: rather than read the file and compare to the buffer, we could save
+    ;; the buffer and compare to the file, but for encrypted data this
+    ;; wouldn't work well (and would risk exposing the data).
+    (save-restriction
+      (widen)
+      (let ((buf (current-buffer))
+            (cs buffer-file-coding-system)
+            (start (point-min))
+            (end (point-max)))
+        ;; FIXME: To avoid a slow `insert-file-contents' on large or
+        ;; remote files, it'd be good to include file size in the
+        ;; "visited-modtime" check.
+        (when (with-temp-buffer
+                (let ((coding-system-for-read cs)
+                      (non-essential t))
+                  (insert-file-contents fn))
+                (when (= (buffer-size) (- end start)) ;Minor optimization.
+                  (= 0 (let ((case-fold-search nil))
+                         (compare-buffer-substrings
+                          buf start end
+                          (current-buffer) (point-min) (point-max))))))
+          (set-visited-file-modtime)
+          'unchanged)))))
+
+;;;###autoload
+(defun userlock--ask-user-about-supersession-threat (fn)
+  ;; Called from filelock.c.
+  (unless (userlock--check-content-unchanged fn)
+    (ask-user-about-supersession-threat fn)))
+
 ;;;###autoload
 (defun ask-user-about-supersession-threat (fn)
   "Ask a user who is about to modify an obsolete buffer what to do.
diff --git src/filelock.c src/filelock.c
index 2f92e0f..bde34e2 100644
--- src/filelock.c
+++ src/filelock.c
@@ -684,7 +684,7 @@ lock_file (Lisp_Object fn)
     if (!NILP (subject_buf)
 	&& NILP (Fverify_visited_file_modtime (subject_buf))
 	&& !NILP (Ffile_exists_p (fn)))
-      call1 (intern ("ask-user-about-supersession-threat"), fn);
+      call1 (intern ("userlock--ask-user-about-supersession-threat"), fn);
 
   }
 



  parent reply	other threads:[~2016-09-06 21:41 UTC|newest]

Thread overview: 71+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-08-29  0:29 Don't complain about changed file when it hasn't changed Stefan Monnier
2016-08-29  3:36 ` Clément Pit--Claudel
2016-08-29  3:39   ` Daniel Colascione
2016-08-29  3:43     ` Clément Pit--Claudel
2016-08-29  6:44       ` Michael Albinus
2016-08-29 14:42         ` Eli Zaretskii
2016-08-29 16:18           ` Michael Albinus
2016-08-29 17:42         ` Davis Herring
2016-08-29 17:57           ` Clément Pit--Claudel
2016-08-29 19:10             ` Davis Herring
2016-08-29 13:17   ` Stefan Monnier
2016-08-30  9:20     ` Michael Albinus
2016-08-30 15:00       ` Stefan Monnier
2016-08-30 13:40     ` Clément Pit--Claudel
2016-08-30 15:01       ` Stefan Monnier
2016-08-30 15:23         ` Clément Pit--Claudel
2016-08-30 15:48           ` Stefan Monnier
2016-08-30 16:55             ` Eli Zaretskii
2016-08-30 16:11           ` Eli Zaretskii
2016-08-30 16:38             ` Clément Pit--Claudel
2016-08-29 14:34 ` Eli Zaretskii
2016-08-29 14:50   ` Stefan Monnier
2016-08-30 15:26     ` Eli Zaretskii
2016-08-30 15:44       ` Stefan Monnier
2016-08-30 16:15         ` Eli Zaretskii
2016-08-30 17:13           ` Stefan Monnier
2016-08-30 17:26             ` Eli Zaretskii
2016-08-30 18:02               ` Stefan Monnier
2016-08-30 15:46       ` Stefan Monnier
2016-08-30 16:19         ` Eli Zaretskii
2016-08-30 17:16           ` Stefan Monnier
2016-08-30 17:32             ` Eli Zaretskii
2016-08-30 18:06               ` Stefan Monnier
2016-09-01 13:49                 ` Eli Zaretskii
2016-09-02 15:22                   ` Stefan Monnier
2016-09-02 15:26                     ` Eli Zaretskii
2016-09-02 15:44                       ` Stefan Monnier
2016-09-02 15:39                     ` Joost Kremers
2016-08-29 16:01   ` Stefan Monnier
2016-08-29 16:26     ` Eli Zaretskii
2016-08-30  0:35       ` Stefan Monnier
2016-08-29 17:50   ` Davis Herring
2016-08-29 18:09     ` Eli Zaretskii
2016-08-29 19:22       ` Davis Herring
2016-08-30  0:39       ` Stefan Monnier
2016-08-30  7:55         ` Andreas Schwab
2016-08-30  0:37     ` Stefan Monnier
2016-08-30  1:23   ` Rolf Ade
2016-08-30 15:12     ` Eli Zaretskii
2016-08-30 15:34       ` Clément Pit--Claudel
2016-08-30 16:14         ` Eli Zaretskii
2016-09-06 16:29 ` John Wiegley
2016-09-06 17:50   ` Stefan Monnier
2016-09-06 17:52     ` John Wiegley
2016-09-06 19:00       ` Andreas Röhler
2016-09-06 21:00         ` Stefan Monnier
2016-09-06 21:29           ` Drew Adams
2016-09-06 21:41     ` Karl Fogel [this message]
2016-09-06 21:59       ` Paul Eggert
2016-09-06 22:01         ` Karl Fogel
2016-09-06 22:07           ` Davis Herring
2016-09-06 22:21             ` Karl Fogel
2016-09-06 22:46               ` Clément Pit--Claudel
2016-09-07  0:24               ` Stefan Monnier
2016-09-07 16:49                 ` Karl Fogel
2016-09-07 18:41                   ` Andreas Röhler
2016-09-07 20:02                     ` Karl Fogel
2016-09-06 22:03         ` Karl Fogel
2016-12-24  1:03 ` Rolf Ade
2016-12-25 15:44   ` Stefan Monnier
2016-12-26  0:29     ` Rolf Ade

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87oa40k8oq.fsf@red-bean.com \
    --to=kfogel@red-bean.com \
    --cc=emacs-devel@gnu.org \
    --cc=monnier@IRO.UMontreal.CA \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.