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