I developed vc-msg which shows "commit message of current line in Emacs". It supports Perforce out of box (you still need p4 login at first.)

My patched emacs-git-gutter show Perforce gutters. You only need setup git-gutter:exp-to-create-diff . Here is a sample,

(setq-local git-gutter:exp-to-create-diff (shell-command-to-string (format "p4 diff -du -db %s" (file-relative-name buffer-file-name))))

I also provided commands like p4edit , p4revert , p4submit , p4diff , and p4history ,

;; {{ perforce utilities (defvar p4-file-to-url '("" "") "(car p4-file-to-url) is the original file prefix (cadr p4-file-to-url) is the url prefix") (defun p4-current-file-url () (replace-regexp-in-string (car p4-file-to-url) (cadr p4-file-to-url) buffer-file-name)) (defun p4-generate-cmd (opts) (format "p4 %s %s" opts (p4-current-file-url))) (defun p4edit () "p4 edit current file." (interactive) (shell-command (p4-generate-cmd "edit")) (read-only-mode -1)) (defun p4submit (&optional file-opened) "p4 submit current file. If FILE-OPENED, current file is still opened." (interactive "P") (let* ((msg (read-string "Say (ENTER to abort):")) (open-opts (if file-opened "-f leaveunchanged+reopen -r" "")) (full-opts (format "submit -d '%s' %s" msg open-opts))) ;; (message "(p4-generate-cmd full-opts)=%s" (p4-generate-cmd full-opts)) (if (string= "" msg) (message "Abort submit.") (shell-command (p4-generate-cmd full-opts)) (unless file-opened (read-only-mode 1)) (message (format "%s submitted." (file-name-nondirectory buffer-file-name)))))) (defun p4revert () "p4 revert current file." (interactive) (shell-command (p4-generate-cmd "revert")) (read-only-mode 1)) (defun p4-show-changelist-patch (line) (let* ((chg (nth 1 (split-string line "[\t ]+"))) (url (p4-current-file-url)) (pattern "^==== //.*====$") sep seps (start 0) (original (if chg (shell-command-to-string (format "p4 describe -du %s" chg)) "")) rlt) (while (setq sep (string-match pattern original start)) (let* ((str (match-string 0 original))) (setq start (+ sep (length str))) (add-to-list 'seps (list sep str) t))) (setq rlt (substring original 0 (car (nth 0 seps)))) (let* ((i 0) found) (while (and (not found) (< i (length seps))) (when (string-match url (cadr (nth i seps))) (setq rlt (concat rlt (substring original (car (nth i seps)) (if (= i (- (length seps) 1)) (length original) (car (nth (+ 1 i) seps)))))) ;; out of loop now since current file patch found (setq found t)) (setq i (+ 1 i)))) ;; remove p4 verbose bullshit (setq rlt (replace-regexp-in-string "^\\(Affected\\|Moved\\) files \.\.\.[\r

]+\\(\.\.\. .*[\r

]+\\)+" "" rlt)) (setq rlt (replace-regexp-in-string "Differences \.\.\.[\r

]+" "" rlt)) ;; one line short description of change list (setq rlt (replace-regexp-in-string "Change \\([0-9]+\\) by \\([^ @]+\\)@[^ @]+ on \\([^ \r

]*\\).*[\r

\t]+\\([^ \t].*\\)" "\\1 by \\2@\\3 \\4" rlt)) rlt)) (defun p4--create-buffer (buf-name content &optional enable-imenu) (let* (rlt-buf) (if (get-buffer buf-name) (kill-buffer buf-name)) (setq rlt-buf (get-buffer-create buf-name)) (save-current-buffer (switch-to-buffer-other-window rlt-buf) (set-buffer rlt-buf) (erase-buffer) (insert content) (diff-mode) (goto-char (point-min)) ;; nice imenu output (if enable-imenu (setq imenu-create-index-function (lambda () (save-excursion (imenu--generic-function '((nil "^[0-9]+ by .*" 0))))))) ;; quit easily in evil-mode (evil-local-set-key 'normal "q" (lambda () (interactive) (quit-window t)))))) (defun p4diff () "Show diff of current file like `git diff'." (interactive) (let* ((content (shell-command-to-string (p4-generate-cmd "diff -du -db")))) (p4--create-buffer "*p4diff*" content))) (defun p4history () "Show history of current file like `git log -p'." (interactive) (let* ((changes (split-string (shell-command-to-string (p4-generate-cmd "changes")) "

")) (content (mapconcat 'p4-show-changelist-patch changes "



"))) (p4--create-buffer "*p4log*" content t))) ;; }}

As a bonus tip, if you use find-file-in-project, insert below code into prog-mode-hook to view any perforce change inside Emacs,

(setq-local ffip-diff-backends '((ivy-read "p4 change to show:" (split-string (shell-command-to-string "p4 changes //depot/development/DIR/PROJ1/...") "

") :action (lambda (i) (if (string-match "^ Change \\([0-9]*\\)" i) (shell-command-to-string (format "p4 describe -du -db %s" (match-string 1 i)))))) "p4 diff -du -db //depot/development/DIR/PROJ1/..."))

You can also check my emacs.d to get latest code.