Remove ‘vc-p4-client’

When I wrote this feature I didn’t realize that this functionality is already
built into Perforce itself. By using the ‘P4CONFIG’ variable you can specify the
name of a file that ‘p4’ will look for that can specify the client (among
others) for that directory and all subdirectories.
This commit is contained in:
Tom Willemse 2021-09-08 00:12:03 -07:00
parent 0f054faa4d
commit 905e88a1bd
Signed by: ryuslash
GPG key ID: 7D5C407B435025C1
4 changed files with 107 additions and 191 deletions

View file

@ -8,10 +8,6 @@ project adheres to [[https://semver.org/spec/v2.0.0.html][Semantic Versioning 2.
* COMMENT Unreleased * COMMENT Unreleased
** ADDED The ~vc-p4-client~ custom variable
This allows users to set a client name to use. The idea is that this lets users set the client to use for a certain directory tree, when they have multiple clients they use.
** CHANGED Enabled ~lexical-binding~ ** CHANGED Enabled ~lexical-binding~
Both =vc-p4.el= and =p4-lowlevel.el= now use lexical binding instead of dynamic binding. Both =vc-p4.el= and =p4-lowlevel.el= now use lexical binding instead of dynamic binding.

View file

@ -27,8 +27,12 @@ pull it into the next generation by making it work on Emacs 26.3 at the moment.
See the [[file:CHANGELOG.org][change log]]. See the [[file:CHANGELOG.org][change log]].
* Usage * Switching workspaces
Customization options: To automatically switch workspaces based on the directory youre in, make sure that you have the [[https://www.perforce.com/manuals/cmdref/Content/CmdRef/P4CONFIG.html][P4CONFIG]] variable set. If this is set, for example to =.p4config=, in any directory that you want to associate with a workspace, add a =.p4config= file and add the following line to it:
- ~vc-p4-client~ :: The client to use to try to connect to Perforce. If you have multiple workspaces in Perforce you can set this as a directory-local variable to make sure you're connecting to the right Perforce workspace. #+begin_example
P4CLIENT=my_client
#+end_example
Where =my_client= is the name of the client you want to use.

View file

@ -377,24 +377,18 @@ Fall back to the value of p4-lowlevel-command-messages."
;; Do NOT need to support "-t". ;; Do NOT need to support "-t".
;; Do NOT need to support the specification of multiple files. ;; Do NOT need to support the specification of multiple files.
(cl-defun p4-lowlevel-add (file &key client) (cl-defun p4-lowlevel-add (file)
"Tell Perforce to add FILE to the repository. "Tell Perforce to add FILE to the repository.
Returns nil or raises an error on failure. If CLIENT is non-nil Returns nil or raises an error on failure."
it is passed along as the Perforce client to use."
(let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "add" file))))
;; Note that because "p4 -s add" has bugs, at least as of p4 99.2, this won't ;; Note that because "p4 -s add" has bugs, at least as of p4 99.2, this won't
;; necessarily detect when the add fails, e.g., because of an attempt to add a ;; necessarily detect when the add fails, e.g., because of an attempt to add a
;; file which already exists in the repository. ;; file which already exists in the repository.
(p4-lowlevel-command-or-error args))) (p4-lowlevel-command-or-error (list "add" file)))
(cl-defun p4-lowlevel-delete (file &key client) (cl-defun p4-lowlevel-delete (file)
"Tell Perforce to delete FILE from the repository. "Tell Perforce to delete FILE from the repository.
Returns nil or raises an error on failure. If CLIENT is non-nil Returns nil or raises an error on failure."
it is passed on as the Perforce client to use." (p4-lowlevel-command-or-error (list "delete" file)))
(let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "delete" file))))
(p4-lowlevel-command-or-error args)))
;; Here's what we need to support from the "p4 change" command, at least for the ;; Here's what we need to support from the "p4 change" command, at least for the
;; time being: ;; time being:
@ -405,7 +399,7 @@ it is passed on as the Perforce client to use."
;; DO need to support "-i". ;; DO need to support "-i".
;; DO need to support specified changelist #'s. ;; DO need to support specified changelist #'s.
(cl-defun p4-lowlevel-change (&key buffer op client) (cl-defun p4-lowlevel-change (&key buffer op)
"Create or edit a P4 changelist from/to BUFFER. "Create or edit a P4 changelist from/to BUFFER.
If optional OP is a number, then the corresponding changelist is If optional OP is a number, then the corresponding changelist is
retrieved into BUFFER, or into a new buffer if BUFFER is nil. If retrieved into BUFFER, or into a new buffer if BUFFER is nil. If
@ -413,13 +407,11 @@ OP is non-nil and not a number, then then BUFFER should contain
an existing changelist which is saved to the database; the number an existing changelist which is saved to the database; the number
of the new or updated changelist is returned. If OP is nil then a of the new or updated changelist is returned. If OP is nil then a
new changelist is retrieved into BUFFER (or a new buffer). The new changelist is retrieved into BUFFER (or a new buffer). The
output buffer is returned. If CLIENT is non-nil it is passed on output buffer is returned."
as the Perforce client to use."
(let* ((input-buffer (if (and op (not (numberp op))) buffer nil)) (let* ((input-buffer (if (and op (not (numberp op))) buffer nil))
(flag-arg (if (or (not op) (numberp op)) "-o" "-i")) (flag-arg (if (or (not op) (numberp op)) "-o" "-i"))
(number-arg (if (numberp op) (list (number-to-string op)))) (number-arg (if (numberp op) (list (number-to-string op))))
(client-args (if client (list "-c" client))) (args (append (list "change" flag-arg) number-arg))
(args (append client-args (list "change" flag-arg) number-arg))
alist info) alist info)
(setq alist (p4-lowlevel-command-or-error args input-buffer nil)) (setq alist (p4-lowlevel-command-or-error args input-buffer nil))
(setq info (p4-lowlevel-info-lines alist)) (setq info (p4-lowlevel-info-lines alist))
@ -436,15 +428,13 @@ as the Perforce client to use."
buffer)))) buffer))))
(cl-defun p4-lowlevel-changes (cl-defun p4-lowlevel-changes
(file-pattern &key output-format rev1 rev2 i-flag l-flag m-val s-val client) (file-pattern &key output-format rev1 rev2 i-flag l-flag m-val s-val)
"Call “p4 changes” command on FILE-PATTERN. "Call “p4 changes” command on FILE-PATTERN.
Optional OUTPUT-FORMAT is as described in Optional OUTPUT-FORMAT is as described in
`p4-lowlevel-command-or-error'. Optionally, limit output to the `p4-lowlevel-command-or-error'. Optionally, limit output to the
revisions between REV1 and REV2. If I-FLAG is non-nil, pass `-i'; revisions between REV1 and REV2. If I-FLAG is non-nil, pass `-i';
if L-FLAG is non-nil, pass `-l'; if M-VAL is non-nil, pass that if L-FLAG is non-nil, pass `-l'; if M-VAL is non-nil, pass that
value with `-m'; if S-VAL is non-nil, pass that value with `-s'. value with `-m'; if S-VAL is non-nil, pass that value with `-s'."
If CLIENT is non-nil it is passed on as the Perforce client to
use."
(setq rev1 (p4-lowlevel-canonicalize-revision rev1) (setq rev1 (p4-lowlevel-canonicalize-revision rev1)
rev2 (p4-lowlevel-canonicalize-revision rev2)) rev2 (p4-lowlevel-canonicalize-revision rev2))
(let ((full-file (let ((full-file
@ -456,10 +446,8 @@ use."
(m-list (if m-val (list "-m" (if (numberp m-val) (m-list (if m-val (list "-m" (if (numberp m-val)
(number-to-string m-val) (number-to-string m-val)
m-val)))) m-val))))
(s-list (if s-val (list "-s" s-val))) (s-list (if s-val (list "-s" s-val))))
(client-args (if client (list "-c" client))))
(p4-lowlevel-command-or-error (append (p4-lowlevel-command-or-error (append
client-args
(list "changes") (list "changes")
i-list l-list m-list s-list i-list l-list m-list s-list
(list full-file)) (list full-file))
@ -475,15 +463,13 @@ use."
;; DO need to support diffing a single file. ;; DO need to support diffing a single file.
;; Do NOT need to support diffing multiple files. ;; Do NOT need to support diffing multiple files.
(cl-defun p4-lowlevel-diff (files &key rev buffer client) (cl-defun p4-lowlevel-diff (files &key rev buffer)
"Run `p4 diff' on FILES at revision REV. "Run `p4 diff' on FILES at revision REV.
Return a buffer containing the results. REV is in the syntax Return a buffer containing the results. REV is in the syntax
described by `p4 help revisions'. If REV is nil, compare the described by `p4 help revisions'. If REV is nil, compare the
client's synced revision to the file on disk. Uses client's synced revision to the file on disk. Uses
`p4-lowlevel-diff-switches' to determine flags to pass to `p4 `p4-lowlevel-diff-switches' to determine flags to pass to `p4
diff'. If optional BUFFER is non-nil, put output in that buffer. diff'. If optional BUFFER is non-nil, put output in that buffer."
If CLIENT is non-nil it is passed on as the Perforce client to
use."
(unless (listp files) (unless (listp files)
(setq files (list files))) (setq files (list files)))
(setq rev (p4-lowlevel-canonicalize-revision rev)) (setq rev (p4-lowlevel-canonicalize-revision rev))
@ -496,22 +482,18 @@ use."
(let* ((file-specs (if rev (let* ((file-specs (if rev
(mapcar (lambda (file) (concat file rev)) files) (mapcar (lambda (file) (concat file rev)) files)
files)) files))
(client-args (if client (list "-c" client))) (diff-args (append (list "diff") p4-lowlevel-diff-switches
(diff-args (append client-args (list "diff") p4-lowlevel-diff-switches
(list "-f" "-t") file-specs)) (list "-f" "-t") file-specs))
(buffer (p4-lowlevel-command-into-buffer diff-args (buffer (p4-lowlevel-command-into-buffer diff-args
(or buffer "diff")))) (or buffer "diff"))))
buffer)) buffer))
(cl-defun p4-lowlevel-diff-s (file flag &key client) (cl-defun p4-lowlevel-diff-s (file flag)
"Run `p4 diff -s' on FILE, using FLAG as the argument to `-s'. "Run `p4 diff -s' on FILE, using FLAG as the argument to `-s'.
Return a list of the matching files. If CLIENT is non-nil its Return a list of the matching files."
passed on as the Perforce client to use."
(p4-lowlevel-items-matching-tag (p4-lowlevel-items-matching-tag
"^info" "^info"
(let* ((client-args (if client (list "-c" client))) (p4-lowlevel-command-or-error (list "diff" (format "-s%s" flag) file))))
(args (append client-args (list "diff" (format "-s%s" flag) file))))
(p4-lowlevel-command-or-error args))))
;; Here's what we need to support from the "p4 diff2" command, at least for the ;; Here's what we need to support from the "p4 diff2" command, at least for the
;; time being: ;; time being:
@ -521,20 +503,18 @@ passed on as the Perforce client to use."
;; DO need to support "-t" (in fact, need to specify it all the time). ;; DO need to support "-t" (in fact, need to specify it all the time).
;; Do NOT need to support "-b". ;; Do NOT need to support "-b".
(cl-defun p4-lowlevel-diff2 (file1 file2 &key rev1 rev2 buffer client) (cl-defun p4-lowlevel-diff2 (file1 file2 &key rev1 rev2 buffer)
"Run `p4 diff2' on FILE1 and FILE2 and return a buffer containing the results. "Run `p4 diff2' on FILE1 and FILE2 and return a buffer containing the results.
If optional REV1 and/or REV2 are non-nil, they specify the If optional REV1 and/or REV2 are non-nil, they specify the
revisions to diff in the syntax described by `p4 help revisions'. revisions to diff in the syntax described by `p4 help revisions'.
If optional BUFFER is non-nil, output goes in that buffer. Uses If optional BUFFER is non-nil, output goes in that buffer. Uses
`p4-lowlevel-diff-switches' to determine flags to pass to `p4 `p4-lowlevel-diff-switches' to determine flags to pass to `p4
diff2'. If CLIENT is non-nil its passed on as the Perforce diff2'."
client to use."
(setq rev1 (p4-lowlevel-canonicalize-revision rev1) (setq rev1 (p4-lowlevel-canonicalize-revision rev1)
rev2 (p4-lowlevel-canonicalize-revision rev2)) rev2 (p4-lowlevel-canonicalize-revision rev2))
(let* ((file1-spec (if rev1 (concat file1 rev1) file1)) (let* ((file1-spec (if rev1 (concat file1 rev1) file1))
(file2-spec (if rev2 (concat file2 rev2) file2)) (file2-spec (if rev2 (concat file2 rev2) file2))
(client-args (if client (list "-c" client))) (diff-args (append (list "diff2") p4-lowlevel-diff-switches
(diff-args (append client-args (list "diff2") p4-lowlevel-diff-switches
(list "-t" file1-spec file2-spec))) (list "-t" file1-spec file2-spec)))
(buffer (p4-lowlevel-command-into-buffer diff-args (buffer (p4-lowlevel-command-into-buffer diff-args
(or buffer "diff")))) (or buffer "diff"))))
@ -547,14 +527,11 @@ client to use."
;; Do NOT need to support "-t". ;; Do NOT need to support "-t".
;; Do NOT need to support the specification of multiple files. ;; Do NOT need to support the specification of multiple files.
(cl-defun p4-lowlevel-edit (file &key client) (cl-defun p4-lowlevel-edit (file)
"Tell Perforce we want to edit FILE. "Tell Perforce we want to edit FILE.
Returns non-nil on success or nil on failure (or raises an Returns non-nil on success or nil on failure (or raises an
error). If CLIENT is non-nil its passed on as the Perforce error)."
client to use." (p4-lowlevel-command-or-error (list "edit" file)))
(let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "edit" file))))
(p4-lowlevel-command-or-error args)))
;; Here's what we need to support from the "p4 filelog" command, at least for ;; Here's what we need to support from the "p4 filelog" command, at least for
;; the time being: ;; the time being:
@ -564,28 +541,22 @@ client to use."
;; Do NOT need to support "-m". ;; Do NOT need to support "-m".
;; Do NOT need to support the specification of multiple files. ;; Do NOT need to support the specification of multiple files.
(cl-defun p4-lowlevel-filelog (file &key buffer long follow-branches limit client) (cl-defun p4-lowlevel-filelog (file &key buffer long follow-branches limit)
"Fetch the p4 log of FILE and return a buffer containing it. "Fetch the p4 log of FILE and return a buffer containing it.
If optional BUFFER is non-nil, put output in that buffer. If If optional BUFFER is non-nil, put output in that buffer. If
optional LONG is non-nil, return long output (i.e., pass the `-l' optional LONG is non-nil, return long output (i.e., pass the `-l'
flag). If optional FOLLOW-BRANCHES is non-nil, include pre-branch flag). If optional FOLLOW-BRANCHES is non-nil, include pre-branch
log entries in output (i.e., pass the `-i' flag). If LIMIT is log entries in output (i.e., pass the `-i' flag). If LIMIT is
non-nil, get only the last LIMIT log entries. If CLIENT is non-nil, get only the last LIMIT log entries."
non-nil it will be passed on as the Perforce client to use."
(let* ((long-flag (if long (list "-l"))) (let* ((long-flag (if long (list "-l")))
(branch-flag (if follow-branches (list "-i"))) (branch-flag (if follow-branches (list "-i")))
(limit-flag (if limit (list "-m" (number-to-string limit)))) (limit-flag (if limit (list "-m" (number-to-string limit))))
(client-args (if client (list "-c" client))) (args (append (list "filelog") long-flag branch-flag limit-flag (list file))))
(args (append client-args (list "filelog") long-flag branch-flag limit-flag (list file))))
(p4-lowlevel-command-into-buffer args (or buffer "log")))) (p4-lowlevel-command-into-buffer args (or buffer "log"))))
(cl-defun p4-lowlevel-opened (file &key client) (cl-defun p4-lowlevel-opened (file)
"Fetch the string returned by running `p4 opened' on FILE. "Fetch the string returned by running `p4 opened' on FILE."
Optional argument CLIENT is passed on to the p4 command as the (p4-lowlevel-command-or-error (list "opened" file) nil 'string))
Perforce client to use for the operation."
(let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "opened" file))))
(p4-lowlevel-command-or-error args nil 'string)))
;; Here's what we need to support from the "p4 fstat" command, at least for the ;; Here's what we need to support from the "p4 fstat" command, at least for the
;; time being: ;; time being:
@ -593,20 +564,17 @@ Perforce client to use for the operation."
;; Do NOT need to support any command-line switches. ;; Do NOT need to support any command-line switches.
;; Do NOT need to support the specification of multiple files. ;; Do NOT need to support the specification of multiple files.
(cl-defun p4-lowlevel-fstat (file &key rev noerror client) (cl-defun p4-lowlevel-fstat (file &key rev noerror)
"Fetch p4 information about FILE (optionally, at REV). "Fetch p4 information about FILE (optionally, at REV).
REV should be in the syntax described by `p4 help revisions'. REV should be in the syntax described by `p4 help revisions'.
Returns a list of field-name/value elements on success, or raises Returns a list of field-name/value elements on success, or raises
an error on failure. If optional third argument NOERROR is true, an error on failure. If optional third argument NOERROR is true,
then returns nil rather than raising an error on failure. If FILE then returns nil rather than raising an error on failure. If FILE
matches multiple files, then returns a list of lists of matches multiple files, then returns a list of lists of
field-name/value elements. Optional argument CLIENT is passed on field-name/value elements."
to the p4 command as the Perforce client to use for the
operation."
(setq rev (p4-lowlevel-canonicalize-revision rev)) (setq rev (p4-lowlevel-canonicalize-revision rev))
(let* ((file-spec (if rev (concat file rev) file)) (let* ((file-spec (if rev (concat file rev) file))
(client-args (when client (list "-c" client))) (args (list "fstat" file-spec))
(args (append client-args (list "fstat" file-spec)))
(alist (p4-lowlevel-re-assoc (alist (p4-lowlevel-re-assoc
"^info" (p4-lowlevel-command-or-error args nil nil noerror))) "^info" (p4-lowlevel-command-or-error args nil nil noerror)))
element line field value values lists) element line field value values lists)
@ -631,13 +599,9 @@ operation."
(car lists) (car lists)
lists))) lists)))
(cl-defun p4-lowlevel-info (&key client) (cl-defun p4-lowlevel-info ()
"Return an alist representing the output of `p4 info'. "Return an alist representing the output of `p4 info'."
Optional argument CLIENT is passed on to the p4 command as the (let* ((base-alist (p4-lowlevel-command-or-error (list "info") nil nil t))
Perforce client to use for the operation."
(let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "info")))
(base-alist (p4-lowlevel-command-or-error args nil nil t))
(info-elements (p4-lowlevel-re-assoc "^info" base-alist)) (info-elements (p4-lowlevel-re-assoc "^info" base-alist))
line tag value info-alist element) line tag value info-alist element)
(while info-elements (while info-elements
@ -650,20 +614,17 @@ Perforce client to use for the operation."
info-alist (cons (cons tag value) info-alist)))) info-alist (cons (cons tag value) info-alist))))
(nreverse info-alist))) (nreverse info-alist)))
(cl-defun p4-lowlevel-print (file &key rev output-format quiet client) (cl-defun p4-lowlevel-print (file &key rev output-format quiet)
"Retrieve the contents of FILE using `p4 print'. "Retrieve the contents of FILE using `p4 print'.
If optional REV is non-nil, retrieve that revision, which should If optional REV is non-nil, retrieve that revision, which should
be in the syntax described by `p4 help revisions'. Optional be in the syntax described by `p4 help revisions'. Optional
OUTPUT-FORMAT is interpreted as described for OUTPUT-FORMAT is interpreted as described for
`p4-lowlevel-command-or-error'. If optional QUIET is non-nil, `p4-lowlevel-command-or-error'. If optional QUIET is non-nil,
then the `-q' flag is passed to `p4 print'. Optional argument then the `-q' flag is passed to `p4 print'."
CLIENT is passed on to the p4 command as the Perforce client to
use for the operation."
(setq rev (p4-lowlevel-canonicalize-revision rev)) (setq rev (p4-lowlevel-canonicalize-revision rev))
(let* ((fullfile (if rev (concat file rev) file)) (let* ((fullfile (if rev (concat file rev) file))
(quiet-args (if quiet (list "-q"))) (quiet-args (if quiet (list "-q")))
(client-args (if client (list "-c" client))) (args (append (list "print") quiet-args (list fullfile))))
(args (append client-args (list "print") quiet-args (list fullfile))))
(p4-lowlevel-command-or-error args nil output-format))) (p4-lowlevel-command-or-error args nil output-format)))
;; Here's what we need to support from the "p4 reopen" command, at least for the ;; Here's what we need to support from the "p4 reopen" command, at least for the
@ -674,14 +635,11 @@ use for the operation."
;; Do NOT need to support "-t". ;; Do NOT need to support "-t".
;; Do NOT need to support the specification of multiple files. ;; Do NOT need to support the specification of multiple files.
(cl-defun p4-lowlevel-reopen (file &key changelist client) (cl-defun p4-lowlevel-reopen (file &key changelist)
"Call `p4 reopen' on FILE. "Call `p4 reopen' on FILE.
Optional CHANGELIST specifies the changelist to which to move it. Optional CHANGELIST specifies the changelist to which to move it."
Optional argument CLIENT is passed on to the p4 command as the (let* ((changelist-args (if changelist (list "-c" changelist)))
Perforce client to use for the operation." (args (append (list "reopen") changelist-args (list file))))
(let* ((client-args (if client (list "-c" client)))
(changelist-args (if changelist (list "-c" changelist)))
(args (append client-args (list "reopen") changelist-args (list file))))
(p4-lowlevel-command-or-error args))) (p4-lowlevel-command-or-error args)))
;; Here's what we need to support from the "p4 resolve" command, at least for ;; Here's what we need to support from the "p4 resolve" command, at least for
@ -695,15 +653,11 @@ Perforce client to use for the operation."
;; Do NOT need to support "-v". ;; Do NOT need to support "-v".
;; Do NOT need to support the specification of multiple files. ;; Do NOT need to support the specification of multiple files.
(cl-defun p4-lowlevel-resolve (file &key client) (cl-defun p4-lowlevel-resolve (file)
"Call `p4 resolve' on FILE. "Call `p4 resolve' on FILE.
Specifies the `-af' and `-t' options to ensure a non-interactive Specifies the `-af' and `-t' options to ensure a non-interactive
resolve. Raises an error if the command fails. Optional argument resolve. Raises an error if the command fails."
CLIENT is passed on to the p4 command as the Perforce client to (p4-lowlevel-command-or-error (list "resolve" "-af" "-t" file)))
use for the operation."
(let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "resolve" "-af" "-t" file))))
(p4-lowlevel-command-or-error args)))
;; Here's what we need to support from the "p4 revert" command, at least for the ;; Here's what we need to support from the "p4 revert" command, at least for the
;; time being: ;; time being:
@ -712,13 +666,9 @@ use for the operation."
;; Do NOT need to support "-c". ;; Do NOT need to support "-c".
;; Do NOT need to support the specification of multiple files. ;; Do NOT need to support the specification of multiple files.
(cl-defun p4-lowlevel-revert (file &key client) (cl-defun p4-lowlevel-revert (file)
"Tell Perforce to revert FILE. "Tell Perforce to revert FILE."
Optional argument CLIENT is passed on to the p4 command as the (p4-lowlevel-command-or-error (list "revert" file)))
Perforce client to use for the operation."
(let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "revert" file))))
(p4-lowlevel-command-or-error args)))
;; Here's what we need to support from the "p4 submit" command, at least for the ;; Here's what we need to support from the "p4 submit" command, at least for the
;; time being: ;; time being:
@ -726,13 +676,9 @@ Perforce client to use for the operation."
;; Only need to support non-interactive use; therefore, only need to ;; Only need to support non-interactive use; therefore, only need to
;; support "p4 submit -i". ;; support "p4 submit -i".
(cl-defun p4-lowlevel-submit (change-spec &key client) (cl-defun p4-lowlevel-submit (change-spec)
"Call `p4 submit' on CHANGE-SPEC, which should be a string or buffer. "Call `p4 submit' on CHANGE-SPEC, which should be a string or buffer."
Optional argument CLIENT is passed on to the p4 command as the (let (buffer)
Perforce client to use for the operation."
(let* ((client-args (if client (list "-c" client)))
(args (append client-args (list "submit" "-i")))
buffer)
(if (bufferp change-spec) (if (bufferp change-spec)
(setq buffer change-spec) (setq buffer change-spec)
(setq buffer (p4-lowlevel-get-buffer-create (setq buffer (p4-lowlevel-get-buffer-create
@ -740,7 +686,7 @@ Perforce client to use for the operation."
(with-current-buffer buffer (with-current-buffer buffer
(erase-buffer) (erase-buffer)
(insert change-spec))) (insert change-spec)))
(p4-lowlevel-command-or-error args buffer))) (p4-lowlevel-command-or-error (list "submit" "-i") buffer)))
;; Here's what we need to support from the "p4 sync" command, at least for the ;; Here's what we need to support from the "p4 sync" command, at least for the
;; time being: ;; time being:
@ -750,33 +696,28 @@ Perforce client to use for the operation."
;; DO need to support the specification of a file revision. ;; DO need to support the specification of a file revision.
;; Do NOT need to support the specification of multiple files. ;; Do NOT need to support the specification of multiple files.
(cl-defun p4-lowlevel-sync (file &key rev force client) (cl-defun p4-lowlevel-sync (file &key rev force)
"Call `p4 sync' for FILE. "Call `p4 sync' for FILE.
If optional REV is specified, use that revision specifier. If If optional REV is specified, use that revision specifier. If
optional FORCE is non-nil, pass the `-f' flag. Optional argument optional FORCE is non-nil, pass the `-f' flag. "
CLIENT is passed on to the p4 command as the Perforce client to
use for the operation."
(setq rev (p4-lowlevel-canonicalize-revision rev)) (setq rev (p4-lowlevel-canonicalize-revision rev))
(let* ((fullfile (if rev (concat file rev) file)) (let* ((fullfile (if rev (concat file rev) file))
(force-args (if force (list "-f"))) (force-args (if force (list "-f")))
(client-args (if client (list "-c" client))) (args (append (list "sync") force-args (list fullfile))))
(args (append client-args (list "sync") force-args (list fullfile))))
(p4-lowlevel-command-or-error args))) (p4-lowlevel-command-or-error args)))
(cl-defun p4-lowlevel-integrate (from-file to-file &key rev1 rev2 force client) (cl-defun p4-lowlevel-integrate (from-file to-file &key rev1 rev2 force)
"Call `p4 integrate' from FROM-FILE to TO-FILE. "Call `p4 integrate' from FROM-FILE to TO-FILE.
Optionally a revision range can be specified by REV1 and REV2, Optionally a revision range can be specified by REV1 and REV2,
forcing the integration (i.e., specifying `-f' to `p4 integrate' forcing the integration (i.e., specifying `-f' to `p4 integrate'
if FORCE is non-nil. Optional argument CLIENT is passed on to the if FORCE is non-nil."
p4 command as the Perforce client to use for the operation."
(setq rev1 (p4-lowlevel-canonicalize-revision rev1) (setq rev1 (p4-lowlevel-canonicalize-revision rev1)
rev2 (p4-lowlevel-canonicalize-revision rev2)) rev2 (p4-lowlevel-canonicalize-revision rev2))
(let* ((force-arg (if force (list "-f"))) (let* ((force-arg (if force (list "-f")))
(from-full (if (or rev1 rev2) (from-full (if (or rev1 rev2)
(format "%s%s,%s" from-file (or rev1 "") (or rev2 "")) (format "%s%s,%s" from-file (or rev1 "") (or rev2 ""))
from-file)) from-file))
(client-args (if client (list "-c" client))) (args (append (list "integrate") force-arg (list from-full to-file))))
(args (append client-args (list "integrate") force-arg (list from-full to-file))))
(p4-lowlevel-command-or-error args))) (p4-lowlevel-command-or-error args)))
(defun p4-lowlevel-client-version (&optional noerror) (defun p4-lowlevel-client-version (&optional noerror)

101
vc-p4.el
View file

@ -103,11 +103,6 @@ a prefix argument."
:type 'string :type 'string
:group 'vc) :group 'vc)
(defcustom vc-p4-client nil
"Specifies the client to use when connecting to Perforce."
:type 'string
:group 'vc)
(defun vc-p4-revision-granularity () (defun vc-p4-revision-granularity ()
"Return file. "Return file.
Perforce has per-file revisions." Perforce has per-file revisions."
@ -124,7 +119,7 @@ Perforce has per-file revisions."
(getenv "P4CONFIG") (getenv "P4CONFIG")
(not (vc-p4-find-p4config (file-name-directory file)))) (not (vc-p4-find-p4config (file-name-directory file))))
nil nil
(let* ((fstat (p4-lowlevel-fstat file :noerror t :client vc-p4-client)) (let* ((fstat (p4-lowlevel-fstat file :noerror t))
(action (cdr (or (assoc "action" fstat) (action (cdr (or (assoc "action" fstat)
(assoc "headAction" fstat))))) (assoc "headAction" fstat)))))
(if (or (not fstat) (if (or (not fstat)
@ -143,7 +138,7 @@ previously fetched. If DONT-COMPARE-NONOPENED is non-nil, don't
compare non-open files to the depot version." compare non-open files to the depot version."
(if (and (not force) (vc-file-getprop file 'vc-p4-did-fstat)) (if (and (not force) (vc-file-getprop file 'vc-p4-did-fstat))
(vc-file-getprop file 'vc-state) (vc-file-getprop file 'vc-state)
(let ((alist (or fstat-list (p4-lowlevel-fstat file :noerror t :client vc-p4-client)))) (let ((alist (or fstat-list (p4-lowlevel-fstat file :noerror t))))
(if (null alist) (if (null alist)
'unregistered 'unregistered
(let* ( (let* (
@ -157,7 +152,7 @@ compare non-open files to the depot version."
((string= action "delete") ((string= action "delete")
'removed) 'removed)
(action (action
(let ((opened (p4-lowlevel-opened file :client vc-p4-client))) (let ((opened (p4-lowlevel-opened file)))
(if (string-match " by \\([^@]+\\)@" opened) (if (string-match " by \\([^@]+\\)@" opened)
(match-string 1 opened) (match-string 1 opened)
(if (equal headRev haveRev) (if (equal headRev haveRev)
@ -165,7 +160,7 @@ compare non-open files to the depot version."
'needs-merge)))) 'needs-merge))))
((and (file-exists-p file) ((and (file-exists-p file)
(not dont-compare-nonopened) (not dont-compare-nonopened)
(p4-lowlevel-diff-s file "e" :client vc-p4-client)) (p4-lowlevel-diff-s file "e"))
'unlocked-changes) 'unlocked-changes)
((or ((or
(equal headRev haveRev) (equal headRev haveRev)
@ -189,12 +184,9 @@ compare non-open files to the depot version."
_FILES is ignored. The UPDATE-FUNCTION is used to process the _FILES is ignored. The UPDATE-FUNCTION is used to process the
results of this function." results of this function."
;; XXX: this should be asynchronous. ;; XXX: this should be asynchronous.
(let* ((vc-p4-client (with-current-buffer (find-file-noselect dir) (let* ((lists (p4-lowlevel-fstat
vc-p4-client))
(lists (p4-lowlevel-fstat
(format "%s/..." (directory-file-name (expand-file-name dir))) (format "%s/..." (directory-file-name (expand-file-name dir)))
:noerror t :noerror t)))
:client vc-p4-client)))
(when (stringp (caar lists)) (when (stringp (caar lists))
(setq lists (list lists))) (setq lists (list lists)))
(dolist (this-list lists) (dolist (this-list lists)
@ -238,7 +230,7 @@ ignored."
(not (equal (vc-file-getprop file 'vc-p4-action) "delete")) (not (equal (vc-file-getprop file 'vc-p4-action) "delete"))
(or (equal state 'up-to-date) (or (equal state 'up-to-date)
(equal state 'needs-update) (equal state 'needs-update)
(p4-lowlevel-diff-s file "r" :client vc-p4-client))))) (p4-lowlevel-diff-s file "r")))))
(defun vc-p4-mode-line-string (file) (defun vc-p4-mode-line-string (file)
"Return string for placement into the mode-line for FILE. "Return string for placement into the mode-line for FILE.
@ -272,7 +264,7 @@ Perforce doesnt support adding a comment to registering a file."
;; before it used to be just a single file. We don't support that ;; before it used to be just a single file. We don't support that
;; interface yet, so just use the first file in the list. ;; interface yet, so just use the first file in the list.
(let* ((file (if (listp files) (car files) files)) (let* ((file (if (listp files) (car files) files))
(fstat (p4-lowlevel-fstat file :noerror t :client vc-p4-client)) (fstat (p4-lowlevel-fstat file :noerror t))
(action (cdr (assoc "action" fstat)))) (action (cdr (assoc "action" fstat))))
(if (string= action "delete") (if (string= action "delete")
(if (yes-or-no-p (if (yes-or-no-p
@ -281,13 +273,13 @@ Perforce doesnt support adding a comment to registering a file."
(if (yes-or-no-p "Preserve current contents? ") (if (yes-or-no-p "Preserve current contents? ")
(let ((tempfile (format "%s.vc-register~" file))) (let ((tempfile (format "%s.vc-register~" file)))
(rename-file file tempfile) (rename-file file tempfile)
(p4-lowlevel-revert file :client vc-p4-client) (p4-lowlevel-revert file)
(delete-file file) (delete-file file)
(rename-file tempfile file)) (rename-file tempfile file))
(p4-lowlevel-revert file :client vc-p4-client)) (p4-lowlevel-revert file))
(p4-lowlevel-edit file :client vc-p4-client)) (p4-lowlevel-edit file))
(error "File %s already opened for delete" file)) (error "File %s already opened for delete" file))
(p4-lowlevel-add file :client vc-p4-client)))) (p4-lowlevel-add file))))
(defun vc-p4-init-revision () (defun vc-p4-init-revision ()
"Return `1', the default initial version for Perforce files." "Return `1', the default initial version for Perforce files."
@ -300,7 +292,7 @@ FILE can point to either a file or a directory."
(getenv "P4CONFIG") (getenv "P4CONFIG")
(not (vc-p4-find-p4config file))) (not (vc-p4-find-p4config file)))
nil nil
(or (p4-lowlevel-fstat file :noerror t :client vc-p4-client) (or (p4-lowlevel-fstat file :noerror t)
(vc-p4-is-in-client (if (file-directory-p file) (vc-p4-is-in-client (if (file-directory-p file)
(file-name-as-directory file) (file-name-as-directory file)
file))))) file)))))
@ -310,8 +302,7 @@ FILE can point to either a file or a directory."
(p4-lowlevel-print file (p4-lowlevel-print file
:rev rev :rev rev
:output-format buffer :output-format buffer
:quiet t :quiet t))
:client vc-p4-client))
(defun vc-p4-checkin (files comment &optional rev) (defun vc-p4-checkin (files comment &optional rev)
"Check FILES into Perforce. "Check FILES into Perforce.
@ -320,10 +311,7 @@ Check in with comment COMMENT. Error if REV is non-nil."
(error "Can't specify revision for Perforce checkin")) (error "Can't specify revision for Perforce checkin"))
(let* (;; XXX: default-directory? this should work for most (all?) cases (let* (;; XXX: default-directory? this should work for most (all?) cases
(default-directory (file-name-directory (car files))) (default-directory (file-name-directory (car files)))
(current-client (change-buffer (p4-lowlevel-change))
(with-current-buffer (find-file-noselect (car files))
vc-p4-client))
(change-buffer (p4-lowlevel-change :client current-client))
(indent-tabs-mode 1) (indent-tabs-mode 1)
insertion-start change-number) insertion-start change-number)
(dolist (file files) (dolist (file files)
@ -342,13 +330,11 @@ Check in with comment COMMENT. Error if REV is non-nil."
(insert "\t" (vc-file-getprop file 'vc-p4-depot-file) "\n")) (insert "\t" (vc-file-getprop file 'vc-p4-depot-file) "\n"))
(setq change-number (p4-lowlevel-change (setq change-number (p4-lowlevel-change
:buffer (current-buffer) :buffer (current-buffer)
:op t :op t))
:client current-client))
(p4-lowlevel-change (p4-lowlevel-change
:buffer (current-buffer) :buffer (current-buffer)
:op change-number :op change-number)
:client current-client) (p4-lowlevel-submit (current-buffer))
(p4-lowlevel-submit (current-buffer) :client current-client)
; Update its properties ; Update its properties
(dolist (file files) (dolist (file files)
(vc-p4-state file nil t))))) (vc-p4-state file nil t)))))
@ -365,8 +351,8 @@ Check in with comment COMMENT. Error if REV is non-nil."
(eq rev t)) (eq rev t))
(setq rev (vc-file-getprop file 'vc-latest-version)))) (setq rev (vc-file-getprop file 'vc-latest-version))))
(if (not (string= rev (vc-file-getprop file 'vc-workfile-version))) (if (not (string= rev (vc-file-getprop file 'vc-workfile-version)))
(p4-lowlevel-sync file :rev rev :client vc-p4-client)) (p4-lowlevel-sync file :rev rev))
(p4-lowlevel-edit file :client vc-p4-client)) (p4-lowlevel-edit file))
(vc-p4-state file nil t)) (vc-p4-state file nil t))
(defun vc-p4-revert (file _contents-done) (defun vc-p4-revert (file _contents-done)
@ -379,10 +365,9 @@ _CONTENTS-DONE is ignored."
;; to use sync instead of revert. ;; to use sync instead of revert.
(p4-lowlevel-sync file (p4-lowlevel-sync file
:rev (vc-workfile-version file) :rev (vc-workfile-version file)
:force t :force t))
:client vc-p4-client))
(t (t
(p4-lowlevel-revert file :client vc-p4-client))) (p4-lowlevel-revert file)))
(if (string= action "add") (if (string= action "add")
(vc-file-clearprops file) (vc-file-clearprops file)
(vc-p4-state file nil t)))) (vc-p4-state file nil t))))
@ -393,9 +378,8 @@ REV1 and REV2 are the revisions to merge together."
(p4-lowlevel-integrate file file (p4-lowlevel-integrate file file
:rev1 rev1 :rev1 rev1
:rev2 rev2 :rev2 rev2
:force t :force t)
:client vc-p4-client) (p4-lowlevel-resolve file)
(p4-lowlevel-resolve file :client vc-p4-client)
(vc-resynch-buffer file t t) (vc-resynch-buffer file t t)
(vc-p4-state file nil t) (vc-p4-state file nil t)
(if (vc-p4-has-unresolved-conflicts-p file) (if (vc-p4-has-unresolved-conflicts-p file)
@ -404,8 +388,8 @@ REV1 and REV2 are the revisions to merge together."
(defun vc-p4-merge-news (file) (defun vc-p4-merge-news (file)
"Get the latest version of FILE from Perforce." "Get the latest version of FILE from Perforce."
(p4-lowlevel-sync file :client vc-p4-client) (p4-lowlevel-sync file)
(p4-lowlevel-resolve file :client vc-p4-client) (p4-lowlevel-resolve file)
(vc-resynch-buffer file t t) (vc-resynch-buffer file t t)
(vc-p4-state file nil t) (vc-p4-state file nil t)
(if (vc-p4-has-unresolved-conflicts-p file) (if (vc-p4-has-unresolved-conflicts-p file)
@ -432,10 +416,8 @@ otherwise this function will raise an error."
(error "Can't specify version when stealing Perforce lock")) (error "Can't specify version when stealing Perforce lock"))
;; Must set default-directory because this is called in a mail send hook and ;; Must set default-directory because this is called in a mail send hook and
;; thus not with the current buffer set to the file being reopened. ;; thus not with the current buffer set to the file being reopened.
(let ((default-directory (file-name-directory file)) (let ((default-directory (file-name-directory file)))
(vc-p4-client (with-current-buffer (find-file file) (p4-lowlevel-reopen file)))
vc-p4-client)))
(p4-lowlevel-reopen file :client vc-p4-client)))
(defun vc-p4-print-log (files &optional buffer shortlog _revision limit) (defun vc-p4-print-log (files &optional buffer shortlog _revision limit)
"Print Perforce log for FILES into BUFFER. "Print Perforce log for FILES into BUFFER.
@ -460,8 +442,7 @@ is ignored. If LIMIT is non-nil only print that many log messages."
(p4-lowlevel-filelog file (p4-lowlevel-filelog file
:buffer (current-buffer) :buffer (current-buffer)
:long (not shortlog) :long (not shortlog)
:limit limit :limit limit)
:client vc-p4-client)
;; Insert the file name at the beginning. ;; Insert the file name at the beginning.
(goto-char (point-min)) (goto-char (point-min))
(insert "File: " (file-name-nondirectory file) "\n")))) (insert "File: " (file-name-nondirectory file) "\n"))))
@ -526,8 +507,7 @@ Limit it to FILES if its non-nil"
:rev1 start-rev :rev1 start-rev
:rev2 end-rev :rev2 end-rev
:l-flag t :l-flag t
:s-val "submitted" :s-val "submitted")))
:client vc-p4-client)))
(if (= (point) (point-min)) t (if (= (point) (point-min)) t
(if (not (= (point) (point-max))) (if (not (= (point) (point-max)))
(insert "\n")) (insert "\n"))
@ -573,8 +553,6 @@ revisions. If BUFF is non-nil output the diff into it, or use the
((stringp buff) (get-buffer-create buff)) ((stringp buff) (get-buffer-create buff))
(t (get-buffer-create "*vc-diff*")))) (t (get-buffer-create "*vc-diff*"))))
(files (if (atom file-or-files) (list file-or-files) file-or-files)) (files (if (atom file-or-files) (list file-or-files) file-or-files))
(vc-p4-client (with-current-buffer (find-file (car files))
vc-p4-client))
(inhibit-read-only t)) (inhibit-read-only t))
(cond (cond
((and (null rev1) (null rev2)) ((and (null rev1) (null rev2))
@ -611,8 +589,7 @@ revisions. If BUFF is non-nil output the diff into it, or use the
(with-temp-buffer (with-temp-buffer
(p4-lowlevel-print file (p4-lowlevel-print file
:output-format (current-buffer) :output-format (current-buffer)
:quiet t :quiet t)
:client vc-p4-client)
(goto-char (point-min)) (goto-char (point-min))
(while (search-forward-regexp "^text: " nil t) (while (search-forward-regexp "^text: " nil t)
(replace-match "" nil nil)) (replace-match "" nil nil))
@ -638,7 +615,7 @@ revisions. If BUFF is non-nil output the diff into it, or use the
(let (temp-buffer) (let (temp-buffer)
(unwind-protect (unwind-protect
(progn (progn
(setq temp-buffer (p4-lowlevel-diff modified :client vc-p4-client)) (setq temp-buffer (p4-lowlevel-diff modified))
(insert-buffer-substring temp-buffer)) (insert-buffer-substring temp-buffer))
(when (buffer-live-p temp-buffer) (when (buffer-live-p temp-buffer)
(kill-buffer temp-buffer)))))))) (kill-buffer temp-buffer))))))))
@ -655,15 +632,13 @@ revisions. If BUFF is non-nil output the diff into it, or use the
((and (not rev1) rev2) ((and (not rev1) rev2)
(p4-lowlevel-diff2 file file (p4-lowlevel-diff2 file file
:rev1 (vc-file-getprop file 'vc-workfile-version) :rev1 (vc-file-getprop file 'vc-workfile-version)
:rev2 rev2 :rev2 rev2))
:client vc-p4-client))
((and rev1 rev2) ((and rev1 rev2)
(p4-lowlevel-diff2 file file (p4-lowlevel-diff2 file file
:rev1 rev1 :rev1 rev1
:rev2 rev2 :rev2 rev2))
:client vc-p4-client))
((and rev1 (not rev2)) ((and rev1 (not rev2))
(p4-lowlevel-diff file :rev rev1 :client vc-p4-client)))) (p4-lowlevel-diff file :rev rev1))))
(insert-buffer-substring temp-buffer) (insert-buffer-substring temp-buffer)
(kill-buffer temp-buffer)))))) (kill-buffer temp-buffer))))))
@ -946,7 +921,7 @@ If DIRNAME is not specified, uses `default-directory'."
(defun vc-p4-is-in-client (file) (defun vc-p4-is-in-client (file)
"Return non-nil if FILE is inside the p4 client hierarchy." "Return non-nil if FILE is inside the p4 client hierarchy."
(let* ((default-directory (file-name-directory file)) (let* ((default-directory (file-name-directory file))
(info (p4-lowlevel-info :client vc-p4-client)) (info (p4-lowlevel-info))
(root (alist-get "Client root" info nil nil #'string=)) (root (alist-get "Client root" info nil nil #'string=))
(cwd (alist-get "Current directory" info nil nil #'string=))) (cwd (alist-get "Current directory" info nil nil #'string=)))
(string-prefix-p root cwd))) (string-prefix-p root cwd)))
@ -1029,7 +1004,7 @@ documentation for that command for their meanings."
(defun vc-p4-delete-file (file) (defun vc-p4-delete-file (file)
"Tell perforce to delete FILE from the repository." "Tell perforce to delete FILE from the repository."
(p4-lowlevel-delete file :client vc-p4-client)) (p4-lowlevel-delete file))
(defun vc-p4-switch-client (client) (defun vc-p4-switch-client (client)
"Switch to CLIENT as the current client used for all operations." "Switch to CLIENT as the current client used for all operations."
@ -1048,7 +1023,7 @@ documentation for that command for their meanings."
(defun vc-p4-dir-extra-headers (dir) (defun vc-p4-dir-extra-headers (dir)
"Get extra Perforce-specific vc-dir headers related to DIR." "Get extra Perforce-specific vc-dir headers related to DIR."
(let ((extra-info (p4-lowlevel-info :client vc-p4-client))) (let ((extra-info (p4-lowlevel-info)))
(concat (concat
(propertize "Client :" 'face 'font-lock-type-face) (propertize "Client :" 'face 'font-lock-type-face)
" " " "