@@ -270,12 +270,22 @@ and passed the value `default-directory'.
270270If any returns non-nil, `diff-hl-update' will run synchronously anyway."
271271 :type '(repeat :tag " Predicate" function))
272272
273+ (defvar diff-hl-reference-revision-projects-cache '()
274+ " Alist of cached directory roots for per-project reference revisions.
275+ Each element in this list has the form (DIR . REV).
276+ DIR is the expanded name of the directory.
277+ REV is the current reference revision." )
278+
273279(defvar diff-hl-reference-revision nil
274280 " Revision to diff against. nil means the most recent one.
275281
276282It can be a relative expression as well, such as \" HEAD^\" with Git, or
277283\" -2\" with Mercurial." )
278284
285+ (put 'diff-hl-reference-revision 'safe-local-variable
286+ (lambda (value )
287+ (or (null value) (stringp value))))
288+
279289(defun diff-hl-define-bitmaps ()
280290 (let* ((scale (if (and (boundp 'text-scale-mode-amount )
281291 (numberp text-scale-mode-amount))
@@ -1257,7 +1267,13 @@ The value of this variable is a mode line template as in
12571267 ; ; Magit versions 2.0-2.3 don't do the above and call this
12581268 ; ; instead, but only when they don't call `revert-buffer' :
12591269 (add-hook 'magit-not-reverted-hook 'diff-hl-update nil t )
1260- (add-hook 'text-scale-mode-hook 'diff-hl-maybe-redefine-bitmaps nil t ))
1270+ (add-hook 'text-scale-mode-hook 'diff-hl-maybe-redefine-bitmaps nil t )
1271+ (when-let* ((rev (map-some
1272+ (lambda (root rev )
1273+ (when (string-prefix-p root default-directory)
1274+ rev))
1275+ diff-hl-reference-revision-projects-cache)))
1276+ (setq-local diff-hl-reference-revision rev)))
12611277 (remove-hook 'after-save-hook 'diff-hl-update t )
12621278 (remove-hook 'after-change-functions 'diff-hl-edit t )
12631279 (remove-hook 'find-file-hook 'diff-hl-update-once t )
@@ -1266,7 +1282,8 @@ The value of this variable is a mode line template as in
12661282 (remove-hook 'magit-not-reverted-hook 'diff-hl-update t )
12671283 (remove-hook 'text-scale-mode-hook 'diff-hl-maybe-redefine-bitmaps t )
12681284 (diff-hl-remove-overlays)
1269- (diff-hl--autohide-margin)))
1285+ (diff-hl--autohide-margin)
1286+ (kill-local-variable 'diff-hl-reference-revision )))
12701287
12711288(defun diff-hl-after-checkin ()
12721289 (let ((fileset (vc-deduce-fileset t )))
@@ -1517,6 +1534,11 @@ CONTEXT-LINES is the size of the unified diff context, defaults to 0."
15171534 " Set the reference revision globally to REV.
15181535When called interactively, REV read with completion.
15191536
1537+ When called with a prefix argument, reset the global reference to the most
1538+ recent one instead. With two prefix arguments, do the same and discard
1539+ every per-project reference created by
1540+ `diff-hl-set-reference-rev-in-project`.
1541+
15201542The default value chosen using one of methods below:
15211543
15221544- In a log view buffer, it uses the revision of current entry.
@@ -1525,42 +1547,159 @@ view buffer.
15251547- In a VC annotate buffer, it uses the revision of current line.
15261548- In other situations, it uses the symbol at point.
15271549
1528- Notice that this sets the reference revision globally, so in
1529- files from other repositories, `diff-hl-mode' will not highlight
1530- changes correctly, until you run `diff-hl-reset-reference-rev' .
1550+ Notice that this sets the reference revision globally, so in files from
1551+ other repositories, `diff-hl-mode' will not highlight changes correctly,
1552+ until you run `diff-hl-reset-reference-rev' . To set the reference on a
1553+ per-project basis, see `diff-hl-set-reference-rev-in-project`.
15311554
15321555Also notice that this will disable `diff-hl-amend-mode' in
15331556buffers that enables it, since `diff-hl-amend-mode' overrides its
15341557effect."
15351558 (interactive
1536- (let* ((def ( or ( and ( equal major-mode 'vc-annotate-mode )
1537- ( car ( vc-annotate-extract-revision-at-line)) )
1538- ( log-view-current-tag )
1539- ( thing-at-point 'symbol t )) )
1540- (prompt ( if def
1541- ( format " Reference revision (default %s ): " def)
1542- " Reference revision: " )) )
1543- ( list ( vc-read-revision prompt nil nil def) )))
1544- ( if rev
1545- ( message " Set reference revision to %s " rev)
1559+ (if current-prefix-arg current-prefix-arg
1560+ ( let* ((def ( or ( and ( equal major-mode ' vc-annotate-mode )
1561+ ( car (vc-annotate-extract-revision-at-line)) )
1562+ ( log-view-current-tag )
1563+ ( thing-at-point 'symbol t )))
1564+ (prompt ( if def
1565+ ( format " Reference revision (default %s ) : " def )
1566+ " Reference revision: " )))
1567+ ( list ( vc-read-revision prompt nil nil def)))))
1568+ ( unless rev
15461569 (user-error " No reference revision specified" ))
1547- (setq diff-hl-reference-revision rev)
1570+ (cond
1571+ ((equal '(4 ) current-prefix-arg)
1572+ ; ; reset global value
1573+ (diff-hl-reset-reference-rev))
1574+ ((equal '(16 ) current-prefix-arg)
1575+ ; ; reset global value and remove per-project value
1576+ (diff-hl-reset-reference-rev '(4 )))
1577+ ; ; change only global value
1578+ (t (setq-default diff-hl-reference-revision rev)
1579+ (unless current-prefix-arg
1580+ (message " Set global reference revision to %s " rev))
1581+ (dolist (buf (buffer-list ))
1582+ (with-current-buffer buf
1583+ (when diff-hl-mode
1584+ (when (bound-and-true-p diff-hl-amend-mode)
1585+ (diff-hl-amend-mode -1 ))
1586+ (when (not (local-variable-p 'diff-hl-reference-revision ))
1587+ (diff-hl-update))))))))
1588+
1589+ ;;;### autoload
1590+ (defun diff-hl-set-reference-rev-in-project (rev )
1591+ " Set the reference revision in the current project to REV.
1592+ When called interactively, REV read with completion.
1593+
1594+ When called with a prefix argument, reset to the global value instead.
1595+
1596+ The default value chosen using one of methods below:
1597+
1598+ - In a log view buffer, it uses the revision of current entry.
1599+ Call `vc-print-log' or `vc-print-root-log' first to open a log
1600+ view buffer.
1601+ - In a VC annotate buffer, it uses the revision of current line.
1602+ - In other situations, it uses the symbol at point.
1603+
1604+ Projects whose reference was set with this command are unaffected by
1605+ subsequent changes to the global reference (see
1606+ `diff-hl-set-reference-rev`).
1607+
1608+ Also notice that this will disable `diff-hl-amend-mode' in
1609+ buffers that enables it, since `diff-hl-amend-mode' overrides its
1610+ effect."
1611+ (interactive
1612+ (if current-prefix-arg current-prefix-arg
1613+ (let* ((def (or (and (equal major-mode 'vc-annotate-mode )
1614+ (car (vc-annotate-extract-revision-at-line)))
1615+ (log-view-current-tag )
1616+ (thing-at-point 'symbol t )))
1617+ (prompt (if def
1618+ (format " Reference revision (default %s ): " def)
1619+ " Reference revision: " )))
1620+ (list (vc-read-revision prompt nil nil def)))))
1621+ (unless rev
1622+ (user-error " No reference revision specified" ))
1623+ (let* ((proj (project-current ))
1624+ (name (project-name proj)))
1625+ (cond
1626+ ; ; reset
1627+ (current-prefix-arg
1628+ (diff-hl-reset-reference-rev-in-project proj))
1629+ ; ; set
1630+ (t
1631+ (diff-hl-set-reference-rev-in-project-internal rev proj)
1632+ (message " Showing changes against %s (project %s ) " rev name)))))
1633+
1634+ (defun diff-hl--project-root (proj )
1635+ ; ; Emacs 26 and 27 don't have `project-root' .
1636+ (expand-file-name
1637+ (or (and (fboundp 'project-root ) (project-root proj))
1638+ (project-roots proj))))
1639+
1640+ (defun diff-hl-set-reference-rev-in-project-internal (rev proj )
1641+ (let* ((root (diff-hl--project-root proj)))
1642+ ; ; newly opened files will share this value
1643+ (setf (alist-get root diff-hl-reference-revision-projects-cache
1644+ nil nil #'string-equal )
1645+ rev)
1646+ ; ; update currently open files
1647+ (dolist (buf (project-buffers proj))
1648+ (with-current-buffer buf
1649+ (when diff-hl-mode
1650+ (when (bound-and-true-p diff-hl-amend-mode)
1651+ (diff-hl-amend-mode -1 ))
1652+ (setq-local diff-hl-reference-revision rev)
1653+ (diff-hl-update))))))
1654+
1655+ ;;;### autoload
1656+ (defun diff-hl-reset-reference-rev (&optional arg )
1657+ " Reset the reference revision globally to the most recent one.
1658+
1659+ When called with a prefix argument, do the same and discard every
1660+ per-project reference created by `diff-hl-set-reference-rev-in-project' ."
1661+ (interactive " P" )
1662+ (setq-default diff-hl-reference-revision nil )
1663+ (when arg
1664+ ; ; reset all cache
1665+ (setq diff-hl-reference-revision-projects-cache nil ))
15481666 (dolist (buf (buffer-list ))
15491667 (with-current-buffer buf
15501668 (when diff-hl-mode
1669+ (when arg
1670+ ; ; reset value in buffers
1671+ (kill-local-variable 'diff-hl-reference-revision ))
15511672 (when (bound-and-true-p diff-hl-amend-mode)
15521673 (diff-hl-amend-mode -1 ))
1553- (diff-hl-update)))))
1554-
1555- ;;;### autoload
1556- (defun diff-hl-reset-reference-rev ()
1557- " Reset the reference revision globally to the most recent one."
1674+ ; ; Don't touch buffers with the local reference (set by
1675+ ; ; `diff-hl-set-reference-rev-in-project' ), when called without a
1676+ ; ; prefix.
1677+ (unless (local-variable-p 'diff-hl-reference-revision )
1678+ (diff-hl-update)))))
1679+ (message " Reference revision reset globally to the most recent revision " ))
1680+
1681+ (defun diff-hl-reset-reference-rev-in-project (&optional proj )
1682+ " Reset the reference revision in the project PROJ to the
1683+ global value.
1684+
1685+ PROJ defaults to the current project."
15581686 (interactive )
1559- (setq diff-hl-reference-revision nil )
1560- (dolist (buf (buffer-list ))
1561- (with-current-buffer buf
1562- (when diff-hl-mode
1563- (diff-hl-update)))))
1687+ (when-let* ((proj (or proj (project-current ))))
1688+ ; ; reset cache for the project
1689+ (setq diff-hl-reference-revision-projects-cache
1690+ (assoc-delete-all (diff-hl--project-root proj)
1691+ diff-hl-reference-revision-projects-cache
1692+ #'string-equal ))
1693+ ; ; reset value in project buffers
1694+ (dolist (buf (project-buffers proj))
1695+ (with-current-buffer buf
1696+ (when diff-hl-mode
1697+ (when (bound-and-true-p diff-hl-amend-mode)
1698+ (diff-hl-amend-mode -1 ))
1699+ (kill-local-variable 'diff-hl-reference-revision )
1700+ (diff-hl-update))))
1701+ (message " Reference revision reset to the global value (project %s ) "
1702+ (project-name proj))))
15641703
15651704;;;### autoload
15661705(define-globalized-minor-mode global-diff-hl-mode diff-hl-mode
0 commit comments