unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#25683: 25.1.91; vc-dir shows conflicted files as "edited"
@ 2017-02-10 22:15 Tom Tromey
       [not found] ` <handler.25683.B.148676496228355.ack@debbugs.gnu.org>
  2017-02-15  4:18 ` bug#25683: done Tom Tromey
  0 siblings, 2 replies; 12+ messages in thread
From: Tom Tromey @ 2017-02-10 22:15 UTC (permalink / raw)
  To: 25683


I was doing a "git rebase" and it had some conflicts.

If I "git status" in the shell I can see conflicts like:

	both modified:   gdb/minidebug.c

However, if I use vc-dir, I just see:

     edited              gdb/minidebug.c

This isn't any different from a file that is touched by this change but
not in conflict.

I think it would be nicer if vc-dir showed me which files had conflicts
and which were merely "edited".

Tom




In GNU Emacs 25.1.91.2 (x86_64-unknown-linux-gnu, GTK+ Version 3.22.7)
 of 2017-02-02 built on bapiya
Repository revision: 7e02a477bbcabb4e65aeecade79b67357c0b9dae
Windowing system distributor 'Fedora Project', version 11.0.11901000
System Description:	Fedora release 25 (Twenty Five)

Configured using:
 'configure --prefix=/home/tromey/Emacs/install/ --with-modules'

Configured features:
XPM JPEG TIFF GIF PNG RSVG IMAGEMAGICK SOUND GPM DBUS GCONF GSETTINGS
NOTIFY LIBSELINUX GNUTLS LIBXML2 FREETYPE M17N_FLT LIBOTF XFT ZLIB
TOOLKIT_SCROLL_BARS GTK3 X11 MODULES

Important settings:
  value of $LANG: en_US.utf8
  value of $XMODIFIERS: @im=ibus
  locale-coding-system: utf-8-unix

Major mode: VC dir

Minor modes in effect:
  vc-parent-buffer: *vc-dir*<binutils-gdb>
  shell-dirtrack-mode: t
  diff-auto-refine-mode: t
  which-function-mode: t
  erc-services-mode: t
  erc-list-mode: t
  erc-menu-mode: t
  erc-autojoin-mode: t
  erc-ring-mode: t
  erc-networks-mode: t
  erc-pcomplete-mode: t
  erc-track-mode: t
  erc-match-mode: t
  erc-netsplit-mode: t
  erc-hl-nicks-mode: t
  erc-button-mode: t
  erc-fill-mode: t
  erc-stamp-mode: t
  erc-irccontrols-mode: t
  erc-noncommands-mode: t
  erc-move-to-prompt-mode: t
  erc-readonly-mode: t
  savehist-mode: t
  tooltip-mode: t
  global-eldoc-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t
  buffer-read-only: t
  column-number-mode: t
  line-number-mode: t
  transient-mark-mode: t

Recent messages:
Delete excess backup versions of /home/tromey/gdb/binutils-gdb/.git/COMMIT_EDITMSG? (y or n) y
Wrote /home/tromey/gdb/binutils-gdb/.git/COMMIT_EDITMSG
There are unresolved conflicts in /home/tromey/gdb/binutils-gdb/gdb/ui-out.h
Making completion list...
Mark saved where search started
Mark set
Mark saved where search started
C-x C-g is undefined
(No files need saving)
Hiding up-to-date and ignored items

Load-path shadows:
/home/tromey/.emacs.d/elpa/bubbles-0.5/bubbles hides /home/tromey/Emacs/install/share/emacs/25.1.91/lisp/play/bubbles

