##// END OF EJS Templates
Merge with crew
Matt Mackall -
r3097:fe9b13e3 merge default
parent child Browse files
Show More
@@ -0,0 +1,179 b''
1 # churn.py - create a graph showing who changed the most lines
2 #
3 # Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
4 #
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
7 #
8 #
9 # Aliases map file format is simple one alias per line in the following
10 # format:
11 #
12 # <alias email> <actual email>
13
14 from mercurial.demandload import *
15 from mercurial.i18n import gettext as _
16 demandload(globals(), 'time sys signal os')
17 demandload(globals(), 'mercurial:hg,mdiff,fancyopts,cmdutil,ui,util,templater,node')
18
19 def __gather(ui, repo, node1, node2):
20 def dirtywork(f, mmap1, mmap2):
21 lines = 0
22
23 to = mmap1 and repo.file(f).read(mmap1[f]) or None
24 tn = mmap2 and repo.file(f).read(mmap2[f]) or None
25
26 diff = mdiff.unidiff(to, "", tn, "", f).split("\n")
27
28 for line in diff:
29 if not line:
30 continue # skip EOF
31 if line.startswith(" "):
32 continue # context line
33 if line.startswith("--- ") or line.startswith("+++ "):
34 continue # begining of diff
35 if line.startswith("@@ "):
36 continue # info line
37
38 # changed lines
39 lines += 1
40
41 return lines
42
43 ##
44
45 lines = 0
46
47 changes = repo.status(node1, node2, None, util.always)[:5]
48
49 modified, added, removed, deleted, unknown = changes
50
51 who = repo.changelog.read(node2)[1]
52 who = templater.email(who) # get the email of the person
53
54 mmap1 = repo.manifest.read(repo.changelog.read(node1)[0])
55 mmap2 = repo.manifest.read(repo.changelog.read(node2)[0])
56 for f in modified:
57 lines += dirtywork(f, mmap1, mmap2)
58
59 for f in added:
60 lines += dirtywork(f, None, mmap2)
61
62 for f in removed:
63 lines += dirtywork(f, mmap1, None)
64
65 for f in deleted:
66 lines += dirtywork(f, mmap1, mmap2)
67
68 for f in unknown:
69 lines += dirtywork(f, mmap1, mmap2)
70
71 return (who, lines)
72
73 def gather_stats(ui, repo, amap, revs=None, progress=False):
74 stats = {}
75
76 cl = repo.changelog
77
78 if not revs:
79 revs = range(0, cl.count())
80
81 nr_revs = len(revs)
82 cur_rev = 0
83
84 for rev in revs:
85 cur_rev += 1 # next revision
86
87 node2 = cl.node(rev)
88 node1 = cl.parents(node2)[0]
89
90 if cl.parents(node2)[1] != node.nullid:
91 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
92 continue
93
94 who, lines = __gather(ui, repo, node1, node2)
95
96 # remap the owner if possible
97 if amap.has_key(who):
98 ui.note("using '%s' alias for '%s'\n" % (amap[who], who))
99 who = amap[who]
100
101 if not stats.has_key(who):
102 stats[who] = 0
103 stats[who] += lines
104
105 ui.note("rev %d: %d lines by %s\n" % (rev, lines, who))
106
107 if progress:
108 if int(100.0*(cur_rev - 1)/nr_revs) < int(100.0*cur_rev/nr_revs):
109 ui.write("%d%%.." % (int(100.0*cur_rev/nr_revs),))
110 sys.stdout.flush()
111
112 if progress:
113 ui.write("done\n")
114 sys.stdout.flush()
115
116 return stats
117
118 def churn(ui, repo, **opts):
119 "Graphs the number of lines changed"
120
121 def pad(s, l):
122 if len(s) < l:
123 return s + " " * (l-len(s))
124 return s[0:l]
125
126 def graph(n, maximum, width, char):
127 n = int(n * width / float(maximum))
128
129 return char * (n)
130
131 def get_aliases(f):
132 aliases = {}
133
134 for l in f.readlines():
135 l = l.strip()
136 alias, actual = l.split(" ")
137 aliases[alias] = actual
138
139 return aliases
140
141 amap = {}
142 aliases = opts.get('aliases')
143 if aliases:
144 try:
145 f = open(aliases,"r")
146 except OSError, e:
147 print "Error: " + e
148 return
149
150 amap = get_aliases(f)
151 f.close()
152
153 revs = [int(r) for r in cmdutil.revrange(ui, repo, opts['rev'])]
154 revs.sort()
155 stats = gather_stats(ui, repo, amap, revs, opts.get('progress'))
156
157 # make a list of tuples (name, lines) and sort it in descending order
158 ordered = stats.items()
159 ordered.sort(lambda x, y: cmp(y[1], x[1]))
160
161 maximum = ordered[0][1]
162
163 ui.note("Assuming 80 character terminal\n")
164 width = 80 - 1
165
166 for i in ordered:
167 person = i[0]
168 lines = i[1]
169 print "%s %6d %s" % (pad(person, 20), lines,
170 graph(lines, maximum, width - 20 - 1 - 6 - 2 - 2, '*'))
171
172 cmdtable = {
173 "churn":
174 (churn,
175 [('r', 'rev', [], _('limit statistics to the specified revisions')),
176 ('', 'aliases', '', _('file with email aliases')),
177 ('', 'progress', None, _('show progress'))],
178 'hg churn [-r revision range] [-a file] [--progress]'),
179 }
@@ -0,0 +1,281 b''
1 ;;; mq.el --- Emacs support for Mercurial Queues
2
3 ;; Copyright (C) 2006 Bryan O'Sullivan
4
5 ;; Author: Bryan O'Sullivan <bos@serpentine.com>
6
7 ;; mq.el is free software; you can redistribute it and/or modify it
8 ;; under the terms of version 2 of the GNU General Public License as
9 ;; published by the Free Software Foundation.
10
11 ;; mq.el is distributed in the hope that it will be useful, but
12 ;; WITHOUT ANY WARRANTY; without even the implied warranty of
13 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 ;; General Public License for more details.
15
16 ;; You should have received a copy of the GNU General Public License
17 ;; along with mq.el, GNU Emacs, or XEmacs; see the file COPYING (`C-h
18 ;; C-l'). If not, write to the Free Software Foundation, Inc., 59
19 ;; Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20
21 (require 'mercurial)
22
23
24 (defcustom mq-mode-hook nil
25 "Hook run when a buffer enters mq-mode."
26 :type 'sexp
27 :group 'mercurial)
28
29 (defcustom mq-global-prefix "\C-cq"
30 "The global prefix for Mercurial Queues keymap bindings."
31 :type 'sexp
32 :group 'mercurial)
33
34 (defcustom mq-edit-mode-hook nil
35 "Hook run after a buffer is populated to edit a patch description."
36 :type 'sexp
37 :group 'mercurial)
38
39
40 ;;; Internal variables.
41
42 (defvar mq-patch-history nil)
43
44 (defvar mq-prev-buffer nil)
45 (make-variable-buffer-local 'mq-prev-buffer)
46 (put 'mq-prev-buffer 'permanent-local t)
47
48
49 ;;; Global keymap.
50
51 (defvar mq-global-map (make-sparse-keymap))
52 (fset 'mq-global-map mq-global-map)
53 (global-set-key mq-global-prefix 'mq-global-map)
54 (define-key mq-global-map "." 'mq-push)
55 (define-key mq-global-map ">" 'mq-push-all)
56 (define-key mq-global-map "," 'mq-pop)
57 (define-key mq-global-map "<" 'mq-pop-all)
58 (define-key mq-global-map "r" 'mq-refresh)
59 (define-key mq-global-map "e" 'mq-refresh-edit)
60 (define-key mq-global-map "n" 'mq-next)
61 (define-key mq-global-map "p" 'mq-previous)
62 (define-key mq-global-map "t" 'mq-top)
63
64
65 ;;; Refresh edit mode keymap.
66
67 (defvar mq-edit-mode-map (make-sparse-keymap))
68 (define-key mq-edit-mode-map "\C-c\C-c" 'mq-edit-finish)
69 (define-key mq-edit-mode-map "\C-c\C-k" 'mq-edit-kill)
70
71
72 ;;; Helper functions.
73
74 (defun mq-read-patch-name (&optional source prompt)
75 "Read a patch name to use with a command.
76 May return nil, meaning \"use the default\"."
77 (let ((patches (split-string
78 (hg-chomp (hg-run0 (or source "qseries"))) "\n")))
79 (when current-prefix-arg
80 (completing-read (format "Patch%s: " (or prompt ""))
81 (map 'list 'cons patches patches)
82 nil
83 nil
84 nil
85 'mq-patch-history))))
86
87 (defun mq-refresh-buffers (root)
88 (save-excursion
89 (dolist (buf (hg-buffers-visiting-repo root))
90 (when (not (verify-visited-file-modtime buf))
91 (set-buffer buf)
92 (let ((ctx (hg-buffer-context)))
93 (message "Refreshing %s..." (buffer-name))
94 (revert-buffer t t t)
95 (hg-restore-context ctx)
96 (message "Refreshing %s...done" (buffer-name))))))
97 (hg-update-mode-lines root))
98
99 (defun mq-last-line ()
100 (goto-char (point-max))
101 (beginning-of-line)
102 (when (looking-at "^$")
103 (forward-line -1))
104 (let ((bol (point)))
105 (end-of-line)
106 (let ((line (buffer-substring bol (point))))
107 (when (> (length line) 0)
108 line))))
109
110 (defun mq-push (&optional patch)
111 "Push patches until PATCH is reached.
112 If PATCH is nil, push at most one patch."
113 (interactive (list (mq-read-patch-name "qunapplied" " to push")))
114 (let ((root (hg-root))
115 (prev-buf (current-buffer))
116 last-line ok)
117 (unless root
118 (error "Cannot push outside a repository!"))
119 (hg-sync-buffers root)
120 (let ((buf-name (format "MQ: Push %s" (or patch "next patch"))))
121 (kill-buffer (get-buffer-create buf-name))
122 (split-window-vertically)
123 (other-window 1)
124 (switch-to-buffer (get-buffer-create buf-name))
125 (cd root)
126 (message "Pushing...")
127 (setq ok (= 0 (apply 'call-process (hg-binary) nil t t "qpush"
128 (if patch (list patch))))
129 last-line (mq-last-line))
130 (let ((lines (count-lines (point-min) (point-max))))
131 (if (and (equal lines 2) (string-match "Now at:" last-line))
132 (progn
133 (kill-buffer (current-buffer))
134 (delete-window))
135 (hg-view-mode prev-buf))))
136 (mq-refresh-buffers root)
137 (sit-for 0)
138 (when last-line
139 (if ok
140 (message "Pushing... %s" last-line)
141 (error "Pushing... %s" last-line)))))
142
143 (defun mq-push-all ()
144 "Push patches until all are applied."
145 (interactive)
146 (mq-push "-a"))
147
148 (defun mq-pop (&optional patch)
149 "Pop patches until PATCH is reached.
150 If PATCH is nil, pop at most one patch."
151 (interactive (list (mq-read-patch-name "qapplied" " to pop to")))
152 (let ((root (hg-root))
153 last-line ok)
154 (unless root
155 (error "Cannot pop outside a repository!"))
156 (hg-sync-buffers root)
157 (set-buffer (generate-new-buffer "qpop"))
158 (cd root)
159 (message "Popping...")
160 (setq ok (= 0 (apply 'call-process (hg-binary) nil t t "qpop"
161 (if patch (list patch))))
162 last-line (mq-last-line))
163 (kill-buffer (current-buffer))
164 (mq-refresh-buffers root)
165 (sit-for 0)
166 (when last-line
167 (if ok
168 (message "Popping... %s" last-line)
169 (error "Popping... %s" last-line)))))
170
171 (defun mq-pop-all ()
172 "Push patches until none are applied."
173 (interactive)
174 (mq-pop "-a"))
175
176 (defun mq-refresh-internal (root &rest args)
177 (hg-sync-buffers root)
178 (let ((patch (mq-patch-info "qtop")))
179 (message "Refreshing %s..." patch)
180 (let ((ret (apply 'hg-run "qrefresh" args)))
181 (if (equal (car ret) 0)
182 (message "Refreshing %s... done." patch)
183 (error "Refreshing %s... %s" patch (hg-chomp (cdr ret)))))))
184
185 (defun mq-refresh ()
186 "Refresh the topmost applied patch."
187 (interactive)
188 (let ((root (hg-root)))
189 (unless root
190 (error "Cannot refresh outside of a repository!"))
191 (mq-refresh-internal root)))
192
193 (defun mq-patch-info (cmd &optional msg)
194 (let* ((ret (hg-run cmd))
195 (info (hg-chomp (cdr ret))))
196 (if (equal (car ret) 0)
197 (if msg
198 (message "%s patch: %s" msg info)
199 info)
200 (error "%s" info))))
201
202 (defun mq-top ()
203 "Print the name of the topmost applied patch."
204 (interactive)
205 (mq-patch-info "qtop" "Top"))
206
207 (defun mq-next ()
208 "Print the name of the next patch to be pushed."
209 (interactive)
210 (mq-patch-info "qnext" "Next"))
211
212 (defun mq-previous ()
213 "Print the name of the first patch below the topmost applied patch.
214 This would become the active patch if popped to."
215 (interactive)
216 (mq-patch-info "qprev" "Previous"))
217
218 (defun mq-edit-finish ()
219 "Finish editing the description of this patch, and refresh the patch."
220 (interactive)
221 (unless (equal (mq-patch-info "qtop") mq-top)
222 (error "Topmost patch has changed!"))
223 (hg-sync-buffers hg-root)
224 (mq-refresh-internal hg-root "-m" (buffer-substring (point-min) (point-max)))
225 (let ((buf mq-prev-buffer))
226 (kill-buffer nil)
227 (switch-to-buffer buf)))
228
229 (defun mq-edit-kill ()
230 "Kill the edit currently being prepared."
231 (interactive)
232 (when (or (not (buffer-modified-p)) (y-or-n-p "Really kill this edit? "))
233 (let ((buf mq-prev-buffer))
234 (kill-buffer nil)
235 (switch-to-buffer buf))))
236
237 (defun mq-edit-mode ()
238 "Mode for editing the description of a patch.
239
240 Key bindings
241 ------------
242 \\[mq-edit-finish] use this description
243 \\[mq-edit-kill] abandon this description"
244 (interactive)
245 (use-local-map mq-edit-mode-map)
246 (set-syntax-table text-mode-syntax-table)
247 (setq local-abbrev-table text-mode-abbrev-table
248 major-mode 'mq-edit-mode
249 mode-name "MQ-Edit")
250 (set-buffer-modified-p nil)
251 (setq buffer-undo-list nil)
252 (run-hooks 'text-mode-hook 'mq-edit-mode-hook))
253
254 (defun mq-refresh-edit ()
255 "Refresh the topmost applied patch, editing the patch description."
256 (interactive)
257 (while mq-prev-buffer
258 (set-buffer mq-prev-buffer))
259 (let ((root (hg-root))
260 (prev-buffer (current-buffer))
261 (patch (mq-patch-info "qtop")))
262 (hg-sync-buffers root)
263 (let ((buf-name (format "*MQ: Edit description of %s*" patch)))
264 (switch-to-buffer (get-buffer-create buf-name))
265 (when (= (point-min) (point-max))
266 (set (make-local-variable 'hg-root) root)
267 (set (make-local-variable 'mq-top) patch)
268 (setq mq-prev-buffer prev-buffer)
269 (insert (hg-run0 "qheader"))
270 (goto-char (point-min)))
271 (mq-edit-mode)
272 (cd root)))
273 (message "Type `C-c C-c' to finish editing and refresh the patch."))
274
275
276 (provide 'mq)
277
278
279 ;;; Local Variables:
280 ;;; prompt-to-byte-compile: nil
281 ;;; end:
@@ -0,0 +1,9 b''
1 #!/bin/sh
2
3 echo 'syntax error' > badext.py
4 abspath=`pwd`/badext.py
5
6 echo '[extensions]' >> $HGRCPATH
7 echo "badext = $abspath" >> $HGRCPATH
8
9 hg -q help help
@@ -0,0 +1,4 b''
1 *** failed to import extension badext: invalid syntax (badext.py, line 1)
2 hg help [COMMAND]
3
4 show help for a command, extension, or list of commands
@@ -0,0 +1,34 b''
1 #!/bin/sh
2 #
3 # test for branch handling
4 #
5 # XXX: need more tests
6
7 hg init
8 echo a > a
9 echo b > b
10 hg ci -A -m 0 -d "1000000 0"
11 echo aa > a
12 echo bb > b
13 hg ci -m 1 -d "1000000 0"
14 hg tag -l foo
15 hg update 0
16 hg parents -b
17
18 # test update
19 hg update -b foo
20 hg parents
21
22 # test merge
23 hg update 0
24 echo c > c
25 hg ci -A -m 0.0 -d "1000000 0"
26 hg merge -b foo
27 hg parents -b
28
29 # re-test with more branches
30 hg update -C 0
31 echo d > d
32 hg ci -A -m 0.0 -d "1000000 0"
33 hg merge -b foo
34 hg parents -b
@@ -0,0 +1,55 b''
1 adding a
2 adding b
3 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
4 changeset: 0:b544c4ac4389
5 user: test
6 date: Mon Jan 12 13:46:40 1970 +0000
7 summary: 0
8
9 Using head f4ac749470f2 for branch foo
10 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
11 changeset: 1:f4ac749470f2
12 tag: foo
13 tag: tip
14 user: test
15 date: Mon Jan 12 13:46:40 1970 +0000
16 summary: 1
17
18 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
19 adding c
20 Using head f4ac749470f2 for branch foo
21 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
22 (branch merge, don't forget to commit)
23 changeset: 2:1505d56ee00e
24 tag: tip
25 parent: 0:b544c4ac4389
26 user: test
27 date: Mon Jan 12 13:46:40 1970 +0000
28 summary: 0.0
29
30 changeset: 1:f4ac749470f2
31 tag: foo
32 branch: foo
33 user: test
34 date: Mon Jan 12 13:46:40 1970 +0000
35 summary: 1
36
37 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
38 adding d
39 Using head f4ac749470f2 for branch foo
40 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
41 (branch merge, don't forget to commit)
42 changeset: 3:53b72df12ae5
43 tag: tip
44 parent: 0:b544c4ac4389
45 user: test
46 date: Mon Jan 12 13:46:40 1970 +0000
47 summary: 0.0
48
49 changeset: 1:f4ac749470f2
50 tag: foo
51 branch: foo
52 user: test
53 date: Mon Jan 12 13:46:40 1970 +0000
54 summary: 1
55
@@ -0,0 +1,35 b''
1 #!/bin/sh
2
3 echo "[extensions]" >> $HGRCPATH
4 echo "mq=" >> $HGRCPATH
5
6 hg init a
7 cd a
8
9 echo 'base' > base
10 hg ci -Ambase -d '1 0'
11
12 hg qnew a
13 hg qnew b
14 hg qnew c
15
16 hg qdel c
17 hg qpop
18 hg qdel c
19 hg qseries
20 ls .hg/patches
21 hg qpop
22 hg qdel -k b
23 ls .hg/patches
24 hg qdel -f a
25 hg qapplied
26 hg log --template '{rev} {desc}\n'
27
28 hg qnew d
29 hg qnew e
30 hg qnew f
31
32 hg qdel -f e
33 hg qdel -f d e
34 hg qapplied
35 hg log --template '{rev} {desc}\n'
@@ -0,0 +1,23 b''
1 adding base
2 abort: cannot delete applied patch c
3 Now at: b
4 a
5 b
6 a
7 b
8 series
9 status
10 Now at: a
11 a
12 b
13 series
14 status
15 1 New patch: a
16 0 base
17 abort: patch e not at base
18 f
19 4 New patch: f
20 3 New patch: e
21 2 New patch: d
22 1 New patch: a
23 0 base
@@ -0,0 +1,25 b''
1 #!/bin/sh
2
3 echo "[extensions]" >> $HGRCPATH
4 echo "mq=" >> $HGRCPATH
5
6 hg init a
7 cd a
8
9 echo 'base' > base
10 hg ci -Ambase -d '1 0'
11
12 hg qnew -mmqbase mqbase
13 hg qrename mqbase renamed
14 mkdir .hg/patches/foo
15 hg qrename renamed foo
16 hg qseries
17 ls .hg/patches/foo
18 mkdir .hg/patches/bar
19 hg qrename foo/renamed bar
20 hg qseries
21 ls .hg/patches/bar
22 hg qrename bar/renamed baz
23 hg qseries
24 ls .hg/patches/baz
25
@@ -0,0 +1,7 b''
1 adding base
2 foo/renamed
3 renamed
4 bar/renamed
5 renamed
6 baz
7 .hg/patches/baz
@@ -0,0 +1,18 b''
1 #!/bin/sh
2
3 hg init
4
5 echo a > a
6 hg ci -d '0 0' -Ama
7
8 hg an a
9
10 echo "[ui]" >> $HGRCPATH
11 echo "strict=True" >> $HGRCPATH
12
13 hg an a
14 hg annotate a
15
16 echo % should succeed - up is an alias, not an abbreviation
17
18 hg up
@@ -0,0 +1,26 b''
1 adding a
2 0: a
3 hg: unknown command 'an'
4 Mercurial Distributed SCM
5
6 basic commands (use "hg help" for the full list or option "-v" for details):
7
8 add add the specified files on the next commit
9 annotate show changeset information per file line
10 clone make a copy of an existing repository
11 commit commit the specified files or all outstanding changes
12 diff diff repository (or selected files)
13 export dump the header and diffs for one or more changesets
14 init create a new repository in the given directory
15 log show revision history of entire repository or files
16 parents show the parents of the working dir or revision
17 pull pull changes from the specified source
18 push push changes to the specified destination
19 remove remove the specified files on the next commit
20 revert revert files or dirs to their states as of some revision
21 serve export the repository via HTTP
22 status show changed files in the working directory
23 update update or merge working directory
24 0: a
25 % should succeed - up is an alias, not an abbreviation
26 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
@@ -0,0 +1,112 b''
1 #!/usr/bin/env python
2 # Since it's not easy to write a test that portably deals
3 # with files from different users/groups, we cheat a bit by
4 # monkey-patching some functions in the util module
5
6 import os
7 from mercurial import ui, util
8
9 hgrc = os.environ['HGRCPATH']
10
11 def testui(user='foo', group='bar', tusers=(), tgroups=(),
12 cuser='foo', cgroup='bar'):
13 # user, group => owners of the file
14 # tusers, tgroups => trusted users/groups
15 # cuser, cgroup => user/group of the current process
16
17 # write a global hgrc with the list of trusted users/groups and
18 # some setting so that we can be sure it was read
19 f = open(hgrc, 'w')
20 f.write('[paths]\n')
21 f.write('global = /some/path\n\n')
22
23 if tusers or tgroups:
24 f.write('[trusted]\n')
25 if tusers:
26 f.write('users = %s\n' % ', '.join(tusers))
27 if tgroups:
28 f.write('groups = %s\n' % ', '.join(tgroups))
29 f.close()
30
31 # override the functions that give names to uids and gids
32 def username(uid=None):
33 if uid is None:
34 return cuser
35 return user
36 util.username = username
37
38 def groupname(gid=None):
39 if gid is None:
40 return 'bar'
41 return group
42 util.groupname = groupname
43
44 # try to read everything
45 #print '# File belongs to user %s, group %s' % (user, group)
46 #print '# trusted users = %s; trusted groups = %s' % (tusers, tgroups)
47 kind = ('different', 'same')
48 who = ('', 'user', 'group', 'user and the group')
49 trusted = who[(user in tusers) + 2*(group in tgroups)]
50 if trusted:
51 trusted = ', but we trust the ' + trusted
52 print '# %s user, %s group%s' % (kind[user == cuser], kind[group == cgroup],
53 trusted)
54
55 parentui = ui.ui()
56 u = ui.ui(parentui=parentui)
57 u.readconfig('.hg/hgrc')
58 for name, path in u.configitems('paths'):
59 print name, '=', path
60 print
61
62 return u
63
64 os.mkdir('repo')
65 os.chdir('repo')
66 os.mkdir('.hg')
67 f = open('.hg/hgrc', 'w')
68 f.write('[paths]\n')
69 f.write('local = /another/path\n\n')
70 f.close()
71
72 #print '# Everything is run by user foo, group bar\n'
73
74 # same user, same group
75 testui()
76 # same user, different group
77 testui(group='def')
78 # different user, same group
79 testui(user='abc')
80 # ... but we trust the group
81 testui(user='abc', tgroups=['bar'])
82 # different user, different group
83 testui(user='abc', group='def')
84 # ... but we trust the user
85 testui(user='abc', group='def', tusers=['abc'])
86 # ... but we trust the group
87 testui(user='abc', group='def', tgroups=['def'])
88 # ... but we trust the user and the group
89 testui(user='abc', group='def', tusers=['abc'], tgroups=['def'])
90 # ... but we trust all users
91 print '# we trust all users'
92 testui(user='abc', group='def', tusers=['*'])
93 # ... but we trust all groups
94 print '# we trust all groups'
95 testui(user='abc', group='def', tgroups=['*'])
96 # ... but we trust the whole universe
97 print '# we trust all users and groups'
98 testui(user='abc', group='def', tusers=['*'], tgroups=['*'])
99 # ... check that users and groups are in different namespaces
100 print "# we don't get confused by users and groups with the same name"
101 testui(user='abc', group='def', tusers=['def'], tgroups=['abc'])
102 # ... lists of user names work
103 print "# list of user names"
104 testui(user='abc', group='def', tusers=['foo', 'xyz', 'abc', 'bleh'],
105 tgroups=['bar', 'baz', 'qux'])
106 # ... lists of group names work
107 print "# list of group names"
108 testui(user='abc', group='def', tusers=['foo', 'xyz', 'bleh'],
109 tgroups=['bar', 'def', 'baz', 'qux'])
110
111 print "# Can't figure out the name of the user running this process"
112 testui(user='abc', group='def', cuser=None)
@@ -0,0 +1,67 b''
1 # same user, same group
2 global = /some/path
3 local = /another/path
4
5 # same user, different group
6 global = /some/path
7 local = /another/path
8
9 # different user, same group
10 not reading file .hg/hgrc from untrusted user abc, group bar
11 global = /some/path
12
13 # different user, same group, but we trust the group
14 global = /some/path
15 local = /another/path
16
17 # different user, different group
18 not reading file .hg/hgrc from untrusted user abc, group def
19 global = /some/path
20
21 # different user, different group, but we trust the user
22 global = /some/path
23 local = /another/path
24
25 # different user, different group, but we trust the group
26 global = /some/path
27 local = /another/path
28
29 # different user, different group, but we trust the user and the group
30 global = /some/path
31 local = /another/path
32
33 # we trust all users
34 # different user, different group
35 global = /some/path
36 local = /another/path
37
38 # we trust all groups
39 # different user, different group
40 global = /some/path
41 local = /another/path
42
43 # we trust all users and groups
44 # different user, different group
45 global = /some/path
46 local = /another/path
47
48 # we don't get confused by users and groups with the same name
49 # different user, different group
50 not reading file .hg/hgrc from untrusted user abc, group def
51 global = /some/path
52
53 # list of user names
54 # different user, different group, but we trust the user
55 global = /some/path
56 local = /another/path
57
58 # list of group names
59 # different user, different group, but we trust the group
60 global = /some/path
61 local = /another/path
62
63 # Can't figure out the name of the user running this process
64 # different user, different group
65 global = /some/path
66 local = /another/path
67
@@ -21,6 +21,7 b' doc/*.[0-9].{x,ht}ml'
21 21 MANIFEST
22 22 patches
23 23 mercurial/__version__.py
24 .DS_Store
24 25
25 26 syntax: regexp
26 27 ^\.pc/
@@ -30,15 +30,29 b' proc getcommits {rargs} {'
30 30 set startmsecs [clock clicks -milliseconds]
31 31 set nextupdate [expr $startmsecs + 100]
32 32 set ncmupdate 1
33 set limit 0
34 set revargs {}
35 for {set i 0} {$i < [llength $rargs]} {incr i} {
36 set opt [lindex $rargs $i]
37 if {$opt == "--limit"} {
38 incr i
39 set limit [lindex $rargs $i]
40 } else {
41 lappend revargs $opt
42 }
43 }
33 44 if [catch {
34 set parse_args [concat --default HEAD $rargs]
45 set parse_args [concat --default HEAD $revargs]
35 46 set parsed_args [split [eval exec hg debug-rev-parse $parse_args] "\n"]
36 }] {
47 } err] {
37 48 # if git-rev-parse failed for some reason...
38 49 if {$rargs == {}} {
39 set rargs HEAD
50 set revargs HEAD
40 51 }
41 set parsed_args $rargs
52 set parsed_args $revargs
53 }
54 if {$limit > 0} {
55 set parsed_args [concat -n $limit $parsed_args]
42 56 }
43 57 if [catch {
44 58 set commfd [open "|hg debug-rev-list --header --topo-order --parents $parsed_args" r]
@@ -100,7 +114,7 b' to allow selection of commits to be disp'
100 114 set ids [string range $cmit 0 [expr {$j - 1}]]
101 115 set ok 1
102 116 foreach id $ids {
103 if {![regexp {^[0-9a-f]{40}$} $id]} {
117 if {![regexp {^[0-9a-f]{12}$} $id]} {
104 118 set ok 0
105 119 break
106 120 }
@@ -176,6 +190,7 b' proc parsecommit {id contents listed old'
176 190 set audate {}
177 191 set comname {}
178 192 set comdate {}
193 set rev {}
179 194 if {![info exists nchildren($id)]} {
180 195 set children($id) {}
181 196 set nchildren($id) 0
@@ -209,6 +224,8 b' proc parsecommit {id contents listed old'
209 224 set x [expr {[llength $line] - 2}]
210 225 set comdate [lindex $line $x]
211 226 set comname [join [lrange $line 1 [expr {$x - 1}]]]
227 } elseif {$tag == "revision"} {
228 set rev [lindex $line 1]
212 229 }
213 230 }
214 231 } else {
@@ -233,7 +250,7 b' proc parsecommit {id contents listed old'
233 250 set comdate [clock format $comdate -format "%Y-%m-%d %H:%M:%S"]
234 251 }
235 252 set commitinfo($id) [list $headline $auname $audate \
236 $comname $comdate $comment]
253 $comname $comdate $comment $rev]
237 254 }
238 255
239 256 proc readrefs {} {
@@ -261,7 +278,7 b' proc readotherrefs {base dname excl} {'
261 278 catch {
262 279 set fd [open $f r]
263 280 set line [read $fd 40]
264 if {[regexp {^[0-9a-f]{40}} $line id]} {
281 if {[regexp {^[0-9a-f]{12}} $line id]} {
265 282 set name "$dname[file tail $f]"
266 283 set otherrefids($name) $id
267 284 lappend idotherrefs($id) $name
@@ -1743,7 +1760,7 b' proc readfindproc {} {'
1743 1760 }
1744 1761 return
1745 1762 }
1746 if {![regexp {^[0-9a-f]{40}} $line id]} {
1763 if {![regexp {^[0-9a-f]{12}} $line id]} {
1747 1764 error_popup "Can't parse git-diff-tree output: $line"
1748 1765 stopfindproc
1749 1766 return
@@ -1856,7 +1873,7 b' proc readfilediffs {df} {'
1856 1873 }
1857 1874 return
1858 1875 }
1859 if {[regexp {^([0-9a-f]{40}) \(from ([0-9a-f]{40})\)} $line match id p]} {
1876 if {[regexp {^([0-9a-f]{12}) \(from ([0-9a-f]{12})\)} $line match id p]} {
1860 1877 # start of a new string of diffs
1861 1878 donefilediff
1862 1879 set fdiffids [list $id $p]
@@ -2002,8 +2019,9 b' proc commit_descriptor {p} {'
2002 2019 set l "..."
2003 2020 if {[info exists commitinfo($p)]} {
2004 2021 set l [lindex $commitinfo($p) 0]
2022 set r [lindex $commitinfo($p) 6]
2005 2023 }
2006 return "$p ($l)"
2024 return "$r:$p ($l)"
2007 2025 }
2008 2026
2009 2027 # append some text to the ctext widget, and make any SHA1 ID
@@ -2014,7 +2032,7 b' proc appendwithlinks {text} {'
2014 2032 set start [$ctext index "end - 1c"]
2015 2033 $ctext insert end $text
2016 2034 $ctext insert end "\n"
2017 set links [regexp -indices -all -inline {[0-9a-f]{40}} $text]
2035 set links [regexp -indices -all -inline {[0-9a-f]{12}} $text]
2018 2036 foreach l $links {
2019 2037 set s [lindex $l 0]
2020 2038 set e [lindex $l 1]
@@ -2107,6 +2125,7 b' proc selectline {l isnew} {'
2107 2125 $ctext mark set fmark.0 0.0
2108 2126 $ctext mark gravity fmark.0 left
2109 2127 set info $commitinfo($id)
2128 $ctext insert end "Revision: [lindex $info 6]\n"
2110 2129 $ctext insert end "Author: [lindex $info 1] [lindex $info 2]\n"
2111 2130 $ctext insert end "Committer: [lindex $info 3] [lindex $info 4]\n"
2112 2131 if {[info exists idtags($id)]} {
@@ -1,6 +1,6 b''
1 1 ;;; mercurial.el --- Emacs support for the Mercurial distributed SCM
2 2
3 ;; Copyright (C) 2005 Bryan O'Sullivan
3 ;; Copyright (C) 2005, 2006 Bryan O'Sullivan
4 4
5 5 ;; Author: Bryan O'Sullivan <bos@serpentine.com>
6 6
@@ -289,7 +289,7 b' XEmacs and GNU Emacs."'
289 289
290 290 (defsubst hg-chomp (str)
291 291 "Strip trailing newlines from a string."
292 (hg-replace-in-string str "[\r\n]+\'" ""))
292 (hg-replace-in-string str "[\r\n]+\\'" ""))
293 293
294 294 (defun hg-run-command (command &rest args)
295 295 "Run the shell command COMMAND, returning (EXIT-CODE . COMMAND-OUTPUT).
@@ -502,6 +502,43 b' directory names from the file system. W'
502 502 (or default "tip")))
503 503 rev))))
504 504
505 (defun hg-parents-for-mode-line (root)
506 "Format the parents of the working directory for the mode line."
507 (let ((parents (split-string (hg-chomp
508 (hg-run0 "--cwd" root "parents" "--template"
509 "{rev}\n")) "\n")))
510 (mapconcat 'identity parents "+")))
511
512 (defun hg-buffers-visiting-repo (&optional path)
513 "Return a list of buffers visiting the repository containing PATH."
514 (let ((root-name (hg-root (or path (buffer-file-name))))
515 bufs)
516 (save-excursion
517 (dolist (buf (buffer-list) bufs)
518 (set-buffer buf)
519 (let ((name (buffer-file-name)))
520 (when (and hg-status name (equal (hg-root name) root-name))
521 (setq bufs (cons buf bufs))))))))
522
523 (defun hg-update-mode-lines (path)
524 "Update the mode lines of all buffers visiting the same repository as PATH."
525 (let* ((root (hg-root path))
526 (parents (hg-parents-for-mode-line root)))
527 (save-excursion
528 (dolist (info (hg-path-status
529 root
530 (mapcar
531 (function
532 (lambda (buf)
533 (substring (buffer-file-name buf) (length root))))
534 (hg-buffers-visiting-repo root))))
535 (let* ((name (car info))
536 (status (cdr info))
537 (buf (find-buffer-visiting (concat root name))))
538 (when buf
539 (set-buffer buf)
540 (hg-mode-line-internal status parents)))))))
541
505 542 (defmacro hg-do-across-repo (path &rest body)
506 543 (let ((root-name (gensym "root-"))
507 544 (buf-name (gensym "buf-")))
@@ -548,13 +585,31 b' current frame."'
548 585 '(("M " . modified)
549 586 ("A " . added)
550 587 ("R " . removed)
588 ("! " . deleted)
551 589 ("? " . nil)))))
552 590 (if state
553 591 (cdr state)
554 592 'normal)))))
555 593
556 (defun hg-tip ()
557 (split-string (hg-chomp (hg-run0 "-q" "tip")) ":"))
594 (defun hg-path-status (root paths)
595 "Return status of PATHS in repo ROOT as an alist.
596 Each entry is a pair (FILE-NAME . STATUS)."
597 (let ((s (apply 'hg-run "--cwd" root "status" "-marduc" paths))
598 result)
599 (dolist (entry (split-string (hg-chomp (cdr s)) "\n") (nreverse result))
600 (let (state name)
601 (if (equal (substring entry 1 2) " ")
602 (setq state (cdr (assoc (substring entry 0 2)
603 '(("M " . modified)
604 ("A " . added)
605 ("R " . removed)
606 ("! " . deleted)
607 ("C " . normal)
608 ("I " . ignored)
609 ("? " . nil))))
610 name (substring entry 2))
611 (setq name (substring entry 0 (search ": " entry :from-end t))))
612 (setq result (cons (cons name state) result))))))
558 613
559 614 (defmacro hg-view-output (args &rest body)
560 615 "Execute BODY in a clean buffer, then quickly display that buffer.
@@ -589,7 +644,7 b' being viewed."'
589 644
590 645 (put 'hg-view-output 'lisp-indent-function 1)
591 646
592 ;;; Context save and restore across revert.
647 ;;; Context save and restore across revert and other operations.
593 648
594 649 (defun hg-position-context (pos)
595 650 "Return information to help find the given position again."
@@ -631,22 +686,28 b' Always returns a valid, hopefully sane, '
631 686
632 687 ;;; Hooks.
633 688
689 (defun hg-mode-line-internal (status parents)
690 (setq hg-status status
691 hg-mode (and status (concat " Hg:"
692 parents
693 (cdr (assq status
694 '((normal . "")
695 (removed . "r")
696 (added . "a")
697 (deleted . "!")
698 (modified . "m"))))))))
699
634 700 (defun hg-mode-line (&optional force)
635 701 "Update the modeline with the current status of a file.
636 702 An update occurs if optional argument FORCE is non-nil,
637 703 hg-update-modeline is non-nil, or we have not yet checked the state of
638 704 the file."
639 (when (and (hg-root) (or force hg-update-modeline (not hg-mode)))
640 (let ((status (hg-file-status buffer-file-name)))
641 (setq hg-status status
642 hg-mode (and status (concat " Hg:"
643 (car (hg-tip))
644 (cdr (assq status
645 '((normal . "")
646 (removed . "r")
647 (added . "a")
648 (modified . "m")))))))
649 status)))
705 (let ((root (hg-root)))
706 (when (and root (or force hg-update-modeline (not hg-mode)))
707 (let ((status (hg-file-status buffer-file-name))
708 (parents (hg-parents-for-mode-line root)))
709 (hg-mode-line-internal status parents)
710 status))))
650 711
651 712 (defun hg-mode (&optional toggle)
652 713 "Minor mode for Mercurial distributed SCM integration.
@@ -724,6 +785,13 b' code by typing `M-x find-library mercuri'
724 785 default-directory)
725 786 (cd hg-root-dir)))))
726 787
788 (defun hg-fix-paths ()
789 "Fix paths reported by some Mercurial commands."
790 (save-excursion
791 (goto-char (point-min))
792 (while (re-search-forward " \\.\\.." nil t)
793 (replace-match " " nil nil))))
794
727 795 (defun hg-add (path)
728 796 "Add PATH to the Mercurial repository on the next commit.
729 797 With a prefix argument, prompt for the path to add."
@@ -732,9 +800,8 b' With a prefix argument, prompt for the p'
732 800 (update (equal buffer-file-name path)))
733 801 (hg-view-output (hg-output-buffer-name)
734 802 (apply 'call-process (hg-binary) nil t nil (list "add" path))
735 ;; "hg add" shows pathes relative NOT TO ROOT BUT TO REPOSITORY
736 (replace-regexp " \\.\\.." " " nil 0 (buffer-size))
737 (goto-char 0)
803 (hg-fix-paths)
804 (goto-char (point-min))
738 805 (cd (hg-root path)))
739 806 (when update
740 807 (unless vc-make-backup-files
@@ -820,8 +887,7 b' hg-commit-allow-empty-file-list is nil, '
820 887 (let ((buf hg-prev-buffer))
821 888 (kill-buffer nil)
822 889 (switch-to-buffer buf))
823 (hg-do-across-repo root
824 (hg-mode-line)))))
890 (hg-update-mode-lines root))))
825 891
826 892 (defun hg-commit-mode ()
827 893 "Mode for describing a commit of changes to a Mercurial repository.
@@ -973,8 +1039,8 b' With a prefix argument, prompt for the p'
973 1039 (hg-view-output (hg-output-buffer-name)
974 1040 (apply 'call-process (hg-binary) nil t nil (list "forget" path))
975 1041 ;; "hg forget" shows pathes relative NOT TO ROOT BUT TO REPOSITORY
976 (replace-regexp " \\.\\.." " " nil 0 (buffer-size))
977 (goto-char 0)
1042 (hg-fix-paths)
1043 (goto-char (point-min))
978 1044 (cd (hg-root path)))
979 1045 (when update
980 1046 (with-current-buffer buf
@@ -1148,6 +1214,21 b' prompts for a path to check."'
1148 1214 root)
1149 1215 hg-root))
1150 1216
1217 (defun hg-cwd (&optional path)
1218 "Return the current directory of PATH within the repository."
1219 (do ((stack nil (cons (file-name-nondirectory
1220 (directory-file-name dir))
1221 stack))
1222 (prev nil dir)
1223 (dir (file-name-directory (or path buffer-file-name
1224 (expand-file-name default-directory)))
1225 (file-name-directory (directory-file-name dir))))
1226 ((equal prev dir))
1227 (when (file-directory-p (concat dir ".hg"))
1228 (let ((cwd (mapconcat 'identity stack "/")))
1229 (unless (equal cwd "")
1230 (return (file-name-as-directory cwd)))))))
1231
1151 1232 (defun hg-status (path)
1152 1233 "Print revision control status of a file or directory.
1153 1234 With prefix argument, prompt for the path to give status for.
@@ -193,6 +193,10 b' FILES'
193 193 global /etc/mercurial/hgrc configuration. See hgrc(5) for details of
194 194 the contents and format of these files.
195 195
196 Some commands (e.g. revert) produce backup files ending in .orig, if
197 the .orig file already exists and is not tracked by Mercurial, it
198 will be overwritten.
199
196 200 BUGS
197 201 ----
198 202 Probably lots, please post them to the mailing list (See Resources below)
@@ -50,6 +50,8 b' installed.'
50 50 particular repository. This file is not version-controlled, and
51 51 will not get transferred during a "clone" operation. Options in
52 52 this file override options in all other configuration files.
53 On Unix, this file is only read if it belongs to a trusted user
54 or to a trusted group.
53 55
54 56 SYNTAX
55 57 ------
@@ -133,6 +135,21 b' decode/encode::'
133 135 # them to the working dir
134 136 **.txt = tempfile: unix2dos -n INFILE OUTFILE
135 137
138 defaults::
139 Use the [defaults] section to define command defaults, i.e. the
140 default options/arguments to pass to the specified commands.
141
142 The following example makes 'hg log' run in verbose mode, and
143 'hg status' show only the modified files, by default.
144
145 [defaults]
146 log = -v
147 status = -m
148
149 The actual commands, instead of their aliases, must be used when
150 defining command defaults. The command defaults will also be
151 applied to the aliases of the commands defined.
152
136 153 email::
137 154 Settings for extensions that send email messages.
138 155 from;;
@@ -349,6 +366,16 b' server::'
349 366 6Mbps), uncompressed streaming is slower, because of the extra
350 367 data transfer overhead. Default is False.
351 368
369 trusted::
370 Mercurial will only read the .hg/hgrc file from a repository if
371 it belongs to a trusted user or to a trusted group. This section
372 specifies what users and groups are trusted. To trust everybody,
373 list a user or a group with name "*".
374 users;;
375 Comma-separated list of trusted users.
376 groups;;
377 Comma-separated list of trusted groups.
378
352 379 ui::
353 380 User interface controls.
354 381 debug;;
@@ -377,6 +404,9 b' ui::'
377 404 remote command to use for clone/push/pull operations. Default is 'hg'.
378 405 ssh;;
379 406 command to use for SSH connections. Default is 'ssh'.
407 strict;;
408 Require exact command names, instead of allowing unambiguous
409 abbreviations. True or False. Default is False.
380 410 timeout;;
381 411 The timeout used when a lock is held (in seconds), a negative value
382 412 means no timeout. Default is 600.
@@ -41,13 +41,15 b' HGTMP="${TMPDIR-/tmp}/hgeditor.$RANDOM.$'
41 41
42 42 cat "$1" > "$HGTMP/msg"
43 43
44 CHECKSUM=`md5sum "$HGTMP/msg"`
44 MD5=$(which md5sum 2>/dev/null) || \
45 MD5=$(which md5 2>/dev/null)
46 [ -x "${MD5}" ] && CHECKSUM=`${MD5} "$HGTMP/msg"`
45 47 if [ -s "$HGTMP/diff" ]; then
46 48 $EDITOR "$HGTMP/msg" "$HGTMP/diff" || exit $?
47 49 else
48 50 $EDITOR "$HGTMP/msg" || exit $?
49 51 fi
50 echo "$CHECKSUM" | md5sum -c >/dev/null 2>&1 && exit 13
52 [ -x "${MD5}" ] && (echo "$CHECKSUM" | ${MD5} -c >/dev/null 2>&1 && exit 13)
51 53
52 54 mv "$HGTMP/msg" "$1"
53 55
@@ -60,8 +60,8 b' class checker(object):'
60 60 return None, False
61 61
62 62 thisuser = self.getuser()
63 pats = [pat for pat, user in self.ui.configitems(key)
64 if user == thisuser]
63 pats = [pat for pat, users in self.ui.configitems(key)
64 if thisuser in users.replace(',', ' ').split()]
65 65 self.ui.debug(_('acl: %s enabled, %d entries for user %s\n') %
66 66 (key, len(pats), thisuser))
67 67 if pats:
@@ -45,7 +45,7 b''
45 45 from mercurial.demandload import demandload
46 46 from mercurial.i18n import gettext as _
47 47 from mercurial.node import *
48 demandload(globals(), 'mercurial:commands,cmdutil,util os shutil tempfile')
48 demandload(globals(), 'mercurial:cmdutil,util os shutil tempfile')
49 49
50 50 def dodiff(ui, repo, diffcmd, diffopts, pats, opts):
51 51 def snapshot_node(files, node):
@@ -90,7 +90,7 b' def dodiff(ui, repo, diffcmd, diffopts, '
90 90 fp.write(chunk)
91 91 return dirname
92 92
93 node1, node2 = commands.revpair(ui, repo, opts['rev'])
93 node1, node2 = cmdutil.revpair(ui, repo, opts['rev'])
94 94 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
95 95 modified, added, removed, deleted, unknown = repo.status(
96 96 node1, node2, files, match=matchfn)[:5]
@@ -105,8 +105,7 b' def dodiff(ui, repo, diffcmd, diffopts, '
105 105 else:
106 106 dir2 = snapshot_wdir(modified + added)
107 107 cmdline = ('%s %s %s %s' %
108 (util.shellquote(diffcmd),
109 ' '.join(map(util.shellquote, diffopts)),
108 (util.shellquote(diffcmd), ' '.join(diffopts),
110 109 util.shellquote(dir1), util.shellquote(dir2)))
111 110 ui.debug('running %r in %s\n' % (cmdline, tmproot))
112 111 util.system(cmdline, cwd=tmproot)
@@ -7,90 +7,39 b''
7 7
8 8 from mercurial.demandload import *
9 9 demandload(globals(), 'time sys signal os')
10 demandload(globals(), 'mercurial:hg,mdiff,fancyopts,commands,ui,util')
11
12 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
13 changes=None, text=False):
14 def date(c):
15 return time.asctime(time.gmtime(c[2][0]))
16
17 if not changes:
18 changes = repo.status(node1, node2, files, match=match)[:5]
19 modified, added, removed, deleted, unknown = changes
20 if files:
21 modified, added, removed = map(lambda x: filterfiles(files, x),
22 (modified, added, removed))
23
24 if not modified and not added and not removed:
25 return
10 demandload(globals(), 'mercurial:hg,fancyopts,commands,ui,util,patch,revlog')
26 11
27 if node2:
28 change = repo.changelog.read(node2)
29 mmap2 = repo.manifest.read(change[0])
30 date2 = date(change)
31 def read(f):
32 return repo.file(f).read(mmap2[f])
33 else:
34 date2 = time.asctime()
35 if not node1:
36 node1 = repo.dirstate.parents()[0]
37 def read(f):
38 return repo.wfile(f).read()
39
40 change = repo.changelog.read(node1)
41 mmap = repo.manifest.read(change[0])
42 date1 = date(change)
43
44 for f in modified:
45 to = None
46 if f in mmap:
47 to = repo.file(f).read(mmap[f])
48 tn = read(f)
49 fp.write("diff --git a/%s b/%s\n" % (f, f))
50 fp.write(mdiff.unidiff(to, date1, tn, date2, f, None, text=text))
51 for f in added:
52 to = None
53 tn = read(f)
54 fp.write("diff --git /dev/null b/%s\n" % (f))
55 fp.write(mdiff.unidiff(to, date1, tn, date2, f, None, text=text))
56 for f in removed:
57 to = repo.file(f).read(mmap[f])
58 tn = None
59 fp.write("diff --git a/%s /dev/null\n" % (f))
60 fp.write(mdiff.unidiff(to, date1, tn, date2, f, None, text=text))
61
62 def difftree(ui, repo, node1=None, node2=None, **opts):
12 def difftree(ui, repo, node1=None, node2=None, *files, **opts):
63 13 """diff trees from two commits"""
64 def __difftree(repo, node1, node2):
65 def date(c):
66 return time.asctime(time.gmtime(c[2][0]))
67
14 def __difftree(repo, node1, node2, files=[]):
68 15 if node2:
69 16 change = repo.changelog.read(node2)
70 17 mmap2 = repo.manifest.read(change[0])
71 modified, added, removed, deleted, unknown = repo.status(node1, node2)[:5]
72 def read(f): return repo.file(f).read(mmap2[f])
73 date2 = date(change)
18 status = repo.status(node1, node2, files=files)[:5]
19 modified, added, removed, deleted, unknown = status
74 20 else:
75 date2 = time.asctime()
76 modified, added, removed, deleted, unknown = repo.status(node1)[:5]
21 status = repo.status(node1, files=files)[:5]
22 modified, added, removed, deleted, unknown = status
77 23 if not node1:
78 24 node1 = repo.dirstate.parents()[0]
79 def read(f): return file(os.path.join(repo.root, f)).read()
80 25
81 26 change = repo.changelog.read(node1)
82 27 mmap = repo.manifest.read(change[0])
83 date1 = date(change)
84 empty = "0" * 40;
28 empty = hg.short(hg.nullid)
85 29
86 30 for f in modified:
87 31 # TODO get file permissions
88 print ":100664 100664 %s %s M\t%s\t%s" % (hg.hex(mmap[f]),
89 hg.hex(mmap2[f]), f, f)
32 print ":100664 100664 %s %s M\t%s\t%s" % (hg.short(mmap[f]),
33 hg.short(mmap2[f]),
34 f, f)
90 35 for f in added:
91 print ":000000 100664 %s %s N\t%s\t%s" % (empty, hg.hex(mmap2[f]), f, f)
36 print ":000000 100664 %s %s N\t%s\t%s" % (empty,
37 hg.short(mmap2[f]),
38 f, f)
92 39 for f in removed:
93 print ":100664 000000 %s %s D\t%s\t%s" % (hg.hex(mmap[f]), empty, f, f)
40 print ":100664 000000 %s %s D\t%s\t%s" % (hg.short(mmap[f]),
41 empty,
42 f, f)
94 43 ##
95 44
96 45 while True:
@@ -113,20 +62,22 b' def difftree(ui, repo, node1=None, node2'
113 62 if opts['patch']:
114 63 if opts['pretty']:
115 64 catcommit(repo, node2, "")
116 dodiff(sys.stdout, ui, repo, node1, node2)
65 patch.diff(repo, node1, node2,
66 files=files,
67 opts=patch.diffopts(ui, {'git': True}))
117 68 else:
118 __difftree(repo, node1, node2)
69 __difftree(repo, node1, node2, files=files)
119 70 if not opts['stdin']:
120 71 break
121 72
122 73 def catcommit(repo, n, prefix, changes=None):
123 74 nlprefix = '\n' + prefix;
124 75 (p1, p2) = repo.changelog.parents(n)
125 (h, h1, h2) = map(hg.hex, (n, p1, p2))
76 (h, h1, h2) = map(hg.short, (n, p1, p2))
126 77 (i1, i2) = map(repo.changelog.rev, (p1, p2))
127 78 if not changes:
128 79 changes = repo.changelog.read(n)
129 print "tree %s" % (hg.hex(changes[0]))
80 print "tree %s" % (hg.short(changes[0]))
130 81 if i1 != -1: print "parent %s" % (h1)
131 82 if i2 != -1: print "parent %s" % (h2)
132 83 date_ar = changes[2]
@@ -139,6 +90,7 b' def catcommit(repo, n, prefix, changes=N'
139 90
140 91 print "author %s %s %s" % (changes[1], date, date_ar[1])
141 92 print "committer %s %s %s" % (committer, date, date_ar[1])
93 print "revision %d" % repo.changelog.rev(n)
142 94 print ""
143 95 if prefix != "":
144 96 print "%s%s" % (prefix, changes[4].replace('\n', nlprefix).strip())
@@ -152,7 +104,7 b' def base(ui, repo, node1, node2):'
152 104 node1 = repo.lookup(node1)
153 105 node2 = repo.lookup(node2)
154 106 n = repo.changelog.ancestor(node1, node2)
155 print hg.hex(n)
107 print hg.short(n)
156 108
157 109 def catfile(ui, repo, type=None, r=None, **opts):
158 110 """cat a specific revision"""
@@ -265,7 +217,6 b' def revtree(args, repo, full="tree", max'
265 217
266 218 # walk the repository looking for commits that are in our
267 219 # reachability graph
268 #for i in range(repo.changelog.count()-1, -1, -1):
269 220 for i, changes in chlogwalk():
270 221 n = repo.changelog.node(i)
271 222 mask = is_reachable(want_sha1, reachable, n)
@@ -274,17 +225,17 b' def revtree(args, repo, full="tree", max'
274 225 if parents:
275 226 pp = repo.changelog.parents(n)
276 227 if pp[0] != hg.nullid:
277 parentstr += " " + hg.hex(pp[0])
228 parentstr += " " + hg.short(pp[0])
278 229 if pp[1] != hg.nullid:
279 parentstr += " " + hg.hex(pp[1])
230 parentstr += " " + hg.short(pp[1])
280 231 if not full:
281 print hg.hex(n) + parentstr
282 elif full is "commit":
283 print hg.hex(n) + parentstr
232 print hg.short(n) + parentstr
233 elif full == "commit":
234 print hg.short(n) + parentstr
284 235 catcommit(repo, n, ' ', changes)
285 236 else:
286 237 (p1, p2) = repo.changelog.parents(n)
287 (h, h1, h2) = map(hg.hex, (n, p1, p2))
238 (h, h1, h2) = map(hg.short, (n, p1, p2))
288 239 (i1, i2) = map(repo.changelog.rev, (p1, p2))
289 240
290 241 date = changes[2][0]
@@ -300,6 +251,19 b' def revtree(args, repo, full="tree", max'
300 251 break
301 252 count += 1
302 253
254 def revparse(ui, repo, *revs, **opts):
255 """Parse given revisions"""
256 def revstr(rev):
257 if rev == 'HEAD':
258 rev = 'tip'
259 return revlog.hex(repo.lookup(rev))
260
261 for r in revs:
262 revrange = r.split(':', 1)
263 ui.write('%s\n' % revstr(revrange[0]))
264 if len(revrange) == 2:
265 ui.write('^%s\n' % revstr(revrange[1]))
266
303 267 # git rev-list tries to order things by date, and has the ability to stop
304 268 # at a given commit without walking the whole repo. TODO add the stop
305 269 # parameter
@@ -312,23 +276,29 b' def revlist(ui, repo, *revs, **opts):'
312 276 copy = [x for x in revs]
313 277 revtree(copy, repo, full, opts['max_count'], opts['parents'])
314 278
315 def view(ui, repo, *etc):
279 def view(ui, repo, *etc, **opts):
316 280 "start interactive history viewer"
317 281 os.chdir(repo.root)
318 os.system(ui.config("hgk", "path", "hgk") + " " + " ".join(etc))
282 optstr = ' '.join(['--%s %s' % (k, v) for k, v in opts.iteritems()])
283 os.system(ui.config("hgk", "path", "hgk") + " %s %s" % (optstr, " ".join(etc)))
319 284
320 285 cmdtable = {
321 "view": (view, [], 'hg view'),
286 "view": (view,
287 [('l', 'limit', '', 'limit number of changes displayed')],
288 'hg view [-l LIMIT] [REVRANGE]'),
322 289 "debug-diff-tree": (difftree, [('p', 'patch', None, 'generate patch'),
323 290 ('r', 'recursive', None, 'recursive'),
324 291 ('P', 'pretty', None, 'pretty'),
325 292 ('s', 'stdin', None, 'stdin'),
326 293 ('C', 'copy', None, 'detect copies'),
327 294 ('S', 'search', "", 'search')],
328 "hg git-diff-tree [options] node1 node2"),
295 "hg git-diff-tree [options] node1 node2 [files...]"),
329 296 "debug-cat-file": (catfile, [('s', 'stdin', None, 'stdin')],
330 297 "hg debug-cat-file [options] type file"),
331 298 "debug-merge-base": (base, [], "hg debug-merge-base node node"),
299 'debug-rev-parse': (revparse,
300 [('', 'default', '', 'ignored')],
301 "hg debug-rev-parse rev"),
332 302 "debug-rev-list": (revlist, [('H', 'header', None, 'header'),
333 303 ('t', 'topo-order', None, 'topo-order'),
334 304 ('p', 'parents', None, 'parents'),
@@ -31,15 +31,16 b' refresh contents of top applied patch '
31 31
32 32 from mercurial.demandload import *
33 33 from mercurial.i18n import gettext as _
34 from mercurial import commands
34 35 demandload(globals(), "os sys re struct traceback errno bz2")
35 demandload(globals(), "mercurial:cmdutil,commands,hg,patch,revlog,ui,util")
36 demandload(globals(), "mercurial:cmdutil,hg,patch,revlog,ui,util")
36 37
37 38 commands.norepo += " qclone qversion"
38 39
39 40 class statusentry:
40 41 def __init__(self, rev, name=None):
41 42 if not name:
42 fields = rev.split(':')
43 fields = rev.split(':', 1)
43 44 if len(fields) == 2:
44 45 self.rev, self.name = fields
45 46 else:
@@ -482,24 +483,35 b' class queue:'
482 483 tr.close()
483 484 return (err, n)
484 485
485 def delete(self, repo, patches, keep=False):
486 def delete(self, repo, patches, opts):
486 487 realpatches = []
488 appliedbase = 0
489 forget = opts.get('forget')
487 490 for patch in patches:
488 491 patch = self.lookup(patch, strict=True)
489 492 info = self.isapplied(patch)
490 if info:
493 if info and not forget:
491 494 raise util.Abort(_("cannot delete applied patch %s") % patch)
492 495 if patch not in self.series:
493 496 raise util.Abort(_("patch %s not in series file") % patch)
497 if forget:
498 if not info:
499 raise util.Abort(_("cannot forget unapplied patch %s") % patch)
500 if info[0] != appliedbase:
501 raise util.Abort(_("patch %s not at base") % patch)
502 appliedbase += 1
494 503 realpatches.append(patch)
495 504
496 if not keep:
505 if not opts.get('keep'):
497 506 r = self.qrepo()
498 507 if r:
499 508 r.remove(realpatches, True)
500 509 else:
501 510 os.unlink(self.join(patch))
502 511
512 if forget:
513 del self.applied[:appliedbase]
514 self.applied_dirty = 1
503 515 indices = [self.find_series(p) for p in realpatches]
504 516 indices.sort()
505 517 for i in indices[-1::-1]:
@@ -693,8 +705,8 b' class queue:'
693 705 stripall(rev, revnum)
694 706
695 707 change = chlog.read(rev)
708 chlog.strip(revnum, revnum)
696 709 repo.manifest.strip(repo.manifest.rev(change[0]), revnum)
697 chlog.strip(revnum, revnum)
698 710 if saveheads:
699 711 self.ui.status("adding branch\n")
700 712 commands.unbundle(self.ui, repo, chgrpfile, update=False)
@@ -756,25 +768,25 b' class queue:'
756 768 # return any partial match made above
757 769 if res:
758 770 return res
759 minus = patch.rsplit('-', 1)
760 if len(minus) > 1:
761 res = partial_name(minus[0])
771 minus = patch.rfind('-')
772 if minus >= 0:
773 res = partial_name(patch[:minus])
762 774 if res:
763 775 i = self.series.index(res)
764 776 try:
765 off = int(minus[1] or 1)
777 off = int(patch[minus+1:] or 1)
766 778 except(ValueError, OverflowError):
767 779 pass
768 780 else:
769 781 if i - off >= 0:
770 782 return self.series[i - off]
771 plus = patch.rsplit('+', 1)
772 if len(plus) > 1:
773 res = partial_name(plus[0])
783 plus = patch.rfind('+')
784 if plus >= 0:
785 res = partial_name(patch[:plus])
774 786 if res:
775 787 i = self.series.index(res)
776 788 try:
777 off = int(plus[1] or 1)
789 off = int(patch[plus+1:] or 1)
778 790 except(ValueError, OverflowError):
779 791 pass
780 792 else:
@@ -915,16 +927,16 b' class queue:'
915 927 def refresh(self, repo, pats=None, **opts):
916 928 if len(self.applied) == 0:
917 929 self.ui.write("No patches applied\n")
918 return
930 return 1
919 931 wlock = repo.wlock()
920 932 self.check_toppatch(repo)
921 (top, patch) = (self.applied[-1].rev, self.applied[-1].name)
933 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
922 934 top = revlog.bin(top)
923 935 cparents = repo.changelog.parents(top)
924 936 patchparent = self.qparents(repo, top)
925 message, comments, user, date, patchfound = self.readheaders(patch)
937 message, comments, user, date, patchfound = self.readheaders(patchfn)
926 938
927 patchf = self.opener(patch, "w")
939 patchf = self.opener(patchfn, "w")
928 940 msg = opts.get('msg', '').rstrip()
929 941 if msg:
930 942 if comments:
@@ -994,8 +1006,11 b' class queue:'
994 1006 r = list(util.unique(dd))
995 1007 a = list(util.unique(aa))
996 1008 filelist = filter(matchfn, util.unique(m + r + a))
997 self.printdiff(repo, patchparent, files=filelist,
998 changes=(m, a, r, [], u), fp=patchf)
1009 if opts.get('git'):
1010 self.diffopts().git = True
1011 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1012 fp=patchf, changes=(m, a, r, [], u),
1013 opts=self.diffopts())
999 1014 patchf.close()
1000 1015
1001 1016 changes = repo.changelog.read(tip)
@@ -1018,7 +1033,7 b' class queue:'
1018 1033
1019 1034 if not msg:
1020 1035 if not message:
1021 message = "patch queue: %s\n" % patch
1036 message = "patch queue: %s\n" % patchfn
1022 1037 else:
1023 1038 message = "\n".join(message)
1024 1039 else:
@@ -1026,7 +1041,7 b' class queue:'
1026 1041
1027 1042 self.strip(repo, top, update=False, backup='strip', wlock=wlock)
1028 1043 n = repo.commit(filelist, message, changes[1], force=1, wlock=wlock)
1029 self.applied[-1] = statusentry(revlog.hex(n), patch)
1044 self.applied[-1] = statusentry(revlog.hex(n), patchfn)
1030 1045 self.applied_dirty = 1
1031 1046 else:
1032 1047 self.printdiff(repo, patchparent, fp=patchf)
@@ -1237,11 +1252,13 b' class queue:'
1237 1252 self.ui.write(p + '\n')
1238 1253 else:
1239 1254 self.ui.write("No patches applied\n")
1255 return 1
1240 1256
1241 1257 def next(self, repo):
1242 1258 end = self.series_end()
1243 1259 if end == len(self.series):
1244 1260 self.ui.write("All patches applied\n")
1261 return 1
1245 1262 else:
1246 1263 p = self.series[end]
1247 1264 if self.ui.verbose:
@@ -1254,8 +1271,10 b' class queue:'
1254 1271 self.ui.write(p + '\n')
1255 1272 elif len(self.applied) == 1:
1256 1273 self.ui.write("Only one patch applied\n")
1274 return 1
1257 1275 else:
1258 1276 self.ui.write("No patches applied\n")
1277 return 1
1259 1278
1260 1279 def qimport(self, repo, files, patch=None, existing=None, force=None):
1261 1280 if len(files) > 1 and patch:
@@ -1298,10 +1317,15 b' class queue:'
1298 1317 def delete(ui, repo, patch, *patches, **opts):
1299 1318 """remove patches from queue
1300 1319
1301 The patches must not be applied.
1302 With -k, the patch files are preserved in the patch directory."""
1320 With --forget, mq will stop managing the named patches. The
1321 patches must be applied and at the base of the stack. This option
1322 is useful when the patches have been applied upstream.
1323
1324 Otherwise, the patches must not be applied.
1325
1326 With --keep, the patch files are preserved in the patch directory."""
1303 1327 q = repo.mq
1304 q.delete(repo, (patch,) + patches, keep=opts.get('keep'))
1328 q.delete(repo, (patch,) + patches, opts)
1305 1329 q.save_dirty()
1306 1330 return 0
1307 1331
@@ -1402,18 +1426,15 b' def series(ui, repo, **opts):'
1402 1426
1403 1427 def top(ui, repo, **opts):
1404 1428 """print the name of the current patch"""
1405 repo.mq.top(repo)
1406 return 0
1429 return repo.mq.top(repo)
1407 1430
1408 1431 def next(ui, repo, **opts):
1409 1432 """print the name of the next patch"""
1410 repo.mq.next(repo)
1411 return 0
1433 return repo.mq.next(repo)
1412 1434
1413 1435 def prev(ui, repo, **opts):
1414 1436 """print the name of the previous patch"""
1415 repo.mq.prev(repo)
1416 return 0
1437 return repo.mq.prev(repo)
1417 1438
1418 1439 def new(ui, repo, patch, **opts):
1419 1440 """create a new patch
@@ -1449,9 +1470,9 b' def refresh(ui, repo, *pats, **opts):'
1449 1470 patch = q.applied[-1].name
1450 1471 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1451 1472 message = ui.edit('\n'.join(message), user or ui.username())
1452 q.refresh(repo, pats, msg=message, **opts)
1473 ret = q.refresh(repo, pats, msg=message, **opts)
1453 1474 q.save_dirty()
1454 return 0
1475 return ret
1455 1476
1456 1477 def diff(ui, repo, *pats, **opts):
1457 1478 """diff of the current patch"""
@@ -1476,7 +1497,7 b' def fold(ui, repo, *files, **opts):'
1476 1497 if not files:
1477 1498 raise util.Abort(_('qfold requires at least one patch name'))
1478 1499 if not q.check_toppatch(repo):
1479 raise util.Abort(_('No patches applied\n'))
1500 raise util.Abort(_('No patches applied'))
1480 1501
1481 1502 message = commands.logmessage(opts)
1482 1503 if opts['edit']:
@@ -1571,7 +1592,7 b' def header(ui, repo, patch=None):'
1571 1592 else:
1572 1593 if not q.applied:
1573 1594 ui.write('No patches applied\n')
1574 return
1595 return 1
1575 1596 patch = q.lookup('qtip')
1576 1597 message = repo.mq.readheaders(patch)[0]
1577 1598
@@ -1648,13 +1669,6 b' def rename(ui, repo, patch, name=None, *'
1648 1669 name = patch
1649 1670 patch = None
1650 1671
1651 if name in q.series:
1652 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1653
1654 absdest = q.join(name)
1655 if os.path.exists(absdest):
1656 raise util.Abort(_('%s already exists') % absdest)
1657
1658 1672 if patch:
1659 1673 patch = q.lookup(patch)
1660 1674 else:
@@ -1662,6 +1676,15 b' def rename(ui, repo, patch, name=None, *'
1662 1676 ui.write(_('No patches applied\n'))
1663 1677 return
1664 1678 patch = q.lookup('qtip')
1679 absdest = q.join(name)
1680 if os.path.isdir(absdest):
1681 name = os.path.join(name, os.path.basename(patch))
1682 absdest = q.join(name)
1683 if os.path.exists(absdest):
1684 raise util.Abort(_('%s already exists') % absdest)
1685
1686 if name in q.series:
1687 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1665 1688
1666 1689 if ui.verbose:
1667 1690 ui.write('Renaming %s to %s\n' % (patch, name))
@@ -1733,7 +1756,8 b' def strip(ui, repo, rev, **opts):'
1733 1756 backup = 'strip'
1734 1757 elif opts['nobackup']:
1735 1758 backup = 'none'
1736 repo.mq.strip(repo, rev, backup=backup)
1759 update = repo.dirstate.parents()[0] != revlog.nullid
1760 repo.mq.strip(repo, rev, backup=backup, update=update)
1737 1761 return 0
1738 1762
1739 1763 def select(ui, repo, *args, **opts):
@@ -1909,8 +1933,9 b' cmdtable = {'
1909 1933 'hg qdiff [-I] [-X] [FILE]...'),
1910 1934 "qdelete|qremove|qrm":
1911 1935 (delete,
1912 [('k', 'keep', None, _('keep patch file'))],
1913 'hg qdelete [-k] PATCH'),
1936 [('f', 'forget', None, _('stop managing an applied patch')),
1937 ('k', 'keep', None, _('keep patch file'))],
1938 'hg qdelete [-f] [-k] PATCH'),
1914 1939 'qfold':
1915 1940 (fold,
1916 1941 [('e', 'edit', None, _('edit patch header')),
@@ -1961,6 +1986,7 b' cmdtable = {'
1961 1986 [('e', 'edit', None, _('edit commit message')),
1962 1987 ('m', 'message', '', _('change commit message with <text>')),
1963 1988 ('l', 'logfile', '', _('change commit message with <file> content')),
1989 ('g', 'git', None, _('use git extended diff format')),
1964 1990 ('s', 'short', None, 'short refresh'),
1965 1991 ('I', 'include', [], _('include names matching the given patterns')),
1966 1992 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
@@ -1992,7 +2018,7 b' cmdtable = {'
1992 2018 (series,
1993 2019 [('m', 'missing', None, 'print patches not in series'),
1994 2020 ('s', 'summary', None, _('print first line of patch header'))],
1995 'hg qseries [-m]'),
2021 'hg qseries [-ms]'),
1996 2022 "^strip":
1997 2023 (strip,
1998 2024 [('f', 'force', None, 'force multi-head removal'),
@@ -238,8 +238,11 b' class notifier(object):'
238 238 return
239 239 fp = templater.stringio()
240 240 prev = self.repo.changelog.parents(node)[0]
241 patch.diff(self.repo, fp, prev, ref)
241 patch.diff(self.repo, prev, ref, fp=fp)
242 242 difflines = fp.getvalue().splitlines(1)
243 if self.ui.configbool('notify', 'diffstat', True):
244 s = patch.diffstat(difflines)
245 self.sio.write('\ndiffstat:\n\n' + s)
243 246 if maxdiff > 0 and len(difflines) > maxdiff:
244 247 self.sio.write(_('\ndiffs (truncated from %d to %d lines):\n\n') %
245 248 (len(difflines), maxdiff))
@@ -65,7 +65,7 b''
65 65
66 66 from mercurial.demandload import *
67 67 demandload(globals(), '''email.MIMEMultipart email.MIMEText email.Utils
68 mercurial:commands,hg,mail,ui
68 mercurial:commands,hg,mail,ui,patch
69 69 os errno popen2 socket sys tempfile time''')
70 70 from mercurial.i18n import gettext as _
71 71 from mercurial.node import *
@@ -76,27 +76,6 b' try:'
76 76 import readline
77 77 except ImportError: pass
78 78
79 def diffstat(patch):
80 fd, name = tempfile.mkstemp(prefix="hg-patchbomb-", suffix=".txt")
81 try:
82 p = popen2.Popen3('diffstat -p1 -w79 2>/dev/null > ' + name)
83 try:
84 for line in patch: print >> p.tochild, line
85 p.tochild.close()
86 if p.wait(): return
87 fp = os.fdopen(fd, 'r')
88 stat = []
89 for line in fp: stat.append(line.lstrip())
90 last = stat.pop()
91 stat.insert(0, last)
92 stat = ''.join(stat)
93 if stat.startswith('0 files'): raise ValueError
94 return stat
95 except: raise
96 finally:
97 try: os.unlink(name)
98 except: pass
99
100 79 def patchbomb(ui, repo, *revs, **opts):
101 80 '''send changesets as a series of patch emails
102 81
@@ -123,8 +102,8 b' def patchbomb(ui, repo, *revs, **opts):'
123 102 if not prompt(s, default = 'y', rest = '? ').lower().startswith('y'):
124 103 raise ValueError
125 104
126 def cdiffstat(summary, patch):
127 s = diffstat(patch)
105 def cdiffstat(summary, patchlines):
106 s = patch.diffstat(patchlines)
128 107 if s:
129 108 if summary:
130 109 ui.write(summary, '\n')
@@ -140,7 +119,9 b' def patchbomb(ui, repo, *revs, **opts):'
140 119 if line.startswith('#'):
141 120 if line.startswith('# Node ID'): node = line.split()[-1]
142 121 continue
143 if line.startswith('diff -r'): break
122 if (line.startswith('diff -r')
123 or line.startswith('diff --git')):
124 break
144 125 desc.append(line)
145 126 if not node: raise ValueError
146 127
@@ -205,7 +186,8 b' def patchbomb(ui, repo, *revs, **opts):'
205 186
206 187 commands.export(ui, repo, *revs, **{'output': exportee(patches),
207 188 'switch_parent': False,
208 'text': None})
189 'text': None,
190 'git': opts.get('git')})
209 191
210 192 jumbo = []
211 193 msgs = []
@@ -322,6 +304,7 b' cmdtable = {'
322 304 ('', 'bcc', [], 'email addresses of blind copy recipients'),
323 305 ('c', 'cc', [], 'email addresses of copy recipients'),
324 306 ('d', 'diffstat', None, 'add diffstat output to messages'),
307 ('g', 'git', None, _('use git extended diff format')),
325 308 ('f', 'from', '', 'email address of sender'),
326 309 ('', 'plain', None, 'omit hg patch header'),
327 310 ('n', 'test', None, 'print messages that would be sent'),
@@ -16,6 +16,14 b' class changelog(revlog):'
16 16 defversion)
17 17
18 18 def extract(self, text):
19 """
20 format used:
21 nodeid\n : manifest node in ascii
22 user\n : user, no \n or \r allowed
23 time tz\n : date (time is int or float, timezone is int)
24 files\n\n : files modified by the cset, no \n or \r allowed
25 (.*) : comment (free text, ideally utf-8)
26 """
19 27 if not text:
20 28 return (nullid, "", (0, 0), [], "")
21 29 last = text.index("\n\n")
@@ -11,6 +11,76 b' from i18n import gettext as _'
11 11 demandload(globals(), 'mdiff util')
12 12 demandload(globals(), 'os sys')
13 13
14 revrangesep = ':'
15
16 def revfix(repo, val, defval):
17 '''turn user-level id of changeset into rev number.
18 user-level id can be tag, changeset, rev number, or negative rev
19 number relative to number of revs (-1 is tip, etc).'''
20 if not val:
21 return defval
22 try:
23 num = int(val)
24 if str(num) != val:
25 raise ValueError
26 if num < 0:
27 num += repo.changelog.count()
28 if num < 0:
29 num = 0
30 elif num >= repo.changelog.count():
31 raise ValueError
32 except ValueError:
33 try:
34 num = repo.changelog.rev(repo.lookup(val))
35 except KeyError:
36 raise util.Abort(_('invalid revision identifier %s') % val)
37 return num
38
39 def revpair(ui, repo, revs):
40 '''return pair of nodes, given list of revisions. second item can
41 be None, meaning use working dir.'''
42 if not revs:
43 return repo.dirstate.parents()[0], None
44 end = None
45 if len(revs) == 1:
46 start = revs[0]
47 if revrangesep in start:
48 start, end = start.split(revrangesep, 1)
49 start = revfix(repo, start, 0)
50 end = revfix(repo, end, repo.changelog.count() - 1)
51 else:
52 start = revfix(repo, start, None)
53 elif len(revs) == 2:
54 if revrangesep in revs[0] or revrangesep in revs[1]:
55 raise util.Abort(_('too many revisions specified'))
56 start = revfix(repo, revs[0], None)
57 end = revfix(repo, revs[1], None)
58 else:
59 raise util.Abort(_('too many revisions specified'))
60 if end is not None: end = repo.lookup(str(end))
61 return repo.lookup(str(start)), end
62
63 def revrange(ui, repo, revs):
64 """Yield revision as strings from a list of revision specifications."""
65 seen = {}
66 for spec in revs:
67 if revrangesep in spec:
68 start, end = spec.split(revrangesep, 1)
69 start = revfix(repo, start, 0)
70 end = revfix(repo, end, repo.changelog.count() - 1)
71 step = start > end and -1 or 1
72 for rev in xrange(start, end+step, step):
73 if rev in seen:
74 continue
75 seen[rev] = 1
76 yield str(rev)
77 else:
78 rev = revfix(repo, spec, None)
79 if rev in seen:
80 continue
81 seen[rev] = 1
82 yield str(rev)
83
14 84 def make_filename(repo, pat, node,
15 85 total=None, seqno=None, revwidth=None, pathname=None):
16 86 node_expander = {
@@ -53,8 +123,8 b' def make_filename(repo, pat, node,'
53 123 i += 1
54 124 return ''.join(newname)
55 125 except KeyError, inst:
56 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
57 inst.args[0])
126 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
127 inst.args[0])
58 128
59 129 def make_file(repo, pat, node=None,
60 130 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
@@ -8,7 +8,7 b''
8 8 from demandload import demandload
9 9 from node import *
10 10 from i18n import gettext as _
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
11 demandload(globals(), "os re sys signal shutil imp urllib pdb shlex")
12 12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
13 13 demandload(globals(), "fnmatch difflib patch random signal tempfile time")
14 14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
@@ -50,6 +50,21 b' def logmessage(opts):'
50 50 (logfile, inst.strerror))
51 51 return message
52 52
53 def defaultrev(repo, rev=None, default='tip'):
54 """returns rev if it is specified, otherwise the working dir
55 parent if there is only one, or tip if there is no working
56 dir"""
57 if rev:
58 return rev
59
60 p1, p2 = repo.dirstate.parents()
61 if p2 != nullid:
62 raise util.Abort(_('uncommitted merge - please provide a '
63 'specific revision'))
64 if p1 != nullid:
65 return hex(p1)
66 return default
67
53 68 def walkchangerevs(ui, repo, pats, opts):
54 69 '''Iterate over files and the revs they changed in.
55 70
@@ -99,16 +114,10 b' def walkchangerevs(ui, repo, pats, opts)'
99 114 return [], False, matchfn
100 115
101 116 if follow:
102 p = repo.dirstate.parents()[0]
103 if p == nullid:
104 ui.warn(_('No working directory revision; defaulting to tip\n'))
105 start = 'tip'
106 else:
107 start = repo.changelog.rev(p)
108 defrange = '%s:0' % start
117 defrange = '%s:0' % defaultrev(repo)
109 118 else:
110 119 defrange = 'tip:0'
111 revs = map(int, revrange(ui, repo, opts['rev'] or [defrange]))
120 revs = map(int, cmdutil.revrange(ui, repo, opts['rev'] or [defrange]))
112 121 wanted = {}
113 122 slowpath = anypats
114 123 fncache = {}
@@ -252,76 +261,6 b' def walkchangerevs(ui, repo, pats, opts)'
252 261 yield 'iter', rev, None
253 262 return iterate(), getchange, matchfn
254 263
255 revrangesep = ':'
256
257 def revfix(repo, val, defval):
258 '''turn user-level id of changeset into rev number.
259 user-level id can be tag, changeset, rev number, or negative rev
260 number relative to number of revs (-1 is tip, etc).'''
261 if not val:
262 return defval
263 try:
264 num = int(val)
265 if str(num) != val:
266 raise ValueError
267 if num < 0:
268 num += repo.changelog.count()
269 if num < 0:
270 num = 0
271 elif num >= repo.changelog.count():
272 raise ValueError
273 except ValueError:
274 try:
275 num = repo.changelog.rev(repo.lookup(val))
276 except KeyError:
277 raise util.Abort(_('invalid revision identifier %s'), val)
278 return num
279
280 def revpair(ui, repo, revs):
281 '''return pair of nodes, given list of revisions. second item can
282 be None, meaning use working dir.'''
283 if not revs:
284 return repo.dirstate.parents()[0], None
285 end = None
286 if len(revs) == 1:
287 start = revs[0]
288 if revrangesep in start:
289 start, end = start.split(revrangesep, 1)
290 start = revfix(repo, start, 0)
291 end = revfix(repo, end, repo.changelog.count() - 1)
292 else:
293 start = revfix(repo, start, None)
294 elif len(revs) == 2:
295 if revrangesep in revs[0] or revrangesep in revs[1]:
296 raise util.Abort(_('too many revisions specified'))
297 start = revfix(repo, revs[0], None)
298 end = revfix(repo, revs[1], None)
299 else:
300 raise util.Abort(_('too many revisions specified'))
301 if end is not None: end = repo.lookup(str(end))
302 return repo.lookup(str(start)), end
303
304 def revrange(ui, repo, revs):
305 """Yield revision as strings from a list of revision specifications."""
306 seen = {}
307 for spec in revs:
308 if revrangesep in spec:
309 start, end = spec.split(revrangesep, 1)
310 start = revfix(repo, start, 0)
311 end = revfix(repo, end, repo.changelog.count() - 1)
312 step = start > end and -1 or 1
313 for rev in xrange(start, end+step, step):
314 if rev in seen:
315 continue
316 seen[rev] = 1
317 yield str(rev)
318 else:
319 rev = revfix(repo, spec, None)
320 if rev in seen:
321 continue
322 seen[rev] = 1
323 yield str(rev)
324
325 264 def write_bundle(cg, filename=None, compress=True):
326 265 """Write a bundle file and return its filename.
327 266
@@ -341,7 +280,7 b' def write_bundle(cg, filename=None, comp'
341 280 try:
342 281 if filename:
343 282 if os.path.exists(filename):
344 raise util.Abort(_("file '%s' already exists"), filename)
283 raise util.Abort(_("file '%s' already exists") % filename)
345 284 fh = open(filename, "wb")
346 285 else:
347 286 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
@@ -505,7 +444,7 b' def help_(ui, name=None, with_version=Fa'
505 444 if with_version:
506 445 show_version(ui)
507 446 ui.write('\n')
508 aliases, i = findcmd(name)
447 aliases, i = findcmd(ui, name)
509 448 # synopsis
510 449 ui.write("%s\n\n" % i[2])
511 450
@@ -707,7 +646,7 b' def annotate(ui, repo, *pats, **opts):'
707 646 if not opts['user'] and not opts['changeset'] and not opts['date']:
708 647 opts['number'] = 1
709 648
710 ctx = repo.changectx(opts['rev'] or repo.dirstate.parents()[0])
649 ctx = repo.changectx(defaultrev(repo, opts['rev']))
711 650
712 651 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
713 652 node=ctx.node()):
@@ -754,14 +693,7 b' def archive(ui, repo, dest, **opts):'
754 693 The default is the basename of the archive, with suffixes removed.
755 694 '''
756 695
757 if opts['rev']:
758 node = repo.lookup(opts['rev'])
759 else:
760 node, p2 = repo.dirstate.parents()
761 if p2 != nullid:
762 raise util.Abort(_('uncommitted merge - please provide a '
763 'specific revision'))
764
696 node = repo.lookup(defaultrev(repo, opts['rev']))
765 697 dest = cmdutil.make_filename(repo, dest, node)
766 698 if os.path.realpath(dest) == repo.root:
767 699 raise util.Abort(_('repository root cannot be destination'))
@@ -818,6 +750,7 b' def backout(ui, repo, rev, **opts):'
818 750 parent = p1
819 751 hg.clean(repo, node, show_stats=False)
820 752 revert_opts = opts.copy()
753 revert_opts['all'] = True
821 754 revert_opts['rev'] = hex(parent)
822 755 revert(ui, repo, **revert_opts)
823 756 commit_opts = opts.copy()
@@ -866,7 +799,8 b' def cat(ui, repo, file1, *pats, **opts):'
866 799 """output the latest or given revisions of files
867 800
868 801 Print the specified files as they were at the given revision.
869 If no revision is given then the tip is used.
802 If no revision is given then working dir parent is used, or tip
803 if no revision is checked out.
870 804
871 805 Output may be to a file, in which case the name of the file is
872 806 given using a format string. The formatting rules are the same as
@@ -876,7 +810,7 b' def cat(ui, repo, file1, *pats, **opts):'
876 810 %d dirname of file being printed, or '.' if in repo root
877 811 %p root-relative path name of file being printed
878 812 """
879 ctx = repo.changectx(opts['rev'] or "-1")
813 ctx = repo.changectx(defaultrev(repo, opts['rev']))
880 814 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
881 815 ctx.node()):
882 816 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
@@ -1151,7 +1085,7 b" def debugcomplete(ui, cmd='', **opts):"
1151 1085 options = []
1152 1086 otables = [globalopts]
1153 1087 if cmd:
1154 aliases, entry = findcmd(cmd)
1088 aliases, entry = findcmd(ui, cmd)
1155 1089 otables.append(entry[1])
1156 1090 for t in otables:
1157 1091 for o in t:
@@ -1161,7 +1095,7 b" def debugcomplete(ui, cmd='', **opts):"
1161 1095 ui.write("%s\n" % "\n".join(options))
1162 1096 return
1163 1097
1164 clist = findpossible(cmd).keys()
1098 clist = findpossible(ui, cmd).keys()
1165 1099 clist.sort()
1166 1100 ui.write("%s\n" % "\n".join(clist))
1167 1101
@@ -1268,7 +1202,7 b' def debugdata(ui, file_, rev):'
1268 1202 try:
1269 1203 ui.write(r.revision(r.lookup(rev)))
1270 1204 except KeyError:
1271 raise util.Abort(_('invalid revision identifier %s'), rev)
1205 raise util.Abort(_('invalid revision identifier %s') % rev)
1272 1206
1273 1207 def debugindex(ui, file_):
1274 1208 """dump the contents of an index file"""
@@ -1343,7 +1277,7 b' def diff(ui, repo, *pats, **opts):'
1343 1277 it detects as binary. With -a, diff will generate a diff anyway,
1344 1278 probably with undesirable results.
1345 1279 """
1346 node1, node2 = revpair(ui, repo, opts['rev'])
1280 node1, node2 = cmdutil.revpair(ui, repo, opts['rev'])
1347 1281
1348 1282 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1349 1283
@@ -1379,7 +1313,7 b' def export(ui, repo, *changesets, **opts'
1379 1313 """
1380 1314 if not changesets:
1381 1315 raise util.Abort(_("export requires at least one changeset"))
1382 revs = list(revrange(ui, repo, changesets))
1316 revs = list(cmdutil.revrange(ui, repo, changesets))
1383 1317 if len(revs) > 1:
1384 1318 ui.note(_('exporting patches:\n'))
1385 1319 else:
@@ -1939,7 +1873,7 b' def merge(ui, repo, node=None, force=Non'
1939 1873 revision to merge with must be provided.
1940 1874 """
1941 1875
1942 if node:
1876 if node or branch:
1943 1877 node = _lookup(repo, node, branch)
1944 1878 else:
1945 1879 heads = repo.heads()
@@ -2271,8 +2205,8 b' def revert(ui, repo, *pats, **opts):'
2271 2205 Modified files are saved with a .orig suffix before reverting.
2272 2206 To disable these backups, use --no-backup.
2273 2207
2274 Using the -r option, revert the given files or directories to
2275 their contents as of a specific revision. This can be helpful to"roll
2208 Using the -r option, revert the given files or directories to their
2209 contents as of a specific revision. This can be helpful to "roll
2276 2210 back" some or all of a change that should not have been committed.
2277 2211
2278 2212 Revert modifies the working directory. It does not commit any
@@ -2286,16 +2220,15 b' def revert(ui, repo, *pats, **opts):'
2286 2220
2287 2221 If names are given, all files matching the names are reverted.
2288 2222
2289 If no arguments are given, all files in the repository are reverted.
2223 If no arguments are given, no files are reverted.
2290 2224 """
2225
2226 if not pats and not opts['all']:
2227 raise util.Abort(_('no files or directories specified; '
2228 'use --all to revert the whole repo'))
2229
2291 2230 parent, p2 = repo.dirstate.parents()
2292 if opts['rev']:
2293 node = repo.lookup(opts['rev'])
2294 elif p2 != nullid:
2295 raise util.Abort(_('working dir has two parents; '
2296 'you must specify the revision to revert to'))
2297 else:
2298 node = parent
2231 node = repo.lookup(defaultrev(repo, opts['rev']))
2299 2232 mf = repo.manifest.read(repo.changelog.read(node)[0])
2300 2233 if node == parent:
2301 2234 pmf = mf
@@ -2452,7 +2385,8 b' def serve(ui, repo, **opts):'
2452 2385
2453 2386 if opts["stdio"]:
2454 2387 if repo is None:
2455 raise hg.RepoError(_('no repo found'))
2388 raise hg.RepoError(_("There is no Mercurial repository here"
2389 " (.hg not found)"))
2456 2390 s = sshserver.sshserver(ui, repo)
2457 2391 s.serve_forever()
2458 2392
@@ -2463,7 +2397,8 b' def serve(ui, repo, **opts):'
2463 2397 ui.setconfig("web", o, opts[o])
2464 2398
2465 2399 if repo is None and not ui.config("web", "webdir_conf"):
2466 raise hg.RepoError(_('no repo found'))
2400 raise hg.RepoError(_("There is no Mercurial repository here"
2401 " (.hg not found)"))
2467 2402
2468 2403 if opts['daemon'] and not opts['daemon_pipefds']:
2469 2404 rfd, wfd = os.pipe()
@@ -2478,7 +2413,7 b' def serve(ui, repo, **opts):'
2478 2413 try:
2479 2414 httpd = hgweb.server.create_server(ui, repo)
2480 2415 except socket.error, inst:
2481 raise util.Abort(_('cannot start server: ') + inst.args[1])
2416 raise util.Abort(_('cannot start server: %s') % inst.args[1])
2482 2417
2483 2418 if ui.verbose:
2484 2419 addr, port = httpd.socket.getsockname()
@@ -2593,15 +2528,10 b' def tag(ui, repo, name, rev_=None, **opt'
2593 2528 raise util.Abort(_("use only one form to specify the revision"))
2594 2529 if opts['rev']:
2595 2530 rev_ = opts['rev']
2596 if rev_:
2597 r = repo.lookup(rev_)
2598 else:
2599 p1, p2 = repo.dirstate.parents()
2600 if p1 == nullid:
2601 raise util.Abort(_('no revision to tag'))
2602 if p2 != nullid:
2603 raise util.Abort(_('outstanding uncommitted merges'))
2604 r = p1
2531 r = defaultrev(repo, rev_, nullid)
2532 if r == nullid:
2533 raise util.Abort(_('no revision to tag'))
2534 r = repo.lookup(r)
2605 2535
2606 2536 message = opts['message']
2607 2537 if not message:
@@ -2728,7 +2658,7 b' def _lookup(repo, node, branch=None):'
2728 2658 repo.ui.warn(_("Using head %s for branch %s\n")
2729 2659 % (short(node), branch))
2730 2660 else:
2731 raise util.Abort(_("branch %s not found\n") % (branch))
2661 raise util.Abort(_("branch %s not found") % branch)
2732 2662 else:
2733 2663 node = node and repo.lookup(node) or repo.changelog.tip()
2734 2664 return node
@@ -2881,6 +2811,7 b' table = {'
2881 2811 (export,
2882 2812 [('o', 'output', '', _('print output to file with formatted name')),
2883 2813 ('a', 'text', None, _('treat all files as text')),
2814 ('g', 'git', None, _('use git extended diff format')),
2884 2815 ('', 'switch-parent', None, _('diff against the second parent'))],
2885 2816 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2886 2817 "debugforget|forget":
@@ -3046,7 +2977,8 b' table = {'
3046 2977 _('hg rename [OPTION]... SOURCE... DEST')),
3047 2978 "^revert":
3048 2979 (revert,
3049 [('r', 'rev', '', _('revision to revert to')),
2980 [('a', 'all', None, _('revert all changes when no arguments given')),
2981 ('r', 'rev', '', _('revision to revert to')),
3050 2982 ('', 'no-backup', None, _('do not save backup copies of files')),
3051 2983 ('I', 'include', [], _('include names matching given patterns')),
3052 2984 ('X', 'exclude', [], _('exclude names matching given patterns')),
@@ -3145,7 +3077,7 b' norepo = ("clone init version help debug'
3145 3077 " debugindex debugindexdot")
3146 3078 optionalrepo = ("paths serve debugconfig")
3147 3079
3148 def findpossible(cmd):
3080 def findpossible(ui, cmd):
3149 3081 """
3150 3082 Return cmd -> (aliases, command table entry)
3151 3083 for each matching command.
@@ -3158,7 +3090,7 b' def findpossible(cmd):'
3158 3090 found = None
3159 3091 if cmd in aliases:
3160 3092 found = cmd
3161 else:
3093 elif not ui.config("ui", "strict"):
3162 3094 for a in aliases:
3163 3095 if a.startswith(cmd):
3164 3096 found = a
@@ -3174,9 +3106,9 b' def findpossible(cmd):'
3174 3106
3175 3107 return choice
3176 3108
3177 def findcmd(cmd):
3109 def findcmd(ui, cmd):
3178 3110 """Return (aliases, command table entry) for command string."""
3179 choice = findpossible(cmd)
3111 choice = findpossible(ui, cmd)
3180 3112
3181 3113 if choice.has_key(cmd):
3182 3114 return choice[cmd]
@@ -3211,11 +3143,11 b' def parse(ui, args):'
3211 3143
3212 3144 if args:
3213 3145 cmd, args = args[0], args[1:]
3214 aliases, i = findcmd(cmd)
3146 aliases, i = findcmd(ui, cmd)
3215 3147 cmd = aliases[0]
3216 3148 defaults = ui.config("defaults", cmd)
3217 3149 if defaults:
3218 args = defaults.split() + args
3150 args = shlex.split(defaults) + args
3219 3151 c = list(i[1])
3220 3152 else:
3221 3153 cmd = None
@@ -3299,12 +3231,14 b' def dispatch(args):'
3299 3231 if num: signal.signal(num, catchterm)
3300 3232
3301 3233 try:
3302 u = ui.ui(traceback='--traceback' in sys.argv[1:],
3303 readhooks=[load_extensions])
3234 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3304 3235 except util.Abort, inst:
3305 3236 sys.stderr.write(_("abort: %s\n") % inst)
3306 3237 return -1
3307 3238
3239 load_extensions(u)
3240 u.addreadhook(load_extensions)
3241
3308 3242 try:
3309 3243 cmd, func, args, options, cmdoptions = parse(u, args)
3310 3244 if options["time"]:
@@ -3439,7 +3373,7 b' def dispatch(args):'
3439 3373 u.warn(_("abort: could not lock %s: %s\n") %
3440 3374 (inst.desc or inst.filename, inst.strerror))
3441 3375 except revlog.RevlogError, inst:
3442 u.warn(_("abort: "), inst, "!\n")
3376 u.warn(_("abort: %s!\n") % inst)
3443 3377 except util.SignalInterrupt:
3444 3378 u.warn(_("killed!\n"))
3445 3379 except KeyboardInterrupt:
@@ -3461,18 +3395,18 b' def dispatch(args):'
3461 3395 u.warn(_("broken pipe\n"))
3462 3396 elif getattr(inst, "strerror", None):
3463 3397 if getattr(inst, "filename", None):
3464 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3398 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3465 3399 else:
3466 3400 u.warn(_("abort: %s\n") % inst.strerror)
3467 3401 else:
3468 3402 raise
3469 3403 except OSError, inst:
3470 if hasattr(inst, "filename"):
3404 if getattr(inst, "filename", None):
3471 3405 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3472 3406 else:
3473 3407 u.warn(_("abort: %s\n") % inst.strerror)
3474 3408 except util.Abort, inst:
3475 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3409 u.warn(_("abort: %s\n") % inst)
3476 3410 except TypeError, inst:
3477 3411 # was this an argument error?
3478 3412 tb = traceback.extract_tb(sys.exc_info()[2])
@@ -115,7 +115,7 b' def clone(ui, source, dest=None, pull=Fa'
115 115 source = localpath(source)
116 116
117 117 if os.path.exists(dest):
118 raise util.Abort(_("destination '%s' already exists"), dest)
118 raise util.Abort(_("destination '%s' already exists") % dest)
119 119
120 120 class DirCleanup(object):
121 121 def __init__(self, dir_):
@@ -127,12 +127,7 b' def clone(ui, source, dest=None, pull=Fa'
127 127 if self.dir_:
128 128 self.rmtree(self.dir_, True)
129 129
130 dest_repo = None
131 try:
132 dest_repo = repository(ui, dest)
133 raise util.Abort(_("destination '%s' already exists." % dest))
134 except RepoError:
135 dest_repo = repository(ui, dest, create=True)
130 dest_repo = repository(ui, dest, create=True)
136 131
137 132 dest_path = None
138 133 dir_cleanup = None
@@ -207,7 +207,8 b' def create_server(ui, repo):'
207 207 hgwebobj = self.repoviewmaker(repo.__class__(repo.ui,
208 208 repo.origroot))
209 209 else:
210 raise hg.RepoError(_('no repo found'))
210 raise hg.RepoError(_("There is no Mercurial repository here"
211 " (.hg not found)"))
211 212 return hgwebobj
212 213
213 214 class IPv6HTTPServer(MercurialHTTPServer):
@@ -325,7 +325,7 b' class httprepository(remoterepository):'
325 325 rfp.close()
326 326 except socket.error, err:
327 327 if err[0] in (errno.ECONNRESET, errno.EPIPE):
328 raise util.Abort(_('push failed: %s'), err[1])
328 raise util.Abort(_('push failed: %s') % err[1])
329 329 raise util.Abort(err[1])
330 330 finally:
331 331 fp.close()
@@ -27,12 +27,21 b' class localrepository(repo.repository):'
27 27 oldp = p
28 28 p = os.path.dirname(p)
29 29 if p == oldp:
30 raise repo.RepoError(_("no repo found"))
30 raise repo.RepoError(_("There is no Mercurial repository"
31 " here (.hg not found)"))
31 32 path = p
32 33 self.path = os.path.join(path, ".hg")
33 34
34 if not create and not os.path.isdir(self.path):
35 raise repo.RepoError(_("repository %s not found") % path)
35 if not os.path.isdir(self.path):
36 if create:
37 if not os.path.exists(path):
38 os.mkdir(path)
39 os.mkdir(self.path)
40 os.mkdir(self.join("data"))
41 else:
42 raise repo.RepoError(_("repository %s not found") % path)
43 elif create:
44 raise repo.RepoError(_("repository %s already exists") % path)
36 45
37 46 self.root = os.path.abspath(path)
38 47 self.origroot = path
@@ -75,12 +84,6 b' class localrepository(repo.repository):'
75 84 self.decodepats = None
76 85 self.transhandle = None
77 86
78 if create:
79 if not os.path.exists(path):
80 os.mkdir(path)
81 os.mkdir(self.path)
82 os.mkdir(self.join("data"))
83
84 87 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
85 88
86 89 def url(self):
@@ -131,7 +134,7 b' class localrepository(repo.repository):'
131 134 except Exception, exc:
132 135 if isinstance(exc, util.Abort):
133 136 self.ui.warn(_('error: %s hook failed: %s\n') %
134 (hname, exc.args[0] % exc.args[1:]))
137 (hname, exc.args[0]))
135 138 else:
136 139 self.ui.warn(_('error: %s hook raised an exception: '
137 140 '%s\n') % (hname, exc))
@@ -641,7 +644,11 b' class localrepository(repo.repository):'
641 644 if node:
642 645 fdict = dict.fromkeys(files)
643 646 for fn in self.manifest.read(self.changelog.read(node)[0]):
644 fdict.pop(fn, None)
647 for ffn in fdict:
648 # match if the file is the exact name or a directory
649 if ffn == fn or fn.startswith("%s/" % ffn):
650 del fdict[ffn]
651 break
645 652 if match(fn):
646 653 yield 'm', fn
647 654 for fn in fdict:
@@ -50,6 +50,9 b' class diffopts(object):'
50 50 defaultopts = diffopts()
51 51
52 52 def unidiff(a, ad, b, bd, fn, r=None, opts=defaultopts):
53 def datetag(date):
54 return opts.git and '\n' or '\t%s\n' % date
55
53 56 if not a and not b: return ""
54 57 epoch = util.datestr((0, 0))
55 58
@@ -58,19 +61,19 b' def unidiff(a, ad, b, bd, fn, r=None, op'
58 61 elif not a:
59 62 b = splitnewlines(b)
60 63 if a is None:
61 l1 = "--- %s\t%s\n" % ("/dev/null", epoch)
64 l1 = '--- /dev/null%s' % datetag(epoch)
62 65 else:
63 l1 = "--- %s\t%s\n" % ("a/" + fn, ad)
64 l2 = "+++ %s\t%s\n" % ("b/" + fn, bd)
66 l1 = "--- %s%s" % ("a/" + fn, datetag(ad))
67 l2 = "+++ %s%s" % ("b/" + fn, datetag(bd))
65 68 l3 = "@@ -0,0 +1,%d @@\n" % len(b)
66 69 l = [l1, l2, l3] + ["+" + e for e in b]
67 70 elif not b:
68 71 a = splitnewlines(a)
69 l1 = "--- %s\t%s\n" % ("a/" + fn, ad)
72 l1 = "--- %s%s" % ("a/" + fn, datetag(ad))
70 73 if b is None:
71 l2 = "+++ %s\t%s\n" % ("/dev/null", epoch)
74 l2 = '+++ /dev/null%s' % datetag(epoch)
72 75 else:
73 l2 = "+++ %s\t%s\n" % ("b/" + fn, bd)
76 l2 = "+++ %s%s" % ("b/" + fn, datetag(bd))
74 77 l3 = "@@ -1,%d +0,0 @@\n" % len(a)
75 78 l = [l1, l2, l3] + ["-" + e for e in a]
76 79 else:
@@ -79,8 +82,8 b' def unidiff(a, ad, b, bd, fn, r=None, op'
79 82 l = list(bunidiff(a, b, al, bl, "a/" + fn, "b/" + fn, opts=opts))
80 83 if not l: return ""
81 84 # difflib uses a space, rather than a tab
82 l[0] = "%s\t%s\n" % (l[0][:-2], ad)
83 l[1] = "%s\t%s\n" % (l[1][:-2], bd)
85 l[0] = "%s%s" % (l[0][:-2], datetag(ad))
86 l[1] = "%s%s" % (l[1][:-2], datetag(bd))
84 87
85 88 for ln in xrange(len(l)):
86 89 if l[ln][-1] != '\n':
@@ -8,7 +8,7 b''
8 8 from node import *
9 9 from i18n import gettext as _
10 10 from demandload import *
11 demandload(globals(), "util os tempfile")
11 demandload(globals(), "errno util os tempfile")
12 12
13 13 def fmerge(f, local, other, ancestor):
14 14 """merge executable flags"""
@@ -166,7 +166,8 b' def update(repo, node, branchmerge=False'
166 166 repo.ui.debug(_(" updating permissions for %s\n") % f)
167 167 util.set_exec(repo.wjoin(f), m2.execf(f))
168 168 else:
169 if fmerge(f, m1, m2, ma) != m1.execf(f):
169 mode = fmerge(f, m1, m2, ma)
170 if mode != m1.execf(f):
170 171 repo.ui.debug(_(" updating permissions for %s\n")
171 172 % f)
172 173 util.set_exec(repo.wjoin(f), mode)
@@ -26,6 +26,7 b' def demandload(scope, modules):'
26 26 foo import foo
27 27 foo bar import foo, bar
28 28 foo.bar import foo.bar
29 foo@bar import foo as bar
29 30 foo:bar from foo import bar
30 31 foo:bar,quux from foo import bar, quux
31 32 foo.bar:quux from foo.bar import quux"""
@@ -38,6 +39,9 b' def demandload(scope, modules):'
38 39 except:
39 40 module = m
40 41 fromlist = []
42 as_ = None
43 if '@' in module:
44 module, as_ = module.split('@')
41 45 mod = __import__(module, scope, scope, fromlist)
42 46 if fromlist == []:
43 47 # mod is only the top package, but we need all packages
@@ -46,7 +50,9 b' def demandload(scope, modules):'
46 50 mn = comp[0]
47 51 while True:
48 52 # mn and mod.__name__ might not be the same
49 scope[mn] = mod
53 if not as_:
54 as_ = mn
55 scope[as_] = mod
50 56 requiredmodules[mod.__name__] = 1
51 57 if len(comp) == i: break
52 58 mod = getattr(mod,comp[i])
@@ -9,7 +9,8 b' from demandload import demandload'
9 9 from i18n import gettext as _
10 10 from node import *
11 11 demandload(globals(), "cmdutil mdiff util")
12 demandload(globals(), "cStringIO email.Parser errno os re shutil sys tempfile")
12 demandload(globals(), '''cStringIO email.Parser errno os re shutil sys tempfile
13 popen2''')
13 14
14 15 # helper functions
15 16
@@ -182,7 +183,7 b' def readgitpatch(patchname):'
182 183
183 184 return (dopatch, gitpatches)
184 185
185 def dogitpatch(patchname, gitpatches):
186 def dogitpatch(patchname, gitpatches, cwd=None):
186 187 """Preprocess git patch so that vanilla patch can handle it"""
187 188 pf = file(patchname)
188 189 pfline = 1
@@ -196,7 +197,7 b' def dogitpatch(patchname, gitpatches):'
196 197 if not p.copymod:
197 198 continue
198 199
199 copyfile(p.oldpath, p.path)
200 copyfile(p.oldpath, p.path, basedir=cwd)
200 201
201 202 # rewrite patch hunk
202 203 while pfline < p.lineno:
@@ -227,23 +228,20 b' def patch(patchname, ui, strip=1, cwd=No'
227 228 """apply the patch <patchname> to the working directory.
228 229 a list of patched files is returned"""
229 230
230 (dopatch, gitpatches) = readgitpatch(patchname)
231 # helper function
232 def __patch(patchname):
233 """patch and updates the files and fuzz variables"""
234 files = {}
235 fuzz = False
231 236
232 files = {}
233 fuzz = False
234 if dopatch:
235 if dopatch == 'filter':
236 patchname = dogitpatch(patchname, gitpatches)
237 patcher = util.find_in_path('gpatch', os.environ.get('PATH', ''), 'patch')
237 patcher = util.find_in_path('gpatch', os.environ.get('PATH', ''),
238 'patch')
238 239 args = []
239 240 if cwd:
240 241 args.append('-d %s' % util.shellquote(cwd))
241 242 fp = os.popen('%s %s -p%d < %s' % (patcher, ' '.join(args), strip,
242 243 util.shellquote(patchname)))
243 244
244 if dopatch == 'filter':
245 False and os.unlink(patchname)
246
247 245 for line in fp:
248 246 line = line.rstrip()
249 247 ui.note(line + '\n')
@@ -264,11 +262,24 b' def patch(patchname, ui, strip=1, cwd=No'
264 262 ui.warn(pf + '\n')
265 263 printed_file = True
266 264 ui.warn(line + '\n')
267
268 265 code = fp.close()
269 266 if code:
270 267 raise util.Abort(_("patch command failed: %s") %
271 268 util.explain_exit(code)[0])
269 return files, fuzz
270
271 (dopatch, gitpatches) = readgitpatch(patchname)
272
273 if dopatch:
274 if dopatch == 'filter':
275 patchname = dogitpatch(patchname, gitpatches, cwd=cwd)
276 try:
277 files, fuzz = __patch(patchname)
278 finally:
279 if dopatch == 'filter':
280 os.unlink(patchname)
281 else:
282 files, fuzz = {}, False
272 283
273 284 for gp in gitpatches:
274 285 files[gp.path] = (gp.op, gp)
@@ -492,7 +503,10 b' def diff(repo, node1=None, node2=None, f'
492 503 header.append('deleted file mode %s\n' % mode)
493 504 else:
494 505 omode = gitmode(mmap.execf(f))
495 nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f)))
506 if node2:
507 nmode = gitmode(mmap2.execf(f))
508 else:
509 nmode = gitmode(util.is_exec(repo.wjoin(f), mmap.execf(f)))
496 510 addmodehdr(header, omode, nmode)
497 511 r = None
498 512 if dodiff:
@@ -537,3 +551,24 b" def export(repo, revs, template='hg-%h.p"
537 551
538 552 for seqno, cset in enumerate(revs):
539 553 single(cset, seqno, fp)
554
555 def diffstat(patchlines):
556 fd, name = tempfile.mkstemp(prefix="hg-patchbomb-", suffix=".txt")
557 try:
558 p = popen2.Popen3('diffstat -p1 -w79 2>/dev/null > ' + name)
559 try:
560 for line in patchlines: print >> p.tochild, line
561 p.tochild.close()
562 if p.wait(): return
563 fp = os.fdopen(fd, 'r')
564 stat = []
565 for line in fp: stat.append(line.lstrip())
566 last = stat.pop()
567 stat.insert(0, last)
568 stat = ''.join(stat)
569 if stat.startswith('0 files'): raise ValueError
570 return stat
571 except: raise
572 finally:
573 try: os.unlink(name)
574 except: pass
@@ -139,6 +139,11 b' class lazyparser(object):'
139 139 if self.all: return
140 140 if data is None:
141 141 self.dataf.seek(blockstart)
142 if blockstart + blocksize > self.datasize:
143 # the revlog may have grown since we've started running,
144 # but we don't have space in self.index for more entries.
145 # limit blocksize so that we don't get too much data.
146 blocksize = max(self.datasize - blockstart, 0)
142 147 data = self.dataf.read(blocksize)
143 148 lend = len(data) / self.s
144 149 i = blockstart / self.s
@@ -32,12 +32,6 b' class sshrepository(remoterepository):'
32 32 remotecmd = self.ui.config("ui", "remotecmd", "hg")
33 33
34 34 if create:
35 try:
36 self.validate_repo(ui, sshcmd, args, remotecmd)
37 return # the repo is good, nothing more to do
38 except hg.RepoError:
39 pass
40
41 35 cmd = '%s %s "%s init %s"'
42 36 cmd = cmd % (sshcmd, args, remotecmd, self.path)
43 37
@@ -52,6 +46,9 b' class sshrepository(remoterepository):'
52 46 return self._url
53 47
54 48 def validate_repo(self, ui, sshcmd, args, remotecmd):
49 # cleanup up previous run
50 self.cleanup()
51
55 52 cmd = '%s %s "%s -R %s serve --stdio"'
56 53 cmd = cmd % (sshcmd, args, remotecmd, self.path)
57 54
@@ -90,7 +87,7 b' class sshrepository(remoterepository):'
90 87 if not l: break
91 88 self.ui.status(_("remote: "), l)
92 89
93 def __del__(self):
90 def cleanup(self):
94 91 try:
95 92 self.pipeo.close()
96 93 self.pipei.close()
@@ -101,6 +98,8 b' class sshrepository(remoterepository):'
101 98 except:
102 99 pass
103 100
101 __del__ = cleanup
102
104 103 def do_cmd(self, cmd, **args):
105 104 self.ui.debug(_("sending %s command\n") % cmd)
106 105 self.pipeo.write("%s\n" % cmd)
@@ -12,13 +12,14 b' demandload(globals(), "ConfigParser mdif'
12 12
13 13 class ui(object):
14 14 def __init__(self, verbose=False, debug=False, quiet=False,
15 interactive=True, traceback=False, parentui=None,
16 readhooks=[]):
15 interactive=True, traceback=False, parentui=None):
17 16 self.overlay = {}
18 17 if parentui is None:
19 18 # this is the parent of all ui children
20 19 self.parentui = None
21 self.readhooks = list(readhooks)
20 self.readhooks = []
21 self.trusted_users = {}
22 self.trusted_groups = {}
22 23 self.cdata = ConfigParser.SafeConfigParser()
23 24 self.readconfig(util.rcpath())
24 25
@@ -36,7 +37,9 b' class ui(object):'
36 37 else:
37 38 # parentui may point to an ui object which is already a child
38 39 self.parentui = parentui.parentui or parentui
39 self.readhooks = list(parentui.readhooks or readhooks)
40 self.readhooks = parentui.readhooks[:]
41 self.trusted_users = parentui.trusted_users.copy()
42 self.trusted_groups = parentui.trusted_groups.copy()
40 43 parent_cdata = self.parentui.cdata
41 44 self.cdata = ConfigParser.SafeConfigParser(parent_cdata.defaults())
42 45 # make interpolation work
@@ -51,7 +54,7 b' class ui(object):'
51 54 def updateopts(self, verbose=False, debug=False, quiet=False,
52 55 interactive=True, traceback=False, config=[]):
53 56 self.quiet = (self.quiet or quiet) and not verbose and not debug
54 self.verbose = (self.verbose or verbose) or debug
57 self.verbose = ((self.verbose or verbose) or debug) and not self.quiet
55 58 self.debugflag = (self.debugflag or debug)
56 59 self.interactive = (self.interactive and interactive)
57 60 self.traceback = self.traceback or traceback
@@ -72,7 +75,22 b' class ui(object):'
72 75 fn = [fn]
73 76 for f in fn:
74 77 try:
75 self.cdata.read(f)
78 fp = open(f)
79 except IOError:
80 continue
81 if ((self.trusted_users or self.trusted_groups) and
82 '*' not in self.trusted_users and
83 '*' not in self.trusted_groups):
84 st = util.fstat(fp)
85 user = util.username(st.st_uid)
86 group = util.groupname(st.st_gid)
87 if (user not in self.trusted_users and
88 group not in self.trusted_groups):
89 self.warn(_('not reading file %s from untrusted '
90 'user %s, group %s\n') % (f, user, group))
91 continue
92 try:
93 self.cdata.readfp(fp, f)
76 94 except ConfigParser.ParsingError, inst:
77 95 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
78 96 # translate paths relative to root (or home) into absolute paths
@@ -81,9 +99,19 b' class ui(object):'
81 99 for name, path in self.configitems("paths"):
82 100 if path and "://" not in path and not os.path.isabs(path):
83 101 self.cdata.set("paths", name, os.path.join(root, path))
102 user = util.username()
103 if user is not None:
104 self.trusted_users[user] = 1
105 for user in self.configlist('trusted', 'users'):
106 self.trusted_users[user] = 1
107 for group in self.configlist('trusted', 'groups'):
108 self.trusted_groups[group] = 1
84 109 for hook in self.readhooks:
85 110 hook(self)
86 111
112 def addreadhook(self, hook):
113 self.readhooks.append(hook)
114
87 115 def setconfig(self, section, name, val):
88 116 self.overlay[(section, name)] = val
89 117
@@ -94,7 +122,9 b' class ui(object):'
94 122 try:
95 123 return self.cdata.get(section, name)
96 124 except ConfigParser.InterpolationError, inst:
97 raise util.Abort(_("Error in configuration:\n%s") % inst)
125 raise util.Abort(_("Error in configuration section [%s] "
126 "parameter '%s':\n%s")
127 % (section, name, inst))
98 128 if self.parentui is None:
99 129 return default
100 130 else:
@@ -116,7 +146,9 b' class ui(object):'
116 146 try:
117 147 return self.cdata.getboolean(section, name)
118 148 except ConfigParser.InterpolationError, inst:
119 raise util.Abort(_("Error in configuration:\n%s") % inst)
149 raise util.Abort(_("Error in configuration section [%s] "
150 "parameter '%s':\n%s")
151 % (section, name, inst))
120 152 if self.parentui is None:
121 153 return default
122 154 else:
@@ -134,7 +166,8 b' class ui(object):'
134 166 try:
135 167 items.update(dict(self.cdata.items(section)))
136 168 except ConfigParser.InterpolationError, inst:
137 raise util.Abort(_("Error in configuration:\n%s") % inst)
169 raise util.Abort(_("Error in configuration section [%s]:\n%s")
170 % (section, inst))
138 171 x = items.items()
139 172 x.sort()
140 173 return x
@@ -146,10 +179,14 b' class ui(object):'
146 179 yield section, name, value
147 180 seen[section, name] = 1
148 181 for section in self.cdata.sections():
149 for name, value in self.cdata.items(section):
150 if (section, name) in seen: continue
151 yield section, name, value.replace('\n', '\\n')
152 seen[section, name] = 1
182 try:
183 for name, value in self.cdata.items(section):
184 if (section, name) in seen: continue
185 yield section, name, value.replace('\n', '\\n')
186 seen[section, name] = 1
187 except ConfigParser.InterpolationError, inst:
188 raise util.Abort(_("Error in configuration section [%s]:\n%s")
189 % (section, inst))
153 190 if self.parentui is not None:
154 191 for parent in self.parentui.walkconfig(seen):
155 192 yield parent
@@ -15,7 +15,7 b' platform-specific details from the core.'
15 15 from i18n import gettext as _
16 16 from demandload import *
17 17 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
18 demandload(globals(), "os threading time")
18 demandload(globals(), "os threading time pwd grp")
19 19
20 20 # used by parsedate
21 21 defaultdateformats = ('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M',
@@ -509,6 +509,38 b' def getuser():'
509 509 raise Abort(_('user name not available - set USERNAME '
510 510 'environment variable'))
511 511
512 def username(uid=None):
513 """Return the name of the user with the given uid.
514
515 If uid is None, return the name of the current user."""
516 try:
517 # force an ImportError if there's no module pwd
518 getpwuid = pwd.getpwuid
519 if uid is None:
520 uid = os.getuid()
521 try:
522 return getpwuid(uid)[0]
523 except KeyError:
524 return str(uid)
525 except ImportError:
526 return None
527
528 def groupname(gid=None):
529 """Return the name of the group with the given gid.
530
531 If gid is None, return the name of the current group."""
532 try:
533 # force an ImportError if there's no module grp
534 getgrgid = grp.getgrgid
535 if gid is None:
536 gid = os.getgid()
537 try:
538 return getgrgid(gid)[0]
539 except KeyError:
540 return str(gid)
541 except ImportError:
542 return None
543
512 544 # Platform specific variants
513 545 if os.name == 'nt':
514 546 demandload(globals(), "msvcrt")
@@ -8,7 +8,7 b' error = error-gitweb.tmpl'
8 8 naventry = '<a href="?cmd=changelog;rev=#rev#;style=gitweb">#label|escape#</a> '
9 9 navshortentry = '<a href="?cmd=shortlog;rev=#rev#;style=gitweb">#label|escape#</a> '
10 10 filedifflink = '<a href="?cmd=filediff;node=#node#;file=#file|urlescape#;style=gitweb">#file|escape#</a> '
11 filenodelink = '<tr class="light"><td><a class="list" href="">#file|escape#</a></td><td></td><td class="link"><a href="?cmd=file;filenode=#filenode#;file=#file|urlescape#;style=gitweb">file</a> | <!-- FIXME: <a href="?fd=#filenode|short#;file=#file|urlescape#;style=gitweb">diff</a> | --> <a href="?cmd=filelog;filenode=#filenode|short#;file=#file|urlescape#;style=gitweb">revisions</a></td></tr>'
11 filenodelink = '<tr class="light"><td><a class="list" href="">#file|escape#</a></td><td></td><td class="link"><a href="?cmd=file;filenode=#filenode#;file=#file|urlescape#;style=gitweb">file</a> | <a href="?fa=#filenode|short#;file=#file|urlescape#;style=gitweb">annotate</a> | <!-- FIXME: <a href="?fd=#filenode|short#;file=#file|urlescape#;style=gitweb">diff</a> | --> <a href="?cmd=filelog;filenode=#filenode|short#;file=#file|urlescape#;style=gitweb">revisions</a></td></tr>'
12 12 fileellipses = '...'
13 13 changelogentry = changelogentry-gitweb.tmpl
14 14 searchentry = changelogentry-gitweb.tmpl
@@ -19,12 +19,12 b' manifestfileentry = \'<tr class="parity#p'
19 19 filerevision = filerevision-gitweb.tmpl
20 20 fileannotate = fileannotate-gitweb.tmpl
21 21 filelog = filelog-gitweb.tmpl
22 fileline = '<div style="font-family:monospace; white-space: pre;" class="parity#parity#"><span class="linenr"> #linenumber#</span> #line|escape#</div>'
23 annotateline = '<tr style="font-family:monospace; white-space: pre;" class="parity#parity#"><td class="linenr" style="text-align: right;"><a href="?cs=#node|short#;style=gitweb">#author|obfuscate#@#rev#</a></td><td>#line|escape#</td></tr>'
24 difflineplus = '<div class="pre" style="color:#008800;">#line|escape#</div>'
25 difflineminus = '<div class="pre" style="color:#cc0000;">#line|escape#</div>'
26 difflineat = '<div class="pre" style="color:#990099;">#line|escape#</div>'
27 diffline = '<div class="pre">#line|escape#</div>'
22 fileline = '<div style="font-family:monospace" class="parity#parity#"><pre><span class="linenr"> #linenumber#</span> #line|escape#</pre></div>'
23 annotateline = '<tr style="font-family:monospace" class="parity#parity#"><td class="linenr" style="text-align: right;"><a href="?cs=#node|short#;style=gitweb">#author|obfuscate#@#rev#</a></td><td><pre>#line|escape#</pre></td></tr>'
24 difflineplus = '<div style="color:#008800;">#line|escape#</div>'
25 difflineminus = '<div style="color:#cc0000;">#line|escape#</div>'
26 difflineat = '<div style="color:#990099;">#line|escape#</div>'
27 diffline = '<div>#line|escape#</div>'
28 28 changelogparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cmd=changeset;node=#node#;style=gitweb">#node|short#</a></td></tr>'
29 29 changesetparent = '<tr><td>parent</td><td style="font-family:monospace"><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb">#node|short#</a></td></tr>'
30 30 filerevparent = '<tr><td class="metatag">parent:</td><td><a href="?cmd=file;file=#file|urlescape#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>'
@@ -37,7 +37,7 b' filerevchild = \'<tr><td class="metatag">'
37 37 fileannotatechild = '<tr><td class="metatag">child:</td><td><a href="?cmd=annotate;file=#file|urlescape#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>'
38 38 tags = tags-gitweb.tmpl
39 39 tagentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#tag|escape#</b></a></td><td class="link"><a href="?cmd=changeset;node=#node|short#;style=gitweb">changeset</a> | <a href="?cmd=changelog;rev=#node|short#;style=gitweb">changelog</a> | <a href="?mf=#tagmanifest|short#;path=/;style=gitweb">manifest</a></td></tr>'
40 diffblock = '#lines#'
40 diffblock = '<pre>#lines#</pre>'
41 41 changelogtag = '<tr><th class="tag">tag:</th><td class="tag">#tag|escape#</td></tr>'
42 42 changesettag = '<tr><td>tag</td><td>#tag|escape#</td></tr>'
43 43 filediffparent = '<tr><th class="parent">parent #rev#:</th><td class="parent"><a href="?cmd=changeset;node=#node#;style=gitweb">#node|short#</a></td></tr>'
@@ -46,5 +46,5 b' filediffchild = \'<tr><th class="child">c'
46 46 filelogchild = '<tr><td align="right">child #rev#:&nbsp;</td><td><a href="?cmd=file;file=#file|urlescape#;filenode=#node#;style=gitweb">#node|short#</a></td></tr>'
47 47 shortlog = shortlog-gitweb.tmpl
48 48 shortlogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><i>#author#</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#desc|strip|firstline|escape#</b></a></td><td class="link"><a href="?cmd=changeset;node=#node|short#;style=gitweb">changeset</a> | <a href="?cmd=manifest;manifest=#manifest|short#;path=/;style=gitweb">manifest</a></td></tr>'
49 filelogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#desc|strip|firstline|escape#</b></a></td><td class="link"><!-- FIXME: <a href="?fd=#node|short#;file=#file|urlescape#;style=gitweb">diff</a> | --> <a href="?fa=#filenode|short#;file=#file|urlescape#;style=gitweb">annotate</a> #rename%filelogrename#</td></tr>'
49 filelogentry = '<tr class="parity#parity#"><td class="age"><i>#date|age# ago</i></td><td><a class="list" href="?cmd=changeset;node=#node|short#;style=gitweb"><b>#desc|strip|firstline|escape#</b></a></td><td class="link"><a href="?f=#node|short#;file=#file|urlescape#;style=gitweb">file</a> | <!-- FIXME: <a href="?fd=#node|short#;file=#file|urlescape#;style=gitweb">diff</a> | --> <a href="?fa=#filenode|short#;file=#file|urlescape#;style=gitweb">annotate</a> #rename%filelogrename#</td></tr>'
50 50 archiveentry = ' | <a href="?ca=#node|short#;type=#type|urlescape#">#type|escape#</a> '
@@ -47,3 +47,4 b' a.rss_logo {'
47 47 text-align:center; text-decoration:none;
48 48 }
49 49 a.rss_logo:hover { background-color:#ee5500; }
50 pre { margin: 0; }
@@ -31,3 +31,65 b' writing tests:'
31 31 use hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
32 32 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
33 33 to strip dates
34
35 - You can append your own hgrc settings to the file that the environment
36 variable HGRCPATH points to. This file is cleared before running a test.
37
38 You also need to be careful that the tests are portable from one platform
39 to another. You're probably working on Linux, where the GNU toolchain has
40 more (or different) functionality than on MacOS, *BSD, Solaris, AIX, etc.
41 While testing on all platforms is the only sure-fire way to make sure that
42 you've written portable code, here's a list of problems that have been
43 found and fixed in the tests. Another, more comprehensive list may be
44 found in the GNU Autoconf manual, online here:
45
46 http://www.gnu.org/software/autoconf/manual/html_node/Portable-Shell.html
47
48 sh:
49
50 The Bourne shell is a very basic shell. /bin/sh on Linux is typically
51 bash, which even in Bourne-shell mode has many features that Bourne shells
52 on other Unix systems don't have (and even on Linux /bin/sh isn't
53 guaranteed to be bash). You'll need to be careful about constructs that
54 seem ubiquitous, but are actually not available in the least common
55 denominator. While using another shell (ksh, bash explicitly, posix shell,
56 etc.) explicitly may seem like another option, these may not exist in a
57 portable location, and so are generally probably not a good idea. You may
58 find that rewriting the test in python will be easier.
59
60 - don't use pushd/popd; save the output of "pwd" and use "cd" in place of
61 the pushd, and cd back to the saved pwd instead of popd.
62
63 - don't use math expressions like let, (( ... )), or $(( ... )); use "expr"
64 instead.
65
66 grep:
67
68 - don't use the -q option; redirect stdout to /dev/null instead.
69
70 - don't use extended regular expressions with grep; use egrep instead, and
71 don't escape any regex operators.
72
73 sed:
74
75 - make sure that the beginning-of-line matcher ("^") is at the very
76 beginning of the expression -- it may not be supported inside parens.
77
78 echo:
79
80 - echo may interpret "\n" and print a newline; use printf instead if you
81 want a literal "\n" (backslash + n).
82
83 false:
84
85 - false is guaranteed only to return a non-zero value; you cannot depend on
86 it being 1. On Solaris in particular, /bin/false returns 255. Rewrite
87 your test to not depend on a particular return value, or create a
88 temporary "false" executable, and call that instead.
89
90 diff:
91
92 - don't use the -N option. There's no particularly good workaround short
93 of writing a reasonably complicated replacement script, but substituting
94 gdiff for diff if you can't rewrite the test not to need -N will probably
95 do.
@@ -211,6 +211,10 b' def run_one(test):'
211 211 sys.stdout.write('.')
212 212 sys.stdout.flush()
213 213
214 # create a fresh hgrc
215 hgrc = file(HGRCPATH, 'w+')
216 hgrc.close()
217
214 218 err = os.path.join(TESTDIR, test+".err")
215 219 ref = os.path.join(TESTDIR, test+".out")
216 220
@@ -319,11 +323,11 b" os.environ['TZ'] = 'GMT'"
319 323 os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
320 324 os.environ["HGMERGE"] = sys.executable + ' -c "import sys; sys.exit(0)"'
321 325 os.environ["HGUSER"] = "test"
322 os.environ["HGRCPATH"] = ""
323 326
324 327 TESTDIR = os.environ["TESTDIR"] = os.getcwd()
325 328 HGTMP = os.environ["HGTMP"] = tempfile.mkdtemp("", "hgtests.")
326 329 DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
330 HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
327 331
328 332 vlog("# Using TESTDIR", TESTDIR)
329 333 vlog("# Using HGTMP", HGTMP)
@@ -1,8 +1,7 b''
1 1 #!/bin/sh
2 2
3 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
4 echo "[extensions]" >> $HGTMP/.hgrc
5 echo "mq=" >> $HGTMP/.hgrc
3 echo "[extensions]" >> $HGRCPATH
4 echo "mq=" >> $HGRCPATH
6 5 cat > $HGTMP/false <<EOF
7 6 #!/bin/sh
8 7 exit 1
@@ -2,9 +2,8 b''
2 2
3 3 set -e
4 4
5 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
6 echo "[extensions]" >> $HGTMP/.hgrc
7 echo "hbisect=" >> $HGTMP/.hgrc
5 echo "[extensions]" >> $HGRCPATH
6 echo "hbisect=" >> $HGRCPATH
8 7
9 8 echo % init
10 9 hg init
@@ -59,9 +59,9 b' 0:-1,-1 1e4e1b8f71e0 1970-01-12 13:4'
59 59 line 1
60 60
61 61 # error if style not readable
62 abort: Permission denied - ./q
62 abort: Permission denied: ./q
63 63 # error if no style
64 abort: No such file or directory - notexist
64 abort: No such file or directory: notexist
65 65 # error if style missing key
66 66 abort: ./t: no key named 'changeset'
67 67 # error if include fails
@@ -13,7 +13,7 b' echo "%%% should show a removed and b ad'
13 13 hg status
14 14
15 15 echo "reverting..."
16 hg revert
16 hg revert --all
17 17
18 18 echo "%%% should show b unknown and a back to normal"
19 19 hg status
@@ -42,10 +42,10 b' echo "%%% should show a removed and b ad'
42 42 hg status
43 43
44 44 echo "%%% revert should fail"
45 hg revert
45 hg revert --all
46 46
47 47 echo "%%% revert should be ok now"
48 hg revert -r2
48 hg revert -r2 --all
49 49
50 50 echo "%%% should show b unknown and a marked modified (merged)"
51 51 hg status
@@ -17,7 +17,7 b' foo-b'
17 17 A b
18 18 R a
19 19 %%% revert should fail
20 abort: working dir has two parents; you must specify the revision to revert to
20 abort: uncommitted merge - please provide a specific revision
21 21 %%% revert should be ok now
22 22 undeleting a
23 23 forgetting b
@@ -1,8 +1,7 b''
1 1 #!/bin/sh
2 2
3 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
4 echo "[extensions]" >> $HGTMP/.hgrc
5 echo "extdiff=" >> $HGTMP/.hgrc
3 echo "[extensions]" >> $HGRCPATH
4 echo "extdiff=" >> $HGRCPATH
6 5
7 6 hg init a
8 7 cd a
@@ -14,9 +13,9 b' if [ $? -ne 0 ]; then'
14 13 fi
15 14 hg extdiff -o -Nr $opt
16 15
17 echo "[extdiff]" >> $HGTMP/.hgrc
18 echo "cmd.falabala=echo" >> $HGTMP/.hgrc
19 echo "opts.falabala=diffing" >> $HGTMP/.hgrc
16 echo "[extdiff]" >> $HGRCPATH
17 echo "cmd.falabala=echo" >> $HGRCPATH
18 echo "opts.falabala=diffing" >> $HGRCPATH
20 19
21 20 hg falabala
22 21
@@ -1,8 +1,7 b''
1 1 #!/bin/sh
2 2
3 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
4 echo "[extensions]" >> $HGTMP/.hgrc
5 echo "fetch=" >> $HGTMP/.hgrc
3 echo "[extensions]" >> $HGRCPATH
4 echo "fetch=" >> $HGRCPATH
6 5
7 6 hg init a
8 7 echo a > a/a
@@ -50,3 +50,7 b" hg ci -mrenamemod -d '0 0'"
50 50 echo '% rename+mod+chmod'
51 51 hg diff --git -r 6:tip | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
52 52 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
53
54 echo '% nonexistent in tip+chmod'
55 hg diff --git -r 5:6 | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
56 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
@@ -40,3 +40,7 b' rename to dst'
40 40 4
41 41 5
42 42 +a
43 % nonexistent in tip+chmod
44 diff --git a/src b/src
45 old mode 100644
46 new mode 100755
@@ -11,7 +11,7 b' cat hg1.pid hg2.pid >> $DAEMON_PIDS'
11 11
12 12 echo % clone via stream
13 13 http_proxy= hg clone --uncompressed http://localhost:20059/ copy 2>&1 | \
14 sed -e 's/[0-9][0-9.]*/XXX/g'
14 sed -e 's/[0-9][0-9.]*/XXX/g' -e 's/[KM]\(B\/sec\)/X\1/'
15 15 hg verify -R copy
16 16
17 17 echo % try to clone via stream, should use pull instead
@@ -15,7 +15,7 b' sleep 2'
15 15
16 16 echo %% url for proxy, stream
17 17 http_proxy=http://localhost:20060/ hg --config http_proxy.always=True clone --uncompressed http://localhost:20059/ b | \
18 sed -e 's/[0-9][0-9.]*/XXX/g'
18 sed -e 's/[0-9][0-9.]*/XXX/g' -e 's/[KM]\(B\/sec\)/X\1/'
19 19 cd b
20 20 hg verify
21 21 cd ..
@@ -2,7 +2,7 b' adding a'
2 2 %% url for proxy, stream
3 3 streaming all changes
4 4 XXX files to transfer, XXX bytes of data
5 transferred XXX bytes in XXX seconds (XXX KB/sec)
5 transferred XXX bytes in XXX seconds (XXX XB/sec)
6 6 XXX files updated, XXX files merged, XXX files removed, XXX files unresolved
7 7 checking changesets
8 8 checking manifests
@@ -2,7 +2,7 b' adding foo'
2 2 % clone via stream
3 3 streaming all changes
4 4 XXX files to transfer, XXX bytes of data
5 transferred XXX bytes in XXX seconds (XXX KB/sec)
5 transferred XXX bytes in XXX seconds (XXX XB/sec)
6 6 XXX files updated, XXX files merged, XXX files removed, XXX files unresolved
7 7 checking changesets
8 8 checking manifests
@@ -27,6 +27,9 b' hg init local'
27 27 echo this > local/foo
28 28 hg ci --cwd local -A -m "init" -d "1000000 0"
29 29
30 echo "#test failure"
31 hg init local
32
30 33 echo "# init+push to remote2"
31 34 hg init -e ./dummyssh ssh://user@dummy/remote2
32 35 hg incoming -R remote2 local
@@ -35,6 +38,12 b' hg push -R local -e ./dummyssh ssh://use'
35 38 echo "# clone to remote1"
36 39 hg clone -e ./dummyssh local ssh://user@dummy/remote1
37 40
41 echo "# init to existing repo"
42 hg init -e ./dummyssh ssh://user@dummy/remote1
43
44 echo "# clone to existing repo"
45 hg clone -e ./dummyssh local ssh://user@dummy/remote1
46
38 47 echo "# output of dummyssh"
39 48 cat dummylog
40 49
@@ -1,5 +1,7 b''
1 1 # creating 'local'
2 2 adding foo
3 #test failure
4 abort: repository local already exists!
3 5 # init+push to remote2
4 6 changeset: 0:c4e059d443be
5 7 tag: tip
@@ -15,20 +17,24 b' remote: adding file changes'
15 17 remote: added 1 changesets with 1 changes to 1 files
16 18 # clone to remote1
17 19 searching for changes
18 remote: abort: repository remote1 not found!
19 20 remote: adding changesets
20 21 remote: adding manifests
21 22 remote: adding file changes
22 23 remote: added 1 changesets with 1 changes to 1 files
24 # init to existing repo
25 abort: repository remote1 already exists!
26 abort: could not create remote repo!
27 # clone to existing repo
28 abort: repository remote1 already exists!
29 abort: could not create remote repo!
23 30 # output of dummyssh
24 Got arguments 1:user@dummy 2:hg -R remote2 serve --stdio 3: 4: 5:
25 31 Got arguments 1:user@dummy 2:hg init remote2 3: 4: 5:
26 32 Got arguments 1:user@dummy 2:hg -R remote2 serve --stdio 3: 4: 5:
27 33 Got arguments 1:user@dummy 2:hg -R remote2 serve --stdio 3: 4: 5:
28 Got arguments 1:user@dummy 2:hg -R remote1 serve --stdio 3: 4: 5:
34 Got arguments 1:user@dummy 2:hg init remote1 3: 4: 5:
29 35 Got arguments 1:user@dummy 2:hg -R remote1 serve --stdio 3: 4: 5:
30 36 Got arguments 1:user@dummy 2:hg init remote1 3: 4: 5:
31 Got arguments 1:user@dummy 2:hg -R remote1 serve --stdio 3: 4: 5:
37 Got arguments 1:user@dummy 2:hg init remote1 3: 4: 5:
32 38 # comparing repositories
33 39 0:c4e059d443be
34 40 0:c4e059d443be
@@ -15,7 +15,7 b' hg update -C 0'
15 15 hg id
16 16 echo "changed file1" >> file1
17 17 hg id
18 hg revert
18 hg revert --all
19 19 hg diff
20 20 hg status
21 21 hg id
@@ -29,11 +29,11 b' HGMERGE=merge hg update'
29 29 hg diff
30 30 hg status
31 31 hg id
32 hg revert
32 hg revert --all
33 33 hg diff
34 34 hg status
35 35 hg id
36 hg revert -r tip
36 hg revert -r tip --all
37 37 hg diff
38 38 hg status
39 39 hg id
@@ -16,7 +16,7 b' hg update -C 0'
16 16 hg id
17 17 echo "changed file1" >> file1
18 18 hg id
19 hg revert --no-backup
19 hg revert --no-backup --all
20 20 hg diff
21 21 hg status
22 22 hg id
@@ -31,11 +31,11 b' hg diff | sed -e "s/\\(+++ [a-zA-Z0-9_/.-'
31 31 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" -e "s/\(>>>>>>>\) .*/\1/"
32 32 hg status
33 33 hg id
34 hg revert --no-backup
34 hg revert --no-backup --all
35 35 hg diff
36 36 hg status
37 37 hg id
38 hg revert -r tip --no-backup
38 hg revert -r tip --no-backup --all
39 39 hg diff
40 40 hg status
41 41 hg id
@@ -1,8 +1,7 b''
1 1 #!/bin/sh
2 2
3 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
4 echo "[extensions]" >> $HGTMP/.hgrc
5 echo "mq=" >> $HGTMP/.hgrc
3 echo "[extensions]" >> $HGRCPATH
4 echo "mq=" >> $HGRCPATH
6 5
7 6 echo % help
8 7 hg help mq
@@ -116,6 +115,19 b' hg push ../../k'
116 115 echo % qunapplied
117 116 hg qunapplied
118 117
118 echo % qpush/qpop with index
119 hg qnew test1b.patch
120 echo 1b > 1b
121 hg add 1b
122 hg qrefresh
123 hg qpush 2
124 hg qpop 0
125 hg qpush test.patch+1
126 hg qpush test.patch+2
127 hg qpop test2.patch-1
128 hg qpop test2.patch-2
129 hg qpush test1b.patch+1
130
119 131 echo % push should succeed
120 132 hg qpop -a
121 133 hg push ../../k
@@ -127,7 +139,28 b' hg ci -Ama'
127 139 hg strip tip 2>&1 | sed 's/\(saving bundle to \).*/\1/'
128 140 hg unbundle .hg/strip-backup/*
129 141
130 cat >>$HGTMP/.hgrc <<EOF
142 echo '% cd b; hg qrefresh'
143 hg init refresh
144 cd refresh
145 echo a > a
146 hg ci -Ama -d'0 0'
147 hg qnew -mfoo foo
148 echo a >> a
149 hg qrefresh
150 mkdir b
151 cd b
152 echo f > f
153 hg add f
154 hg qrefresh
155 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
156 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" ../.hg/patches/foo
157 echo % hg qrefresh .
158 hg qrefresh .
159 sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
160 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/" ../.hg/patches/foo
161 hg status
162
163 cat >>$HGRCPATH <<EOF
131 164 [diff]
132 165 git = True
133 166 EOF
@@ -1,8 +1,7 b''
1 1 #!/bin/sh
2 2
3 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
4 echo "[extensions]" >> $HGTMP/.hgrc
5 echo "mq=" >> $HGTMP/.hgrc
3 echo "[extensions]" >> $HGRCPATH
4 echo "mq=" >> $HGRCPATH
6 5
7 6 hg init
8 7 hg qinit
@@ -1,8 +1,7 b''
1 1 #!/bin/sh
2 2
3 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
4 echo "[extensions]" >> $HGTMP/.hgrc
5 echo "mq=" >> $HGTMP/.hgrc
3 echo "[extensions]" >> $HGRCPATH
4 echo "mq=" >> $HGRCPATH
6 5
7 6 echo % init
8 7 hg init a
@@ -1,8 +1,7 b''
1 1 #!/bin/sh
2 2
3 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
4 echo "[extensions]" >> $HGTMP/.hgrc
5 echo "mq=" >> $HGTMP/.hgrc
3 echo "[extensions]" >> $HGRCPATH
4 echo "mq=" >> $HGRCPATH
6 5
7 6 hg init a
8 7 cd a
@@ -1,9 +1,8 b''
1 1 #!/bin/sh
2 2
3 3 # Environement setup for MQ
4 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
5 echo "[extensions]" >> $HGTMP/.hgrc
6 echo "mq=" >> $HGTMP/.hgrc
4 echo "[extensions]" >> $HGRCPATH
5 echo "mq=" >> $HGRCPATH
7 6
8 7 #Repo init
9 8 hg init
@@ -1,8 +1,7 b''
1 1 #!/bin/sh
2 2
3 HGRCPATH=$HGTMP/.hgrc; export HGRCPATH
4 echo "[extensions]" >> $HGTMP/.hgrc
5 echo "mq=" >> $HGTMP/.hgrc
3 echo "[extensions]" >> $HGRCPATH
4 echo "mq=" >> $HGRCPATH
6 5
7 6 hg init a
8 7 cd a
@@ -110,6 +110,19 b' pushing to ../../k'
110 110 abort: source has mq patches applied
111 111 % qunapplied
112 112 test2.patch
113 % qpush/qpop with index
114 applying test2.patch
115 Now at: test2.patch
116 Now at: test.patch
117 applying test1b.patch
118 Now at: test1b.patch
119 applying test2.patch
120 Now at: test2.patch
121 Now at: test1b.patch
122 Now at: test.patch
123 applying test1b.patch
124 applying test2.patch
125 Now at: test2.patch
113 126 % push should succeed
114 127 Patch queue now empty
115 128 pushing to ../../k
@@ -127,6 +140,30 b' adding manifests'
127 140 adding file changes
128 141 added 1 changesets with 1 changes to 1 files
129 142 (run 'hg update' to get a working copy)
143 % cd b; hg qrefresh
144 adding a
145 foo
146
147 diff -r cb9a9f314b8b a
148 --- a/a
149 +++ b/a
150 @@ -1,1 +1,2 @@ a
151 a
152 +a
153 diff -r cb9a9f314b8b b/f
154 --- /dev/null
155 +++ b/b/f
156 @@ -0,0 +1,1 @@
157 +f
158 % hg qrefresh .
159 foo
160
161 diff -r cb9a9f314b8b b/f
162 --- /dev/null
163 +++ b/b/f
164 @@ -0,0 +1,1 @@
165 +f
166 M a
130 167 new file
131 168
132 169 diff --git a/new b/new
@@ -14,6 +14,6 b' hg add b/x'
14 14 echo '# should print A b/x'
15 15 hg st
16 16 echo '# should forget b/x'
17 hg revert
17 hg revert --all
18 18 echo '# should print nothing'
19 19 hg st b
@@ -9,7 +9,7 b' hg commit -m 1 -d "1000000 0"'
9 9 hg remove
10 10 rm foo
11 11 hg remove foo
12 hg revert
12 hg revert --all
13 13 rm foo
14 14 hg remove --after
15 15 hg commit -m 2 -d "1000000 0"
@@ -31,7 +31,7 b' echo %% should show a b c e'
31 31 ls
32 32 echo %% should verbosely save backup to e.orig
33 33 echo z > e
34 hg revert -v
34 hg revert --all -v
35 35 echo %% should say no changes needed
36 36 hg revert a
37 37 echo %% should say file not managed
@@ -46,9 +46,9 b' echo z > z'
46 46 hg add z
47 47 hg st
48 48 echo %% should add a, forget z
49 hg revert -r0
49 hg revert --all -r0
50 50 echo %% should forget a
51 hg revert -rtip
51 hg revert --all -rtip
52 52 rm -f a *.orig
53 53 echo %% should silently add a
54 54 hg revert -r0 a
@@ -56,7 +56,7 b' hg st a'
56 56
57 57 hg update -C
58 58 chmod +x c
59 hg revert
59 hg revert --all
60 60 echo %% should print non-executable
61 61 test -x c || echo non-executable
62 62
@@ -64,7 +64,7 b' chmod +x c'
64 64 hg commit -d '1000001 0' -m exe
65 65
66 66 chmod -x c
67 hg revert
67 hg revert --all
68 68 echo %% should print executable
69 69 test -x c && echo executable
70 70
@@ -78,6 +78,15 b" hg commit -d '2 0' -m a"
78 78 hg update 0
79 79 mkdir b
80 80 echo b > b/b
81
82 echo % should fail - no arguments
81 83 hg revert -rtip
82 84
85 echo % should succeed
86 hg revert --all -rtip
87
88 echo %% issue332
89 hg ci -A -m b -d '1000001 0'
90 echo foobar > b/b
91 hg revert b
83 92 true
@@ -13,7 +13,7 b' hg ci -m "2" -d "1000000 0"'
13 13
14 14 echo %% Should show unknown
15 15 hg status
16 hg revert -r 0
16 hg revert -r 0 --all
17 17 echo %% Should show unknown and b removed
18 18 hg status
19 19 echo %% Should show a and unknown
@@ -54,4 +54,10 b' executable'
54 54 %% issue 241
55 55 adding a
56 56 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
57 % should fail - no arguments
58 abort: no files or directories specified; use --all to revert the whole repo
59 % should succeed
57 60 reverting a
61 %% issue332
62 adding b/b
63 reverting b/b
@@ -1,3 +1,3 b''
1 1 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
2 2 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
3 abort: Permission denied - test-ro-message/b/vehicle
3 abort: Permission denied: test-ro-message/b/vehicle
@@ -38,7 +38,7 b' cd ..'
38 38
39 39 echo "# clone remote via stream"
40 40 hg clone -e ./dummyssh --uncompressed ssh://user@dummy/remote local-stream 2>&1 | \
41 sed -e 's/[0-9][0-9.]*/XXX/g'
41 sed -e 's/[0-9][0-9.]*/XXX/g' -e 's/[KM]\(B\/sec\)/X\1/'
42 42 cd local-stream
43 43 hg verify
44 44 cd ..
@@ -80,7 +80,7 b' cd ../remote'
80 80 echo "# check remote tip"
81 81 hg tip
82 82 hg verify
83 hg cat foo
83 hg cat -r tip foo
84 84
85 85 echo z > z
86 86 hg ci -A -m z -d '1000001 0' z
@@ -2,7 +2,7 b''
2 2 # clone remote via stream
3 3 streaming all changes
4 4 XXX files to transfer, XXX bytes of data
5 transferred XXX bytes in XXX seconds (XXX KB/sec)
5 transferred XXX bytes in XXX seconds (XXX XB/sec)
6 6 XXX files updated, XXX files merged, XXX files removed, XXX files unresolved
7 7 checking changesets
8 8 checking manifests
@@ -1,6 +1,6 b''
1 1 #!/usr/bin/env python
2 2
3 from mercurial import ui
3 from mercurial import ui, util
4 4
5 5 testui = ui.ui()
6 6 testui.updateopts(config=[
@@ -11,10 +11,19 b' testui.updateopts(config=['
11 11 'lists.list2=foo bar baz',
12 12 'lists.list3=alice, bob',
13 13 'lists.list4=foo bar baz alice, bob',
14 'interpolation.value1=hallo',
15 'interpolation.value2=%(value1)s world',
16 'interpolation.value3=%(novalue)s',
17 'interpolation.value4=%(bad)1',
18 'interpolation.value5=%bad2',
14 19 ])
15 20
16 21 print repr(testui.configitems('values'))
17 22 print repr(testui.configitems('lists'))
23 try:
24 print repr(testui.configitems('interpolation'))
25 except util.Abort, inst:
26 print inst
18 27 print "---"
19 28 print repr(testui.config('values', 'string'))
20 29 print repr(testui.config('values', 'bool1'))
@@ -45,3 +54,18 b" print repr(testui.configlist('lists', 'u"
45 54 print repr(testui.configlist('lists', 'unknown', ['foo bar']))
46 55 print repr(testui.configlist('lists', 'unknown', ['foo', 'bar']))
47 56 print "---"
57 print repr(testui.config('interpolation', 'value1'))
58 print repr(testui.config('interpolation', 'value2'))
59 try:
60 print repr(testui.config('interpolation', 'value3'))
61 except util.Abort, inst:
62 print inst
63 try:
64 print repr(testui.config('interpolation', 'value4'))
65 except util.Abort, inst:
66 print inst
67 try:
68 print repr(testui.config('interpolation', 'value5'))
69 except util.Abort, inst:
70 print inst
71 print "---"
@@ -1,5 +1,7 b''
1 1 [('bool1', 'true'), ('bool2', 'false'), ('string', 'string value')]
2 2 [('list1', 'foo'), ('list2', 'foo bar baz'), ('list3', 'alice, bob'), ('list4', 'foo bar baz alice, bob')]
3 Error in configuration section [interpolation]:
4 '%' must be followed by '%' or '(', found: '%bad2'
3 5 ---
4 6 'string value'
5 7 'true'
@@ -27,3 +29,17 b' True'
27 29 ['foo bar']
28 30 ['foo', 'bar']
29 31 ---
32 'hallo'
33 'hallo world'
34 Error in configuration section [interpolation] parameter 'value3':
35 Bad value substitution:
36 section: [interpolation]
37 option : value3
38 key : novalue
39 rawval :
40
41 Error in configuration section [interpolation] parameter 'value4':
42 bad interpolation variable reference '%(bad)1'
43 Error in configuration section [interpolation] parameter 'value5':
44 '%' must be followed by '%' or '(', found: '%bad2'
45 ---
General Comments 0
You need to be logged in to leave comments. Login now