diff options
| author | Eric S. Raymond | 2014-12-08 06:11:49 -0500 |
|---|---|---|
| committer | Eric S. Raymond | 2014-12-08 06:11:49 -0500 |
| commit | 7fb8fc35ebf980ed299ce9dfd1694fa0f1ea169b (patch) | |
| tree | 69e17185e659db146b4ae4ec381503abdf5e952d /lisp/obsolete | |
| parent | eb5b08bef5af19203b88d64b9052ad4ad5ea2eba (diff) | |
| download | emacs-7fb8fc35ebf980ed299ce9dfd1694fa0f1ea169b.tar.gz emacs-7fb8fc35ebf980ed299ce9dfd1694fa0f1ea169b.zip | |
vc/vc-arch.el: Moved to obsolete directory...
...so a test framework won't trip over bit-rot in it. There has been no
Arch snapshot for nine years.
Diffstat (limited to 'lisp/obsolete')
| -rw-r--r-- | lisp/obsolete/vc-arch.el | 644 |
1 files changed, 644 insertions, 0 deletions
diff --git a/lisp/obsolete/vc-arch.el b/lisp/obsolete/vc-arch.el new file mode 100644 index 00000000000..d1344f2b1cc --- /dev/null +++ b/lisp/obsolete/vc-arch.el | |||
| @@ -0,0 +1,644 @@ | |||
| 1 | ;;; vc-arch.el --- VC backend for the Arch version-control system -*- lexical-binding: t -*- | ||
| 2 | |||
| 3 | ;; Copyright (C) 2004-2014 Free Software Foundation, Inc. | ||
| 4 | |||
| 5 | ;; Author: FSF (see vc.el for full credits) | ||
| 6 | ;; Maintainer: Stefan Monnier <monnier@gnu.org> | ||
| 7 | ;; Package: vc | ||
| 8 | |||
| 9 | ;; This file is part of GNU Emacs. | ||
| 10 | |||
| 11 | ;; GNU Emacs is free software: you can redistribute it and/or modify | ||
| 12 | ;; it under the terms of the GNU General Public License as published by | ||
| 13 | ;; the Free Software Foundation, either version 3 of the License, or | ||
| 14 | ;; (at your option) any later version. | ||
| 15 | |||
| 16 | ;; GNU Emacs is distributed in the hope that it will be useful, | ||
| 17 | ;; but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 18 | ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 19 | ;; GNU General Public License for more details. | ||
| 20 | |||
| 21 | ;; You should have received a copy of the GNU General Public License | ||
| 22 | ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. | ||
| 23 | |||
| 24 | ;;; Commentary: | ||
| 25 | |||
| 26 | ;; The home page of the Arch version control system is at | ||
| 27 | ;; | ||
| 28 | ;; http://www.gnuarch.org/ | ||
| 29 | ;; | ||
| 30 | ;; This is derived from vc-mcvs.el as follows: | ||
| 31 | ;; - cp vc-mcvs.el vc-arch.el and then M-% mcvs RET arch RET | ||
| 32 | ;; | ||
| 33 | ;; Then of course started the hacking. | ||
| 34 | ;; | ||
| 35 | ;; What has been partly tested: | ||
| 36 | ;; - Open a file. | ||
| 37 | ;; - C-x v = without any prefix arg. | ||
| 38 | ;; - C-x v v to commit a change to a single file. | ||
| 39 | |||
| 40 | ;; Bugs: | ||
| 41 | |||
| 42 | ;; - *vc-log*'s initial content lacks the `Summary:' lines. | ||
| 43 | ;; - All files under the tree are considered as "under Arch's control" | ||
| 44 | ;; without regards to =tagging-method and such. | ||
| 45 | ;; - Files are always considered as `edited'. | ||
| 46 | ;; - C-x v l does not work. | ||
| 47 | ;; - C-x v i does not work. | ||
| 48 | ;; - C-x v ~ does not work. | ||
| 49 | ;; - C-x v u does not work. | ||
| 50 | ;; - C-x v s does not work. | ||
| 51 | ;; - C-x v r does not work. | ||
| 52 | ;; - VC directory listings do not work. | ||
| 53 | ;; - And more... | ||
| 54 | |||
| 55 | ;;; Code: | ||
| 56 | |||
| 57 | (eval-when-compile (require 'vc)) | ||
| 58 | |||
| 59 | ;;; Properties of the backend | ||
| 60 | |||
| 61 | (defun vc-arch-revision-granularity () 'repository) | ||
| 62 | (defun vc-arch-checkout-model (_files) 'implicit) | ||
| 63 | |||
| 64 | ;;; | ||
| 65 | ;;; Customization options | ||
| 66 | ;;; | ||
| 67 | |||
| 68 | (defgroup vc-arch nil | ||
| 69 | "VC Arch backend." | ||
| 70 | :version "24.1" | ||
| 71 | :group 'vc) | ||
| 72 | |||
| 73 | ;; It seems Arch diff does not accept many options, so this is not | ||
| 74 | ;; very useful. It exists mainly so that the VC backends are all | ||
| 75 | ;; consistent with regards to their treatment of diff switches. | ||
| 76 | (defcustom vc-arch-diff-switches t | ||
| 77 | "String or list of strings specifying switches for Arch diff under VC. | ||
| 78 | If nil, use the value of `vc-diff-switches'. If t, use no switches." | ||
| 79 | :type '(choice (const :tag "Unspecified" nil) | ||
| 80 | (const :tag "None" t) | ||
| 81 | (string :tag "Argument String") | ||
| 82 | (repeat :tag "Argument List" :value ("") string)) | ||
| 83 | :version "23.1" | ||
| 84 | :group 'vc-arch) | ||
| 85 | |||
| 86 | (define-obsolete-variable-alias 'vc-arch-command 'vc-arch-program "23.1") | ||
| 87 | |||
| 88 | (defcustom vc-arch-program | ||
| 89 | (let ((candidates '("tla" "baz"))) | ||
| 90 | (while (and candidates (not (executable-find (car candidates)))) | ||
| 91 | (setq candidates (cdr candidates))) | ||
| 92 | (or (car candidates) "tla")) | ||
| 93 | "Name of the Arch executable." | ||
| 94 | :type 'string | ||
| 95 | :group 'vc-arch) | ||
| 96 | |||
| 97 | ;; Clear up the cache to force vc-call to check again and discover | ||
| 98 | ;; new functions when we reload this file. | ||
| 99 | (put 'Arch 'vc-functions nil) | ||
| 100 | |||
| 101 | ;;;###autoload (defun vc-arch-registered (file) | ||
| 102 | ;;;###autoload (if (vc-find-root file "{arch}/=tagging-method") | ||
| 103 | ;;;###autoload (progn | ||
| 104 | ;;;###autoload (load "vc-arch" nil t) | ||
| 105 | ;;;###autoload (vc-arch-registered file)))) | ||
| 106 | |||
| 107 | (defun vc-arch-add-tagline () | ||
| 108 | "Add an `arch-tag' to the end of the current file." | ||
| 109 | (interactive) | ||
| 110 | (comment-normalize-vars) | ||
| 111 | (goto-char (point-max)) | ||
| 112 | (forward-comment -1) | ||
| 113 | (skip-chars-forward " \t\n") | ||
| 114 | (cond | ||
| 115 | ((not (bolp)) (insert "\n\n")) | ||
| 116 | ((not (eq ?\n (char-before (1- (point))))) (insert "\n"))) | ||
| 117 | (let ((beg (point)) | ||
| 118 | (idfile (and buffer-file-name | ||
| 119 | (expand-file-name | ||
| 120 | (concat ".arch-ids/" | ||
| 121 | (file-name-nondirectory buffer-file-name) | ||
| 122 | ".id") | ||
| 123 | (file-name-directory buffer-file-name))))) | ||
| 124 | (insert "arch-tag: ") | ||
| 125 | (if (and idfile (file-exists-p idfile)) | ||
| 126 | ;; If the file is unreadable, we do want to get an error here. | ||
| 127 | (progn | ||
| 128 | (insert-file-contents idfile) | ||
| 129 | (forward-line 1) | ||
| 130 | (delete-file idfile)) | ||
| 131 | (condition-case nil | ||
| 132 | (call-process "uuidgen" nil t) | ||
| 133 | (file-error (insert (format "%s <%s> %s" | ||
| 134 | (current-time-string) | ||
| 135 | user-mail-address | ||
| 136 | (+ (nth 2 (current-time)) | ||
| 137 | (buffer-size))))))) | ||
| 138 | (comment-region beg (point)))) | ||
| 139 | |||
| 140 | (defconst vc-arch-tagline-re "^\\W*arch-tag:[ \t]*\\(.*[^ \t\n]\\)") | ||
| 141 | |||
| 142 | (defmacro vc-with-current-file-buffer (file &rest body) | ||
| 143 | (declare (indent 2) (debug t)) | ||
| 144 | `(let ((-kill-buf- nil) | ||
| 145 | (-file- ,file)) | ||
| 146 | (with-current-buffer (or (find-buffer-visiting -file-) | ||
| 147 | (setq -kill-buf- (generate-new-buffer " temp"))) | ||
| 148 | ;; Avoid find-file-literally since it can do many undesirable extra | ||
| 149 | ;; things (among which, call us back into an infinite loop). | ||
| 150 | (if -kill-buf- (insert-file-contents -file-)) | ||
| 151 | (unwind-protect | ||
| 152 | (progn ,@body) | ||
| 153 | (if (buffer-live-p -kill-buf-) (kill-buffer -kill-buf-)))))) | ||
| 154 | |||
| 155 | (defun vc-arch-file-source-p (file) | ||
| 156 | "Can return nil, `maybe' or a non-nil value. | ||
| 157 | Only the value `maybe' can be trusted :-(." | ||
| 158 | ;; FIXME: Check the tag and name of parent dirs. | ||
| 159 | (unless (string-match "\\`[,+]" (file-name-nondirectory file)) | ||
| 160 | (or (string-match "\\`{arch}/" | ||
| 161 | (file-relative-name file (vc-arch-root file))) | ||
| 162 | (file-exists-p | ||
| 163 | ;; Check the presence of an ID file. | ||
| 164 | (expand-file-name | ||
| 165 | (concat ".arch-ids/" (file-name-nondirectory file) ".id") | ||
| 166 | (file-name-directory file))) | ||
| 167 | ;; Check the presence of a tagline. | ||
| 168 | (vc-with-current-file-buffer file | ||
| 169 | (save-excursion | ||
| 170 | (goto-char (point-max)) | ||
| 171 | (or (re-search-backward vc-arch-tagline-re (- (point) 1000) t) | ||
| 172 | (progn | ||
| 173 | (goto-char (point-min)) | ||
| 174 | (re-search-forward vc-arch-tagline-re (+ (point) 1000) t))))) | ||
| 175 | ;; FIXME: check =tagging-method to see whether untagged files might | ||
| 176 | ;; be source or not. | ||
| 177 | (with-current-buffer | ||
| 178 | (find-file-noselect (expand-file-name "{arch}/=tagging-method" | ||
| 179 | (vc-arch-root file))) | ||
| 180 | (let ((untagged-source t)) ;Default is `names'. | ||
| 181 | (save-excursion | ||
| 182 | (goto-char (point-min)) | ||
| 183 | (if (re-search-forward "^[ \t]*\\(\\(tagline\\|implicit\\|names\\)\\|explicit\\)" nil t) | ||
| 184 | (setq untagged-source (match-end 2))) | ||
| 185 | (if (re-search-forward "^[ \t]*untagged-source[ \t]+\\(\\(source\\)\\|precious\\|backup\\|junk\\|unrecognized\\)" nil t) | ||
| 186 | (setq untagged-source (match-end 2)))) | ||
| 187 | (if untagged-source 'maybe)))))) | ||
| 188 | |||
| 189 | (defun vc-arch-file-id (file) | ||
| 190 | ;; Don't include the kind of ID this is because it seems to be too messy. | ||
| 191 | (let ((idfile (expand-file-name | ||
| 192 | (concat ".arch-ids/" (file-name-nondirectory file) ".id") | ||
| 193 | (file-name-directory file)))) | ||
| 194 | (if (file-exists-p idfile) | ||
| 195 | (with-temp-buffer | ||
| 196 | (insert-file-contents idfile) | ||
| 197 | (looking-at ".*[^ \n\t]") | ||
| 198 | (match-string 0)) | ||
| 199 | (with-current-buffer (find-file-noselect file) | ||
| 200 | (save-excursion | ||
| 201 | (goto-char (point-max)) | ||
| 202 | (if (or (re-search-backward vc-arch-tagline-re (- (point) 1000) t) | ||
| 203 | (progn | ||
| 204 | (goto-char (point-min)) | ||
| 205 | (re-search-forward vc-arch-tagline-re (+ (point) 1000) t))) | ||
| 206 | (match-string 1) | ||
| 207 | (concat "./" (file-relative-name file (vc-arch-root file))))))))) | ||
| 208 | |||
| 209 | (defun vc-arch-tagging-method (file) | ||
| 210 | (with-current-buffer | ||
| 211 | (find-file-noselect | ||
| 212 | (expand-file-name "{arch}/=tagging-method" (vc-arch-root file))) | ||
| 213 | (save-excursion | ||
| 214 | (goto-char (point-min)) | ||
| 215 | (if (re-search-forward | ||
| 216 | "^[ \t]*\\(tagline\\|implicit\\|names\\|explicit\\)" nil t) | ||
| 217 | (intern (match-string 1)) | ||
| 218 | 'names)))) | ||
| 219 | |||
| 220 | (defun vc-arch-root (file) | ||
| 221 | "Return the root directory of an Arch project, if any." | ||
| 222 | (or (vc-file-getprop file 'arch-root) | ||
| 223 | ;; Check the =tagging-method, in case someone naively manually | ||
| 224 | ;; creates a {arch} directory somewhere. | ||
| 225 | (let ((root (vc-find-root file "{arch}/=tagging-method"))) | ||
| 226 | (when root | ||
| 227 | (vc-file-setprop | ||
| 228 | file 'arch-root root))))) | ||
| 229 | |||
| 230 | (defun vc-arch-find-admin-dir (file) | ||
| 231 | "Return the administrative directory of FILE." | ||
| 232 | (expand-file-name "{arch}" (vc-arch-root file))) | ||
| 233 | |||
| 234 | (defun vc-arch-register (files &optional _comment) | ||
| 235 | (dolist (file files) | ||
| 236 | (let ((tagmet (vc-arch-tagging-method file))) | ||
| 237 | (if (and (memq tagmet '(tagline implicit)) comment-start) | ||
| 238 | (with-current-buffer (find-file-noselect file) | ||
| 239 | (if (buffer-modified-p) | ||
| 240 | (error "Save %s first" (buffer-name))) | ||
| 241 | (vc-arch-add-tagline) | ||
| 242 | (save-buffer))))) | ||
| 243 | (vc-arch-command nil 0 files "add")) | ||
| 244 | |||
| 245 | (defun vc-arch-registered (file) | ||
| 246 | ;; Don't seriously check whether it's source or not. Checking would | ||
| 247 | ;; require running TLA, so it's better to not do it, so it also works if | ||
| 248 | ;; TLA is not installed. | ||
| 249 | (and (vc-arch-root file) | ||
| 250 | (vc-arch-file-source-p file))) | ||
| 251 | |||
| 252 | (defun vc-arch-default-version (file) | ||
| 253 | (or (vc-file-getprop (vc-arch-root file) 'arch-default-version) | ||
| 254 | (let* ((root (vc-arch-root file)) | ||
| 255 | (f (expand-file-name "{arch}/++default-version" root))) | ||
| 256 | (if (file-readable-p f) | ||
| 257 | (vc-file-setprop | ||
| 258 | root 'arch-default-version | ||
| 259 | (with-temp-buffer | ||
| 260 | (insert-file-contents f) | ||
| 261 | ;; Strip the terminating newline. | ||
| 262 | (buffer-substring (point-min) (1- (point-max))))))))) | ||
| 263 | |||
| 264 | (defun vc-arch-state (file) | ||
| 265 | ;; There's no checkout operation and merging is not done from VC | ||
| 266 | ;; so the only operation that's state dependent that VC supports is commit | ||
| 267 | ;; which is only activated if the file is `edited'. | ||
| 268 | (let* ((root (vc-arch-root file)) | ||
| 269 | (ver (vc-arch-default-version file)) | ||
| 270 | (pat (concat "\\`" (subst-char-in-string ?/ ?% ver))) | ||
| 271 | (dir (expand-file-name ",,inode-sigs/" | ||
| 272 | (expand-file-name "{arch}" root))) | ||
| 273 | (sigfile nil)) | ||
| 274 | (dolist (f (if (file-directory-p dir) (directory-files dir t pat))) | ||
| 275 | (if (or (not sigfile) (file-newer-than-file-p f sigfile)) | ||
| 276 | (setq sigfile f))) | ||
| 277 | (if (not sigfile) | ||
| 278 | 'edited ;We know nothing. | ||
| 279 | (let ((id (vc-arch-file-id file))) | ||
| 280 | (setq id (replace-regexp-in-string "[ \t]" "_" id)) | ||
| 281 | (with-current-buffer (find-file-noselect sigfile) | ||
| 282 | (goto-char (point-min)) | ||
| 283 | (while (and (search-forward id nil 'move) | ||
| 284 | (save-excursion | ||
| 285 | (goto-char (- (match-beginning 0) 2)) | ||
| 286 | ;; For `names', the lines start with `?./foo/bar'. | ||
| 287 | ;; For others there's 2 chars before the ./foo/bar. | ||
| 288 | (or (not (or (bolp) (looking-at "\n?"))) | ||
| 289 | ;; Ignore E_ entries used for foo.id files. | ||
| 290 | (looking-at "E_"))))) | ||
| 291 | (if (eobp) | ||
| 292 | ;; ID not found. | ||
| 293 | (if (equal (file-name-nondirectory sigfile) | ||
| 294 | (subst-char-in-string | ||
| 295 | ?/ ?% (vc-arch-working-revision file))) | ||
| 296 | 'added | ||
| 297 | ;; Might be `added' or `up-to-date' as well. | ||
| 298 | ;; FIXME: Check in the patch logs to find out. | ||
| 299 | 'edited) | ||
| 300 | ;; Found the ID, let's check the inode. | ||
| 301 | (if (not (re-search-forward | ||
| 302 | "\t.*mtime=\\([0-9]+\\):size=\\([0-9]+\\)" | ||
| 303 | (line-end-position) t)) | ||
| 304 | ;; Buh? Unexpected format. | ||
| 305 | 'edited | ||
| 306 | (let ((ats (file-attributes file))) | ||
| 307 | (if (and (eq (nth 7 ats) (string-to-number (match-string 2))) | ||
| 308 | (equal (format-time-string "%s" (nth 5 ats)) | ||
| 309 | (match-string 1))) | ||
| 310 | 'up-to-date | ||
| 311 | 'edited))))))))) | ||
| 312 | |||
| 313 | ;; dir-status-files called from vc-dir, which loads vc, | ||
| 314 | ;; which loads vc-dispatcher. | ||
| 315 | (declare-function vc-exec-after "vc-dispatcher" (code)) | ||
| 316 | |||
| 317 | (defun vc-arch-dir-status-files (dir _files callback) | ||
| 318 | "Run 'tla inventory' for DIR and pass results to CALLBACK. | ||
| 319 | CALLBACK expects (ENTRIES &optional MORE-TO-COME); see | ||
| 320 | `vc-dir-refresh'." | ||
| 321 | (let ((default-directory dir)) | ||
| 322 | (vc-arch-command t 'async nil "changes")) | ||
| 323 | ;; The updating could be done asynchronously. | ||
| 324 | (vc-run-delayed | ||
| 325 | (vc-arch-after-dir-status callback))) | ||
| 326 | |||
| 327 | (defun vc-arch-after-dir-status (callback) | ||
| 328 | (let* ((state-map '(("M " . edited) | ||
| 329 | ("Mb" . edited) ;binary | ||
| 330 | ("D " . removed) | ||
| 331 | ("D/" . removed) ;directory | ||
| 332 | ("A " . added) | ||
| 333 | ("A/" . added) ;directory | ||
| 334 | ("=>" . renamed) | ||
| 335 | ("/>" . renamed) ;directory | ||
| 336 | ("lf" . symlink-to-file) | ||
| 337 | ("fl" . file-to-symlink) | ||
| 338 | ("--" . permissions-changed) | ||
| 339 | ("-/" . permissions-changed) ;directory | ||
| 340 | )) | ||
| 341 | (state-map-regexp (regexp-opt (mapcar 'car state-map) t)) | ||
| 342 | (entry-regexp (concat "^" state-map-regexp " \\(.*\\)$")) | ||
| 343 | result) | ||
| 344 | (goto-char (point-min)) | ||
| 345 | ;;(message "Got %s" (buffer-string)) | ||
| 346 | (while (re-search-forward entry-regexp nil t) | ||
| 347 | (let* ((state-string (match-string 1)) | ||
| 348 | (state (cdr (assoc state-string state-map))) | ||
| 349 | (filename (match-string 2))) | ||
| 350 | (push (list filename state) result))) | ||
| 351 | |||
| 352 | (funcall callback result nil))) | ||
| 353 | |||
| 354 | (defun vc-arch-working-revision (file) | ||
| 355 | (let* ((root (expand-file-name "{arch}" (vc-arch-root file))) | ||
| 356 | (defbranch (vc-arch-default-version file))) | ||
| 357 | (when (and defbranch (string-match "\\`\\(.+@[^/\n]+\\)/\\(\\(\\(.*?\\)\\(?:--.*\\)?\\)--.*\\)\\'" defbranch)) | ||
| 358 | (let* ((archive (match-string 1 defbranch)) | ||
| 359 | (category (match-string 4 defbranch)) | ||
| 360 | (branch (match-string 3 defbranch)) | ||
| 361 | (version (match-string 2 defbranch)) | ||
| 362 | (sealed nil) (rev-nb 0) | ||
| 363 | (rev nil) | ||
| 364 | logdir tmp) | ||
| 365 | (setq logdir (expand-file-name category root)) | ||
| 366 | (setq logdir (expand-file-name branch logdir)) | ||
| 367 | (setq logdir (expand-file-name version logdir)) | ||
| 368 | (setq logdir (expand-file-name archive logdir)) | ||
| 369 | (setq logdir (expand-file-name "patch-log" logdir)) | ||
| 370 | (dolist (file (if (file-directory-p logdir) (directory-files logdir))) | ||
| 371 | ;; Revision names go: base-0, patch-N, version-0, versionfix-M. | ||
| 372 | (when (and (eq (aref file 0) ?v) (not sealed)) | ||
| 373 | (setq sealed t rev-nb 0)) | ||
| 374 | (if (and (string-match "-\\([0-9]+\\)\\'" file) | ||
| 375 | (setq tmp (string-to-number (match-string 1 file))) | ||
| 376 | (or (not sealed) (eq (aref file 0) ?v)) | ||
| 377 | (>= tmp rev-nb)) | ||
| 378 | (setq rev-nb tmp rev file))) | ||
| 379 | ;; Use "none-000" if the tree hasn't yet been committed on the | ||
| 380 | ;; default branch. We'll then get "Arch:000[branch]" on the mode-line. | ||
| 381 | (concat defbranch "--" (or rev "none-000")))))) | ||
| 382 | |||
| 383 | |||
| 384 | (defcustom vc-arch-mode-line-rewrite | ||
| 385 | '(("\\`.*--\\(.*--.*\\)--\\(v?\\).*-\\([0-9]+\\)\\'" . "\\2\\3[\\1]")) | ||
| 386 | "Rewrite rules to shorten Arch's revision names on the mode-line." | ||
| 387 | :type '(repeat (cons regexp string)) | ||
| 388 | :group 'vc-arch) | ||
| 389 | |||
| 390 | (defun vc-arch-mode-line-string (file) | ||
| 391 | "Return a string for `vc-mode-line' to put in the mode line for FILE." | ||
| 392 | (let ((rev (vc-working-revision file))) | ||
| 393 | (dolist (rule vc-arch-mode-line-rewrite) | ||
| 394 | (if (string-match (car rule) rev) | ||
| 395 | (setq rev (replace-match (cdr rule) t nil rev)))) | ||
| 396 | (format "Arch%c%s" | ||
| 397 | (pcase (vc-state file) | ||
| 398 | ((or `up-to-date `needs-update) ?-) | ||
| 399 | (`added ?@) | ||
| 400 | (t ?:)) | ||
| 401 | rev))) | ||
| 402 | |||
| 403 | (defun vc-arch-diff3-rej-p (rej) | ||
| 404 | (let ((attrs (file-attributes rej))) | ||
| 405 | (and attrs (< (nth 7 attrs) 60) | ||
| 406 | (with-temp-buffer | ||
| 407 | (insert-file-contents rej) | ||
| 408 | (goto-char (point-min)) | ||
| 409 | (looking-at "Conflicts occurred, diff3 conflict markers left in file\\."))))) | ||
| 410 | |||
| 411 | (defun vc-arch-delete-rej-if-obsolete () | ||
| 412 | "For use in `after-save-hook'." | ||
| 413 | (save-excursion | ||
| 414 | (let ((rej (concat buffer-file-name ".rej"))) | ||
| 415 | (when (and buffer-file-name (vc-arch-diff3-rej-p rej)) | ||
| 416 | (unless (re-search-forward "^<<<<<<< " nil t) | ||
| 417 | ;; The .rej file is obsolete. | ||
| 418 | (condition-case nil (delete-file rej) (error nil)) | ||
| 419 | ;; Remove the hook so that it is not called multiple times. | ||
| 420 | (remove-hook 'after-save-hook 'vc-arch-delete-rej-if-obsolete t)))))) | ||
| 421 | |||
| 422 | (defun vc-arch-find-file-hook () | ||
| 423 | (let ((rej (concat buffer-file-name ".rej"))) | ||
| 424 | (when (and buffer-file-name (file-exists-p rej)) | ||
| 425 | (if (vc-arch-diff3-rej-p rej) | ||
| 426 | (save-excursion | ||
| 427 | (goto-char (point-min)) | ||
| 428 | (if (not (re-search-forward "^<<<<<<< " nil t)) | ||
| 429 | ;; The .rej file is obsolete. | ||
| 430 | (condition-case nil (delete-file rej) (error nil)) | ||
| 431 | (smerge-mode 1) | ||
| 432 | (add-hook 'after-save-hook | ||
| 433 | 'vc-arch-delete-rej-if-obsolete nil t) | ||
| 434 | (message "There are unresolved conflicts in this file"))) | ||
| 435 | (message "There are unresolved conflicts in %s" | ||
| 436 | (file-name-nondirectory rej)))))) | ||
| 437 | |||
| 438 | (autoload 'vc-switches "vc") | ||
| 439 | |||
| 440 | (defun vc-arch-checkin (files comment) | ||
| 441 | ;; FIXME: This implementation probably only works for singleton filesets | ||
| 442 | (let ((summary (file-relative-name (car files) (vc-arch-root (car files))))) | ||
| 443 | ;; Extract a summary from the comment. | ||
| 444 | (when (or (string-match "\\`Summary:[ \t]*\\(.*[^ \t\n]\\)\\([ \t]*\n\\)*" comment) | ||
| 445 | (string-match "\\`[ \t]*\\(.*[^ \t\n]\\)[ \t]*\\(\n?\\'\\|\n\\([ \t]*\n\\)+\\)" comment)) | ||
| 446 | (setq summary (match-string 1 comment)) | ||
| 447 | (setq comment (substring comment (match-end 0)))) | ||
| 448 | (vc-arch-command nil 0 files "commit" "-s" summary "-L" comment "--" | ||
| 449 | (vc-switches 'Arch 'checkin)))) | ||
| 450 | |||
| 451 | (defun vc-arch-diff (files &optional async oldvers newvers buffer) | ||
| 452 | "Get a difference report using Arch between two versions of FILES." | ||
| 453 | ;; FIXME: This implementation only works for singleton filesets. To make | ||
| 454 | ;; it work for more cases, we have to either call `file-diffs' manually on | ||
| 455 | ;; each and every `file' in the fileset, or use `changes --diffs' (and | ||
| 456 | ;; variants) and maybe filter the output with `filterdiff' to only include | ||
| 457 | ;; the files in which we're interested. | ||
| 458 | (let ((file (car files))) | ||
| 459 | (if (and newvers | ||
| 460 | (vc-up-to-date-p file) | ||
| 461 | (equal newvers (vc-working-revision file))) | ||
| 462 | ;; Newvers is the base revision and the current file is unchanged, | ||
| 463 | ;; so we can diff with the current file. | ||
| 464 | (setq newvers nil)) | ||
| 465 | (if newvers | ||
| 466 | (error "Diffing specific revisions not implemented") | ||
| 467 | (let* (process-file-side-effects | ||
| 468 | ;; Run the command from the root dir. | ||
| 469 | (default-directory (vc-arch-root file)) | ||
| 470 | (status | ||
| 471 | (vc-arch-command | ||
| 472 | (or buffer "*vc-diff*") | ||
| 473 | (if async 'async 1) | ||
| 474 | nil "file-diffs" | ||
| 475 | (vc-switches 'Arch 'diff) | ||
| 476 | (file-relative-name file) | ||
| 477 | (if (equal oldvers (vc-working-revision file)) | ||
| 478 | nil | ||
| 479 | oldvers)))) | ||
| 480 | (if async 1 status))))) ; async diff, pessimistic assumption. | ||
| 481 | |||
| 482 | (defun vc-arch-delete-file (file) | ||
| 483 | (vc-arch-command nil 0 file "rm")) | ||
| 484 | |||
| 485 | (defun vc-arch-rename-file (old new) | ||
| 486 | (vc-arch-command nil 0 new "mv" (file-relative-name old))) | ||
| 487 | |||
| 488 | (defalias 'vc-arch-responsible-p 'vc-arch-root) | ||
| 489 | |||
| 490 | (defun vc-arch-command (buffer okstatus file &rest flags) | ||
| 491 | "A wrapper around `vc-do-command' for use in vc-arch.el." | ||
| 492 | (apply 'vc-do-command (or buffer "*vc*") okstatus vc-arch-program file flags)) | ||
| 493 | |||
| 494 | ;;; Completion of versions and revisions. | ||
| 495 | |||
| 496 | (defun vc-arch--version-completion-table (root string) | ||
| 497 | (delq nil | ||
| 498 | (mapcar | ||
| 499 | (lambda (d) | ||
| 500 | (when (string-match "/\\([^/]+\\)/\\([^/]+\\)\\'" d) | ||
| 501 | (concat (match-string 2 d) "/" (match-string 1 d)))) | ||
| 502 | (let ((default-directory root)) | ||
| 503 | (file-expand-wildcards | ||
| 504 | (concat "*/*/" | ||
| 505 | (if (string-match "/" string) | ||
| 506 | (concat (substring string (match-end 0)) | ||
| 507 | "*/" (substring string 0 (match-beginning 0))) | ||
| 508 | (concat "*/" string)) | ||
| 509 | "*")))))) | ||
| 510 | |||
| 511 | (defun vc-arch-revision-completion-table (files) | ||
| 512 | (lambda (string pred action) | ||
| 513 | ;; FIXME: complete revision patches as well. | ||
| 514 | (let* ((root (expand-file-name "{arch}" (vc-arch-root (car files)))) | ||
| 515 | (table (vc-arch--version-completion-table root string))) | ||
| 516 | (complete-with-action action table string pred)))) | ||
| 517 | |||
| 518 | ;;; Trimming revision libraries. | ||
| 519 | |||
| 520 | ;; This code is not directly related to VC and there are many variants of | ||
| 521 | ;; this functionality available as scripts, but I like this version better, | ||
| 522 | ;; so maybe others will like it too. | ||
| 523 | |||
| 524 | (defun vc-arch-trim-find-least-useful-rev (revs) | ||
| 525 | (let* ((first (pop revs)) | ||
| 526 | (second (pop revs)) | ||
| 527 | (third (pop revs)) | ||
| 528 | ;; We try to give more importance to recent revisions. The idea is | ||
| 529 | ;; that it's OK if checking out a revision 1000-patch-old is ten | ||
| 530 | ;; times slower than checking out a revision 100-patch-old. But at | ||
| 531 | ;; the same time a 2-patch-old rev isn't really ten times more | ||
| 532 | ;; important than a 20-patch-old, so we use an arbitrary constant | ||
| 533 | ;; "100" to reduce this effect for recent revisions. Making this | ||
| 534 | ;; constant a float has the side effect of causing the subsequent | ||
| 535 | ;; computations to be done as floats as well. | ||
| 536 | (max (+ 100.0 (car (or (car (last revs)) third)))) | ||
| 537 | (cost (lambda () (/ (- (car third) (car first)) (- max (car second))))) | ||
| 538 | (minrev second) | ||
| 539 | (mincost (funcall cost))) | ||
| 540 | (while revs | ||
| 541 | (setq first second) | ||
| 542 | (setq second third) | ||
| 543 | (setq third (pop revs)) | ||
| 544 | (when (< (funcall cost) mincost) | ||
| 545 | (setq minrev second) | ||
| 546 | (setq mincost (funcall cost)))) | ||
| 547 | minrev)) | ||
| 548 | |||
| 549 | (defun vc-arch-trim-make-sentinel (revs) | ||
| 550 | (if (null revs) (lambda (_proc _msg) (message "VC-Arch trimming ... done")) | ||
| 551 | (lambda (_proc _msg) | ||
| 552 | (message "VC-Arch trimming %s..." (file-name-nondirectory (car revs))) | ||
| 553 | (rename-file (car revs) (concat (car revs) "*rm*")) | ||
| 554 | (let ((proc (start-process "vc-arch-trim" nil | ||
| 555 | "rm" "-rf" (concat (car revs) "*rm*")))) | ||
| 556 | (set-process-sentinel proc (vc-arch-trim-make-sentinel (cdr revs))))))) | ||
| 557 | |||
| 558 | (defun vc-arch-trim-one-revlib (dir) | ||
| 559 | "Delete half of the revisions in the revision library." | ||
| 560 | (interactive "Ddirectory: ") | ||
| 561 | (let ((garbage (directory-files dir 'full "\\`,," 'nosort))) | ||
| 562 | (when garbage | ||
| 563 | (funcall (vc-arch-trim-make-sentinel garbage) nil nil))) | ||
| 564 | (let ((revs | ||
| 565 | (sort (delq nil | ||
| 566 | (mapcar | ||
| 567 | (lambda (f) | ||
| 568 | (when (string-match "-\\([0-9]+\\)\\'" f) | ||
| 569 | (cons (string-to-number (match-string 1 f)) f))) | ||
| 570 | (directory-files dir nil nil 'nosort))) | ||
| 571 | 'car-less-than-car)) | ||
| 572 | (subdirs nil)) | ||
| 573 | (when (cddr revs) | ||
| 574 | (dotimes (_i (/ (length revs) 2)) | ||
| 575 | (let ((minrev (vc-arch-trim-find-least-useful-rev revs))) | ||
| 576 | (setq revs (delq minrev revs)) | ||
| 577 | (push minrev subdirs))) | ||
| 578 | (funcall (vc-arch-trim-make-sentinel | ||
| 579 | (mapcar (lambda (x) (expand-file-name (cdr x) dir)) subdirs)) | ||
| 580 | nil nil)))) | ||
| 581 | |||
| 582 | (defun vc-arch-trim-revlib () | ||
| 583 | "Delete half of the revisions in the revision library." | ||
| 584 | (interactive) | ||
| 585 | (let ((rl-dir (with-output-to-string | ||
| 586 | (call-process vc-arch-program nil standard-output nil | ||
| 587 | "my-revision-library")))) | ||
| 588 | (while (string-match "\\(.*\\)\n" rl-dir) | ||
| 589 | (let ((dir (match-string 1 rl-dir))) | ||
| 590 | (setq rl-dir | ||
| 591 | (if (and (file-directory-p dir) (file-writable-p dir)) | ||
| 592 | dir | ||
| 593 | (substring rl-dir (match-end 0)))))) | ||
| 594 | (unless (file-writable-p rl-dir) | ||
| 595 | (error "No writable revlib directory found")) | ||
| 596 | (message "Revlib at %s" rl-dir) | ||
| 597 | (let* ((archives (directory-files rl-dir 'full "[^.]\\|...")) | ||
| 598 | (categories | ||
| 599 | (apply 'append | ||
| 600 | (mapcar (lambda (dir) | ||
| 601 | (when (file-directory-p dir) | ||
| 602 | (directory-files dir 'full "[^.]\\|..."))) | ||
| 603 | archives))) | ||
| 604 | (branches | ||
| 605 | (apply 'append | ||
| 606 | (mapcar (lambda (dir) | ||
| 607 | (when (file-directory-p dir) | ||
| 608 | (directory-files dir 'full "[^.]\\|..."))) | ||
| 609 | categories))) | ||
| 610 | (versions | ||
| 611 | (apply 'append | ||
| 612 | (mapcar (lambda (dir) | ||
| 613 | (when (file-directory-p dir) | ||
| 614 | (directory-files dir 'full "--.*--"))) | ||
| 615 | branches)))) | ||
| 616 | (mapc 'vc-arch-trim-one-revlib versions)) | ||
| 617 | )) | ||
| 618 | |||
| 619 | (defvar vc-arch-extra-menu-map | ||
| 620 | (let ((map (make-sparse-keymap))) | ||
| 621 | (define-key map [add-tagline] | ||
| 622 | '(menu-item "Add tagline" vc-arch-add-tagline)) | ||
| 623 | map)) | ||
| 624 | |||
| 625 | (defun vc-arch-extra-menu () vc-arch-extra-menu-map) | ||
| 626 | |||
| 627 | |||
| 628 | ;;; Less obvious implementations. | ||
| 629 | |||
| 630 | (defun vc-arch-find-revision (file rev buffer) | ||
| 631 | (let ((out (make-temp-file "vc-out"))) | ||
| 632 | (unwind-protect | ||
| 633 | (progn | ||
| 634 | (with-temp-buffer | ||
| 635 | (vc-arch-command (current-buffer) 1 nil "file-diffs" file rev) | ||
| 636 | (call-process-region (point-min) (point-max) | ||
| 637 | "patch" nil nil nil "-R" "-o" out file)) | ||
| 638 | (with-current-buffer buffer | ||
| 639 | (insert-file-contents out))) | ||
| 640 | (delete-file out)))) | ||
| 641 | |||
| 642 | (provide 'vc-arch) | ||
| 643 | |||
| 644 | ;;; vc-arch.el ends here | ||