Features:
(shadow emacsbug conf-mode two-column iso-transl perl-mode sh-script
executable cal-move nnregistry python tramp-sh tramp tramp-compat
tramp-loaddefs trampver ucs-normalize make-mode etags cursor-sensor
mhtml-mode org-bullets org-element org-rmail org-mhe org-irc org-info
org-gnus org-docview doc-view image-mode org-bibtex bibtex org-bbdb
org-w3m org org-macro org-footnote org-pcomplete org-list org-faces
org-entities org-version ob-emacs-lisp ob ob-tangle ob-ref ob-lob
ob-table ob-exp org-src ob-keys ob-comint ob-core ob-eval org-compat
org-macs org-loaddefs gnus-fun gnus-draft noutline outline ido skeleton
css-mode smie eww url-queue trace find-dired descr-text gdb-shell
lisp-mnt url-handlers url-http url-gw url-auth lex edebug debug m4-mode
bbdb-sc supercite regi xref project log-edit js json map sgml-mode
flow-fill gnus-html xml url-cache mm-url url url-proxy url-privacy
url-expand url-methods url-history url-cookie url-domsuf eieio-opt
speedbar sb-image ezimage dframe find-func whitespace tcl log-view
pcvs-util vc-annotate term/xterm xterm smerge-mode goto-addr shell
find-file idutils derived bug-reference cc-mode cc-fonts cc-guess
cc-menus cc-cmds jka-compr mailalias mail-hist nnir sort gnus-cite
smiley shr-color url-util url-parse url-vars shr dom subr-x browse-url
mm-archive gnus-async gnus-bcklg qp gnus-ml disp-table gnus-topic
nndraft nnmh nnfolder utf-7 bbdb-gnus bbdb-mua bbdb-com crm
network-stream nsm starttls gnus-agent gnus-srvr gnus-score score-mode
nnvirtual gnus-msg nntp gnus-cache gnus-registry registry eieio-compat
eieio-base gnus-art mm-uu mml2015 mm-view mml-smime smime dig mailcap
gnus-sum gnus-group gnus-undo smtpmail gnus-start gnus-cloud nnimap
nnmail mail-source tls gnutls utf7 netrc nnoo parse-time gnus-spec
gnus-int gnus-range gnus-win gnus gnus-ems nnheader bbdb-message
sendmail mail-extr message idna dired rfc822 mml mml-sec epg mm-decode
mm-bodies mm-encode mail-parse rfc2231 rfc2047 rfc2045 ietf-drums
mailabbrev mail-utils gmm-utils mailheader gud dwarf-mode copyright
dabbrev misearch multi-isearch vc-mtn vc-hg vc-bzr vc-src vc-sccs vc-svn
vc-cvs vc-rcs add-log rx vc-git diff-mode easy-mmode rust-mode flyspell
ispell diminish edmacro kmacro projectile grep compile ibuf-ext ibuffer
dash appt diary-lib diary-loaddefs cal-menu calendar cal-loaddefs
which-func imenu minimap autorevert filenotify cus-start cus-load status
erc-services erc-list erc-menu erc-join erc-ring erc-networks
erc-pcomplete pcomplete erc-track erc-match erc-netsplit erc-hl-nicks
color erc-button erc-fill erc-stamp wid-edit erc-goodies erc erc-backend
erc-compat format-spec auth-source eieio gnus-util mm-util help-fns
mail-prsvr password-cache thingatpt pp warnings advice vc-dir ewoc vc
vc-dispatcher cc-styles cc-align cc-engine cc-vars cc-defs bbdb
bbdb-site timezone ange-ftp comint ansi-color ring server savehist
finder-inf dwarf-mode-autoloads gdb-shell-autoloads eieio-core
lisppaste-autoloads pydoc-info-autoloads info-look cl-seq cl-macs cl
weblogger-autoloads info package epg-config seq byte-opt gv bytecomp
byte-compile cl-extra help-mode easymenu cconv cl-loaddefs pcase cl-lib
bbdb-loaddefs time-date mule-util tooltip eldoc electric uniquify
ediff-hook vc-hooks lisp-float-type mwheel x-win term/common-win x-dnd
tool-bar dnd fontset image regexp-opt fringe tabulated-list newcomment
elisp-mode lisp-mode prog-mode register page menu-bar rfn-eshadow timer
select scroll-bar mouse jit-lock font-lock syntax facemenu font-core
frame cl-generic cham georgian utf-8-lang misc-lang vietnamese tibetan
thai tai-viet lao korean japanese eucjp-ms cp51932 hebrew greek romanian
slovak czech european ethiopic indian cyrillic chinese charscript
case-table epa-hook jka-cmpr-hook help simple abbrev minibuffer
cl-preloaded nadvice loaddefs button faces cus-face macroexp files
text-properties overlay sha1 md5 base64 format env code-pages mule
custom widget hashtable-print-readable backquote dbusbind inotify
dynamic-setting system-font-setting font-render-setting move-toolbar gtk
x-toolkit x multi-tty make-network-process emacs)

Memory information:
((conses 16 1841388 223499)
 (symbols 48 137600 13)
 (miscs 40 30171 9433)
 (strings 32 518292 186214)
 (string-bytes 1 14434760)
 (vectors 16 128453)
 (vector-slots 8 2744148 100168)
 (floats 8 924 1207)
 (intervals 56 168057 715)
 (buffers 976 269))





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#25683: semi-working patch
       [not found] ` <handler.25683.B.148676496228355.ack@debbugs.gnu.org>
@ 2017-02-11 20:45   ` Tom Tromey
  2017-02-11 21:40     ` Tom Tromey
  0 siblings, 1 reply; 12+ messages in thread
From: Tom Tromey @ 2017-02-11 20:45 UTC (permalink / raw)
  To: 25683

This patch kind of works.

That is, it correctly notices files that have a conflict.  However, when
typing "g" in vc-dir, such a file briefly appears as "edited", then
switches to "conflict".  This happens because a file is first noticed in
the diff-index phase, then later its state is corrected in the new
ls-files-conflict stage.

Ideally it would be possible to get the conflict state directly from git
diff-index, but I couldn't see a way to do that :(.  The manual says:

        8. sha1 for "dst"; 0{40} if creation, unmerged or "look at work tree".

... but this just means the value can be all-0 for either the edited or
conflict states.

One fix for this might be to pass maintain more state here and only call
the update-function when all the passes are done.

Another idea for a fix would be to notice files with an all-0 sha in
diff-index, then push these names on "files"; then let the new
ls-files-conflict stage determine the result.

Tom

diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el
index 24dabb6..db19eb0 100644
--- a/lisp/vc/vc-git.el
+++ b/lisp/vc/vc-git.el
@@ -417,10 +417,22 @@ vc-git-after-dir-status-stage
                  result))))
       (`ls-files-up-to-date
        (setq next-stage 'ls-files-unknown)
-       (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 0\t\\([^\0]+\\)\0" nil t)
+       (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} \\([0-3]\\)\t\\([^\0]+\\)\0" nil t)
+         (let ((perm (string-to-number (match-string 1) 8))
+               (state (match-string 2))
+               (name (match-string 3)))
+           (push (list name (if (equal state "0")
+                                'up-to-date
+                              'conflict)
+                       (vc-git-create-extra-fileinfo perm perm))
+                 result))))
+      (`ls-files-conflict
+       (setq next-stage 'ls-files-unknown)
+       ;; It's enough to look for "3" to notice a conflict.
+       (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 3\t\\([^\0]+\\)\0" nil t)
          (let ((perm (string-to-number (match-string 1) 8))
                (name (match-string 2)))
-           (push (list name 'up-to-date
+           (push (list name 'conflict
                        (vc-git-create-extra-fileinfo perm perm))
                  result))))
       (`ls-files-unknown
@@ -435,7 +447,7 @@ vc-git-after-dir-status-stage
                      (vc-git-create-extra-fileinfo 0 0))
                result)))
       (`diff-index
-       (setq next-stage (if files 'ls-files-up-to-date 'ls-files-unknown))
+       (setq next-stage (if files 'ls-files-up-to-date 'ls-files-conflict))
        (while (re-search-forward
                ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMUT]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0"
                nil t 1)
@@ -491,6 +503,9 @@ vc-git-dir-status-goto-stage
     (`ls-files-up-to-date
      (vc-git-command (current-buffer) 'async files
                      "ls-files" "-z" "-c" "-s" "--"))
