2021-01-15 08:12:30 +01:00
|
|
|
|
;;; vc-p4.el --- Integrate Perforce support into VC mode in Emacs 21 -*- lexical-binding: t; -*-
|
2002-01-06 04:45:11 +01:00
|
|
|
|
|
2010-01-13 17:57:06 +01:00
|
|
|
|
;; Copyright (C) 2009, 2010 Magnus Henoch
|
2002-01-06 04:45:11 +01:00
|
|
|
|
;; Copyright (C) 2002 Curl Corporation.
|
|
|
|
|
|
|
|
|
|
;; Author: Jonathan Kamens <jik@kamens.brookline.ma.us>
|
2009-05-27 13:11:58 +02:00
|
|
|
|
;; Maintainer: Magnus Henoch <magnus.henoch@gmail.com>
|
2002-01-06 04:45:11 +01:00
|
|
|
|
|
|
|
|
|
;; $Id$
|
2002-01-06 04:55:28 +01:00
|
|
|
|
;; The path above is on the Perforce server public.perforce.com:1666.
|
|
|
|
|
;; You can get this file using a P4 client talking to that depot, or
|
|
|
|
|
;; from the URL
|
2009-05-27 13:11:58 +02:00
|
|
|
|
;; http://public.perforce.com/guest/magnus_henoch/vc-p4/vc-p4.el.
|
|
|
|
|
|
|
|
|
|
;; This version is intended for Emacs 23 and later; for Emacs 22 or
|
|
|
|
|
;; earlier, you might want the original version:
|
2002-01-06 04:55:28 +01:00
|
|
|
|
;; http://public.perforce.com/guest/jonathan_kamens/vc-p4/vc-p4.el.
|
2002-01-06 04:45:11 +01:00
|
|
|
|
|
|
|
|
|
;; This program is free software; you can redistribute it and/or modify
|
|
|
|
|
;; it under the terms of the GNU General Public License as published by
|
|
|
|
|
;; the Free Software Foundation; either version 2, or (at your option)
|
|
|
|
|
;; any later version.
|
|
|
|
|
;;
|
|
|
|
|
;; This program is distributed in the hope that it will be useful,
|
|
|
|
|
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
|
;; GNU General Public License for more details.
|
|
|
|
|
;;
|
|
|
|
|
;; You should have received a copy of the GNU General Public License
|
|
|
|
|
;; along with this program; if not, you can either send email to this
|
|
|
|
|
;; program's maintainer or write to: The Free Software Foundation,
|
|
|
|
|
;; Inc.; 59 Temple Place, Suite 330; Boston, MA 02111-1307, USA.
|
|
|
|
|
|
|
|
|
|
;;; Commentary:
|
|
|
|
|
|
2009-07-20 23:07:17 +02:00
|
|
|
|
;; This file adds support for Perforce to VC mode in Emacs 23 and any
|
|
|
|
|
;; other Emacs variant which uses the VC mode included in Emacs 23.
|
2002-01-06 04:45:11 +01:00
|
|
|
|
;;
|
|
|
|
|
;; To use this file, you also need p4-lowlevel.el somewhere in your
|
|
|
|
|
;; load path (or load it explicitly with "load" if it's not in your
|
|
|
|
|
;; load path).
|
|
|
|
|
;;
|
|
|
|
|
;; The easiest way to use this functionality is to put this file
|
|
|
|
|
;; somewhere in your load-path and put this in one of your init files:
|
|
|
|
|
;;
|
|
|
|
|
;; (require 'vc-p4)
|
|
|
|
|
;;
|
|
|
|
|
;; Alternatively, as long as this file is in your load path, you can
|
|
|
|
|
;; customize the variable vc-handled-backends and add "P4" to it; this
|
|
|
|
|
;; will cause VC to load this file automatically when it is needed.
|
|
|
|
|
;;
|
|
|
|
|
;; You can't use this support and the full functionality of Rajesh
|
|
|
|
|
;; Vaidheeswarran's "p4.el" at the same time. You can, however, use
|
2002-01-06 05:23:05 +01:00
|
|
|
|
;; much of p4.el's functionality by setting the customization variable
|
2002-01-06 04:45:11 +01:00
|
|
|
|
;; "p4-do-find-file" to nil. This will prevent p4.el from attempting
|
|
|
|
|
;; to "take ownership" of a Perforce file when you load it, but it
|
|
|
|
|
;; will allow you to use all of the p4.el commands that don't apply to
|
|
|
|
|
;; the current buffer.
|
|
|
|
|
|
|
|
|
|
;;; Code:
|
|
|
|
|
|
2021-02-28 00:11:22 +01:00
|
|
|
|
(require 'log-edit)
|
2022-03-14 05:25:57 +01:00
|
|
|
|
(require 'vc-dir)
|
2021-02-28 00:11:22 +01:00
|
|
|
|
|
2015-03-10 23:43:47 +01:00
|
|
|
|
(eval-and-compile
|
|
|
|
|
(if (not (string-match "XEmacs" emacs-version))
|
2021-02-28 00:11:22 +01:00
|
|
|
|
(require 'vc-annotate))
|
2002-01-09 15:15:24 +01:00
|
|
|
|
(require 'vc-hooks)
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(require 'vc)
|
2007-07-18 22:14:09 +02:00
|
|
|
|
(require 'ediff))
|
|
|
|
|
;; FIXME: setq ediff-quit-hook maybe should be add-hook...
|
|
|
|
|
|
|
|
|
|
(eval-and-compile
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(require 'p4-lowlevel))
|
|
|
|
|
|
|
|
|
|
(if (not (memq 'P4 vc-handled-backends))
|
|
|
|
|
(setq vc-handled-backends (cons 'P4 vc-handled-backends)))
|
2020-05-13 20:29:35 +02:00
|
|
|
|
;; This is useful during development to ensure that we can simply reeval this
|
|
|
|
|
;; buffer to get any new functions that have been added.
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(put 'P4 'vc-functions nil)
|
|
|
|
|
|
|
|
|
|
(defcustom vc-p4-require-p4config nil
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Flag indicating the ‘P4CONFIG’ environment variable is required.
|
|
|
|
|
If non-nil and the ‘P4CONFIG’ environment variable is set, then
|
|
|
|
|
only perform p4 operations on a file when a P4CONFIG file can be
|
|
|
|
|
found in one of its parent directories. This is useful if P4
|
|
|
|
|
operations are expensive to start, e.g., if you are connect to
|
|
|
|
|
the network over a slow dial-up connection and/or using a SSH
|
|
|
|
|
tunnel for P4 access. To avoid delays when opening non-P4 files,
|
|
|
|
|
simply set P4CONFIG as described in the Perforce documentation,
|
|
|
|
|
create an empty P4CONFIG file at the root of your client
|
|
|
|
|
workspace, and set ‘vc-p4-require-p4config’ to t."
|
2002-01-06 04:45:11 +01:00
|
|
|
|
:type 'boolean
|
|
|
|
|
:group 'vc)
|
|
|
|
|
|
2007-08-02 04:27:55 +02:00
|
|
|
|
(defcustom vc-p4-annotate-command nil
|
2017-06-07 09:03:49 +02:00
|
|
|
|
"*Specifies the name of a command to call to annotate Perforce files.
|
2021-02-27 23:09:44 +01:00
|
|
|
|
If nil, then `vc-p4-annotate-command-internal' will be used. I
|
|
|
|
|
recommend //guest/jonathan_kamens/p4pr.perl in the Perforce
|
|
|
|
|
repository public.perforce.com:1666. Note that you need a version
|
|
|
|
|
of this script which accept `--after=date', if you want to be
|
|
|
|
|
able to specify a starting date when you run \\[vc-annotate] with
|
|
|
|
|
a prefix argument."
|
2002-01-06 04:45:11 +01:00
|
|
|
|
:type 'string
|
|
|
|
|
:group 'vc)
|
|
|
|
|
|
2017-06-07 09:05:59 +02:00
|
|
|
|
(defun vc-p4-revision-granularity ()
|
|
|
|
|
"Return file.
|
|
|
|
|
Perforce has per-file revisions."
|
|
|
|
|
'file)
|
|
|
|
|
|
2007-08-02 04:27:55 +02:00
|
|
|
|
(defun vc-p4-create-repo ()
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"This function is not supported."
|
|
|
|
|
(error "The create-repo function isn’t supported yet for P4"))
|
2007-08-02 04:27:55 +02:00
|
|
|
|
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(defun vc-p4-registered (file)
|
|
|
|
|
"Return non-nil is FILE is handled by Perforce."
|
2015-03-10 23:43:47 +01:00
|
|
|
|
(if (p4-lowlevel-locate-p4)
|
2017-06-07 21:34:06 +02:00
|
|
|
|
(if (and vc-p4-require-p4config
|
|
|
|
|
(getenv "P4CONFIG")
|
|
|
|
|
(not (vc-p4-find-p4config (file-name-directory file))))
|
|
|
|
|
nil
|
2021-09-08 09:12:03 +02:00
|
|
|
|
(let* ((fstat (p4-lowlevel-fstat file :noerror t))
|
2017-06-07 21:34:06 +02:00
|
|
|
|
(action (cdr (or (assoc "action" fstat)
|
|
|
|
|
(assoc "headAction" fstat)))))
|
|
|
|
|
(if (or (not fstat)
|
|
|
|
|
(string= action "delete"))
|
|
|
|
|
nil
|
|
|
|
|
;; This sets a bunch of VC properties
|
|
|
|
|
(vc-p4-state file fstat)
|
|
|
|
|
t)))))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
|
2009-05-27 19:54:01 +02:00
|
|
|
|
(defun vc-p4-state (file &optional fstat-list force dont-compare-nonopened)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Return the current version control state of FILE in Perforce.
|
2009-05-27 19:54:01 +02:00
|
|
|
|
If optional FSTAT-LIST is non-nil, use that list of attributes
|
|
|
|
|
from p4-lowlevel-fstat instead of calling it. If optional FORCE
|
2021-02-27 23:09:44 +01:00
|
|
|
|
is non-nil, re-fetch all properties even if properties were
|
2009-05-27 19:54:01 +02:00
|
|
|
|
previously fetched. If DONT-COMPARE-NONOPENED is non-nil, don't
|
|
|
|
|
compare non-open files to the depot version."
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(if (and (not force) (vc-file-getprop file 'vc-p4-did-fstat))
|
|
|
|
|
(vc-file-getprop file 'vc-state)
|
2021-09-08 09:12:03 +02:00
|
|
|
|
(let ((alist (or fstat-list (p4-lowlevel-fstat file :noerror t))))
|
2009-09-09 16:16:58 +02:00
|
|
|
|
(if (null alist)
|
|
|
|
|
'unregistered
|
|
|
|
|
(let* (
|
|
|
|
|
(headRev (cdr (assoc "headRev" alist)))
|
|
|
|
|
(haveRev (cdr (assoc "haveRev" alist)))
|
|
|
|
|
(depotFile (cdr (assoc "depotFile" alist)))
|
|
|
|
|
(action (cdr (assoc "action" alist)))
|
|
|
|
|
(headAction (cdr (assoc "headAction" alist)))
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(state
|
2009-09-09 16:16:58 +02:00
|
|
|
|
(cond
|
2010-08-09 17:53:27 +02:00
|
|
|
|
((string= action "delete")
|
|
|
|
|
'removed)
|
2009-09-09 16:16:58 +02:00
|
|
|
|
(action
|
2021-09-08 09:12:03 +02:00
|
|
|
|
(let ((opened (p4-lowlevel-opened file)))
|
2009-09-09 16:16:58 +02:00
|
|
|
|
(if (string-match " by \\([^@]+\\)@" opened)
|
|
|
|
|
(match-string 1 opened)
|
|
|
|
|
(if (equal headRev haveRev)
|
2010-08-09 19:58:53 +02:00
|
|
|
|
(if (string= action "add") 'added 'edited)
|
2009-09-09 16:16:58 +02:00
|
|
|
|
'needs-merge))))
|
2010-08-09 18:46:04 +02:00
|
|
|
|
((and (file-exists-p file)
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(not dont-compare-nonopened)
|
2021-09-08 09:12:03 +02:00
|
|
|
|
(p4-lowlevel-diff-s file "e"))
|
2009-09-09 16:16:58 +02:00
|
|
|
|
'unlocked-changes)
|
|
|
|
|
((or
|
|
|
|
|
(equal headRev haveRev)
|
2022-03-15 07:47:29 +01:00
|
|
|
|
(and (null haveRev)
|
|
|
|
|
(or (string= headAction "delete")
|
|
|
|
|
(string= headAction "move/delete"))))
|
2009-09-09 16:16:58 +02:00
|
|
|
|
'up-to-date)
|
|
|
|
|
(t
|
2021-01-29 01:18:02 +01:00
|
|
|
|
'needs-update))))
|
2009-09-09 16:16:58 +02:00
|
|
|
|
(vc-file-setprop file 'vc-p4-did-fstat t)
|
|
|
|
|
(vc-file-setprop file 'vc-p4-depot-file depotFile)
|
|
|
|
|
(vc-file-setprop file 'vc-p4-action action)
|
|
|
|
|
(vc-file-setprop file 'vc-backend 'P4)
|
|
|
|
|
(vc-file-setprop file 'vc-checkout-model 'announce)
|
|
|
|
|
(vc-file-setprop file 'vc-latest-version headRev)
|
|
|
|
|
(vc-file-setprop file 'vc-name file)
|
|
|
|
|
(vc-file-setprop file 'vc-state state)
|
|
|
|
|
(vc-file-setprop file 'vc-workfile-version haveRev)
|
|
|
|
|
state)))))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
|
2021-02-28 00:11:22 +01:00
|
|
|
|
(defun vc-p4-dir-status-files (dir _files update-function)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Find status information for files in the directory DIR.
|
2021-02-28 00:11:22 +01:00
|
|
|
|
_FILES is ignored. The UPDATE-FUNCTION is used to process the
|
2021-02-27 23:09:44 +01:00
|
|
|
|
results of this function."
|
2009-05-27 15:53:57 +02:00
|
|
|
|
;; XXX: this should be asynchronous.
|
2021-09-08 09:12:03 +02:00
|
|
|
|
(let* ((lists (p4-lowlevel-fstat
|
2021-03-11 03:10:16 +01:00
|
|
|
|
(format "%s/..." (directory-file-name (expand-file-name dir)))
|
2021-09-08 09:12:03 +02:00
|
|
|
|
:noerror t)))
|
2009-05-27 15:53:57 +02:00
|
|
|
|
(when (stringp (caar lists))
|
|
|
|
|
(setq lists (list lists)))
|
|
|
|
|
(dolist (this-list lists)
|
2009-05-27 19:54:01 +02:00
|
|
|
|
(let* ((this-file (cdr (assoc "clientFile" this-list)))
|
2022-03-14 05:25:57 +01:00
|
|
|
|
(type (cdr (assoc "headType" this-list)))
|
2009-05-27 19:54:01 +02:00
|
|
|
|
(state (vc-p4-state this-file this-list t t)))
|
2009-08-27 14:14:59 +02:00
|
|
|
|
(unless (eq state 'up-to-date)
|
|
|
|
|
(funcall update-function
|
|
|
|
|
(list
|
2022-03-14 05:25:57 +01:00
|
|
|
|
(list (file-relative-name this-file dir) state type))
|
2009-08-27 14:14:59 +02:00
|
|
|
|
t))))
|
2009-05-27 15:53:57 +02:00
|
|
|
|
(funcall update-function nil nil)))
|
|
|
|
|
|
2009-06-05 21:51:36 +02:00
|
|
|
|
(defun vc-p4-working-revision (file)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Return the Perforce version of FILE."
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(vc-p4-state file)
|
|
|
|
|
(vc-file-getprop file 'vc-workfile-version))
|
|
|
|
|
|
2021-02-28 00:11:22 +01:00
|
|
|
|
(defun vc-p4-previous-revision (_file rev)
|
|
|
|
|
"Return the previous revision before REV.
|
|
|
|
|
_FILE is ignored."
|
2010-01-13 18:01:58 +01:00
|
|
|
|
(let ((newrev (1- (string-to-number rev))))
|
|
|
|
|
(when (< 0 newrev)
|
|
|
|
|
(number-to-string newrev))))
|
|
|
|
|
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(defun vc-p4-latest-on-branch-p (file)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Return non-nil if the Perforce version of FILE is the head revision."
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(vc-p4-state file)
|
|
|
|
|
(string= (vc-file-getprop file 'vc-latest-version)
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(vc-file-getprop file 'vc-workfile-version)))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
|
2021-02-28 00:11:22 +01:00
|
|
|
|
(defun vc-p4-checkout-model (_file)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Return the checkout model for Perforce (`announce').
|
2021-02-28 00:11:22 +01:00
|
|
|
|
Perforce only has one checkout model for all files, so _FILE is
|
|
|
|
|
ignored."
|
2002-01-06 04:45:11 +01:00
|
|
|
|
'announce)
|
|
|
|
|
|
|
|
|
|
(defun vc-p4-workfile-unchanged-p (file)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Return non-nil if FILE is unchanged from the version in Perforce."
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(let ((state (vc-p4-state file)))
|
|
|
|
|
(and (not (equal (vc-file-getprop file 'vc-p4-action) "add"))
|
2010-08-09 18:35:34 +02:00
|
|
|
|
(not (equal (vc-file-getprop file 'vc-p4-action) "delete"))
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(or (equal state 'up-to-date)
|
2021-01-29 01:18:02 +01:00
|
|
|
|
(equal state 'needs-update)
|
2021-09-08 09:12:03 +02:00
|
|
|
|
(p4-lowlevel-diff-s file "r")))))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
|
|
|
|
|
(defun vc-p4-mode-line-string (file)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Return string for placement into the mode-line for FILE.
|
2002-01-06 04:45:11 +01:00
|
|
|
|
Compared to the default implementation, this function handles the
|
|
|
|
|
special case of a Perforce file that is added but not yet committed."
|
|
|
|
|
(let ((state (vc-state file))
|
2021-09-08 09:22:54 +02:00
|
|
|
|
(rev (vc-working-revision file)))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(if (or (not rev) (string= rev "0"))
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(setq rev "@@"))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(cond ((or (eq state 'up-to-date)
|
2021-01-29 01:18:02 +01:00
|
|
|
|
(eq state 'needs-update))
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(concat "P4-" rev))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
((stringp state)
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(concat "P4:" state ":" rev))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(t
|
|
|
|
|
;; Not just for the 'edited state, but also a fallback
|
|
|
|
|
;; for all other states. Think about different symbols
|
2021-01-29 01:18:02 +01:00
|
|
|
|
;; for 'needs-update and 'needs-merge.
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(concat "P4:" rev)))))
|
|
|
|
|
|
2007-08-02 04:27:55 +02:00
|
|
|
|
(defun vc-p4-register (files &optional rev comment)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Register FILES with Perforce.
|
|
|
|
|
REV can only be 1, Perforce doesn’t support registering at any
|
|
|
|
|
other revision. COMMENT can only be nil or the empty string since
|
|
|
|
|
Perforce doesn’t support adding a comment to registering a file."
|
2002-01-09 15:15:24 +01:00
|
|
|
|
(if (and rev (not (string= rev "1")))
|
2021-02-27 23:09:44 +01:00
|
|
|
|
(error "Can't specify revision when registering Perforce file"))
|
2002-01-09 15:15:24 +01:00
|
|
|
|
(if (and comment (not (string= comment "")))
|
2021-02-27 23:09:44 +01:00
|
|
|
|
(error "Can't specify comment when registering Perforce file"))
|
2007-08-02 04:27:55 +02:00
|
|
|
|
;; In emacs-23 vc-register has a list of files as a parameter,
|
|
|
|
|
;; 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.
|
|
|
|
|
(let* ((file (if (listp files) (car files) files))
|
2021-09-08 09:12:03 +02:00
|
|
|
|
(fstat (p4-lowlevel-fstat file :noerror t))
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(action (cdr (assoc "action" fstat))))
|
2002-01-10 03:15:43 +01:00
|
|
|
|
(if (string= action "delete")
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(if (yes-or-no-p
|
|
|
|
|
"File already opened for delete; revert and edit it? ")
|
|
|
|
|
(progn
|
|
|
|
|
(if (yes-or-no-p "Preserve current contents? ")
|
|
|
|
|
(let ((tempfile (format "%s.vc-register~" file)))
|
|
|
|
|
(rename-file file tempfile)
|
2021-09-08 09:12:03 +02:00
|
|
|
|
(p4-lowlevel-revert file)
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(delete-file file)
|
|
|
|
|
(rename-file tempfile file))
|
2021-09-08 09:12:03 +02:00
|
|
|
|
(p4-lowlevel-revert file))
|
|
|
|
|
(p4-lowlevel-edit file))
|
2021-02-27 23:09:44 +01:00
|
|
|
|
(error "File %s already opened for delete" file))
|
2021-09-08 09:12:03 +02:00
|
|
|
|
(p4-lowlevel-add file))))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
|
2009-06-05 21:40:15 +02:00
|
|
|
|
(defun vc-p4-init-revision ()
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Return `1', the default initial version for Perforce files."
|
2002-01-09 15:15:24 +01:00
|
|
|
|
"1")
|
|
|
|
|
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(defun vc-p4-responsible-p (file)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Return non-nil if FILE is administered by Perforce.
|
|
|
|
|
FILE can point to either a file or a directory."
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(if (and vc-p4-require-p4config
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(getenv "P4CONFIG")
|
|
|
|
|
(not (vc-p4-find-p4config file)))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
nil
|
2021-09-08 09:12:03 +02:00
|
|
|
|
(or (p4-lowlevel-fstat file :noerror t)
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(vc-p4-is-in-client (if (file-directory-p file)
|
|
|
|
|
(file-name-as-directory file)
|
|
|
|
|
file)))))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
|
2021-02-28 00:25:22 +01:00
|
|
|
|
(defun vc-p4-find-revision (file rev buffer)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Get the contents of FILE at revision REV and put it into BUFFER."
|
2021-01-23 06:58:40 +01:00
|
|
|
|
(p4-lowlevel-print file
|
|
|
|
|
:rev rev
|
|
|
|
|
:output-format buffer
|
2021-09-08 09:12:03 +02:00
|
|
|
|
:quiet t))
|
2007-08-02 04:27:55 +02:00
|
|
|
|
|
2021-01-26 02:06:41 +01:00
|
|
|
|
(defun vc-p4-checkin (files comment &optional rev)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Check FILES into Perforce.
|
|
|
|
|
Check in with comment COMMENT. Error if REV is non-nil."
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(if rev
|
2021-02-27 23:09:44 +01:00
|
|
|
|
(error "Can't specify revision for Perforce checkin"))
|
2009-06-05 21:47:35 +02:00
|
|
|
|
(let* (;; XXX: default-directory? this should work for most (all?) cases
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(default-directory (file-name-directory (car files)))
|
2021-09-08 09:12:03 +02:00
|
|
|
|
(change-buffer (p4-lowlevel-change))
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(indent-tabs-mode 1)
|
|
|
|
|
insertion-start change-number)
|
2009-06-05 21:47:35 +02:00
|
|
|
|
(dolist (file files)
|
|
|
|
|
(if (vc-p4-has-unresolved-conflicts-p file)
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(error "File %s has unresolved conflicts" file)))
|
2021-02-28 00:11:22 +01:00
|
|
|
|
(with-current-buffer change-buffer
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(re-search-forward "^Description:\\s-*\n")
|
|
|
|
|
(kill-line 1)
|
|
|
|
|
(setq insertion-start (point))
|
2021-01-26 02:06:41 +01:00
|
|
|
|
(insert (car (log-edit-extract-headers nil comment)) "\n")
|
|
|
|
|
(indent-rigidly insertion-start (point) tab-width)
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(re-search-forward "^Files:\\s-*\n")
|
|
|
|
|
(delete-region (point) (point-max))
|
2009-06-05 21:47:35 +02:00
|
|
|
|
(dolist (file files)
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(insert "\t" (vc-file-getprop file 'vc-p4-depot-file) "\n"))
|
2021-01-23 18:50:29 +01:00
|
|
|
|
(setq change-number (p4-lowlevel-change
|
|
|
|
|
:buffer (current-buffer)
|
2021-09-08 09:12:03 +02:00
|
|
|
|
:op t))
|
2021-01-23 18:50:29 +01:00
|
|
|
|
(p4-lowlevel-change
|
|
|
|
|
:buffer (current-buffer)
|
2021-09-08 09:12:03 +02:00
|
|
|
|
:op change-number)
|
|
|
|
|
(p4-lowlevel-submit (current-buffer))
|
2017-06-07 09:03:49 +02:00
|
|
|
|
; Update its properties
|
2009-06-05 21:47:35 +02:00
|
|
|
|
(dolist (file files)
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(vc-p4-state file nil t)))))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
|
2017-06-07 09:09:07 +02:00
|
|
|
|
(defun vc-p4-checkout (file &optional rev)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Checkout FILE from Perforce, optionally at revision REV."
|
2021-02-28 00:11:22 +01:00
|
|
|
|
(let ((default-directory (file-name-directory file)))
|
2017-06-07 09:09:07 +02:00
|
|
|
|
;; Make sure we've got all the current state of the file
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(vc-p4-state file)
|
|
|
|
|
(cond
|
|
|
|
|
((not rev)
|
|
|
|
|
(setq rev (vc-file-getprop file 'vc-workfile-version)))
|
2017-06-07 09:09:07 +02:00
|
|
|
|
((or (string= rev "")
|
|
|
|
|
(eq rev t))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(setq rev (vc-file-getprop file 'vc-latest-version))))
|
2017-06-07 09:09:07 +02:00
|
|
|
|
(if (not (string= rev (vc-file-getprop file 'vc-workfile-version)))
|
2021-09-08 09:12:03 +02:00
|
|
|
|
(p4-lowlevel-sync file :rev rev))
|
|
|
|
|
(p4-lowlevel-edit file))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(vc-p4-state file nil t))
|
|
|
|
|
|
2021-02-28 00:11:22 +01:00
|
|
|
|
(defun vc-p4-revert (file _contents-done)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Revert FILE in Perforce.
|
2021-02-28 00:11:22 +01:00
|
|
|
|
_CONTENTS-DONE is ignored."
|
2002-01-09 15:15:24 +01:00
|
|
|
|
(let ((action (vc-file-getprop file 'vc-p4-action)))
|
2009-11-17 17:06:39 +01:00
|
|
|
|
(cond
|
|
|
|
|
((null action)
|
|
|
|
|
;; If Perforce doesn't believe that we edited the file, we have
|
|
|
|
|
;; to use sync instead of revert.
|
2021-01-23 06:35:20 +01:00
|
|
|
|
(p4-lowlevel-sync file
|
2021-09-08 09:22:54 +02:00
|
|
|
|
:rev (vc-working-revision file)
|
2021-09-08 09:12:03 +02:00
|
|
|
|
:force t))
|
2009-11-17 17:06:39 +01:00
|
|
|
|
(t
|
2021-09-08 09:12:03 +02:00
|
|
|
|
(p4-lowlevel-revert file)))
|
2002-01-09 15:15:24 +01:00
|
|
|
|
(if (string= action "add")
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(vc-file-clearprops file)
|
2002-01-09 15:15:24 +01:00
|
|
|
|
(vc-p4-state file nil t))))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
|
|
|
|
|
(defun vc-p4-merge (file rev1 rev2)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Merge together two revisions of FILE.
|
|
|
|
|
REV1 and REV2 are the revisions to merge together."
|
2021-01-23 19:01:21 +01:00
|
|
|
|
(p4-lowlevel-integrate file file
|
|
|
|
|
:rev1 rev1
|
|
|
|
|
:rev2 rev2
|
2021-09-08 09:12:03 +02:00
|
|
|
|
:force t)
|
|
|
|
|
(p4-lowlevel-resolve file)
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(vc-resynch-buffer file t t)
|
|
|
|
|
(vc-p4-state file nil t)
|
|
|
|
|
(if (vc-p4-has-unresolved-conflicts-p file)
|
|
|
|
|
1
|
|
|
|
|
0))
|
|
|
|
|
|
|
|
|
|
(defun vc-p4-merge-news (file)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Get the latest version of FILE from Perforce."
|
2021-09-08 09:12:03 +02:00
|
|
|
|
(p4-lowlevel-sync file)
|
|
|
|
|
(p4-lowlevel-resolve file)
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(vc-resynch-buffer file t t)
|
|
|
|
|
(vc-p4-state file nil t)
|
|
|
|
|
(if (vc-p4-has-unresolved-conflicts-p file)
|
|
|
|
|
1
|
|
|
|
|
0))
|
|
|
|
|
|
|
|
|
|
(defun vc-p4-resolve-select-yours ()
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Resolve a file by selecting your version."
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(vc-p4-select-conflict-text (current-buffer) 3))
|
|
|
|
|
|
|
|
|
|
(defun vc-p4-resolve-select-theirs ()
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Resolve a file by selecting their version."
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(vc-p4-select-conflict-text (current-buffer) 2))
|
|
|
|
|
|
|
|
|
|
(defun vc-p4-resolve-select-original ()
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Resolve a file by selecting the original version."
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(vc-p4-select-conflict-text (current-buffer) 1))
|
|
|
|
|
|
|
|
|
|
(defun vc-p4-steal-lock (file &optional version)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Steal Perforce lock on FILE.
|
|
|
|
|
VERSION can only be the current version used in the workspace,
|
|
|
|
|
otherwise this function will raise an error."
|
2021-09-08 09:22:54 +02:00
|
|
|
|
(if (and version (not (equal version (vc-working-revision file))))
|
2021-02-27 23:09:44 +01:00
|
|
|
|
(error "Can't specify version when stealing Perforce lock"))
|
2020-05-13 20:29:35 +02:00
|
|
|
|
;; 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.
|
2021-09-08 09:12:03 +02:00
|
|
|
|
(let ((default-directory (file-name-directory file)))
|
|
|
|
|
(p4-lowlevel-reopen file)))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
|
2021-02-28 00:11:22 +01:00
|
|
|
|
(defun vc-p4-print-log (files &optional buffer shortlog _revision limit)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Print Perforce log for FILES into BUFFER.
|
2021-02-28 00:11:22 +01:00
|
|
|
|
If SHORTLOG is non-nil print a short version of the log. _REVISION
|
2021-02-27 23:09:44 +01:00
|
|
|
|
is ignored. If LIMIT is non-nil only print that many log messages."
|
2007-07-27 14:28:08 +02:00
|
|
|
|
;; `log-view-mode' needs to have the file name in order to function
|
|
|
|
|
;; correctly. "p4 logview" does not print it, so we insert it here by
|
|
|
|
|
;; hand.
|
|
|
|
|
|
|
|
|
|
;; `vc-do-command' creates the buffer, but we need it before running
|
|
|
|
|
;; the command.
|
|
|
|
|
(vc-setup-buffer buffer)
|
2007-08-02 04:27:55 +02:00
|
|
|
|
(let* ((inhibit-read-only t)
|
2017-06-07 09:03:49 +02:00
|
|
|
|
;; In emacs-23 vc-print-log has a list of files as a
|
|
|
|
|
;; parameter, 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.
|
|
|
|
|
(file (if (listp files) (car files) files))
|
|
|
|
|
(default-directory (file-name-directory file)))
|
2007-07-27 14:28:08 +02:00
|
|
|
|
(with-current-buffer
|
2017-06-07 09:03:49 +02:00
|
|
|
|
buffer
|
2021-01-23 19:25:34 +01:00
|
|
|
|
(p4-lowlevel-filelog file
|
|
|
|
|
:buffer (current-buffer)
|
|
|
|
|
:long (not shortlog)
|
2021-09-08 09:12:03 +02:00
|
|
|
|
:limit limit)
|
2007-07-27 14:28:08 +02:00
|
|
|
|
;; Insert the file name at the beginning.
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(insert "File: " (file-name-nondirectory file) "\n"))))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
|
|
|
|
|
(defun vc-p4-show-log-entry (version)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Make sure Perforce log entry for VERSION is displayed in the current buffer."
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(let (start end lines)
|
|
|
|
|
(if (not (search-forward (format "\n#%s " version) nil t)) t
|
|
|
|
|
(beginning-of-line)
|
|
|
|
|
(setq start (point))
|
|
|
|
|
(if (not (search-forward "\n#" nil t))
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(setq end (point-max))
|
|
|
|
|
(beginning-of-line)
|
|
|
|
|
(setq end (point)))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(setq lines (count-lines start end))
|
|
|
|
|
(cond
|
|
|
|
|
;; if the global information and this log entry fit
|
|
|
|
|
;; into the window, display from the beginning
|
|
|
|
|
((< (count-lines (point-min) end) (window-height))
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(recenter 0)
|
|
|
|
|
(goto-char start))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
;; if the whole entry fits into the window,
|
|
|
|
|
;; display it centered
|
|
|
|
|
((< (1+ lines) (window-height))
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(goto-char start)
|
|
|
|
|
(recenter (1- (- (/ (window-height) 2) (/ lines 2)))))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
;; otherwise (the entry is too large for the window),
|
|
|
|
|
;; display from the start
|
|
|
|
|
(t
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(goto-char start)
|
|
|
|
|
(recenter 0))))))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
|
2021-02-28 00:11:22 +01:00
|
|
|
|
(defun vc-p4-wash-log (_file)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Remove all non-comment information from the log in the current buffer.
|
2021-02-28 00:11:22 +01:00
|
|
|
|
_FILE is ignored."
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(delete-non-matching-lines "^\t"))
|
|
|
|
|
|
|
|
|
|
(defun vc-p4-update-changelog (&optional files)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Create ChangeLog entries for the files under ‘default-directory’.
|
|
|
|
|
Limit it to FILES if it’s non-nil"
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(let ((odefault default-directory)
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(changelog (find-change-log))
|
|
|
|
|
default-directory start-rev end-rev)
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(find-file-other-window changelog)
|
|
|
|
|
(setq default-directory odefault)
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(if (looking-at
|
2017-06-07 09:03:49 +02:00
|
|
|
|
"^\\([0-9]\\{4\\}\\)[-/]\\([0-9]\\{2\\}\\)[-/]\\([0-9]\\{2\\}\\) ")
|
|
|
|
|
(setq start-rev (format "@%s/%s/%s"
|
|
|
|
|
(match-string 1)
|
|
|
|
|
(match-string 2)
|
|
|
|
|
(match-string 3))
|
|
|
|
|
end-rev "@now"))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(if (not files)
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(setq files "..."))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(message "Computing change log entries...")
|
|
|
|
|
(insert (p4-lowlevel-info-lines
|
2021-01-23 19:30:34 +01:00
|
|
|
|
(p4-lowlevel-changes files
|
|
|
|
|
:rev1 start-rev
|
|
|
|
|
:rev2 end-rev
|
|
|
|
|
:l-flag t
|
2021-09-08 09:12:03 +02:00
|
|
|
|
:s-val "submitted")))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(if (= (point) (point-min)) t
|
|
|
|
|
(if (not (= (point) (point-max)))
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(insert "\n"))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(while (re-search-backward
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(concat "^Change [0-9]+ on \\([0-9]+\\)/"
|
|
|
|
|
"\\([0-9]+\\)/\\([0-9]+\\) by \\(.+\\)")
|
|
|
|
|
nil t nil)
|
|
|
|
|
(replace-match "\n\\1-\\2-\\3 \\4" t))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(if (looking-at "\n")
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(kill-line)))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(message "Computing change log entries... done")))
|
|
|
|
|
|
2007-07-27 14:28:08 +02:00
|
|
|
|
(defvar log-view-message-re)
|
|
|
|
|
(defvar log-view-file-re)
|
|
|
|
|
(defvar log-view-font-lock-keywords)
|
|
|
|
|
|
|
|
|
|
(define-derived-mode vc-p4-log-view-mode log-view-mode "P4-Log-View"
|
|
|
|
|
(require 'add-log) ;; we need the faces add-log
|
|
|
|
|
(set (make-local-variable 'log-view-file-re) "^File:[ \t]+\\(.+\\)")
|
|
|
|
|
(set (make-local-variable 'log-view-message-re)
|
|
|
|
|
"^#\\([0-9]+\\) .*")
|
|
|
|
|
(set (make-local-variable 'log-view-font-lock-keywords)
|
|
|
|
|
(append `((,log-view-message-re . 'log-view-message-face)
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(,log-view-file-re . 'log-view-file-face))
|
|
|
|
|
'(("^user:[ \t]+\\([A-Za-z0-9_.+-]+@[A-Za-z0-9_.-]+\\)"
|
|
|
|
|
(1 'change-log-email))
|
|
|
|
|
;; Handle the case:
|
|
|
|
|
;; user: FirstName LastName <foo@bar>
|
|
|
|
|
("^user:[ \t]+\\([^<(]+?\\)[ \t]*[(<]\\([A-Za-z0-9_.+-]+@[A-Za-z0-9_.-]+\\)[>)]"
|
|
|
|
|
(1 'change-log-name)
|
|
|
|
|
(2 'change-log-email))
|
|
|
|
|
("^date: \\(.+\\)" (1 'change-log-date))
|
|
|
|
|
("^summary:[ \t]+\\(.+\\)" (1 'log-view-message))))))
|
2007-07-27 14:28:08 +02:00
|
|
|
|
|
2017-06-07 21:08:30 +02:00
|
|
|
|
(defun vc-p4-diff (file-or-files &optional rev1 rev2 buff _async)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Do a Perforce diff of FILE-OR-FILES.
|
|
|
|
|
If REV1 and REV2 are non-nil display the diff of the two
|
|
|
|
|
revisions. If BUFF is non-nil output the diff into it, or use the
|
|
|
|
|
*vc-diff* buffer otherwise. _async is ignored."
|
2017-06-07 09:12:36 +02:00
|
|
|
|
(let* ((buffer (cond
|
|
|
|
|
((bufferp buff) buff)
|
|
|
|
|
((stringp buff) (get-buffer-create buff))
|
|
|
|
|
(t (get-buffer-create "*vc-diff*"))))
|
2009-11-16 21:56:58 +01:00
|
|
|
|
(files (if (atom file-or-files) (list file-or-files) file-or-files))
|
|
|
|
|
(inhibit-read-only t))
|
|
|
|
|
(cond
|
|
|
|
|
((and (null rev1) (null rev2))
|
2010-08-09 18:28:09 +02:00
|
|
|
|
(let (added modified deleted)
|
2009-11-16 21:56:58 +01:00
|
|
|
|
(dolist (file files)
|
2010-08-09 18:28:09 +02:00
|
|
|
|
(cond
|
|
|
|
|
((string= (vc-file-getprop file 'vc-p4-action) "add")
|
|
|
|
|
(push file added))
|
|
|
|
|
((string= (vc-file-getprop file 'vc-p4-action) "delete")
|
|
|
|
|
(push file deleted))
|
|
|
|
|
(t
|
|
|
|
|
(push file modified))))
|
2009-11-16 21:56:58 +01:00
|
|
|
|
(setq added (nreverse added)
|
2010-08-09 18:28:09 +02:00
|
|
|
|
modified (nreverse modified)
|
|
|
|
|
deleted (nreverse deleted))
|
2009-11-16 21:56:58 +01:00
|
|
|
|
|
2010-08-09 18:28:09 +02:00
|
|
|
|
;; For added and deleted files, Perforce can't give us what we
|
|
|
|
|
;; want (diff against /dev/null), so we do it ourselves.
|
2009-11-16 21:56:58 +01:00
|
|
|
|
(with-current-buffer buffer
|
|
|
|
|
(erase-buffer)
|
|
|
|
|
(dolist (file added)
|
|
|
|
|
(apply 'call-process
|
|
|
|
|
(append
|
|
|
|
|
(list diff-command
|
|
|
|
|
nil
|
|
|
|
|
buffer
|
|
|
|
|
nil)
|
|
|
|
|
(if (listp diff-switches)
|
|
|
|
|
diff-switches
|
|
|
|
|
(list diff-switches))
|
|
|
|
|
(list "/dev/null"
|
|
|
|
|
file))))
|
2010-08-09 18:28:09 +02:00
|
|
|
|
(dolist (file deleted)
|
|
|
|
|
(with-temp-buffer
|
2021-01-23 06:58:40 +01:00
|
|
|
|
(p4-lowlevel-print file
|
|
|
|
|
:output-format (current-buffer)
|
2021-09-08 09:12:03 +02:00
|
|
|
|
:quiet t)
|
2010-08-09 18:28:09 +02:00
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(while (search-forward-regexp "^text: " nil t)
|
|
|
|
|
(replace-match "" nil nil))
|
|
|
|
|
(apply 'call-process-region
|
|
|
|
|
(point-min) (point-max)
|
|
|
|
|
diff-command
|
|
|
|
|
:delete
|
|
|
|
|
buffer
|
|
|
|
|
nil
|
|
|
|
|
(append
|
|
|
|
|
(list "-N"
|
|
|
|
|
;; Not sure this is the most useful labeling...
|
|
|
|
|
(concat "--label=" (vc-file-getprop file 'vc-p4-depot-file))
|
|
|
|
|
(concat "--label=" file))
|
|
|
|
|
(if (listp diff-switches)
|
|
|
|
|
diff-switches
|
|
|
|
|
(list diff-switches))
|
|
|
|
|
(list "-"
|
|
|
|
|
"/dev/null")))))
|
2009-11-16 21:56:58 +01:00
|
|
|
|
|
|
|
|
|
;; Now diff all the modified files in a single call to the server.
|
|
|
|
|
(when modified
|
|
|
|
|
(let (temp-buffer)
|
|
|
|
|
(unwind-protect
|
|
|
|
|
(progn
|
2021-09-08 09:12:03 +02:00
|
|
|
|
(setq temp-buffer (p4-lowlevel-diff modified))
|
2009-11-16 21:56:58 +01:00
|
|
|
|
(insert-buffer-substring temp-buffer))
|
|
|
|
|
(when (buffer-live-p temp-buffer)
|
|
|
|
|
(kill-buffer temp-buffer))))))))
|
|
|
|
|
|
|
|
|
|
(t
|
|
|
|
|
;; At this point things get complicated. Let's just make one
|
|
|
|
|
;; request per file and hope the server administrator doesn't
|
|
|
|
|
;; mind.
|
|
|
|
|
(with-current-buffer buffer
|
|
|
|
|
(let (temp-buffer)
|
|
|
|
|
(dolist (file files)
|
|
|
|
|
(setq temp-buffer
|
|
|
|
|
(cond
|
|
|
|
|
((and (not rev1) rev2)
|
|
|
|
|
(p4-lowlevel-diff2 file file
|
2021-01-23 06:58:40 +01:00
|
|
|
|
:rev1 (vc-file-getprop file 'vc-workfile-version)
|
2021-09-08 09:12:03 +02:00
|
|
|
|
:rev2 rev2))
|
2009-11-16 21:56:58 +01:00
|
|
|
|
((and rev1 rev2)
|
2021-01-23 06:58:40 +01:00
|
|
|
|
(p4-lowlevel-diff2 file file
|
|
|
|
|
:rev1 rev1
|
2021-09-08 09:12:03 +02:00
|
|
|
|
:rev2 rev2))
|
2009-11-16 21:56:58 +01:00
|
|
|
|
((and rev1 (not rev2))
|
2021-09-08 09:12:03 +02:00
|
|
|
|
(p4-lowlevel-diff file :rev rev1))))
|
2009-11-16 21:56:58 +01:00
|
|
|
|
(insert-buffer-substring temp-buffer)
|
2009-11-16 23:10:55 +01:00
|
|
|
|
(kill-buffer temp-buffer))))))
|
|
|
|
|
|
|
|
|
|
;; Emacs doesn't understand Perforce's file headers, so we add
|
|
|
|
|
;; them ourselves.
|
|
|
|
|
(with-current-buffer buffer
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(while (re-search-forward "^==== \\(.+#[0-9]+\\) - \\(.+\\) ====$" nil t)
|
|
|
|
|
(let ((depot-name (match-string 1))
|
|
|
|
|
(filename (match-string 2)))
|
|
|
|
|
(insert "\n--- " depot-name
|
|
|
|
|
"\n+++ " filename))))))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
|
|
|
|
|
(defun vc-p4-annotate-command (file buffer &optional version)
|
|
|
|
|
"Annotate FILE into BUFFER file using `vc-p4-annotate-command'.
|
|
|
|
|
Annotate version VERSION if it's specified."
|
2007-08-02 04:27:55 +02:00
|
|
|
|
(if vc-p4-annotate-command
|
|
|
|
|
(let ((full-file (if version
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(concat file
|
|
|
|
|
(p4-lowlevel-canonicalize-revision version))
|
|
|
|
|
file))
|
|
|
|
|
(starting-date (if current-prefix-arg
|
|
|
|
|
(read-string "Starting date: (default none) ")))
|
2021-02-28 00:11:22 +01:00
|
|
|
|
args)
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(setq args (append (list buffer nil vc-p4-annotate-command nil)
|
|
|
|
|
(if starting-date
|
|
|
|
|
(list "--after" starting-date))
|
|
|
|
|
(list full-file)))
|
|
|
|
|
(apply 'vc-do-command args))
|
2007-08-02 04:27:55 +02:00
|
|
|
|
(vc-p4-annotate-command-internal file buffer version)))
|
|
|
|
|
|
|
|
|
|
;;; Adapted from p4.el
|
|
|
|
|
(defun vc-p4-read-output (buffer)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Read the first line of BUFFER and return it.
|
2007-08-02 04:27:55 +02:00
|
|
|
|
Read lines are deleted from buffer."
|
2021-02-28 00:11:22 +01:00
|
|
|
|
(with-current-buffer buffer
|
2007-08-02 04:27:55 +02:00
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(forward-line)
|
|
|
|
|
(let ((line (buffer-substring (point-min) (point))))
|
|
|
|
|
(if (string= line "")
|
2017-06-07 09:03:49 +02:00
|
|
|
|
nil
|
|
|
|
|
(delete-region (point-min) (point))
|
|
|
|
|
;; remove trailing newline
|
|
|
|
|
(if (equal (substring line (1- (length line)) (length line)) "\n")
|
|
|
|
|
(substring line 0 (1- (length line)))
|
|
|
|
|
line)))))
|
2007-08-02 04:27:55 +02:00
|
|
|
|
|
|
|
|
|
;;; Adapted from p4.el
|
2021-02-28 00:11:22 +01:00
|
|
|
|
(defun vc-p4-annotate-command-internal (file buffer &optional _version)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Execute \"p4 annotate\" on FILE, inserting the contents in BUFFER.
|
2021-02-28 00:11:22 +01:00
|
|
|
|
_VERSION is ignored."
|
2007-08-02 04:27:55 +02:00
|
|
|
|
;; XXX maybe not needed, but just in case.
|
|
|
|
|
(vc-setup-buffer buffer)
|
|
|
|
|
;; (with-current-buffer buffer
|
|
|
|
|
(let ((file-name file)
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(file-spec file)
|
|
|
|
|
(blame-change-regex
|
|
|
|
|
(concat "^\\.\\.\\. #" "\\([0-9]+\\)" ;; revision
|
|
|
|
|
"\\s-+change\\s-+" "\\([0-9]+\\)" ;; change
|
|
|
|
|
"\\s-+" "\\([^ \t]+\\)" ;; type
|
|
|
|
|
"\\s-+on\\s-+" "\\([^ \t]+\\)" ;; date
|
|
|
|
|
"\\s-+by\\s-+" "\\([^ \t]+\\)" ;; author
|
|
|
|
|
"@"))
|
2020-05-13 20:29:35 +02:00
|
|
|
|
head-name ; file spec of the head revision for this blame assignment
|
|
|
|
|
cur-file ; file name of the current branch during blame assignment
|
2017-06-07 09:03:49 +02:00
|
|
|
|
change ch-alist fullname head-rev headseen)
|
2007-08-02 04:27:55 +02:00
|
|
|
|
|
|
|
|
|
;; we asked for blame constrained by a change number
|
|
|
|
|
(if (string-match "\\(.*\\)@\\([0-9]+\\)" file-spec)
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(progn
|
|
|
|
|
(setq file-name (match-string 1 file-spec))
|
|
|
|
|
(setq change (string-to-number (match-string 2 file-spec)))))
|
2007-08-02 04:27:55 +02:00
|
|
|
|
|
|
|
|
|
;; we asked for blame constrained by a revision
|
|
|
|
|
(if (string-match "\\(.*\\)#\\([0-9]+\\)" file-spec)
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(progn
|
|
|
|
|
(setq file-name (match-string 1 file-spec))
|
|
|
|
|
(setq head-rev (string-to-number (match-string 2 file-spec)))))
|
2007-08-02 04:27:55 +02:00
|
|
|
|
|
|
|
|
|
;; make sure the filespec is unambiguous
|
|
|
|
|
;;(p4-exec-p4 buffer (list "files" file-name) t)
|
|
|
|
|
(with-temp-buffer
|
|
|
|
|
(vc-p4-command (current-buffer) nil nil "files" file-name)
|
|
|
|
|
(save-excursion
|
2017-06-07 09:03:49 +02:00
|
|
|
|
;; (set-buffer buffer)
|
|
|
|
|
(if (> (count-lines (point-min) (point-max)) 1)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
(error "File pattern maps to more than one file"))))
|
2007-08-02 04:27:55 +02:00
|
|
|
|
;; get the file change history:
|
|
|
|
|
;;(p4-exec-p4 buffer (list "filelog" "-i" file-spec) t)
|
|
|
|
|
(with-temp-buffer
|
|
|
|
|
(vc-p4-command (current-buffer) 0 nil "filelog" "-i" file-spec)
|
|
|
|
|
(setq fullname (vc-p4-read-output (current-buffer))
|
2017-06-07 09:03:49 +02:00
|
|
|
|
cur-file fullname
|
|
|
|
|
head-name fullname)
|
2007-08-02 04:27:55 +02:00
|
|
|
|
|
|
|
|
|
;; parse the history:
|
|
|
|
|
(save-excursion
|
2017-06-07 09:03:49 +02:00
|
|
|
|
;; (set-buffer buffer)
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(while (< (point) (point-max))
|
|
|
|
|
|
|
|
|
|
;; record the current file name (and the head file name,
|
|
|
|
|
;; if we have not yet seen one):
|
|
|
|
|
(if (looking-at "^\\(//.*\\)$")
|
|
|
|
|
(setq cur-file (match-string 1)))
|
|
|
|
|
|
|
|
|
|
;; a non-branch change:
|
|
|
|
|
(if (looking-at blame-change-regex)
|
|
|
|
|
(let ((rev (string-to-number (match-string 1)))
|
|
|
|
|
(ch (string-to-number (match-string 2)))
|
|
|
|
|
(op (match-string 3))
|
|
|
|
|
(date (match-string 4))
|
|
|
|
|
(author (match-string 5)))
|
|
|
|
|
(cond
|
|
|
|
|
;; after the change constraint, OR
|
|
|
|
|
;; after the revision constraint _for this file_
|
|
|
|
|
;; [remember, branches complicate this]:
|
|
|
|
|
((or (and change (< change ch))
|
|
|
|
|
(and head-rev (< head-rev rev)
|
|
|
|
|
(string= head-name cur-file))) nil)
|
|
|
|
|
|
|
|
|
|
;; file has been deleted, can't assign blame:
|
|
|
|
|
((string= op "delete")
|
|
|
|
|
(if (not headseen) (goto-char (point-max))))
|
|
|
|
|
|
|
|
|
|
;; OK, we actually want to look at this one:
|
|
|
|
|
(t
|
|
|
|
|
(setq ch-alist
|
|
|
|
|
(cons
|
|
|
|
|
(cons ch (list rev date author cur-file)) ch-alist))
|
|
|
|
|
(if (not head-rev) (setq head-rev rev))
|
2021-02-28 00:11:22 +01:00
|
|
|
|
(setq headseen t)))))
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(forward-line))))
|
|
|
|
|
|
2007-08-02 04:27:55 +02:00
|
|
|
|
(if (< (length ch-alist) 1)
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(error "Head revision not available"))
|
|
|
|
|
|
2007-08-02 04:27:55 +02:00
|
|
|
|
(let ((base-ch (int-to-string (caar ch-alist)))
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(ch-buffer (get-buffer-create " *p4-ch-buf*"))
|
|
|
|
|
(tmp-alst (copy-alist ch-alist)))
|
2007-08-02 04:27:55 +02:00
|
|
|
|
;; (p4-exec-p4 ch-buffer (list "print" "-q" (concat cur-file "@" base-ch)) t)
|
|
|
|
|
(vc-p4-command ch-buffer nil nil "print" "-q" (concat cur-file "@" base-ch))
|
2021-02-28 00:11:22 +01:00
|
|
|
|
(with-current-buffer ch-buffer
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(while (re-search-forward ".*\n" nil t)
|
|
|
|
|
(replace-match (concat base-ch "\n"))))
|
2007-08-02 04:27:55 +02:00
|
|
|
|
(while (> (length tmp-alst) 1)
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(let ((ch-1 (car (car tmp-alst)))
|
|
|
|
|
(ch-2 (car (cadr tmp-alst)))
|
|
|
|
|
(file1 (nth 3 (cdr (car tmp-alst))))
|
|
|
|
|
(file2 (nth 3 (cdr (cadr tmp-alst))))
|
|
|
|
|
(blame-revision-regex
|
|
|
|
|
(concat "^\\([0-9]+\\),?"
|
|
|
|
|
"\\([0-9]*\\)"
|
|
|
|
|
"\\([acd]\\)"
|
|
|
|
|
"\\([0-9]+\\),?"
|
|
|
|
|
"\\([0-9]*\\)"))
|
|
|
|
|
ins-string)
|
|
|
|
|
(setq ins-string (format "%d\n" ch-2))
|
|
|
|
|
;; (p4-exec-p4 buffer (list "diff2"
|
2017-06-07 21:08:30 +02:00
|
|
|
|
;; (format "%s@%d" file1 ch-1)
|
|
|
|
|
;; (format "%s@%d" file2 ch-2)) t)
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(with-temp-buffer
|
|
|
|
|
(vc-p4-command (current-buffer) nil nil
|
|
|
|
|
"diff2" (format "%s@%d" file1 ch-1)
|
|
|
|
|
(format "%s@%d" file2 ch-2))
|
|
|
|
|
(save-excursion
|
|
|
|
|
(goto-char (point-max))
|
|
|
|
|
(while (re-search-backward blame-revision-regex nil t)
|
|
|
|
|
(let ((la (string-to-number (match-string 1)))
|
|
|
|
|
(lb (string-to-number (match-string 2)))
|
|
|
|
|
(op (match-string 3))
|
|
|
|
|
(ra (string-to-number (match-string 4)))
|
|
|
|
|
(rb (string-to-number (match-string 5))))
|
|
|
|
|
(if (= lb 0)
|
|
|
|
|
(setq lb la))
|
|
|
|
|
(if (= rb 0)
|
|
|
|
|
(setq rb ra))
|
|
|
|
|
(cond ((string= op "a")
|
|
|
|
|
(setq la (1+ la)))
|
|
|
|
|
((string= op "d")
|
|
|
|
|
(setq ra (1+ ra))))
|
2021-02-28 00:11:22 +01:00
|
|
|
|
(with-current-buffer ch-buffer
|
2022-01-24 05:55:08 +01:00
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(forward-line (1- la))
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(let ((beg (point)))
|
|
|
|
|
(forward-line (1+ (- lb la)))
|
|
|
|
|
(delete-region beg (point)))
|
|
|
|
|
(while (<= ra rb)
|
|
|
|
|
(insert ins-string)
|
|
|
|
|
(setq ra (1+ ra))))))))
|
|
|
|
|
(setq tmp-alst (cdr tmp-alst))))
|
2007-08-02 04:27:55 +02:00
|
|
|
|
;; (p4-noinput-buffer-action "print" nil t
|
2017-06-07 21:08:30 +02:00
|
|
|
|
;; (list (format "%s#%d" fullname head-rev))
|
|
|
|
|
;; t)
|
2007-08-02 04:27:55 +02:00
|
|
|
|
(vc-p4-command buffer nil nil
|
2017-06-07 09:03:49 +02:00
|
|
|
|
"print" (format "%s#%d" fullname head-rev))
|
2007-08-02 04:27:55 +02:00
|
|
|
|
(let (line cnum (old-cnum 0) change-data
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(blame-index-regex
|
|
|
|
|
(concat " *\\([0-9]+/[0-9]+/[0-9]+\\)" ;; date
|
|
|
|
|
"\\s-+\\([^ \t]*\\)" ;; author
|
|
|
|
|
" *\\([0-9]+\\)" ;; change
|
|
|
|
|
" *\\([0-9]+\\)" ;; revision
|
|
|
|
|
" "))
|
2021-02-28 00:11:22 +01:00
|
|
|
|
xth-rev xth-date xth-auth)
|
|
|
|
|
(with-current-buffer buffer
|
2022-01-24 05:55:08 +01:00
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(forward-line 1)
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(move-to-column 0)
|
|
|
|
|
(insert (format "%10s %7s %6s %4s\n" "Date" "Author" "Change" "Rev"))
|
|
|
|
|
(while (setq line (vc-p4-read-output ch-buffer))
|
|
|
|
|
(setq cnum (string-to-number line))
|
|
|
|
|
(if (and nil (= cnum old-cnum))
|
|
|
|
|
(insert (format "%29s " ""))
|
|
|
|
|
|
|
|
|
|
;; extract the change data from our alist: remember,
|
|
|
|
|
;; `eq' works for integers so we can use assq here:
|
|
|
|
|
(setq change-data (cdr (assq cnum ch-alist))
|
|
|
|
|
xth-rev (nth 0 change-data)
|
|
|
|
|
xth-date (nth 1 change-data)
|
2021-02-28 00:11:22 +01:00
|
|
|
|
xth-auth (nth 2 change-data))
|
2017-06-07 09:03:49 +02:00
|
|
|
|
|
|
|
|
|
(insert
|
|
|
|
|
(format "%10s %7s %6d %4d " xth-date xth-auth cnum xth-rev))
|
|
|
|
|
(move-to-column 0)
|
|
|
|
|
(if (looking-at blame-index-regex)
|
2021-02-28 00:11:22 +01:00
|
|
|
|
(let ((start (+ (match-beginning 2) 7))
|
|
|
|
|
(end (match-end 2)))
|
|
|
|
|
(if (> end start)
|
|
|
|
|
(delete-region start end)))))
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(setq old-cnum cnum)
|
|
|
|
|
(forward-line))))
|
2007-08-02 04:27:55 +02:00
|
|
|
|
|
|
|
|
|
(kill-buffer ch-buffer))))
|
|
|
|
|
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(defconst vc-p4-annotate-re
|
2007-08-02 04:27:55 +02:00
|
|
|
|
(concat "^\\([[:digit:]/]+\\)[[:space:]]*[[:digit:]]+[[:space:]]+"
|
2017-06-07 09:03:49 +02:00
|
|
|
|
"[^[:space:]]+[[:space:]]+\\([[:digit:]]+\\)"
|
|
|
|
|
"[[:space:]]+\\([[:digit:]]+\\) "))
|
2002-01-06 20:23:04 +01:00
|
|
|
|
|
|
|
|
|
(defun vc-p4-annotate-time ()
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Return the time of the next Perforce annotation at or after point.
|
|
|
|
|
The value is returned as a floating point fractional number of
|
|
|
|
|
days. Moves the point to the end of the annotation."
|
2015-03-10 23:43:47 +01:00
|
|
|
|
(when (and (looking-at vc-p4-annotate-re) (fboundp 'vc-annotate-convert-time))
|
2007-08-02 04:27:55 +02:00
|
|
|
|
(goto-char (match-end 0))
|
|
|
|
|
(let ((timestr (match-string-no-properties 1)))
|
|
|
|
|
(string-match "\\([0-9]+\\)/\\([0-9]+\\)/\\([0-9]+\\)" timestr)
|
|
|
|
|
(vc-annotate-convert-time
|
|
|
|
|
(encode-time 0 0 0
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(string-to-number (match-string 3 timestr))
|
|
|
|
|
(string-to-number (match-string 2 timestr))
|
|
|
|
|
(string-to-number (match-string 1 timestr)))))))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
|
2007-07-27 14:28:08 +02:00
|
|
|
|
(defun vc-p4-annotate-extract-revision-at-line ()
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Get the annotated revision on the current line."
|
2007-07-27 14:28:08 +02:00
|
|
|
|
(save-excursion
|
2007-08-02 04:27:55 +02:00
|
|
|
|
(beginning-of-line)
|
|
|
|
|
(if (looking-at vc-p4-annotate-re) (match-string-no-properties 3))))
|
2007-07-27 14:28:08 +02:00
|
|
|
|
|
2021-02-28 00:11:22 +01:00
|
|
|
|
(defun vc-p4-previous-version (_file rev)
|
|
|
|
|
"Return the Perforce revision prior to REV.
|
|
|
|
|
_FILE is ignored."
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(number-to-string (- (string-to-number rev) 1)))
|
|
|
|
|
|
|
|
|
|
(defun vc-p4-find-p4config (&optional dirname)
|
|
|
|
|
"See if there is a $P4CONFIG file in DIRNAME or any of its parents.
|
|
|
|
|
If DIRNAME is not specified, uses `default-directory'."
|
2002-01-15 18:31:41 +01:00
|
|
|
|
(let ((this-directory (expand-file-name (or dirname default-directory)))
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(p4config (getenv "P4CONFIG"))
|
|
|
|
|
child)
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(if (not p4config)
|
2017-06-07 09:03:49 +02:00
|
|
|
|
nil
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(catch 'found
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(while (not (equal this-directory child))
|
|
|
|
|
(if (file-exists-p (concat this-directory p4config))
|
|
|
|
|
(throw 'found (concat this-directory p4config)))
|
|
|
|
|
(setq child this-directory)
|
|
|
|
|
(setq this-directory (file-name-directory
|
|
|
|
|
(directory-file-name this-directory))))))))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
|
|
|
|
|
(defun vc-p4-is-in-client (file)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Return non-nil if FILE is inside the p4 client hierarchy."
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(let* ((default-directory (file-name-directory file))
|
2021-09-08 09:12:03 +02:00
|
|
|
|
(info (p4-lowlevel-info))
|
2021-01-23 18:30:46 +01:00
|
|
|
|
(root (alist-get "Client root" info nil nil #'string=))
|
|
|
|
|
(cwd (alist-get "Current directory" info nil nil #'string=)))
|
2020-05-28 02:51:25 +02:00
|
|
|
|
(string-prefix-p root cwd)))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
|
|
|
|
|
(defun vc-p4-has-unresolved-conflicts-p (file)
|
|
|
|
|
"Search through FILE's buffer for unresolved P4 conflicts.
|
|
|
|
|
If FILE is a string, then the buffer visiting that file is searched;
|
|
|
|
|
no search occurs if no buffer is visiting the file. If FILE is a
|
|
|
|
|
buffer, then that buffer is searched.
|
|
|
|
|
|
|
|
|
|
Returns nil if there are no conflicts. If there are conflicts,
|
|
|
|
|
returns a list of buffer positions containing the start and end of the
|
|
|
|
|
first conflict block in the file and then the start and end of each
|
2021-02-27 23:09:44 +01:00
|
|
|
|
sub-block within it."
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(let ((buffer (if (bufferp file) file (get-file-buffer file)))
|
|
|
|
|
block-start block-end block1-start block1-end block2-start block2-end
|
|
|
|
|
block3-start block3-end)
|
|
|
|
|
(if (not buffer)
|
|
|
|
|
nil
|
|
|
|
|
(save-excursion
|
|
|
|
|
(save-restriction
|
|
|
|
|
(set-buffer buffer)
|
|
|
|
|
(widen)
|
|
|
|
|
(goto-char (point-min))
|
|
|
|
|
(if (not (re-search-forward "^>>>>\\( .*\\|\\)\n" nil t))
|
|
|
|
|
nil
|
|
|
|
|
(setq block-start (match-beginning 0)
|
|
|
|
|
block1-start (match-end 0))
|
|
|
|
|
(if (not (re-search-forward "^<<<<\\( .*\\|\\)\n" nil t))
|
|
|
|
|
nil
|
2017-06-07 09:03:49 +02:00
|
|
|
|
; Could actually be block 3, but but we'll figure that out later.
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(setq block2-end (match-beginning 0)
|
|
|
|
|
block-end (match-end 0))
|
|
|
|
|
(goto-char block1-start)
|
|
|
|
|
(if (not (re-search-forward "^====\\( .*\\|\\)\n" block-end t))
|
|
|
|
|
nil
|
|
|
|
|
(setq block1-end (match-beginning 0)
|
|
|
|
|
block2-start (match-end 0))
|
|
|
|
|
(if (not (re-search-forward "^====\\( .*\\|\\)\n" block-end t))
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(list block-start block-end
|
|
|
|
|
block1-start block1-end
|
2002-01-06 04:45:11 +01:00
|
|
|
|
block2-start block2-end)
|
|
|
|
|
(setq block3-end block2-end
|
|
|
|
|
block2-end (match-beginning 0)
|
|
|
|
|
block3-start (match-end 0))
|
|
|
|
|
(list block-start block-end
|
|
|
|
|
block1-start block1-end
|
|
|
|
|
block2-start block2-end
|
|
|
|
|
block3-start block3-end))))))))))
|
|
|
|
|
|
|
|
|
|
(defun vc-p4-select-conflict-text (buffer which)
|
|
|
|
|
"Search for P4 conflict markers in BUFFER and select the WHICH text of each.
|
|
|
|
|
WHICH should be either 1, 2, or 3 to indicate the first, second or
|
2021-02-27 23:09:44 +01:00
|
|
|
|
third sub-block in each conflict block."
|
2021-02-28 00:11:22 +01:00
|
|
|
|
(let (block-list block-start block-end subcount replacement)
|
|
|
|
|
(with-current-buffer buffer
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(while (setq block-list (vc-p4-has-unresolved-conflicts-p buffer))
|
|
|
|
|
(setq block-start (car block-list)
|
|
|
|
|
block-end (cadr block-list)
|
|
|
|
|
subcount which)
|
|
|
|
|
(while (and block-list (> subcount 0))
|
|
|
|
|
(setq block-list (cddr block-list)
|
|
|
|
|
subcount (1- subcount)))
|
|
|
|
|
(setq replacement (if block-list
|
2017-06-07 09:03:49 +02:00
|
|
|
|
(buffer-substring (car block-list)
|
|
|
|
|
(cadr block-list))
|
2002-01-06 04:45:11 +01:00
|
|
|
|
""))
|
|
|
|
|
(delete-region block-start block-end)
|
|
|
|
|
(goto-char block-start)
|
|
|
|
|
(insert replacement)))
|
|
|
|
|
(if block-start t nil)))
|
|
|
|
|
|
2007-07-27 14:28:08 +02:00
|
|
|
|
(defun vc-p4-command (buffer okstatus file &rest flags)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"A wrapper around ‘vc-do-command’ for use in vc-p4.el.
|
|
|
|
|
The difference to ‘vc-do-command’ is that this function always
|
|
|
|
|
invokes ‘p4’. The arguments BUFFER, OKSTATUS, FILE, and FLAGS are
|
|
|
|
|
all passed directly to ‘vc-do-command’, so check the
|
|
|
|
|
documentation for that command for their meanings."
|
2007-07-27 14:28:08 +02:00
|
|
|
|
(apply 'vc-do-command buffer okstatus "p4" file flags))
|
|
|
|
|
|
2020-05-13 20:07:38 +02:00
|
|
|
|
(defun vc-p4-delete-file (file)
|
|
|
|
|
"Tell perforce to delete FILE from the repository."
|
2021-09-08 09:12:03 +02:00
|
|
|
|
(p4-lowlevel-delete file))
|
2020-05-13 20:07:38 +02:00
|
|
|
|
|
2020-03-03 06:08:23 +01:00
|
|
|
|
(defun vc-p4-switch-client (client)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Switch to CLIENT as the current client used for all operations."
|
2020-03-03 06:08:23 +01:00
|
|
|
|
(interactive
|
|
|
|
|
(list (completing-read "Client: " (p4-lowlevel-local-clients))))
|
|
|
|
|
(p4-lowlevel-command-or-error `("set" ,(format "P4CLIENT=%s" client))))
|
|
|
|
|
|
2021-01-15 07:39:21 +01:00
|
|
|
|
(defun vc-p4-login (password)
|
|
|
|
|
"Call the p4 login command user PASSWORD."
|
|
|
|
|
(interactive (list (read-passwd "P4 Password: ")))
|
|
|
|
|
(p4-lowlevel-login :password password))
|
|
|
|
|
|
|
|
|
|
(defun vc-p4-logged-in-p ()
|
|
|
|
|
"Check if there is an active session for Perforce."
|
|
|
|
|
(p4-lowlevel-login :status t))
|
|
|
|
|
|
2021-01-29 02:56:51 +01:00
|
|
|
|
(defun vc-p4-dir-extra-headers (dir)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
"Get extra Perforce-specific vc-dir headers related to DIR."
|
2021-09-17 08:02:04 +02:00
|
|
|
|
(let* ((default-directory dir)
|
|
|
|
|
(extra-info (p4-lowlevel-info)))
|
2021-01-29 02:56:51 +01:00
|
|
|
|
(concat
|
|
|
|
|
(propertize "Client :" 'face 'font-lock-type-face)
|
|
|
|
|
" "
|
|
|
|
|
(propertize (alist-get "Client name" extra-info nil nil #'string=)
|
|
|
|
|
'face 'font-lock-variable-name-face)
|
|
|
|
|
"\n"
|
|
|
|
|
(propertize "Stream :" 'face 'font-lock-type-face)
|
|
|
|
|
" "
|
|
|
|
|
(propertize (alist-get "Client stream" extra-info nil nil #'string=)
|
|
|
|
|
'face 'font-lock-variable-name-face)
|
|
|
|
|
"\n"
|
|
|
|
|
(propertize "Root :" 'face 'font-lock-type-face)
|
|
|
|
|
" "
|
|
|
|
|
(propertize (alist-get "Client root" extra-info nil nil #'string=)
|
|
|
|
|
'face 'font-lock-variable-name-face)
|
|
|
|
|
"\n"
|
|
|
|
|
(propertize "Server :" 'face 'font-lock-type-face)
|
|
|
|
|
" "
|
|
|
|
|
(propertize (alist-get "Server address" extra-info nil nil #'string=)
|
|
|
|
|
'face 'font-lock-variable-name-face))))
|
|
|
|
|
|
2022-03-14 05:25:57 +01:00
|
|
|
|
(defun vc-p4-dir-printer (fileinfo)
|
|
|
|
|
"Call ‘vc-default-dir-printer’ and append the extra part of FILEINFO."
|
|
|
|
|
(vc-default-dir-printer 'P4 fileinfo)
|
|
|
|
|
(when-let ((extra (vc-dir-fileinfo->extra fileinfo)))
|
|
|
|
|
(insert " <" extra ">")))
|
|
|
|
|
|
2022-03-03 01:39:11 +01:00
|
|
|
|
(defun vc-p4-rename-file (old new)
|
2021-02-25 07:17:00 +01:00
|
|
|
|
"Rename OLD to NEW in Perforce."
|
|
|
|
|
(p4-lowlevel-rename old new))
|
|
|
|
|
|
2002-01-06 04:45:11 +01:00
|
|
|
|
(provide 'vc-p4)
|
2021-02-27 23:09:44 +01:00
|
|
|
|
;;; vc-p4.el ends here
|