>From ba77e001b719ff289cf0b1d3dcffd647af6d175d Mon Sep 17 00:00:00 2001 From: Jonathan Ganc Date: Thu, 30 Mar 2017 23:13:15 -0400 Subject: [PATCH] Use 'git status' in vc-git-status --- lisp/vc/vc-git.el | 79 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 26 deletions(-) diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el index 1a3f1bf..ce662fb 100644 --- a/lisp/vc/vc-git.el +++ b/lisp/vc/vc-git.el @@ -231,34 +231,61 @@ (defun vc-git--state-code (code) (?U 'edited) ;; FIXME (?T 'edited))) ;; FIXME +(defun vc-git--git-status-to-vc-state (code-list) + "Convert a list CODE-LIST of two-letter git status strings to a vc status. + +Each element of CODE-LIST comes from the first two characters of +a line returned by 'git status' and should be passed in the order given by 'git status'. Elements that are nil are simply ignored. + +Note that this function may destroy and/or alter `code-list'. + +\(It is necessary to allow CODE-LIST to be a list because +sometimes git status returns multiple lines, e.g. for a file that +is removed from the index but is present in the HEAD and working +tree.) " + (setq code-list (remq nil code-list)) + (pcase code-list + ('nil 'up-to-date) + (`(,code) + (pcase code + ("!!" 'ignored) + ("??" 'unregistered) + ;; have only seen this with a file that is only present in the + ;; index. let us call this `removed' + ("AD" 'removed) + (_ (cond + ((string-match-p "^[ RD]+$" code) 'removed) + ((string-match-p "^[ M]+$" code) 'edited) + ((string-match-p "^[ A]+$" code) 'added) + ((string-match-p "^[ U]+$" code) 'conflict) + (t 'edited))))) + ;; I know of two times when git state returns more than one element, + ;; in both cases returning '("D " "??")': + ;; 1. when a file is removed from the index but present in the + ;; HEAD and working tree, the CODE-LIST will be + ;; 2. when a file A is renamed to B in the index and then back to A + ;; in the working tree, the CODE-LIST will be '("??" "RD") + ;; In both these instances, `unregistered' is a reasonable response. + (`("D " "??") 'unregistered) + ;; In other cases, let us return `edited' + (_ 'edited))) + (defun vc-git-state (file) "Git-specific version of `vc-state'." - ;; FIXME: This can't set 'ignored or 'conflict 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 - ;; 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 - ;; operation. - ;; 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)))) + + (save-match-data + (let* ((default-directory (file-name-directory (expand-file-name file))) + (status + (vc-git--run-command-string file "status" "--porcelain" "-z" + "--untracked-files" "--ignored" "--")) + code-list) + ;; if this code is adapted to parse git-status for a directory, note + ;; that a renamed file takes up two null values and needs to be + ;; treated slightly more carefully + (while (string-match "^\\(..\\)[^\0]+\0\\(\\(?:a\\|[^a]\\)*\\)$" status) + (add-to-list 'code-list (match-string 1 status) t 'ignore) + (setq status (match-string 2 status))) + (vc-git--git-status-to-vc-state code-list)))) (defun vc-git-working-revision (_file) "Git-specific version of `vc-working-revision'." -- 2.9.3