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