@@ -282,12 +282,22 @@ and passed the value `default-directory'.
282282If any returns non-nil, `diff-hl-update' will run synchronously anyway."
283283 :type '(repeat :tag " Predicate" function))
284284
285+ (defvar diff-hl-reference-revision-projects-cache '()
286+ " Alist of cached directory roots for per-project reference revisions.
287+ Each element in this list has the form (DIR . REV).
288+ DIR is the expanded name of the directory.
289+ REV is the current reference revision." )
290+
285291(defvar diff-hl-reference-revision nil
286292 " Revision to diff against. nil means the most recent one.
287293
288294It can be a relative expression as well, such as \" HEAD^\" with Git, or
289295\" -2\" with Mercurial." )
290296
297+ (put 'diff-hl-reference-revision 'safe-local-variable
298+ (lambda (value )
299+ (or (null value) (stringp value))))
300+
291301(defun diff-hl-define-bitmaps ()
292302 (let* ((scale (if (and (boundp 'text-scale-mode-amount )
293303 (numberp text-scale-mode-amount))
@@ -1282,7 +1292,13 @@ The value of this variable is a mode line template as in
12821292 ; ; Magit versions 2.0-2.3 don't do the above and call this
12831293 ; ; instead, but only when they don't call `revert-buffer' :
12841294 (add-hook 'magit-not-reverted-hook 'diff-hl-update nil t )
1285- (add-hook 'text-scale-mode-hook 'diff-hl-maybe-redefine-bitmaps nil t ))
1295+ (add-hook 'text-scale-mode-hook 'diff-hl-maybe-redefine-bitmaps nil t )
1296+ (when-let* ((rev (map-some
1297+ (lambda (root rev )
1298+ (when (string-prefix-p root default-directory)
1299+ rev))
1300+ diff-hl-reference-revision-projects-cache)))
1301+ (setq-local diff-hl-reference-revision rev)))
12861302 (remove-hook 'after-save-hook 'diff-hl-update t )
12871303 (remove-hook 'after-change-functions 'diff-hl-edit t )
12881304 (remove-hook 'find-file-hook 'diff-hl-update-once t )
@@ -1291,7 +1307,8 @@ The value of this variable is a mode line template as in
12911307 (remove-hook 'magit-not-reverted-hook 'diff-hl-update t )
12921308 (remove-hook 'text-scale-mode-hook 'diff-hl-maybe-redefine-bitmaps t )
12931309 (diff-hl-remove-overlays)
1294- (diff-hl--autohide-margin)))
1310+ (diff-hl--autohide-margin)
1311+ (kill-local-variable 'diff-hl-reference-revision )))
12951312
12961313(defun diff-hl-after-checkin ()
12971314 (let ((fileset (vc-deduce-fileset t )))
@@ -1556,6 +1573,11 @@ CONTEXT-LINES is the size of the unified diff context, defaults to 0."
15561573 " Set the reference revision globally to REV.
15571574When called interactively, REV read with completion.
15581575
1576+ When called with a prefix argument, reset the global reference to the most
1577+ recent one instead. With two prefix arguments, do the same and discard
1578+ every per-project reference created by
1579+ `diff-hl-set-reference-rev-in-project`.
1580+
15591581The default value chosen using one of methods below:
15601582
15611583- In a log view buffer, it uses the revision of current entry.
@@ -1564,42 +1586,159 @@ view buffer.
15641586- In a VC annotate buffer, it uses the revision of current line.
15651587- In other situations, it uses the symbol at point.
15661588
1567- Notice that this sets the reference revision globally, so in
1568- files from other repositories, `diff-hl-mode' will not highlight
1569- changes correctly, until you run `diff-hl-reset-reference-rev' .
1589+ Notice that this sets the reference revision globally, so in files from
1590+ other repositories, `diff-hl-mode' will not highlight changes correctly,
1591+ until you run `diff-hl-reset-reference-rev' . To set the reference on a
1592+ per-project basis, see `diff-hl-set-reference-rev-in-project`.
15701593
15711594Also notice that this will disable `diff-hl-amend-mode' in
15721595buffers that enables it, since `diff-hl-amend-mode' overrides its
15731596effect."
15741597 (interactive
1575- (let* ((def ( or ( and ( equal major-mode 'vc-annotate-mode )
1576- ( car ( vc-annotate-extract-revision-at-line)) )
1577- ( log-view-current-tag )
1578- ( thing-at-point 'symbol t )) )
1579- (prompt ( if def
1580- ( format " Reference revision (default %s ): " def)
1581- " Reference revision: " )) )
1582- ( list ( vc-read-revision prompt nil nil def) )))
1583- ( if rev
1584- ( message " Set reference revision to %s " rev)
1598+ (if current-prefix-arg current-prefix-arg
1599+ ( let* ((def ( or ( and ( equal major-mode ' vc-annotate-mode )
1600+ ( car (vc-annotate-extract-revision-at-line)) )
1601+ ( log-view-current-tag )
1602+ ( thing-at-point 'symbol t )))
1603+ (prompt ( if def
1604+ ( format " Reference revision (default %s ) : " def )
1605+ " Reference revision: " )))
1606+ ( list ( vc-read-revision prompt nil nil def)))))
1607+ ( unless rev
15851608 (user-error " No reference revision specified" ))
1586- (setq diff-hl-reference-revision rev)
1609+ (cond
1610+ ((equal '(4 ) current-prefix-arg)
1611+ ; ; reset global value
1612+ (diff-hl-reset-reference-rev))
1613+ ((equal '(16 ) current-prefix-arg)
1614+ ; ; reset global value and remove per-project value
1615+ (diff-hl-reset-reference-rev '(4 )))
1616+ ; ; change only global value
1617+ (t (setq-default diff-hl-reference-revision rev)
1618+ (unless current-prefix-arg
1619+ (message " Set global reference revision to %s " rev))
1620+ (dolist (buf (buffer-list ))
1621+ (with-current-buffer buf
1622+ (when diff-hl-mode
1623+ (when (bound-and-true-p diff-hl-amend-mode)
1624+ (diff-hl-amend-mode -1 ))
1625+ (when (not (local-variable-p 'diff-hl-reference-revision ))
1626+ (diff-hl-update))))))))
1627+
1628+ ;;;### autoload
1629+ (defun diff-hl-set-reference-rev-in-project (rev )
1630+ " Set the reference revision in the current project to REV.
1631+ When called interactively, REV read with completion.
1632+
1633+ When called with a prefix argument, reset to the global value instead.
1634+
1635+ The default value chosen using one of methods below:
1636+
1637+ - In a log view buffer, it uses the revision of current entry.
1638+ Call `vc-print-log' or `vc-print-root-log' first to open a log
1639+ view buffer.
1640+ - In a VC annotate buffer, it uses the revision of current line.
1641+ - In other situations, it uses the symbol at point.
1642+
1643+ Projects whose reference was set with this command are unaffected by
1644+ subsequent changes to the global reference (see
1645+ `diff-hl-set-reference-rev`).
1646+
1647+ Also notice that this will disable `diff-hl-amend-mode' in
1648+ buffers that enables it, since `diff-hl-amend-mode' overrides its
1649+ effect."
1650+ (interactive
1651+ (if current-prefix-arg current-prefix-arg
1652+ (let* ((def (or (and (equal major-mode 'vc-annotate-mode )
1653+ (car (vc-annotate-extract-revision-at-line)))
1654+ (log-view-current-tag )
1655+ (thing-at-point 'symbol t )))
1656+ (prompt (if def
1657+ (format " Reference revision (default %s ): " def)
1658+ " Reference revision: " )))
1659+ (list (vc-read-revision prompt nil nil def)))))
1660+ (unless rev
1661+ (user-error " No reference revision specified" ))
1662+ (let* ((proj (project-current ))
1663+ (name (project-name proj)))
1664+ (cond
1665+ ; ; reset
1666+ (current-prefix-arg
1667+ (diff-hl-reset-reference-rev-in-project proj))
1668+ ; ; set
1669+ (t
1670+ (diff-hl-set-reference-rev-in-project-internal rev proj)
1671+ (message " Showing changes against %s (project %s ) " rev name)))))
1672+
1673+ (defun diff-hl--project-root (proj )
1674+ ; ; Emacs 26 and 27 don't have `project-root' .
1675+ (expand-file-name
1676+ (or (and (fboundp 'project-root ) (project-root proj))
1677+ (project-roots proj))))
1678+
1679+ (defun diff-hl-set-reference-rev-in-project-internal (rev proj )
1680+ (let* ((root (diff-hl--project-root proj)))
1681+ ; ; newly opened files will share this value
1682+ (setf (alist-get root diff-hl-reference-revision-projects-cache
1683+ nil nil #'string-equal )
1684+ rev)
1685+ ; ; update currently open files
1686+ (dolist (buf (project-buffers proj))
1687+ (with-current-buffer buf
1688+ (when diff-hl-mode
1689+ (when (bound-and-true-p diff-hl-amend-mode)
1690+ (diff-hl-amend-mode -1 ))
1691+ (setq-local diff-hl-reference-revision rev)
1692+ (diff-hl-update))))))
1693+
1694+ ;;;### autoload
1695+ (defun diff-hl-reset-reference-rev (&optional arg )
1696+ " Reset the reference revision globally to the most recent one.
1697+
1698+ When called with a prefix argument, do the same and discard every
1699+ per-project reference created by `diff-hl-set-reference-rev-in-project' ."
1700+ (interactive " P" )
1701+ (setq-default diff-hl-reference-revision nil )
1702+ (when arg
1703+ ; ; reset all cache
1704+ (setq diff-hl-reference-revision-projects-cache nil ))
15871705 (dolist (buf (buffer-list ))
15881706 (with-current-buffer buf
15891707 (when diff-hl-mode
1708+ (when arg
1709+ ; ; reset value in buffers
1710+ (kill-local-variable 'diff-hl-reference-revision ))
15901711 (when (bound-and-true-p diff-hl-amend-mode)
15911712 (diff-hl-amend-mode -1 ))
1592- (diff-hl-update)))))
1593-
1594- ;;;### autoload
1595- (defun diff-hl-reset-reference-rev ()
1596- " Reset the reference revision globally to the most recent one."
1713+ ; ; Don't touch buffers with the local reference (set by
1714+ ; ; `diff-hl-set-reference-rev-in-project' ), when called without a
1715+ ; ; prefix.
1716+ (unless (local-variable-p 'diff-hl-reference-revision )
1717+ (diff-hl-update)))))
1718+ (message " Reference revision reset globally to the most recent revision " ))
1719+
1720+ (defun diff-hl-reset-reference-rev-in-project (&optional proj )
1721+ " Reset the reference revision in the project PROJ to the
1722+ global value.
1723+
1724+ PROJ defaults to the current project."
15971725 (interactive )
1598- (setq diff-hl-reference-revision nil )
1599- (dolist (buf (buffer-list ))
1600- (with-current-buffer buf
1601- (when diff-hl-mode
1602- (diff-hl-update)))))
1726+ (when-let* ((proj (or proj (project-current ))))
1727+ ; ; reset cache for the project
1728+ (setq diff-hl-reference-revision-projects-cache
1729+ (assoc-delete-all (diff-hl--project-root proj)
1730+ diff-hl-reference-revision-projects-cache
1731+ #'string-equal ))
1732+ ; ; reset value in project buffers
1733+ (dolist (buf (project-buffers proj))
1734+ (with-current-buffer buf
1735+ (when diff-hl-mode
1736+ (when (bound-and-true-p diff-hl-amend-mode)
1737+ (diff-hl-amend-mode -1 ))
1738+ (kill-local-variable 'diff-hl-reference-revision )
1739+ (diff-hl-update))))
1740+ (message " Reference revision reset to the global value (project %s ) "
1741+ (project-name proj))))
16031742
16041743;;;### autoload
16051744(define-globalized-minor-mode global-diff-hl-mode diff-hl-mode
0 commit comments