+    (`ls-files-conflict
+     (vc-git-command (current-buffer) 'async files
+                     "ls-files" "-z" "-c" "-s" "--"))
     (`ls-files-unknown
      (vc-git-command (current-buffer) 'async files
                      "ls-files" "-z" "-o" "--directory"





^ permalink raw reply related	[flat|nested] 12+ messages in thread

* bug#25683: semi-working patch
  2017-02-11 20:45   ` bug#25683: semi-working patch Tom Tromey
@ 2017-02-11 21:40     ` Tom Tromey
  2017-02-12  6:17       ` Tom Tromey
  0 siblings, 1 reply; 12+ messages in thread
From: Tom Tromey @ 2017-02-11 21:40 UTC (permalink / raw)
  To: Tom Tromey; +Cc: 25683

Tom> One fix for this might be to pass maintain more state here and only call
Tom> the update-function when all the passes are done.

I've implemented this idea.
Let me know what you think.

Tom

diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el
index 24dabb6..de07ea2 100644
--- a/lisp/vc/vc-git.el
+++ b/lisp/vc/vc-git.el
@@ -401,11 +401,30 @@ vc-git-dir-printer
      (vc-git-file-type-as-string old-perm new-perm)
      (vc-git-rename-as-string state extra))))
 
-(defun vc-git-after-dir-status-stage (stage files update-function)
+(cl-defstruct (vc-git-dir-status-state
+               (:copier nil)
+               (:conc-name vc-git-dir-status-state->))
+  ;; Current stage.
+  stage
+  ;; List of files still to be processed.
+  files
+  ;; Update function to be called at the end.
+  update-function
+  ;; Hash table of entries for files we've computed so far.
+  (hash (make-hash-table :test 'equal)))
+
+(defsubst vc-git-dir-status-update-file (state filename file-state file-info)
+  (puthash filename (list file-state file-info)
+           (vc-git-dir-status-state->hash state))
+  (setf (vc-git-dir-status-state->files state)
+        (delete filename (vc-git-dir-status-state->files state))))
+
+(defun vc-git-after-dir-status-stage (git-state)
   "Process sentinel for the various dir-status stages."
-  (let (next-stage result)
+  (let (next-stage
+        (files (vc-git-dir-status-state->files git-state)))
     (goto-char (point-min))
-    (pcase stage
+    (pcase (vc-git-dir-status-state->stage git-state)
       (`update-index
        (setq next-stage (if (vc-git--empty-db-p) 'ls-files-added 'diff-index)))
       (`ls-files-added
@@ -413,29 +432,40 @@ vc-git-after-dir-status-stage
        (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 0\t\\([^\0]+\\)\0" nil t)
          (let ((new-perm (string-to-number (match-string 1) 8))
                (name (match-string 2)))
-           (push (list name 'added (vc-git-create-extra-fileinfo 0 new-perm))
-                 result))))
+           (vc-git-dir-status-update-file
+            git-state name 'added
+            (vc-git-create-extra-fileinfo 0 new-perm)))))
       (`ls-files-up-to-date
        (setq next-stage 'ls-files-unknown)
-       (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 0\t\\([^\0]+\\)\0" nil t)
+       (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} \\([0-3]\\)\t\\([^\0]+\\)\0" nil t)
+         (let ((perm (string-to-number (match-string 1) 8))
+               (state (match-string 2))
+               (name (match-string 3)))
+           (vc-git-dir-status-update-file
+            git-state name (if (equal state "0")
+                               'up-to-date
+                             'conflict)
+            (vc-git-create-extra-fileinfo perm perm)))))
+      (`ls-files-conflict
+       (setq next-stage 'ls-files-unknown)
+       ;; It's enough to look for "3" to notice a conflict.
+       (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 3\t\\([^\0]+\\)\0" nil t)
          (let ((perm (string-to-number (match-string 1) 8))
                (name (match-string 2)))
-           (push (list name 'up-to-date
-                       (vc-git-create-extra-fileinfo perm perm))
-                 result))))
+           (vc-git-dir-status-update-file
+            git-state name 'conflict
+            (vc-git-create-extra-fileinfo perm perm)))))
       (`ls-files-unknown
        (when files (setq next-stage 'ls-files-ignored))
        (while (re-search-forward "\\([^\0]*?\\)\0" nil t 1)
-         (push (list (match-string 1) 'unregistered
-                     (vc-git-create-extra-fileinfo 0 0))
-               result)))
+         (vc-git-create-extra-fileinfo git-state (match-string 1) 'unregistered
+                                       (vc-git-create-extra-fileinfo 0 0))))
       (`ls-files-ignored
        (while (re-search-forward "\\([^\0]*?\\)\0" nil t 1)
-         (push (list (match-string 1) 'ignored
-                     (vc-git-create-extra-fileinfo 0 0))
-               result)))
+         (vc-git-dir-status-update-file git-state (match-string 1) 'ignored
+                                        (vc-git-create-extra-fileinfo 0 0))))
       (`diff-index
-       (setq next-stage (if files 'ls-files-up-to-date 'ls-files-unknown))
+       (setq next-stage (if files 'ls-files-up-to-date 'ls-files-conflict))
        (while (re-search-forward
                ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMUT]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0"
                nil t 1)
@@ -446,30 +476,34 @@ vc-git-after-dir-status-stage
                (new-name (match-string 8)))
            (if new-name  ; Copy or rename.
                (if (eq ?C (string-to-char state))
-                   (push (list new-name 'added
-                               (vc-git-create-extra-fileinfo old-perm new-perm
-                                                             'copy name))
-                         result)
-                 (push (list name 'removed
-                             (vc-git-create-extra-fileinfo 0 0
-                                                           'rename new-name))
-                       result)
-                 (push (list new-name 'added
-                             (vc-git-create-extra-fileinfo old-perm new-perm
-                                                           'rename name))
-                       result))
-             (push (list name (vc-git--state-code state)
-                         (vc-git-create-extra-fileinfo old-perm new-perm))
-                   result))))))
-    (when result
-      (setq result (nreverse result))
-      (when files
-        (dolist (entry result) (setq files (delete (car entry) files)))
-        (unless files (setq next-stage nil))))
-    (when (or result (not next-stage))
-      (funcall update-function result next-stage))
-    (when next-stage
-      (vc-git-dir-status-goto-stage next-stage files update-function))))
+                   (vc-git-dir-status-update-file
+                    git-state new-name 'added
+                    (vc-git-create-extra-fileinfo old-perm new-perm
+                                                  'copy name))
+                 (vc-git-dir-status-update-file
+                  git-state name 'removed
+                  (vc-git-create-extra-fileinfo 0 0 'rename new-name))
+                 (vc-git-dir-status-update-file
+                  git-state new-name 'added
+                  (vc-git-create-extra-fileinfo old-perm new-perm
+                                                'rename name)))
+             (vc-git-dir-status-update-file
+              git-state name (vc-git--state-code state)
+              (vc-git-create-extra-fileinfo old-perm new-perm)))))))
+    ;; If we had files but now we don't, it's time to stop.
+    (when (and files (not (vc-git-dir-status-state->files git-state)))
+      (setq next-stage nil))
+    (setf (vc-git-dir-status-state->stage git-state) next-stage)
+    (setf (vc-git-dir-status-state->files git-state) files)
+    (if next-stage
+        (vc-git-dir-status-goto-stage git-state)
+      (funcall (vc-git-dir-status-state->update-function git-state)
+               (let ((result nil))
+                 (maphash (lambda (key value)
+                            (push (cons key value) result))
+                          (vc-git-dir-status-state->hash git-state))
+                 result)
+               nil))))
 
 ;; Follows vc-git-command (or vc-do-async-command), which uses vc-do-command
 ;; from vc-dispatcher.
@@ -477,41 +511,48 @@ vc-git-after-dir-status-stage
 ;; Follows vc-exec-after.
 (declare-function vc-set-async-update "vc-dispatcher" (process-buffer))
 
-(defun vc-git-dir-status-goto-stage (stage files update-function)
-  (erase-buffer)
-  (pcase stage
-    (`update-index
-     (if files
-         (vc-git-command (current-buffer) 'async files "add" "--refresh" "--")
-       (vc-git-command (current-buffer) 'async nil
-                       "update-index" "--refresh")))
-    (`ls-files-added
-     (vc-git-command (current-buffer) 'async files
-                     "ls-files" "-z" "-c" "-s" "--"))
-    (`ls-files-up-to-date
-     (vc-git-command (current-buffer) 'async files
-                     "ls-files" "-z" "-c" "-s" "--"))
-    (`ls-files-unknown
-     (vc-git-command (current-buffer) 'async files
-                     "ls-files" "-z" "-o" "--directory"
-                     "--no-empty-directory" "--exclude-standard" "--"))
-    (`ls-files-ignored
-     (vc-git-command (current-buffer) 'async files
-                     "ls-files" "-z" "-o" "-i" "--directory"
-                     "--no-empty-directory" "--exclude-standard" "--"))
-    ;; --relative added in Git 1.5.5.
-    (`diff-index
-     (vc-git-command (current-buffer) 'async files
-                     "diff-index" "--relative" "-z" "-M" "HEAD" "--")))
-  (vc-run-delayed
-   (vc-git-after-dir-status-stage stage files update-function)))
+(defun vc-git-dir-status-goto-stage (git-state)
+  (let ((files (vc-git-dir-status-state->files git-state)))
+    (erase-buffer)
+    (pcase (vc-git-dir-status-state->stage git-state)
+      (`update-index
+       (if files
+           (vc-git-command (current-buffer) 'async files "add" "--refresh" "--")
+         (vc-git-command (current-buffer) 'async nil
+                         "update-index" "--refresh")))
+      (`ls-files-added
+       (vc-git-command (current-buffer) 'async files
+                       "ls-files" "-z" "-c" "-s" "--"))
+      (`ls-files-up-to-date
+       (vc-git-command (current-buffer) 'async files
+                       "ls-files" "-z" "-c" "-s" "--"))
+      (`ls-files-conflict
+       (vc-git-command (current-buffer) 'async files
+                       "ls-files" "-z" "-c" "-s" "--"))
+      (`ls-files-unknown
+       (vc-git-command (current-buffer) 'async files
+                       "ls-files" "-z" "-o" "--directory"
+                       "--no-empty-directory" "--exclude-standard" "--"))
+      (`ls-files-ignored
+       (vc-git-command (current-buffer) 'async files
+                       "ls-files" "-z" "-o" "-i" "--directory"
+                       "--no-empty-directory" "--exclude-standard" "--"))
+      ;; --relative added in Git 1.5.5.
+      (`diff-index
+       (vc-git-command (current-buffer) 'async files
+                       "diff-index" "--relative" "-z" "-M" "HEAD" "--")))
+    (vc-run-delayed
+      (vc-git-after-dir-status-stage git-state))))
 
 (defun vc-git-dir-status-files (_dir files update-function)
   "Return a list of (FILE STATE EXTRA) entries for DIR."
   ;; Further things that would have to be fixed later:
   ;; - how to handle unregistered directories
   ;; - how to support vc-dir on a subdir of the project tree
-  (vc-git-dir-status-goto-stage 'update-index files update-function))
+  (vc-git-dir-status-goto-stage
+   (make-vc-git-dir-status-state :stage 'update-index
+                                 :files files
+                                 :update-function update-function)))
 
 (defvar vc-git-stash-map
   (let ((map (make-sparse-keymap)))





^ permalink raw reply related	[flat|nested] 12+ messages in thread

* bug#25683: semi-working patch
  2017-02-11 21:40     ` Tom Tromey
@ 2017-02-12  6:17       ` Tom Tromey
  2017-02-14  1:12         ` Tom Tromey
  0 siblings, 1 reply; 12+ messages in thread
From: Tom Tromey @ 2017-02-12  6:17 UTC (permalink / raw)
  To: Tom Tromey; +Cc: 25683

Tom> I've implemented this idea.
Tom> Let me know what you think.

It seems that this patch regresses the case where a new file is created
and not registered.  So, there's more debugging to be done.
I'd still appreciate comments on the direction.

Tom





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#25683: semi-working patch
  2017-02-12  6:17       ` Tom Tromey
@ 2017-02-14  1:12         ` Tom Tromey
  2017-02-14 14:03           ` Dmitry Gutov
  0 siblings, 1 reply; 12+ messages in thread
From: Tom Tromey @ 2017-02-14  1:12 UTC (permalink / raw)
  To: Tom Tromey; +Cc: 25683

Tom> It seems that this patch regresses the case where a new file is created
Tom> and not registered.  So, there's more debugging to be done.
Tom> I'd still appreciate comments on the direction.

It was just a spot where I didn't properly update a call.
This version works.

Tom

commit 4921bfdbbd7009749e371a6ec2b76b78679e4a3d
Author: Tom Tromey <tom@tromey.com>
Date:   Mon Feb 13 18:09:36 2017 -0700

    Make vc-git detect conflict state for vc-dir
    
    * lisp/vc/vc-git.el (vc-git-dir-status-state): New struct.
    (vc-git-dir-status-update-file): New function.
    (vc-git-after-dir-status-stage, vc-git-dir-status-goto-stage): Use
    vc-git-dir-status-state; add 'ls-files-conflict state.
    (vc-git-dir-status-files): Create a vc-git-dir-status-state.

diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el
index d985cb3..713965e 100644
--- a/lisp/vc/vc-git.el
+++ b/lisp/vc/vc-git.el
@@ -401,11 +401,30 @@ vc-git-dir-printer
      (vc-git-file-type-as-string old-perm new-perm)
      (vc-git-rename-as-string state extra))))
 
-(defun vc-git-after-dir-status-stage (stage files update-function)
+(cl-defstruct (vc-git-dir-status-state
+               (:copier nil)
+               (:conc-name vc-git-dir-status-state->))
+  ;; Current stage.
+  stage
+  ;; List of files still to be processed.
+  files
+  ;; Update function to be called at the end.
+  update-function
+  ;; Hash table of entries for files we've computed so far.
+  (hash (make-hash-table :test 'equal)))
+
+(defsubst vc-git-dir-status-update-file (state filename file-state file-info)
+  (puthash filename (list file-state file-info)
+           (vc-git-dir-status-state->hash state))
+  (setf (vc-git-dir-status-state->files state)
+        (delete filename (vc-git-dir-status-state->files state))))
+
+(defun vc-git-after-dir-status-stage (git-state)
   "Process sentinel for the various dir-status stages."
-  (let (next-stage result)
+  (let (next-stage
+        (files (vc-git-dir-status-state->files git-state)))
     (goto-char (point-min))
-    (pcase stage
+    (pcase (vc-git-dir-status-state->stage git-state)
       (`update-index
        (setq next-stage (if (vc-git--empty-db-p) 'ls-files-added 'diff-index)))
       (`ls-files-added
@@ -413,29 +432,40 @@ vc-git-after-dir-status-stage
        (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 0\t\\([^\0]+\\)\0" nil t)
          (let ((new-perm (string-to-number (match-string 1) 8))
                (name (match-string 2)))
-           (push (list name 'added (vc-git-create-extra-fileinfo 0 new-perm))
-                 result))))
+           (vc-git-dir-status-update-file
+            git-state name 'added
+            (vc-git-create-extra-fileinfo 0 new-perm)))))
       (`ls-files-up-to-date
        (setq next-stage 'ls-files-unknown)
-       (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 0\t\\([^\0]+\\)\0" nil t)
+       (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} \\([0-3]\\)\t\\([^\0]+\\)\0" nil t)
+         (let ((perm (string-to-number (match-string 1) 8))
+               (state (match-string 2))
+               (name (match-string 3)))
+           (vc-git-dir-status-update-file
+            git-state name (if (equal state "0")
+                               'up-to-date
+                             'conflict)
+            (vc-git-create-extra-fileinfo perm perm)))))
+      (`ls-files-conflict
+       (setq next-stage 'ls-files-unknown)
+       ;; It's enough to look for "3" to notice a conflict.
+       (while (re-search-forward "\\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} 3\t\\([^\0]+\\)\0" nil t)
          (let ((perm (string-to-number (match-string 1) 8))
                (name (match-string 2)))
-           (push (list name 'up-to-date
-                       (vc-git-create-extra-fileinfo perm perm))
-                 result))))
+           (vc-git-dir-status-update-file
+            git-state name 'conflict
+            (vc-git-create-extra-fileinfo perm perm)))))
       (`ls-files-unknown
        (when files (setq next-stage 'ls-files-ignored))
        (while (re-search-forward "\\([^\0]*?\\)\0" nil t 1)
-         (push (list (match-string 1) 'unregistered
-                     (vc-git-create-extra-fileinfo 0 0))
-               result)))
+         (vc-git-dir-status-update-file git-state (match-string 1) 'unregistered
+                                        (vc-git-create-extra-fileinfo 0 0))))
       (`ls-files-ignored
        (while (re-search-forward "\\([^\0]*?\\)\0" nil t 1)
-         (push (list (match-string 1) 'ignored
-                     (vc-git-create-extra-fileinfo 0 0))
-               result)))
+         (vc-git-dir-status-update-file git-state (match-string 1) 'ignored
+                                        (vc-git-create-extra-fileinfo 0 0))))
       (`diff-index
-       (setq next-stage (if files 'ls-files-up-to-date 'ls-files-unknown))
+       (setq next-stage (if files 'ls-files-up-to-date 'ls-files-conflict))
        (while (re-search-forward
                ":\\([0-7]\\{6\\}\\) \\([0-7]\\{6\\}\\) [0-9a-f]\\{40\\} [0-9a-f]\\{40\\} \\(\\([ADMUT]\\)\0\\([^\0]+\\)\\|\\([CR]\\)[0-9]*\0\\([^\0]+\\)\0\\([^\0]+\\)\\)\0"
                nil t 1)
@@ -446,30 +476,34 @@ vc-git-after-dir-status-stage
                (new-name (match-string 8)))
            (if new-name  ; Copy or rename.
                (if (eq ?C (string-to-char state))
-                   (push (list new-name 'added
-                               (vc-git-create-extra-fileinfo old-perm new-perm
-                                                             'copy name))
-                         result)
-                 (push (list name 'removed
-                             (vc-git-create-extra-fileinfo 0 0
-                                                           'rename new-name))
-                       result)
-                 (push (list new-name 'added
-                             (vc-git-create-extra-fileinfo old-perm new-perm
-                                                           'rename name))
-                       result))
-             (push (list name (vc-git--state-code state)
-                         (vc-git-create-extra-fileinfo old-perm new-perm))
-                   result))))))
-    (when result
-      (setq result (nreverse result))
-      (when files
-        (dolist (entry result) (setq files (delete (car entry) files)))
-        (unless files (setq next-stage nil))))
-    (when (or result (not next-stage))
-      (funcall update-function result next-stage))
-    (when next-stage
-      (vc-git-dir-status-goto-stage next-stage files update-function))))
+                   (vc-git-dir-status-update-file
+                    git-state new-name 'added
+                    (vc-git-create-extra-fileinfo old-perm new-perm
+                                                  'copy name))
+                 (vc-git-dir-status-update-file
+                  git-state name 'removed
+                  (vc-git-create-extra-fileinfo 0 0 'rename new-name))
+                 (vc-git-dir-status-update-file
+                  git-state new-name 'added
+                  (vc-git-create-extra-fileinfo old-perm new-perm
+                                                'rename name)))
+             (vc-git-dir-status-update-file
+              git-state name (vc-git--state-code state)
+              (vc-git-create-extra-fileinfo old-perm new-perm)))))))
+    ;; If we had files but now we don't, it's time to stop.
+    (when (and files (not (vc-git-dir-status-state->files git-state)))
+      (setq next-stage nil))
+    (setf (vc-git-dir-status-state->stage git-state) next-stage)
+    (setf (vc-git-dir-status-state->files git-state) files)
+    (if next-stage
+        (vc-git-dir-status-goto-stage git-state)
+      (funcall (vc-git-dir-status-state->update-function git-state)
+               (let ((result nil))
+                 (maphash (lambda (key value)
+                            (push (cons key value) result))
+                          (vc-git-dir-status-state->hash git-state))
+                 result)
+               nil))))
 
 ;; Follows vc-git-command (or vc-do-async-command), which uses vc-do-command
 ;; from vc-dispatcher.
@@ -477,41 +511,48 @@ vc-git-after-dir-status-stage
 ;; Follows vc-exec-after.
 (declare-function vc-set-async-update "vc-dispatcher" (process-buffer))
 
-(defun vc-git-dir-status-goto-stage (stage files update-function)
-  (erase-buffer)
-  (pcase stage
-    (`update-index
-     (if files
-         (vc-git-command (current-buffer) 'async files "add" "--refresh" "--")
-       (vc-git-command (current-buffer) 'async nil
-                       "update-index" "--refresh")))
-    (`ls-files-added
-     (vc-git-command (current-buffer) 'async files
-                     "ls-files" "-z" "-c" "-s" "--"))
-    (`ls-files-up-to-date
-     (vc-git-command (current-buffer) 'async files
-                     "ls-files" "-z" "-c" "-s" "--"))
-    (`ls-files-unknown
-     (vc-git-command (current-buffer) 'async files
-                     "ls-files" "-z" "-o" "--directory"
-                     "--no-empty-directory" "--exclude-standard" "--"))
-    (`ls-files-ignored
-     (vc-git-command (current-buffer) 'async files
-                     "ls-files" "-z" "-o" "-i" "--directory"
-                     "--no-empty-directory" "--exclude-standard" "--"))
-    ;; --relative added in Git 1.5.5.
-    (`diff-index
-     (vc-git-command (current-buffer) 'async files
-                     "diff-index" "--relative" "-z" "-M" "HEAD" "--")))
-  (vc-run-delayed
-   (vc-git-after-dir-status-stage stage files update-function)))
+(defun vc-git-dir-status-goto-stage (git-state)
+  (let ((files (vc-git-dir-status-state->files git-state)))
+    (erase-buffer)
+    (pcase (vc-git-dir-status-state->stage git-state)
+      (`update-index
+       (if files
+           (vc-git-command (current-buffer) 'async files "add" "--refresh" "--")
+         (vc-git-command (current-buffer) 'async nil
+                         "update-index" "--refresh")))
+      (`ls-files-added
+       (vc-git-command (current-buffer) 'async files
+                       "ls-files" "-z" "-c" "-s" "--"))
+      (`ls-files-up-to-date
+       (vc-git-command (current-buffer) 'async files
+                       "ls-files" "-z" "-c" "-s" "--"))
+      (`ls-files-conflict
+       (vc-git-command (current-buffer) 'async files
+                       "ls-files" "-z" "-c" "-s" "--"))
+      (`ls-files-unknown
+       (vc-git-command (current-buffer) 'async files
+                       "ls-files" "-z" "-o" "--directory"
+                       "--no-empty-directory" "--exclude-standard" "--"))
+      (`ls-files-ignored
+       (vc-git-command (current-buffer) 'async files
+                       "ls-files" "-z" "-o" "-i" "--directory"
+                       "--no-empty-directory" "--exclude-standard" "--"))
+      ;; --relative added in Git 1.5.5.
+      (`diff-index
+       (vc-git-command (current-buffer) 'async files
+                       "diff-index" "--relative" "-z" "-M" "HEAD" "--")))
+    (vc-run-delayed
+      (vc-git-after-dir-status-stage git-state))))
 
 (defun vc-git-dir-status-files (_dir files update-function)
   "Return a list of (FILE STATE EXTRA) entries for DIR."
   ;; Further things that would have to be fixed later:
   ;; - how to handle unregistered directories
   ;; - how to support vc-dir on a subdir of the project tree
-  (vc-git-dir-status-goto-stage 'update-index files update-function))
+  (vc-git-dir-status-goto-stage
+   (make-vc-git-dir-status-state :stage 'update-index
+                                 :files files
+                                 :update-function update-function)))
 
 (defvar vc-git-stash-map
   (let ((map (make-sparse-keymap)))





^ permalink raw reply related	[flat|nested] 12+ messages in thread

* bug#25683: semi-working patch
  2017-02-14  1:12         ` Tom Tromey
@ 2017-02-14 14:03           ` Dmitry Gutov
  2017-02-14 21:05             ` Tom Tromey
  0 siblings, 1 reply; 12+ messages in thread
From: Dmitry Gutov @ 2017-02-14 14:03 UTC (permalink / raw)
  To: Tom Tromey; +Cc: 25683

Hey Tom,

On 14.02.2017 03:12, Tom Tromey wrote:

> It was just a spot where I didn't properly update a call.
> This version works.

This is looking good (working, too). Please feel free to install.

However, if you were looking for a more fundamental solution, switching 
to parsing the output of 'git status --porcelain' should be faster and 
ultimately less complex.

See vc-git-conflicted-files for the first example of its usage, and 
https://debbugs.gnu.org/19343 for a related discussion.





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#25683: semi-working patch
  2017-02-14 14:03           ` Dmitry Gutov
@ 2017-02-14 21:05             ` Tom Tromey
  2017-02-15  0:07               ` Dmitry Gutov
  0 siblings, 1 reply; 12+ messages in thread
From: Tom Tromey @ 2017-02-14 21:05 UTC (permalink / raw)
  To: Dmitry Gutov; +Cc: Tom Tromey, 25683

>>>>> "Dmitry" == Dmitry Gutov <dgutov@yandex.ru> writes:

Dmitry> This is looking good (working, too). Please feel free to install.

Thanks!

Dmitry> However, if you were looking for a more fundamental solution,
Dmitry> switching to parsing the output of 'git status --porcelain' should be
Dmitry> faster and ultimately less complex.

I had looked at this a bit but I didn't see how get the permissions info
out of git status --porcelain; and then I found the bug you refer to and
it seemed like it was being dealt with by someone else already; and/or
maybe waiting until --porcelain was considered "old enough"?

Tom





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#25683: semi-working patch
  2017-02-14 21:05             ` Tom Tromey
@ 2017-02-15  0:07               ` Dmitry Gutov
  2017-02-15  4:17                 ` Tom Tromey
  0 siblings, 1 reply; 12+ messages in thread
From: Dmitry Gutov @ 2017-02-15  0:07 UTC (permalink / raw)
  To: Tom Tromey; +Cc: 25683

On 14.02.2017 23:05, Tom Tromey wrote:

> I had looked at this a bit but I didn't see how get the permissions info
> out of git status --porcelain;

Good objection, and I don't know (this info might be in the porcelain=v2 
format, but it was only added in Git 2.12 last year).

If we could fetch that data using just one extra call, that would be 
great, though.

> and then I found the bug you refer to and
> it seemed like it was being dealt with by someone else already;

Someone else is yours truly, so not sure when that happens.

> and/or
> maybe waiting until --porcelain was considered "old enough"?

No, --porcelain is old enough, aside from the problem you mentioned. We 
discussed the --ignored flag which still is "too new", but that's a 
minor problem, only tangentially related.

Anyway, thanks for the fix! --porcelain can wait some more.





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#25683: semi-working patch
  2017-02-15  0:07               ` Dmitry Gutov
@ 2017-02-15  4:17                 ` Tom Tromey
  2017-02-15 13:42                   ` Dmitry Gutov
  2017-05-01 14:33                   ` Dmitry Gutov
  0 siblings, 2 replies; 12+ messages in thread
From: Tom Tromey @ 2017-02-15  4:17 UTC (permalink / raw)
  To: Dmitry Gutov; +Cc: Tom Tromey, 25683

Dmitry> Someone else is yours truly, so not sure when that happens.

Oops, sorry about that.  I didn't read the bug very closely it seems...

Dmitry> Anyway, thanks for the fix! --porcelain can wait some more.

It's on my wish-list, but after a bunch of other things.
I think I look at the per-file state very rarely if at all;
whereas vc-dir is one of my main views when working on a project.

Tom





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#25683: done
  2017-02-10 22:15 bug#25683: 25.1.91; vc-dir shows conflicted files as "edited" Tom Tromey
       [not found] ` <handler.25683.B.148676496228355.ack@debbugs.gnu.org>
@ 2017-02-15  4:18 ` Tom Tromey
  1 sibling, 0 replies; 12+ messages in thread
From: Tom Tromey @ 2017-02-15  4:18 UTC (permalink / raw)
  To: 25683-done

Fix checked in.

Tom





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#25683: semi-working patch
  2017-02-15  4:17                 ` Tom Tromey
@ 2017-02-15 13:42                   ` Dmitry Gutov
  2017-05-01 14:33                   ` Dmitry Gutov
  1 sibling, 0 replies; 12+ messages in thread
From: Dmitry Gutov @ 2017-02-15 13:42 UTC (permalink / raw)
  To: Tom Tromey; +Cc: 25683

On 15.02.2017 06:17, Tom Tromey wrote:

> It's on my wish-list, but after a bunch of other things.

Thanks.

> I think I look at the per-file state very rarely if at all;
> whereas vc-dir is one of my main views when working on a project.

Per-file state also affects the vc-dir view as well (that is the exact 
situation in bug#19343). But I was only suggesting you reimplement 
vc-git-dir-status-files based on 'git status --porcelain'.

The fact that reusing the resulting code in vc-git-status would be very 
easy is just the icing. ;)

Anyway, the file permissions question complicates the issue.





^ permalink raw reply	[flat|nested] 12+ messages in thread

* bug#25683: semi-working patch
  2017-02-15  4:17                 ` Tom Tromey
  2017-02-15 13:42                   ` Dmitry Gutov
@ 2017-05-01 14:33                   ` Dmitry Gutov
  1 sibling, 0 replies; 12+ messages in thread
From: Dmitry Gutov @ 2017-05-01 14:33 UTC (permalink / raw)
  To: Tom Tromey; +Cc: 25683

Hey Tom,

On 15.02.2017 6:17, Tom Tromey wrote:

> Dmitry> Anyway, thanks for the fix! --porcelain can wait some more.
> 
> It's on my wish-list, but after a bunch of other things.
> I think I look at the per-file state very rarely if at all;
> whereas vc-dir is one of my main views when working on a project.

'git status --porcelain' for vc-git-state is now in. Did you have any 
new ideas about getting the file permissions?

I have recently noticed that your patch makes vc-dir-status-files slower.

E.g., in Emacs's own repository checkout, a refresh took a fraction of a 
second before 3fb9f5452fbd0458f90115b0a95151b8e7a482a1, and about 3 
seconds now.





^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2017-05-01 14:33 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-10 22:15 bug#25683: 25.1.91; vc-dir shows conflicted files as "edited" Tom Tromey
     [not found] ` <handler.25683.B.148676496228355.ack@debbugs.gnu.org>
2017-02-11 20:45   ` bug#25683: semi-working patch Tom Tromey
2017-02-11 21:40     ` Tom Tromey
2017-02-12  6:17       ` Tom Tromey
2017-02-14  1:12         ` Tom Tromey
2017-02-14 14:03           ` Dmitry Gutov
2017-02-14 21:05             ` Tom Tromey
2017-02-15  0:07               ` Dmitry Gutov
2017-02-15  4:17                 ` Tom Tromey
2017-02-15 13:42                   ` Dmitry Gutov
2017-05-01 14:33                   ` Dmitry Gutov
2017-02-15  4:18 ` bug#25683: done Tom Tromey

Code repositories for project(s) associated with this public inbox

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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).