##// END OF EJS Templates
Merge with BOS
mpm@selenic.com -
r948:ffb06650 merge default
parent child Browse files
Show More
This diff has been collapsed as it changes many lines, (590 lines changed) Show them Hide them
@@ -0,0 +1,590 b''
1 ;;; mercurial.el --- Emacs support for the Mercurial distributed SCM
2
3 ;; Copyright (C) 2005 Bryan O'Sullivan
4
5 ;; Author: Bryan O'Sullivan <bos@serpentine.com>
6
7 ;; $Id$
8
9 ;; mercurial.el is free software; you can redistribute it and/or
10 ;; modify it under the terms of version 2 of the GNU General Public
11 ;; License as published by the Free Software Foundation.
12
13 ;; mercurial.el is distributed in the hope that it will be useful, but
14 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 ;; General Public License for more details.
17
18 ;; You should have received a copy of the GNU General Public License
19 ;; along with mercurial.el, GNU Emacs, or XEmacs; see the file COPYING
20 ;; (`C-h C-l'). If not, write to the Free Software Foundation, Inc.,
21 ;; 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22
23 ;;; Commentary:
24
25 ;; This mode builds upon Emacs's VC mode to provide flexible
26 ;; integration with the Mercurial distributed SCM tool.
27
28 ;; To get going as quickly as possible, load mercurial.el into Emacs and
29 ;; type `C-c h h'; this runs hg-help-overview, which prints a helpful
30 ;; usage overview.
31
32 ;; Much of the inspiration for mercurial.el comes from Rajesh
33 ;; Vaidheeswarran's excellent p4.el, which does an admirably thorough
34 ;; job for the commercial Perforce SCM product. In fact, substantial
35 ;; chunks of code are adapted from p4.el.
36
37 ;; This code has been developed under XEmacs 21.5, and may will not
38 ;; work as well under GNU Emacs (albeit tested under 21.2). Patches
39 ;; to enhance the portability of this code, fix bugs, and add features
40 ;; are most welcome. You can clone a Mercurial repository for this
41 ;; package from http://www.serpentine.com/hg/hg-emacs
42
43 ;; Please send problem reports and suggestions to bos@serpentine.com.
44
45
46 ;;; Code:
47
48 (require 'advice)
49 (require 'cl)
50 (require 'diff-mode)
51 (require 'easymenu)
52 (require 'vc)
53
54
55 ;;; XEmacs has view-less, while GNU Emacs has view. Joy.
56
57 (condition-case nil
58 (require 'view-less)
59 (error nil))
60 (condition-case nil
61 (require 'view)
62 (error nil))
63
64
65 ;;; Variables accessible through the custom system.
66
67 (defgroup mercurial nil
68 "Mercurial distributed SCM."
69 :group 'tools)
70
71 (defcustom hg-binary
72 (dolist (path '("~/bin/hg"
73 "/usr/bin/hg"
74 "/usr/local/bin/hg"))
75 (when (file-executable-p path)
76 (return path)))
77 "The path to Mercurial's hg executable."
78 :type '(file :must-match t)
79 :group 'mercurial)
80
81 (defcustom hg-mode-hook nil
82 "Hook run when a buffer enters hg-mode."
83 :type 'sexp
84 :group 'mercurial)
85
86 (defcustom hg-global-prefix "\C-ch"
87 "The global prefix for Mercurial keymap bindings."
88 :type 'sexp
89 :group 'mercurial)
90
91 (defcustom hg-rev-completion-limit 100
92 "The maximum number of revisions that hg-read-rev will offer to complete.
93 This affects memory usage and performance when prompting for revisions
94 in a repository with a lot of history."
95 :type 'integer
96 :group 'mercurial)
97
98 (defcustom hg-log-limit 50
99 "The maximum number of revisions that hg-log will display."
100 :type 'integer
101 :group 'mercurial)
102
103
104 ;;; Other variables.
105
106 (defconst hg-running-xemacs (string-match "XEmacs" emacs-version)
107 "Is mercurial.el running under XEmacs?")
108
109 (defvar hg-mode nil
110 "Is this file managed by Mercurial?")
111 (make-variable-buffer-local 'hg-mode)
112 (put 'hg-mode 'permanent-local t)
113
114 (defvar hg-status nil)
115 (make-variable-buffer-local 'hg-status)
116 (put 'hg-status 'permanent-local t)
117
118 (defvar hg-output-buffer-name "*Hg*"
119 "The name to use for Mercurial output buffers.")
120
121 (defvar hg-file-history nil)
122 (defvar hg-rev-history nil)
123
124
125 ;;; hg-mode keymap.
126
127 (defvar hg-prefix-map
128 (let ((map (copy-keymap vc-prefix-map)))
129 (set-keymap-name map 'hg-prefix-map)
130 map)
131 "This keymap overrides some default vc-mode bindings.")
132 (fset 'hg-prefix-map hg-prefix-map)
133 (define-key hg-prefix-map "=" 'hg-diff)
134 (define-key hg-prefix-map "c" 'hg-undo)
135 (define-key hg-prefix-map "g" 'hg-annotate)
136 (define-key hg-prefix-map "l" 'hg-log)
137 (define-key hg-prefix-map "n" 'hg-commit-file)
138 ;; (define-key hg-prefix-map "r" 'hg-update)
139 (define-key hg-prefix-map "u" 'hg-revert-file)
140 (define-key hg-prefix-map "~" 'hg-version-other-window)
141
142 (defvar hg-mode-map (make-sparse-keymap))
143 (define-key hg-mode-map "\C-xv" 'hg-prefix-map)
144
145 (add-minor-mode 'hg-mode 'hg-mode hg-mode-map)
146
147
148 ;;; Global keymap.
149
150 (global-set-key "\C-xvi" 'hg-add)
151
152 (defvar hg-global-map (make-sparse-keymap))
153 (fset 'hg-global-map hg-global-map)
154 (global-set-key hg-global-prefix 'hg-global-map)
155 (define-key hg-global-map "," 'hg-incoming)
156 (define-key hg-global-map "." 'hg-outgoing)
157 (define-key hg-global-map "<" 'hg-pull)
158 (define-key hg-global-map "=" 'hg-diff)
159 (define-key hg-global-map ">" 'hg-push)
160 (define-key hg-global-map "?" 'hg-help-overview)
161 (define-key hg-global-map "A" 'hg-addremove)
162 (define-key hg-global-map "U" 'hg-revert)
163 (define-key hg-global-map "a" 'hg-add)
164 (define-key hg-global-map "c" 'hg-commit)
165 (define-key hg-global-map "f" 'hg-forget)
166 (define-key hg-global-map "h" 'hg-help-overview)
167 (define-key hg-global-map "i" 'hg-init)
168 (define-key hg-global-map "l" 'hg-log)
169 (define-key hg-global-map "r" 'hg-root)
170 (define-key hg-global-map "s" 'hg-status)
171 (define-key hg-global-map "u" 'hg-update)
172
173
174 ;;; View mode keymap.
175
176 (defvar hg-view-mode-map
177 (let ((map (copy-keymap (if (boundp 'view-minor-mode-map)
178 view-minor-mode-map
179 view-mode-map))))
180 (set-keymap-name map 'hg-view-mode-map)
181 map))
182 (fset 'hg-view-mode-map hg-view-mode-map)
183 (define-key hg-view-mode-map
184 (if hg-running-xemacs [button2] [mouse-2])
185 'hg-buffer-mouse-clicked)
186
187
188 ;;; Convenience functions.
189
190 (defun hg-binary ()
191 (if hg-binary
192 hg-binary
193 (error "No `hg' executable found!")))
194
195 (defun hg-replace-in-string (str regexp newtext &optional literal)
196 "Replace all matches in STR for REGEXP with NEWTEXT string.
197 Return the new string. Optional LITERAL non-nil means do a literal
198 replacement.
199
200 This function bridges yet another pointless impedance gap between
201 XEmacs and GNU Emacs."
202 (if (fboundp 'replace-in-string)
203 (replace-in-string str regexp newtext literal)
204 (replace-regexp-in-string regexp newtext str nil literal)))
205
206 (defun hg-chomp (str)
207 "Strip trailing newlines from a string."
208 (hg-replace-in-string str "[\r\n]+$" ""))
209
210 (defun hg-run-command (command &rest args)
211 "Run the shell command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT).
212 The list ARGS contains a list of arguments to pass to the command."
213 (let* (exit-code
214 (output
215 (with-output-to-string
216 (with-current-buffer
217 standard-output
218 (setq exit-code
219 (apply 'call-process command nil t nil args))))))
220 (cons exit-code output)))
221
222 (defun hg-run (command &rest args)
223 "Run the Mercurial command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT)."
224 (apply 'hg-run-command (hg-binary) command args))
225
226 (defun hg-run0 (command &rest args)
227 "Run the Mercurial command COMMAND, returning its output.
228 If the command does not exit with a zero status code, raise an error."
229 (let ((res (apply 'hg-run-command (hg-binary) command args)))
230 (if (not (eq (car res) 0))
231 (error "Mercurial command failed %s - exit code %s"
232 (cons command args)
233 (car res))
234 (cdr res))))
235
236 (defun hg-buffer-commands (pnt)
237 "Use the properties of a character to do something sensible."
238 (interactive "d")
239 (let ((rev (get-char-property pnt 'rev))
240 (file (get-char-property pnt 'file))
241 (date (get-char-property pnt 'date))
242 (user (get-char-property pnt 'user))
243 (host (get-char-property pnt 'host))
244 (prev-buf (current-buffer)))
245 (cond
246 (file
247 (find-file-other-window file))
248 (rev
249 (hg-diff hg-view-file-name rev rev prev-buf))
250 ((message "I don't know how to do that yet")))))
251
252 (defun hg-buffer-mouse-clicked (event)
253 "Translate the mouse clicks in a HG log buffer to character events.
254 These are then handed off to `hg-buffer-commands'.
255
256 Handle frickin' frackin' gratuitous event-related incompatibilities."
257 (interactive "e")
258 (if hg-running-xemacs
259 (progn
260 (select-window (event-window event))
261 (hg-buffer-commands (event-point event)))
262 (select-window (posn-window (event-end event)))
263 (hg-buffer-commands (posn-point (event-start event)))))
264
265 (unless (fboundp 'view-minor-mode)
266 (defun view-minor-mode (prev-buffer exit-func)
267 (view-mode)))
268
269 (defun hg-abbrev-file-name (file)
270 (if hg-running-xemacs
271 (abbreviate-file-name file t)
272 (abbreviate-file-name file)))
273
274 (defun hg-read-file-name (&optional prompt default)
275 "Read a file or directory name, or a pattern, to use with a command."
276 (let ((path (or default (buffer-file-name))))
277 (if (or (not path) current-prefix-arg)
278 (expand-file-name
279 (read-file-name (format "File, directory or pattern%s: "
280 (or prompt ""))
281 (and path (file-name-directory path))
282 nil nil
283 (and path (file-name-nondirectory path))
284 'hg-file-history))
285 path)))
286
287 (defun hg-read-rev (&optional prompt default)
288 "Read a revision or tag, offering completions."
289 (let ((rev (or default "tip")))
290 (if (or (not rev) current-prefix-arg)
291 (let ((revs (split-string (hg-chomp
292 (hg-run0 "-q" "log" "-r"
293 (format "-%d"
294 hg-rev-completion-limit)
295 "-r" "tip"))
296 "[\n:]")))
297 (dolist (line (split-string (hg-chomp (hg-run0 "tags")) "\n"))
298 (setq revs (cons (car (split-string line "\\s-")) revs)))
299 (completing-read (format "Revision%s (%s): "
300 (or prompt "")
301 (or default "tip"))
302 (map 'list 'cons revs revs)
303 nil
304 nil
305 nil
306 'hg-rev-history
307 (or default "tip")))
308 rev)))
309
310 ;;; View mode bits.
311
312 (defun hg-exit-view-mode (buf)
313 "Exit from hg-view-mode.
314 We delete the current window if entering hg-view-mode split the
315 current frame."
316 (when (and (eq buf (current-buffer))
317 (> (length (window-list)) 1))
318 (delete-window))
319 (when (buffer-live-p buf)
320 (kill-buffer buf)))
321
322 (defun hg-view-mode (prev-buffer &optional file-name)
323 (goto-char (point-min))
324 (set-buffer-modified-p nil)
325 (toggle-read-only t)
326 (view-minor-mode prev-buffer 'hg-exit-view-mode)
327 (use-local-map hg-view-mode-map)
328 (setq truncate-lines t)
329 (when file-name
330 (set (make-local-variable 'hg-view-file-name)
331 (hg-abbrev-file-name file-name))))
332
333 (defun hg-file-status (file)
334 "Return status of FILE, or nil if FILE does not exist or is unmanaged."
335 (let* ((s (hg-run "status" file))
336 (exit (car s))
337 (output (cdr s)))
338 (if (= exit 0)
339 (let ((state (assoc (substring output 0 (min (length output) 2))
340 '(("M " . modified)
341 ("A " . added)
342 ("R " . removed)))))
343 (if state
344 (cdr state)
345 'normal)))))
346
347 (defun hg-tip ()
348 (split-string (hg-chomp (hg-run0 "-q" "tip")) ":"))
349
350 (defmacro hg-view-output (args &rest body)
351 "Execute BODY in a clean buffer, then quickly display that buffer.
352 If the buffer contains one line, its contents are displayed in the
353 minibuffer. Otherwise, the buffer is displayed in view-mode.
354 ARGS is of the form (BUFFER-NAME &optional FILE), where BUFFER-NAME is
355 the name of the buffer to create, and FILE is the name of the file
356 being viewed."
357 (let ((prev-buf (gensym "prev-buf-"))
358 (v-b-name (car args))
359 (v-m-rest (cdr args)))
360 `(let ((view-buf-name ,v-b-name)
361 (,prev-buf (current-buffer)))
362 (get-buffer-create view-buf-name)
363 (kill-buffer view-buf-name)
364 (get-buffer-create view-buf-name)
365 (set-buffer view-buf-name)
366 (save-excursion
367 ,@body)
368 (case (count-lines (point-min) (point-max))
369 ((0)
370 (kill-buffer view-buf-name)
371 (message "(No output)"))
372 ((1)
373 (let ((msg (hg-chomp (buffer-substring (point-min) (point-max)))))
374 (kill-buffer view-buf-name)
375 (message "%s" msg)))
376 (t
377 (pop-to-buffer view-buf-name)
378 (hg-view-mode ,prev-buf ,@v-m-rest))))))
379
380 (put 'hg-view-output 'lisp-indent-function 1)
381
382 ;;; Hooks.
383
384 (defun hg-mode-line ()
385 (when (hg-root)
386 (let ((status (hg-file-status buffer-file-name)))
387 (setq hg-status status
388 hg-mode (and status (concat " Hg:"
389 (car (hg-tip))
390 (cdr (assq status
391 '((normal . "")
392 (removed . "r")
393 (added . "a")
394 (modified . "m")))))))
395 status)))
396
397 (defun hg-find-file-hook ()
398 (when (hg-mode-line)
399 (run-hooks 'hg-mode-hook)))
400
401 (add-hook 'find-file-hooks 'hg-find-file-hook)
402
403 (defun hg-after-save-hook ()
404 (let ((old-status hg-status))
405 (hg-mode-line)
406 (if (and (not old-status) hg-status)
407 (run-hooks 'hg-mode-hook))))
408
409 (add-hook 'after-save-hook 'hg-after-save-hook)
410
411
412 ;;; User interface functions.
413
414 (defun hg-help-overview ()
415 "This is an overview of the Mercurial SCM mode for Emacs.
416
417 You can find the source code, license (GPL v2), and credits for this
418 code by typing `M-x find-library mercurial RET'.
419
420 The Mercurial mode user interface is based on that of the older VC
421 mode, so if you're already familiar with VC, the same keybindings and
422 functions will generally work.
423
424 Below is a list of common SCM tasks, with the key bindings needed to
425 perform them, and the command names. This list is not exhaustive.
426
427 In the list below, `G/L' indicates whether a key binding is global (G)
428 or local (L). Global keybindings work on any file inside a Mercurial
429 repository. Local keybindings only apply to files under the control
430 of Mercurial. Many commands take a prefix argument.
431
432
433 SCM Task G/L Key Binding Command Name
434 -------- --- ----------- ------------
435 Help overview (what you are reading) G C-c h h hg-help-overview
436
437 Tell Mercurial to manage a file G C-c h a hg-add
438 Commit changes to current file only L C-x v n hg-commit
439 Undo changes to file since commit L C-x v u hg-revert-file
440
441 Diff file vs last checkin L C-x v = hg-diff
442
443 View file change history L C-x v l hg-log
444 View annotated file L C-x v a hg-annotate
445
446 Diff repo vs last checkin G C-c h = hg-diff
447 View status of files in repo G C-c h s hg-status
448 Commit all changes G C-c h c hg-commit
449
450 Undo all changes since last commit G C-c h U hg-revert
451 View repo change history G C-c h l hg-log
452
453 See changes that can be pulled G C-c h , hg-incoming
454 Pull changes G C-c h < hg-pull
455 Update working directory after pull G C-c h u hg-update
456 See changes that can be pushed G C-c h . hg-outgoing
457 Push changes G C-c h > hg-push"
458 (interactive)
459 (hg-view-output ("Mercurial Help Overview")
460 (insert (documentation 'hg-help-overview))))
461
462 (defun hg-add (path)
463 (interactive (list (hg-read-file-name " to add")))
464 (let ((buf (current-buffer))
465 (update (equal buffer-file-name path)))
466 (hg-view-output (hg-output-buffer-name)
467 (apply 'call-process (hg-binary) nil t nil (list "add" path)))
468 (when update
469 (with-current-buffer buf
470 (hg-mode-line)))))
471
472 (defun hg-addremove ()
473 (interactive)
474 (error "not implemented"))
475
476 (defun hg-annotate ()
477 (interactive)
478 (error "not implemented"))
479
480 (defun hg-commit ()
481 (interactive)
482 (error "not implemented"))
483
484 (defun hg-diff (path &optional rev1 rev2)
485 (interactive (list (hg-read-file-name " to diff")
486 (hg-read-rev " to start with")
487 (let ((rev2 (hg-read-rev " to end with" 'working-dir)))
488 (and (not (eq rev2 'working-dir)) rev2))))
489 (let ((a-path (hg-abbrev-file-name path)))
490 (hg-view-output ((if (equal rev1 rev2)
491 (format "Mercurial: Rev %s of %s" rev1 a-path)
492 (format "Mercurial: Rev %s to %s of %s"
493 rev1 (or rev2 "Current") a-path)))
494 (if rev2
495 (call-process (hg-binary) nil t nil "diff" "-r" rev1 "-r" rev2 path)
496 (call-process (hg-binary) nil t nil "diff" "-r" rev1 path))
497 (diff-mode)
498 (font-lock-fontify-buffer))))
499
500 (defun hg-forget (path)
501 (interactive (list (hg-read-file-name " to forget")))
502 (let ((buf (current-buffer))
503 (update (equal buffer-file-name path)))
504 (hg-view-output (hg-output-buffer-name)
505 (apply 'call-process (hg-binary) nil t nil (list "forget" path)))
506 (when update
507 (with-current-buffer buf
508 (hg-mode-line)))))
509
510 (defun hg-incoming ()
511 (interactive)
512 (error "not implemented"))
513
514 (defun hg-init ()
515 (interactive)
516 (error "not implemented"))
517
518 (defun hg-log (path &optional rev1 rev2)
519 (interactive (list (hg-read-file-name " to log")
520 (hg-read-rev " to start with" "-1")
521 (hg-read-rev " to end with" (format "-%d" hg-log-limit))))
522 (message "log %s %s" rev1 rev2)
523 (sit-for 1)
524 (let ((a-path (hg-abbrev-file-name path)))
525 (hg-view-output ((if (equal rev1 rev2)
526 (format "Mercurial: Rev %s of %s" rev1 a-path)
527 (format "Mercurial: Rev %s to %s of %s"
528 rev1 (or rev2 "Current") a-path)))
529 (call-process (hg-binary) nil t nil "log" "-r" rev1 "-r" rev2 path)
530 (diff-mode)
531 (font-lock-fontify-buffer))))
532
533 (defun hg-outgoing ()
534 (interactive)
535 (error "not implemented"))
536
537 (defun hg-pull ()
538 (interactive)
539 (error "not implemented"))
540
541 (defun hg-push ()
542 (interactive)
543 (error "not implemented"))
544
545 (defun hg-revert ()
546 (interactive)
547 (error "not implemented"))
548
549 (defun hg-revert-file ()
550 (interactive)
551 (error "not implemented"))
552
553 (defun hg-root (&optional path)
554 (interactive (list (hg-read-file-name)))
555 (let ((root (do ((prev nil dir)
556 (dir (file-name-directory (or path (buffer-file-name)))
557 (file-name-directory (directory-file-name dir))))
558 ((equal prev dir))
559 (when (file-directory-p (concat dir ".hg"))
560 (return dir)))))
561 (when (interactive-p)
562 (if root
563 (message "The root of this repository is `%s'." root)
564 (message "The path `%s' is not in a Mercurial repository."
565 (abbreviate-file-name path t))))
566 root))
567
568 (defun hg-status (path)
569 (interactive (list (hg-read-file-name " for status" (hg-root))))
570 (let ((root (hg-root)))
571 (hg-view-output (hg-output-buffer-name)
572 (apply 'call-process (hg-binary) nil t nil
573 (list "-C" root "status" path)))))
574
575 (defun hg-undo ()
576 (interactive)
577 (error "not implemented"))
578
579 (defun hg-version-other-window ()
580 (interactive)
581 (error "not implemented"))
582
583
584 (provide 'mercurial)
585
586
587 ;;; Local Variables:
588 ;;; mode: emacs-lisp
589 ;;; prompt-to-byte-compile: nil
590 ;;; end:
@@ -1,15 +1,16 b''
1 \.elc$
1 \.orig$
2 \.orig$
2 \.rej$
3 \.rej$
3 ~$
4 ~$
4 \.so$
5 \.so$
5 \.pyc$
6 \.pyc$
6 \.swp$
7 \.swp$
7 \.prof$
8 \.prof$
8 ^tests/.*\.err$
9 ^tests/.*\.err$
9 ^build/
10 ^build/
10 ^dist/
11 ^dist/
11 ^doc/.*\.[0-9](\.(x|ht)ml)?$
12 ^doc/.*\.[0-9](\.(x|ht)ml)?$
12 ^MANIFEST$
13 ^MANIFEST$
13 ^\.pc/
14 ^\.pc/
14 ^patches/
15 ^patches/
15 ^mercurial/__version__.py$
16 ^mercurial/__version__.py$
@@ -1,1577 +1,1588 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from demandload import demandload
8 from demandload import demandload
9 demandload(globals(), "os re sys signal shutil")
9 demandload(globals(), "os re sys signal shutil")
10 demandload(globals(), "fancyopts ui hg util lock")
10 demandload(globals(), "fancyopts ui hg util lock")
11 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
11 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
12 demandload(globals(), "errno socket version struct atexit")
12 demandload(globals(), "errno socket version struct atexit")
13
13
14 class UnknownCommand(Exception):
14 class UnknownCommand(Exception):
15 """Exception raised if command is not in the command table."""
15 """Exception raised if command is not in the command table."""
16
16
17 def filterfiles(filters, files):
17 def filterfiles(filters, files):
18 l = [x for x in files if x in filters]
18 l = [x for x in files if x in filters]
19
19
20 for t in filters:
20 for t in filters:
21 if t and t[-1] != "/":
21 if t and t[-1] != "/":
22 t += "/"
22 t += "/"
23 l += [x for x in files if x.startswith(t)]
23 l += [x for x in files if x.startswith(t)]
24 return l
24 return l
25
25
26 def relfilter(repo, files):
26 def relfilter(repo, files):
27 cwd = repo.getcwd()
27 cwd = repo.getcwd()
28 if cwd:
28 if cwd:
29 return filterfiles([util.pconvert(cwd)], files)
29 return filterfiles([util.pconvert(cwd)], files)
30 return files
30 return files
31
31
32 def relpath(repo, args):
32 def relpath(repo, args):
33 cwd = repo.getcwd()
33 cwd = repo.getcwd()
34 if cwd:
34 if cwd:
35 return [util.normpath(os.path.join(cwd, x)) for x in args]
35 return [util.normpath(os.path.join(cwd, x)) for x in args]
36 return args
36 return args
37
37
38 def matchpats(repo, cwd, pats = [], opts = {}, head = ''):
38 def matchpats(repo, cwd, pats = [], opts = {}, head = ''):
39 return util.matcher(repo, cwd, pats or ['.'], opts.get('include'),
39 return util.matcher(repo, cwd, pats or ['.'], opts.get('include'),
40 opts.get('exclude'), head)
40 opts.get('exclude'), head)
41
41
42 def makewalk(repo, pats, opts, head = ''):
42 def makewalk(repo, pats, opts, head = ''):
43 cwd = repo.getcwd()
43 cwd = repo.getcwd()
44 files, matchfn = matchpats(repo, cwd, pats, opts, head)
44 files, matchfn = matchpats(repo, cwd, pats, opts, head)
45 exact = dict(zip(files, files))
45 def walk():
46 def walk():
46 for src, fn in repo.walk(files = files, match = matchfn):
47 for src, fn in repo.walk(files = files, match = matchfn):
47 yield src, fn, util.pathto(cwd, fn)
48 yield src, fn, util.pathto(cwd, fn), fn in exact
48 return files, matchfn, walk()
49 return files, matchfn, walk()
49
50
50 def walk(repo, pats, opts, head = ''):
51 def walk(repo, pats, opts, head = ''):
51 files, matchfn, results = makewalk(repo, pats, opts, head)
52 files, matchfn, results = makewalk(repo, pats, opts, head)
52 for r in results: yield r
53 for r in results: yield r
53
54
54 revrangesep = ':'
55 revrangesep = ':'
55
56
56 def revrange(ui, repo, revs, revlog=None):
57 def revrange(ui, repo, revs, revlog=None):
57 if revlog is None:
58 if revlog is None:
58 revlog = repo.changelog
59 revlog = repo.changelog
59 revcount = revlog.count()
60 revcount = revlog.count()
60 def fix(val, defval):
61 def fix(val, defval):
61 if not val:
62 if not val:
62 return defval
63 return defval
63 try:
64 try:
64 num = int(val)
65 num = int(val)
65 if str(num) != val:
66 if str(num) != val:
66 raise ValueError
67 raise ValueError
67 if num < 0:
68 if num < 0:
68 num += revcount
69 num += revcount
69 if not (0 <= num < revcount):
70 if not (0 <= num < revcount):
70 raise ValueError
71 raise ValueError
71 except ValueError:
72 except ValueError:
72 try:
73 try:
73 num = repo.changelog.rev(repo.lookup(val))
74 num = repo.changelog.rev(repo.lookup(val))
74 except KeyError:
75 except KeyError:
75 try:
76 try:
76 num = revlog.rev(revlog.lookup(val))
77 num = revlog.rev(revlog.lookup(val))
77 except KeyError:
78 except KeyError:
78 raise util.Abort('invalid revision identifier %s', val)
79 raise util.Abort('invalid revision identifier %s', val)
79 return num
80 return num
80 for spec in revs:
81 for spec in revs:
81 if spec.find(revrangesep) >= 0:
82 if spec.find(revrangesep) >= 0:
82 start, end = spec.split(revrangesep, 1)
83 start, end = spec.split(revrangesep, 1)
83 start = fix(start, 0)
84 start = fix(start, 0)
84 end = fix(end, revcount - 1)
85 end = fix(end, revcount - 1)
85 if end > start:
86 if end > start:
86 end += 1
87 end += 1
87 step = 1
88 step = 1
88 else:
89 else:
89 end -= 1
90 end -= 1
90 step = -1
91 step = -1
91 for rev in xrange(start, end, step):
92 for rev in xrange(start, end, step):
92 yield str(rev)
93 yield str(rev)
93 else:
94 else:
94 yield spec
95 yield spec
95
96
96 def make_filename(repo, r, pat, node=None,
97 def make_filename(repo, r, pat, node=None,
97 total=None, seqno=None, revwidth=None):
98 total=None, seqno=None, revwidth=None):
98 node_expander = {
99 node_expander = {
99 'H': lambda: hg.hex(node),
100 'H': lambda: hg.hex(node),
100 'R': lambda: str(r.rev(node)),
101 'R': lambda: str(r.rev(node)),
101 'h': lambda: hg.short(node),
102 'h': lambda: hg.short(node),
102 }
103 }
103 expander = {
104 expander = {
104 '%': lambda: '%',
105 '%': lambda: '%',
105 'b': lambda: os.path.basename(repo.root),
106 'b': lambda: os.path.basename(repo.root),
106 }
107 }
107
108
108 try:
109 try:
109 if node:
110 if node:
110 expander.update(node_expander)
111 expander.update(node_expander)
111 if node and revwidth is not None:
112 if node and revwidth is not None:
112 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
113 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
113 if total is not None:
114 if total is not None:
114 expander['N'] = lambda: str(total)
115 expander['N'] = lambda: str(total)
115 if seqno is not None:
116 if seqno is not None:
116 expander['n'] = lambda: str(seqno)
117 expander['n'] = lambda: str(seqno)
117 if total is not None and seqno is not None:
118 if total is not None and seqno is not None:
118 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
119 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
119
120
120 newname = []
121 newname = []
121 patlen = len(pat)
122 patlen = len(pat)
122 i = 0
123 i = 0
123 while i < patlen:
124 while i < patlen:
124 c = pat[i]
125 c = pat[i]
125 if c == '%':
126 if c == '%':
126 i += 1
127 i += 1
127 c = pat[i]
128 c = pat[i]
128 c = expander[c]()
129 c = expander[c]()
129 newname.append(c)
130 newname.append(c)
130 i += 1
131 i += 1
131 return ''.join(newname)
132 return ''.join(newname)
132 except KeyError, inst:
133 except KeyError, inst:
133 raise util.Abort("invalid format spec '%%%s' in output file name",
134 raise util.Abort("invalid format spec '%%%s' in output file name",
134 inst.args[0])
135 inst.args[0])
135
136
136 def make_file(repo, r, pat, node=None,
137 def make_file(repo, r, pat, node=None,
137 total=None, seqno=None, revwidth=None, mode='wb'):
138 total=None, seqno=None, revwidth=None, mode='wb'):
138 if not pat or pat == '-':
139 if not pat or pat == '-':
139 if 'w' in mode: return sys.stdout
140 if 'w' in mode: return sys.stdout
140 else: return sys.stdin
141 else: return sys.stdin
141 if hasattr(pat, 'write') and 'w' in mode:
142 if hasattr(pat, 'write') and 'w' in mode:
142 return pat
143 return pat
143 if hasattr(pat, 'read') and 'r' in mode:
144 if hasattr(pat, 'read') and 'r' in mode:
144 return pat
145 return pat
145 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
146 return open(make_filename(repo, r, pat, node, total, seqno, revwidth),
146 mode)
147 mode)
147
148
148 def dodiff(fp, ui, repo, files=None, node1=None, node2=None, match=util.always, changes=None):
149 def dodiff(fp, ui, repo, files=None, node1=None, node2=None, match=util.always, changes=None):
149 def date(c):
150 def date(c):
150 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
151 return time.asctime(time.gmtime(float(c[2].split(' ')[0])))
151
152
152 if not changes:
153 if not changes:
153 (c, a, d, u) = repo.changes(node1, node2, files, match = match)
154 (c, a, d, u) = repo.changes(node1, node2, files, match = match)
154 else:
155 else:
155 (c, a, d, u) = changes
156 (c, a, d, u) = changes
156 if files:
157 if files:
157 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
158 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
158
159
159 if not c and not a and not d:
160 if not c and not a and not d:
160 return
161 return
161
162
162 if node2:
163 if node2:
163 change = repo.changelog.read(node2)
164 change = repo.changelog.read(node2)
164 mmap2 = repo.manifest.read(change[0])
165 mmap2 = repo.manifest.read(change[0])
165 date2 = date(change)
166 date2 = date(change)
166 def read(f):
167 def read(f):
167 return repo.file(f).read(mmap2[f])
168 return repo.file(f).read(mmap2[f])
168 else:
169 else:
169 date2 = time.asctime()
170 date2 = time.asctime()
170 if not node1:
171 if not node1:
171 node1 = repo.dirstate.parents()[0]
172 node1 = repo.dirstate.parents()[0]
172 def read(f):
173 def read(f):
173 return repo.wfile(f).read()
174 return repo.wfile(f).read()
174
175
175 if ui.quiet:
176 if ui.quiet:
176 r = None
177 r = None
177 else:
178 else:
178 hexfunc = ui.verbose and hg.hex or hg.short
179 hexfunc = ui.verbose and hg.hex or hg.short
179 r = [hexfunc(node) for node in [node1, node2] if node]
180 r = [hexfunc(node) for node in [node1, node2] if node]
180
181
181 change = repo.changelog.read(node1)
182 change = repo.changelog.read(node1)
182 mmap = repo.manifest.read(change[0])
183 mmap = repo.manifest.read(change[0])
183 date1 = date(change)
184 date1 = date(change)
184
185
185 for f in c:
186 for f in c:
186 to = None
187 to = None
187 if f in mmap:
188 if f in mmap:
188 to = repo.file(f).read(mmap[f])
189 to = repo.file(f).read(mmap[f])
189 tn = read(f)
190 tn = read(f)
190 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
191 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
191 for f in a:
192 for f in a:
192 to = None
193 to = None
193 tn = read(f)
194 tn = read(f)
194 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
195 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
195 for f in d:
196 for f in d:
196 to = repo.file(f).read(mmap[f])
197 to = repo.file(f).read(mmap[f])
197 tn = None
198 tn = None
198 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
199 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r))
199
200
200 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None, brinfo=None):
201 def show_changeset(ui, repo, rev=0, changenode=None, filelog=None, brinfo=None):
201 """show a single changeset or file revision"""
202 """show a single changeset or file revision"""
202 changelog = repo.changelog
203 changelog = repo.changelog
203 if filelog:
204 if filelog:
204 log = filelog
205 log = filelog
205 filerev = rev
206 filerev = rev
206 node = filenode = filelog.node(filerev)
207 node = filenode = filelog.node(filerev)
207 changerev = filelog.linkrev(filenode)
208 changerev = filelog.linkrev(filenode)
208 changenode = changenode or changelog.node(changerev)
209 changenode = changenode or changelog.node(changerev)
209 else:
210 else:
210 log = changelog
211 log = changelog
211 changerev = rev
212 changerev = rev
212 if changenode is None:
213 if changenode is None:
213 changenode = changelog.node(changerev)
214 changenode = changelog.node(changerev)
214 elif not changerev:
215 elif not changerev:
215 rev = changerev = changelog.rev(changenode)
216 rev = changerev = changelog.rev(changenode)
216 node = changenode
217 node = changenode
217
218
218 if ui.quiet:
219 if ui.quiet:
219 ui.write("%d:%s\n" % (rev, hg.short(node)))
220 ui.write("%d:%s\n" % (rev, hg.short(node)))
220 return
221 return
221
222
222 changes = changelog.read(changenode)
223 changes = changelog.read(changenode)
223
224
224 parents = [(log.rev(p), ui.verbose and hg.hex(p) or hg.short(p))
225 parents = [(log.rev(p), ui.verbose and hg.hex(p) or hg.short(p))
225 for p in log.parents(node)
226 for p in log.parents(node)
226 if ui.debugflag or p != hg.nullid]
227 if ui.debugflag or p != hg.nullid]
227 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
228 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
228 parents = []
229 parents = []
229
230
230 if ui.verbose:
231 if ui.verbose:
231 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
232 ui.write("changeset: %d:%s\n" % (changerev, hg.hex(changenode)))
232 else:
233 else:
233 ui.write("changeset: %d:%s\n" % (changerev, hg.short(changenode)))
234 ui.write("changeset: %d:%s\n" % (changerev, hg.short(changenode)))
234
235
235 for tag in repo.nodetags(changenode):
236 for tag in repo.nodetags(changenode):
236 ui.status("tag: %s\n" % tag)
237 ui.status("tag: %s\n" % tag)
237 for parent in parents:
238 for parent in parents:
238 ui.write("parent: %d:%s\n" % parent)
239 ui.write("parent: %d:%s\n" % parent)
239 if filelog:
240 if filelog:
240 ui.debug("file rev: %d:%s\n" % (filerev, hg.hex(filenode)))
241 ui.debug("file rev: %d:%s\n" % (filerev, hg.hex(filenode)))
241
242
242 if brinfo and changenode in brinfo:
243 if brinfo and changenode in brinfo:
243 br = brinfo[changenode]
244 br = brinfo[changenode]
244 ui.write("branch: %s\n" % " ".join(br))
245 ui.write("branch: %s\n" % " ".join(br))
245
246
246 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
247 ui.debug("manifest: %d:%s\n" % (repo.manifest.rev(changes[0]),
247 hg.hex(changes[0])))
248 hg.hex(changes[0])))
248 ui.status("user: %s\n" % changes[1])
249 ui.status("user: %s\n" % changes[1])
249 ui.status("date: %s\n" % time.asctime(
250 ui.status("date: %s\n" % time.asctime(
250 time.localtime(float(changes[2].split(' ')[0]))))
251 time.localtime(float(changes[2].split(' ')[0]))))
251
252
252 if ui.debugflag:
253 if ui.debugflag:
253 files = repo.changes(changelog.parents(changenode)[0], changenode)
254 files = repo.changes(changelog.parents(changenode)[0], changenode)
254 for key, value in zip(["files:", "files+:", "files-:"], files):
255 for key, value in zip(["files:", "files+:", "files-:"], files):
255 if value:
256 if value:
256 ui.note("%-12s %s\n" % (key, " ".join(value)))
257 ui.note("%-12s %s\n" % (key, " ".join(value)))
257 else:
258 else:
258 ui.note("files: %s\n" % " ".join(changes[3]))
259 ui.note("files: %s\n" % " ".join(changes[3]))
259
260
260 description = changes[4].strip()
261 description = changes[4].strip()
261 if description:
262 if description:
262 if ui.verbose:
263 if ui.verbose:
263 ui.status("description:\n")
264 ui.status("description:\n")
264 ui.status(description)
265 ui.status(description)
265 ui.status("\n\n")
266 ui.status("\n\n")
266 else:
267 else:
267 ui.status("summary: %s\n" % description.splitlines()[0])
268 ui.status("summary: %s\n" % description.splitlines()[0])
268 ui.status("\n")
269 ui.status("\n")
269
270
270 def show_version(ui):
271 def show_version(ui):
271 """output version and copyright information"""
272 """output version and copyright information"""
272 ui.write("Mercurial Distributed SCM (version %s)\n"
273 ui.write("Mercurial Distributed SCM (version %s)\n"
273 % version.get_version())
274 % version.get_version())
274 ui.status(
275 ui.status(
275 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
276 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
276 "This is free software; see the source for copying conditions. "
277 "This is free software; see the source for copying conditions. "
277 "There is NO\nwarranty; "
278 "There is NO\nwarranty; "
278 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
279 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
279 )
280 )
280
281
281 def help_(ui, cmd=None):
282 def help_(ui, cmd=None):
282 """show help for a given command or all commands"""
283 """show help for a given command or all commands"""
283 if cmd and cmd != 'shortlist':
284 if cmd and cmd != 'shortlist':
284 key, i = find(cmd)
285 key, i = find(cmd)
285 # synopsis
286 # synopsis
286 ui.write("%s\n\n" % i[2])
287 ui.write("%s\n\n" % i[2])
287
288
288 # description
289 # description
289 doc = i[0].__doc__
290 doc = i[0].__doc__
290 if ui.quiet:
291 if ui.quiet:
291 doc = doc.splitlines(0)[0]
292 doc = doc.splitlines(0)[0]
292 ui.write("%s\n" % doc.rstrip())
293 ui.write("%s\n" % doc.rstrip())
293
294
294 # aliases
295 # aliases
295 if not ui.quiet:
296 if not ui.quiet:
296 aliases = ', '.join(key.split('|')[1:])
297 aliases = ', '.join(key.split('|')[1:])
297 if aliases:
298 if aliases:
298 ui.write("\naliases: %s\n" % aliases)
299 ui.write("\naliases: %s\n" % aliases)
299
300
300 # options
301 # options
301 if not ui.quiet and i[1]:
302 if not ui.quiet and i[1]:
302 ui.write("\noptions:\n\n")
303 ui.write("\noptions:\n\n")
303 for s, l, d, c in i[1]:
304 for s, l, d, c in i[1]:
304 opt = ' '
305 opt = ' '
305 if s:
306 if s:
306 opt = opt + '-' + s + ' '
307 opt = opt + '-' + s + ' '
307 if l:
308 if l:
308 opt = opt + '--' + l + ' '
309 opt = opt + '--' + l + ' '
309 if d:
310 if d:
310 opt = opt + '(' + str(d) + ')'
311 opt = opt + '(' + str(d) + ')'
311 ui.write(opt, "\n")
312 ui.write(opt, "\n")
312 if c:
313 if c:
313 ui.write(' %s\n' % c)
314 ui.write(' %s\n' % c)
314
315
315 else:
316 else:
316 # program name
317 # program name
317 if ui.verbose:
318 if ui.verbose:
318 show_version(ui)
319 show_version(ui)
319 else:
320 else:
320 ui.status("Mercurial Distributed SCM\n")
321 ui.status("Mercurial Distributed SCM\n")
321 ui.status('\n')
322 ui.status('\n')
322
323
323 # list of commands
324 # list of commands
324 if cmd == "shortlist":
325 if cmd == "shortlist":
325 ui.status('basic commands (use "hg help" '
326 ui.status('basic commands (use "hg help" '
326 'for the full list or option "-v" for details):\n\n')
327 'for the full list or option "-v" for details):\n\n')
327 elif ui.verbose:
328 elif ui.verbose:
328 ui.status('list of commands:\n\n')
329 ui.status('list of commands:\n\n')
329 else:
330 else:
330 ui.status('list of commands (use "hg help -v" '
331 ui.status('list of commands (use "hg help -v" '
331 'to show aliases and global options):\n\n')
332 'to show aliases and global options):\n\n')
332
333
333 h = {}
334 h = {}
334 cmds = {}
335 cmds = {}
335 for c, e in table.items():
336 for c, e in table.items():
336 f = c.split("|")[0]
337 f = c.split("|")[0]
337 if cmd == "shortlist" and not f.startswith("^"):
338 if cmd == "shortlist" and not f.startswith("^"):
338 continue
339 continue
339 f = f.lstrip("^")
340 f = f.lstrip("^")
340 if not ui.debugflag and f.startswith("debug"):
341 if not ui.debugflag and f.startswith("debug"):
341 continue
342 continue
342 d = ""
343 d = ""
343 if e[0].__doc__:
344 if e[0].__doc__:
344 d = e[0].__doc__.splitlines(0)[0].rstrip()
345 d = e[0].__doc__.splitlines(0)[0].rstrip()
345 h[f] = d
346 h[f] = d
346 cmds[f]=c.lstrip("^")
347 cmds[f]=c.lstrip("^")
347
348
348 fns = h.keys()
349 fns = h.keys()
349 fns.sort()
350 fns.sort()
350 m = max(map(len, fns))
351 m = max(map(len, fns))
351 for f in fns:
352 for f in fns:
352 if ui.verbose:
353 if ui.verbose:
353 commands = cmds[f].replace("|",", ")
354 commands = cmds[f].replace("|",", ")
354 ui.write(" %s:\n %s\n"%(commands,h[f]))
355 ui.write(" %s:\n %s\n"%(commands,h[f]))
355 else:
356 else:
356 ui.write(' %-*s %s\n' % (m, f, h[f]))
357 ui.write(' %-*s %s\n' % (m, f, h[f]))
357
358
358 # global options
359 # global options
359 if ui.verbose:
360 if ui.verbose:
360 ui.write("\nglobal options:\n\n")
361 ui.write("\nglobal options:\n\n")
361 for s, l, d, c in globalopts:
362 for s, l, d, c in globalopts:
362 opt = ' '
363 opt = ' '
363 if s:
364 if s:
364 opt = opt + '-' + s + ' '
365 opt = opt + '-' + s + ' '
365 if l:
366 if l:
366 opt = opt + '--' + l + ' '
367 opt = opt + '--' + l + ' '
367 if d:
368 if d:
368 opt = opt + '(' + str(d) + ')'
369 opt = opt + '(' + str(d) + ')'
369 ui.write(opt, "\n")
370 ui.write(opt, "\n")
370 if c:
371 if c:
371 ui.write(' %s\n' % c)
372 ui.write(' %s\n' % c)
372
373
373 # Commands start here, listed alphabetically
374 # Commands start here, listed alphabetically
374
375
375 def add(ui, repo, *pats, **opts):
376 def add(ui, repo, *pats, **opts):
376 '''add the specified files on the next commit'''
377 '''add the specified files on the next commit'''
377 names = []
378 names = []
378 q = dict(zip(pats, pats))
379 for src, abs, rel, exact in walk(repo, pats, opts):
379 for src, abs, rel in walk(repo, pats, opts):
380 if exact:
380 if rel in q or abs in q:
381 names.append(abs)
381 names.append(abs)
382 elif repo.dirstate.state(abs) == '?':
382 elif repo.dirstate.state(abs) == '?':
383 ui.status('adding %s\n' % rel)
383 ui.status('adding %s\n' % rel)
384 names.append(abs)
384 names.append(abs)
385 repo.add(names)
385 repo.add(names)
386
386
387 def addremove(ui, repo, *pats, **opts):
387 def addremove(ui, repo, *pats, **opts):
388 """add all new files, delete all missing files"""
388 """add all new files, delete all missing files"""
389 q = dict(zip(pats, pats))
390 add, remove = [], []
389 add, remove = [], []
391 for src, abs, rel in walk(repo, pats, opts):
390 for src, abs, rel, exact in walk(repo, pats, opts):
392 if src == 'f' and repo.dirstate.state(abs) == '?':
391 if src == 'f' and repo.dirstate.state(abs) == '?':
393 add.append(abs)
392 add.append(abs)
394 if rel not in q: ui.status('adding ', rel, '\n')
393 if not exact: ui.status('adding ', rel, '\n')
395 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
394 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
396 remove.append(abs)
395 remove.append(abs)
397 if rel not in q: ui.status('removing ', rel, '\n')
396 if not exact: ui.status('removing ', rel, '\n')
398 repo.add(add)
397 repo.add(add)
399 repo.remove(remove)
398 repo.remove(remove)
400
399
401 def annotate(ui, repo, *pats, **opts):
400 def annotate(ui, repo, *pats, **opts):
402 """show changeset information per file line"""
401 """show changeset information per file line"""
403 def getnode(rev):
402 def getnode(rev):
404 return hg.short(repo.changelog.node(rev))
403 return hg.short(repo.changelog.node(rev))
405
404
406 def getname(rev):
405 def getname(rev):
407 try:
406 try:
408 return bcache[rev]
407 return bcache[rev]
409 except KeyError:
408 except KeyError:
410 cl = repo.changelog.read(repo.changelog.node(rev))
409 cl = repo.changelog.read(repo.changelog.node(rev))
411 name = cl[1]
410 name = cl[1]
412 f = name.find('@')
411 f = name.find('@')
413 if f >= 0:
412 if f >= 0:
414 name = name[:f]
413 name = name[:f]
415 f = name.find('<')
414 f = name.find('<')
416 if f >= 0:
415 if f >= 0:
417 name = name[f+1:]
416 name = name[f+1:]
418 bcache[rev] = name
417 bcache[rev] = name
419 return name
418 return name
420
419
421 if not pats:
420 if not pats:
422 raise util.Abort('at least one file name or pattern required')
421 raise util.Abort('at least one file name or pattern required')
423
422
424 bcache = {}
423 bcache = {}
425 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
424 opmap = [['user', getname], ['number', str], ['changeset', getnode]]
426 if not opts['user'] and not opts['changeset']:
425 if not opts['user'] and not opts['changeset']:
427 opts['number'] = 1
426 opts['number'] = 1
428
427
429 if opts['rev']:
428 if opts['rev']:
430 node = repo.changelog.lookup(opts['rev'])
429 node = repo.changelog.lookup(opts['rev'])
431 else:
430 else:
432 node = repo.dirstate.parents()[0]
431 node = repo.dirstate.parents()[0]
433 change = repo.changelog.read(node)
432 change = repo.changelog.read(node)
434 mmap = repo.manifest.read(change[0])
433 mmap = repo.manifest.read(change[0])
435 for src, abs, rel in walk(repo, pats, opts):
434 for src, abs, rel, exact in walk(repo, pats, opts):
436 if abs not in mmap:
435 if abs not in mmap:
437 ui.warn("warning: %s is not in the repository!\n" % rel)
436 ui.warn("warning: %s is not in the repository!\n" % rel)
438 continue
437 continue
439
438
440 lines = repo.file(abs).annotate(mmap[abs])
439 lines = repo.file(abs).annotate(mmap[abs])
441 pieces = []
440 pieces = []
442
441
443 for o, f in opmap:
442 for o, f in opmap:
444 if opts[o]:
443 if opts[o]:
445 l = [f(n) for n, dummy in lines]
444 l = [f(n) for n, dummy in lines]
446 if l:
445 if l:
447 m = max(map(len, l))
446 m = max(map(len, l))
448 pieces.append(["%*s" % (m, x) for x in l])
447 pieces.append(["%*s" % (m, x) for x in l])
449
448
450 if pieces:
449 if pieces:
451 for p, l in zip(zip(*pieces), lines):
450 for p, l in zip(zip(*pieces), lines):
452 ui.write("%s: %s" % (" ".join(p), l[1]))
451 ui.write("%s: %s" % (" ".join(p), l[1]))
453
452
454 def cat(ui, repo, file1, rev=None, **opts):
453 def cat(ui, repo, file1, rev=None, **opts):
455 """output the latest or given revision of a file"""
454 """output the latest or given revision of a file"""
456 r = repo.file(relpath(repo, [file1])[0])
455 r = repo.file(relpath(repo, [file1])[0])
457 if rev:
456 if rev:
458 try:
457 try:
459 # assume all revision numbers are for changesets
458 # assume all revision numbers are for changesets
460 n = repo.lookup(rev)
459 n = repo.lookup(rev)
461 change = repo.changelog.read(n)
460 change = repo.changelog.read(n)
462 m = repo.manifest.read(change[0])
461 m = repo.manifest.read(change[0])
463 n = m[relpath(repo, [file1])[0]]
462 n = m[relpath(repo, [file1])[0]]
464 except hg.RepoError, KeyError:
463 except hg.RepoError, KeyError:
465 n = r.lookup(rev)
464 n = r.lookup(rev)
466 else:
465 else:
467 n = r.tip()
466 n = r.tip()
468 fp = make_file(repo, r, opts['output'], node=n)
467 fp = make_file(repo, r, opts['output'], node=n)
469 fp.write(r.read(n))
468 fp.write(r.read(n))
470
469
471 def clone(ui, source, dest=None, **opts):
470 def clone(ui, source, dest=None, **opts):
472 """make a copy of an existing repository"""
471 """make a copy of an existing repository"""
473 if dest is None:
472 if dest is None:
474 dest = os.path.basename(os.path.normpath(source))
473 dest = os.path.basename(os.path.normpath(source))
475
474
476 if os.path.exists(dest):
475 if os.path.exists(dest):
477 ui.warn("abort: destination '%s' already exists\n" % dest)
476 ui.warn("abort: destination '%s' already exists\n" % dest)
478 return 1
477 return 1
479
478
480 dest = os.path.realpath(dest)
479 dest = os.path.realpath(dest)
481
480
482 class Dircleanup:
481 class Dircleanup:
483 def __init__(self, dir_):
482 def __init__(self, dir_):
484 self.rmtree = shutil.rmtree
483 self.rmtree = shutil.rmtree
485 self.dir_ = dir_
484 self.dir_ = dir_
486 os.mkdir(dir_)
485 os.mkdir(dir_)
487 def close(self):
486 def close(self):
488 self.dir_ = None
487 self.dir_ = None
489 def __del__(self):
488 def __del__(self):
490 if self.dir_:
489 if self.dir_:
491 self.rmtree(self.dir_, True)
490 self.rmtree(self.dir_, True)
492
491
493 d = Dircleanup(dest)
492 d = Dircleanup(dest)
494 abspath = source
493 abspath = source
495 source = ui.expandpath(source)
494 source = ui.expandpath(source)
496 other = hg.repository(ui, source)
495 other = hg.repository(ui, source)
497
496
498 if other.dev() != -1:
497 if other.dev() != -1:
499 abspath = os.path.abspath(source)
498 abspath = os.path.abspath(source)
500 copyfile = (os.stat(dest).st_dev == other.dev()
499 copyfile = (os.stat(dest).st_dev == other.dev()
501 and getattr(os, 'link', None) or shutil.copy2)
500 and getattr(os, 'link', None) or shutil.copy2)
502 if copyfile is not shutil.copy2:
501 if copyfile is not shutil.copy2:
503 ui.note("cloning by hardlink\n")
502 ui.note("cloning by hardlink\n")
504 # we use a lock here because because we're not nicely ordered
503 # we use a lock here because because we're not nicely ordered
505 l = lock.lock(os.path.join(source, ".hg", "lock"))
504 l = lock.lock(os.path.join(source, ".hg", "lock"))
506
505
507 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
506 util.copytree(os.path.join(source, ".hg"), os.path.join(dest, ".hg"),
508 copyfile)
507 copyfile)
509 try:
508 try:
510 os.unlink(os.path.join(dest, ".hg", "dirstate"))
509 os.unlink(os.path.join(dest, ".hg", "dirstate"))
511 except OSError:
510 except OSError:
512 pass
511 pass
513
512
514 repo = hg.repository(ui, dest)
513 repo = hg.repository(ui, dest)
515
514
516 else:
515 else:
517 repo = hg.repository(ui, dest, create=1)
516 repo = hg.repository(ui, dest, create=1)
518 repo.pull(other)
517 repo.pull(other)
519
518
520 f = repo.opener("hgrc", "w")
519 f = repo.opener("hgrc", "w")
521 f.write("[paths]\n")
520 f.write("[paths]\n")
522 f.write("default = %s\n" % abspath)
521 f.write("default = %s\n" % abspath)
523
522
524 if not opts['noupdate']:
523 if not opts['noupdate']:
525 update(ui, repo)
524 update(ui, repo)
526
525
527 d.close()
526 d.close()
528
527
529 def commit(ui, repo, *pats, **opts):
528 def commit(ui, repo, *pats, **opts):
530 """commit the specified files or all outstanding changes"""
529 """commit the specified files or all outstanding changes"""
531 if opts['text']:
530 if opts['text']:
532 ui.warn("Warning: -t and --text is deprecated,"
531 ui.warn("Warning: -t and --text is deprecated,"
533 " please use -m or --message instead.\n")
532 " please use -m or --message instead.\n")
534 message = opts['message'] or opts['text']
533 message = opts['message'] or opts['text']
535 logfile = opts['logfile']
534 logfile = opts['logfile']
536 if not message and logfile:
535 if not message and logfile:
537 try:
536 try:
538 if logfile == '-':
537 if logfile == '-':
539 message = sys.stdin.read()
538 message = sys.stdin.read()
540 else:
539 else:
541 message = open(logfile).read()
540 message = open(logfile).read()
542 except IOError, why:
541 except IOError, why:
543 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
542 ui.warn("Can't read commit message %s: %s\n" % (logfile, why))
544
543
545 if opts['addremove']:
544 if opts['addremove']:
546 addremove(ui, repo, *pats, **opts)
545 addremove(ui, repo, *pats, **opts)
547 cwd = repo.getcwd()
546 cwd = repo.getcwd()
548 if not pats and cwd:
547 if not pats and cwd:
549 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
548 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
550 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
549 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
551 fns, match = matchpats(repo, (pats and repo.getcwd()) or '', pats, opts)
550 fns, match = matchpats(repo, (pats and repo.getcwd()) or '', pats, opts)
552 if pats:
551 if pats:
553 c, a, d, u = repo.changes(files = fns, match = match)
552 c, a, d, u = repo.changes(files = fns, match = match)
554 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
553 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
555 else:
554 else:
556 files = []
555 files = []
557 repo.commit(files, message, opts['user'], opts['date'], match)
556 repo.commit(files, message, opts['user'], opts['date'], match)
558
557
559 def copy(ui, repo, source, dest):
558 def copy(ui, repo, source, dest):
560 """mark a file as copied or renamed for the next commit"""
559 """mark a file as copied or renamed for the next commit"""
561 return repo.copy(*relpath(repo, (source, dest)))
560 return repo.copy(*relpath(repo, (source, dest)))
562
561
563 def debugcheckstate(ui, repo):
562 def debugcheckstate(ui, repo):
564 """validate the correctness of the current dirstate"""
563 """validate the correctness of the current dirstate"""
565 parent1, parent2 = repo.dirstate.parents()
564 parent1, parent2 = repo.dirstate.parents()
566 repo.dirstate.read()
565 repo.dirstate.read()
567 dc = repo.dirstate.map
566 dc = repo.dirstate.map
568 keys = dc.keys()
567 keys = dc.keys()
569 keys.sort()
568 keys.sort()
570 m1n = repo.changelog.read(parent1)[0]
569 m1n = repo.changelog.read(parent1)[0]
571 m2n = repo.changelog.read(parent2)[0]
570 m2n = repo.changelog.read(parent2)[0]
572 m1 = repo.manifest.read(m1n)
571 m1 = repo.manifest.read(m1n)
573 m2 = repo.manifest.read(m2n)
572 m2 = repo.manifest.read(m2n)
574 errors = 0
573 errors = 0
575 for f in dc:
574 for f in dc:
576 state = repo.dirstate.state(f)
575 state = repo.dirstate.state(f)
577 if state in "nr" and f not in m1:
576 if state in "nr" and f not in m1:
578 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
577 ui.warn("%s in state %s, but not in manifest1\n" % (f, state))
579 errors += 1
578 errors += 1
580 if state in "a" and f in m1:
579 if state in "a" and f in m1:
581 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
580 ui.warn("%s in state %s, but also in manifest1\n" % (f, state))
582 errors += 1
581 errors += 1
583 if state in "m" and f not in m1 and f not in m2:
582 if state in "m" and f not in m1 and f not in m2:
584 ui.warn("%s in state %s, but not in either manifest\n" %
583 ui.warn("%s in state %s, but not in either manifest\n" %
585 (f, state))
584 (f, state))
586 errors += 1
585 errors += 1
587 for f in m1:
586 for f in m1:
588 state = repo.dirstate.state(f)
587 state = repo.dirstate.state(f)
589 if state not in "nrm":
588 if state not in "nrm":
590 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
589 ui.warn("%s in manifest1, but listed as state %s" % (f, state))
591 errors += 1
590 errors += 1
592 if errors:
591 if errors:
593 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
592 raise util.Abort(".hg/dirstate inconsistent with current parent's manifest")
594
593
595 def debugstate(ui, repo):
594 def debugstate(ui, repo):
596 """show the contents of the current dirstate"""
595 """show the contents of the current dirstate"""
597 repo.dirstate.read()
596 repo.dirstate.read()
598 dc = repo.dirstate.map
597 dc = repo.dirstate.map
599 keys = dc.keys()
598 keys = dc.keys()
600 keys.sort()
599 keys.sort()
601 for file_ in keys:
600 for file_ in keys:
602 ui.write("%c %3o %10d %s %s\n"
601 ui.write("%c %3o %10d %s %s\n"
603 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
602 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
604 time.strftime("%x %X",
603 time.strftime("%x %X",
605 time.localtime(dc[file_][3])), file_))
604 time.localtime(dc[file_][3])), file_))
606
605
607 def debugindex(ui, file_):
606 def debugindex(ui, file_):
608 """dump the contents of an index file"""
607 """dump the contents of an index file"""
609 r = hg.revlog(hg.opener(""), file_, "")
608 r = hg.revlog(hg.opener(""), file_, "")
610 ui.write(" rev offset length base linkrev" +
609 ui.write(" rev offset length base linkrev" +
611 " p1 p2 nodeid\n")
610 " p1 p2 nodeid\n")
612 for i in range(r.count()):
611 for i in range(r.count()):
613 e = r.index[i]
612 e = r.index[i]
614 ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % (
613 ui.write("% 6d % 9d % 7d % 6d % 7d %s.. %s.. %s..\n" % (
615 i, e[0], e[1], e[2], e[3],
614 i, e[0], e[1], e[2], e[3],
616 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5])))
615 hg.hex(e[4][:5]), hg.hex(e[5][:5]), hg.hex(e[6][:5])))
617
616
618 def debugindexdot(ui, file_):
617 def debugindexdot(ui, file_):
619 """dump an index DAG as a .dot file"""
618 """dump an index DAG as a .dot file"""
620 r = hg.revlog(hg.opener(""), file_, "")
619 r = hg.revlog(hg.opener(""), file_, "")
621 ui.write("digraph G {\n")
620 ui.write("digraph G {\n")
622 for i in range(r.count()):
621 for i in range(r.count()):
623 e = r.index[i]
622 e = r.index[i]
624 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
623 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
625 if e[5] != hg.nullid:
624 if e[5] != hg.nullid:
626 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
625 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
627 ui.write("}\n")
626 ui.write("}\n")
628
627
629 def debugwalk(ui, repo, *pats, **opts):
628 def debugwalk(ui, repo, *pats, **opts):
630 items = list(walk(repo, pats, opts))
629 items = list(walk(repo, pats, opts))
631 if not items: return
630 if not items: return
632 fmt = '%%s %%-%ds %%s' % max([len(abs) for (src, abs, rel) in items])
631 fmt = '%%s %%-%ds %%-%ds %%s' % (
633 for i in items: print fmt % i
632 max([len(abs) for (src, abs, rel, exact) in items]),
633 max([len(rel) for (src, abs, rel, exact) in items]))
634 exactly = {True: 'exact', False: ''}
635 for src, abs, rel, exact in items:
636 print fmt % (src, abs, rel, exactly[exact])
634
637
635 def diff(ui, repo, *pats, **opts):
638 def diff(ui, repo, *pats, **opts):
636 """diff working directory (or selected files)"""
639 """diff working directory (or selected files)"""
637 revs = []
640 revs = []
638 if opts['rev']:
641 if opts['rev']:
639 revs = map(lambda x: repo.lookup(x), opts['rev'])
642 revs = map(lambda x: repo.lookup(x), opts['rev'])
640
643
641 if len(revs) > 2:
644 if len(revs) > 2:
642 raise util.Abort("too many revisions to diff")
645 raise util.Abort("too many revisions to diff")
643
646
644 files = []
647 files = []
645 match = util.always
648 match = util.always
646 if pats:
649 if pats:
647 roots, match, results = makewalk(repo, pats, opts)
650 roots, match, results = makewalk(repo, pats, opts)
648 for src, abs, rel in results:
651 for src, abs, rel, exact in results:
649 files.append(abs)
652 files.append(abs)
650 dodiff(sys.stdout, ui, repo, files, *revs, **{'match': match})
653 dodiff(sys.stdout, ui, repo, files, *revs, **{'match': match})
651
654
652 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
655 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
653 node = repo.lookup(changeset)
656 node = repo.lookup(changeset)
654 prev, other = repo.changelog.parents(node)
657 prev, other = repo.changelog.parents(node)
655 change = repo.changelog.read(node)
658 change = repo.changelog.read(node)
656
659
657 fp = make_file(repo, repo.changelog, opts['output'],
660 fp = make_file(repo, repo.changelog, opts['output'],
658 node=node, total=total, seqno=seqno,
661 node=node, total=total, seqno=seqno,
659 revwidth=revwidth)
662 revwidth=revwidth)
660 if fp != sys.stdout:
663 if fp != sys.stdout:
661 ui.note("%s\n" % fp.name)
664 ui.note("%s\n" % fp.name)
662
665
663 fp.write("# HG changeset patch\n")
666 fp.write("# HG changeset patch\n")
664 fp.write("# User %s\n" % change[1])
667 fp.write("# User %s\n" % change[1])
665 fp.write("# Node ID %s\n" % hg.hex(node))
668 fp.write("# Node ID %s\n" % hg.hex(node))
666 fp.write("# Parent %s\n" % hg.hex(prev))
669 fp.write("# Parent %s\n" % hg.hex(prev))
667 if other != hg.nullid:
670 if other != hg.nullid:
668 fp.write("# Parent %s\n" % hg.hex(other))
671 fp.write("# Parent %s\n" % hg.hex(other))
669 fp.write(change[4].rstrip())
672 fp.write(change[4].rstrip())
670 fp.write("\n\n")
673 fp.write("\n\n")
671
674
672 dodiff(fp, ui, repo, None, prev, node)
675 dodiff(fp, ui, repo, None, prev, node)
673 if fp != sys.stdout: fp.close()
676 if fp != sys.stdout: fp.close()
674
677
675 def export(ui, repo, *changesets, **opts):
678 def export(ui, repo, *changesets, **opts):
676 """dump the header and diffs for one or more changesets"""
679 """dump the header and diffs for one or more changesets"""
677 if not changesets:
680 if not changesets:
678 raise util.Abort("export requires at least one changeset")
681 raise util.Abort("export requires at least one changeset")
679 seqno = 0
682 seqno = 0
680 revs = list(revrange(ui, repo, changesets))
683 revs = list(revrange(ui, repo, changesets))
681 total = len(revs)
684 total = len(revs)
682 revwidth = max(len(revs[0]), len(revs[-1]))
685 revwidth = max(len(revs[0]), len(revs[-1]))
683 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
686 ui.note(len(revs) > 1 and "Exporting patches:\n" or "Exporting patch:\n")
684 for cset in revs:
687 for cset in revs:
685 seqno += 1
688 seqno += 1
686 doexport(ui, repo, cset, seqno, total, revwidth, opts)
689 doexport(ui, repo, cset, seqno, total, revwidth, opts)
687
690
688 def forget(ui, repo, *pats, **opts):
691 def forget(ui, repo, *pats, **opts):
689 """don't add the specified files on the next commit"""
692 """don't add the specified files on the next commit"""
690 q = dict(zip(pats, pats))
691 forget = []
693 forget = []
692 for src, abs, rel in walk(repo, pats, opts):
694 for src, abs, rel, exact in walk(repo, pats, opts):
693 if repo.dirstate.state(abs) == 'a':
695 if repo.dirstate.state(abs) == 'a':
694 forget.append(abs)
696 forget.append(abs)
695 if rel not in q: ui.status('forgetting ', rel, '\n')
697 if not exact: ui.status('forgetting ', rel, '\n')
696 repo.forget(forget)
698 repo.forget(forget)
697
699
698 def heads(ui, repo, **opts):
700 def heads(ui, repo, **opts):
699 """show current repository heads"""
701 """show current repository heads"""
700 heads = repo.changelog.heads()
702 heads = repo.changelog.heads()
701 br = None
703 br = None
702 if opts['branches']:
704 if opts['branches']:
703 br = repo.branchlookup(heads)
705 br = repo.branchlookup(heads)
704 for n in repo.changelog.heads():
706 for n in repo.changelog.heads():
705 show_changeset(ui, repo, changenode=n, brinfo=br)
707 show_changeset(ui, repo, changenode=n, brinfo=br)
706
708
707 def identify(ui, repo):
709 def identify(ui, repo):
708 """print information about the working copy"""
710 """print information about the working copy"""
709 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
711 parents = [p for p in repo.dirstate.parents() if p != hg.nullid]
710 if not parents:
712 if not parents:
711 ui.write("unknown\n")
713 ui.write("unknown\n")
712 return
714 return
713
715
714 hexfunc = ui.verbose and hg.hex or hg.short
716 hexfunc = ui.verbose and hg.hex or hg.short
715 (c, a, d, u) = repo.changes()
717 (c, a, d, u) = repo.changes()
716 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
718 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
717 (c or a or d) and "+" or "")]
719 (c or a or d) and "+" or "")]
718
720
719 if not ui.quiet:
721 if not ui.quiet:
720 # multiple tags for a single parent separated by '/'
722 # multiple tags for a single parent separated by '/'
721 parenttags = ['/'.join(tags)
723 parenttags = ['/'.join(tags)
722 for tags in map(repo.nodetags, parents) if tags]
724 for tags in map(repo.nodetags, parents) if tags]
723 # tags for multiple parents separated by ' + '
725 # tags for multiple parents separated by ' + '
724 if parenttags:
726 if parenttags:
725 output.append(' + '.join(parenttags))
727 output.append(' + '.join(parenttags))
726
728
727 ui.write("%s\n" % ' '.join(output))
729 ui.write("%s\n" % ' '.join(output))
728
730
729 def import_(ui, repo, patch1, *patches, **opts):
731 def import_(ui, repo, patch1, *patches, **opts):
730 """import an ordered set of patches"""
732 """import an ordered set of patches"""
731 patches = (patch1,) + patches
733 patches = (patch1,) + patches
732
734
733 d = opts["base"]
735 d = opts["base"]
734 strip = opts["strip"]
736 strip = opts["strip"]
735
737
736 for patch in patches:
738 for patch in patches:
737 ui.status("applying %s\n" % patch)
739 ui.status("applying %s\n" % patch)
738 pf = os.path.join(d, patch)
740 pf = os.path.join(d, patch)
739
741
740 message = []
742 message = []
741 user = None
743 user = None
742 hgpatch = False
744 hgpatch = False
743 for line in file(pf):
745 for line in file(pf):
744 line = line.rstrip()
746 line = line.rstrip()
745 if line.startswith("--- ") or line.startswith("diff -r"):
747 if line.startswith("--- ") or line.startswith("diff -r"):
746 break
748 break
747 elif hgpatch:
749 elif hgpatch:
748 # parse values when importing the result of an hg export
750 # parse values when importing the result of an hg export
749 if line.startswith("# User "):
751 if line.startswith("# User "):
750 user = line[7:]
752 user = line[7:]
751 ui.debug('User: %s\n' % user)
753 ui.debug('User: %s\n' % user)
752 elif not line.startswith("# ") and line:
754 elif not line.startswith("# ") and line:
753 message.append(line)
755 message.append(line)
754 hgpatch = False
756 hgpatch = False
755 elif line == '# HG changeset patch':
757 elif line == '# HG changeset patch':
756 hgpatch = True
758 hgpatch = True
757 message = [] # We may have collected garbage
759 message = [] # We may have collected garbage
758 else:
760 else:
759 message.append(line)
761 message.append(line)
760
762
761 # make sure message isn't empty
763 # make sure message isn't empty
762 if not message:
764 if not message:
763 message = "imported patch %s\n" % patch
765 message = "imported patch %s\n" % patch
764 else:
766 else:
765 message = "%s\n" % '\n'.join(message)
767 message = "%s\n" % '\n'.join(message)
766 ui.debug('message:\n%s\n' % message)
768 ui.debug('message:\n%s\n' % message)
767
769
768 f = os.popen("patch -p%d < '%s'" % (strip, pf))
770 f = os.popen("patch -p%d < '%s'" % (strip, pf))
769 files = []
771 files = []
770 for l in f.read().splitlines():
772 for l in f.read().splitlines():
771 l.rstrip('\r\n');
773 l.rstrip('\r\n');
772 ui.status("%s\n" % l)
774 ui.status("%s\n" % l)
773 if l.startswith('patching file '):
775 if l.startswith('patching file '):
774 pf = l[14:]
776 pf = l[14:]
775 if pf not in files:
777 if pf not in files:
776 files.append(pf)
778 files.append(pf)
777 patcherr = f.close()
779 patcherr = f.close()
778 if patcherr:
780 if patcherr:
779 raise util.Abort("patch failed")
781 raise util.Abort("patch failed")
780
782
781 if len(files) > 0:
783 if len(files) > 0:
782 addremove(ui, repo, *files)
784 addremove(ui, repo, *files)
783 repo.commit(files, message, user)
785 repo.commit(files, message, user)
784
786
785 def incoming(ui, repo, source="default"):
787 def incoming(ui, repo, source="default"):
786 """show new changesets found in source"""
788 """show new changesets found in source"""
787 source = ui.expandpath(source)
789 source = ui.expandpath(source)
788 other = hg.repository(ui, source)
790 other = hg.repository(ui, source)
789 if not other.local():
791 if not other.local():
790 ui.warn("abort: incoming doesn't work for remote"
792 ui.warn("abort: incoming doesn't work for remote"
791 + " repositories yet, sorry!\n")
793 + " repositories yet, sorry!\n")
792 return 1
794 return 1
793 o = repo.findincoming(other)
795 o = repo.findincoming(other)
794 if not o:
796 if not o:
795 return
797 return
796 o = other.newer(o)
798 o = other.newer(o)
797 o.reverse()
799 o.reverse()
798 for n in o:
800 for n in o:
799 show_changeset(ui, other, changenode=n)
801 show_changeset(ui, other, changenode=n)
800
802
801 def init(ui, dest="."):
803 def init(ui, dest="."):
802 """create a new repository in the given directory"""
804 """create a new repository in the given directory"""
803 if not os.path.exists(dest):
805 if not os.path.exists(dest):
804 os.mkdir(dest)
806 os.mkdir(dest)
805 hg.repository(ui, dest, create=1)
807 hg.repository(ui, dest, create=1)
806
808
807 def locate(ui, repo, *pats, **opts):
809 def locate(ui, repo, *pats, **opts):
808 """locate files matching specific patterns"""
810 """locate files matching specific patterns"""
809 end = '\n'
811 end = '\n'
810 if opts['print0']: end = '\0'
812 if opts['print0']: end = '\0'
811
813
812 for src, abs, rel in walk(repo, pats, opts, '(?:.*/|)'):
814 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
813 if repo.dirstate.state(abs) == '?': continue
815 if repo.dirstate.state(abs) == '?': continue
814 if opts['fullpath']:
816 if opts['fullpath']:
815 ui.write(os.path.join(repo.root, abs), end)
817 ui.write(os.path.join(repo.root, abs), end)
816 else:
818 else:
817 ui.write(rel, end)
819 ui.write(rel, end)
818
820
819 def log(ui, repo, f=None, **opts):
821 def log(ui, repo, f=None, **opts):
820 """show the revision history of the repository or a single file"""
822 """show the revision history of the repository or a single file"""
821 if f:
823 if f:
822 files = relpath(repo, [f])
824 files = relpath(repo, [f])
823 filelog = repo.file(files[0])
825 filelog = repo.file(files[0])
824 log = filelog
826 log = filelog
825 lookup = filelog.lookup
827 lookup = filelog.lookup
826 else:
828 else:
827 files = None
829 files = None
828 filelog = None
830 filelog = None
829 log = repo.changelog
831 log = repo.changelog
830 lookup = repo.lookup
832 lookup = repo.lookup
831 revlist = []
833 revlist = []
832 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
834 revs = [log.rev(lookup(rev)) for rev in opts['rev']]
833 while revs:
835 while revs:
834 if len(revs) == 1:
836 if len(revs) == 1:
835 revlist.append(revs.pop(0))
837 revlist.append(revs.pop(0))
836 else:
838 else:
837 a = revs.pop(0)
839 a = revs.pop(0)
838 b = revs.pop(0)
840 b = revs.pop(0)
839 off = a > b and -1 or 1
841 off = a > b and -1 or 1
840 revlist.extend(range(a, b + off, off))
842 revlist.extend(range(a, b + off, off))
841
843
842 for i in revlist or range(log.count() - 1, -1, -1):
844 for i in revlist or range(log.count() - 1, -1, -1):
843 show_changeset(ui, repo, filelog=filelog, rev=i)
845 show_changeset(ui, repo, filelog=filelog, rev=i)
844 if opts['patch']:
846 if opts['patch']:
845 if filelog:
847 if filelog:
846 filenode = filelog.node(i)
848 filenode = filelog.node(i)
847 i = filelog.linkrev(filenode)
849 i = filelog.linkrev(filenode)
848 changenode = repo.changelog.node(i)
850 changenode = repo.changelog.node(i)
849 prev, other = repo.changelog.parents(changenode)
851 prev, other = repo.changelog.parents(changenode)
850 dodiff(sys.stdout, ui, repo, files, prev, changenode)
852 dodiff(sys.stdout, ui, repo, files, prev, changenode)
851 ui.write("\n\n")
853 ui.write("\n\n")
852
854
853 def manifest(ui, repo, rev=None):
855 def manifest(ui, repo, rev=None):
854 """output the latest or given revision of the project manifest"""
856 """output the latest or given revision of the project manifest"""
855 if rev:
857 if rev:
856 try:
858 try:
857 # assume all revision numbers are for changesets
859 # assume all revision numbers are for changesets
858 n = repo.lookup(rev)
860 n = repo.lookup(rev)
859 change = repo.changelog.read(n)
861 change = repo.changelog.read(n)
860 n = change[0]
862 n = change[0]
861 except hg.RepoError:
863 except hg.RepoError:
862 n = repo.manifest.lookup(rev)
864 n = repo.manifest.lookup(rev)
863 else:
865 else:
864 n = repo.manifest.tip()
866 n = repo.manifest.tip()
865 m = repo.manifest.read(n)
867 m = repo.manifest.read(n)
866 mf = repo.manifest.readflags(n)
868 mf = repo.manifest.readflags(n)
867 files = m.keys()
869 files = m.keys()
868 files.sort()
870 files.sort()
869
871
870 for f in files:
872 for f in files:
871 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
873 ui.write("%40s %3s %s\n" % (hg.hex(m[f]), mf[f] and "755" or "644", f))
872
874
873 def outgoing(ui, repo, dest="default-push"):
875 def outgoing(ui, repo, dest="default-push"):
874 """show changesets not found in destination"""
876 """show changesets not found in destination"""
875 dest = ui.expandpath(dest)
877 dest = ui.expandpath(dest)
876 other = hg.repository(ui, dest)
878 other = hg.repository(ui, dest)
877 o = repo.findoutgoing(other)
879 o = repo.findoutgoing(other)
878 o = repo.newer(o)
880 o = repo.newer(o)
879 o.reverse()
881 o.reverse()
880 for n in o:
882 for n in o:
881 show_changeset(ui, repo, changenode=n)
883 show_changeset(ui, repo, changenode=n)
882
884
883 def parents(ui, repo, rev=None):
885 def parents(ui, repo, rev=None):
884 """show the parents of the working dir or revision"""
886 """show the parents of the working dir or revision"""
885 if rev:
887 if rev:
886 p = repo.changelog.parents(repo.lookup(rev))
888 p = repo.changelog.parents(repo.lookup(rev))
887 else:
889 else:
888 p = repo.dirstate.parents()
890 p = repo.dirstate.parents()
889
891
890 for n in p:
892 for n in p:
891 if n != hg.nullid:
893 if n != hg.nullid:
892 show_changeset(ui, repo, changenode=n)
894 show_changeset(ui, repo, changenode=n)
893
895
894 def paths(ui, search = None):
896 def paths(ui, search = None):
895 """show definition of symbolic path names"""
897 """show definition of symbolic path names"""
896 try:
898 try:
897 repo = hg.repository(ui=ui)
899 repo = hg.repository(ui=ui)
898 except:
900 except:
899 pass
901 pass
900
902
901 if search:
903 if search:
902 for name, path in ui.configitems("paths"):
904 for name, path in ui.configitems("paths"):
903 if name == search:
905 if name == search:
904 ui.write("%s\n" % path)
906 ui.write("%s\n" % path)
905 return
907 return
906 ui.warn("not found!\n")
908 ui.warn("not found!\n")
907 return 1
909 return 1
908 else:
910 else:
909 for name, path in ui.configitems("paths"):
911 for name, path in ui.configitems("paths"):
910 ui.write("%s = %s\n" % (name, path))
912 ui.write("%s = %s\n" % (name, path))
911
913
912 def pull(ui, repo, source="default", **opts):
914 def pull(ui, repo, source="default", **opts):
913 """pull changes from the specified source"""
915 """pull changes from the specified source"""
914 source = ui.expandpath(source)
916 source = ui.expandpath(source)
915 ui.status('pulling from %s\n' % (source))
917 ui.status('pulling from %s\n' % (source))
916
918
917 other = hg.repository(ui, source)
919 other = hg.repository(ui, source)
918 r = repo.pull(other)
920 r = repo.pull(other)
919 if not r:
921 if not r:
920 if opts['update']:
922 if opts['update']:
921 return update(ui, repo)
923 return update(ui, repo)
922 else:
924 else:
923 ui.status("(run 'hg update' to get a working copy)\n")
925 ui.status("(run 'hg update' to get a working copy)\n")
924
926
925 return r
927 return r
926
928
927 def push(ui, repo, dest="default-push", force=False):
929 def push(ui, repo, dest="default-push", force=False):
928 """push changes to the specified destination"""
930 """push changes to the specified destination"""
929 dest = ui.expandpath(dest)
931 dest = ui.expandpath(dest)
930 ui.status('pushing to %s\n' % (dest))
932 ui.status('pushing to %s\n' % (dest))
931
933
932 other = hg.repository(ui, dest)
934 other = hg.repository(ui, dest)
933 r = repo.push(other, force)
935 r = repo.push(other, force)
934 return r
936 return r
935
937
936 def rawcommit(ui, repo, *flist, **rc):
938 def rawcommit(ui, repo, *flist, **rc):
937 "raw commit interface"
939 "raw commit interface"
938 if rc['text']:
940 if rc['text']:
939 ui.warn("Warning: -t and --text is deprecated,"
941 ui.warn("Warning: -t and --text is deprecated,"
940 " please use -m or --message instead.\n")
942 " please use -m or --message instead.\n")
941 message = rc['message'] or rc['text']
943 message = rc['message'] or rc['text']
942 if not message and rc['logfile']:
944 if not message and rc['logfile']:
943 try:
945 try:
944 message = open(rc['logfile']).read()
946 message = open(rc['logfile']).read()
945 except IOError:
947 except IOError:
946 pass
948 pass
947 if not message and not rc['logfile']:
949 if not message and not rc['logfile']:
948 ui.warn("abort: missing commit message\n")
950 ui.warn("abort: missing commit message\n")
949 return 1
951 return 1
950
952
951 files = relpath(repo, list(flist))
953 files = relpath(repo, list(flist))
952 if rc['files']:
954 if rc['files']:
953 files += open(rc['files']).read().splitlines()
955 files += open(rc['files']).read().splitlines()
954
956
955 rc['parent'] = map(repo.lookup, rc['parent'])
957 rc['parent'] = map(repo.lookup, rc['parent'])
956
958
957 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
959 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
958
960
959 def recover(ui, repo):
961 def recover(ui, repo):
960 """roll back an interrupted transaction"""
962 """roll back an interrupted transaction"""
961 repo.recover()
963 repo.recover()
962
964
963 def remove(ui, repo, file1, *files):
965 def remove(ui, repo, file1, *files):
964 """remove the specified files on the next commit"""
966 """remove the specified files on the next commit"""
965 repo.remove(relpath(repo, (file1,) + files))
967 repo.remove(relpath(repo, (file1,) + files))
966
968
967 def revert(ui, repo, *names, **opts):
969 def revert(ui, repo, *names, **opts):
968 """revert modified files or dirs back to their unmodified states"""
970 """revert modified files or dirs back to their unmodified states"""
969 node = opts['rev'] and repo.lookup(opts['rev']) or \
971 node = opts['rev'] and repo.lookup(opts['rev']) or \
970 repo.dirstate.parents()[0]
972 repo.dirstate.parents()[0]
971 root = os.path.realpath(repo.root)
973 root = os.path.realpath(repo.root)
972
974
973 def trimpath(p):
975 def trimpath(p):
974 p = os.path.realpath(p)
976 p = os.path.realpath(p)
975 if p.startswith(root):
977 if p.startswith(root):
976 rest = p[len(root):]
978 rest = p[len(root):]
977 if not rest:
979 if not rest:
978 return rest
980 return rest
979 if p.startswith(os.sep):
981 if p.startswith(os.sep):
980 return rest[1:]
982 return rest[1:]
981 return p
983 return p
982
984
983 relnames = map(trimpath, names or [os.getcwd()])
985 relnames = map(trimpath, names or [os.getcwd()])
984 chosen = {}
986 chosen = {}
985
987
986 def choose(name):
988 def choose(name):
987 def body(name):
989 def body(name):
988 for r in relnames:
990 for r in relnames:
989 if not name.startswith(r):
991 if not name.startswith(r):
990 continue
992 continue
991 rest = name[len(r):]
993 rest = name[len(r):]
992 if not rest:
994 if not rest:
993 return r, True
995 return r, True
994 depth = rest.count(os.sep)
996 depth = rest.count(os.sep)
995 if not r:
997 if not r:
996 if depth == 0 or not opts['nonrecursive']:
998 if depth == 0 or not opts['nonrecursive']:
997 return r, True
999 return r, True
998 elif rest[0] == os.sep:
1000 elif rest[0] == os.sep:
999 if depth == 1 or not opts['nonrecursive']:
1001 if depth == 1 or not opts['nonrecursive']:
1000 return r, True
1002 return r, True
1001 return None, False
1003 return None, False
1002 relname, ret = body(name)
1004 relname, ret = body(name)
1003 if ret:
1005 if ret:
1004 chosen[relname] = 1
1006 chosen[relname] = 1
1005 return ret
1007 return ret
1006
1008
1007 r = repo.update(node, False, True, choose, False)
1009 r = repo.update(node, False, True, choose, False)
1008 for n in relnames:
1010 for n in relnames:
1009 if n not in chosen:
1011 if n not in chosen:
1010 ui.warn('error: no matches for %s\n' % n)
1012 ui.warn('error: no matches for %s\n' % n)
1011 r = 1
1013 r = 1
1012 sys.stdout.flush()
1014 sys.stdout.flush()
1013 return r
1015 return r
1014
1016
1015 def root(ui, repo):
1017 def root(ui, repo):
1016 """print the root (top) of the current working dir"""
1018 """print the root (top) of the current working dir"""
1017 ui.write(repo.root + "\n")
1019 ui.write(repo.root + "\n")
1018
1020
1019 def serve(ui, repo, **opts):
1021 def serve(ui, repo, **opts):
1020 """export the repository via HTTP"""
1022 """export the repository via HTTP"""
1021
1023
1022 if opts["stdio"]:
1024 if opts["stdio"]:
1023 fin, fout = sys.stdin, sys.stdout
1025 fin, fout = sys.stdin, sys.stdout
1024 sys.stdout = sys.stderr
1026 sys.stdout = sys.stderr
1025
1027
1026 def getarg():
1028 def getarg():
1027 argline = fin.readline()[:-1]
1029 argline = fin.readline()[:-1]
1028 arg, l = argline.split()
1030 arg, l = argline.split()
1029 val = fin.read(int(l))
1031 val = fin.read(int(l))
1030 return arg, val
1032 return arg, val
1031 def respond(v):
1033 def respond(v):
1032 fout.write("%d\n" % len(v))
1034 fout.write("%d\n" % len(v))
1033 fout.write(v)
1035 fout.write(v)
1034 fout.flush()
1036 fout.flush()
1035
1037
1036 lock = None
1038 lock = None
1037
1039
1038 while 1:
1040 while 1:
1039 cmd = fin.readline()[:-1]
1041 cmd = fin.readline()[:-1]
1040 if cmd == '':
1042 if cmd == '':
1041 return
1043 return
1042 if cmd == "heads":
1044 if cmd == "heads":
1043 h = repo.heads()
1045 h = repo.heads()
1044 respond(" ".join(map(hg.hex, h)) + "\n")
1046 respond(" ".join(map(hg.hex, h)) + "\n")
1045 if cmd == "lock":
1047 if cmd == "lock":
1046 lock = repo.lock()
1048 lock = repo.lock()
1047 respond("")
1049 respond("")
1048 if cmd == "unlock":
1050 if cmd == "unlock":
1049 if lock:
1051 if lock:
1050 lock.release()
1052 lock.release()
1051 lock = None
1053 lock = None
1052 respond("")
1054 respond("")
1053 elif cmd == "branches":
1055 elif cmd == "branches":
1054 arg, nodes = getarg()
1056 arg, nodes = getarg()
1055 nodes = map(hg.bin, nodes.split(" "))
1057 nodes = map(hg.bin, nodes.split(" "))
1056 r = []
1058 r = []
1057 for b in repo.branches(nodes):
1059 for b in repo.branches(nodes):
1058 r.append(" ".join(map(hg.hex, b)) + "\n")
1060 r.append(" ".join(map(hg.hex, b)) + "\n")
1059 respond("".join(r))
1061 respond("".join(r))
1060 elif cmd == "between":
1062 elif cmd == "between":
1061 arg, pairs = getarg()
1063 arg, pairs = getarg()
1062 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
1064 pairs = [map(hg.bin, p.split("-")) for p in pairs.split(" ")]
1063 r = []
1065 r = []
1064 for b in repo.between(pairs):
1066 for b in repo.between(pairs):
1065 r.append(" ".join(map(hg.hex, b)) + "\n")
1067 r.append(" ".join(map(hg.hex, b)) + "\n")
1066 respond("".join(r))
1068 respond("".join(r))
1067 elif cmd == "changegroup":
1069 elif cmd == "changegroup":
1068 nodes = []
1070 nodes = []
1069 arg, roots = getarg()
1071 arg, roots = getarg()
1070 nodes = map(hg.bin, roots.split(" "))
1072 nodes = map(hg.bin, roots.split(" "))
1071
1073
1072 cg = repo.changegroup(nodes)
1074 cg = repo.changegroup(nodes)
1073 while 1:
1075 while 1:
1074 d = cg.read(4096)
1076 d = cg.read(4096)
1075 if not d:
1077 if not d:
1076 break
1078 break
1077 fout.write(d)
1079 fout.write(d)
1078
1080
1079 fout.flush()
1081 fout.flush()
1080
1082
1081 elif cmd == "addchangegroup":
1083 elif cmd == "addchangegroup":
1082 if not lock:
1084 if not lock:
1083 respond("not locked")
1085 respond("not locked")
1084 continue
1086 continue
1085 respond("")
1087 respond("")
1086
1088
1087 r = repo.addchangegroup(fin)
1089 r = repo.addchangegroup(fin)
1088 respond("")
1090 respond("")
1089
1091
1090 httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
1092 httpd = hgweb.create_server(repo.root, opts["name"], opts["templates"],
1091 opts["address"], opts["port"], opts["ipv6"],
1093 opts["address"], opts["port"], opts["ipv6"],
1092 opts['accesslog'], opts['errorlog'])
1094 opts['accesslog'], opts['errorlog'])
1093 if ui.verbose:
1095 if ui.verbose:
1094 addr, port = httpd.socket.getsockname()
1096 addr, port = httpd.socket.getsockname()
1095 if addr == '0.0.0.0':
1097 if addr == '0.0.0.0':
1096 addr = socket.gethostname()
1098 addr = socket.gethostname()
1097 else:
1099 else:
1098 try:
1100 try:
1099 addr = socket.gethostbyaddr(addr)[0]
1101 addr = socket.gethostbyaddr(addr)[0]
1100 except socket.error:
1102 except socket.error:
1101 pass
1103 pass
1102 if port != 80:
1104 if port != 80:
1103 ui.status('listening at http://%s:%d/\n' % (addr, port))
1105 ui.status('listening at http://%s:%d/\n' % (addr, port))
1104 else:
1106 else:
1105 ui.status('listening at http://%s/\n' % addr)
1107 ui.status('listening at http://%s/\n' % addr)
1106 httpd.serve_forever()
1108 httpd.serve_forever()
1107
1109
1108 def status(ui, repo, *pats, **opts):
1110 def status(ui, repo, *pats, **opts):
1109 '''show changed files in the working directory
1111 '''show changed files in the working directory
1110
1112
1111 M = modified
1113 M = modified
1112 A = added
1114 A = added
1113 R = removed
1115 R = removed
1114 ? = not tracked
1116 ? = not tracked
1115 '''
1117 '''
1116
1118
1117 cwd = repo.getcwd()
1119 cwd = repo.getcwd()
1118 files, matchfn = matchpats(repo, cwd, pats, opts)
1120 files, matchfn = matchpats(repo, cwd, pats, opts)
1119 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1121 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
1120 for n in repo.changes(files=files, match=matchfn)]
1122 for n in repo.changes(files=files, match=matchfn)]
1121
1123
1122 changetypes = [('modified', 'M', c),
1124 changetypes = [('modified', 'M', c),
1123 ('added', 'A', a),
1125 ('added', 'A', a),
1124 ('removed', 'R', d),
1126 ('removed', 'R', d),
1125 ('unknown', '?', u)]
1127 ('unknown', '?', u)]
1126
1128
1127 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1129 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
1128 or changetypes):
1130 or changetypes):
1129 for f in changes:
1131 for f in changes:
1130 ui.write("%s %s\n" % (char, f))
1132 ui.write("%s %s\n" % (char, f))
1131
1133
1132 def tag(ui, repo, name, rev=None, **opts):
1134 def tag(ui, repo, name, rev=None, **opts):
1133 """add a tag for the current tip or a given revision"""
1135 """add a tag for the current tip or a given revision"""
1134 if opts['text']:
1136 if opts['text']:
1135 ui.warn("Warning: -t and --text is deprecated,"
1137 ui.warn("Warning: -t and --text is deprecated,"
1136 " please use -m or --message instead.\n")
1138 " please use -m or --message instead.\n")
1137 if name == "tip":
1139 if name == "tip":
1138 ui.warn("abort: 'tip' is a reserved name!\n")
1140 ui.warn("abort: 'tip' is a reserved name!\n")
1139 return -1
1141 return -1
1140 if rev:
1142 if rev:
1141 r = hg.hex(repo.lookup(rev))
1143 r = hg.hex(repo.lookup(rev))
1142 else:
1144 else:
1143 r = hg.hex(repo.changelog.tip())
1145 r = hg.hex(repo.changelog.tip())
1144
1146
1145 if name.find(revrangesep) >= 0:
1147 if name.find(revrangesep) >= 0:
1146 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1148 ui.warn("abort: '%s' cannot be used in a tag name\n" % revrangesep)
1147 return -1
1149 return -1
1148
1150
1149 if opts['local']:
1151 if opts['local']:
1150 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1152 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
1151 return
1153 return
1152
1154
1153 (c, a, d, u) = repo.changes()
1155 (c, a, d, u) = repo.changes()
1154 for x in (c, a, d, u):
1156 for x in (c, a, d, u):
1155 if ".hgtags" in x:
1157 if ".hgtags" in x:
1156 ui.warn("abort: working copy of .hgtags is changed!\n")
1158 ui.warn("abort: working copy of .hgtags is changed!\n")
1157 ui.status("(please commit .hgtags manually)\n")
1159 ui.status("(please commit .hgtags manually)\n")
1158 return -1
1160 return -1
1159
1161
1160 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1162 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
1161 if repo.dirstate.state(".hgtags") == '?':
1163 if repo.dirstate.state(".hgtags") == '?':
1162 repo.add([".hgtags"])
1164 repo.add([".hgtags"])
1163
1165
1164 message = (opts['message'] or opts['text'] or
1166 message = (opts['message'] or opts['text'] or
1165 "Added tag %s for changeset %s" % (name, r))
1167 "Added tag %s for changeset %s" % (name, r))
1166 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1168 repo.commit([".hgtags"], message, opts['user'], opts['date'])
1167
1169
1168 def tags(ui, repo):
1170 def tags(ui, repo):
1169 """list repository tags"""
1171 """list repository tags"""
1170
1172
1171 l = repo.tagslist()
1173 l = repo.tagslist()
1172 l.reverse()
1174 l.reverse()
1173 for t, n in l:
1175 for t, n in l:
1174 try:
1176 try:
1175 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1177 r = "%5d:%s" % (repo.changelog.rev(n), hg.hex(n))
1176 except KeyError:
1178 except KeyError:
1177 r = " ?:?"
1179 r = " ?:?"
1178 ui.write("%-30s %s\n" % (t, r))
1180 ui.write("%-30s %s\n" % (t, r))
1179
1181
1180 def tip(ui, repo):
1182 def tip(ui, repo):
1181 """show the tip revision"""
1183 """show the tip revision"""
1182 n = repo.changelog.tip()
1184 n = repo.changelog.tip()
1183 show_changeset(ui, repo, changenode=n)
1185 show_changeset(ui, repo, changenode=n)
1184
1186
1185 def undo(ui, repo):
1187 def undo(ui, repo):
1186 """undo the last commit or pull
1188 """undo the last commit or pull
1187
1189
1188 Roll back the last pull or commit transaction on the
1190 Roll back the last pull or commit transaction on the
1189 repository, restoring the project to its earlier state.
1191 repository, restoring the project to its earlier state.
1190
1192
1191 This command should be used with care. There is only one level of
1193 This command should be used with care. There is only one level of
1192 undo and there is no redo.
1194 undo and there is no redo.
1193
1195
1194 This command is not intended for use on public repositories. Once
1196 This command is not intended for use on public repositories. Once
1195 a change is visible for pull by other users, undoing it locally is
1197 a change is visible for pull by other users, undoing it locally is
1196 ineffective.
1198 ineffective.
1197 """
1199 """
1198 repo.undo()
1200 repo.undo()
1199
1201
1200 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1202 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
1201 '''update or merge working directory
1203 '''update or merge working directory
1202
1204
1203 If there are no outstanding changes in the working directory and
1205 If there are no outstanding changes in the working directory and
1204 there is a linear relationship between the current version and the
1206 there is a linear relationship between the current version and the
1205 requested version, the result is the requested version.
1207 requested version, the result is the requested version.
1206
1208
1207 Otherwise the result is a merge between the contents of the
1209 Otherwise the result is a merge between the contents of the
1208 current working directory and the requested version. Files that
1210 current working directory and the requested version. Files that
1209 changed between either parent are marked as changed for the next
1211 changed between either parent are marked as changed for the next
1210 commit and a commit must be performed before any further updates
1212 commit and a commit must be performed before any further updates
1211 are allowed.
1213 are allowed.
1212 '''
1214 '''
1213 if branch:
1215 if branch:
1214 br = repo.branchlookup(branch=branch)
1216 br = repo.branchlookup(branch=branch)
1215 found = []
1217 found = []
1216 for x in br:
1218 for x in br:
1217 if branch in br[x]:
1219 if branch in br[x]:
1218 found.append(x)
1220 found.append(x)
1219 if len(found) > 1:
1221 if len(found) > 1:
1220 ui.warn("Found multiple heads for %s\n" % branch)
1222 ui.warn("Found multiple heads for %s\n" % branch)
1221 for x in found:
1223 for x in found:
1222 show_changeset(ui, repo, changenode=x, brinfo=br)
1224 show_changeset(ui, repo, changenode=x, brinfo=br)
1223 return 1
1225 return 1
1224 if len(found) == 1:
1226 if len(found) == 1:
1225 node = found[0]
1227 node = found[0]
1226 ui.warn("Using head %s for branch %s\n" % (hg.short(node), branch))
1228 ui.warn("Using head %s for branch %s\n" % (hg.short(node), branch))
1227 else:
1229 else:
1228 ui.warn("branch %s not found\n" % (branch))
1230 ui.warn("branch %s not found\n" % (branch))
1229 return 1
1231 return 1
1230 else:
1232 else:
1231 node = node and repo.lookup(node) or repo.changelog.tip()
1233 node = node and repo.lookup(node) or repo.changelog.tip()
1232 return repo.update(node, allow=merge, force=clean)
1234 return repo.update(node, allow=merge, force=clean)
1233
1235
1234 def verify(ui, repo):
1236 def verify(ui, repo):
1235 """verify the integrity of the repository"""
1237 """verify the integrity of the repository"""
1236 return repo.verify()
1238 return repo.verify()
1237
1239
1238 # Command options and aliases are listed here, alphabetically
1240 # Command options and aliases are listed here, alphabetically
1239
1241
1240 table = {
1242 table = {
1241 "^add":
1243 "^add":
1242 (add,
1244 (add,
1243 [('I', 'include', [], 'include path in search'),
1245 [('I', 'include', [], 'include path in search'),
1244 ('X', 'exclude', [], 'exclude path from search')],
1246 ('X', 'exclude', [], 'exclude path from search')],
1245 "hg add [OPTION]... [FILE]..."),
1247 "hg add [OPTION]... [FILE]..."),
1246 "addremove":
1248 "addremove":
1247 (addremove,
1249 (addremove,
1248 [('I', 'include', [], 'include path in search'),
1250 [('I', 'include', [], 'include path in search'),
1249 ('X', 'exclude', [], 'exclude path from search')],
1251 ('X', 'exclude', [], 'exclude path from search')],
1250 "hg addremove [OPTION]... [FILE]..."),
1252 "hg addremove [OPTION]... [FILE]..."),
1251 "^annotate":
1253 "^annotate":
1252 (annotate,
1254 (annotate,
1253 [('r', 'rev', '', 'revision'),
1255 [('r', 'rev', '', 'revision'),
1254 ('u', 'user', None, 'show user'),
1256 ('u', 'user', None, 'show user'),
1255 ('n', 'number', None, 'show revision number'),
1257 ('n', 'number', None, 'show revision number'),
1256 ('c', 'changeset', None, 'show changeset'),
1258 ('c', 'changeset', None, 'show changeset'),
1257 ('I', 'include', [], 'include path in search'),
1259 ('I', 'include', [], 'include path in search'),
1258 ('X', 'exclude', [], 'exclude path from search')],
1260 ('X', 'exclude', [], 'exclude path from search')],
1259 'hg annotate [OPTION]... FILE...'),
1261 'hg annotate [OPTION]... FILE...'),
1260 "cat":
1262 "cat":
1261 (cat,
1263 (cat,
1262 [('o', 'output', "", 'output to file')],
1264 [('o', 'output', "", 'output to file')],
1263 'hg cat [-o OUTFILE] FILE [REV]'),
1265 'hg cat [-o OUTFILE] FILE [REV]'),
1264 "^clone":
1266 "^clone":
1265 (clone,
1267 (clone,
1266 [('U', 'noupdate', None, 'skip update after cloning')],
1268 [('U', 'noupdate', None, 'skip update after cloning')],
1267 'hg clone [-U] SOURCE [DEST]'),
1269 'hg clone [-U] SOURCE [DEST]'),
1268 "^commit|ci":
1270 "^commit|ci":
1269 (commit,
1271 (commit,
1270 [('A', 'addremove', None, 'run add/remove during commit'),
1272 [('A', 'addremove', None, 'run add/remove during commit'),
1271 ('I', 'include', [], 'include path in search'),
1273 ('I', 'include', [], 'include path in search'),
1272 ('X', 'exclude', [], 'exclude path from search'),
1274 ('X', 'exclude', [], 'exclude path from search'),
1273 ('m', 'message', "", 'commit message'),
1275 ('m', 'message', "", 'commit message'),
1274 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1276 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1275 ('l', 'logfile', "", 'commit message file'),
1277 ('l', 'logfile', "", 'commit message file'),
1276 ('d', 'date', "", 'date code'),
1278 ('d', 'date', "", 'date code'),
1277 ('u', 'user', "", 'user')],
1279 ('u', 'user', "", 'user')],
1278 'hg commit [OPTION]... [FILE]...'),
1280 'hg commit [OPTION]... [FILE]...'),
1279 "copy": (copy, [], 'hg copy SOURCE DEST'),
1281 "copy": (copy, [], 'hg copy SOURCE DEST'),
1280 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1282 "debugcheckstate": (debugcheckstate, [], 'debugcheckstate'),
1281 "debugstate": (debugstate, [], 'debugstate'),
1283 "debugstate": (debugstate, [], 'debugstate'),
1282 "debugindex": (debugindex, [], 'debugindex FILE'),
1284 "debugindex": (debugindex, [], 'debugindex FILE'),
1283 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1285 "debugindexdot": (debugindexdot, [], 'debugindexdot FILE'),
1284 "debugwalk":
1286 "debugwalk":
1285 (debugwalk,
1287 (debugwalk,
1286 [('I', 'include', [], 'include path in search'),
1288 [('I', 'include', [], 'include path in search'),
1287 ('X', 'exclude', [], 'exclude path from search')],
1289 ('X', 'exclude', [], 'exclude path from search')],
1288 'debugwalk [OPTION]... [FILE]...'),
1290 'debugwalk [OPTION]... [FILE]...'),
1289 "^diff":
1291 "^diff":
1290 (diff,
1292 (diff,
1291 [('r', 'rev', [], 'revision'),
1293 [('r', 'rev', [], 'revision'),
1292 ('I', 'include', [], 'include path in search'),
1294 ('I', 'include', [], 'include path in search'),
1293 ('X', 'exclude', [], 'exclude path from search')],
1295 ('X', 'exclude', [], 'exclude path from search')],
1294 'hg diff [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1296 'hg diff [-I] [-X] [-r REV1 [-r REV2]] [FILE]...'),
1295 "^export":
1297 "^export":
1296 (export,
1298 (export,
1297 [('o', 'output', "", 'output to file')],
1299 [('o', 'output', "", 'output to file')],
1298 "hg export [-o OUTFILE] REV..."),
1300 "hg export [-o OUTFILE] REV..."),
1299 "forget":
1301 "forget":
1300 (forget,
1302 (forget,
1301 [('I', 'include', [], 'include path in search'),
1303 [('I', 'include', [], 'include path in search'),
1302 ('X', 'exclude', [], 'exclude path from search')],
1304 ('X', 'exclude', [], 'exclude path from search')],
1303 "hg forget [OPTION]... FILE..."),
1305 "hg forget [OPTION]... FILE..."),
1304 "heads":
1306 "heads":
1305 (heads,
1307 (heads,
1306 [('b', 'branches', None, 'find branch info')],
1308 [('b', 'branches', None, 'find branch info')],
1307 'hg [-b] heads'),
1309 'hg [-b] heads'),
1308 "help": (help_, [], 'hg help [COMMAND]'),
1310 "help": (help_, [], 'hg help [COMMAND]'),
1309 "identify|id": (identify, [], 'hg identify'),
1311 "identify|id": (identify, [], 'hg identify'),
1310 "import|patch":
1312 "import|patch":
1311 (import_,
1313 (import_,
1312 [('p', 'strip', 1, 'path strip'),
1314 [('p', 'strip', 1, 'path strip'),
1313 ('b', 'base', "", 'base path')],
1315 ('b', 'base', "", 'base path')],
1314 "hg import [-p NUM] [-b BASE] PATCH..."),
1316 "hg import [-p NUM] [-b BASE] PATCH..."),
1315 "incoming|in": (incoming, [], 'hg incoming [SOURCE]'),
1317 "incoming|in": (incoming, [], 'hg incoming [SOURCE]'),
1316 "^init": (init, [], 'hg init [DEST]'),
1318 "^init": (init, [], 'hg init [DEST]'),
1317 "locate":
1319 "locate":
1318 (locate,
1320 (locate,
1319 [('r', 'rev', '', 'revision'),
1321 [('r', 'rev', '', 'revision'),
1320 ('0', 'print0', None, 'end records with NUL'),
1322 ('0', 'print0', None, 'end records with NUL'),
1321 ('f', 'fullpath', None, 'print complete paths'),
1323 ('f', 'fullpath', None, 'print complete paths'),
1322 ('I', 'include', [], 'include path in search'),
1324 ('I', 'include', [], 'include path in search'),
1323 ('X', 'exclude', [], 'exclude path from search')],
1325 ('X', 'exclude', [], 'exclude path from search')],
1324 'hg locate [OPTION]... [PATTERN]...'),
1326 'hg locate [OPTION]... [PATTERN]...'),
1325 "^log|history":
1327 "^log|history":
1326 (log,
1328 (log,
1327 [('r', 'rev', [], 'revision'),
1329 [('r', 'rev', [], 'revision'),
1328 ('p', 'patch', None, 'show patch')],
1330 ('p', 'patch', None, 'show patch')],
1329 'hg log [-r REV1 [-r REV2]] [-p] [FILE]'),
1331 'hg log [-r REV1 [-r REV2]] [-p] [FILE]'),
1330 "manifest": (manifest, [], 'hg manifest [REV]'),
1332 "manifest": (manifest, [], 'hg manifest [REV]'),
1331 "outgoing|out": (outgoing, [], 'hg outgoing [DEST]'),
1333 "outgoing|out": (outgoing, [], 'hg outgoing [DEST]'),
1332 "parents": (parents, [], 'hg parents [REV]'),
1334 "parents": (parents, [], 'hg parents [REV]'),
1333 "paths": (paths, [], 'hg paths [NAME]'),
1335 "paths": (paths, [], 'hg paths [NAME]'),
1334 "^pull":
1336 "^pull":
1335 (pull,
1337 (pull,
1336 [('u', 'update', None, 'update working directory')],
1338 [('u', 'update', None, 'update working directory')],
1337 'hg pull [-u] [SOURCE]'),
1339 'hg pull [-u] [SOURCE]'),
1338 "^push":
1340 "^push":
1339 (push,
1341 (push,
1340 [('f', 'force', None, 'force push')],
1342 [('f', 'force', None, 'force push')],
1341 'hg push [-f] [DEST]'),
1343 'hg push [-f] [DEST]'),
1342 "rawcommit":
1344 "rawcommit":
1343 (rawcommit,
1345 (rawcommit,
1344 [('p', 'parent', [], 'parent'),
1346 [('p', 'parent', [], 'parent'),
1345 ('d', 'date', "", 'date code'),
1347 ('d', 'date', "", 'date code'),
1346 ('u', 'user', "", 'user'),
1348 ('u', 'user', "", 'user'),
1347 ('F', 'files', "", 'file list'),
1349 ('F', 'files', "", 'file list'),
1348 ('m', 'message', "", 'commit message'),
1350 ('m', 'message', "", 'commit message'),
1349 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1351 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1350 ('l', 'logfile', "", 'commit message file')],
1352 ('l', 'logfile', "", 'commit message file')],
1351 'hg rawcommit [OPTION]... [FILE]...'),
1353 'hg rawcommit [OPTION]... [FILE]...'),
1352 "recover": (recover, [], "hg recover"),
1354 "recover": (recover, [], "hg recover"),
1353 "^remove|rm": (remove, [], "hg remove FILE..."),
1355 "^remove|rm": (remove, [], "hg remove FILE..."),
1354 "^revert":
1356 "^revert":
1355 (revert,
1357 (revert,
1356 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1358 [("n", "nonrecursive", None, "don't recurse into subdirs"),
1357 ("r", "rev", "", "revision")],
1359 ("r", "rev", "", "revision")],
1358 "hg revert [-n] [-r REV] [NAME]..."),
1360 "hg revert [-n] [-r REV] [NAME]..."),
1359 "root": (root, [], "hg root"),
1361 "root": (root, [], "hg root"),
1360 "^serve":
1362 "^serve":
1361 (serve,
1363 (serve,
1362 [('A', 'accesslog', '', 'access log file'),
1364 [('A', 'accesslog', '', 'access log file'),
1363 ('E', 'errorlog', '', 'error log file'),
1365 ('E', 'errorlog', '', 'error log file'),
1364 ('p', 'port', 0, 'listen port'),
1366 ('p', 'port', 0, 'listen port'),
1365 ('a', 'address', '', 'interface address'),
1367 ('a', 'address', '', 'interface address'),
1366 ('n', 'name', "", 'repository name'),
1368 ('n', 'name', "", 'repository name'),
1367 ('', 'stdio', None, 'for remote clients'),
1369 ('', 'stdio', None, 'for remote clients'),
1368 ('t', 'templates', "", 'template map'),
1370 ('t', 'templates', "", 'template map'),
1369 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1371 ('6', 'ipv6', None, 'use IPv6 in addition to IPv4')],
1370 "hg serve [OPTION]..."),
1372 "hg serve [OPTION]..."),
1371 "^status":
1373 "^status":
1372 (status,
1374 (status,
1373 [('m', 'modified', None, 'show only modified files'),
1375 [('m', 'modified', None, 'show only modified files'),
1374 ('a', 'added', None, 'show only added files'),
1376 ('a', 'added', None, 'show only added files'),
1375 ('r', 'removed', None, 'show only removed files'),
1377 ('r', 'removed', None, 'show only removed files'),
1376 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1378 ('u', 'unknown', None, 'show only unknown (not tracked) files'),
1377 ('I', 'include', [], 'include path in search'),
1379 ('I', 'include', [], 'include path in search'),
1378 ('X', 'exclude', [], 'exclude path from search')],
1380 ('X', 'exclude', [], 'exclude path from search')],
1379 "hg status [OPTION]... [FILE]..."),
1381 "hg status [OPTION]... [FILE]..."),
1380 "tag":
1382 "tag":
1381 (tag,
1383 (tag,
1382 [('l', 'local', None, 'make the tag local'),
1384 [('l', 'local', None, 'make the tag local'),
1383 ('m', 'message', "", 'commit message'),
1385 ('m', 'message', "", 'commit message'),
1384 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1386 ('t', 'text', "", 'commit message (deprecated: use -m)'),
1385 ('d', 'date', "", 'date code'),
1387 ('d', 'date', "", 'date code'),
1386 ('u', 'user', "", 'user')],
1388 ('u', 'user', "", 'user')],
1387 'hg tag [OPTION]... NAME [REV]'),
1389 'hg tag [OPTION]... NAME [REV]'),
1388 "tags": (tags, [], 'hg tags'),
1390 "tags": (tags, [], 'hg tags'),
1389 "tip": (tip, [], 'hg tip'),
1391 "tip": (tip, [], 'hg tip'),
1390 "undo": (undo, [], 'hg undo'),
1392 "undo": (undo, [], 'hg undo'),
1391 "^update|up|checkout|co":
1393 "^update|up|checkout|co":
1392 (update,
1394 (update,
1393 [('b', 'branch', "", 'checkout the head of a specific branch'),
1395 [('b', 'branch', "", 'checkout the head of a specific branch'),
1394 ('m', 'merge', None, 'allow merging of conflicts'),
1396 ('m', 'merge', None, 'allow merging of conflicts'),
1395 ('C', 'clean', None, 'overwrite locally modified files')],
1397 ('C', 'clean', None, 'overwrite locally modified files')],
1396 'hg update [-b TAG] [-m] [-C] [REV]'),
1398 'hg update [-b TAG] [-m] [-C] [REV]'),
1397 "verify": (verify, [], 'hg verify'),
1399 "verify": (verify, [], 'hg verify'),
1398 "version": (show_version, [], 'hg version'),
1400 "version": (show_version, [], 'hg version'),
1399 }
1401 }
1400
1402
1401 globalopts = [('v', 'verbose', None, 'verbose mode'),
1403 globalopts = [('v', 'verbose', None, 'verbose mode'),
1402 ('', 'debug', None, 'debug mode'),
1404 ('', 'debug', None, 'debug mode'),
1403 ('q', 'quiet', None, 'quiet mode'),
1405 ('q', 'quiet', None, 'quiet mode'),
1404 ('', 'profile', None, 'profile'),
1406 ('', 'profile', None, 'profile'),
1407 ('C', 'cwd', '', 'change working directory'),
1405 ('R', 'repository', "", 'repository root directory'),
1408 ('R', 'repository', "", 'repository root directory'),
1406 ('', 'traceback', None, 'print traceback on exception'),
1409 ('', 'traceback', None, 'print traceback on exception'),
1407 ('y', 'noninteractive', None, 'run non-interactively'),
1410 ('y', 'noninteractive', None, 'run non-interactively'),
1408 ('', 'version', None, 'output version information and exit'),
1411 ('', 'version', None, 'output version information and exit'),
1409 ('', 'time', None, 'time how long the command takes'),
1412 ('', 'time', None, 'time how long the command takes'),
1410 ]
1413 ]
1411
1414
1412 norepo = "clone init version help debugindex debugindexdot paths"
1415 norepo = "clone init version help debugindex debugindexdot paths"
1413
1416
1414 def find(cmd):
1417 def find(cmd):
1415 for e in table.keys():
1418 for e in table.keys():
1416 if re.match("(%s)$" % e, cmd):
1419 if re.match("(%s)$" % e, cmd):
1417 return e, table[e]
1420 return e, table[e]
1418
1421
1419 raise UnknownCommand(cmd)
1422 raise UnknownCommand(cmd)
1420
1423
1421 class SignalInterrupt(Exception):
1424 class SignalInterrupt(Exception):
1422 """Exception raised on SIGTERM and SIGHUP."""
1425 """Exception raised on SIGTERM and SIGHUP."""
1423
1426
1424 def catchterm(*args):
1427 def catchterm(*args):
1425 raise SignalInterrupt
1428 raise SignalInterrupt
1426
1429
1427 def run():
1430 def run():
1428 sys.exit(dispatch(sys.argv[1:]))
1431 sys.exit(dispatch(sys.argv[1:]))
1429
1432
1430 class ParseError(Exception):
1433 class ParseError(Exception):
1431 """Exception raised on errors in parsing the command line."""
1434 """Exception raised on errors in parsing the command line."""
1432
1435
1433 def parse(args):
1436 def parse(args):
1434 options = {}
1437 options = {}
1435 cmdoptions = {}
1438 cmdoptions = {}
1436
1439
1437 try:
1440 try:
1438 args = fancyopts.fancyopts(args, globalopts, options)
1441 args = fancyopts.fancyopts(args, globalopts, options)
1439 except fancyopts.getopt.GetoptError, inst:
1442 except fancyopts.getopt.GetoptError, inst:
1440 raise ParseError(None, inst)
1443 raise ParseError(None, inst)
1441
1444
1442 if options["version"]:
1445 if options["version"]:
1443 return ("version", show_version, [], options, cmdoptions)
1446 return ("version", show_version, [], options, cmdoptions)
1444 elif not args:
1447 elif not args:
1445 return ("help", help_, ["shortlist"], options, cmdoptions)
1448 return ("help", help_, ["shortlist"], options, cmdoptions)
1446 else:
1449 else:
1447 cmd, args = args[0], args[1:]
1450 cmd, args = args[0], args[1:]
1448
1451
1449 i = find(cmd)[1]
1452 i = find(cmd)[1]
1450
1453
1451 # combine global options into local
1454 # combine global options into local
1452 c = list(i[1])
1455 c = list(i[1])
1453 for o in globalopts:
1456 for o in globalopts:
1454 c.append((o[0], o[1], options[o[1]], o[3]))
1457 c.append((o[0], o[1], options[o[1]], o[3]))
1455
1458
1456 try:
1459 try:
1457 args = fancyopts.fancyopts(args, c, cmdoptions)
1460 args = fancyopts.fancyopts(args, c, cmdoptions)
1458 except fancyopts.getopt.GetoptError, inst:
1461 except fancyopts.getopt.GetoptError, inst:
1459 raise ParseError(cmd, inst)
1462 raise ParseError(cmd, inst)
1460
1463
1461 # separate global options back out
1464 # separate global options back out
1462 for o in globalopts:
1465 for o in globalopts:
1463 n = o[1]
1466 n = o[1]
1464 options[n] = cmdoptions[n]
1467 options[n] = cmdoptions[n]
1465 del cmdoptions[n]
1468 del cmdoptions[n]
1466
1469
1467 return (cmd, i[0], args, options, cmdoptions)
1470 return (cmd, i[0], args, options, cmdoptions)
1468
1471
1469 def dispatch(args):
1472 def dispatch(args):
1470 signal.signal(signal.SIGTERM, catchterm)
1473 signal.signal(signal.SIGTERM, catchterm)
1471 try:
1474 try:
1472 signal.signal(signal.SIGHUP, catchterm)
1475 signal.signal(signal.SIGHUP, catchterm)
1473 except AttributeError:
1476 except AttributeError:
1474 pass
1477 pass
1475
1478
1476 try:
1479 try:
1477 cmd, func, args, options, cmdoptions = parse(args)
1480 cmd, func, args, options, cmdoptions = parse(args)
1478 except ParseError, inst:
1481 except ParseError, inst:
1479 u = ui.ui()
1482 u = ui.ui()
1480 if inst.args[0]:
1483 if inst.args[0]:
1481 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1484 u.warn("hg %s: %s\n" % (inst.args[0], inst.args[1]))
1482 help_(u, inst.args[0])
1485 help_(u, inst.args[0])
1483 else:
1486 else:
1484 u.warn("hg: %s\n" % inst.args[1])
1487 u.warn("hg: %s\n" % inst.args[1])
1485 help_(u, 'shortlist')
1488 help_(u, 'shortlist')
1486 sys.exit(-1)
1489 sys.exit(-1)
1487 except UnknownCommand, inst:
1490 except UnknownCommand, inst:
1488 u = ui.ui()
1491 u = ui.ui()
1489 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1492 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1490 help_(u, 'shortlist')
1493 help_(u, 'shortlist')
1491 sys.exit(1)
1494 sys.exit(1)
1492
1495
1496 if options['cwd']:
1497 try:
1498 os.chdir(options['cwd'])
1499 except OSError, inst:
1500 u = ui.ui()
1501 u.warn('abort: %s: %s\n' % (options['cwd'], inst.strerror))
1502 sys.exit(1)
1503
1493 if options["time"]:
1504 if options["time"]:
1494 def get_times():
1505 def get_times():
1495 t = os.times()
1506 t = os.times()
1496 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
1507 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
1497 t = (t[0], t[1], t[2], t[3], time.clock())
1508 t = (t[0], t[1], t[2], t[3], time.clock())
1498 return t
1509 return t
1499 s = get_times()
1510 s = get_times()
1500 def print_time():
1511 def print_time():
1501 t = get_times()
1512 t = get_times()
1502 u = ui.ui()
1513 u = ui.ui()
1503 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
1514 u.warn("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n" %
1504 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
1515 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
1505 atexit.register(print_time)
1516 atexit.register(print_time)
1506
1517
1507 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1518 u = ui.ui(options["verbose"], options["debug"], options["quiet"],
1508 not options["noninteractive"])
1519 not options["noninteractive"])
1509
1520
1510 try:
1521 try:
1511 try:
1522 try:
1512 if cmd not in norepo.split():
1523 if cmd not in norepo.split():
1513 path = options["repository"] or ""
1524 path = options["repository"] or ""
1514 repo = hg.repository(ui=u, path=path)
1525 repo = hg.repository(ui=u, path=path)
1515 d = lambda: func(u, repo, *args, **cmdoptions)
1526 d = lambda: func(u, repo, *args, **cmdoptions)
1516 else:
1527 else:
1517 d = lambda: func(u, *args, **cmdoptions)
1528 d = lambda: func(u, *args, **cmdoptions)
1518
1529
1519 if options['profile']:
1530 if options['profile']:
1520 import hotshot, hotshot.stats
1531 import hotshot, hotshot.stats
1521 prof = hotshot.Profile("hg.prof")
1532 prof = hotshot.Profile("hg.prof")
1522 r = prof.runcall(d)
1533 r = prof.runcall(d)
1523 prof.close()
1534 prof.close()
1524 stats = hotshot.stats.load("hg.prof")
1535 stats = hotshot.stats.load("hg.prof")
1525 stats.strip_dirs()
1536 stats.strip_dirs()
1526 stats.sort_stats('time', 'calls')
1537 stats.sort_stats('time', 'calls')
1527 stats.print_stats(40)
1538 stats.print_stats(40)
1528 return r
1539 return r
1529 else:
1540 else:
1530 return d()
1541 return d()
1531 except:
1542 except:
1532 if options['traceback']:
1543 if options['traceback']:
1533 traceback.print_exc()
1544 traceback.print_exc()
1534 raise
1545 raise
1535 except hg.RepoError, inst:
1546 except hg.RepoError, inst:
1536 u.warn("abort: ", inst, "!\n")
1547 u.warn("abort: ", inst, "!\n")
1537 except SignalInterrupt:
1548 except SignalInterrupt:
1538 u.warn("killed!\n")
1549 u.warn("killed!\n")
1539 except KeyboardInterrupt:
1550 except KeyboardInterrupt:
1540 try:
1551 try:
1541 u.warn("interrupted!\n")
1552 u.warn("interrupted!\n")
1542 except IOError, inst:
1553 except IOError, inst:
1543 if inst.errno == errno.EPIPE:
1554 if inst.errno == errno.EPIPE:
1544 if u.debugflag:
1555 if u.debugflag:
1545 u.warn("\nbroken pipe\n")
1556 u.warn("\nbroken pipe\n")
1546 else:
1557 else:
1547 raise
1558 raise
1548 except IOError, inst:
1559 except IOError, inst:
1549 if hasattr(inst, "code"):
1560 if hasattr(inst, "code"):
1550 u.warn("abort: %s\n" % inst)
1561 u.warn("abort: %s\n" % inst)
1551 elif hasattr(inst, "reason"):
1562 elif hasattr(inst, "reason"):
1552 u.warn("abort: error: %s\n" % inst.reason[1])
1563 u.warn("abort: error: %s\n" % inst.reason[1])
1553 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1564 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
1554 if u.debugflag: u.warn("broken pipe\n")
1565 if u.debugflag: u.warn("broken pipe\n")
1555 else:
1566 else:
1556 raise
1567 raise
1557 except OSError, inst:
1568 except OSError, inst:
1558 if hasattr(inst, "filename"):
1569 if hasattr(inst, "filename"):
1559 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1570 u.warn("abort: %s: %s\n" % (inst.strerror, inst.filename))
1560 else:
1571 else:
1561 u.warn("abort: %s\n" % inst.strerror)
1572 u.warn("abort: %s\n" % inst.strerror)
1562 except util.Abort, inst:
1573 except util.Abort, inst:
1563 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1574 u.warn('abort: ', inst.args[0] % inst.args[1:], '\n')
1564 sys.exit(1)
1575 sys.exit(1)
1565 except TypeError, inst:
1576 except TypeError, inst:
1566 # was this an argument error?
1577 # was this an argument error?
1567 tb = traceback.extract_tb(sys.exc_info()[2])
1578 tb = traceback.extract_tb(sys.exc_info()[2])
1568 if len(tb) > 2: # no
1579 if len(tb) > 2: # no
1569 raise
1580 raise
1570 u.debug(inst, "\n")
1581 u.debug(inst, "\n")
1571 u.warn("%s: invalid arguments\n" % cmd)
1582 u.warn("%s: invalid arguments\n" % cmd)
1572 help_(u, cmd)
1583 help_(u, cmd)
1573 except UnknownCommand, inst:
1584 except UnknownCommand, inst:
1574 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1585 u.warn("hg: unknown command '%s'\n" % inst.args[0])
1575 help_(u, 'shortlist')
1586 help_(u, 'shortlist')
1576
1587
1577 sys.exit(-1)
1588 sys.exit(-1)
@@ -1,87 +1,87 b''
1 adding fennel
1 adding fennel
2 adding fenugreek
2 adding fenugreek
3 adding fiddlehead
3 adding fiddlehead
4 adding glob:glob
4 adding glob:glob
5 adding beans/black
5 adding beans/black
6 adding beans/borlotti
6 adding beans/borlotti
7 adding beans/kidney
7 adding beans/kidney
8 adding beans/navy
8 adding beans/navy
9 adding beans/pinto
9 adding beans/pinto
10 adding beans/turtle
10 adding beans/turtle
11 adding mammals/skunk
11 adding mammals/skunk
12 adding mammals/Procyonidae/cacomistle
12 adding mammals/Procyonidae/cacomistle
13 adding mammals/Procyonidae/coatimundi
13 adding mammals/Procyonidae/coatimundi
14 adding mammals/Procyonidae/raccoon
14 adding mammals/Procyonidae/raccoon
15 f fennel fennel
15 f fennel fennel
16 f fenugreek fenugreek
16 f fenugreek fenugreek
17 f fiddlehead fiddlehead
17 f fiddlehead fiddlehead
18 f glob:glob glob:glob
18 f glob:glob glob:glob
19 f beans/black beans/black
19 f beans/black beans/black
20 f beans/borlotti beans/borlotti
20 f beans/borlotti beans/borlotti
21 f beans/kidney beans/kidney
21 f beans/kidney beans/kidney
22 f beans/navy beans/navy
22 f beans/navy beans/navy
23 f beans/pinto beans/pinto
23 f beans/pinto beans/pinto
24 f beans/turtle beans/turtle
24 f beans/turtle beans/turtle
25 f mammals/skunk mammals/skunk
25 f mammals/skunk mammals/skunk
26 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
26 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
27 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
27 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
28 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
28 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
29 f mammals/skunk skunk
29 f mammals/skunk skunk
30 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
30 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
31 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
31 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
32 f mammals/Procyonidae/raccoon Procyonidae/raccoon
32 f mammals/Procyonidae/raccoon Procyonidae/raccoon
33 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
33 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
34 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
34 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
35 f mammals/Procyonidae/raccoon Procyonidae/raccoon
35 f mammals/Procyonidae/raccoon Procyonidae/raccoon
36 f mammals/Procyonidae/cacomistle cacomistle
36 f mammals/Procyonidae/cacomistle cacomistle
37 f mammals/Procyonidae/coatimundi coatimundi
37 f mammals/Procyonidae/coatimundi coatimundi
38 f mammals/Procyonidae/raccoon raccoon
38 f mammals/Procyonidae/raccoon raccoon
39 f mammals/skunk ../skunk
39 f mammals/skunk ../skunk
40 f mammals/Procyonidae/cacomistle cacomistle
40 f mammals/Procyonidae/cacomistle cacomistle
41 f mammals/Procyonidae/coatimundi coatimundi
41 f mammals/Procyonidae/coatimundi coatimundi
42 f mammals/Procyonidae/raccoon raccoon
42 f mammals/Procyonidae/raccoon raccoon
43 f beans/black ../beans/black
43 f beans/black ../beans/black
44 f beans/borlotti ../beans/borlotti
44 f beans/borlotti ../beans/borlotti
45 f beans/kidney ../beans/kidney
45 f beans/kidney ../beans/kidney
46 f beans/navy ../beans/navy
46 f beans/navy ../beans/navy
47 f beans/pinto ../beans/pinto
47 f beans/pinto ../beans/pinto
48 f beans/turtle ../beans/turtle
48 f beans/turtle ../beans/turtle
49 f mammals/skunk skunk
49 f mammals/skunk skunk
50 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
50 f mammals/Procyonidae/cacomistle Procyonidae/cacomistle
51 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
51 f mammals/Procyonidae/coatimundi Procyonidae/coatimundi
52 f mammals/Procyonidae/raccoon Procyonidae/raccoon
52 f mammals/Procyonidae/raccoon Procyonidae/raccoon
53 f beans/black beans/black
53 f beans/black beans/black
54 f beans/borlotti beans/borlotti
54 f beans/borlotti beans/borlotti
55 f beans/kidney beans/kidney
55 f beans/kidney beans/kidney
56 f beans/navy beans/navy
56 f beans/navy beans/navy
57 f beans/pinto beans/pinto
57 f beans/pinto beans/pinto
58 f beans/turtle beans/turtle
58 f beans/turtle beans/turtle
59 f beans/black beans/black
59 f beans/black beans/black
60 f beans/borlotti beans/borlotti
60 f beans/borlotti beans/borlotti
61 f mammals/skunk mammals/skunk
61 f mammals/skunk mammals/skunk
62 f mammals/skunk mammals/skunk
62 f mammals/skunk mammals/skunk
63 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
63 f mammals/Procyonidae/cacomistle mammals/Procyonidae/cacomistle
64 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
64 f mammals/Procyonidae/coatimundi mammals/Procyonidae/coatimundi
65 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
65 f mammals/Procyonidae/raccoon mammals/Procyonidae/raccoon
66 abort: .. not under repository root
66 abort: .. not under repository root
67 abort: beans/../.. not under repository root
67 abort: beans/../.. not under repository root
68 f fennel fennel
68 f fennel fennel
69 f fenugreek fenugreek
69 f fenugreek fenugreek
70 f fiddlehead fiddlehead
70 f fiddlehead fiddlehead
71 f glob:glob glob:glob
71 f glob:glob glob:glob
72 f fenugreek fenugreek
72 f fenugreek fenugreek
73 f glob:glob glob:glob
73 f glob:glob glob:glob
74 f beans/black beans/black
74 f beans/black beans/black
75 f mammals/skunk mammals/skunk
75 f mammals/skunk mammals/skunk
76 f beans/black beans/black
76 f beans/black beans/black
77 f beans/black beans/black
77 f beans/black beans/black
78 f beans/borlotti beans/borlotti
78 f beans/borlotti beans/borlotti
79 f beans/kidney beans/kidney
79 f beans/kidney beans/kidney
80 f beans/navy beans/navy
80 f beans/navy beans/navy
81 f beans/pinto beans/pinto
81 f beans/pinto beans/pinto
82 f beans/turtle beans/turtle
82 f beans/turtle beans/turtle
83 NOEXIST: No such file or directory
83 NOEXIST: No such file or directory
84 fifo: unsupported file type (type is fifo)
84 fifo: unsupported file type (type is fifo)
85 m fenugreek fenugreek
85 m fenugreek fenugreek exact
86 m fenugreek fenugreek
86 m fenugreek fenugreek exact
87 f new new
87 f new new exact
General Comments 0
You need to be logged in to leave comments. Login now