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