diff options
| author | André Spiegel | 1995-08-22 17:52:42 +0000 |
|---|---|---|
| committer | André Spiegel | 1995-08-22 17:52:42 +0000 |
| commit | 7064821ce79d0fb5d300bef06b268a4dac549fd0 (patch) | |
| tree | e4f47732a3ef61cd7dd91a85ab8b447179dc7841 | |
| parent | 8967cd6efdc50a046633c99ea12536786f09dffe (diff) | |
| download | emacs-7064821ce79d0fb5d300bef06b268a4dac549fd0.tar.gz emacs-7064821ce79d0fb5d300bef06b268a4dac549fd0.zip | |
(vc-simple-command): New function.
(vc-fetch-master-properties): CVS case: Use it.
(vc-lock-from-permissions, vc-file-owner, vc-rcs-lock-from-diff):
New functions.
(vc-locking-user): Largely rewritten. Uses the above, handles RCS
non-strict locking. Under CVS in CVSREAD-mode, learn the locking state
from the permissions.
(vc-find-cvs-master): Use vc-insert-file, rather than
find-file-noselect. Greatly speeds up things.
(vc-consult-rcs-headers): Bug fix, return status in all cases.
| -rw-r--r-- | lisp/vc-hooks.el | 265 |
1 files changed, 148 insertions, 117 deletions
diff --git a/lisp/vc-hooks.el b/lisp/vc-hooks.el index c46ddff3e46..eb251b096ec 100644 --- a/lisp/vc-hooks.el +++ b/lisp/vc-hooks.el | |||
| @@ -231,6 +231,29 @@ value of this flag.") | |||
| 231 | (vc-file-setprop file 'vc-checkout-model 'implicit)))) | 231 | (vc-file-setprop file 'vc-checkout-model 'implicit)))) |
| 232 | (vc-file-setprop file 'vc-master-locks (or master-locks 'none))))) | 232 | (vc-file-setprop file 'vc-master-locks (or master-locks 'none))))) |
| 233 | 233 | ||
| 234 | (defun vc-simple-command (okstatus command file &rest args) | ||
| 235 | ;; Simple version of vc-do-command, for use in vc-hooks only. | ||
| 236 | ;; Don't switch to the *vc-info* buffer before running the | ||
| 237 | ;; command, because that would change its default directory | ||
| 238 | (save-excursion (set-buffer (get-buffer-create "*vc-info*")) | ||
| 239 | (erase-buffer)) | ||
| 240 | (let ((exec-path (append vc-path exec-path)) exec-status | ||
| 241 | ;; Add vc-path to PATH for the execution of this command. | ||
| 242 | (process-environment | ||
| 243 | (cons (concat "PATH=" (getenv "PATH") | ||
| 244 | path-separator | ||
| 245 | (mapconcat 'identity vc-path path-separator)) | ||
| 246 | process-environment))) | ||
| 247 | (setq exec-status | ||
| 248 | (apply 'call-process command nil "*vc-info*" nil | ||
| 249 | (append args (list file)))) | ||
| 250 | (cond ((> exec-status okstatus) | ||
| 251 | (switch-to-buffer (get-file-buffer file)) | ||
| 252 | (shrink-window-if-larger-than-buffer | ||
| 253 | (display-buffer "*vc-info*")) | ||
| 254 | (error "Couldn't find version control information"))) | ||
| 255 | exec-status)) | ||
| 256 | |||
| 234 | (defun vc-fetch-master-properties (file) | 257 | (defun vc-fetch-master-properties (file) |
| 235 | ;; Fetch those properties of FILE that are stored in the master file. | 258 | ;; Fetch those properties of FILE that are stored in the master file. |
| 236 | ;; For an RCS file, we don't get vc-latest-version vc-your-latest-version | 259 | ;; For an RCS file, we don't get vc-latest-version vc-your-latest-version |
| @@ -287,51 +310,32 @@ value of this flag.") | |||
| 287 | (vc-parse-locks file (vc-file-getprop file 'vc-master-locks))) | 310 | (vc-parse-locks file (vc-file-getprop file 'vc-master-locks))) |
| 288 | 311 | ||
| 289 | ((eq (vc-backend file) 'CVS) | 312 | ((eq (vc-backend file) 'CVS) |
| 290 | ;; don't switch to the *vc-info* buffer before running the | 313 | (save-excursion |
| 291 | ;; command, because that would change its default directory | 314 | (vc-simple-command 0 "cvs" file "status") |
| 292 | (save-excursion (set-buffer (get-buffer-create "*vc-info*")) | 315 | (set-buffer (get-buffer "*vc-info*")) |
| 293 | (erase-buffer)) | 316 | (vc-parse-buffer |
| 294 | (let ((exec-path (append vc-path exec-path)) exec-status | 317 | ;; CVS 1.3 says "RCS Version:", other releases "RCS Revision:", |
| 295 | ;; Add vc-path to PATH for the execution of this command. | 318 | ;; and CVS 1.4a1 says "Repository revision:". |
| 296 | (process-environment | 319 | '(("\\(RCS Version\\|RCS Revision\\|Repository revision\\):[\t ]+\\([0-9.]+\\)" 2) |
| 297 | (cons (concat "PATH=" (getenv "PATH") | 320 | ("^File: [^ \t]+[ \t]+Status: \\(.*\\)" 1)) |
| 298 | path-separator | 321 | file |
| 299 | (mapconcat 'identity vc-path path-separator)) | 322 | '(vc-latest-version vc-cvs-status)) |
| 300 | process-environment))) | 323 | ;; Translate those status values that we understand into symbols. |
| 301 | (setq exec-status | 324 | ;; Any other value is converted to nil. |
| 302 | (apply 'call-process "cvs" nil "*vc-info*" nil | 325 | (let ((status (vc-file-getprop file 'vc-cvs-status))) |
| 303 | (list "status" file))) | 326 | (cond |
| 304 | (cond ((> exec-status 0) | 327 | ((string-match "Up-to-date" status) |
| 305 | (switch-to-buffer (get-file-buffer file)) | 328 | (vc-file-setprop file 'vc-cvs-status 'up-to-date) |
| 306 | (shrink-window-if-larger-than-buffer | 329 | (vc-file-setprop file 'vc-checkout-time |
| 307 | (display-buffer "*vc-info*")) | 330 | (nth 5 (file-attributes file)))) |
| 308 | (error "Couldn't find version control information")))) | 331 | ((vc-file-setprop file 'vc-cvs-status |
| 309 | (set-buffer (get-buffer "*vc-info*")) | ||
| 310 | (set-buffer-modified-p nil) | ||
| 311 | (auto-save-mode nil) | ||
| 312 | (vc-parse-buffer | ||
| 313 | ;; CVS 1.3 says "RCS Version:", other releases "RCS Revision:", | ||
| 314 | ;; and CVS 1.4a1 says "Repository revision:". | ||
| 315 | '(("\\(RCS Version\\|RCS Revision\\|Repository revision\\):[\t ]+\\([0-9.]+\\)" 2) | ||
| 316 | ("^File: [^ \t]+[ \t]+Status: \\(.*\\)" 1)) | ||
| 317 | file | ||
| 318 | '(vc-latest-version vc-cvs-status)) | ||
| 319 | ;; Translate those status values that are needed into symbols. | ||
| 320 | ;; Any other value is converted to nil. | ||
| 321 | (let ((status (vc-file-getprop file 'vc-cvs-status))) | ||
| 322 | (cond | ||
| 323 | ((string-match "Up-to-date" status) | ||
| 324 | (vc-file-setprop file 'vc-cvs-status 'up-to-date) | ||
| 325 | (vc-file-setprop file 'vc-checkout-time | ||
| 326 | (nth 5 (file-attributes file)))) | ||
| 327 | ((vc-file-setprop file 'vc-cvs-status | ||
| 328 | (cond | 332 | (cond |
| 329 | ((string-match "Locally Modified" status) 'locally-modified) | 333 | ((string-match "Locally Modified" status) 'locally-modified) |
| 330 | ((string-match "Needs Merge" status) 'needs-merge) | 334 | ((string-match "Needs Merge" status) 'needs-merge) |
| 331 | ((string-match "Needs Checkout" status) 'needs-checkout) | 335 | ((string-match "Needs Checkout" status) 'needs-checkout) |
| 332 | ((string-match "Unresolved Conflict" status) 'unresolved-conflict) | 336 | ((string-match "Unresolved Conflict" status) 'unresolved-conflict) |
| 333 | ((string-match "Locally Added" status) 'locally-added) | 337 | ((string-match "Locally Added" status) 'locally-added) |
| 334 | ))))))) | 338 | )))))))) |
| 335 | (if (get-buffer "*vc-info*") | 339 | (if (get-buffer "*vc-info*") |
| 336 | (kill-buffer (get-buffer "*vc-info*"))))) | 340 | (kill-buffer (get-buffer "*vc-info*"))))) |
| 337 | 341 | ||
| @@ -426,8 +430,8 @@ value of this flag.") | |||
| 426 | (not (vc-locking-user file)) | 430 | (not (vc-locking-user file)) |
| 427 | (if (string-match ".r-..-..-." (nth 8 (file-attributes file))) | 431 | (if (string-match ".r-..-..-." (nth 8 (file-attributes file))) |
| 428 | (vc-file-setprop file 'vc-checkout-model 'manual) | 432 | (vc-file-setprop file 'vc-checkout-model 'manual) |
| 429 | (vc-file-setprop file 'vc-checkout-model 'implicit)) | 433 | (vc-file-setprop file 'vc-checkout-model 'implicit))) |
| 430 | status))))) | 434 | status)))) |
| 431 | 435 | ||
| 432 | ;;; Access functions to file properties | 436 | ;;; Access functions to file properties |
| 433 | ;;; (Properties should be _set_ using vc-file-setprop, but | 437 | ;;; (Properties should be _set_ using vc-file-setprop, but |
| @@ -511,15 +515,65 @@ value of this flag.") | |||
| 511 | (cond (lock (cdr lock)) | 515 | (cond (lock (cdr lock)) |
| 512 | ('none))))) | 516 | ('none))))) |
| 513 | 517 | ||
| 518 | (defun vc-lock-from-permissions (file) | ||
| 519 | ;; If the permissions can be trusted for this file, determine the | ||
| 520 | ;; locking state from them. Returns (user-login-name), `none', or nil. | ||
| 521 | ;; This implementation assumes that any file which is under version | ||
| 522 | ;; control and has -rw-r--r-- is locked by its owner. This is true | ||
| 523 | ;; for both RCS and SCCS, which keep unlocked files at -r--r--r--. | ||
| 524 | ;; We have to be careful not to exclude files with execute bits on; | ||
| 525 | ;; scripts can be under version control too. Also, we must ignore the | ||
| 526 | ;; group-read and other-read bits, since paranoid users turn them off. | ||
| 527 | ;; This hack wins because calls to the somewhat expensive | ||
| 528 | ;; `vc-fetch-master-properties' function only have to be made if | ||
| 529 | ;; (a) the file is locked by someone other than the current user, | ||
| 530 | ;; or (b) some untoward manipulation behind vc's back has changed | ||
| 531 | ;; the owner or the `group' or `other' write bits. | ||
| 532 | (let ((attributes (file-attributes file))) | ||
| 533 | (if (not (vc-mistrust-permissions file)) | ||
| 534 | (cond ((string-match ".r-..-..-." (nth 8 attributes)) | ||
| 535 | (vc-file-setprop file 'vc-locking-user 'none)) | ||
| 536 | ((and (= (nth 2 attributes) (user-uid)) | ||
| 537 | (string-match ".rw..-..-." (nth 8 attributes))) | ||
| 538 | (vc-file-setprop file 'vc-locking-user (user-login-name))) | ||
| 539 | (nil))))) | ||
| 540 | |||
| 541 | (defun vc-file-owner (file) | ||
| 542 | ;; The expression below should return the username of the owner | ||
| 543 | ;; of the file. It doesn't. It returns the username if it is | ||
| 544 | ;; you, or otherwise the UID of the owner of the file. The | ||
| 545 | ;; return value from this function is only used by | ||
| 546 | ;; vc-dired-reformat-line, and it does the proper thing if a UID | ||
| 547 | ;; is returned. | ||
| 548 | ;; The *proper* way to fix this would be to implement a built-in | ||
| 549 | ;; function in Emacs, say, (username UID), that returns the | ||
| 550 | ;; username of a given UID. | ||
| 551 | ;; The result of this hack is that vc-directory will print the | ||
| 552 | ;; name of the owner of the file for any files that are | ||
| 553 | ;; modified. | ||
| 554 | (let ((uid (nth 2 (file-attributes file)))) | ||
| 555 | (if (= uid (user-uid)) (user-login-name) uid))) | ||
| 556 | |||
| 557 | (defun vc-rcs-lock-from-diff (file) | ||
| 558 | ;; Diff the file against the master version. If differences are found, | ||
| 559 | ;; mark the file locked. This is only meaningful for RCS with non-strict | ||
| 560 | ;; locking. | ||
| 561 | (if (zerop (vc-simple-command 1 "rcsdiff" file | ||
| 562 | "--brief" ; Some diffs don't understand "--brief", but | ||
| 563 | ; for non-strict locking under VC we require it. | ||
| 564 | (concat "-r" (vc-workfile-version file)))) | ||
| 565 | (vc-file-setprop file 'vc-locking-user 'none) | ||
| 566 | (vc-file-setprop file 'vc-locking-user (vc-file-owner file)))) | ||
| 567 | |||
| 514 | (defun vc-locking-user (file) | 568 | (defun vc-locking-user (file) |
| 515 | ;; Return the name of the person currently holding a lock on FILE. | 569 | ;; Return the name of the person currently holding a lock on FILE. |
| 516 | ;; Return nil if there is no such person. | 570 | ;; Return nil if there is no such person. (Sometimes, not the name |
| 571 | ;; of the locking user but his uid will be returned.) | ||
| 517 | ;; Under CVS, a file is considered locked if it has been modified since | 572 | ;; Under CVS, a file is considered locked if it has been modified since |
| 518 | ;; it was checked out. Under CVS, this will sometimes return the uid of | 573 | ;; it was checked out. |
| 519 | ;; the owner of the file (as a number) instead of a string. | ||
| 520 | ;; The property is cached. It is only looked up if it is currently nil. | 574 | ;; The property is cached. It is only looked up if it is currently nil. |
| 521 | ;; Note that, for a file that is not locked, the actual property value | 575 | ;; Note that, for a file that is not locked, the actual property value |
| 522 | ;; is 'none, to distinguish it from an unknown locking state. That value | 576 | ;; is `none', to distinguish it from an unknown locking state. That value |
| 523 | ;; is converted to nil by this function, and returned to the caller. | 577 | ;; is converted to nil by this function, and returned to the caller. |
| 524 | (let ((locking-user (vc-file-getprop file 'vc-locking-user))) | 578 | (let ((locking-user (vc-file-getprop file 'vc-locking-user))) |
| 525 | (if locking-user | 579 | (if locking-user |
| @@ -528,70 +582,51 @@ value of this flag.") | |||
| 528 | 582 | ||
| 529 | ;; otherwise, infer the property... | 583 | ;; otherwise, infer the property... |
| 530 | (cond | 584 | (cond |
| 531 | ;; in the CVS case, check the status | ||
| 532 | ((eq (vc-backend file) 'CVS) | 585 | ((eq (vc-backend file) 'CVS) |
| 533 | (if (or (eq (vc-cvs-status file) 'up-to-date) | 586 | (or (and (eq (vc-checkout-model file) 'manual) |
| 534 | (eq (vc-cvs-status file) 'needs-checkout)) | 587 | (vc-lock-from-permissions file)) |
| 535 | (vc-file-setprop file 'vc-locking-user 'none) | 588 | (if (or (eq (vc-cvs-status file) 'up-to-date) |
| 536 | ;; The expression below should return the username of the owner | 589 | (eq (vc-cvs-status file) 'needs-checkout)) |
| 537 | ;; of the file. It doesn't. It returns the username if it is | 590 | (vc-file-setprop file 'vc-locking-user 'none) |
| 538 | ;; you, or otherwise the UID of the owner of the file. The | 591 | (vc-file-setprop file 'vc-locking-user (vc-file-owner file))))) |
| 539 | ;; return value from this function is only used by | 592 | |
| 540 | ;; vc-dired-reformat-line, and it does the proper thing if a UID | 593 | ((eq (vc-backend file) 'RCS) |
| 541 | ;; is returned. | 594 | (let (p-lock) |
| 542 | ;; | 595 | |
| 543 | ;; The *proper* way to fix this would be to implement a built-in | 596 | ;; Check for RCS headers first |
| 544 | ;; function in Emacs, say, (username UID), that returns the | 597 | (or (eq (vc-consult-rcs-headers file) 'rev-and-lock) |
| 545 | ;; username of a given UID. | 598 | |
| 546 | ;; | 599 | ;; If there are no headers, try to learn it |
| 547 | ;; The result of this hack is that vc-directory will print the | 600 | ;; from the permissions. |
| 548 | ;; name of the owner of the file for any files that are | 601 | (and (setq p-lock (vc-lock-from-permissions file)) |
| 549 | ;; modified. | 602 | (if (eq p-lock 'none) |
| 550 | (let ((uid (nth 2 (file-attributes file)))) | 603 | |
| 551 | (if (= uid (user-uid)) | 604 | ;; If the permissions say "not locked", we know |
| 552 | (vc-file-setprop file 'vc-locking-user (user-login-name)) | 605 | ;; that the checkout model must be `manual'. |
| 553 | (vc-file-setprop file 'vc-locking-user uid))))) | 606 | (vc-file-setprop file 'vc-checkout-model 'manual) |
| 554 | 607 | ||
| 555 | ;; RCS case: attempt a header search. If this feature is | 608 | ;; If the permissions say "locked", we can only trust |
| 556 | ;; disabled, vc-consult-rcs-headers always returns nil. | 609 | ;; this *if* the checkout model is `manual'. |
| 557 | ((and (eq (vc-backend file) 'RCS) | 610 | (eq (vc-checkout-model file) 'manual))) |
| 558 | (eq (vc-consult-rcs-headers file) 'rev-and-lock))) | 611 | |
| 559 | 612 | ;; Otherwise, use lock information from the master file. | |
| 560 | ;; if the file permissions are not trusted, | 613 | (vc-file-setprop file 'vc-locking-user |
| 561 | ;; or if locking is not strict, | 614 | (vc-master-locking-user file))) |
| 562 | ;; use the information from the master file | 615 | |
| 563 | ((or (not vc-keep-workfiles) | 616 | ;; Finally, if the file is not explicitly locked |
| 564 | (vc-mistrust-permissions file) | 617 | ;; it might still be locked implicitly. |
| 565 | (eq (vc-checkout-model file) 'implicit)) | 618 | (and (eq (vc-file-getprop file 'vc-locking-user) 'none) |
| 566 | (vc-file-setprop file 'vc-locking-user (vc-master-locking-user file))) | 619 | (eq (vc-checkout-model file) 'implicit) |
| 567 | 620 | (vc-rcs-lock-from-diff file)))) | |
| 568 | ;; Otherwise: Use the file permissions. (But if it turns out that the | 621 | |
| 569 | ;; file is not owned by the user, use the master file.) | 622 | ((eq (vc-backend file) 'SCCS) |
| 570 | ;; This implementation assumes that any file which is under version | 623 | (or (vc-lock-from-permissions file) |
| 571 | ;; control and has -rw-r--r-- is locked by its owner. This is true | 624 | (vc-file-setprop file 'vc-locking-user |
| 572 | ;; for both RCS and SCCS, which keep unlocked files at -r--r--r--. | 625 | (vc-master-locking-user file)))))) |
| 573 | ;; We have to be careful not to exclude files with execute bits on; | 626 | |
| 574 | ;; scripts can be under version control too. Also, we must ignore the | 627 | ;; convert a possible 'none value |
| 575 | ;; group-read and other-read bits, since paranoid users turn them off. | 628 | (setq locking-user (vc-file-getprop file 'vc-locking-user)) |
| 576 | ;; This hack wins because calls to the somewhat expensive | 629 | (if (eq locking-user 'none) nil locking-user))) |
| 577 | ;; `vc-fetch-master-properties' function only have to be made if | ||
| 578 | ;; (a) the file is locked by someone other than the current user, | ||
| 579 | ;; or (b) some untoward manipulation behind vc's back has changed | ||
| 580 | ;; the owner or the `group' or `other' write bits. | ||
| 581 | (t | ||
| 582 | (let ((attributes (file-attributes file))) | ||
| 583 | (cond ((string-match ".r-..-..-." (nth 8 attributes)) | ||
| 584 | (vc-file-setprop file 'vc-locking-user 'none)) | ||
| 585 | ((and (= (nth 2 attributes) (user-uid)) | ||
| 586 | (string-match ".rw..-..-." (nth 8 attributes))) | ||
| 587 | (vc-file-setprop file 'vc-locking-user (user-login-name))) | ||
| 588 | (t | ||
| 589 | (vc-file-setprop file 'vc-locking-user | ||
| 590 | (vc-master-locking-user file)))) | ||
| 591 | ))) | ||
| 592 | ;; recursively call the function again, | ||
| 593 | ;; to convert a possible 'none value | ||
| 594 | (vc-locking-user file)))) | ||
| 595 | 630 | ||
| 596 | ;;; properties to store current and recent version numbers | 631 | ;;; properties to store current and recent version numbers |
| 597 | 632 | ||
| @@ -704,12 +739,11 @@ value of this flag.") | |||
| 704 | (file-directory-p (concat dirname "CVS/")) | 739 | (file-directory-p (concat dirname "CVS/")) |
| 705 | (file-readable-p (concat dirname "CVS/Entries")) | 740 | (file-readable-p (concat dirname "CVS/Entries")) |
| 706 | (file-readable-p (concat dirname "CVS/Repository"))) | 741 | (file-readable-p (concat dirname "CVS/Repository"))) |
| 707 | (let ((bufs nil) (fold case-fold-search)) | 742 | (let (buffer (fold case-fold-search)) |
| 708 | (unwind-protect | 743 | (unwind-protect |
| 709 | (save-excursion | 744 | (save-excursion |
| 710 | (setq bufs (list | 745 | (setq buffer (set-buffer (get-buffer-create "*vc-info*"))) |
| 711 | (find-file-noselect (concat dirname "CVS/Entries")))) | 746 | (vc-insert-file (concat dirname "CVS/Entries")) |
| 712 | (set-buffer (car bufs)) | ||
| 713 | (goto-char (point-min)) | 747 | (goto-char (point-min)) |
| 714 | ;; make sure the file name is searched | 748 | ;; make sure the file name is searched |
| 715 | ;; case-sensitively | 749 | ;; case-sensitively |
| @@ -725,10 +759,7 @@ value of this flag.") | |||
| 725 | 'vc-workfile-version | 759 | 'vc-workfile-version |
| 726 | (buffer-substring (match-beginning 1) | 760 | (buffer-substring (match-beginning 1) |
| 727 | (match-end 1))) | 761 | (match-end 1))) |
| 728 | (setq bufs (cons (find-file-noselect | 762 | (vc-insert-file (concat dirname "CVS/Repository")) |
| 729 | (concat dirname "CVS/Repository")) | ||
| 730 | bufs)) | ||
| 731 | (set-buffer (car bufs)) | ||
| 732 | (let ((master | 763 | (let ((master |
| 733 | (concat (file-name-as-directory | 764 | (concat (file-name-as-directory |
| 734 | (buffer-substring (point-min) | 765 | (buffer-substring (point-min) |
| @@ -738,7 +769,7 @@ value of this flag.") | |||
| 738 | (throw 'found (cons master 'CVS)))) | 769 | (throw 'found (cons master 'CVS)))) |
| 739 | (t (setq case-fold-search fold) ;; restore the old value | 770 | (t (setq case-fold-search fold) ;; restore the old value |
| 740 | nil))) | 771 | nil))) |
| 741 | (mapcar (function kill-buffer) bufs))))) | 772 | (kill-buffer buffer))))) |
| 742 | 773 | ||
| 743 | (defun vc-buffer-backend () | 774 | (defun vc-buffer-backend () |
| 744 | "Return the version-control type of the visited file, or nil if none." | 775 | "Return the version-control type of the visited file, or nil if none." |