aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorEric S. Raymond2014-12-01 14:51:03 -0500
committerEric S. Raymond2014-12-01 14:51:26 -0500
commit808699f13673a881949ab94d3c0e87f5ba9cd4cf (patch)
tree9cd17372e8eb75cdeb40372548042aeee521abf2 /lisp
parent6d80f26a9ae8e43973dbc946a9c070192fd5c431 (diff)
downloademacs-808699f13673a881949ab94d3c0e87f5ba9cd4cf.tar.gz
emacs-808699f13673a881949ab94d3c0e87f5ba9cd4cf.zip
bzr-state randomly/unpredictably fails on non-bzr files.
Diffstat (limited to 'lisp')
-rw-r--r--lisp/vc/vc-bzr.el109
1 files changed, 108 insertions, 1 deletions
diff --git a/lisp/vc/vc-bzr.el b/lisp/vc/vc-bzr.el
index 34a7c7be786..7f30378227f 100644
--- a/lisp/vc/vc-bzr.el
+++ b/lisp/vc/vc-bzr.el
@@ -178,6 +178,113 @@ in the repository root directory of FILE."
178 (insert-file-contents-literally file) 178 (insert-file-contents-literally file)
179 (sha1 (current-buffer)))) 179 (sha1 (current-buffer))))
180 180
181(defun vc-bzr-state-heuristic (file)
182 "Like `vc-bzr-state' but hopefully without running Bzr."
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 ;;
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'."
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
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))))))
287
181;; This is a cheap approximation that is autoloaded. If it finds a 288;; This is a cheap approximation that is autoloaded. If it finds a
182;; possible match it loads this file and runs the real function. 289;; possible match it loads this file and runs the real function.
183;; It requires vc-bzr-admin-checkout-format-file to be autoloaded too. 290;; It requires vc-bzr-admin-checkout-format-file to be autoloaded too.
@@ -189,7 +296,7 @@ in the repository root directory of FILE."
189 296
190(defun vc-bzr-registered (file) 297(defun vc-bzr-registered (file)
191 "Return non-nil if FILE is registered with bzr." 298 "Return non-nil if FILE is registered with bzr."
192 (let ((state (vc-bzr-state file))) 299 (let ((state (vc-bzr-state-heuristic file)))
193 (not (memq state '(nil unregistered ignored))))) 300 (not (memq state '(nil unregistered ignored)))))
194 301
195(defconst vc-bzr-state-words 302(defconst vc-bzr-state-words