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