branch: externals/diff-hl commit 4b860c5ee571429dd0e3210fd50058a29988b98d Author: Dmitry Gutov <dgu...@yandex.ru> Commit: Dmitry Gutov <dgu...@yandex.ru>
Add new option: diff-hl-show-staged-changes Resolves #161 --- diff-hl.el | 131 ++++++++++++++++++++++++++++++++++++--------------- test/diff-hl-test.el | 44 ++++++++++++++++- 2 files changed, 134 insertions(+), 41 deletions(-) diff --git a/diff-hl.el b/diff-hl.el index d643482..aa528bc 100644 --- a/diff-hl.el +++ b/diff-hl.el @@ -167,6 +167,11 @@ performance when viewing such files in certain conditions." (repeat :inline t (symbol :tag "mode")))) :group 'diff-hl) +(defcustom diff-hl-show-staged-changes t + "Whether to include staged changes in the indicators. +Only affects Git, it's the only backend that has staging area." + :type 'boolean) + (defvar diff-hl-reference-revision nil "Revision to diff against. nil means the most recent one.") @@ -279,22 +284,31 @@ performance when viewing such files in certain conditions." ;; Diffing against an older revision. diff-hl-reference-revision)))) +(declare-function vc-git-command "vc-git") + (defun diff-hl-changes-buffer (file backend) - ;; FIXME: To diff against the staging area, call 'git diff-files -p'. (let ((buf-name " *diff-hl* ")) - (condition-case err + (if (and (eq backend 'Git) + (not diff-hl-reference-revision) + (not diff-hl-show-staged-changes)) (diff-hl-with-diff-switches - (vc-call-backend backend 'diff (list file) - diff-hl-reference-revision nil - buf-name)) - (error - ;; https://github.com/dgutov/diff-hl/issues/117 - (when (string-match-p "\\`Failed (status 128)" (error-message-string err)) - (diff-hl-with-diff-switches - (vc-call-backend backend 'diff (list file) - "4b825dc642cb6eb9a060e54bf8d69288fbee4904" - nil - buf-name))))) + (apply #'vc-git-command buf-name 1 + (list file) + "diff-files" + (cons "-p" (vc-switches 'git 'diff)))) + (condition-case err + (diff-hl-with-diff-switches + (vc-call-backend backend 'diff (list file) + diff-hl-reference-revision nil + buf-name)) + (error + ;; https://github.com/dgutov/diff-hl/issues/117 + (when (string-match-p "\\`Failed (status 128)" (error-message-string err)) + (diff-hl-with-diff-switches + (vc-call-backend backend 'diff (list file) + "4b825dc642cb6eb9a060e54bf8d69288fbee4904" + nil + buf-name)))))) buf-name)) (defun diff-hl-changes () @@ -304,33 +318,37 @@ performance when viewing such files in certain conditions." (let ((state (vc-state file backend))) (cond ((diff-hl-modified-p state) - (let* (diff-auto-refine-mode res) - (with-current-buffer (diff-hl-changes-buffer file backend) - (goto-char (point-min)) - (unless (eobp) - (ignore-errors - (diff-beginning-of-hunk t)) - (while (looking-at diff-hunk-header-re-unified) - (let ((line (string-to-number (match-string 3))) - (len (let ((m (match-string 4))) - (if m (string-to-number m) 1))) - (beg (point))) - (diff-end-of-hunk) - (let* ((inserts (diff-count-matches "^\\+" beg (point))) - (deletes (diff-count-matches "^-" beg (point))) - (type (cond ((zerop deletes) 'insert) - ((zerop inserts) 'delete) - (t 'change)))) - (when (eq type 'delete) - (setq len 1) - (cl-incf line)) - (push (list line len type) res)))))) - (nreverse res))) + (diff-hl-changes-from-buffer + (diff-hl-changes-buffer file backend))) ((eq state 'added) `((1 ,(line-number-at-pos (point-max)) insert))) ((eq state 'removed) `((1 ,(line-number-at-pos (point-max)) delete)))))))) +(defun diff-hl-changes-from-buffer (buf) + (with-current-buffer buf + (let* (diff-auto-refine-mode res) + (goto-char (point-min)) + (unless (eobp) + (ignore-errors + (diff-beginning-of-hunk t)) + (while (looking-at diff-hunk-header-re-unified) + (let ((line (string-to-number (match-string 3))) + (len (let ((m (match-string 4))) + (if m (string-to-number m) 1))) + (beg (point))) + (diff-end-of-hunk) + (let* ((inserts (diff-count-matches "^\\+" beg (point))) + (deletes (diff-count-matches "^-" beg (point))) + (type (cond ((zerop deletes) 'insert) + ((zerop inserts) 'delete) + (t 'change)))) + (when (eq type 'delete) + (setq len 1) + (cl-incf line)) + (push (list line len type) res))))) + (nreverse res)))) + (defun diff-hl-update () (let ((changes (diff-hl-changes)) (current-line 1)) @@ -788,11 +806,17 @@ the `diff-program' to be in your `exec-path'." (if (file-directory-p "/dev/shm/") "/dev/shm/" temporary-file-directory)) - (rev (diff-hl-create-revision + (rev + (if (and (eq backend 'Git) + (not diff-hl-reference-revision) + (not diff-hl-show-staged-changes)) + (diff-hl-git-index-revision file - (or diff-hl-reference-revision - (diff-hl-working-revision file backend))))) - ;; FIXME: When against staging, do it differently! + (diff-hl-git-index-object-name file)) + (diff-hl-create-revision + file + (or diff-hl-reference-revision + (diff-hl-working-revision file backend)))))) (diff-no-select rev (current-buffer) "-U 0 --strip-trailing-cr" 'noasync (get-buffer-create dest-buffer)) (with-current-buffer dest-buffer @@ -801,6 +825,35 @@ the `diff-program' to be in your `exec-path'." (delete-matching-lines "^Diff finished.*"))) (get-buffer-create dest-buffer)))) +(defun diff-hl-git-index-object-name (file) + (with-temp-buffer + (vc-git-command (current-buffer) 0 file "ls-files" "-s") + (and + (goto-char (point-min)) + (re-search-forward "^[0-9]+ \\([0-9a-f]+\\)") + (match-string-no-properties 1)))) + +(defun diff-hl-git-index-revision (file object-name) + (let ((filename (diff-hl-make-temp-file-name file + (concat ":" object-name) + 'manual)) + (filebuf (get-file-buffer file))) + (unless (file-exists-p filename) + (with-current-buffer filebuf + (let ((coding-system-for-read 'no-conversion) + (coding-system-for-write 'no-conversion)) + (condition-case nil + (with-temp-file filename + (let ((outbuf (current-buffer))) + ;; Change buffer to be inside the repo. + (with-current-buffer filebuf + (vc-git-command outbuf 0 nil + "cat-file" "blob" object-name)))) + (error + (when (file-exists-p filename) + (delete-file filename))))))) + filename)) + ;;;###autoload (defun turn-on-diff-hl-mode () "Turn on `diff-hl-mode' or `diff-hl-dir-mode' in a buffer if appropriate." diff --git a/test/diff-hl-test.el b/test/diff-hl-test.el index b94c316..4b8563b 100644 --- a/test/diff-hl-test.el +++ b/test/diff-hl-test.el @@ -1,6 +1,6 @@ ;;; diff-hl-test.el --- tests for diff-hl -*- lexical-binding: t -*- -;; Copyright (C) 2020 Free Software Foundation, Inc. +;; Copyright (C) 2020, 2021 Free Software Foundation, Inc. ;; Author: Nathan Moreau <nathan.mor...@m4x.org> @@ -48,7 +48,8 @@ (diff-hl-test-in-source (erase-buffer) (insert diff-hl-test-initial-content) - (save-buffer))) + (save-buffer) + (vc-git-command nil 0 buffer-file-name "reset"))) (defun diff-hl-test-compute-diff-lines () (diff-hl-test-in-source @@ -120,6 +121,45 @@ (should (looking-at "added")) (should-error (diff-hl-next-hunk) :type 'user-error))) +(diff-hl-deftest diff-hl-can-ignore-staged-changes () + (diff-hl-test-in-source + (goto-char (point-min)) + (insert "new line 1\n") + (save-buffer) + (vc-git-command nil 0 buffer-file-name "add") + (goto-char (point-max)) + (insert "new line 2\n") + (save-buffer) + (let ((diff-hl-show-staged-changes t)) + (should + (equal (diff-hl-changes) + '((1 1 insert) + (12 1 insert))))) + (let ((diff-hl-show-staged-changes nil)) + (should + (equal (diff-hl-changes) + '((12 1 insert))))))) + +(diff-hl-deftest diff-hl-flydiff-can-ignore-staged-changes () + (diff-hl-test-in-source + (goto-char (point-min)) + (insert "new line 1\n") + (save-buffer) + (vc-git-command nil 0 buffer-file-name "add") + (goto-char (point-max)) + (insert "new line 2\n") + (let ((diff-hl-show-staged-changes t)) + (should + (equal (diff-hl-changes-from-buffer + (diff-hl-diff-buffer-with-head buffer-file-name nil 'Git)) + '((1 1 insert) + (12 1 insert))))) + (let ((diff-hl-show-staged-changes nil)) + (should + (equal (diff-hl-changes-from-buffer + (diff-hl-diff-buffer-with-head buffer-file-name nil 'Git)) + '((12 1 insert))))))) + (defun diff-hl-run-tests () (ert-run-tests-batch))