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