diff options
| author | Stefan Monnier | 2004-03-23 21:34:06 +0000 |
|---|---|---|
| committer | Stefan Monnier | 2004-03-23 21:34:06 +0000 |
| commit | df617e7ff8962604218644b7c0f96aa488384c98 (patch) | |
| tree | 487c2173ebb75b6cb3ffce95c86997ea3c1ef010 | |
| parent | 81c63b50e29e5afd38d48af09860a730be5a84bd (diff) | |
| download | emacs-df617e7ff8962604218644b7c0f96aa488384c98.tar.gz emacs-df617e7ff8962604218644b7c0f96aa488384c98.zip | |
(vc-arch-diff): Handle the special case where `newvers' is equivalent to nil.
(vc-arch-diff3-rej-p): Be a bit more flexible in what we accept.
(vc-arch-mode-line-string): Accept `added' state.
(vc-arch-state): Use inode-sigs if available.
(vc-arch-add-tagline): Rename from vc-arch-add-tag.
Copy&delete existing id file if any. Fallback if uuidgen is absent.
(vc-arch-tagline-re): New var.
(vc-arch-file-source-p, vc-arch-file-id, vc-arch-tagging-method): New funs.
(vc-arch-find-file-not-found-hook, vc-arch-register): New backend ops.
(vc-arch-registered): Try our best guess using vc-arch-file-source-p.
| -rw-r--r-- | lisp/vc-arch.el | 190 |
1 files changed, 169 insertions, 21 deletions
diff --git a/lisp/vc-arch.el b/lisp/vc-arch.el index 52dc623e9c4..042b4bbbdca 100644 --- a/lisp/vc-arch.el +++ b/lisp/vc-arch.el | |||
| @@ -25,22 +25,21 @@ | |||
| 25 | ;;; Commentary: | 25 | ;;; Commentary: |
| 26 | 26 | ||
| 27 | ;; The home page of the Arch version control system is at | 27 | ;; The home page of the Arch version control system is at |
| 28 | ;; | 28 | ;; |
| 29 | ;; http://www.gnuarch.org/ | 29 | ;; http://www.gnuarch.org/ |
| 30 | ;; | 30 | ;; |
| 31 | ;; This is derived from vc-mcvs.el as follows: | 31 | ;; This is derived from vc-mcvs.el as follows: |
| 32 | ;; - cp vc-mcvs.el vc-arch.el and then M-% mcvs RET arch RET | 32 | ;; - cp vc-mcvs.el vc-arch.el and then M-% mcvs RET arch RET |
| 33 | ;; | 33 | ;; |
| 34 | ;; Then of course started the hacking. | 34 | ;; Then of course started the hacking. |
| 35 | ;; | 35 | ;; |
| 36 | ;; What has been partly tested: | 36 | ;; What has been partly tested: |
| 37 | ;; - Open a file | 37 | ;; - Open a file. |
| 38 | ;; - C-x v = without any prefix arg | 38 | ;; - C-x v = without any prefix arg. |
| 39 | ;; - C-x v v to commit a change to a single file | 39 | ;; - C-x v v to commit a change to a single file. |
| 40 | 40 | ||
| 41 | ;; Bugs: | 41 | ;; Bugs: |
| 42 | 42 | ||
| 43 | ;; - Opening a new file prompts "blabla was lost; check out? (yes or no)". | ||
| 44 | ;; - *VC-log*'s initial content lacks the `Summary:' lines. | 43 | ;; - *VC-log*'s initial content lacks the `Summary:' lines. |
| 45 | ;; - All files under the tree are considered as "under Arch's control" | 44 | ;; - All files under the tree are considered as "under Arch's control" |
| 46 | ;; without regards to =tagging-method and such. | 45 | ;; without regards to =tagging-method and such. |
| @@ -86,17 +85,103 @@ | |||
| 86 | ;;;###autoload (load "vc-arch") | 85 | ;;;###autoload (load "vc-arch") |
| 87 | ;;;###autoload (vc-arch-registered file))))) | 86 | ;;;###autoload (vc-arch-registered file))))) |
| 88 | 87 | ||
| 89 | (defun vc-arch-add-tag () | 88 | (defun vc-arch-add-tagline () |
| 90 | "Add an `arch-tag' to the end of the current file." | 89 | "Add an `arch-tag' to the end of the current file." |
| 91 | (interactive) | 90 | (interactive) |
| 91 | (comment-normalize-vars) | ||
| 92 | (goto-char (point-max)) | 92 | (goto-char (point-max)) |
| 93 | (forward-comment -1) | 93 | (forward-comment -1) |
| 94 | (unless (bolp) (insert "\n")) | 94 | (unless (bolp) (insert "\n")) |
| 95 | (let ((beg (point))) | 95 | (let ((beg (point)) |
| 96 | (idfile (and buffer-file-name | ||
| 97 | (expand-file-name | ||
| 98 | (concat ".arch-ids/" | ||
| 99 | (file-name-nondirectory buffer-file-name) | ||
| 100 | ".id") | ||
| 101 | (file-name-directory buffer-file-name))))) | ||
| 96 | (insert "arch-tag: ") | 102 | (insert "arch-tag: ") |
| 97 | (call-process "uuidgen" nil t) ;Also inserts a terminal newline. | 103 | (if (and idfile (file-exists-p idfile)) |
| 104 | ;; If the file is unreadable, we do want to get an error here. | ||
| 105 | (progn | ||
| 106 | (insert-file-contents idfile) | ||
| 107 | (forward-line 1) | ||
| 108 | (delete-file idfile)) | ||
| 109 | (condition-case nil | ||
| 110 | (call-process "uuidgen" nil t) | ||
| 111 | (file-error (insert (format "%s <%s> %s" | ||
| 112 | (current-time-string) | ||
| 113 | user-mail-address | ||
| 114 | (+ (nth 2 (current-time)) | ||
| 115 | (buffer-size))))))) | ||
| 98 | (comment-region beg (point)))) | 116 | (comment-region beg (point)))) |
| 99 | 117 | ||
| 118 | (defconst vc-arch-tagline-re "^\\W*arch-tag:[ \t]*\\(.*[^ \t\n]\\)") | ||
| 119 | |||
| 120 | (defun vc-arch-file-source-p (file) | ||
| 121 | "Can return nil, `maybe' or a non-nil value. | ||
| 122 | Only the value `maybe' can be trusted :-(." | ||
| 123 | ;; FIXME: Check the tag and name of parent dirs. | ||
| 124 | (unless (string-match "\\`[,+]" (file-name-nondirectory file)) | ||
| 125 | (or (string-match "\\`{arch}/" | ||
| 126 | (file-relative-name file (vc-arch-root file))) | ||
| 127 | (file-exists-p | ||
| 128 | ;; Check the presence of an ID file. | ||
| 129 | (expand-file-name | ||
| 130 | (concat ".arch-ids/" (file-name-nondirectory file) ".id") | ||
| 131 | (file-name-directory file))) | ||
| 132 | ;; Check the presence of a tagline. | ||
| 133 | (with-current-buffer (find-file-noselect file) | ||
| 134 | (save-excursion | ||
| 135 | (goto-char (point-max)) | ||
| 136 | (or (re-search-backward vc-arch-tagline-re (- (point) 1000) t) | ||
| 137 | (progn | ||
| 138 | (goto-char (point-min)) | ||
| 139 | (re-search-forward vc-arch-tagline-re (+ (point) 1000) t))))) | ||
| 140 | ;; FIXME: check =tagging-method to see whether untagged files might | ||
| 141 | ;; be source or not. | ||
| 142 | (with-current-buffer | ||
| 143 | (find-file-noselect (expand-file-name "{arch}/=tagging-method" | ||
| 144 | (vc-arch-root file))) | ||
| 145 | (let ((untagged-source t)) ;Default is `names'. | ||
| 146 | (save-excursion | ||
| 147 | (goto-char (point-min)) | ||
| 148 | (if (re-search-forward "^[ \t]*\\(\\(tagline\\|implicit\\|names\\)\\|explicit\\)" nil t) | ||
| 149 | (setq untagged-source (match-end 2))) | ||
| 150 | (if (re-search-forward "^[ \t]*untagged-source[ \t]+\\(\\(source\\)\\|precious\\|backup\\|junk\\|unrecognized\\)" nil t) | ||
| 151 | (setq untagged-source (match-end 2)))) | ||
| 152 | (if untagged-source 'maybe)))))) | ||
| 153 | |||
| 154 | (defun vc-arch-file-id (file) | ||
| 155 | ;; Don't include the kind of ID this is because it seems to be too messy. | ||
| 156 | (let ((idfile (expand-file-name | ||
| 157 | (concat ".arch-ids/" (file-name-nondirectory file) ".id") | ||
| 158 | (file-name-directory file)))) | ||
| 159 | (if (file-exists-p idfile) | ||
| 160 | (with-temp-buffer | ||
| 161 | (insert-file-contents idfile) | ||
| 162 | (looking-at ".*[^ \n\t]") | ||
| 163 | (match-string 0))) | ||
| 164 | (with-current-buffer (find-file-noselect file) | ||
| 165 | (save-excursion | ||
| 166 | (goto-char (point-max)) | ||
| 167 | (if (or (re-search-backward vc-arch-tagline-re (- (point) 1000) t) | ||
| 168 | (progn | ||
| 169 | (goto-char (point-min)) | ||
| 170 | (re-search-forward vc-arch-tagline-re (+ (point) 1000) t))) | ||
| 171 | (match-string 1) | ||
| 172 | (concat "./" (file-relative-name file (vc-arch-root file)))))))) | ||
| 173 | |||
| 174 | (defun vc-arch-tagging-method (file) | ||
| 175 | (with-current-buffer | ||
| 176 | (find-file-noselect | ||
| 177 | (expand-file-name "{arch}/=tagging-method" (vc-arch-root file))) | ||
| 178 | (save-excursion | ||
| 179 | (goto-char (point-min)) | ||
| 180 | (if (re-search-forward | ||
| 181 | "^[ \t]*\\(tagline\\|implicit\\|names\\|explicit\\)" nil t) | ||
| 182 | (intern (match-string 1)) | ||
| 183 | 'names)))) | ||
| 184 | |||
| 100 | (defun vc-arch-root (file) | 185 | (defun vc-arch-root (file) |
| 101 | "Return the root directory of a Arch project, if any." | 186 | "Return the root directory of a Arch project, if any." |
| 102 | (or (vc-file-getprop file 'arch-root) | 187 | (or (vc-file-getprop file 'arch-root) |
| @@ -111,11 +196,20 @@ | |||
| 111 | (setq file (directory-file-name file)))) | 196 | (setq file (directory-file-name file)))) |
| 112 | root)))) | 197 | root)))) |
| 113 | 198 | ||
| 199 | (defun vc-arch-register (file &optional rev comment) | ||
| 200 | (if rev (error "Explicit initial revision not supported for Arch.")) | ||
| 201 | (let ((tagmet (vc-arch-tagging-method file))) | ||
| 202 | (if (and (memq tagmet '(tagline implicit)) comment-start) | ||
| 203 | (with-current-buffer (find-file-noselect file) | ||
| 204 | (vc-arch-add-tagline)) | ||
| 205 | (vc-arch-command nil 0 file "add")))) | ||
| 206 | |||
| 114 | (defun vc-arch-registered (file) | 207 | (defun vc-arch-registered (file) |
| 115 | ;; Don't check whether it's source or not. Checking would require | 208 | ;; Don't seriously check whether it's source or not. Checking would |
| 116 | ;; running TLA, so it's better to not do it, so it also works if TLA is | 209 | ;; require running TLA, so it's better to not do it, so it also works if |
| 117 | ;; not installed. | 210 | ;; TLA is not installed. |
| 118 | (vc-arch-root file)) | 211 | (and (vc-arch-root file) |
| 212 | (vc-arch-file-source-p file))) | ||
| 119 | 213 | ||
| 120 | (defun vc-arch-default-version (file) | 214 | (defun vc-arch-default-version (file) |
| 121 | (or (vc-file-getprop (vc-arch-root file) 'arch-default-version) | 215 | (or (vc-file-getprop (vc-arch-root file) 'arch-default-version) |
| @@ -138,8 +232,47 @@ Return non-nil if FILE is unchanged." | |||
| 138 | ;; There's no checkout operation and merging is not done from VC | 232 | ;; There's no checkout operation and merging is not done from VC |
| 139 | ;; so the only operation that's state dependent that VC supports is commit | 233 | ;; so the only operation that's state dependent that VC supports is commit |
| 140 | ;; which is only activated if the file is `edited'. | 234 | ;; which is only activated if the file is `edited'. |
| 141 | 'edited) | 235 | (let* ((root (vc-arch-root file)) |
| 142 | 236 | (ver (vc-arch-default-version file)) | |
| 237 | (pat (concat "\\`" (subst-char-in-string ?/ ?% ver))) | ||
| 238 | (dir (expand-file-name ",,inode-sigs/" | ||
| 239 | (expand-file-name "{arch}" root))) | ||
| 240 | (sigfile nil)) | ||
| 241 | (dolist (f (if (file-directory-p dir) (directory-files dir t pat))) | ||
| 242 | (if (or (not sigfile) (file-newer-than-file-p f sigfile)) | ||
| 243 | (setq sigfile f))) | ||
| 244 | (if (not sigfile) | ||
| 245 | 'edited ;We know nothing. | ||
| 246 | (let ((id (vc-arch-file-id file))) | ||
| 247 | (setq id (replace-regexp-in-string "[ \t]" "_" id)) | ||
| 248 | (with-current-buffer (find-file-noselect sigfile) | ||
| 249 | (goto-char (point-min)) | ||
| 250 | (while (and (search-forward id nil 'move) | ||
| 251 | (progn (goto-char (- (match-beginning 0) 2)) | ||
| 252 | ;; Ignore E_ entries used for foo.id files. | ||
| 253 | (or (not (bolp)) (looking-at "E_"))))) | ||
| 254 | (if (eobp) | ||
| 255 | ;; ID not found. | ||
| 256 | (if (equal (file-name-nondirectory sigfile) | ||
| 257 | (subst-char-in-string | ||
| 258 | ?/ ?% (vc-arch-workfile-version file))) | ||
| 259 | 'added | ||
| 260 | ;; Might be `added' or `up-to-date' as well. | ||
| 261 | ;; FIXME: Check in the patch logs to find out. | ||
| 262 | 'edited) | ||
| 263 | ;; Found the ID, let's check the inode. | ||
| 264 | (if (not (re-search-forward | ||
| 265 | "\t.*mtime=\\([0-9]+\\):size=\\([0-9]+\\)" | ||
| 266 | (line-end-position) t)) | ||
| 267 | ;; Buh? Unexpected format. | ||
| 268 | 'edited | ||
| 269 | (let ((ats (file-attributes file))) | ||
| 270 | (if (and (= (nth 7 ats) (string-to-number (match-string 2))) | ||
| 271 | (equal (format-time-string "%s" (nth 5 ats)) | ||
| 272 | (match-string 1))) | ||
| 273 | 'up-to-date | ||
| 274 | 'edited))))))))) | ||
| 275 | |||
| 143 | (defun vc-arch-workfile-version (file) | 276 | (defun vc-arch-workfile-version (file) |
| 144 | (let* ((root (expand-file-name "{arch}" (vc-arch-root file))) | 277 | (let* ((root (expand-file-name "{arch}" (vc-arch-root file))) |
| 145 | (defbranch (vc-arch-default-version file))) | 278 | (defbranch (vc-arch-default-version file))) |
| @@ -180,15 +313,19 @@ Return non-nil if FILE is unchanged." | |||
| 180 | (if (string-match (car rule) rev) | 313 | (if (string-match (car rule) rev) |
| 181 | (setq rev (replace-match (cdr rule) t nil rev)))) | 314 | (setq rev (replace-match (cdr rule) t nil rev)))) |
| 182 | (format "Arch%c%s" | 315 | (format "Arch%c%s" |
| 183 | (if (memq (vc-state file) '(up-to-date needs-patch)) ?- ?:) | 316 | (case (vc-state file) |
| 317 | ((up-to-date needs-patch) ?-) | ||
| 318 | (added ?@) | ||
| 319 | (t ?:)) | ||
| 184 | rev))) | 320 | rev))) |
| 185 | 321 | ||
| 186 | (defun vc-arch-diff3-rej-p (rej) | 322 | (defun vc-arch-diff3-rej-p (rej) |
| 187 | (and (eq (nth 7 (file-attributes rej)) 56) | 323 | (let ((attrs (file-attributes rej))) |
| 188 | (with-temp-buffer | 324 | (and attrs (< (nth 7 attrs) 60) |
| 189 | (insert-file-contents rej) | 325 | (with-temp-buffer |
| 190 | (goto-char (point-min)) | 326 | (insert-file-contents rej) |
| 191 | (looking-at "Conflicts occured, diff3 conflict markers left in file\\.$")))) | 327 | (goto-char (point-min)) |
| 328 | (looking-at "Conflicts occured, diff3 conflict markers left in file\\."))))) | ||
| 192 | 329 | ||
| 193 | (defun vc-arch-delete-rej-if-obsolete () | 330 | (defun vc-arch-delete-rej-if-obsolete () |
| 194 | "For use in `write-file-functions'." | 331 | "For use in `write-file-functions'." |
| @@ -216,6 +353,11 @@ Return non-nil if FILE is unchanged." | |||
| 216 | (message "There are unresolved conflicts in %s" | 353 | (message "There are unresolved conflicts in %s" |
| 217 | (file-name-nondirectory rej)))))) | 354 | (file-name-nondirectory rej)))))) |
| 218 | 355 | ||
| 356 | (defun vc-arch-find-file-not-found-hook () | ||
| 357 | ;; Do nothing. We are not sure whether the file is `source' or not, | ||
| 358 | ;; so we shouldn't ask the user whether she wants to check it out. | ||
| 359 | ) | ||
| 360 | |||
| 219 | (defun vc-arch-checkout-model (file) 'implicit) | 361 | (defun vc-arch-checkout-model (file) 'implicit) |
| 220 | 362 | ||
| 221 | (defun vc-arch-checkin (file rev comment) | 363 | (defun vc-arch-checkin (file rev comment) |
| @@ -231,6 +373,12 @@ Return non-nil if FILE is unchanged." | |||
| 231 | 373 | ||
| 232 | (defun vc-arch-diff (file &optional oldvers newvers) | 374 | (defun vc-arch-diff (file &optional oldvers newvers) |
| 233 | "Get a difference report using Arch between two versions of FILE." | 375 | "Get a difference report using Arch between two versions of FILE." |
| 376 | (if (and newvers | ||
| 377 | (vc-up-to-date-p file) | ||
| 378 | (equal newvers (vc-workfile-version file))) | ||
| 379 | ;; Newvers is the base revision and the current file is unchanged, | ||
| 380 | ;; so we can diff with the current file. | ||
| 381 | (setq newvers nil)) | ||
| 234 | (if newvers | 382 | (if newvers |
| 235 | (error "Diffing specific revisions not implemented.") | 383 | (error "Diffing specific revisions not implemented.") |
| 236 | (let* ((async (fboundp 'start-process)) | 384 | (let* ((async (fboundp 'start-process)) |