These are nifty tricks for working with PclCvs.

With projects large and small, it is desirable to have ChangeLog file to track your changes to the project. But often this process is so monotonous. If you start project in the CVS there is easily way to automatically generate ChangeLog entries according to the diff between working file and the file in CVS.

You will need pcl-cvs/pcvs and add-log packages to do this. pcl-cvs is great front-end to CVS and add-log is for maintenance ChangeLog files.

Note as of 2003-11-26, this apparently will only work with XEmacs, as GnuEmacs does not contain the patch-to-change-log function in its add-log.el package (even in Emacs 22). As of 2007-06-02, Emacs 22 has a ‘diff-current-defun’ function provided by DiffMode.

The new name for pcl-cvs is pcvs and comes with Emacs starting with version 21.

See GIT:vc/pcvs.el. http://repo.or.cz/w/emacs.git/history/HEAD:/lisp/vc/pcvs.el

It comes with a manual. Visit it in Info: ‘C-h i m pcl-cvs RET’.

(require 'pcl-cvs)
(require 'add-log)

With Emacs21, this would supposedly be

(require 'pcvs)
(require 'add-log)

Generate ChangeLog entries from the patch

If you do changes in a buffer and don’t save it before calling add-change-log-entry, it (add-change-log-entry) will take the buffer contents and buffer-file-name file contents and make proper ChangeLog entries. But if you saved your buffer so there is no place for add-log to take the origin, pcl-cvs will help. We always can take the original file from CVS. add-log has a useful function - patch-to-change-log which will take a diff file in unified format and translate it to ChangeLog entries.

(defun my-cvs-changelog-in-other-window ()
  "Switch to ChangeLog file in other window."
  (let* ((fi (cvs-mode-marked nil nil :one t))
	 (default-directory (cvs-expand-dir-name (cvs-fileinfo->dir fi)))
	 (buffer-file-name (expand-file-name (cvs-fileinfo->file fi)))
    (save-excursion (find-file-other-window (find-change-log)))))

(defun-cvs-mode (cvs-mode-mydiff . DOUBLE) (flags)
  "Diff the selected files against the repository and
patch-to-change-log it after processing."
   (list (cvs-add-branch-prefix
	   (cvs-flags-query 'cvs-diff-flags "cvs diff flags")))))
  (cvs-mode-do "diff" flags 'diff
	       :show nil :postproc '((patch-to-change-log default-directory))))

(defun my-cvs-patch-to-change-log ()
  "Make patch of file in working directory and in CVS. Create
ChangeLog entries according to diff."

(define-key 'cvs-mode-map "\C-cA" 'my-cvs-patch-to-change-log)

Also you may need to add:

(setq-default diff-switches "-u")

(You could also do this with ‘diff -u’ in your ~/.cvsrc.)

Key bindings

Add the following line to your ~/.emacs, which will bind F12 to PCL CVS without asking for a directory to examine:

(global-set-key [f12] '(lambda () 
			   (buffer-file-name)) '("-d" "-P"))))

I found that I often miss the following default keybindings from the menu, so I list them here:

From: KaiGrossjohann
Subject: Re: checking in two files while in CVS mode
Newsgroups: comp.emacs
Message-ID: <84smutapnf.fsf@lucy.is.informatik.uni-duisburg.de>

I did (global-set-key (kbd "C-x v D") 'cvs-update) so instead of C-x
v d I can use C-x v D to get PCL-CVS.

PCL-CVS also supports doing `=' on a directory.

I think you will find that the additional information that PCL-CVS
gives you will more than compensate for the simple command that you
have to run.

Another useful feature in PCL-CVS is its handling of ChangeLog files
and commit entries.  While you are editing, when you are in the
function you've changed, you can type C-x 4 a.  This will prepare a
ChangeLog entry for that file and that function.  Then, from PCL-CVS,
you can just type `C' (instead of `c') to commit, and that will snarf
all the relevant ChangeLog entries and use them for the commit
message.  I like this a lot, especially since you get information on
the current spot in the file for free.

After having requested a diff with `=', you can use `a' (or `C-x 4
a') from the diff buffer, too -- no need to change back to the source
code buffer.

Substitute $TAG$ by CVS tag

Not really a pcl-cvs function, but maybe someone will find it useful:

(defun my-insert-cvs-tag ()
  "Substitutes $TAG$ by CVS tag."
  (let ((tbuf nil)
	(tstr "HEAD"))

    (when (file-exists-p "CVS/Tag")
      (setq tbuf (find-file-noselect "CVS/Tag")))

    (when tbuf
      (with-current-buffer tbuf
	(if (re-search-forward "T\\(.*\\)")
	  (setq tstr (match-string 1))
	  (setq tstr "unknown"))
	(kill-buffer tbuf)))

      (when (re-search-forward "\\$TAG\\(: .*\\$\\|\\$\\)")
	(replace-match (format "$TAG: %s$" tstr) nil nil)))

Different CVS repositories

I find myself using a few different CVS repositories for different projects, and sometimes my CVSROOT isn’t in sync with the file I’m working on currently. Here’s a little function I keep in my .emacs to pick which server I want to work with on the fly:

;; I have to ssh into all the computers I connect to.  Make sure CVS_RSH is set right
(if (not (getenv "CVS_RSH"))
    (setenv "CVS_RSH" "ssh"))

;; An alist of name/cvsroot pairs
(defvar cvsroot-alist '(
			("host1" . "user@host1:/home/user/cvs")
			("host2" . "user@host2:/usr/local/CVS")
			("host3" . "user@host3:/home/user/cvsroot")))

(defun select-cvs (server)
   (let ((out
          ;;This will ask for the name of the cvs server you want, pulling the
          ;;names from cvsroot-alist, above.  It does completion.
	  (completing-read "Cvs server: "
			   (cdr (cdr cvsroot-alist))
     (list out)))
     ;;the variable we actualy want to set is cvs-cvsroot
  (setq cvs-cvsroot (cdr (assoc server cvsroot-alist))))

M-x select-cvs will now prompt you for the name of a host you want to user for your repository (i.e. host1, host2, host3 from cvsroot-alist). Of course, you can bind this to any key combination you want.