;;; vc-hooks.el --- resident support for version-control
-;; Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc.
+;; Copyright (C) 1992, 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
-;; Author: Eric S. Raymond <esr@snark.thyrsus.com>
-;; Modified by:
-;; Per Cederqvist <ceder@lysator.liu.se>
-;; Andre Spiegel <spiegel@berlin.informatik.uni-stuttgart.de>
+;; Author: Eric S. Raymond <esr@snark.thyrsus.com>
+;; Maintainer: Andre Spiegel <spiegel@inf.fu-berlin.de>
;; This file is part of GNU Emacs.
vc-find-cvs-master)
"*Where to look for version-control master files.
The first pair corresponding to a given back end is used as a template
-when creating new masters.")
+when creating new masters.
+Setting this variable to nil turns off use of VC entirely.")
(defvar vc-make-backup-files nil
"*If non-nil, backups of registered files are made as with other files.
(vc-parse-buffer
(list '("^\001d D \\([^ ]+\\)" 1)
(list (concat "^\001d D \\([^ ]+\\) .* "
- (regexp-quote (user-login-name)) " ") 1))
+ (regexp-quote (vc-user-login-name)) " ") 1))
file
'(vc-latest-version vc-your-latest-version)))
'needs-checkout)
((string-match "Unresolved Conflict" status) 'unresolved-conflict)
((string-match "Locally Added" status) 'locally-added)
+ ((string-match "New file!" status) 'locally-added)
(t 'unknown)
))))))))
(if (get-buffer "*vc-info*")
(looking-at "[^ ]+ \\([0-9.]+\\) ")))
(goto-char (match-end 0))
;; if found, store the revision number ...
- (setq version (buffer-substring (match-beginning 1) (match-end 1)))
+ (setq version (buffer-substring-no-properties (match-beginning 1)
+ (match-end 1)))
;; ... and check for the locking state
(cond
((looking-at
;; revision is locked by some user
((looking-at "\\([^ ]+\\) \\$")
(setq locking-user
- (buffer-substring (match-beginning 1) (match-end 1)))
+ (buffer-substring-no-properties (match-beginning 1)
+ (match-end 1)))
(setq status 'rev-and-lock))
;; everything else: false
(nil)))
"Revision: \\([0-9.]+\\) \\$")
nil t)
;; if found, store the revision number ...
- (setq version (buffer-substring (match-beginning 1) (match-end 1)))
+ (setq version (buffer-substring-no-properties (match-beginning 1)
+ (match-end 1)))
;; and see if there's any lock information
(goto-char (point-min))
(if (re-search-forward (concat "\\$" "Locker:") nil t)
(cond ((looking-at " \\([^ ]+\\) \\$")
- (setq locking-user (buffer-substring (match-beginning 1)
- (match-end 1)))
+ (setq locking-user (buffer-substring-no-properties
+ (match-beginning 1)
+ (match-end 1)))
(setq status 'rev-and-lock))
((looking-at " *\\$")
(setq locking-user 'none)
(vc-file-setprop file 'vc-locking-user 'none))
((and (= (nth 2 attributes) (user-uid))
(string-match ".rw..-..-." (nth 8 attributes)))
- (vc-file-setprop file 'vc-locking-user (user-login-name)))
+ (vc-file-setprop file 'vc-locking-user (vc-user-login-name)))
(nil)))))
+(defun vc-user-login-name (&optional uid)
+ ;; Return the name under which the user is logged in, as a string.
+ ;; (With optional argument UID, return the name of that user.)
+ ;; This function does the same as `user-login-name', but unlike
+ ;; that, it never returns nil. If a UID cannot be resolved, that
+ ;; UID is returned as a string.
+ (or (user-login-name uid)
+ (and uid (number-to-string uid))
+ (number-to-string (user-uid))))
+
(defun vc-file-owner (file)
- ;; The expression below should return the username of the owner
- ;; of the file. It doesn't. It returns the username if it is
- ;; you, or otherwise the UID of the owner of the file. The
- ;; return value from this function is only used by
- ;; vc-dired-reformat-line, and it does the proper thing if a UID
- ;; is returned.
- ;; The *proper* way to fix this would be to implement a built-in
- ;; function in Emacs, say, (username UID), that returns the
- ;; username of a given UID.
- ;; The result of this hack is that vc-directory will print the
- ;; name of the owner of the file for any files that are
- ;; modified.
- (let ((uid (nth 2 (file-attributes file))))
- (if (= uid (user-uid)) (user-login-name) uid)))
+ ;; Return who owns FILE (user name, as a string).
+ (vc-user-login-name (nth 2 (file-attributes file))))
(defun vc-rcs-lock-from-diff (file)
;; Diff the file against the master version. If differences are found,
(defun vc-locking-user (file)
;; Return the name of the person currently holding a lock on FILE.
- ;; Return nil if there is no such person. (Sometimes, not the name
- ;; of the locking user but his uid will be returned.)
+ ;; Return nil if there is no such person.
;; Under CVS, a file is considered locked if it has been modified since
;; it was checked out.
;; The property is cached. It is only looked up if it is currently nil.
(and (equal (vc-file-getprop file 'vc-checkout-time)
(nth 5 (file-attributes file)))
(vc-file-setprop file 'vc-locking-user 'none))
- (let ((locker (vc-file-owner file)))
- (vc-file-setprop file 'vc-locking-user
- (if (stringp locker) locker
- (format "%d" locker))))))
+ (vc-file-setprop file 'vc-locking-user (vc-file-owner file))))
((eq (vc-backend file) 'RCS)
(let (p-lock)
(list (concat "^\\([0-9]+\\.[0-9.]+\\)\n"
"date[ \t]+\\([0-9.]+\\);[ \t]+"
"author[ \t]+"
- (regexp-quote (user-login-name)) ";") 1 2))
+ (regexp-quote (vc-user-login-name)) ";") 1 2))
file
'(vc-latest-version vc-your-latest-version))
(if (get-buffer "*vc-info*")
;; case-sensitively
(setq case-fold-search nil)
(cond
+ ;; normal entry
((re-search-forward
(concat "^/" (regexp-quote basename)
- "/\\([^/]*\\)/[^ /]* \\([A-Z][a-z][a-z]\\) *\\([0-9]*\\) \\([0-9]*\\):\\([0-9]*\\):\\([0-9]*\\) \\([0-9]*\\)/")
+ "/\\([^/]*\\)/\\([^/+]*\\+\\)?[^ /]* \\([A-Z][a-z][a-z]\\) *\\([0-9]*\\) \\([0-9]*\\):\\([0-9]*\\):\\([0-9]*\\) \\([0-9]*\\)")
nil t)
(setq case-fold-search fold) ;; restore the old value
;; We found it. Store away version number now that we
;; If the file hasn't been modified since checkout,
;; store the checkout-time.
(let ((mtime (nth 5 (file-attributes file)))
- (second (string-to-number (match-string 6)))
- (minute (string-to-number (match-string 5)))
- (hour (string-to-number (match-string 4)))
- (day (string-to-number (match-string 3)))
- (year (string-to-number (match-string 7))))
+ (second (string-to-number (match-string 7)))
+ (minute (string-to-number (match-string 6)))
+ (hour (string-to-number (match-string 5)))
+ (day (string-to-number (match-string 4)))
+ (year (string-to-number (match-string 8))))
(if (equal mtime
(encode-time
second minute hour day
(/ (string-match
- (match-string 2)
+ (match-string 3)
"xxxJanFebMarAprMayJunJulAugSepOctNovDec")
3)
year 0))
(vc-file-setprop file 'vc-checkout-time mtime)
(vc-file-setprop file 'vc-checkout-time 0)))
(throw 'found (cons (concat dirname "CVS/Entries") 'CVS)))
+ ;; entry for a "locally added" file (not yet committed)
+ ((re-search-forward
+ (concat "^/" (regexp-quote basename) "/0/Initial ") nil t)
+ (setq case-fold-search fold) ;; restore the old value
+ (vc-file-setprop file 'vc-checkout-time 0)
+ (vc-file-setprop file 'vc-workfile-version "0")
+ (throw 'found (cons (concat dirname "CVS/Entries") 'CVS)))
+ ((re-search-forward
+ (concat "^/" (regexp-quote basename)
+ "/\\([^/]*\\)/Initial") nil t)
+ (setq case-fold-search fold) ;; restore the old value
+ (vc-file-setprop file 'vc-workfile-version "0")
+ (vc-file-setprop file 'vc-checkout-time 0)
+ (throw 'found (cons (concat dirname "CVS/Entries") 'CVS)))
(t (setq case-fold-search fold) ;; restore the old value
nil)))
(kill-buffer buffer)))))
t)
(not (vc-locking-user file))
(eq (vc-checkout-model file) 'implicit)
- (vc-file-setprop file 'vc-locking-user (user-login-name))
+ (vc-file-setprop file 'vc-locking-user (vc-user-login-name))
(or (and (eq (vc-backend file) 'CVS)
(vc-file-setprop file 'vc-cvs-status nil))
t)
(and vc-type
(concat " " (or label (symbol-name vc-type))
(and vc-display-status (vc-status file)))))
+ ;; If the file is locked by some other user, make
+ ;; the buffer read-only. Like this, even root
+ ;; cannot modify a file that someone else has locked.
(and vc-type
(equal file (buffer-file-name))
(vc-locking-user file)
- ;; If the file is locked by some other user, make
- ;; the buffer read-only. Like this, even root
- ;; cannot modify a file without locking it first.
- (not (string= (user-login-name) (vc-locking-user file)))
+ (not (string= (vc-user-login-name) (vc-locking-user file)))
+ (setq buffer-read-only t))
+ ;; If the user is root, and the file is not owner-writable,
+ ;; then pretend that we can't write it
+ ;; even though we can (because root can write anything).
+ ;; This way, even root cannot modify a file that isn't locked.
+ (and vc-type
+ (equal file (buffer-file-name))
+ (not buffer-read-only)
+ (zerop (user-real-uid))
+ (zerop (logand (file-modes (buffer-file-name)) 128))
(setq buffer-read-only t))
(force-mode-line-update)
;;(set-buffer-modified-p (buffer-modified-p)) ;;use this if Emacs 18
" @@")
((not locker)
(concat "-" rev))
- ((if (stringp locker)
- (string= locker (user-login-name))
- (= locker (user-uid)))
+ ((string= locker (vc-user-login-name))
(concat ":" rev))
(t
(concat ":" locker ":" rev)))))
+(defun vc-follow-link ()
+ ;; If the current buffer visits a symbolic link, this function makes it
+ ;; visit the real file instead. If the real file is already visited in
+ ;; another buffer, make that buffer current, and kill the buffer
+ ;; that visits the link.
+ (let* ((truename (abbreviate-file-name (file-chase-links buffer-file-name)))
+ (true-buffer (find-buffer-visiting truename))
+ (this-buffer (current-buffer)))
+ (if (eq true-buffer this-buffer)
+ (progn
+ (kill-buffer this-buffer)
+ ;; In principle, we could do something like set-visited-file-name.
+ ;; However, it can't be exactly the same as set-visited-file-name.
+ ;; I'm not going to work out the details right now. -- rms.
+ (set-buffer (find-file-noselect truename)))
+ (set-buffer true-buffer)
+ (kill-buffer this-buffer))))
+
;;; install a call to the above as a find-file hook
(defun vc-find-file-hook ()
;; Recompute whether file is version controlled,
(make-local-variable 'backup-inhibited)
(setq backup-inhibited t))))
((let* ((link (file-symlink-p buffer-file-name))
- (link-type (and link (vc-backend link))))
+ (link-type (and link (vc-backend (file-chase-links link)))))
(if link-type
(cond ((eq vc-follow-symlinks nil)
(message
"Warning: symbolic link to %s-controlled source file" link-type))
- ((eq vc-follow-symlinks 'ask)
+ ((or (not (eq vc-follow-symlinks 'ask))
+ ;; If we already visited this file by following
+ ;; the link, don't ask again if we try to visit
+ ;; it again. GUD does that, and repeated questions
+ ;; are painful.
+ (get-file-buffer
+ (abbreviate-file-name (file-chase-links buffer-file-name))))
+
+ (vc-follow-link)
+ (message "Followed link to %s" buffer-file-name)
+ (vc-find-file-hook))
+ (t
(if (yes-or-no-p (format
"Symbolic link to %s-controlled source file; follow link? " link-type))
- (progn (setq buffer-file-name
- (file-truename buffer-file-name))
+ (progn (vc-follow-link)
(message "Followed link to %s" buffer-file-name)
(vc-find-file-hook))
(message
"Warning: editing through the link bypasses version control")
- ))
- (t (setq buffer-file-name (file-truename buffer-file-name))
- (message "Followed link to %s" buffer-file-name)
- (vc-find-file-hook))))))))))
+ ))))))))))
(add-hook 'find-file-hooks 'vc-find-file-hook)
(define-key vc-prefix-map "a" 'vc-update-change-log)
(define-key vc-prefix-map "c" 'vc-cancel-version)
(define-key vc-prefix-map "d" 'vc-directory)
+ (define-key vc-prefix-map "g" 'vc-annotate)
(define-key vc-prefix-map "h" 'vc-insert-headers)
(define-key vc-prefix-map "i" 'vc-register)
(define-key vc-prefix-map "l" 'vc-print-log)
()
;;(define-key vc-menu-map [show-files]
;; '("Show Files under VC" . (vc-directory t)))
+ (define-key vc-menu-map [vc-retrieve-snapshot]
+ '("Retrieve Snapshot" . vc-retrieve-snapshot))
+ (define-key vc-menu-map [vc-create-snapshot]
+ '("Create Snapshot" . vc-create-snapshot))
(define-key vc-menu-map [vc-directory] '("Show Locked Files" . vc-directory))
(define-key vc-menu-map [separator1] '("----"))
+ (define-key vc-menu-map [vc-annotate] '("Annotate" . vc-annotate))
(define-key vc-menu-map [vc-rename-file] '("Rename File" . vc-rename-file))
(define-key vc-menu-map [vc-version-other-window]
'("Show Other Version" . vc-version-other-window))
'("Insert Header" . vc-insert-headers))
(define-key vc-menu-map [vc-menu-check-in] '("Check In" . vc-next-action))
(define-key vc-menu-map [vc-check-out] '("Check Out" . vc-toggle-read-only))
- (define-key vc-menu-map [vc-register] '("Register" . vc-register))
- (put 'vc-rename-file 'menu-enable 'vc-mode)
- (put 'vc-version-other-window 'menu-enable 'vc-mode)
- (put 'vc-diff 'menu-enable 'vc-mode)
- (put 'vc-update-change-log 'menu-enable
- '(eq (vc-buffer-backend) 'RCS))
- (put 'vc-print-log 'menu-enable 'vc-mode)
- (put 'vc-cancel-version 'menu-enable 'vc-mode)
- (put 'vc-revert-buffer 'menu-enable 'vc-mode)
- (put 'vc-insert-headers 'menu-enable 'vc-mode)
- (put 'vc-next-action 'menu-enable '(and vc-mode (not buffer-read-only)))
- (put 'vc-toggle-read-only 'menu-enable '(and vc-mode buffer-read-only))
- (put 'vc-register 'menu-enable '(and buffer-file-name (not vc-mode)))
- )
+ (define-key vc-menu-map [vc-register] '("Register" . vc-register)))
+
+(put 'vc-rename-file 'menu-enable 'vc-mode)
+(put 'vc-annotate 'menu-enable '(eq (vc-buffer-backend) 'CVS))
+(put 'vc-version-other-window 'menu-enable 'vc-mode)
+(put 'vc-diff 'menu-enable 'vc-mode)
+(put 'vc-update-change-log 'menu-enable
+ '(eq (vc-buffer-backend) 'RCS))
+(put 'vc-print-log 'menu-enable 'vc-mode)
+(put 'vc-cancel-version 'menu-enable 'vc-mode)
+(put 'vc-revert-buffer 'menu-enable 'vc-mode)
+(put 'vc-insert-headers 'menu-enable 'vc-mode)
+(put 'vc-next-action 'menu-enable 'vc-mode)
+(put 'vc-toggle-read-only 'menu-enable 'vc-mode)
+(put 'vc-register 'menu-enable '(and buffer-file-name (not vc-mode)))
(provide 'vc-hooks)