emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Git conflict VC state detection


From: Óscar Fuentes
Subject: Git conflict VC state detection
Date: Sun, 10 Nov 2013 21:30:08 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.3.50 (gnu/linux)

Stefan Monnier <address@hidden> writes:

>>> Is it really all it takes to detect `conflict' state?
>> Git conflict state detection worked when I implemented the feature many
>> moons ago, although it was not 100% reliable. It doesn't work anymore.
>> Maybe something was lost on one of the many merges.
>
> Could you try and make it work again and submit/install the patch for that?

I've revised the initial commit implementing conflict state detection on
my private branch and the problem with it was that it didn't detect
edited state on a staged file, i.e. a file containing staged (but
uncommitted) changes would show as up to date.

So I pursued a more precise method. The problem is that the git commands
that tells you that a file is unmerged doesn't tell that a file contains
changes if those are staged. The solution is to execute two git
commands: the first one informs about edited (but unstaged) files plus
unmerged files. If that command says that the file is up to date, a
second command is executed for detecting staged changes.

The problem with this approach is that on some platforms (i.e. Windows)
invoking git is slow. The delay is already noticeable when the modeline
is updated. Adding yet another call makes things somewhat worse.

I CC'ed Dmitry as he implemented the part that my patch touches. I'll
like to get the ok from him before installing the patch, if you (Stefan)
think that conflict detection is valuable enough to warrant an extra git
invocation.

diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el
index 1d67dee..cb500eb 100644
--- a/lisp/vc/vc-git.el
+++ b/lisp/vc/vc-git.el
@@ -204,15 +204,26 @@ matching the resulting Git log output, and KEYWORDS is a 
list of
     (?M 'edited)
     (?A 'added)
     (?D 'removed)
-    (?U 'edited)     ;; FIXME
+    (?U 'conflict)
     (?T 'edited)))   ;; FIXME
 
+(defun vc-git--state-letter (file index-p)
+  (let ((diff (vc-git--run-command-string
+               file
+              "diff-index"
+              ;; How to conditionally add --cached without this kludge?
+              (if index-p "--cached" "-z")
+              "--name-status" "--raw" "-z" "HEAD" "--")))
+    (and diff
+        (string-match "^\\([ADMUT]\\)\0" diff)
+        (match-string 1 diff))))
+
 (defun vc-git-state (file)
   "Git-specific version of `vc-state'."
-  ;; FIXME: This can't set 'ignored or 'conflict yet
+  ;; FIXME: This can't set 'ignored yet.
   ;; The 'ignored state could be detected with `git ls-files -i -o
-  ;; --exclude-standard` It also can't set 'needs-update or
-  ;; 'needs-merge. The rough equivalent would be that upstream branch
+  ;; --exclude-standard` It also can't set 'needs-update.
+  ;; The rough equivalent would be that upstream branch
   ;; for current branch is in fast-forward state i.e. current branch
   ;; is direct ancestor of corresponding upstream branch, and the file
   ;; was modified upstream.  But we can't check that without a network
@@ -220,21 +231,16 @@ matching the resulting Git log output, and KEYWORDS is a 
list of
   ;; This assumes that status is known to be not `unregistered' because
   ;; we've been successfully dispatched here from `vc-state', that
   ;; means `vc-git-registered' returned t earlier once.  Bug#11757
-  (let ((diff (vc-git--run-command-string
-               file "diff-index" "-p" "--raw" "-z" "HEAD" "--")))
-    (if (and diff
-             (string-match ":[0-7]\\{6\\} [0-7]\\{6\\} [0-9a-f]\\{40\\} 
[0-9a-f]\\{40\\} \\([ADMUT]\\)\0[^\0]+\0\\(.*\n.\\)?"
-                           diff))
-        (let ((diff-letter (match-string 1 diff)))
-          (if (not (match-beginning 2))
-              ;; Empty diff: file contents is the same as the HEAD
-              ;; revision, but timestamps are different (eg, file
-              ;; was "touch"ed).  Update timestamp in index:
-              (prog1 'up-to-date
-                (vc-git--call nil "add" "--refresh" "--"
-                              (file-relative-name file)))
-            (vc-git--state-code diff-letter)))
-      (if (vc-git--empty-db-p) 'added 'up-to-date))))
+  (let ((diff-letter (or (vc-git--state-letter file t)
+                        (vc-git--state-letter file nil))))
+    (if (not diff-letter)
+       ;; Empty diff: file contents is the same as the HEAD
+       ;; revision, but timestamps are different (eg, file
+       ;; was "touch"ed).  Update timestamp in index:
+       (prog1 'up-to-date
+         (vc-git--call nil "add" "--refresh" "--"
+                       (file-relative-name file)))
+      (vc-git--state-code diff-letter))))
 
 (defun vc-git-working-revision (file)
   "Git-specific version of `vc-working-revision'."

reply via email to

[Prev in Thread] Current Thread [Next in Thread]