]> code.delx.au - gnu-emacs/blob - lisp/vc/vc-bzr.el
1f368e1169bed583e5074df5ef538dc0c8e66301
[gnu-emacs] / lisp / vc / vc-bzr.el
1 ;;; vc-bzr.el --- VC backend for the bzr revision control system
2
3 ;; Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
4
5 ;; Author: Dave Love <fx@gnu.org>
6 ;; Riccardo Murri <riccardo.murri@gmail.com>
7 ;; Maintainer: FSF
8 ;; Keywords: vc tools
9 ;; Created: Sept 2006
10 ;; Package: vc
11
12 ;; This file is part of GNU Emacs.
13
14 ;; GNU Emacs is free software: you can redistribute it and/or modify
15 ;; it under the terms of the GNU General Public License as published by
16 ;; the Free Software Foundation, either version 3 of the License, or
17 ;; (at your option) any later version.
18
19 ;; GNU Emacs is distributed in the hope that it will be useful,
20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 ;; GNU General Public License for more details.
23
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
26
27 ;;; Commentary:
28
29 ;; See <URL:http://bazaar.canonical.com/> concerning bzr.
30
31 ;; This library provides bzr support in VC.
32
33 ;; Known bugs
34 ;; ==========
35
36 ;; When editing a symlink and *both* the symlink and its target
37 ;; are bzr-versioned, `vc-bzr` presently runs `bzr status` on the
38 ;; symlink, thereby not detecting whether the actual contents
39 ;; (that is, the target contents) are changed.
40 ;; See https://bugs.launchpad.net/vc-bzr/+bug/116607
41
42 ;;; Properties of the backend
43
44 (defun vc-bzr-revision-granularity () 'repository)
45 (defun vc-bzr-checkout-model (files) 'implicit)
46
47 ;;; Code:
48
49 (eval-when-compile
50 (require 'cl)
51 (require 'vc) ;; for vc-exec-after
52 (require 'vc-dir))
53
54 ;; Clear up the cache to force vc-call to check again and discover
55 ;; new functions when we reload this file.
56 (put 'Bzr 'vc-functions nil)
57
58 (defgroup vc-bzr nil
59 "VC bzr backend."
60 :version "22.2"
61 :group 'vc)
62
63 (defcustom vc-bzr-program "bzr"
64 "Name of the bzr command (excluding any arguments)."
65 :group 'vc-bzr
66 :type 'string)
67
68 (defcustom vc-bzr-diff-switches nil
69 "String or list of strings specifying switches for bzr diff under VC.
70 If nil, use the value of `vc-diff-switches'. If t, use no switches."
71 :type '(choice (const :tag "Unspecified" nil)
72 (const :tag "None" t)
73 (string :tag "Argument String")
74 (repeat :tag "Argument List" :value ("") string))
75 :group 'vc-bzr)
76
77 (defcustom vc-bzr-log-switches nil
78 "String or list of strings specifying switches for bzr log under VC."
79 :type '(choice (const :tag "None" nil)
80 (string :tag "Argument String")
81 (repeat :tag "Argument List" :value ("") string))
82 :group 'vc-bzr)
83
84 ;; since v0.9, bzr supports removing the progress indicators
85 ;; by setting environment variable BZR_PROGRESS_BAR to "none".
86 (defun vc-bzr-command (bzr-command buffer okstatus file-or-list &rest args)
87 "Wrapper round `vc-do-command' using `vc-bzr-program' as COMMAND.
88 Invoke the bzr command adding `BZR_PROGRESS_BAR=none' and
89 `LC_MESSAGES=C' to the environment."
90 (let ((process-environment
91 (list* "BZR_PROGRESS_BAR=none" ; Suppress progress output (bzr >=0.9)
92 "LC_MESSAGES=C" ; Force English output
93 process-environment)))
94 (apply 'vc-do-command (or buffer "*vc*") okstatus vc-bzr-program
95 file-or-list bzr-command args)))
96
97
98 ;;;###autoload
99 (defconst vc-bzr-admin-dirname ".bzr"
100 "Name of the directory containing Bzr repository status files.")
101 ;; Used in the autoloaded vc-bzr-registered; see below.
102 ;;;###autoload
103 (defconst vc-bzr-admin-checkout-format-file
104 (concat vc-bzr-admin-dirname "/checkout/format"))
105 (defconst vc-bzr-admin-dirstate
106 (concat vc-bzr-admin-dirname "/checkout/dirstate"))
107 (defconst vc-bzr-admin-branch-format-file
108 (concat vc-bzr-admin-dirname "/branch/format"))
109 (defconst vc-bzr-admin-revhistory
110 (concat vc-bzr-admin-dirname "/branch/revision-history"))
111 (defconst vc-bzr-admin-lastrev
112 (concat vc-bzr-admin-dirname "/branch/last-revision"))
113 (defconst vc-bzr-admin-branchconf
114 (concat vc-bzr-admin-dirname "/branch/branch.conf"))
115
116 ;;;###autoload (defun vc-bzr-registered (file)
117 ;;;###autoload (if (vc-find-root file vc-bzr-admin-checkout-format-file)
118 ;;;###autoload (progn
119 ;;;###autoload (load "vc-bzr")
120 ;;;###autoload (vc-bzr-registered file))))
121
122 (defun vc-bzr-root (file)
123 "Return the root directory of the bzr repository containing FILE."
124 ;; Cache technique copied from vc-arch.el.
125 (or (vc-file-getprop file 'bzr-root)
126 (let ((root (vc-find-root file vc-bzr-admin-checkout-format-file)))
127 (when root (vc-file-setprop file 'bzr-root root)))))
128
129 (defun vc-bzr--branch-conf (file)
130 "Return the Bzr branch config for file FILE, as a string."
131 (with-temp-buffer
132 (insert-file-contents
133 (expand-file-name vc-bzr-admin-branchconf (vc-bzr-root file)))
134 (buffer-string)))
135
136 (require 'sha1) ;For sha1-program
137
138 (defun vc-bzr-sha1 (file)
139 (with-temp-buffer
140 (set-buffer-multibyte nil)
141 (let ((prog sha1-program)
142 (args nil)
143 process-file-side-effects)
144 (when (consp prog)
145 (setq args (cdr prog))
146 (setq prog (car prog)))
147 (apply 'process-file prog (file-relative-name file) t nil args)
148 (buffer-substring (point-min) (+ (point-min) 40)))))
149
150 (defun vc-bzr-state-heuristic (file)
151 "Like `vc-bzr-state' but hopefully without running Bzr."
152 ;; `bzr status' was excruciatingly slow with large histories and
153 ;; pending merges, so try to avoid using it until they fix their
154 ;; performance problems.
155 ;; This function tries first to parse Bzr internal file
156 ;; `checkout/dirstate', but it may fail if Bzr internal file format
157 ;; has changed. As a safeguard, the `checkout/dirstate' file is
158 ;; only parsed if it contains the string `#bazaar dirstate flat
159 ;; format 3' in the first line.
160 ;; If the `checkout/dirstate' file cannot be parsed, fall back to
161 ;; running `vc-bzr-state'."
162 (lexical-let ((root (vc-bzr-root file)))
163 (when root ; Short cut.
164 ;; This looks at internal files. May break if they change
165 ;; their format.
166 (lexical-let ((dirstate (expand-file-name vc-bzr-admin-dirstate root)))
167 (condition-case nil
168 (with-temp-buffer
169 (insert-file-contents dirstate)
170 (goto-char (point-min))
171 (if (not (looking-at "#bazaar dirstate flat format 3"))
172 (vc-bzr-state file) ; Some other unknown format?
173 (let* ((relfile (file-relative-name file root))
174 (reldir (file-name-directory relfile)))
175 (if (re-search-forward
176 (concat "^\0"
177 (if reldir (regexp-quote
178 (directory-file-name reldir)))
179 "\0"
180 (regexp-quote (file-name-nondirectory relfile))
181 "\0"
182 "[^\0]*\0" ;id?
183 "\\([^\0]*\\)\0" ;"a/f/d", a=removed?
184 "\\([^\0]*\\)\0" ;sha1 (empty if conflicted)?
185 "\\([^\0]*\\)\0" ;size?p
186 ;; y/n. Whether or not the current copy
187 ;; was executable the last time bzr checked?
188 "[^\0]*\0"
189 "[^\0]*\0" ;?
190 "\\([^\0]*\\)\0" ;"a/f/d" a=added?
191 "\\([^\0]*\\)\0" ;sha1 again?
192 "\\([^\0]*\\)\0" ;size again?
193 ;; y/n. Whether or not the repo thinks
194 ;; the file should be executable?
195 "\\([^\0]*\\)\0"
196 "[^\0]*\0" ;last revid?
197 ;; There are more fields when merges are pending.
198 )
199 nil t)
200 ;; Apparently the second sha1 is the one we want: when
201 ;; there's a conflict, the first sha1 is absent (and the
202 ;; first size seems to correspond to the file with
203 ;; conflict markers).
204 (cond
205 ((eq (char-after (match-beginning 1)) ?a) 'removed)
206 ((eq (char-after (match-beginning 4)) ?a) 'added)
207 ((or (and (eq (string-to-number (match-string 3))
208 (nth 7 (file-attributes file)))
209 (equal (match-string 5)
210 (vc-bzr-sha1 file))
211 ;; For a file, does the executable state match?
212 ;; (Bug#7544)
213 (or (not
214 (eq (char-after (match-beginning 1)) ?f))
215 (let ((exe
216 (memq
217 ?x
218 (mapcar
219 'identity
220 (nth 8 (file-attributes file))))))
221 (if (eq (char-after (match-beginning 7))
222 ?y)
223 exe
224 (not exe)))))
225 (and
226 ;; It looks like for lightweight
227 ;; checkouts \2 is empty and we need to
228 ;; look for size in \6.
229 (eq (match-beginning 2) (match-end 2))
230 (eq (string-to-number (match-string 6))
231 (nth 7 (file-attributes file)))
232 (equal (match-string 5)
233 (vc-bzr-sha1 file))))
234 'up-to-date)
235 (t 'edited))
236 'unregistered))))
237 ;; Either the dirstate file can't be read, or the sha1
238 ;; executable is missing, or ...
239 ;; In either case, recent versions of Bzr aren't that slow
240 ;; any more.
241 (error (vc-bzr-state file)))))))
242
243
244 (defun vc-bzr-registered (file)
245 "Return non-nil if FILE is registered with bzr."
246 (let ((state (vc-bzr-state-heuristic file)))
247 (not (memq state '(nil unregistered ignored)))))
248
249 (defconst vc-bzr-state-words
250 "added\\|ignored\\|kind changed\\|modified\\|removed\\|renamed\\|unknown"
251 "Regexp matching file status words as reported in `bzr' output.")
252
253 ;; History of Bzr commands.
254 (defvar vc-bzr-history nil)
255
256 (defun vc-bzr-file-name-relative (filename)
257 "Return file name FILENAME stripped of the initial Bzr repository path."
258 (lexical-let*
259 ((filename* (expand-file-name filename))
260 (rootdir (vc-bzr-root filename*)))
261 (when rootdir
262 (file-relative-name filename* rootdir))))
263
264 (defun vc-bzr-async-command (command args)
265 "Run Bzr COMMAND asynchronously with ARGS, displaying the result.
266 Send the output to a buffer named \"*vc-bzr : NAME*\", where NAME
267 is the root of the current Bzr branch. Display the buffer in
268 some window, but don't select it."
269 ;; TODO: set up hyperlinks.
270 (let* ((dir default-directory)
271 (root (vc-bzr-root default-directory))
272 (buffer (get-buffer-create
273 (format "*vc-bzr : %s*"
274 (expand-file-name root)))))
275 (with-current-buffer buffer
276 (setq default-directory root)
277 (goto-char (point-max))
278 (unless (eq (point) (point-min))
279 (insert "\f\n"))
280 (insert "Running \"" vc-bzr-program " " command)
281 (dolist (arg args)
282 (insert " " arg))
283 (insert "\"...\n")
284 ;; Run bzr in the original working directory.
285 (let ((default-directory dir))
286 (apply 'vc-bzr-command command t 'async nil args)))
287 (display-buffer buffer)))
288
289 (defun vc-bzr-pull (prompt)
290 "Pull changes into the current Bzr branch.
291 Normally, this runs \"bzr pull\". However, if the branch is a
292 bound branch, run \"bzr update\" instead. If there is no default
293 location from which to pull or update, or if PROMPT is non-nil,
294 prompt for the Bzr command to run."
295 (let* ((vc-bzr-program vc-bzr-program)
296 (branch-conf (vc-bzr--branch-conf default-directory))
297 ;; Check whether the branch is bound.
298 (bound (string-match "^bound\\s-*=\\s-*True" branch-conf))
299 ;; If we need to do a "bzr pull", check for a parent. If it
300 ;; does not exist, bzr will need a pull location.
301 (parent (unless bound
302 (string-match
303 "^parent_location\\s-*=\\s-*[^\n[:space:]]+"
304 branch-conf)))
305 (command (if bound "update" "pull"))
306 args)
307 ;; If necessary, prompt for the exact command.
308 (when (or prompt (not (or bound parent)))
309 (setq args (split-string
310 (read-shell-command
311 "Run Bzr (like this): "
312 (concat vc-bzr-program " " command)
313 'vc-bzr-history)
314 " " t))
315 (setq vc-bzr-program (car args)
316 command (cadr args)
317 args (cddr args)))
318 (vc-bzr-async-command command args)))
319
320 (defun vc-bzr-merge-branch ()
321 "Merge another Bzr branch into the current one.
322 Prompt for the Bzr command to run, providing a pre-defined merge
323 source (an upstream branch or a previous merge source) as a
324 default if it is available."
325 (let* ((branch-conf (vc-bzr--branch-conf default-directory))
326 ;; "bzr merge" without an argument defaults to submit_branch,
327 ;; then parent_location. We extract the specific location
328 ;; and add it explicitly to the command line.
329 (location
330 (cond
331 ((string-match
332 "^submit_branch\\s-*=\\s-*\\(?:file://\\)?\\([^\n[:space:]]+\\)$"
333 branch-conf)
334 (match-string 1 branch-conf))
335 ((string-match
336 "^parent_location\\s-*=\\s-*\\(?:file://\\)?\\([^\n[:space:]]+\\)$"
337 branch-conf)
338 (match-string 1 branch-conf))))
339 (cmd
340 (split-string
341 (read-shell-command
342 "Run Bzr (like this): "
343 (concat vc-bzr-program " merge --pull"
344 (if location (concat " " location) ""))
345 'vc-bzr-history)
346 " " t))
347 (vc-bzr-program (car cmd))
348 (command (cadr cmd))
349 (args (cddr cmd)))
350 (vc-bzr-async-command command args)))
351
352 (defun vc-bzr-status (file)
353 "Return FILE status according to Bzr.
354 Return value is a cons (STATUS . WARNING), where WARNING is a
355 string or nil, and STATUS is one of the symbols: `added',
356 `ignored', `kindchanged', `modified', `removed', `renamed', `unknown',
357 which directly correspond to `bzr status' output, or 'unchanged
358 for files whose copy in the working tree is identical to the one
359 in the branch repository, or nil for files that are not
360 registered with Bzr.
361
362 If any error occurred in running `bzr status', then return nil."
363 (with-temp-buffer
364 (let ((ret (condition-case nil
365 (vc-bzr-command "status" t 0 file)
366 (file-error nil))) ; vc-bzr-program not found.
367 (status 'unchanged))
368 ;; the only secure status indication in `bzr status' output
369 ;; is a couple of lines following the pattern::
370 ;; | <status>:
371 ;; | <file name>
372 ;; if the file is up-to-date, we get no status report from `bzr',
373 ;; so if the regexp search for the above pattern fails, we consider
374 ;; the file to be up-to-date.
375 (goto-char (point-min))
376 (when (re-search-forward
377 ;; bzr prints paths relative to the repository root.
378 (concat "^\\(" vc-bzr-state-words "\\):[ \t\n]+"
379 (regexp-quote (vc-bzr-file-name-relative file))
380 ;; Bzr appends a '/' to directory names and
381 ;; '*' to executable files
382 (if (file-directory-p file) "/?" "\\*?")
383 "[ \t\n]*$")
384 nil t)
385 (lexical-let ((statusword (match-string 1)))
386 ;; Erase the status text that matched.
387 (delete-region (match-beginning 0) (match-end 0))
388 (setq status
389 (intern (replace-regexp-in-string " " "" statusword)))))
390 (when status
391 (goto-char (point-min))
392 (skip-chars-forward " \n\t") ;Throw away spaces.
393 (cons status
394 ;; "bzr" will output warnings and informational messages to
395 ;; stderr; due to Emacs' `vc-do-command' (and, it seems,
396 ;; `start-process' itself) limitations, we cannot catch stderr
397 ;; and stdout into different buffers. So, if there's anything
398 ;; left in the buffer after removing the above status
399 ;; keywords, let us just presume that any other message from
400 ;; "bzr" is a user warning, and display it.
401 (unless (eobp) (buffer-substring (point) (point-max))))))))
402
403 (defun vc-bzr-state (file)
404 (lexical-let ((result (vc-bzr-status file)))
405 (when (consp result)
406 (when (cdr result)
407 (message "Warnings in `bzr' output: %s" (cdr result)))
408 (cdr (assq (car result)
409 '((added . added)
410 (kindchanged . edited)
411 (renamed . edited)
412 (modified . edited)
413 (removed . removed)
414 (ignored . ignored)
415 (unknown . unregistered)
416 (unchanged . up-to-date)))))))
417
418 (defun vc-bzr-resolve-when-done ()
419 "Call \"bzr resolve\" if the conflict markers have been removed."
420 (save-excursion
421 (goto-char (point-min))
422 (unless (re-search-forward "^<<<<<<< " nil t)
423 (vc-bzr-command "resolve" nil 0 buffer-file-name)
424 ;; Remove the hook so that it is not called multiple times.
425 (remove-hook 'after-save-hook 'vc-bzr-resolve-when-done t))))
426
427 (defun vc-bzr-find-file-hook ()
428 (when (and buffer-file-name
429 ;; FIXME: We should check that "bzr status" says "conflict".
430 (file-exists-p (concat buffer-file-name ".BASE"))
431 (file-exists-p (concat buffer-file-name ".OTHER"))
432 (file-exists-p (concat buffer-file-name ".THIS"))
433 ;; If "bzr status" says there's a conflict but there are no
434 ;; conflict markers, it's not clear what we should do.
435 (save-excursion
436 (goto-char (point-min))
437 (re-search-forward "^<<<<<<< " nil t)))
438 ;; TODO: the merge algorithm used in `bzr merge' is nicely configurable,
439 ;; but the one in `bzr pull' isn't, so it would be good to provide an
440 ;; elisp function to remerge from the .BASE/OTHER/THIS files.
441 (smerge-start-session)
442 (add-hook 'after-save-hook 'vc-bzr-resolve-when-done nil t)
443 (message "There are unresolved conflicts in this file")))
444
445 (defun vc-bzr-workfile-unchanged-p (file)
446 (eq 'unchanged (car (vc-bzr-status file))))
447
448 (defun vc-bzr-working-revision (file)
449 ;; Together with the code in vc-state-heuristic, this makes it possible
450 ;; to get the initial VC state of a Bzr file even if Bzr is not installed.
451 (lexical-let*
452 ((rootdir (vc-bzr-root file))
453 (branch-format-file (expand-file-name vc-bzr-admin-branch-format-file
454 rootdir))
455 (revhistory-file (expand-file-name vc-bzr-admin-revhistory rootdir))
456 (lastrev-file (expand-file-name vc-bzr-admin-lastrev rootdir)))
457 ;; This looks at internal files to avoid forking a bzr process.
458 ;; May break if they change their format.
459 (if (and (file-exists-p branch-format-file)
460 ;; For lightweight checkouts (obtained with bzr checkout --lightweight)
461 ;; the branch-format-file does not contain the revision
462 ;; information, we need to look up the branch-format-file
463 ;; in the place where the lightweight checkout comes
464 ;; from. We only do that if it's a local file.
465 (let ((location-fname (expand-file-name
466 (concat vc-bzr-admin-dirname
467 "/branch/location") rootdir)))
468 ;; The existence of this file is how we distinguish
469 ;; lightweight checkouts.
470 (if (file-exists-p location-fname)
471 (with-temp-buffer
472 (insert-file-contents location-fname)
473 ;; If the lightweight checkout points to a
474 ;; location in the local file system, then we can
475 ;; look there for the version information.
476 (when (re-search-forward "file://\\(.+\\)" nil t)
477 (let ((l-c-parent-dir (match-string 1)))
478 (when (and (memq system-type '(ms-dos windows-nt))
479 (string-match-p "^/[[:alpha:]]:" l-c-parent-dir))
480 ;;; The non-Windows code takes a shortcut by using the host/path
481 ;;; separator slash as the start of the absolute path. That
482 ;;; does not work on Windows, so we must remove it (bug#5345)
483 (setq l-c-parent-dir (substring l-c-parent-dir 1)))
484 (setq branch-format-file
485 (expand-file-name vc-bzr-admin-branch-format-file
486 l-c-parent-dir))
487 (setq lastrev-file
488 (expand-file-name vc-bzr-admin-lastrev l-c-parent-dir))
489 ;; FIXME: maybe it's overkill to check if both these files exist.
490 (and (file-exists-p branch-format-file)
491 (file-exists-p lastrev-file)))))
492 t)))
493 (with-temp-buffer
494 (insert-file-contents branch-format-file)
495 (goto-char (point-min))
496 (cond
497 ((or
498 (looking-at "Bazaar-NG branch, format 0.0.4")
499 (looking-at "Bazaar-NG branch format 5"))
500 ;; count lines in .bzr/branch/revision-history
501 (insert-file-contents revhistory-file)
502 (number-to-string (count-lines (line-end-position) (point-max))))
503 ((or
504 (looking-at "Bazaar Branch Format 6 (bzr 0.15)")
505 (looking-at "Bazaar Branch Format 7 (needs bzr 1.6)"))
506 ;; revno is the first number in .bzr/branch/last-revision
507 (insert-file-contents lastrev-file)
508 (when (re-search-forward "[0-9]+" nil t)
509 (buffer-substring (match-beginning 0) (match-end 0))))))
510 ;; fallback to calling "bzr revno"
511 (lexical-let*
512 ((result (vc-bzr-command-discarding-stderr
513 vc-bzr-program "revno" (file-relative-name file)))
514 (exitcode (car result))
515 (output (cdr result)))
516 (cond
517 ((eq exitcode 0) (substring output 0 -1))
518 (t nil))))))
519
520 (defun vc-bzr-create-repo ()
521 "Create a new Bzr repository."
522 (vc-bzr-command "init" nil 0 nil))
523
524 (defun vc-bzr-init-revision (&optional file)
525 "Always return nil, as Bzr cannot register explicit versions."
526 nil)
527
528 (defun vc-bzr-previous-revision (file rev)
529 (if (string-match "\\`[0-9]+\\'" rev)
530 (number-to-string (1- (string-to-number rev)))
531 (concat "before:" rev)))
532
533 (defun vc-bzr-next-revision (file rev)
534 (if (string-match "\\`[0-9]+\\'" rev)
535 (number-to-string (1+ (string-to-number rev)))
536 (error "Don't know how to compute the next revision of %s" rev)))
537
538 (defun vc-bzr-register (files &optional rev comment)
539 "Register FILES under bzr.
540 Signal an error unless REV is nil.
541 COMMENT is ignored."
542 (if rev (error "Can't register explicit revision with bzr"))
543 (vc-bzr-command "add" nil 0 files))
544
545 ;; Could run `bzr status' in the directory and see if it succeeds, but
546 ;; that's relatively expensive.
547 (defalias 'vc-bzr-responsible-p 'vc-bzr-root
548 "Return non-nil if FILE is (potentially) controlled by bzr.
549 The criterion is that there is a `.bzr' directory in the same
550 or a superior directory.")
551
552 (defun vc-bzr-could-register (file)
553 "Return non-nil if FILE could be registered under bzr."
554 (and (vc-bzr-responsible-p file) ; shortcut
555 (condition-case ()
556 (with-temp-buffer
557 (vc-bzr-command "add" t 0 file "--dry-run")
558 ;; The command succeeds with no output if file is
559 ;; registered (in bzr 0.8).
560 (goto-char (point-min))
561 (looking-at "added "))
562 (error))))
563
564 (defun vc-bzr-unregister (file)
565 "Unregister FILE from bzr."
566 (vc-bzr-command "remove" nil 0 file "--keep"))
567
568 (declare-function log-edit-extract-headers "log-edit" (headers string))
569
570 (defun vc-bzr-checkin (files rev comment)
571 "Check FILES in to bzr with log message COMMENT.
572 REV non-nil gets an error."
573 (if rev (error "Can't check in a specific revision with bzr"))
574 (apply 'vc-bzr-command "commit" nil 0
575 files (cons "-m" (log-edit-extract-headers '(("Author" . "--author")
576 ("Date" . "--commit-time")
577 ("Fixes" . "--fixes"))
578 comment))))
579
580 (defun vc-bzr-find-revision (file rev buffer)
581 "Fetch revision REV of file FILE and put it into BUFFER."
582 (with-current-buffer buffer
583 (if (and rev (stringp rev) (not (string= rev "")))
584 (vc-bzr-command "cat" t 0 file "-r" rev)
585 (vc-bzr-command "cat" t 0 file))))
586
587 (defun vc-bzr-checkout (file &optional editable rev)
588 (if rev (error "Operation not supported")
589 ;; Else, there's nothing to do.
590 nil))
591
592 (defun vc-bzr-revert (file &optional contents-done)
593 (unless contents-done
594 (with-temp-buffer (vc-bzr-command "revert" t 0 file))))
595
596 (defvar log-view-message-re)
597 (defvar log-view-file-re)
598 (defvar log-view-font-lock-keywords)
599 (defvar log-view-current-tag-function)
600 (defvar log-view-per-file-logs)
601
602 (define-derived-mode vc-bzr-log-view-mode log-view-mode "Bzr-Log-View"
603 (remove-hook 'log-view-mode-hook 'vc-bzr-log-view-mode) ;Deactivate the hack.
604 (require 'add-log)
605 (set (make-local-variable 'log-view-per-file-logs) nil)
606 (set (make-local-variable 'log-view-file-re) "\\`a\\`")
607 (set (make-local-variable 'log-view-message-re)
608 (if (eq vc-log-view-type 'short)
609 "^ *\\([0-9.]+\\): \\(.*?\\)[ \t]+\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)\\( \\[merge\\]\\)?"
610 "^ *\\(?:revno: \\([0-9.]+\\)\\|merged: .+\\)"))
611 (set (make-local-variable 'log-view-font-lock-keywords)
612 ;; log-view-font-lock-keywords is careful to use the buffer-local
613 ;; value of log-view-message-re only since Emacs-23.
614 (if (eq vc-log-view-type 'short)
615 (append `((,log-view-message-re
616 (1 'log-view-message-face)
617 (2 'change-log-name)
618 (3 'change-log-date)
619 (4 'change-log-list nil lax))))
620 (append `((,log-view-message-re . 'log-view-message-face))
621 ;; log-view-font-lock-keywords
622 '(("^ *\\(?:committer\\|author\\): \
623 \\([^<(]+?\\)[ ]*[(<]\\([[:alnum:]_.+-]+@[[:alnum:]_.-]+\\)[>)]"
624 (1 'change-log-name)
625 (2 'change-log-email))
626 ("^ *timestamp: \\(.*\\)" (1 'change-log-date-face)))))))
627
628 (defun vc-bzr-print-log (files buffer &optional shortlog start-revision limit)
629 "Get bzr change log for FILES into specified BUFFER."
630 ;; `vc-do-command' creates the buffer, but we need it before running
631 ;; the command.
632 (vc-setup-buffer buffer)
633 ;; If the buffer exists from a previous invocation it might be
634 ;; read-only.
635 ;; FIXME: `vc-bzr-command' runs `bzr log' with `LC_MESSAGES=C', so
636 ;; the log display may not what the user wants - but I see no other
637 ;; way of getting the above regexps working.
638 (with-current-buffer buffer
639 (apply 'vc-bzr-command "log" buffer 'async files
640 (append
641 (when shortlog '("--line"))
642 (when start-revision (list (format "-r..%s" start-revision)))
643 (when limit (list "-l" (format "%s" limit)))
644 (if (stringp vc-bzr-log-switches)
645 (list vc-bzr-log-switches)
646 vc-bzr-log-switches)))))
647
648 (defun vc-bzr-log-incoming (buffer remote-location)
649 (apply 'vc-bzr-command "missing" buffer 'async nil
650 (list "--theirs-only" (unless (string= remote-location "") remote-location))))
651
652 (defun vc-bzr-log-outgoing (buffer remote-location)
653 (apply 'vc-bzr-command "missing" buffer 'async nil
654 (list "--mine-only" (unless (string= remote-location "") remote-location))))
655
656 (defun vc-bzr-show-log-entry (revision)
657 "Find entry for patch name REVISION in bzr change log buffer."
658 (goto-char (point-min))
659 (when revision
660 (let (case-fold-search
661 found)
662 (if (re-search-forward
663 ;; "revno:" can appear either at the beginning of a line,
664 ;; or indented.
665 (concat "^[ ]*-+\n[ ]*revno: "
666 ;; The revision can contain ".", quote it so that it
667 ;; does not interfere with regexp matching.
668 (regexp-quote revision) "$") nil t)
669 (progn
670 (beginning-of-line 0)
671 (setq found t))
672 (goto-char (point-min)))
673 found)))
674
675 (defun vc-bzr-diff (files &optional rev1 rev2 buffer)
676 "VC bzr backend for diff."
677 ;; `bzr diff' exits with code 1 if diff is non-empty.
678 (apply #'vc-bzr-command "diff" (or buffer "*vc-diff*")
679 (if vc-disable-async-diff 1 'async) files
680 "--diff-options" (mapconcat 'identity
681 (vc-switches 'bzr 'diff)
682 " ")
683 ;; This `when' is just an optimization because bzr-1.2 is *much*
684 ;; faster when the revision argument is not given.
685 (when (or rev1 rev2)
686 (list "-r" (format "%s..%s"
687 (or rev1 "revno:-1")
688 (or rev2 ""))))))
689
690
691 ;; FIXME: vc-{next,previous}-revision need fixing in vc.el to deal with
692 ;; straight integer revisions.
693
694 (defun vc-bzr-delete-file (file)
695 "Delete FILE and delete it in the bzr repository."
696 (condition-case ()
697 (delete-file file)
698 (file-error nil))
699 (vc-bzr-command "remove" nil 0 file))
700
701 (defun vc-bzr-rename-file (old new)
702 "Rename file from OLD to NEW using `bzr mv'."
703 (vc-bzr-command "mv" nil 0 new old))
704
705 (defvar vc-bzr-annotation-table nil
706 "Internal use.")
707 (make-variable-buffer-local 'vc-bzr-annotation-table)
708
709 (defun vc-bzr-annotate-command (file buffer &optional revision)
710 "Prepare BUFFER for `vc-annotate' on FILE.
711 Each line is tagged with the revision number, which has a `help-echo'
712 property containing author and date information."
713 (apply #'vc-bzr-command "annotate" buffer 'async file "--long" "--all"
714 (if revision (list "-r" revision)))
715 (lexical-let ((table (make-hash-table :test 'equal)))
716 (set-process-filter
717 (get-buffer-process buffer)
718 (lambda (proc string)
719 (when (process-buffer proc)
720 (with-current-buffer (process-buffer proc)
721 (setq string (concat (process-get proc :vc-left-over) string))
722 ;; Eg: 102020 Gnus developers 20101020 | regexp."
723 ;; As of bzr 2.2.2, no email address in whoami (which can
724 ;; lead to spaces in the author field) is allowed but discouraged.
725 ;; See bug#7792.
726 (while (string-match "^\\( *[0-9.]+ *\\) \\(.+?\\) +\\([0-9]\\{8\\}\\)\\( |.*\n\\)" string)
727 (let* ((rev (match-string 1 string))
728 (author (match-string 2 string))
729 (date (match-string 3 string))
730 (key (substring string (match-beginning 0)
731 (match-beginning 4)))
732 (line (match-string 4 string))
733 (tag (gethash key table))
734 (inhibit-read-only t))
735 (setq string (substring string (match-end 0)))
736 (unless tag
737 (setq tag
738 (propertize
739 (format "%s %-7.7s" rev author)
740 'help-echo (format "Revision: %d, author: %s, date: %s"
741 (string-to-number rev)
742 author date)
743 'mouse-face 'highlight))
744 (puthash key tag table))
745 (goto-char (process-mark proc))
746 (insert tag line)
747 (move-marker (process-mark proc) (point))))
748 (process-put proc :vc-left-over string)))))))
749
750 (declare-function vc-annotate-convert-time "vc-annotate" (time))
751
752 (defun vc-bzr-annotate-time ()
753 (when (re-search-forward "^ *[0-9.]+ +.+? +|" nil t)
754 (let ((prop (get-text-property (line-beginning-position) 'help-echo)))
755 (string-match "[0-9]+\\'" prop)
756 (let ((str (match-string-no-properties 0 prop)))
757 (vc-annotate-convert-time
758 (encode-time 0 0 0
759 (string-to-number (substring str 6 8))
760 (string-to-number (substring str 4 6))
761 (string-to-number (substring str 0 4))))))))
762
763 (defun vc-bzr-annotate-extract-revision-at-line ()
764 "Return revision for current line of annotation buffer, or nil.
765 Return nil if current line isn't annotated."
766 (save-excursion
767 (beginning-of-line)
768 (if (looking-at "^ *\\([0-9.]+\\) +.* +|")
769 (match-string-no-properties 1))))
770
771 (defun vc-bzr-command-discarding-stderr (command &rest args)
772 "Execute shell command COMMAND (with ARGS); return its output and exitcode.
773 Return value is a cons (EXITCODE . OUTPUT), where EXITCODE is
774 the (numerical) exit code of the process, and OUTPUT is a string
775 containing whatever the process sent to its standard output
776 stream. Standard error output is discarded."
777 (with-temp-buffer
778 (cons
779 (apply #'process-file command nil (list (current-buffer) nil) nil args)
780 (buffer-substring (point-min) (point-max)))))
781
782 (defstruct (vc-bzr-extra-fileinfo
783 (:copier nil)
784 (:constructor vc-bzr-create-extra-fileinfo (extra-name))
785 (:conc-name vc-bzr-extra-fileinfo->))
786 extra-name) ;; original name for rename targets, new name for
787
788 (defun vc-bzr-dir-printer (info)
789 "Pretty-printer for the vc-dir-fileinfo structure."
790 (let ((extra (vc-dir-fileinfo->extra info)))
791 (vc-default-dir-printer 'Bzr info)
792 (when extra
793 (insert (propertize
794 (format " (renamed from %s)"
795 (vc-bzr-extra-fileinfo->extra-name extra))
796 'face 'font-lock-comment-face)))))
797
798 ;; FIXME: this needs testing, it's probably incomplete.
799 (defun vc-bzr-after-dir-status (update-function relative-dir)
800 (let ((status-str nil)
801 (translation '(("+N " . added)
802 ("-D " . removed)
803 (" M " . edited) ;; file text modified
804 (" *" . edited) ;; execute bit changed
805 (" M*" . edited) ;; text modified + execute bit changed
806 ;; FIXME: what about ignored files?
807 (" D " . missing)
808 ;; For conflicts, should we list the .THIS/.BASE/.OTHER?
809 ("C " . conflict)
810 ("? " . unregistered)
811 ;; No such state, but we need to distinguish this case.
812 ("R " . renamed)
813 ("RM " . renamed)
814 ;; For a non existent file FOO, the output is:
815 ;; bzr: ERROR: Path(s) do not exist: FOO
816 ("bzr" . not-found)
817 ;; If the tree is not up to date, bzr will print this warning:
818 ;; working tree is out of date, run 'bzr update'
819 ;; ignore it.
820 ;; FIXME: maybe this warning can be put in the vc-dir header...
821 ("wor" . not-found)
822 ;; Ignore "P " and "P." for pending patches.
823 ("P " . not-found)
824 ("P. " . not-found)
825 ))
826 (translated nil)
827 (result nil))
828 (goto-char (point-min))
829 (while (not (eobp))
830 (setq status-str
831 (buffer-substring-no-properties (point) (+ (point) 3)))
832 (setq translated (cdr (assoc status-str translation)))
833 (cond
834 ((eq translated 'conflict)
835 ;; For conflicts the file appears twice in the listing: once
836 ;; with the M flag and once with the C flag, so take care
837 ;; not to add it twice to `result'. Ugly.
838 (let* ((file
839 (buffer-substring-no-properties
840 ;;For files with conflicts the format is:
841 ;;C Text conflict in FILENAME
842 ;; Bah.
843 (+ (point) 21) (line-end-position)))
844 (entry (assoc file result)))
845 (when entry
846 (setf (nth 1 entry) 'conflict))))
847 ((eq translated 'renamed)
848 (re-search-forward "R[ M] \\(.*\\) => \\(.*\\)$" (line-end-position) t)
849 (let ((new-name (file-relative-name (match-string 2) relative-dir))
850 (old-name (file-relative-name (match-string 1) relative-dir)))
851 (push (list new-name 'edited
852 (vc-bzr-create-extra-fileinfo old-name)) result)))
853 ;; do nothing for non existent files
854 ((eq translated 'not-found))
855 (t
856 (push (list (file-relative-name
857 (buffer-substring-no-properties
858 (+ (point) 4)
859 (line-end-position)) relative-dir)
860 translated) result)))
861 (forward-line))
862 (funcall update-function result)))
863
864 (defun vc-bzr-dir-status (dir update-function)
865 "Return a list of conses (file . state) for DIR."
866 (vc-bzr-command "status" (current-buffer) 'async dir "-v" "-S")
867 (vc-exec-after
868 `(vc-bzr-after-dir-status (quote ,update-function)
869 ;; "bzr status" results are relative to
870 ;; the bzr root directory, NOT to the
871 ;; directory "bzr status" was invoked in.
872 ;; Ugh.
873 ;; We pass the relative directory here so
874 ;; that `vc-bzr-after-dir-status' can
875 ;; frob the results accordingly.
876 (file-relative-name ,dir (vc-bzr-root ,dir)))))
877
878 (defun vc-bzr-dir-status-files (dir files default-state update-function)
879 "Return a list of conses (file . state) for DIR."
880 (apply 'vc-bzr-command "status" (current-buffer) 'async dir "-v" "-S" files)
881 (vc-exec-after
882 `(vc-bzr-after-dir-status (quote ,update-function)
883 (file-relative-name ,dir (vc-bzr-root ,dir)))))
884
885 (defvar vc-bzr-shelve-map
886 (let ((map (make-sparse-keymap)))
887 ;; Turn off vc-dir marking
888 (define-key map [mouse-2] 'ignore)
889
890 (define-key map [down-mouse-3] 'vc-bzr-shelve-menu)
891 (define-key map "\C-k" 'vc-bzr-shelve-delete-at-point)
892 (define-key map "=" 'vc-bzr-shelve-show-at-point)
893 (define-key map "\C-m" 'vc-bzr-shelve-show-at-point)
894 (define-key map "A" 'vc-bzr-shelve-apply-and-keep-at-point)
895 (define-key map "P" 'vc-bzr-shelve-apply-at-point)
896 (define-key map "S" 'vc-bzr-shelve-snapshot)
897 map))
898
899 (defvar vc-bzr-shelve-menu-map
900 (let ((map (make-sparse-keymap "Bzr Shelve")))
901 (define-key map [de]
902 '(menu-item "Delete shelf" vc-bzr-shelve-delete-at-point
903 :help "Delete the current shelf"))
904 (define-key map [ap]
905 '(menu-item "Apply and keep shelf" vc-bzr-shelve-apply-and-keep-at-point
906 :help "Apply the current shelf and keep it"))
907 (define-key map [po]
908 '(menu-item "Apply and remove shelf (pop)" vc-bzr-shelve-apply-at-point
909 :help "Apply the current shelf and remove it"))
910 (define-key map [sh]
911 '(menu-item "Show shelve" vc-bzr-shelve-show-at-point
912 :help "Show the contents of the current shelve"))
913 map))
914
915 (defvar vc-bzr-extra-menu-map
916 (let ((map (make-sparse-keymap)))
917 (define-key map [bzr-sn]
918 '(menu-item "Shelve a snapshot" vc-bzr-shelve-snapshot
919 :help "Shelve the current state of the tree and keep the current state"))
920 (define-key map [bzr-sh]
921 '(menu-item "Shelve..." vc-bzr-shelve
922 :help "Shelve changes"))
923 map))
924
925 (defun vc-bzr-extra-menu () vc-bzr-extra-menu-map)
926
927 (defun vc-bzr-extra-status-menu () vc-bzr-extra-menu-map)
928
929 (defun vc-bzr-dir-extra-headers (dir)
930 (let*
931 ((str (with-temp-buffer
932 (vc-bzr-command "info" t 0 dir)
933 (buffer-string)))
934 (shelve (vc-bzr-shelve-list))
935 (shelve-help-echo "Use M-x vc-bzr-shelve to create shelves")
936 (root-dir (vc-bzr-root dir))
937 (pending-merge
938 ;; FIXME: looking for .bzr/checkout/merge-hashes is not a
939 ;; reliable method to detect pending merges, disable this
940 ;; until a proper solution is implemented.
941 (and nil
942 (file-exists-p
943 (expand-file-name ".bzr/checkout/merge-hashes" root-dir))))
944 (pending-merge-help-echo
945 (format "A merge has been performed.\nA commit from the top-level directory (%s)\nis required before being able to check in anything else" root-dir))
946 (light-checkout
947 (when (string-match ".+light checkout root: \\(.+\\)$" str)
948 (match-string 1 str)))
949 (light-checkout-branch
950 (when light-checkout
951 (when (string-match ".+checkout of branch: \\(.+\\)$" str)
952 (match-string 1 str)))))
953 (concat
954 (propertize "Parent branch : " 'face 'font-lock-type-face)
955 (propertize
956 (if (string-match "parent branch: \\(.+\\)$" str)
957 (match-string 1 str)
958 "None")
959 'face 'font-lock-variable-name-face)
960 "\n"
961 (when light-checkout
962 (concat
963 (propertize "Light checkout root: " 'face 'font-lock-type-face)
964 (propertize light-checkout 'face 'font-lock-variable-name-face)
965 "\n"))
966 (when light-checkout-branch
967 (concat
968 (propertize "Checkout of branch : " 'face 'font-lock-type-face)
969 (propertize light-checkout-branch 'face 'font-lock-variable-name-face)
970 "\n"))
971 (when pending-merge
972 (concat
973 (propertize "Warning : " 'face 'font-lock-warning-face
974 'help-echo pending-merge-help-echo)
975 (propertize "Pending merges, commit recommended before any other action"
976 'help-echo pending-merge-help-echo
977 'face 'font-lock-warning-face)
978 "\n"))
979 (if shelve
980 (concat
981 (propertize "Shelves :\n" 'face 'font-lock-type-face
982 'help-echo shelve-help-echo)
983 (mapconcat
984 (lambda (x)
985 (propertize x
986 'face 'font-lock-variable-name-face
987 'mouse-face 'highlight
988 'help-echo "mouse-3: Show shelve menu\nA: Apply and keep shelf\nP: Apply and remove shelf (pop)\nS: Snapshot to a shelf\nC-k: Delete shelf"
989 'keymap vc-bzr-shelve-map))
990 shelve "\n"))
991 (concat
992 (propertize "Shelves : " 'face 'font-lock-type-face
993 'help-echo shelve-help-echo)
994 (propertize "No shelved changes"
995 'help-echo shelve-help-echo
996 'face 'font-lock-variable-name-face))))))
997
998 (defun vc-bzr-shelve (name)
999 "Create a shelve."
1000 (interactive "sShelf name: ")
1001 (let ((root (vc-bzr-root default-directory)))
1002 (when root
1003 (vc-bzr-command "shelve" nil 0 nil "--all" "-m" name)
1004 (vc-resynch-buffer root t t))))
1005
1006 (defun vc-bzr-shelve-show (name)
1007 "Show the contents of shelve NAME."
1008 (interactive "sShelve name: ")
1009 (vc-setup-buffer "*vc-diff*")
1010 ;; FIXME: how can you show the contents of a shelf?
1011 (vc-bzr-command "unshelve" "*vc-diff*" 'async nil "--preview" name)
1012 (set-buffer "*vc-diff*")
1013 (diff-mode)
1014 (setq buffer-read-only t)
1015 (pop-to-buffer (current-buffer)))
1016
1017 (defun vc-bzr-shelve-apply (name)
1018 "Apply shelve NAME and remove it afterwards."
1019 (interactive "sApply (and remove) shelf: ")
1020 (vc-bzr-command "unshelve" nil 0 nil "--apply" name)
1021 (vc-resynch-buffer (vc-bzr-root default-directory) t t))
1022
1023 (defun vc-bzr-shelve-apply-and-keep (name)
1024 "Apply shelve NAME and keep it afterwards."
1025 (interactive "sApply (and keep) shelf: ")
1026 (vc-bzr-command "unshelve" nil 0 nil "--apply" "--keep" name)
1027 (vc-resynch-buffer (vc-bzr-root default-directory) t t))
1028
1029 (defun vc-bzr-shelve-snapshot ()
1030 "Create a stash with the current tree state."
1031 (interactive)
1032 (vc-bzr-command "shelve" nil 0 nil "--all" "-m"
1033 (let ((ct (current-time)))
1034 (concat
1035 (format-time-string "Snapshot on %Y-%m-%d" ct)
1036 (format-time-string " at %H:%M" ct))))
1037 (vc-bzr-command "unshelve" nil 0 nil "--apply" "--keep")
1038 (vc-resynch-buffer (vc-bzr-root default-directory) t t))
1039
1040 (defun vc-bzr-shelve-list ()
1041 (with-temp-buffer
1042 (vc-bzr-command "shelve" (current-buffer) 1 nil "--list" "-q")
1043 (delete
1044 ""
1045 (split-string
1046 (buffer-substring (point-min) (point-max))
1047 "\n"))))
1048
1049 (defun vc-bzr-shelve-get-at-point (point)
1050 (save-excursion
1051 (goto-char point)
1052 (beginning-of-line)
1053 (if (looking-at "^ +\\([0-9]+\\):")
1054 (match-string 1)
1055 (error "Cannot find shelf at point"))))
1056
1057 (defun vc-bzr-shelve-delete-at-point ()
1058 (interactive)
1059 (let ((shelve (vc-bzr-shelve-get-at-point (point))))
1060 (when (y-or-n-p (format "Remove shelf %s ? " shelve))
1061 (vc-bzr-command "unshelve" nil 0 nil "--delete-only" shelve)
1062 (vc-dir-refresh))))
1063
1064 (defun vc-bzr-shelve-show-at-point ()
1065 (interactive)
1066 (vc-bzr-shelve-show (vc-bzr-shelve-get-at-point (point))))
1067
1068 (defun vc-bzr-shelve-apply-at-point ()
1069 (interactive)
1070 (vc-bzr-shelve-apply (vc-bzr-shelve-get-at-point (point))))
1071
1072 (defun vc-bzr-shelve-apply-and-keep-at-point ()
1073 (interactive)
1074 (vc-bzr-shelve-apply-and-keep (vc-bzr-shelve-get-at-point (point))))
1075
1076 (defun vc-bzr-shelve-menu (e)
1077 (interactive "e")
1078 (vc-dir-at-event e (popup-menu vc-bzr-shelve-menu-map e)))
1079
1080 (defun vc-bzr-revision-table (files)
1081 (let ((vc-bzr-revisions '())
1082 (default-directory (file-name-directory (car files))))
1083 (with-temp-buffer
1084 (vc-bzr-command "log" t 0 files "--line")
1085 (let ((start (point-min))
1086 (loglines (buffer-substring-no-properties (point-min) (point-max))))
1087 (while (string-match "^\\([0-9]+\\):" loglines)
1088 (push (match-string 1 loglines) vc-bzr-revisions)
1089 (setq start (+ start (match-end 0)))
1090 (setq loglines (buffer-substring-no-properties start (point-max))))))
1091 vc-bzr-revisions))
1092
1093 (defun vc-bzr-conflicted-files (dir)
1094 (let ((default-directory (vc-bzr-root dir))
1095 (files ()))
1096 (with-temp-buffer
1097 (vc-bzr-command "status" t 0 default-directory)
1098 (goto-char (point-min))
1099 (when (re-search-forward "^conflicts:\n" nil t)
1100 (while (looking-at " \\(?:Text conflict in \\(.*\\)\\|.*\\)\n")
1101 (if (match-end 1)
1102 (push (expand-file-name (match-string 1)) files))
1103 (goto-char (match-end 0)))))
1104 files))
1105
1106 ;;; Revision completion
1107
1108 (eval-and-compile
1109 (defconst vc-bzr-revision-keywords
1110 '("revno" "revid" "last" "before"
1111 "tag" "date" "ancestor" "branch" "submit")))
1112
1113 (defun vc-bzr-revision-completion-table (files)
1114 (lexical-let ((files files))
1115 ;; What about using `files'?!? --Stef
1116 (lambda (string pred action)
1117 (cond
1118 ((string-match "\\`\\(ancestor\\|branch\\|\\(revno:\\)?[-0-9]+:\\):"
1119 string)
1120 (completion-table-with-context (substring string 0 (match-end 0))
1121 (apply-partially
1122 'completion-table-with-predicate
1123 'completion-file-name-table
1124 'file-directory-p t)
1125 (substring string (match-end 0))
1126 pred
1127 action))
1128 ((string-match "\\`\\(before\\):" string)
1129 (completion-table-with-context (substring string 0 (match-end 0))
1130 (vc-bzr-revision-completion-table files)
1131 (substring string (match-end 0))
1132 pred
1133 action))
1134 ((string-match "\\`\\(tag\\):" string)
1135 (let ((prefix (substring string 0 (match-end 0)))
1136 (tag (substring string (match-end 0)))
1137 (table nil)
1138 process-file-side-effects)
1139 (with-temp-buffer
1140 ;; "bzr-1.2 tags" is much faster with --show-ids.
1141 (process-file vc-bzr-program nil '(t) nil "tags" "--show-ids")
1142 ;; The output is ambiguous, unless we assume that revids do not
1143 ;; contain spaces.
1144 (goto-char (point-min))
1145 (while (re-search-forward "^\\(.*[^ \n]\\) +[^ \n]*$" nil t)
1146 (push (match-string-no-properties 1) table)))
1147 (completion-table-with-context prefix table tag pred action)))
1148
1149 ((string-match "\\`\\([a-z]+\\):" string)
1150 ;; no actual completion for the remaining keywords.
1151 (completion-table-with-context (substring string 0 (match-end 0))
1152 (if (member (match-string 1 string)
1153 vc-bzr-revision-keywords)
1154 ;; If it's a valid keyword,
1155 ;; use a non-empty table to
1156 ;; indicate it.
1157 '("") nil)
1158 (substring string (match-end 0))
1159 pred
1160 action))
1161 (t
1162 ;; Could use completion-table-with-terminator, except that it
1163 ;; currently doesn't work right w.r.t pcm and doesn't give
1164 ;; the *Completions* output we want.
1165 (complete-with-action action (eval-when-compile
1166 (mapcar (lambda (s) (concat s ":"))
1167 vc-bzr-revision-keywords))
1168 string pred))))))
1169
1170 (provide 'vc-bzr)
1171
1172 ;;; vc-bzr.el ends here