Merge changes made in Gnus trunk.
[bpt/emacs.git] / lisp / vc / vc-bzr.el
CommitLineData
b6e0e86c
SM
1;;; vc-bzr.el --- VC backend for the bzr revision control system
2
73b0cd50 3;; Copyright (C) 2006-2011 Free Software Foundation, Inc.
b6e0e86c 4
eb2ffb18
GM
5;; Author: Dave Love <fx@gnu.org>
6;; Riccardo Murri <riccardo.murri@gmail.com>
2b404597 7;; Maintainer: FSF
9766adfb 8;; Keywords: vc tools
b6e0e86c 9;; Created: Sept 2006
bd78fa1d 10;; Package: vc
b6e0e86c 11
eb3fa2cf
GM
12;; This file is part of GNU Emacs.
13
14;; GNU Emacs is free software: you can redistribute it and/or modify
b6e0e86c 15;; it under the terms of the GNU General Public License as published by
eb3fa2cf
GM
16;; the Free Software Foundation, either version 3 of the License, or
17;; (at your option) any later version.
b6e0e86c 18
eb3fa2cf 19;; GNU Emacs is distributed in the hope that it will be useful,
b6e0e86c
SM
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
eb3fa2cf 25;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
b6e0e86c
SM
26
27;;; Commentary:
28
2b404597 29;; See <URL:http://bazaar.canonical.com/> concerning bzr.
b6e0e86c 30
2b404597 31;; This library provides bzr support in VC.
77b5d458
SM
32
33;; Known bugs
34;; ==========
35
ba5bf642 36;; When editing a symlink and *both* the symlink and its target
77b5d458
SM
37;; are bzr-versioned, `vc-bzr` presently runs `bzr status` on the
38;; symlink, thereby not detecting whether the actual contents
eb3fa2cf 39;; (that is, the target contents) are changed.
77b5d458
SM
40;; See https://bugs.launchpad.net/vc-bzr/+bug/116607
41
70e2f6c7
ER
42;;; Properties of the backend
43
44(defun vc-bzr-revision-granularity () 'repository)
45(defun vc-bzr-checkout-model (files) 'implicit)
b6e0e86c
SM
46
47;;; Code:
48
49(eval-when-compile
37320a58 50 (require 'cl)
a0c38937
DN
51 (require 'vc) ;; for vc-exec-after
52 (require 'vc-dir))
b6e0e86c 53
360cf7bc
SM
54;; Clear up the cache to force vc-call to check again and discover
55;; new functions when we reload this file.
4211679b 56(put 'Bzr 'vc-functions nil)
360cf7bc 57
b6e0e86c
SM
58(defgroup vc-bzr nil
59 "VC bzr backend."
fba500b6 60 :version "22.2"
b6e0e86c
SM
61 :group 'vc)
62
63(defcustom vc-bzr-program "bzr"
37320a58 64 "Name of the bzr command (excluding any arguments)."
b6e0e86c
SM
65 :group 'vc-bzr
66 :type 'string)
67
b6e0e86c 68(defcustom vc-bzr-diff-switches nil
f9528daa
GM
69 "String or list of strings specifying switches for bzr diff under VC.
70If 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)
b6e0e86c
SM
73 (string :tag "Argument String")
74 (repeat :tag "Argument List" :value ("") string))
75 :group 'vc-bzr)
76
f4d0cf23 77(defcustom vc-bzr-log-switches nil
f9528daa 78 "String or list of strings specifying switches for bzr log under VC."
f4d0cf23
SM
79 :type '(choice (const :tag "None" nil)
80 (string :tag "Argument String")
81 (repeat :tag "Argument List" :value ("") string))
82 :group 'vc-bzr)
83
37320a58
SM
84;; since v0.9, bzr supports removing the progress indicators
85;; by setting environment variable BZR_PROGRESS_BAR to "none".
8cdd17b4 86(defun vc-bzr-command (bzr-command buffer okstatus file-or-list &rest args)
37320a58 87 "Wrapper round `vc-do-command' using `vc-bzr-program' as COMMAND.
f4d0cf23
SM
88Invoke the bzr command adding `BZR_PROGRESS_BAR=none' and
89`LC_MESSAGES=C' to the environment."
37320a58
SM
90 (let ((process-environment
91 (list* "BZR_PROGRESS_BAR=none" ; Suppress progress output (bzr >=0.9)
f4d0cf23 92 "LC_MESSAGES=C" ; Force English output
a460c94c 93 process-environment)))
2888a97e 94 (apply 'vc-do-command (or buffer "*vc*") okstatus vc-bzr-program
f4d0cf23 95 file-or-list bzr-command args)))
b6e0e86c 96
37320a58
SM
97
98;;;###autoload
f4d0cf23 99(defconst vc-bzr-admin-dirname ".bzr"
b6e6e09a 100 "Name of the directory containing Bzr repository status files.")
2b404597 101;; Used in the autoloaded vc-bzr-registered; see below.
a460c94c 102;;;###autoload
b6e6e09a
SM
103(defconst vc-bzr-admin-checkout-format-file
104 (concat vc-bzr-admin-dirname "/checkout/format"))
a460c94c 105(defconst vc-bzr-admin-dirstate
b6e6e09a
SM
106 (concat vc-bzr-admin-dirname "/checkout/dirstate"))
107(defconst vc-bzr-admin-branch-format-file
108 (concat vc-bzr-admin-dirname "/branch/format"))
a460c94c 109(defconst vc-bzr-admin-revhistory
b6e6e09a 110 (concat vc-bzr-admin-dirname "/branch/revision-history"))
12451866
RF
111(defconst vc-bzr-admin-lastrev
112 (concat vc-bzr-admin-dirname "/branch/last-revision"))
2c3160c5
CY
113(defconst vc-bzr-admin-branchconf
114 (concat vc-bzr-admin-dirname "/branch/branch.conf"))
37320a58
SM
115
116;;;###autoload (defun vc-bzr-registered (file)
b6e6e09a 117;;;###autoload (if (vc-find-root file vc-bzr-admin-checkout-format-file)
37320a58
SM
118;;;###autoload (progn
119;;;###autoload (load "vc-bzr")
120;;;###autoload (vc-bzr-registered file))))
b6e0e86c 121
a460c94c
SM
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)
b38f5e6f
DN
126 (let ((root (vc-find-root file vc-bzr-admin-checkout-format-file)))
127 (when root (vc-file-setprop file 'bzr-root root)))))
b6e0e86c 128
2c3160c5
CY
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
82eb83ff 136(require 'sha1) ;For sha1-program
a460c94c 137
82eb83ff
SM
138(defun vc-bzr-sha1 (file)
139 (with-temp-buffer
140 (set-buffer-multibyte nil)
141 (let ((prog sha1-program)
e82b1099
MA
142 (args nil)
143 process-file-side-effects)
82eb83ff
SM
144 (when (consp prog)
145 (setq args (cdr prog))
146 (setq prog (car prog)))
07c4b87c 147 (apply 'process-file prog (file-relative-name file) t nil args)
82eb83ff
SM
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."
2b404597 152 ;; `bzr status' was excruciatingly slow with large histories and
82eb83ff
SM
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'."
fba500b6
SM
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)))
2793b89e
SM
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?
137d88ca
DN
184 "\\([^\0]*\\)\0" ;sha1 (empty if conflicted)?
185 "\\([^\0]*\\)\0" ;size?p
2b404597
GM
186 ;; y/n. Whether or not the current copy
187 ;; was executable the last time bzr checked?
188 "[^\0]*\0"
2793b89e
SM
189 "[^\0]*\0" ;?
190 "\\([^\0]*\\)\0" ;"a/f/d" a=added?
191 "\\([^\0]*\\)\0" ;sha1 again?
137d88ca 192 "\\([^\0]*\\)\0" ;size again?
2b404597
GM
193 ;; y/n. Whether or not the repo thinks
194 ;; the file should be executable?
195 "\\([^\0]*\\)\0"
2793b89e
SM
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)
137d88ca
DN
206 ((eq (char-after (match-beginning 4)) ?a) 'added)
207 ((or (and (eq (string-to-number (match-string 3))
2793b89e 208 (nth 7 (file-attributes file)))
2b404597
GM
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)))))
137d88ca
DN
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))))
2793b89e
SM
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
82eb83ff
SM
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)))))
77b5d458
SM
248
249(defconst vc-bzr-state-words
33e5d7d4 250 "added\\|ignored\\|kind changed\\|modified\\|removed\\|renamed\\|unknown"
77b5d458
SM
251 "Regexp matching file status words as reported in `bzr' output.")
252
2c3160c5
CY
253;; History of Bzr commands.
254(defvar vc-bzr-history nil)
255
b6e6e09a
SM
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))
f4d0cf23 260 (rootdir (vc-bzr-root filename*)))
def61be2 261 (when rootdir
b6e6e09a
SM
262 (file-relative-name filename* rootdir))))
263
2c3160c5
CY
264(defun vc-bzr-async-command (command args)
265 "Run Bzr COMMAND asynchronously with ARGS, displaying the result.
266Send the output to a buffer named \"*vc-bzr : NAME*\", where NAME
267is the root of the current Bzr branch. Display the buffer in
268some window, but don't select it."
269 ;; TODO: set up hyperlinks.
270 (let* ((dir default-directory)
271 (root (vc-bzr-root default-directory))
3d92f44e
CY
272 (buffer (get-buffer-create
273 (format "*vc-bzr : %s*"
274 (expand-file-name root)))))
2c3160c5
CY
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.
291Normally, this runs \"bzr pull\". However, if the branch is a
292bound branch, run \"bzr update\" instead. If there is no default
293location from which to pull or update, or if PROMPT is non-nil,
294prompt 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"))
3d92f44e 306 args)
2c3160c5
CY
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
3d92f44e 320(defun vc-bzr-merge-branch ()
2c3160c5 321 "Merge another Bzr branch into the current one.
3d92f44e
CY
322Prompt for the Bzr command to run, providing a pre-defined merge
323source (an upstream branch or a previous merge source) as a
324default 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)))
2c3160c5
CY
350 (vc-bzr-async-command command args)))
351
33e5d7d4
SM
352(defun vc-bzr-status (file)
353 "Return FILE status according to Bzr.
354Return value is a cons (STATUS . WARNING), where WARNING is a
fba500b6
SM
355string or nil, and STATUS is one of the symbols: `added',
356`ignored', `kindchanged', `modified', `removed', `renamed', `unknown',
33e5d7d4
SM
357which directly correspond to `bzr status' output, or 'unchanged
358for files whose copy in the working tree is identical to the one
359in the branch repository, or nil for files that are not
360registered with Bzr.
361
362If any error occurred in running `bzr status', then return nil."
77b5d458 363 (with-temp-buffer
fba500b6
SM
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))
f4d0cf23
SM
380 ;; Bzr appends a '/' to directory names and
381 ;; '*' to executable files
382 (if (file-directory-p file) "/?" "\\*?")
fba500b6
SM
383 "[ \t\n]*$")
384 nil t)
6e98ad29 385 (lexical-let ((statusword (match-string 1)))
fba500b6
SM
386 ;; Erase the status text that matched.
387 (delete-region (match-beginning 0) (match-end 0))
33e5d7d4 388 (setq status
f4d0cf23 389 (intern (replace-regexp-in-string " " "" statusword)))))
fba500b6
SM
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))))))))
a0e5e075 402
33e5d7d4
SM
403(defun vc-bzr-state (file)
404 (lexical-let ((result (vc-bzr-status file)))
405 (when (consp result)
6efbb10c
DN
406 (when (cdr result)
407 (message "Warnings in `bzr' output: %s" (cdr result)))
33e5d7d4 408 (cdr (assq (car result)
6a3f9bb7 409 '((added . added)
fba500b6 410 (kindchanged . edited)
33e5d7d4
SM
411 (renamed . edited)
412 (modified . edited)
54bf3704 413 (removed . removed)
3702367b 414 (ignored . ignored)
54bf3704 415 (unknown . unregistered)
33e5d7d4 416 (unchanged . up-to-date)))))))
b6e0e86c 417
b0a08954
SM
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
b6e0e86c 445(defun vc-bzr-workfile-unchanged-p (file)
33e5d7d4 446 (eq 'unchanged (car (vc-bzr-status file))))
b6e0e86c 447
ac3f4c6f 448(defun vc-bzr-working-revision (file)
82eb83ff
SM
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.
a460c94c
SM
451 (lexical-let*
452 ((rootdir (vc-bzr-root file))
6e98ad29
SM
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.
be14a425
DN
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)
37860caf
DN
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)))
6ee86780
JB
478 (when (and (memq system-type '(ms-dos windows-nt))
479 (string-match-p "^/[[:alpha:]]:" l-c-parent-dir))
6c49ab95
JB
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)
6ee86780 483 (setq l-c-parent-dir (substring l-c-parent-dir 1)))
37860caf
DN
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)))))
be14a425 492 t)))
fba500b6 493 (with-temp-buffer
def61be2 494 (insert-file-contents branch-format-file)
a460c94c
SM
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
def61be2 501 (insert-file-contents revhistory-file)
a460c94c 502 (number-to-string (count-lines (line-end-position) (point-max))))
65105010
DN
503 ((or
504 (looking-at "Bazaar Branch Format 6 (bzr 0.15)")
505 (looking-at "Bazaar Branch Format 7 (needs bzr 1.6)"))
a460c94c 506 ;; revno is the first number in .bzr/branch/last-revision
def61be2 507 (insert-file-contents lastrev-file)
65105010
DN
508 (when (re-search-forward "[0-9]+" nil t)
509 (buffer-substring (match-beginning 0) (match-end 0))))))
a460c94c 510 ;; fallback to calling "bzr revno"
b6e6e09a 511 (lexical-let*
a460c94c 512 ((result (vc-bzr-command-discarding-stderr
07c4b87c 513 vc-bzr-program "revno" (file-relative-name file)))
b6e6e09a
SM
514 (exitcode (car result))
515 (output (cdr result)))
516 (cond
517 ((eq exitcode 0) (substring output 0 -1))
518 (t nil))))))
b6e0e86c 519
a6ea7ffc 520(defun vc-bzr-create-repo ()
4211679b 521 "Create a new Bzr repository."
a6ea7ffc
DN
522 (vc-bzr-command "init" nil 0 nil))
523
25a4ea6d 524(defun vc-bzr-init-revision (&optional file)
f4d0cf23
SM
525 "Always return nil, as Bzr cannot register explicit versions."
526 nil)
527
882e82db
SM
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
8cdd17b4 538(defun vc-bzr-register (files &optional rev comment)
2b404597 539 "Register FILES under bzr.
b6e0e86c
SM
540Signal an error unless REV is nil.
541COMMENT is ignored."
5b5afd50 542 (if rev (error "Can't register explicit revision with bzr"))
8cdd17b4 543 (vc-bzr-command "add" nil 0 files))
b6e0e86c
SM
544
545;; Could run `bzr status' in the directory and see if it succeeds, but
546;; that's relatively expensive.
b6e6e09a 547(defalias 'vc-bzr-responsible-p 'vc-bzr-root
b6e0e86c
SM
548 "Return non-nil if FILE is (potentially) controlled by bzr.
549The criterion is that there is a `.bzr' directory in the same
37320a58 550or a superior directory.")
b6e0e86c
SM
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).
360cf7bc 560 (goto-char (point-min))
b6e0e86c
SM
561 (looking-at "added "))
562 (error))))
563
564(defun vc-bzr-unregister (file)
565 "Unregister FILE from bzr."
f4d0cf23 566 (vc-bzr-command "remove" nil 0 file "--keep"))
b6e0e86c 567
e97a42c1
SM
568(declare-function log-edit-extract-headers "log-edit" (headers string))
569
570(defun vc-bzr-checkin (files rev comment)
2b404597 571 "Check FILES in to bzr with log message COMMENT.
b6e0e86c 572REV non-nil gets an error."
5b5afd50 573 (if rev (error "Can't check in a specific revision with bzr"))
a1d830c7 574 (apply 'vc-bzr-command "commit" nil 0
e97a42c1 575 files (cons "-m" (log-edit-extract-headers '(("Author" . "--author")
fab43c76 576 ("Date" . "--commit-time")
e97a42c1
SM
577 ("Fixes" . "--fixes"))
578 comment))))
b6e0e86c 579
d3c24c25
DN
580(defun vc-bzr-find-revision (file rev buffer)
581 "Fetch revision REV of file FILE and put it into BUFFER."
f4d0cf23
SM
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
5a3b79c4
SM
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))
b6e0e86c
SM
591
592(defun vc-bzr-revert (file &optional contents-done)
593 (unless contents-done
33e5d7d4 594 (with-temp-buffer (vc-bzr-command "revert" t 0 file))))
b6e0e86c 595
37320a58
SM
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)
def61be2 600(defvar log-view-per-file-logs)
37320a58
SM
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.
b6e0e86c 604 (require 'add-log)
6653c6b7 605 (set (make-local-variable 'log-view-per-file-logs) nil)
1c0f0c3b 606 (set (make-local-variable 'log-view-file-re) "\\`a\\`")
37320a58 607 (set (make-local-variable 'log-view-message-re)
31527c56 608 (if (eq vc-log-view-type 'short)
755da7fa 609 "^ *\\([0-9.]+\\): \\(.*?\\)[ \t]+\\([0-9]\\{4\\}-[0-9]\\{2\\}-[0-9]\\{2\\}\\)\\( \\[merge\\]\\)?"
32ba3abc 610 "^ *\\(?:revno: \\([0-9.]+\\)\\|merged: .+\\)"))
b6e0e86c 611 (set (make-local-variable 'log-view-font-lock-keywords)
37320a58
SM
612 ;; log-view-font-lock-keywords is careful to use the buffer-local
613 ;; value of log-view-message-re only since Emacs-23.
31527c56 614 (if (eq vc-log-view-type 'short)
32ba3abc
DN
615 (append `((,log-view-message-re
616 (1 'log-view-message-face)
617 (2 'change-log-name)
618 (3 'change-log-date)
99999a1d 619 (4 'change-log-list nil lax))))
32ba3abc
DN
620 (append `((,log-view-message-re . 'log-view-message-face))
621 ;; log-view-font-lock-keywords
3d5d0aa9 622 '(("^ *\\(?:committer\\|author\\): \
5ec05779 623\\([^<(]+?\\)[ ]*[(<]\\([[:alnum:]_.+-]+@[[:alnum:]_.-]+\\)[>)]"
32ba3abc
DN
624 (1 'change-log-name)
625 (2 'change-log-email))
626 ("^ *timestamp: \\(.*\\)" (1 'change-log-date-face)))))))
b6e0e86c 627
662c5698 628(defun vc-bzr-print-log (files buffer &optional shortlog start-revision limit)
8cdd17b4 629 "Get bzr change log for FILES into specified BUFFER."
ac51b151
DN
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.
5a3b79c4
SM
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.
1c0f0c3b
DN
638 (with-current-buffer buffer
639 (apply 'vc-bzr-command "log" buffer 'async files
3f6bd790 640 (append
755da7fa 641 (when shortlog '("--line"))
662c5698 642 (when start-revision (list (format "-r..%s" start-revision)))
3f6bd790
DN
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)))))
b6e0e86c 647
31527c56
DN
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
5b5afd50
ER
656(defun vc-bzr-show-log-entry (revision)
657 "Find entry for patch name REVISION in bzr change log buffer."
b6e0e86c 658 (goto-char (point-min))
89d98000 659 (when revision
662c5698
DN
660 (let (case-fold-search
661 found)
89d98000
DN
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)
662c5698
DN
669 (progn
670 (beginning-of-line 0)
671 (setq found t))
672 (goto-char (point-min)))
673 found)))
b6e0e86c 674
8cdd17b4 675(defun vc-bzr-diff (files &optional rev1 rev2 buffer)
b6e0e86c 676 "VC bzr backend for diff."
27bbeb29
DD
677 (let* ((switches (vc-switches 'bzr 'diff))
678 (args
679 (append
680 ;; Only add --diff-options if there are any diff switches.
681 (unless (zerop (length switches))
682 (list "--diff-options" (mapconcat 'identity switches " ")))
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 ;; `bzr diff' exits with code 1 if diff is non-empty.
690 (apply #'vc-bzr-command "diff" (or buffer "*vc-diff*")
691 (if vc-disable-async-diff 1 'async) files
692 args)))
b6e0e86c 693
b6e0e86c 694
5b5afd50
ER
695;; FIXME: vc-{next,previous}-revision need fixing in vc.el to deal with
696;; straight integer revisions.
b6e0e86c
SM
697
698(defun vc-bzr-delete-file (file)
699 "Delete FILE and delete it in the bzr repository."
700 (condition-case ()
701 (delete-file file)
702 (file-error nil))
703 (vc-bzr-command "remove" nil 0 file))
704
705(defun vc-bzr-rename-file (old new)
706 "Rename file from OLD to NEW using `bzr mv'."
707 (vc-bzr-command "mv" nil 0 new old))
708
709(defvar vc-bzr-annotation-table nil
710 "Internal use.")
711(make-variable-buffer-local 'vc-bzr-annotation-table)
712
5b5afd50 713(defun vc-bzr-annotate-command (file buffer &optional revision)
b6e0e86c
SM
714 "Prepare BUFFER for `vc-annotate' on FILE.
715Each line is tagged with the revision number, which has a `help-echo'
716property containing author and date information."
62ccc42c 717 (apply #'vc-bzr-command "annotate" buffer 'async file "--long" "--all"
a6762235 718 (if revision (list "-r" revision)))
62ccc42c
SM
719 (lexical-let ((table (make-hash-table :test 'equal)))
720 (set-process-filter
721 (get-buffer-process buffer)
722 (lambda (proc string)
723 (when (process-buffer proc)
724 (with-current-buffer (process-buffer proc)
725 (setq string (concat (process-get proc :vc-left-over) string))
b0a8e46b
GM
726 ;; Eg: 102020 Gnus developers 20101020 | regexp."
727 ;; As of bzr 2.2.2, no email address in whoami (which can
728 ;; lead to spaces in the author field) is allowed but discouraged.
729 ;; See bug#7792.
730 (while (string-match "^\\( *[0-9.]+ *\\) \\(.+?\\) +\\([0-9]\\{8\\}\\)\\( |.*\n\\)" string)
62ccc42c
SM
731 (let* ((rev (match-string 1 string))
732 (author (match-string 2 string))
733 (date (match-string 3 string))
734 (key (substring string (match-beginning 0)
735 (match-beginning 4)))
736 (line (match-string 4 string))
737 (tag (gethash key table))
738 (inhibit-read-only t))
739 (setq string (substring string (match-end 0)))
2c6bb71a
CY
740 (unless tag
741 (setq tag
742 (propertize
f82b1493 743 (format "%s %-7.7s" rev author)
2c6bb71a
CY
744 'help-echo (format "Revision: %d, author: %s, date: %s"
745 (string-to-number rev)
746 author date)
747 'mouse-face 'highlight))
62ccc42c
SM
748 (puthash key tag table))
749 (goto-char (process-mark proc))
750 (insert tag line)
751 (move-marker (process-mark proc) (point))))
752 (process-put proc :vc-left-over string)))))))
b6e0e86c 753
f8bd9ac6
DN
754(declare-function vc-annotate-convert-time "vc-annotate" (time))
755
b6e0e86c 756(defun vc-bzr-annotate-time ()
362b9d48 757 (when (re-search-forward "^ *[0-9.]+ +.+? +|" nil t)
a6762235
DN
758 (let ((prop (get-text-property (line-beginning-position) 'help-echo)))
759 (string-match "[0-9]+\\'" prop)
f8381803 760 (let ((str (match-string-no-properties 0 prop)))
b6e0e86c
SM
761 (vc-annotate-convert-time
762 (encode-time 0 0 0
f8381803
SM
763 (string-to-number (substring str 6 8))
764 (string-to-number (substring str 4 6))
765 (string-to-number (substring str 0 4))))))))
b6e0e86c
SM
766
767(defun vc-bzr-annotate-extract-revision-at-line ()
2b404597 768 "Return revision for current line of annotation buffer, or nil.
b6e0e86c
SM
769Return nil if current line isn't annotated."
770 (save-excursion
771 (beginning-of-line)
b0a8e46b 772 (if (looking-at "^ *\\([0-9.]+\\) +.* +|")
a6762235 773 (match-string-no-properties 1))))
b6e0e86c 774
a460c94c
SM
775(defun vc-bzr-command-discarding-stderr (command &rest args)
776 "Execute shell command COMMAND (with ARGS); return its output and exitcode.
b6e6e09a
SM
777Return value is a cons (EXITCODE . OUTPUT), where EXITCODE is
778the (numerical) exit code of the process, and OUTPUT is a string
779containing whatever the process sent to its standard output
780stream. Standard error output is discarded."
781 (with-temp-buffer
a460c94c 782 (cons
07c4b87c 783 (apply #'process-file command nil (list (current-buffer) nil) nil args)
b6e6e09a 784 (buffer-substring (point-min) (point-max)))))
b6e0e86c 785
a0c38937
DN
786(defstruct (vc-bzr-extra-fileinfo
787 (:copier nil)
788 (:constructor vc-bzr-create-extra-fileinfo (extra-name))
789 (:conc-name vc-bzr-extra-fileinfo->))
790 extra-name) ;; original name for rename targets, new name for
791
da4d4066 792(defun vc-bzr-dir-printer (info)
a0c38937
DN
793 "Pretty-printer for the vc-dir-fileinfo structure."
794 (let ((extra (vc-dir-fileinfo->extra info)))
da4d4066 795 (vc-default-dir-printer 'Bzr info)
a0c38937
DN
796 (when extra
797 (insert (propertize
798 (format " (renamed from %s)"
799 (vc-bzr-extra-fileinfo->extra-name extra))
800 'face 'font-lock-comment-face)))))
801
701d018c 802;; FIXME: this needs testing, it's probably incomplete.
460f6e7c 803(defun vc-bzr-after-dir-status (update-function relative-dir)
7ee8e7eb 804 (let ((status-str nil)
701d018c
DN
805 (translation '(("+N " . added)
806 ("-D " . removed)
807 (" M " . edited) ;; file text modified
808 (" *" . edited) ;; execute bit changed
809 (" M*" . edited) ;; text modified + execute bit changed
810 ;; FIXME: what about ignored files?
811 (" D " . missing)
f8381803 812 ;; For conflicts, should we list the .THIS/.BASE/.OTHER?
701d018c
DN
813 ("C " . conflict)
814 ("? " . unregistered)
a0c38937
DN
815 ;; No such state, but we need to distinguish this case.
816 ("R " . renamed)
b9236874 817 ("RM " . renamed)
11b4001c
DN
818 ;; For a non existent file FOO, the output is:
819 ;; bzr: ERROR: Path(s) do not exist: FOO
820 ("bzr" . not-found)
be14a425
DN
821 ;; If the tree is not up to date, bzr will print this warning:
822 ;; working tree is out of date, run 'bzr update'
823 ;; ignore it.
824 ;; FIXME: maybe this warning can be put in the vc-dir header...
825 ("wor" . not-found)
f8381803 826 ;; Ignore "P " and "P." for pending patches.
7534fa5e
DN
827 ("P " . not-found)
828 ("P. " . not-found)
f8381803 829 ))
7ee8e7eb
DN
830 (translated nil)
831 (result nil))
832 (goto-char (point-min))
833 (while (not (eobp))
834 (setq status-str
701d018c 835 (buffer-substring-no-properties (point) (+ (point) 3)))
21f7bc38 836 (setq translated (cdr (assoc status-str translation)))
a0c38937
DN
837 (cond
838 ((eq translated 'conflict)
839 ;; For conflicts the file appears twice in the listing: once
840 ;; with the M flag and once with the C flag, so take care
841 ;; not to add it twice to `result'. Ugly.
842 (let* ((file
843 (buffer-substring-no-properties
844 ;;For files with conflicts the format is:
845 ;;C Text conflict in FILENAME
846 ;; Bah.
847 (+ (point) 21) (line-end-position)))
848 (entry (assoc file result)))
849 (when entry
850 (setf (nth 1 entry) 'conflict))))
851 ((eq translated 'renamed)
b9236874 852 (re-search-forward "R[ M] \\(.*\\) => \\(.*\\)$" (line-end-position) t)
460f6e7c
DN
853 (let ((new-name (file-relative-name (match-string 2) relative-dir))
854 (old-name (file-relative-name (match-string 1) relative-dir)))
a0c38937
DN
855 (push (list new-name 'edited
856 (vc-bzr-create-extra-fileinfo old-name)) result)))
11b4001c
DN
857 ;; do nothing for non existent files
858 ((eq translated 'not-found))
a0c38937 859 (t
460f6e7c
DN
860 (push (list (file-relative-name
861 (buffer-substring-no-properties
862 (+ (point) 4)
863 (line-end-position)) relative-dir)
a0c38937 864 translated) result)))
7ee8e7eb 865 (forward-line))
c1b51374 866 (funcall update-function result)))
7ee8e7eb 867
c1b51374 868(defun vc-bzr-dir-status (dir update-function)
7ee8e7eb 869 "Return a list of conses (file . state) for DIR."
115c0061
DN
870 (vc-bzr-command "status" (current-buffer) 'async dir "-v" "-S")
871 (vc-exec-after
460f6e7c
DN
872 `(vc-bzr-after-dir-status (quote ,update-function)
873 ;; "bzr status" results are relative to
874 ;; the bzr root directory, NOT to the
875 ;; directory "bzr status" was invoked in.
876 ;; Ugh.
877 ;; We pass the relative directory here so
878 ;; that `vc-bzr-after-dir-status' can
879 ;; frob the results accordingly.
880 (file-relative-name ,dir (vc-bzr-root ,dir)))))
7ee8e7eb 881
11b4001c
DN
882(defun vc-bzr-dir-status-files (dir files default-state update-function)
883 "Return a list of conses (file . state) for DIR."
884 (apply 'vc-bzr-command "status" (current-buffer) 'async dir "-v" "-S" files)
885 (vc-exec-after
460f6e7c
DN
886 `(vc-bzr-after-dir-status (quote ,update-function)
887 (file-relative-name ,dir (vc-bzr-root ,dir)))))
da4d4066 888
4dfb3b9c
DN
889(defvar vc-bzr-shelve-map
890 (let ((map (make-sparse-keymap)))
891 ;; Turn off vc-dir marking
892 (define-key map [mouse-2] 'ignore)
893
894 (define-key map [down-mouse-3] 'vc-bzr-shelve-menu)
895 (define-key map "\C-k" 'vc-bzr-shelve-delete-at-point)
855a2294
DN
896 (define-key map "=" 'vc-bzr-shelve-show-at-point)
897 (define-key map "\C-m" 'vc-bzr-shelve-show-at-point)
898 (define-key map "A" 'vc-bzr-shelve-apply-and-keep-at-point)
8e7e2286 899 (define-key map "P" 'vc-bzr-shelve-apply-at-point)
855a2294 900 (define-key map "S" 'vc-bzr-shelve-snapshot)
4dfb3b9c
DN
901 map))
902
903(defvar vc-bzr-shelve-menu-map
904 (let ((map (make-sparse-keymap "Bzr Shelve")))
905 (define-key map [de]
906 '(menu-item "Delete shelf" vc-bzr-shelve-delete-at-point
907 :help "Delete the current shelf"))
855a2294
DN
908 (define-key map [ap]
909 '(menu-item "Apply and keep shelf" vc-bzr-shelve-apply-and-keep-at-point
910 :help "Apply the current shelf and keep it"))
8e7e2286
DN
911 (define-key map [po]
912 '(menu-item "Apply and remove shelf (pop)" vc-bzr-shelve-apply-at-point
913 :help "Apply the current shelf and remove it"))
855a2294
DN
914 (define-key map [sh]
915 '(menu-item "Show shelve" vc-bzr-shelve-show-at-point
916 :help "Show the contents of the current shelve"))
4dfb3b9c
DN
917 map))
918
919(defvar vc-bzr-extra-menu-map
920 (let ((map (make-sparse-keymap)))
855a2294
DN
921 (define-key map [bzr-sn]
922 '(menu-item "Shelve a snapshot" vc-bzr-shelve-snapshot
923 :help "Shelve the current state of the tree and keep the current state"))
4dfb3b9c
DN
924 (define-key map [bzr-sh]
925 '(menu-item "Shelve..." vc-bzr-shelve
926 :help "Shelve changes"))
927 map))
928
929(defun vc-bzr-extra-menu () vc-bzr-extra-menu-map)
930
931(defun vc-bzr-extra-status-menu () vc-bzr-extra-menu-map)
932
da4d4066 933(defun vc-bzr-dir-extra-headers (dir)
be14a425
DN
934 (let*
935 ((str (with-temp-buffer
936 (vc-bzr-command "info" t 0 dir)
937 (buffer-string)))
4dfb3b9c
DN
938 (shelve (vc-bzr-shelve-list))
939 (shelve-help-echo "Use M-x vc-bzr-shelve to create shelves")
7a444e2a
DN
940 (root-dir (vc-bzr-root dir))
941 (pending-merge
4775ecad
DN
942 ;; FIXME: looking for .bzr/checkout/merge-hashes is not a
943 ;; reliable method to detect pending merges, disable this
944 ;; until a proper solution is implemented.
945 (and nil
946 (file-exists-p
947 (expand-file-name ".bzr/checkout/merge-hashes" root-dir))))
7a444e2a
DN
948 (pending-merge-help-echo
949 (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
950 (light-checkout
951 (when (string-match ".+light checkout root: \\(.+\\)$" str)
952 (match-string 1 str)))
953 (light-checkout-branch
954 (when light-checkout
955 (when (string-match ".+checkout of branch: \\(.+\\)$" str)
956 (match-string 1 str)))))
da4d4066 957 (concat
be14a425
DN
958 (propertize "Parent branch : " 'face 'font-lock-type-face)
959 (propertize
da4d4066 960 (if (string-match "parent branch: \\(.+\\)$" str)
be14a425
DN
961 (match-string 1 str)
962 "None")
963 'face 'font-lock-variable-name-face)
964 "\n"
965 (when light-checkout
966 (concat
967 (propertize "Light checkout root: " 'face 'font-lock-type-face)
968 (propertize light-checkout 'face 'font-lock-variable-name-face)
969 "\n"))
970 (when light-checkout-branch
971 (concat
972 (propertize "Checkout of branch : " 'face 'font-lock-type-face)
973 (propertize light-checkout-branch 'face 'font-lock-variable-name-face)
a3abb176 974 "\n"))
7a444e2a
DN
975 (when pending-merge
976 (concat
977 (propertize "Warning : " 'face 'font-lock-warning-face
978 'help-echo pending-merge-help-echo)
979 (propertize "Pending merges, commit recommended before any other action"
980 'help-echo pending-merge-help-echo
981 'face 'font-lock-warning-face)
982 "\n"))
983 (if shelve
984 (concat
985 (propertize "Shelves :\n" 'face 'font-lock-type-face
986 'help-echo shelve-help-echo)
987 (mapconcat
988 (lambda (x)
989 (propertize x
990 'face 'font-lock-variable-name-face
991 'mouse-face 'highlight
992 '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"
993 'keymap vc-bzr-shelve-map))
994 shelve "\n"))
995 (concat
996 (propertize "Shelves : " 'face 'font-lock-type-face
997 'help-echo shelve-help-echo)
998 (propertize "No shelved changes"
999 'help-echo shelve-help-echo
1000 'face 'font-lock-variable-name-face))))))
4dfb3b9c
DN
1001
1002(defun vc-bzr-shelve (name)
1003 "Create a shelve."
1004 (interactive "sShelf name: ")
1005 (let ((root (vc-bzr-root default-directory)))
1006 (when root
1007 (vc-bzr-command "shelve" nil 0 nil "--all" "-m" name)
1008 (vc-resynch-buffer root t t))))
1009
855a2294
DN
1010(defun vc-bzr-shelve-show (name)
1011 "Show the contents of shelve NAME."
1012 (interactive "sShelve name: ")
c80fa13c 1013 (vc-setup-buffer "*vc-diff*")
855a2294 1014 ;; FIXME: how can you show the contents of a shelf?
c80fa13c
SM
1015 (vc-bzr-command "unshelve" "*vc-diff*" 'async nil "--preview" name)
1016 (set-buffer "*vc-diff*")
855a2294
DN
1017 (diff-mode)
1018 (setq buffer-read-only t)
1019 (pop-to-buffer (current-buffer)))
4dfb3b9c
DN
1020
1021(defun vc-bzr-shelve-apply (name)
8e7e2286
DN
1022 "Apply shelve NAME and remove it afterwards."
1023 (interactive "sApply (and remove) shelf: ")
c80fa13c 1024 (vc-bzr-command "unshelve" nil 0 nil "--apply" name)
4dfb3b9c
DN
1025 (vc-resynch-buffer (vc-bzr-root default-directory) t t))
1026
855a2294
DN
1027(defun vc-bzr-shelve-apply-and-keep (name)
1028 "Apply shelve NAME and keep it afterwards."
1029 (interactive "sApply (and keep) shelf: ")
c80fa13c 1030 (vc-bzr-command "unshelve" nil 0 nil "--apply" "--keep" name)
855a2294
DN
1031 (vc-resynch-buffer (vc-bzr-root default-directory) t t))
1032
1033(defun vc-bzr-shelve-snapshot ()
1034 "Create a stash with the current tree state."
1035 (interactive)
1036 (vc-bzr-command "shelve" nil 0 nil "--all" "-m"
1037 (let ((ct (current-time)))
1038 (concat
1039 (format-time-string "Snapshot on %Y-%m-%d" ct)
1040 (format-time-string " at %H:%M" ct))))
c80fa13c 1041 (vc-bzr-command "unshelve" nil 0 nil "--apply" "--keep")
855a2294
DN
1042 (vc-resynch-buffer (vc-bzr-root default-directory) t t))
1043
4dfb3b9c
DN
1044(defun vc-bzr-shelve-list ()
1045 (with-temp-buffer
1046 (vc-bzr-command "shelve" (current-buffer) 1 nil "--list" "-q")
1047 (delete
1048 ""
1049 (split-string
1050 (buffer-substring (point-min) (point-max))
1051 "\n"))))
1052
1053(defun vc-bzr-shelve-get-at-point (point)
1054 (save-excursion
1055 (goto-char point)
1056 (beginning-of-line)
1057 (if (looking-at "^ +\\([0-9]+\\):")
1058 (match-string 1)
1059 (error "Cannot find shelf at point"))))
1060
1061(defun vc-bzr-shelve-delete-at-point ()
1062 (interactive)
1063 (let ((shelve (vc-bzr-shelve-get-at-point (point))))
2b404597 1064 (when (y-or-n-p (format "Remove shelf %s ? " shelve))
4dfb3b9c
DN
1065 (vc-bzr-command "unshelve" nil 0 nil "--delete-only" shelve)
1066 (vc-dir-refresh))))
1067
855a2294
DN
1068(defun vc-bzr-shelve-show-at-point ()
1069 (interactive)
1070 (vc-bzr-shelve-show (vc-bzr-shelve-get-at-point (point))))
4dfb3b9c
DN
1071
1072(defun vc-bzr-shelve-apply-at-point ()
1073 (interactive)
1074 (vc-bzr-shelve-apply (vc-bzr-shelve-get-at-point (point))))
1075
855a2294
DN
1076(defun vc-bzr-shelve-apply-and-keep-at-point ()
1077 (interactive)
1078 (vc-bzr-shelve-apply-and-keep (vc-bzr-shelve-get-at-point (point))))
1079
4dfb3b9c
DN
1080(defun vc-bzr-shelve-menu (e)
1081 (interactive "e")
1082 (vc-dir-at-event e (popup-menu vc-bzr-shelve-menu-map e)))
da4d4066 1083
d9de6d6f
MH
1084(defun vc-bzr-revision-table (files)
1085 (let ((vc-bzr-revisions '())
1086 (default-directory (file-name-directory (car files))))
1087 (with-temp-buffer
1088 (vc-bzr-command "log" t 0 files "--line")
1089 (let ((start (point-min))
1090 (loglines (buffer-substring-no-properties (point-min) (point-max))))
1091 (while (string-match "^\\([0-9]+\\):" loglines)
1092 (push (match-string 1 loglines) vc-bzr-revisions)
1093 (setq start (+ start (match-end 0)))
1094 (setq loglines (buffer-substring-no-properties start (point-max))))))
1095 vc-bzr-revisions))
1096
e97a42c1
SM
1097(defun vc-bzr-conflicted-files (dir)
1098 (let ((default-directory (vc-bzr-root dir))
1099 (files ()))
1100 (with-temp-buffer
1101 (vc-bzr-command "status" t 0 default-directory)
1102 (goto-char (point-min))
1103 (when (re-search-forward "^conflicts:\n" nil t)
1104 (while (looking-at " \\(?:Text conflict in \\(.*\\)\\|.*\\)\n")
1105 (if (match-end 1)
1106 (push (expand-file-name (match-string 1)) files))
1107 (goto-char (match-end 0)))))
1108 files))
1109
39f44442
SM
1110;;; Revision completion
1111
00f71f39
SM
1112(eval-and-compile
1113 (defconst vc-bzr-revision-keywords
1114 '("revno" "revid" "last" "before"
1115 "tag" "date" "ancestor" "branch" "submit")))
1116
39f44442
SM
1117(defun vc-bzr-revision-completion-table (files)
1118 (lexical-let ((files files))
1119 ;; What about using `files'?!? --Stef
1120 (lambda (string pred action)
1121 (cond
1122 ((string-match "\\`\\(ancestor\\|branch\\|\\(revno:\\)?[-0-9]+:\\):"
1123 string)
ec50e665 1124 (completion-table-with-context (substring string 0 (match-end 0))
feceda26
SM
1125 (apply-partially
1126 'completion-table-with-predicate
1127 'completion-file-name-table
1128 'file-directory-p t)
ec50e665 1129 (substring string (match-end 0))
feceda26 1130 pred
ec50e665 1131 action))
39f44442 1132 ((string-match "\\`\\(before\\):" string)
ec50e665
SM
1133 (completion-table-with-context (substring string 0 (match-end 0))
1134 (vc-bzr-revision-completion-table files)
1135 (substring string (match-end 0))
1136 pred
1137 action))
39f44442
SM
1138 ((string-match "\\`\\(tag\\):" string)
1139 (let ((prefix (substring string 0 (match-end 0)))
1140 (tag (substring string (match-end 0)))
e82b1099
MA
1141 (table nil)
1142 process-file-side-effects)
39f44442
SM
1143 (with-temp-buffer
1144 ;; "bzr-1.2 tags" is much faster with --show-ids.
07c4b87c 1145 (process-file vc-bzr-program nil '(t) nil "tags" "--show-ids")
39f44442
SM
1146 ;; The output is ambiguous, unless we assume that revids do not
1147 ;; contain spaces.
1148 (goto-char (point-min))
1149 (while (re-search-forward "^\\(.*[^ \n]\\) +[^ \n]*$" nil t)
1150 (push (match-string-no-properties 1) table)))
ec50e665 1151 (completion-table-with-context prefix table tag pred action)))
39f44442 1152
00f71f39
SM
1153 ((string-match "\\`\\([a-z]+\\):" string)
1154 ;; no actual completion for the remaining keywords.
1155 (completion-table-with-context (substring string 0 (match-end 0))
1156 (if (member (match-string 1 string)
1157 vc-bzr-revision-keywords)
1158 ;; If it's a valid keyword,
1159 ;; use a non-empty table to
1160 ;; indicate it.
1161 '("") nil)
1162 (substring string (match-end 0))
1163 pred
1164 action))
39f44442 1165 (t
f8381803
SM
1166 ;; Could use completion-table-with-terminator, except that it
1167 ;; currently doesn't work right w.r.t pcm and doesn't give
1168 ;; the *Completions* output we want.
00f71f39
SM
1169 (complete-with-action action (eval-when-compile
1170 (mapcar (lambda (s) (concat s ":"))
1171 vc-bzr-revision-keywords))
39f44442
SM
1172 string pred))))))
1173
b6e0e86c 1174(provide 'vc-bzr)
2b404597 1175
b6e0e86c 1176;;; vc-bzr.el ends here