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