##// END OF EJS Templates
merge with hg-stable
Thomas Arendsen Hein -
r6438:a60b711c merge default
parent child Browse files
Show More
@@ -0,0 +1,31 b''
1 #!/bin/bash
2
3 for i in aaa zzz; do
4 hg init t
5 cd t
6
7 echo "-- With $i"
8
9 touch file
10 hg add file
11 hg ci -m "Add"
12
13 hg cp file $i
14 hg ci -m "a -> $i"
15
16 hg cp $i other-file
17 echo "different" >> $i
18 hg ci -m "$i -> other-file"
19
20 hg cp other-file somename
21
22 echo "Status":
23 hg st -C
24 echo
25 echo "Diff:"
26 hg diff -g
27 echo
28
29 cd ..
30 rm -rf t
31 done
@@ -0,0 +1,20 b''
1 -- With aaa
2 Status:
3 A somename
4 other-file
5
6 Diff:
7 diff --git a/other-file b/somename
8 copy from other-file
9 copy to somename
10
11 -- With zzz
12 Status:
13 A somename
14 other-file
15
16 Diff:
17 diff --git a/other-file b/somename
18 copy from other-file
19 copy to somename
20
@@ -1,206 +1,206 b''
1 1 # churn.py - create a graph showing who changed the most lines
2 2 #
3 3 # Copyright 2006 Josef "Jeff" Sipek <jeffpc@josefsipek.net>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7 #
8 8 #
9 9 # Aliases map file format is simple one alias per line in the following
10 10 # format:
11 11 #
12 12 # <alias email> <actual email>
13 13
14 14 from mercurial.i18n import gettext as _
15 15 from mercurial import mdiff, cmdutil, util, node
16 16 import os, sys
17 17
18 18 def get_tty_width():
19 19 if 'COLUMNS' in os.environ:
20 20 try:
21 21 return int(os.environ['COLUMNS'])
22 22 except ValueError:
23 23 pass
24 24 try:
25 25 import termios, array, fcntl
26 26 for dev in (sys.stdout, sys.stdin):
27 27 try:
28 28 fd = dev.fileno()
29 29 if not os.isatty(fd):
30 30 continue
31 31 arri = fcntl.ioctl(fd, termios.TIOCGWINSZ, '\0' * 8)
32 32 return array.array('h', arri)[1]
33 33 except ValueError:
34 34 pass
35 35 except ImportError:
36 36 pass
37 37 return 80
38 38
39 39 def __gather(ui, repo, node1, node2):
40 40 def dirtywork(f, mmap1, mmap2):
41 41 lines = 0
42 42
43 43 to = mmap1 and repo.file(f).read(mmap1[f]) or None
44 44 tn = mmap2 and repo.file(f).read(mmap2[f]) or None
45 45
46 46 diff = mdiff.unidiff(to, "", tn, "", f, f).split("\n")
47 47
48 48 for line in diff:
49 49 if not line:
50 50 continue # skip EOF
51 51 if line.startswith(" "):
52 52 continue # context line
53 53 if line.startswith("--- ") or line.startswith("+++ "):
54 54 continue # begining of diff
55 55 if line.startswith("@@ "):
56 56 continue # info line
57 57
58 58 # changed lines
59 59 lines += 1
60 60
61 61 return lines
62 62
63 63 ##
64 64
65 65 lines = 0
66 66
67 67 changes = repo.status(node1, node2, None, util.always)[:5]
68 68
69 69 modified, added, removed, deleted, unknown = changes
70 70
71 71 who = repo.changelog.read(node2)[1]
72 72 who = util.email(who) # get the email of the person
73 73
74 74 mmap1 = repo.manifest.read(repo.changelog.read(node1)[0])
75 75 mmap2 = repo.manifest.read(repo.changelog.read(node2)[0])
76 76 for f in modified:
77 77 lines += dirtywork(f, mmap1, mmap2)
78 78
79 79 for f in added:
80 80 lines += dirtywork(f, None, mmap2)
81 81
82 82 for f in removed:
83 83 lines += dirtywork(f, mmap1, None)
84 84
85 85 for f in deleted:
86 86 lines += dirtywork(f, mmap1, mmap2)
87 87
88 88 for f in unknown:
89 89 lines += dirtywork(f, mmap1, mmap2)
90 90
91 91 return (who, lines)
92 92
93 93 def gather_stats(ui, repo, amap, revs=None, progress=False):
94 94 stats = {}
95 95
96 96 cl = repo.changelog
97 97
98 98 if not revs:
99 99 revs = range(0, cl.count())
100 100
101 101 nr_revs = len(revs)
102 102 cur_rev = 0
103 103
104 104 for rev in revs:
105 105 cur_rev += 1 # next revision
106 106
107 107 node2 = cl.node(rev)
108 108 node1 = cl.parents(node2)[0]
109 109
110 110 if cl.parents(node2)[1] != node.nullid:
111 111 ui.note(_('Revision %d is a merge, ignoring...\n') % (rev,))
112 112 continue
113 113
114 114 who, lines = __gather(ui, repo, node1, node2)
115 115
116 116 # remap the owner if possible
117 117 if who in amap:
118 118 ui.note("using '%s' alias for '%s'\n" % (amap[who], who))
119 119 who = amap[who]
120 120
121 121 if not who in stats:
122 122 stats[who] = 0
123 123 stats[who] += lines
124 124
125 125 ui.note("rev %d: %d lines by %s\n" % (rev, lines, who))
126 126
127 127 if progress:
128 128 nr_revs = max(nr_revs, 1)
129 129 if int(100.0*(cur_rev - 1)/nr_revs) < int(100.0*cur_rev/nr_revs):
130 130 ui.write("\rGenerating stats: %d%%" % (int(100.0*cur_rev/nr_revs),))
131 131 sys.stdout.flush()
132 132
133 133 if progress:
134 134 ui.write("\r")
135 135 sys.stdout.flush()
136 136
137 137 return stats
138 138
139 139 def churn(ui, repo, **opts):
140 140 "Graphs the number of lines changed"
141 141
142 142 def pad(s, l):
143 143 if len(s) < l:
144 144 return s + " " * (l-len(s))
145 145 return s[0:l]
146 146
147 147 def graph(n, maximum, width, char):
148 148 maximum = max(1, maximum)
149 149 n = int(n * width / float(maximum))
150 150
151 151 return char * (n)
152 152
153 153 def get_aliases(f):
154 154 aliases = {}
155 155
156 156 for l in f.readlines():
157 157 l = l.strip()
158 alias, actual = l.split(" ")
158 alias, actual = l.split()
159 159 aliases[alias] = actual
160 160
161 161 return aliases
162 162
163 163 amap = {}
164 164 aliases = opts.get('aliases')
165 165 if aliases:
166 166 try:
167 167 f = open(aliases,"r")
168 168 except OSError, e:
169 169 print "Error: " + e
170 170 return
171 171
172 172 amap = get_aliases(f)
173 173 f.close()
174 174
175 175 revs = [int(r) for r in cmdutil.revrange(repo, opts['rev'])]
176 176 revs.sort()
177 177 stats = gather_stats(ui, repo, amap, revs, opts.get('progress'))
178 178
179 179 # make a list of tuples (name, lines) and sort it in descending order
180 180 ordered = stats.items()
181 181 if not ordered:
182 182 return
183 183 ordered.sort(lambda x, y: cmp(y[1], x[1]))
184 184 max_churn = ordered[0][1]
185 185
186 186 tty_width = get_tty_width()
187 187 ui.note(_("assuming %i character terminal\n") % tty_width)
188 188 tty_width -= 1
189 189
190 190 max_user_width = max([len(user) for user, churn in ordered])
191 191
192 192 graph_width = tty_width - max_user_width - 1 - 6 - 2 - 2
193 193
194 194 for user, churn in ordered:
195 195 print "%s %6d %s" % (pad(user, max_user_width),
196 196 churn,
197 197 graph(churn, max_churn, graph_width, '*'))
198 198
199 199 cmdtable = {
200 200 "churn":
201 201 (churn,
202 202 [('r', 'rev', [], _('limit statistics to the specified revisions')),
203 203 ('', 'aliases', '', _('file with email aliases')),
204 204 ('', 'progress', None, _('show progress'))],
205 205 'hg churn [-r revision range] [-a file] [--progress]'),
206 206 }
@@ -1,134 +1,83 b''
1 1 # ancestor.py - generic DAG ancestor algorithm for mercurial
2 2 #
3 3 # Copyright 2006 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 import heapq
9 9
10 10 def ancestor(a, b, pfunc):
11 11 """
12 12 return the least common ancestor of nodes a and b or None if there
13 13 is no such ancestor.
14 14
15 15 pfunc must return a list of parent vertices
16 16 """
17 17
18 18 if a == b:
19 19 return a
20 20
21 21 # find depth from root of all ancestors
22 22 visit = [a, b]
23 23 depth = {}
24 24 while visit:
25 25 vertex = visit[-1]
26 26 pl = pfunc(vertex)
27 27 if not pl:
28 28 depth[vertex] = 0
29 29 visit.pop()
30 30 else:
31 31 for p in pl:
32 32 if p == a or p == b: # did we find a or b as a parent?
33 33 return p # we're done
34 34 if p not in depth:
35 35 visit.append(p)
36 36 if visit[-1] == vertex:
37 37 depth[vertex] = min([depth[p] for p in pl]) - 1
38 38 visit.pop()
39 39
40 40 # traverse ancestors in order of decreasing distance from root
41 41 def ancestors(vertex):
42 42 h = [(depth[vertex], vertex)]
43 43 seen = {}
44 44 while h:
45 45 d, n = heapq.heappop(h)
46 46 if n not in seen:
47 47 seen[n] = 1
48 48 yield (d, n)
49 49 for p in pfunc(n):
50 50 heapq.heappush(h, (depth[p], p))
51 51
52 52 def generations(vertex):
53 53 sg, s = None, {}
54 54 for g, v in ancestors(vertex):
55 55 if g != sg:
56 56 if sg:
57 57 yield sg, s
58 58 sg, s = g, {v:1}
59 59 else:
60 60 s[v] = 1
61 61 yield sg, s
62 62
63 63 x = generations(a)
64 64 y = generations(b)
65 65 gx = x.next()
66 66 gy = y.next()
67 67
68 68 # increment each ancestor list until it is closer to root than
69 69 # the other, or they match
70 70 try:
71 71 while 1:
72 72 if gx[0] == gy[0]:
73 73 for v in gx[1]:
74 74 if v in gy[1]:
75 75 return v
76 76 gy = y.next()
77 77 gx = x.next()
78 78 elif gx[0] > gy[0]:
79 79 gy = y.next()
80 80 else:
81 81 gx = x.next()
82 82 except StopIteration:
83 83 return None
84
85 def symmetricdifference(a, b, pfunc):
86 """symmetric difference of the sets of ancestors of a and b
87
88 I.e. revisions that are ancestors of a or b, but not both.
89 """
90 # basic idea:
91 # - mark a and b with different colors
92 # - walk the graph in topological order with the help of a heap;
93 # for each revision r:
94 # - if r has only one color, we want to return it
95 # - add colors[r] to its parents
96 #
97 # We keep track of the number of revisions in the heap that
98 # we may be interested in. We stop walking the graph as soon
99 # as this number reaches 0.
100 if a == b:
101 return [a]
102
103 WHITE = 1
104 BLACK = 2
105 ALLCOLORS = WHITE | BLACK
106 colors = {a: WHITE, b: BLACK}
107
108 visit = [-a, -b]
109 heapq.heapify(visit)
110 n_wanted = len(visit)
111 ret = []
112
113 while n_wanted:
114 r = -heapq.heappop(visit)
115 wanted = colors[r] != ALLCOLORS
116 n_wanted -= wanted
117 if wanted:
118 ret.append(r)
119
120 for p in pfunc(r):
121 if p not in colors:
122 # first time we see p; add it to visit
123 n_wanted += wanted
124 colors[p] = colors[r]
125 heapq.heappush(visit, -p)
126 elif colors[p] != ALLCOLORS and colors[p] != colors[r]:
127 # at first we thought we wanted p, but now
128 # we know we don't really want it
129 n_wanted -= 1
130 colors[p] |= colors[r]
131
132 del colors[r]
133
134 return ret
@@ -1,3266 +1,3269 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from node import hex, nullid, nullrev, short
9 9 from repo import RepoError, NoCapability
10 10 from i18n import _
11 11 import os, re, sys, urllib
12 12 import hg, util, revlog, bundlerepo, extensions, copies
13 13 import difflib, patch, time, help, mdiff, tempfile
14 14 import version, socket
15 15 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
16 16
17 17 # Commands start here, listed alphabetically
18 18
19 19 def add(ui, repo, *pats, **opts):
20 20 """add the specified files on the next commit
21 21
22 22 Schedule files to be version controlled and added to the repository.
23 23
24 24 The files will be added to the repository at the next commit. To
25 25 undo an add before that, see hg revert.
26 26
27 27 If no names are given, add all files in the repository.
28 28 """
29 29
30 30 rejected = None
31 31 exacts = {}
32 32 names = []
33 33 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
34 34 badmatch=util.always):
35 35 if exact:
36 36 if ui.verbose:
37 37 ui.status(_('adding %s\n') % rel)
38 38 names.append(abs)
39 39 exacts[abs] = 1
40 40 elif abs not in repo.dirstate:
41 41 ui.status(_('adding %s\n') % rel)
42 42 names.append(abs)
43 43 if not opts.get('dry_run'):
44 44 rejected = repo.add(names)
45 45 rejected = [p for p in rejected if p in exacts]
46 46 return rejected and 1 or 0
47 47
48 48 def addremove(ui, repo, *pats, **opts):
49 49 """add all new files, delete all missing files
50 50
51 51 Add all new files and remove all missing files from the repository.
52 52
53 53 New files are ignored if they match any of the patterns in .hgignore. As
54 54 with add, these changes take effect at the next commit.
55 55
56 56 Use the -s option to detect renamed files. With a parameter > 0,
57 57 this compares every removed file with every added file and records
58 58 those similar enough as renames. This option takes a percentage
59 59 between 0 (disabled) and 100 (files must be identical) as its
60 60 parameter. Detecting renamed files this way can be expensive.
61 61 """
62 62 try:
63 63 sim = float(opts.get('similarity') or 0)
64 64 except ValueError:
65 65 raise util.Abort(_('similarity must be a number'))
66 66 if sim < 0 or sim > 100:
67 67 raise util.Abort(_('similarity must be between 0 and 100'))
68 68 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
69 69
70 70 def annotate(ui, repo, *pats, **opts):
71 71 """show changeset information per file line
72 72
73 73 List changes in files, showing the revision id responsible for each line
74 74
75 75 This command is useful to discover who did a change or when a change took
76 76 place.
77 77
78 78 Without the -a option, annotate will avoid processing files it
79 79 detects as binary. With -a, annotate will generate an annotation
80 80 anyway, probably with undesirable results.
81 81 """
82 82 datefunc = ui.quiet and util.shortdate or util.datestr
83 83 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
84 84
85 85 if not pats:
86 86 raise util.Abort(_('at least one file name or pattern required'))
87 87
88 88 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
89 89 ('number', lambda x: str(x[0].rev())),
90 90 ('changeset', lambda x: short(x[0].node())),
91 91 ('date', getdate),
92 92 ('follow', lambda x: x[0].path()),
93 93 ]
94 94
95 95 if (not opts['user'] and not opts['changeset'] and not opts['date']
96 96 and not opts['follow']):
97 97 opts['number'] = 1
98 98
99 99 linenumber = opts.get('line_number') is not None
100 100 if (linenumber and (not opts['changeset']) and (not opts['number'])):
101 101 raise util.Abort(_('at least one of -n/-c is required for -l'))
102 102
103 103 funcmap = [func for op, func in opmap if opts.get(op)]
104 104 if linenumber:
105 105 lastfunc = funcmap[-1]
106 106 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
107 107
108 108 ctx = repo.changectx(opts['rev'])
109 109
110 110 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
111 111 node=ctx.node()):
112 112 fctx = ctx.filectx(abs)
113 113 if not opts['text'] and util.binary(fctx.data()):
114 114 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
115 115 continue
116 116
117 117 lines = fctx.annotate(follow=opts.get('follow'),
118 118 linenumber=linenumber)
119 119 pieces = []
120 120
121 121 for f in funcmap:
122 122 l = [f(n) for n, dummy in lines]
123 123 if l:
124 124 m = max(map(len, l))
125 125 pieces.append(["%*s" % (m, x) for x in l])
126 126
127 127 if pieces:
128 128 for p, l in zip(zip(*pieces), lines):
129 129 ui.write("%s: %s" % (" ".join(p), l[1]))
130 130
131 131 def archive(ui, repo, dest, **opts):
132 132 '''create unversioned archive of a repository revision
133 133
134 134 By default, the revision used is the parent of the working
135 135 directory; use "-r" to specify a different revision.
136 136
137 137 To specify the type of archive to create, use "-t". Valid
138 138 types are:
139 139
140 140 "files" (default): a directory full of files
141 141 "tar": tar archive, uncompressed
142 142 "tbz2": tar archive, compressed using bzip2
143 143 "tgz": tar archive, compressed using gzip
144 144 "uzip": zip archive, uncompressed
145 145 "zip": zip archive, compressed using deflate
146 146
147 147 The exact name of the destination archive or directory is given
148 148 using a format string; see "hg help export" for details.
149 149
150 150 Each member added to an archive file has a directory prefix
151 151 prepended. Use "-p" to specify a format string for the prefix.
152 152 The default is the basename of the archive, with suffixes removed.
153 153 '''
154 154
155 155 ctx = repo.changectx(opts['rev'])
156 156 if not ctx:
157 157 raise util.Abort(_('repository has no revisions'))
158 158 node = ctx.node()
159 159 dest = cmdutil.make_filename(repo, dest, node)
160 160 if os.path.realpath(dest) == repo.root:
161 161 raise util.Abort(_('repository root cannot be destination'))
162 162 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
163 163 kind = opts.get('type') or 'files'
164 164 prefix = opts['prefix']
165 165 if dest == '-':
166 166 if kind == 'files':
167 167 raise util.Abort(_('cannot archive plain files to stdout'))
168 168 dest = sys.stdout
169 169 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
170 170 prefix = cmdutil.make_filename(repo, prefix, node)
171 171 archival.archive(repo, dest, node, kind, not opts['no_decode'],
172 172 matchfn, prefix)
173 173
174 174 def backout(ui, repo, node=None, rev=None, **opts):
175 175 '''reverse effect of earlier changeset
176 176
177 177 Commit the backed out changes as a new changeset. The new
178 178 changeset is a child of the backed out changeset.
179 179
180 180 If you back out a changeset other than the tip, a new head is
181 181 created. This head will be the new tip and you should merge this
182 182 backout changeset with another head (current one by default).
183 183
184 184 The --merge option remembers the parent of the working directory
185 185 before starting the backout, then merges the new head with that
186 186 changeset afterwards. This saves you from doing the merge by
187 187 hand. The result of this merge is not committed, as for a normal
188 188 merge.
189 189
190 190 See 'hg help dates' for a list of formats valid for -d/--date.
191 191 '''
192 192 if rev and node:
193 193 raise util.Abort(_("please specify just one revision"))
194 194
195 195 if not rev:
196 196 rev = node
197 197
198 198 if not rev:
199 199 raise util.Abort(_("please specify a revision to backout"))
200 200
201 201 date = opts.get('date')
202 202 if date:
203 203 opts['date'] = util.parsedate(date)
204 204
205 205 cmdutil.bail_if_changed(repo)
206 206 node = repo.lookup(rev)
207 207
208 208 op1, op2 = repo.dirstate.parents()
209 209 a = repo.changelog.ancestor(op1, node)
210 210 if a != node:
211 211 raise util.Abort(_('cannot back out change on a different branch'))
212 212
213 213 p1, p2 = repo.changelog.parents(node)
214 214 if p1 == nullid:
215 215 raise util.Abort(_('cannot back out a change with no parents'))
216 216 if p2 != nullid:
217 217 if not opts['parent']:
218 218 raise util.Abort(_('cannot back out a merge changeset without '
219 219 '--parent'))
220 220 p = repo.lookup(opts['parent'])
221 221 if p not in (p1, p2):
222 222 raise util.Abort(_('%s is not a parent of %s') %
223 223 (short(p), short(node)))
224 224 parent = p
225 225 else:
226 226 if opts['parent']:
227 227 raise util.Abort(_('cannot use --parent on non-merge changeset'))
228 228 parent = p1
229 229
230 # the backout should appear on the same branch
231 branch = repo.dirstate.branch()
230 232 hg.clean(repo, node, show_stats=False)
233 repo.dirstate.setbranch(branch)
231 234 revert_opts = opts.copy()
232 235 revert_opts['date'] = None
233 236 revert_opts['all'] = True
234 237 revert_opts['rev'] = hex(parent)
235 238 revert_opts['no_backup'] = None
236 239 revert(ui, repo, **revert_opts)
237 240 commit_opts = opts.copy()
238 241 commit_opts['addremove'] = False
239 242 if not commit_opts['message'] and not commit_opts['logfile']:
240 243 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
241 244 commit_opts['force_editor'] = True
242 245 commit(ui, repo, **commit_opts)
243 246 def nice(node):
244 247 return '%d:%s' % (repo.changelog.rev(node), short(node))
245 248 ui.status(_('changeset %s backs out changeset %s\n') %
246 249 (nice(repo.changelog.tip()), nice(node)))
247 250 if op1 != node:
248 251 hg.clean(repo, op1, show_stats=False)
249 252 if opts['merge']:
250 253 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
251 254 hg.merge(repo, hex(repo.changelog.tip()))
252 255 else:
253 256 ui.status(_('the backout changeset is a new head - '
254 257 'do not forget to merge\n'))
255 258 ui.status(_('(use "backout --merge" '
256 259 'if you want to auto-merge)\n'))
257 260
258 261 def bisect(ui, repo, rev=None, extra=None,
259 262 reset=None, good=None, bad=None, skip=None, noupdate=None):
260 263 """subdivision search of changesets
261 264
262 265 This command helps to find changesets which introduce problems.
263 266 To use, mark the earliest changeset you know exhibits the problem
264 267 as bad, then mark the latest changeset which is free from the
265 268 problem as good. Bisect will update your working directory to a
266 269 revision for testing. Once you have performed tests, mark the
267 270 working directory as bad or good and bisect will either update to
268 271 another candidate changeset or announce that it has found the bad
269 272 revision.
270 273 """
271 274 # backward compatibility
272 275 if rev in "good bad reset init".split():
273 276 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
274 277 cmd, rev, extra = rev, extra, None
275 278 if cmd == "good":
276 279 good = True
277 280 elif cmd == "bad":
278 281 bad = True
279 282 else:
280 283 reset = True
281 284 elif extra or good + bad + skip + reset > 1:
282 285 raise util.Abort("Incompatible arguments")
283 286
284 287 if reset:
285 288 p = repo.join("bisect.state")
286 289 if os.path.exists(p):
287 290 os.unlink(p)
288 291 return
289 292
290 293 # load state
291 294 state = {'good': [], 'bad': [], 'skip': []}
292 295 if os.path.exists(repo.join("bisect.state")):
293 296 for l in repo.opener("bisect.state"):
294 297 kind, node = l[:-1].split()
295 298 node = repo.lookup(node)
296 299 if kind not in state:
297 300 raise util.Abort(_("unknown bisect kind %s") % kind)
298 301 state[kind].append(node)
299 302
300 303 # update state
301 304 node = repo.lookup(rev or '.')
302 305 if good:
303 306 state['good'].append(node)
304 307 elif bad:
305 308 state['bad'].append(node)
306 309 elif skip:
307 310 state['skip'].append(node)
308 311
309 312 # save state
310 313 f = repo.opener("bisect.state", "w", atomictemp=True)
311 314 wlock = repo.wlock()
312 315 try:
313 316 for kind in state:
314 317 for node in state[kind]:
315 318 f.write("%s %s\n" % (kind, hex(node)))
316 319 f.rename()
317 320 finally:
318 321 del wlock
319 322
320 323 if not state['good'] or not state['bad']:
321 324 return
322 325
323 326 # actually bisect
324 327 node, changesets, good = hbisect.bisect(repo.changelog, state)
325 328 if changesets == 0:
326 329 ui.write(_("The first %s revision is:\n") % (good and "good" or "bad"))
327 330 displayer = cmdutil.show_changeset(ui, repo, {})
328 331 displayer.show(changenode=node)
329 332 elif node is not None:
330 333 # compute the approximate number of remaining tests
331 334 tests, size = 0, 2
332 335 while size <= changesets:
333 336 tests, size = tests + 1, size * 2
334 337 rev = repo.changelog.rev(node)
335 338 ui.write(_("Testing changeset %s:%s "
336 339 "(%s changesets remaining, ~%s tests)\n")
337 340 % (rev, short(node), changesets, tests))
338 341 if not noupdate:
339 342 cmdutil.bail_if_changed(repo)
340 343 return hg.clean(repo, node)
341 344
342 345 def branch(ui, repo, label=None, **opts):
343 346 """set or show the current branch name
344 347
345 348 With no argument, show the current branch name. With one argument,
346 349 set the working directory branch name (the branch does not exist in
347 350 the repository until the next commit).
348 351
349 352 Unless --force is specified, branch will not let you set a
350 353 branch name that shadows an existing branch.
351 354
352 355 Use the command 'hg update' to switch to an existing branch.
353 356 """
354 357
355 358 if label:
356 359 if not opts.get('force') and label in repo.branchtags():
357 360 if label not in [p.branch() for p in repo.workingctx().parents()]:
358 361 raise util.Abort(_('a branch of the same name already exists'
359 362 ' (use --force to override)'))
360 363 repo.dirstate.setbranch(util.fromlocal(label))
361 364 ui.status(_('marked working directory as branch %s\n') % label)
362 365 else:
363 366 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
364 367
365 368 def branches(ui, repo, active=False):
366 369 """list repository named branches
367 370
368 371 List the repository's named branches, indicating which ones are
369 372 inactive. If active is specified, only show active branches.
370 373
371 374 A branch is considered active if it contains unmerged heads.
372 375
373 376 Use the command 'hg update' to switch to an existing branch.
374 377 """
375 378 b = repo.branchtags()
376 379 heads = dict.fromkeys(repo.heads(), 1)
377 380 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
378 381 l.sort()
379 382 l.reverse()
380 383 for ishead, r, n, t in l:
381 384 if active and not ishead:
382 385 # If we're only displaying active branches, abort the loop on
383 386 # encountering the first inactive head
384 387 break
385 388 else:
386 389 hexfunc = ui.debugflag and hex or short
387 390 if ui.quiet:
388 391 ui.write("%s\n" % t)
389 392 else:
390 393 spaces = " " * (30 - util.locallen(t))
391 394 # The code only gets here if inactive branches are being
392 395 # displayed or the branch is active.
393 396 isinactive = ((not ishead) and " (inactive)") or ''
394 397 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
395 398
396 399 def bundle(ui, repo, fname, dest=None, **opts):
397 400 """create a changegroup file
398 401
399 402 Generate a compressed changegroup file collecting changesets not
400 403 found in the other repository.
401 404
402 405 If no destination repository is specified the destination is
403 406 assumed to have all the nodes specified by one or more --base
404 407 parameters. To create a bundle containing all changesets, use
405 408 --all (or --base null).
406 409
407 410 The bundle file can then be transferred using conventional means and
408 411 applied to another repository with the unbundle or pull command.
409 412 This is useful when direct push and pull are not available or when
410 413 exporting an entire repository is undesirable.
411 414
412 415 Applying bundles preserves all changeset contents including
413 416 permissions, copy/rename information, and revision history.
414 417 """
415 418 revs = opts.get('rev') or None
416 419 if revs:
417 420 revs = [repo.lookup(rev) for rev in revs]
418 421 if opts.get('all'):
419 422 base = ['null']
420 423 else:
421 424 base = opts.get('base')
422 425 if base:
423 426 if dest:
424 427 raise util.Abort(_("--base is incompatible with specifiying "
425 428 "a destination"))
426 429 base = [repo.lookup(rev) for rev in base]
427 430 # create the right base
428 431 # XXX: nodesbetween / changegroup* should be "fixed" instead
429 432 o = []
430 433 has = {nullid: None}
431 434 for n in base:
432 435 has.update(repo.changelog.reachable(n))
433 436 if revs:
434 437 visit = list(revs)
435 438 else:
436 439 visit = repo.changelog.heads()
437 440 seen = {}
438 441 while visit:
439 442 n = visit.pop(0)
440 443 parents = [p for p in repo.changelog.parents(n) if p not in has]
441 444 if len(parents) == 0:
442 445 o.insert(0, n)
443 446 else:
444 447 for p in parents:
445 448 if p not in seen:
446 449 seen[p] = 1
447 450 visit.append(p)
448 451 else:
449 452 cmdutil.setremoteconfig(ui, opts)
450 453 dest, revs, checkout = hg.parseurl(
451 454 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
452 455 other = hg.repository(ui, dest)
453 456 o = repo.findoutgoing(other, force=opts['force'])
454 457
455 458 if revs:
456 459 cg = repo.changegroupsubset(o, revs, 'bundle')
457 460 else:
458 461 cg = repo.changegroup(o, 'bundle')
459 462 changegroup.writebundle(cg, fname, "HG10BZ")
460 463
461 464 def cat(ui, repo, file1, *pats, **opts):
462 465 """output the current or given revision of files
463 466
464 467 Print the specified files as they were at the given revision.
465 468 If no revision is given, the parent of the working directory is used,
466 469 or tip if no revision is checked out.
467 470
468 471 Output may be to a file, in which case the name of the file is
469 472 given using a format string. The formatting rules are the same as
470 473 for the export command, with the following additions:
471 474
472 475 %s basename of file being printed
473 476 %d dirname of file being printed, or '.' if in repo root
474 477 %p root-relative path name of file being printed
475 478 """
476 479 ctx = repo.changectx(opts['rev'])
477 480 err = 1
478 481 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
479 482 ctx.node()):
480 483 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
481 484 data = ctx.filectx(abs).data()
482 485 if opts.get('decode'):
483 486 data = repo.wwritedata(abs, data)
484 487 fp.write(data)
485 488 err = 0
486 489 return err
487 490
488 491 def clone(ui, source, dest=None, **opts):
489 492 """make a copy of an existing repository
490 493
491 494 Create a copy of an existing repository in a new directory.
492 495
493 496 If no destination directory name is specified, it defaults to the
494 497 basename of the source.
495 498
496 499 The location of the source is added to the new repository's
497 500 .hg/hgrc file, as the default to be used for future pulls.
498 501
499 502 For efficiency, hardlinks are used for cloning whenever the source
500 503 and destination are on the same filesystem (note this applies only
501 504 to the repository data, not to the checked out files). Some
502 505 filesystems, such as AFS, implement hardlinking incorrectly, but
503 506 do not report errors. In these cases, use the --pull option to
504 507 avoid hardlinking.
505 508
506 509 You can safely clone repositories and checked out files using full
507 510 hardlinks with
508 511
509 512 $ cp -al REPO REPOCLONE
510 513
511 514 which is the fastest way to clone. However, the operation is not
512 515 atomic (making sure REPO is not modified during the operation is
513 516 up to you) and you have to make sure your editor breaks hardlinks
514 517 (Emacs and most Linux Kernel tools do so).
515 518
516 519 If you use the -r option to clone up to a specific revision, no
517 520 subsequent revisions will be present in the cloned repository.
518 521 This option implies --pull, even on local repositories.
519 522
520 523 See pull for valid source format details.
521 524
522 525 It is possible to specify an ssh:// URL as the destination, but no
523 526 .hg/hgrc and working directory will be created on the remote side.
524 527 Look at the help text for the pull command for important details
525 528 about ssh:// URLs.
526 529 """
527 530 cmdutil.setremoteconfig(ui, opts)
528 531 hg.clone(ui, source, dest,
529 532 pull=opts['pull'],
530 533 stream=opts['uncompressed'],
531 534 rev=opts['rev'],
532 535 update=not opts['noupdate'])
533 536
534 537 def commit(ui, repo, *pats, **opts):
535 538 """commit the specified files or all outstanding changes
536 539
537 540 Commit changes to the given files into the repository.
538 541
539 542 If a list of files is omitted, all changes reported by "hg status"
540 543 will be committed.
541 544
542 545 If you are committing the result of a merge, do not provide any
543 546 file names or -I/-X filters.
544 547
545 548 If no commit message is specified, the configured editor is started to
546 549 enter a message.
547 550
548 551 See 'hg help dates' for a list of formats valid for -d/--date.
549 552 """
550 553 def commitfunc(ui, repo, files, message, match, opts):
551 554 return repo.commit(files, message, opts['user'], opts['date'], match,
552 555 force_editor=opts.get('force_editor'))
553 556
554 557 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
555 558 if not node:
556 559 return
557 560 cl = repo.changelog
558 561 rev = cl.rev(node)
559 562 parents = cl.parentrevs(rev)
560 563 if rev - 1 in parents:
561 564 # one of the parents was the old tip
562 565 return
563 566 if (parents == (nullrev, nullrev) or
564 567 len(cl.heads(cl.node(parents[0]))) > 1 and
565 568 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
566 569 ui.status(_('created new head\n'))
567 570
568 571 def copy(ui, repo, *pats, **opts):
569 572 """mark files as copied for the next commit
570 573
571 574 Mark dest as having copies of source files. If dest is a
572 575 directory, copies are put in that directory. If dest is a file,
573 576 there can only be one source.
574 577
575 578 By default, this command copies the contents of files as they
576 579 stand in the working directory. If invoked with --after, the
577 580 operation is recorded, but no copying is performed.
578 581
579 582 This command takes effect in the next commit. To undo a copy
580 583 before that, see hg revert.
581 584 """
582 585 wlock = repo.wlock(False)
583 586 try:
584 587 return cmdutil.copy(ui, repo, pats, opts)
585 588 finally:
586 589 del wlock
587 590
588 591 def debugancestor(ui, repo, *args):
589 592 """find the ancestor revision of two revisions in a given index"""
590 593 if len(args) == 3:
591 594 index, rev1, rev2 = args
592 595 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
593 596 lookup = r.lookup
594 597 elif len(args) == 2:
595 598 if not repo:
596 599 raise util.Abort(_("There is no Mercurial repository here "
597 600 "(.hg not found)"))
598 601 rev1, rev2 = args
599 602 r = repo.changelog
600 603 lookup = repo.lookup
601 604 else:
602 605 raise util.Abort(_('either two or three arguments required'))
603 606 a = r.ancestor(lookup(rev1), lookup(rev2))
604 607 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
605 608
606 609 def debugcomplete(ui, cmd='', **opts):
607 610 """returns the completion list associated with the given command"""
608 611
609 612 if opts['options']:
610 613 options = []
611 614 otables = [globalopts]
612 615 if cmd:
613 616 aliases, entry = cmdutil.findcmd(ui, cmd, table)
614 617 otables.append(entry[1])
615 618 for t in otables:
616 619 for o in t:
617 620 if o[0]:
618 621 options.append('-%s' % o[0])
619 622 options.append('--%s' % o[1])
620 623 ui.write("%s\n" % "\n".join(options))
621 624 return
622 625
623 626 clist = cmdutil.findpossible(ui, cmd, table).keys()
624 627 clist.sort()
625 628 ui.write("%s\n" % "\n".join(clist))
626 629
627 630 def debugfsinfo(ui, path = "."):
628 631 file('.debugfsinfo', 'w').write('')
629 632 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
630 633 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
631 634 ui.write('case-sensitive: %s\n' % (util.checkfolding('.debugfsinfo')
632 635 and 'yes' or 'no'))
633 636 os.unlink('.debugfsinfo')
634 637
635 638 def debugrebuildstate(ui, repo, rev=""):
636 639 """rebuild the dirstate as it would look like for the given revision"""
637 640 if rev == "":
638 641 rev = repo.changelog.tip()
639 642 ctx = repo.changectx(rev)
640 643 files = ctx.manifest()
641 644 wlock = repo.wlock()
642 645 try:
643 646 repo.dirstate.rebuild(rev, files)
644 647 finally:
645 648 del wlock
646 649
647 650 def debugcheckstate(ui, repo):
648 651 """validate the correctness of the current dirstate"""
649 652 parent1, parent2 = repo.dirstate.parents()
650 653 m1 = repo.changectx(parent1).manifest()
651 654 m2 = repo.changectx(parent2).manifest()
652 655 errors = 0
653 656 for f in repo.dirstate:
654 657 state = repo.dirstate[f]
655 658 if state in "nr" and f not in m1:
656 659 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
657 660 errors += 1
658 661 if state in "a" and f in m1:
659 662 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
660 663 errors += 1
661 664 if state in "m" and f not in m1 and f not in m2:
662 665 ui.warn(_("%s in state %s, but not in either manifest\n") %
663 666 (f, state))
664 667 errors += 1
665 668 for f in m1:
666 669 state = repo.dirstate[f]
667 670 if state not in "nrm":
668 671 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
669 672 errors += 1
670 673 if errors:
671 674 error = _(".hg/dirstate inconsistent with current parent's manifest")
672 675 raise util.Abort(error)
673 676
674 677 def showconfig(ui, repo, *values, **opts):
675 678 """show combined config settings from all hgrc files
676 679
677 680 With no args, print names and values of all config items.
678 681
679 682 With one arg of the form section.name, print just the value of
680 683 that config item.
681 684
682 685 With multiple args, print names and values of all config items
683 686 with matching section names."""
684 687
685 688 untrusted = bool(opts.get('untrusted'))
686 689 if values:
687 690 if len([v for v in values if '.' in v]) > 1:
688 691 raise util.Abort(_('only one config item permitted'))
689 692 for section, name, value in ui.walkconfig(untrusted=untrusted):
690 693 sectname = section + '.' + name
691 694 if values:
692 695 for v in values:
693 696 if v == section:
694 697 ui.write('%s=%s\n' % (sectname, value))
695 698 elif v == sectname:
696 699 ui.write(value, '\n')
697 700 else:
698 701 ui.write('%s=%s\n' % (sectname, value))
699 702
700 703 def debugsetparents(ui, repo, rev1, rev2=None):
701 704 """manually set the parents of the current working directory
702 705
703 706 This is useful for writing repository conversion tools, but should
704 707 be used with care.
705 708 """
706 709
707 710 if not rev2:
708 711 rev2 = hex(nullid)
709 712
710 713 wlock = repo.wlock()
711 714 try:
712 715 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
713 716 finally:
714 717 del wlock
715 718
716 719 def debugstate(ui, repo, nodates=None):
717 720 """show the contents of the current dirstate"""
718 721 k = repo.dirstate._map.items()
719 722 k.sort()
720 723 timestr = ""
721 724 showdate = not nodates
722 725 for file_, ent in k:
723 726 if showdate:
724 727 if ent[3] == -1:
725 728 # Pad or slice to locale representation
726 729 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
727 730 timestr = 'unset'
728 731 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
729 732 else:
730 733 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
731 734 if ent[1] & 020000:
732 735 mode = 'lnk'
733 736 else:
734 737 mode = '%3o' % (ent[1] & 0777)
735 738 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
736 739 for f in repo.dirstate.copies():
737 740 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
738 741
739 742 def debugdata(ui, file_, rev):
740 743 """dump the contents of a data file revision"""
741 744 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
742 745 try:
743 746 ui.write(r.revision(r.lookup(rev)))
744 747 except KeyError:
745 748 raise util.Abort(_('invalid revision identifier %s') % rev)
746 749
747 750 def debugdate(ui, date, range=None, **opts):
748 751 """parse and display a date"""
749 752 if opts["extended"]:
750 753 d = util.parsedate(date, util.extendeddateformats)
751 754 else:
752 755 d = util.parsedate(date)
753 756 ui.write("internal: %s %s\n" % d)
754 757 ui.write("standard: %s\n" % util.datestr(d))
755 758 if range:
756 759 m = util.matchdate(range)
757 760 ui.write("match: %s\n" % m(d[0]))
758 761
759 762 def debugindex(ui, file_):
760 763 """dump the contents of an index file"""
761 764 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
762 765 ui.write(" rev offset length base linkrev" +
763 766 " nodeid p1 p2\n")
764 767 for i in xrange(r.count()):
765 768 node = r.node(i)
766 769 try:
767 770 pp = r.parents(node)
768 771 except:
769 772 pp = [nullid, nullid]
770 773 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
771 774 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
772 775 short(node), short(pp[0]), short(pp[1])))
773 776
774 777 def debugindexdot(ui, file_):
775 778 """dump an index DAG as a .dot file"""
776 779 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
777 780 ui.write("digraph G {\n")
778 781 for i in xrange(r.count()):
779 782 node = r.node(i)
780 783 pp = r.parents(node)
781 784 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
782 785 if pp[1] != nullid:
783 786 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
784 787 ui.write("}\n")
785 788
786 789 def debuginstall(ui):
787 790 '''test Mercurial installation'''
788 791
789 792 def writetemp(contents):
790 793 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
791 794 f = os.fdopen(fd, "wb")
792 795 f.write(contents)
793 796 f.close()
794 797 return name
795 798
796 799 problems = 0
797 800
798 801 # encoding
799 802 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
800 803 try:
801 804 util.fromlocal("test")
802 805 except util.Abort, inst:
803 806 ui.write(" %s\n" % inst)
804 807 ui.write(_(" (check that your locale is properly set)\n"))
805 808 problems += 1
806 809
807 810 # compiled modules
808 811 ui.status(_("Checking extensions...\n"))
809 812 try:
810 813 import bdiff, mpatch, base85
811 814 except Exception, inst:
812 815 ui.write(" %s\n" % inst)
813 816 ui.write(_(" One or more extensions could not be found"))
814 817 ui.write(_(" (check that you compiled the extensions)\n"))
815 818 problems += 1
816 819
817 820 # templates
818 821 ui.status(_("Checking templates...\n"))
819 822 try:
820 823 import templater
821 824 t = templater.templater(templater.templatepath("map-cmdline.default"))
822 825 except Exception, inst:
823 826 ui.write(" %s\n" % inst)
824 827 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
825 828 problems += 1
826 829
827 830 # patch
828 831 ui.status(_("Checking patch...\n"))
829 832 patchproblems = 0
830 833 a = "1\n2\n3\n4\n"
831 834 b = "1\n2\n3\ninsert\n4\n"
832 835 fa = writetemp(a)
833 836 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
834 837 os.path.basename(fa))
835 838 fd = writetemp(d)
836 839
837 840 files = {}
838 841 try:
839 842 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
840 843 except util.Abort, e:
841 844 ui.write(_(" patch call failed:\n"))
842 845 ui.write(" " + str(e) + "\n")
843 846 patchproblems += 1
844 847 else:
845 848 if list(files) != [os.path.basename(fa)]:
846 849 ui.write(_(" unexpected patch output!\n"))
847 850 patchproblems += 1
848 851 a = file(fa).read()
849 852 if a != b:
850 853 ui.write(_(" patch test failed!\n"))
851 854 patchproblems += 1
852 855
853 856 if patchproblems:
854 857 if ui.config('ui', 'patch'):
855 858 ui.write(_(" (Current patch tool may be incompatible with patch,"
856 859 " or misconfigured. Please check your .hgrc file)\n"))
857 860 else:
858 861 ui.write(_(" Internal patcher failure, please report this error"
859 862 " to http://www.selenic.com/mercurial/bts\n"))
860 863 problems += patchproblems
861 864
862 865 os.unlink(fa)
863 866 os.unlink(fd)
864 867
865 868 # editor
866 869 ui.status(_("Checking commit editor...\n"))
867 870 editor = ui.geteditor()
868 871 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
869 872 if not cmdpath:
870 873 if editor == 'vi':
871 874 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
872 875 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
873 876 else:
874 877 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
875 878 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
876 879 problems += 1
877 880
878 881 # check username
879 882 ui.status(_("Checking username...\n"))
880 883 user = os.environ.get("HGUSER")
881 884 if user is None:
882 885 user = ui.config("ui", "username")
883 886 if user is None:
884 887 user = os.environ.get("EMAIL")
885 888 if not user:
886 889 ui.warn(" ")
887 890 ui.username()
888 891 ui.write(_(" (specify a username in your .hgrc file)\n"))
889 892
890 893 if not problems:
891 894 ui.status(_("No problems detected\n"))
892 895 else:
893 896 ui.write(_("%s problems detected,"
894 897 " please check your install!\n") % problems)
895 898
896 899 return problems
897 900
898 901 def debugrename(ui, repo, file1, *pats, **opts):
899 902 """dump rename information"""
900 903
901 904 ctx = repo.changectx(opts.get('rev', 'tip'))
902 905 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
903 906 ctx.node()):
904 907 fctx = ctx.filectx(abs)
905 908 m = fctx.filelog().renamed(fctx.filenode())
906 909 if m:
907 910 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
908 911 else:
909 912 ui.write(_("%s not renamed\n") % rel)
910 913
911 914 def debugwalk(ui, repo, *pats, **opts):
912 915 """show how files match on given patterns"""
913 916 items = list(cmdutil.walk(repo, pats, opts))
914 917 if not items:
915 918 return
916 919 fmt = '%%s %%-%ds %%-%ds %%s' % (
917 920 max([len(abs) for (src, abs, rel, exact) in items]),
918 921 max([len(rel) for (src, abs, rel, exact) in items]))
919 922 for src, abs, rel, exact in items:
920 923 line = fmt % (src, abs, rel, exact and 'exact' or '')
921 924 ui.write("%s\n" % line.rstrip())
922 925
923 926 def diff(ui, repo, *pats, **opts):
924 927 """diff repository (or selected files)
925 928
926 929 Show differences between revisions for the specified files.
927 930
928 931 Differences between files are shown using the unified diff format.
929 932
930 933 NOTE: diff may generate unexpected results for merges, as it will
931 934 default to comparing against the working directory's first parent
932 935 changeset if no revisions are specified.
933 936
934 937 When two revision arguments are given, then changes are shown
935 938 between those revisions. If only one revision is specified then
936 939 that revision is compared to the working directory, and, when no
937 940 revisions are specified, the working directory files are compared
938 941 to its parent.
939 942
940 943 Without the -a option, diff will avoid generating diffs of files
941 944 it detects as binary. With -a, diff will generate a diff anyway,
942 945 probably with undesirable results.
943 946 """
944 947 node1, node2 = cmdutil.revpair(repo, opts['rev'])
945 948
946 949 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
947 950
948 951 patch.diff(repo, node1, node2, fns, match=matchfn,
949 952 opts=patch.diffopts(ui, opts))
950 953
951 954 def export(ui, repo, *changesets, **opts):
952 955 """dump the header and diffs for one or more changesets
953 956
954 957 Print the changeset header and diffs for one or more revisions.
955 958
956 959 The information shown in the changeset header is: author,
957 960 changeset hash, parent(s) and commit comment.
958 961
959 962 NOTE: export may generate unexpected diff output for merge changesets,
960 963 as it will compare the merge changeset against its first parent only.
961 964
962 965 Output may be to a file, in which case the name of the file is
963 966 given using a format string. The formatting rules are as follows:
964 967
965 968 %% literal "%" character
966 969 %H changeset hash (40 bytes of hexadecimal)
967 970 %N number of patches being generated
968 971 %R changeset revision number
969 972 %b basename of the exporting repository
970 973 %h short-form changeset hash (12 bytes of hexadecimal)
971 974 %n zero-padded sequence number, starting at 1
972 975 %r zero-padded changeset revision number
973 976
974 977 Without the -a option, export will avoid generating diffs of files
975 978 it detects as binary. With -a, export will generate a diff anyway,
976 979 probably with undesirable results.
977 980
978 981 With the --switch-parent option, the diff will be against the second
979 982 parent. It can be useful to review a merge.
980 983 """
981 984 if not changesets:
982 985 raise util.Abort(_("export requires at least one changeset"))
983 986 revs = cmdutil.revrange(repo, changesets)
984 987 if len(revs) > 1:
985 988 ui.note(_('exporting patches:\n'))
986 989 else:
987 990 ui.note(_('exporting patch:\n'))
988 991 patch.export(repo, revs, template=opts['output'],
989 992 switch_parent=opts['switch_parent'],
990 993 opts=patch.diffopts(ui, opts))
991 994
992 995 def grep(ui, repo, pattern, *pats, **opts):
993 996 """search for a pattern in specified files and revisions
994 997
995 998 Search revisions of files for a regular expression.
996 999
997 1000 This command behaves differently than Unix grep. It only accepts
998 1001 Python/Perl regexps. It searches repository history, not the
999 1002 working directory. It always prints the revision number in which
1000 1003 a match appears.
1001 1004
1002 1005 By default, grep only prints output for the first revision of a
1003 1006 file in which it finds a match. To get it to print every revision
1004 1007 that contains a change in match status ("-" for a match that
1005 1008 becomes a non-match, or "+" for a non-match that becomes a match),
1006 1009 use the --all flag.
1007 1010 """
1008 1011 reflags = 0
1009 1012 if opts['ignore_case']:
1010 1013 reflags |= re.I
1011 1014 try:
1012 1015 regexp = re.compile(pattern, reflags)
1013 1016 except Exception, inst:
1014 1017 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1015 1018 return None
1016 1019 sep, eol = ':', '\n'
1017 1020 if opts['print0']:
1018 1021 sep = eol = '\0'
1019 1022
1020 1023 fcache = {}
1021 1024 def getfile(fn):
1022 1025 if fn not in fcache:
1023 1026 fcache[fn] = repo.file(fn)
1024 1027 return fcache[fn]
1025 1028
1026 1029 def matchlines(body):
1027 1030 begin = 0
1028 1031 linenum = 0
1029 1032 while True:
1030 1033 match = regexp.search(body, begin)
1031 1034 if not match:
1032 1035 break
1033 1036 mstart, mend = match.span()
1034 1037 linenum += body.count('\n', begin, mstart) + 1
1035 1038 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1036 1039 lend = body.find('\n', mend)
1037 1040 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1038 1041 begin = lend + 1
1039 1042
1040 1043 class linestate(object):
1041 1044 def __init__(self, line, linenum, colstart, colend):
1042 1045 self.line = line
1043 1046 self.linenum = linenum
1044 1047 self.colstart = colstart
1045 1048 self.colend = colend
1046 1049
1047 1050 def __eq__(self, other):
1048 1051 return self.line == other.line
1049 1052
1050 1053 matches = {}
1051 1054 copies = {}
1052 1055 def grepbody(fn, rev, body):
1053 1056 matches[rev].setdefault(fn, [])
1054 1057 m = matches[rev][fn]
1055 1058 for lnum, cstart, cend, line in matchlines(body):
1056 1059 s = linestate(line, lnum, cstart, cend)
1057 1060 m.append(s)
1058 1061
1059 1062 def difflinestates(a, b):
1060 1063 sm = difflib.SequenceMatcher(None, a, b)
1061 1064 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1062 1065 if tag == 'insert':
1063 1066 for i in xrange(blo, bhi):
1064 1067 yield ('+', b[i])
1065 1068 elif tag == 'delete':
1066 1069 for i in xrange(alo, ahi):
1067 1070 yield ('-', a[i])
1068 1071 elif tag == 'replace':
1069 1072 for i in xrange(alo, ahi):
1070 1073 yield ('-', a[i])
1071 1074 for i in xrange(blo, bhi):
1072 1075 yield ('+', b[i])
1073 1076
1074 1077 prev = {}
1075 1078 def display(fn, rev, states, prevstates):
1076 1079 datefunc = ui.quiet and util.shortdate or util.datestr
1077 1080 found = False
1078 1081 filerevmatches = {}
1079 1082 r = prev.get(fn, -1)
1080 1083 if opts['all']:
1081 1084 iter = difflinestates(states, prevstates)
1082 1085 else:
1083 1086 iter = [('', l) for l in prevstates]
1084 1087 for change, l in iter:
1085 1088 cols = [fn, str(r)]
1086 1089 if opts['line_number']:
1087 1090 cols.append(str(l.linenum))
1088 1091 if opts['all']:
1089 1092 cols.append(change)
1090 1093 if opts['user']:
1091 1094 cols.append(ui.shortuser(get(r)[1]))
1092 1095 if opts.get('date'):
1093 1096 cols.append(datefunc(get(r)[2]))
1094 1097 if opts['files_with_matches']:
1095 1098 c = (fn, r)
1096 1099 if c in filerevmatches:
1097 1100 continue
1098 1101 filerevmatches[c] = 1
1099 1102 else:
1100 1103 cols.append(l.line)
1101 1104 ui.write(sep.join(cols), eol)
1102 1105 found = True
1103 1106 return found
1104 1107
1105 1108 fstate = {}
1106 1109 skip = {}
1107 1110 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1108 1111 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1109 1112 found = False
1110 1113 follow = opts.get('follow')
1111 1114 for st, rev, fns in changeiter:
1112 1115 if st == 'window':
1113 1116 matches.clear()
1114 1117 elif st == 'add':
1115 1118 ctx = repo.changectx(rev)
1116 1119 matches[rev] = {}
1117 1120 for fn in fns:
1118 1121 if fn in skip:
1119 1122 continue
1120 1123 try:
1121 1124 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1122 1125 fstate.setdefault(fn, [])
1123 1126 if follow:
1124 1127 copied = getfile(fn).renamed(ctx.filenode(fn))
1125 1128 if copied:
1126 1129 copies.setdefault(rev, {})[fn] = copied[0]
1127 1130 except revlog.LookupError:
1128 1131 pass
1129 1132 elif st == 'iter':
1130 1133 states = matches[rev].items()
1131 1134 states.sort()
1132 1135 for fn, m in states:
1133 1136 copy = copies.get(rev, {}).get(fn)
1134 1137 if fn in skip:
1135 1138 if copy:
1136 1139 skip[copy] = True
1137 1140 continue
1138 1141 if fn in prev or fstate[fn]:
1139 1142 r = display(fn, rev, m, fstate[fn])
1140 1143 found = found or r
1141 1144 if r and not opts['all']:
1142 1145 skip[fn] = True
1143 1146 if copy:
1144 1147 skip[copy] = True
1145 1148 fstate[fn] = m
1146 1149 if copy:
1147 1150 fstate[copy] = m
1148 1151 prev[fn] = rev
1149 1152
1150 1153 fstate = fstate.items()
1151 1154 fstate.sort()
1152 1155 for fn, state in fstate:
1153 1156 if fn in skip:
1154 1157 continue
1155 1158 if fn not in copies.get(prev[fn], {}):
1156 1159 found = display(fn, rev, {}, state) or found
1157 1160 return (not found and 1) or 0
1158 1161
1159 1162 def heads(ui, repo, *branchrevs, **opts):
1160 1163 """show current repository heads or show branch heads
1161 1164
1162 1165 With no arguments, show all repository head changesets.
1163 1166
1164 1167 If branch or revisions names are given this will show the heads of
1165 1168 the specified branches or the branches those revisions are tagged
1166 1169 with.
1167 1170
1168 1171 Repository "heads" are changesets that don't have child
1169 1172 changesets. They are where development generally takes place and
1170 1173 are the usual targets for update and merge operations.
1171 1174
1172 1175 Branch heads are changesets that have a given branch tag, but have
1173 1176 no child changesets with that tag. They are usually where
1174 1177 development on the given branch takes place.
1175 1178 """
1176 1179 if opts['rev']:
1177 1180 start = repo.lookup(opts['rev'])
1178 1181 else:
1179 1182 start = None
1180 1183 if not branchrevs:
1181 1184 # Assume we're looking repo-wide heads if no revs were specified.
1182 1185 heads = repo.heads(start)
1183 1186 else:
1184 1187 heads = []
1185 1188 visitedset = util.set()
1186 1189 for branchrev in branchrevs:
1187 1190 branch = repo.changectx(branchrev).branch()
1188 1191 if branch in visitedset:
1189 1192 continue
1190 1193 visitedset.add(branch)
1191 1194 bheads = repo.branchheads(branch, start)
1192 1195 if not bheads:
1193 1196 if branch != branchrev:
1194 1197 ui.warn(_("no changes on branch %s containing %s are "
1195 1198 "reachable from %s\n")
1196 1199 % (branch, branchrev, opts['rev']))
1197 1200 else:
1198 1201 ui.warn(_("no changes on branch %s are reachable from %s\n")
1199 1202 % (branch, opts['rev']))
1200 1203 heads.extend(bheads)
1201 1204 if not heads:
1202 1205 return 1
1203 1206 displayer = cmdutil.show_changeset(ui, repo, opts)
1204 1207 for n in heads:
1205 1208 displayer.show(changenode=n)
1206 1209
1207 1210 def help_(ui, name=None, with_version=False):
1208 1211 """show help for a command, extension, or list of commands
1209 1212
1210 1213 With no arguments, print a list of commands and short help.
1211 1214
1212 1215 Given a command name, print help for that command.
1213 1216
1214 1217 Given an extension name, print help for that extension, and the
1215 1218 commands it provides."""
1216 1219 option_lists = []
1217 1220
1218 1221 def addglobalopts(aliases):
1219 1222 if ui.verbose:
1220 1223 option_lists.append((_("global options:"), globalopts))
1221 1224 if name == 'shortlist':
1222 1225 option_lists.append((_('use "hg help" for the full list '
1223 1226 'of commands'), ()))
1224 1227 else:
1225 1228 if name == 'shortlist':
1226 1229 msg = _('use "hg help" for the full list of commands '
1227 1230 'or "hg -v" for details')
1228 1231 elif aliases:
1229 1232 msg = _('use "hg -v help%s" to show aliases and '
1230 1233 'global options') % (name and " " + name or "")
1231 1234 else:
1232 1235 msg = _('use "hg -v help %s" to show global options') % name
1233 1236 option_lists.append((msg, ()))
1234 1237
1235 1238 def helpcmd(name):
1236 1239 if with_version:
1237 1240 version_(ui)
1238 1241 ui.write('\n')
1239 1242 aliases, i = cmdutil.findcmd(ui, name, table)
1240 1243 # synopsis
1241 1244 ui.write("%s\n" % i[2])
1242 1245
1243 1246 # aliases
1244 1247 if not ui.quiet and len(aliases) > 1:
1245 1248 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1246 1249
1247 1250 # description
1248 1251 doc = i[0].__doc__
1249 1252 if not doc:
1250 1253 doc = _("(No help text available)")
1251 1254 if ui.quiet:
1252 1255 doc = doc.splitlines(0)[0]
1253 1256 ui.write("\n%s\n" % doc.rstrip())
1254 1257
1255 1258 if not ui.quiet:
1256 1259 # options
1257 1260 if i[1]:
1258 1261 option_lists.append((_("options:\n"), i[1]))
1259 1262
1260 1263 addglobalopts(False)
1261 1264
1262 1265 def helplist(header, select=None):
1263 1266 h = {}
1264 1267 cmds = {}
1265 1268 for c, e in table.items():
1266 1269 f = c.split("|", 1)[0]
1267 1270 if select and not select(f):
1268 1271 continue
1269 1272 if name == "shortlist" and not f.startswith("^"):
1270 1273 continue
1271 1274 f = f.lstrip("^")
1272 1275 if not ui.debugflag and f.startswith("debug"):
1273 1276 continue
1274 1277 doc = e[0].__doc__
1275 1278 if not doc:
1276 1279 doc = _("(No help text available)")
1277 1280 h[f] = doc.splitlines(0)[0].rstrip()
1278 1281 cmds[f] = c.lstrip("^")
1279 1282
1280 1283 if not h:
1281 1284 ui.status(_('no commands defined\n'))
1282 1285 return
1283 1286
1284 1287 ui.status(header)
1285 1288 fns = h.keys()
1286 1289 fns.sort()
1287 1290 m = max(map(len, fns))
1288 1291 for f in fns:
1289 1292 if ui.verbose:
1290 1293 commands = cmds[f].replace("|",", ")
1291 1294 ui.write(" %s:\n %s\n"%(commands, h[f]))
1292 1295 else:
1293 1296 ui.write(' %-*s %s\n' % (m, f, h[f]))
1294 1297
1295 1298 if not ui.quiet:
1296 1299 addglobalopts(True)
1297 1300
1298 1301 def helptopic(name):
1299 1302 v = None
1300 1303 for i in help.helptable:
1301 1304 l = i.split('|')
1302 1305 if name in l:
1303 1306 v = i
1304 1307 header = l[-1]
1305 1308 if not v:
1306 1309 raise cmdutil.UnknownCommand(name)
1307 1310
1308 1311 # description
1309 1312 doc = help.helptable[v]
1310 1313 if not doc:
1311 1314 doc = _("(No help text available)")
1312 1315 if callable(doc):
1313 1316 doc = doc()
1314 1317
1315 1318 ui.write("%s\n" % header)
1316 1319 ui.write("%s\n" % doc.rstrip())
1317 1320
1318 1321 def helpext(name):
1319 1322 try:
1320 1323 mod = extensions.find(name)
1321 1324 except KeyError:
1322 1325 raise cmdutil.UnknownCommand(name)
1323 1326
1324 1327 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1325 1328 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1326 1329 for d in doc[1:]:
1327 1330 ui.write(d, '\n')
1328 1331
1329 1332 ui.status('\n')
1330 1333
1331 1334 try:
1332 1335 ct = mod.cmdtable
1333 1336 except AttributeError:
1334 1337 ct = {}
1335 1338
1336 1339 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1337 1340 helplist(_('list of commands:\n\n'), modcmds.has_key)
1338 1341
1339 1342 if name and name != 'shortlist':
1340 1343 i = None
1341 1344 for f in (helpcmd, helptopic, helpext):
1342 1345 try:
1343 1346 f(name)
1344 1347 i = None
1345 1348 break
1346 1349 except cmdutil.UnknownCommand, inst:
1347 1350 i = inst
1348 1351 if i:
1349 1352 raise i
1350 1353
1351 1354 else:
1352 1355 # program name
1353 1356 if ui.verbose or with_version:
1354 1357 version_(ui)
1355 1358 else:
1356 1359 ui.status(_("Mercurial Distributed SCM\n"))
1357 1360 ui.status('\n')
1358 1361
1359 1362 # list of commands
1360 1363 if name == "shortlist":
1361 1364 header = _('basic commands:\n\n')
1362 1365 else:
1363 1366 header = _('list of commands:\n\n')
1364 1367
1365 1368 helplist(header)
1366 1369
1367 1370 # list all option lists
1368 1371 opt_output = []
1369 1372 for title, options in option_lists:
1370 1373 opt_output.append(("\n%s" % title, None))
1371 1374 for shortopt, longopt, default, desc in options:
1372 1375 if "DEPRECATED" in desc and not ui.verbose: continue
1373 1376 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1374 1377 longopt and " --%s" % longopt),
1375 1378 "%s%s" % (desc,
1376 1379 default
1377 1380 and _(" (default: %s)") % default
1378 1381 or "")))
1379 1382
1380 1383 if opt_output:
1381 1384 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1382 1385 for first, second in opt_output:
1383 1386 if second:
1384 1387 ui.write(" %-*s %s\n" % (opts_len, first, second))
1385 1388 else:
1386 1389 ui.write("%s\n" % first)
1387 1390
1388 1391 def identify(ui, repo, source=None,
1389 1392 rev=None, num=None, id=None, branch=None, tags=None):
1390 1393 """identify the working copy or specified revision
1391 1394
1392 1395 With no revision, print a summary of the current state of the repo.
1393 1396
1394 1397 With a path, do a lookup in another repository.
1395 1398
1396 1399 This summary identifies the repository state using one or two parent
1397 1400 hash identifiers, followed by a "+" if there are uncommitted changes
1398 1401 in the working directory, a list of tags for this revision and a branch
1399 1402 name for non-default branches.
1400 1403 """
1401 1404
1402 1405 if not repo and not source:
1403 1406 raise util.Abort(_("There is no Mercurial repository here "
1404 1407 "(.hg not found)"))
1405 1408
1406 1409 hexfunc = ui.debugflag and hex or short
1407 1410 default = not (num or id or branch or tags)
1408 1411 output = []
1409 1412
1410 1413 if source:
1411 1414 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1412 1415 srepo = hg.repository(ui, source)
1413 1416 if not rev and revs:
1414 1417 rev = revs[0]
1415 1418 if not rev:
1416 1419 rev = "tip"
1417 1420 if num or branch or tags:
1418 1421 raise util.Abort(
1419 1422 "can't query remote revision number, branch, or tags")
1420 1423 output = [hexfunc(srepo.lookup(rev))]
1421 1424 elif not rev:
1422 1425 ctx = repo.workingctx()
1423 1426 parents = ctx.parents()
1424 1427 changed = False
1425 1428 if default or id or num:
1426 1429 changed = ctx.files() + ctx.deleted()
1427 1430 if default or id:
1428 1431 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1429 1432 (changed) and "+" or "")]
1430 1433 if num:
1431 1434 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1432 1435 (changed) and "+" or ""))
1433 1436 else:
1434 1437 ctx = repo.changectx(rev)
1435 1438 if default or id:
1436 1439 output = [hexfunc(ctx.node())]
1437 1440 if num:
1438 1441 output.append(str(ctx.rev()))
1439 1442
1440 1443 if not source and default and not ui.quiet:
1441 1444 b = util.tolocal(ctx.branch())
1442 1445 if b != 'default':
1443 1446 output.append("(%s)" % b)
1444 1447
1445 1448 # multiple tags for a single parent separated by '/'
1446 1449 t = "/".join(ctx.tags())
1447 1450 if t:
1448 1451 output.append(t)
1449 1452
1450 1453 if branch:
1451 1454 output.append(util.tolocal(ctx.branch()))
1452 1455
1453 1456 if tags:
1454 1457 output.extend(ctx.tags())
1455 1458
1456 1459 ui.write("%s\n" % ' '.join(output))
1457 1460
1458 1461 def import_(ui, repo, patch1, *patches, **opts):
1459 1462 """import an ordered set of patches
1460 1463
1461 1464 Import a list of patches and commit them individually.
1462 1465
1463 1466 If there are outstanding changes in the working directory, import
1464 1467 will abort unless given the -f flag.
1465 1468
1466 1469 You can import a patch straight from a mail message. Even patches
1467 1470 as attachments work (body part must be type text/plain or
1468 1471 text/x-patch to be used). From and Subject headers of email
1469 1472 message are used as default committer and commit message. All
1470 1473 text/plain body parts before first diff are added to commit
1471 1474 message.
1472 1475
1473 1476 If the imported patch was generated by hg export, user and description
1474 1477 from patch override values from message headers and body. Values
1475 1478 given on command line with -m and -u override these.
1476 1479
1477 1480 If --exact is specified, import will set the working directory
1478 1481 to the parent of each patch before applying it, and will abort
1479 1482 if the resulting changeset has a different ID than the one
1480 1483 recorded in the patch. This may happen due to character set
1481 1484 problems or other deficiencies in the text patch format.
1482 1485
1483 1486 To read a patch from standard input, use patch name "-".
1484 1487 See 'hg help dates' for a list of formats valid for -d/--date.
1485 1488 """
1486 1489 patches = (patch1,) + patches
1487 1490
1488 1491 date = opts.get('date')
1489 1492 if date:
1490 1493 opts['date'] = util.parsedate(date)
1491 1494
1492 1495 if opts.get('exact') or not opts['force']:
1493 1496 cmdutil.bail_if_changed(repo)
1494 1497
1495 1498 d = opts["base"]
1496 1499 strip = opts["strip"]
1497 1500 wlock = lock = None
1498 1501 try:
1499 1502 wlock = repo.wlock()
1500 1503 lock = repo.lock()
1501 1504 for p in patches:
1502 1505 pf = os.path.join(d, p)
1503 1506
1504 1507 if pf == '-':
1505 1508 ui.status(_("applying patch from stdin\n"))
1506 1509 data = patch.extract(ui, sys.stdin)
1507 1510 else:
1508 1511 ui.status(_("applying %s\n") % p)
1509 1512 if os.path.exists(pf):
1510 1513 data = patch.extract(ui, file(pf, 'rb'))
1511 1514 else:
1512 1515 data = patch.extract(ui, urllib.urlopen(pf))
1513 1516 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1514 1517
1515 1518 if tmpname is None:
1516 1519 raise util.Abort(_('no diffs found'))
1517 1520
1518 1521 try:
1519 1522 cmdline_message = cmdutil.logmessage(opts)
1520 1523 if cmdline_message:
1521 1524 # pickup the cmdline msg
1522 1525 message = cmdline_message
1523 1526 elif message:
1524 1527 # pickup the patch msg
1525 1528 message = message.strip()
1526 1529 else:
1527 1530 # launch the editor
1528 1531 message = None
1529 1532 ui.debug(_('message:\n%s\n') % message)
1530 1533
1531 1534 wp = repo.workingctx().parents()
1532 1535 if opts.get('exact'):
1533 1536 if not nodeid or not p1:
1534 1537 raise util.Abort(_('not a mercurial patch'))
1535 1538 p1 = repo.lookup(p1)
1536 1539 p2 = repo.lookup(p2 or hex(nullid))
1537 1540
1538 1541 if p1 != wp[0].node():
1539 1542 hg.clean(repo, p1)
1540 1543 repo.dirstate.setparents(p1, p2)
1541 1544 elif p2:
1542 1545 try:
1543 1546 p1 = repo.lookup(p1)
1544 1547 p2 = repo.lookup(p2)
1545 1548 if p1 == wp[0].node():
1546 1549 repo.dirstate.setparents(p1, p2)
1547 1550 except RepoError:
1548 1551 pass
1549 1552 if opts.get('exact') or opts.get('import_branch'):
1550 1553 repo.dirstate.setbranch(branch or 'default')
1551 1554
1552 1555 files = {}
1553 1556 try:
1554 1557 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1555 1558 files=files)
1556 1559 finally:
1557 1560 files = patch.updatedir(ui, repo, files)
1558 1561 if not opts.get('no_commit'):
1559 1562 n = repo.commit(files, message, opts.get('user') or user,
1560 1563 opts.get('date') or date)
1561 1564 if opts.get('exact'):
1562 1565 if hex(n) != nodeid:
1563 1566 repo.rollback()
1564 1567 raise util.Abort(_('patch is damaged'
1565 1568 ' or loses information'))
1566 1569 # Force a dirstate write so that the next transaction
1567 1570 # backups an up-do-date file.
1568 1571 repo.dirstate.write()
1569 1572 finally:
1570 1573 os.unlink(tmpname)
1571 1574 finally:
1572 1575 del lock, wlock
1573 1576
1574 1577 def incoming(ui, repo, source="default", **opts):
1575 1578 """show new changesets found in source
1576 1579
1577 1580 Show new changesets found in the specified path/URL or the default
1578 1581 pull location. These are the changesets that would be pulled if a pull
1579 1582 was requested.
1580 1583
1581 1584 For remote repository, using --bundle avoids downloading the changesets
1582 1585 twice if the incoming is followed by a pull.
1583 1586
1584 1587 See pull for valid source format details.
1585 1588 """
1586 1589 limit = cmdutil.loglimit(opts)
1587 1590 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
1588 1591 cmdutil.setremoteconfig(ui, opts)
1589 1592
1590 1593 other = hg.repository(ui, source)
1591 1594 ui.status(_('comparing with %s\n') % util.hidepassword(source))
1592 1595 if revs:
1593 1596 revs = [other.lookup(rev) for rev in revs]
1594 1597 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1595 1598 if not incoming:
1596 1599 try:
1597 1600 os.unlink(opts["bundle"])
1598 1601 except:
1599 1602 pass
1600 1603 ui.status(_("no changes found\n"))
1601 1604 return 1
1602 1605
1603 1606 cleanup = None
1604 1607 try:
1605 1608 fname = opts["bundle"]
1606 1609 if fname or not other.local():
1607 1610 # create a bundle (uncompressed if other repo is not local)
1608 1611 if revs is None:
1609 1612 cg = other.changegroup(incoming, "incoming")
1610 1613 else:
1611 1614 cg = other.changegroupsubset(incoming, revs, 'incoming')
1612 1615 bundletype = other.local() and "HG10BZ" or "HG10UN"
1613 1616 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1614 1617 # keep written bundle?
1615 1618 if opts["bundle"]:
1616 1619 cleanup = None
1617 1620 if not other.local():
1618 1621 # use the created uncompressed bundlerepo
1619 1622 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1620 1623
1621 1624 o = other.changelog.nodesbetween(incoming, revs)[0]
1622 1625 if opts['newest_first']:
1623 1626 o.reverse()
1624 1627 displayer = cmdutil.show_changeset(ui, other, opts)
1625 1628 count = 0
1626 1629 for n in o:
1627 1630 if count >= limit:
1628 1631 break
1629 1632 parents = [p for p in other.changelog.parents(n) if p != nullid]
1630 1633 if opts['no_merges'] and len(parents) == 2:
1631 1634 continue
1632 1635 count += 1
1633 1636 displayer.show(changenode=n)
1634 1637 finally:
1635 1638 if hasattr(other, 'close'):
1636 1639 other.close()
1637 1640 if cleanup:
1638 1641 os.unlink(cleanup)
1639 1642
1640 1643 def init(ui, dest=".", **opts):
1641 1644 """create a new repository in the given directory
1642 1645
1643 1646 Initialize a new repository in the given directory. If the given
1644 1647 directory does not exist, it is created.
1645 1648
1646 1649 If no directory is given, the current directory is used.
1647 1650
1648 1651 It is possible to specify an ssh:// URL as the destination.
1649 1652 Look at the help text for the pull command for important details
1650 1653 about ssh:// URLs.
1651 1654 """
1652 1655 cmdutil.setremoteconfig(ui, opts)
1653 1656 hg.repository(ui, dest, create=1)
1654 1657
1655 1658 def locate(ui, repo, *pats, **opts):
1656 1659 """locate files matching specific patterns
1657 1660
1658 1661 Print all files under Mercurial control whose names match the
1659 1662 given patterns.
1660 1663
1661 1664 This command searches the entire repository by default. To search
1662 1665 just the current directory and its subdirectories, use
1663 1666 "--include .".
1664 1667
1665 1668 If no patterns are given to match, this command prints all file
1666 1669 names.
1667 1670
1668 1671 If you want to feed the output of this command into the "xargs"
1669 1672 command, use the "-0" option to both this command and "xargs".
1670 1673 This will avoid the problem of "xargs" treating single filenames
1671 1674 that contain white space as multiple filenames.
1672 1675 """
1673 1676 end = opts['print0'] and '\0' or '\n'
1674 1677 rev = opts['rev']
1675 1678 if rev:
1676 1679 node = repo.lookup(rev)
1677 1680 else:
1678 1681 node = None
1679 1682
1680 1683 ret = 1
1681 1684 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1682 1685 badmatch=util.always,
1683 1686 default='relglob'):
1684 1687 if src == 'b':
1685 1688 continue
1686 1689 if not node and abs not in repo.dirstate:
1687 1690 continue
1688 1691 if opts['fullpath']:
1689 1692 ui.write(os.path.join(repo.root, abs), end)
1690 1693 else:
1691 1694 ui.write(((pats and rel) or abs), end)
1692 1695 ret = 0
1693 1696
1694 1697 return ret
1695 1698
1696 1699 def log(ui, repo, *pats, **opts):
1697 1700 """show revision history of entire repository or files
1698 1701
1699 1702 Print the revision history of the specified files or the entire
1700 1703 project.
1701 1704
1702 1705 File history is shown without following rename or copy history of
1703 1706 files. Use -f/--follow with a file name to follow history across
1704 1707 renames and copies. --follow without a file name will only show
1705 1708 ancestors or descendants of the starting revision. --follow-first
1706 1709 only follows the first parent of merge revisions.
1707 1710
1708 1711 If no revision range is specified, the default is tip:0 unless
1709 1712 --follow is set, in which case the working directory parent is
1710 1713 used as the starting revision.
1711 1714
1712 1715 See 'hg help dates' for a list of formats valid for -d/--date.
1713 1716
1714 1717 By default this command outputs: changeset id and hash, tags,
1715 1718 non-trivial parents, user, date and time, and a summary for each
1716 1719 commit. When the -v/--verbose switch is used, the list of changed
1717 1720 files and full commit message is shown.
1718 1721
1719 1722 NOTE: log -p may generate unexpected diff output for merge
1720 1723 changesets, as it will compare the merge changeset against its
1721 1724 first parent only. Also, the files: list will only reflect files
1722 1725 that are different from BOTH parents.
1723 1726
1724 1727 """
1725 1728
1726 1729 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1727 1730 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1728 1731
1729 1732 limit = cmdutil.loglimit(opts)
1730 1733 count = 0
1731 1734
1732 1735 if opts['copies'] and opts['rev']:
1733 1736 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1734 1737 else:
1735 1738 endrev = repo.changelog.count()
1736 1739 rcache = {}
1737 1740 ncache = {}
1738 1741 def getrenamed(fn, rev):
1739 1742 '''looks up all renames for a file (up to endrev) the first
1740 1743 time the file is given. It indexes on the changerev and only
1741 1744 parses the manifest if linkrev != changerev.
1742 1745 Returns rename info for fn at changerev rev.'''
1743 1746 if fn not in rcache:
1744 1747 rcache[fn] = {}
1745 1748 ncache[fn] = {}
1746 1749 fl = repo.file(fn)
1747 1750 for i in xrange(fl.count()):
1748 1751 node = fl.node(i)
1749 1752 lr = fl.linkrev(node)
1750 1753 renamed = fl.renamed(node)
1751 1754 rcache[fn][lr] = renamed
1752 1755 if renamed:
1753 1756 ncache[fn][node] = renamed
1754 1757 if lr >= endrev:
1755 1758 break
1756 1759 if rev in rcache[fn]:
1757 1760 return rcache[fn][rev]
1758 1761
1759 1762 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1760 1763 # filectx logic.
1761 1764
1762 1765 try:
1763 1766 return repo.changectx(rev).filectx(fn).renamed()
1764 1767 except revlog.LookupError:
1765 1768 pass
1766 1769 return None
1767 1770
1768 1771 df = False
1769 1772 if opts["date"]:
1770 1773 df = util.matchdate(opts["date"])
1771 1774
1772 1775 only_branches = opts['only_branch']
1773 1776
1774 1777 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1775 1778 for st, rev, fns in changeiter:
1776 1779 if st == 'add':
1777 1780 changenode = repo.changelog.node(rev)
1778 1781 parents = [p for p in repo.changelog.parentrevs(rev)
1779 1782 if p != nullrev]
1780 1783 if opts['no_merges'] and len(parents) == 2:
1781 1784 continue
1782 1785 if opts['only_merges'] and len(parents) != 2:
1783 1786 continue
1784 1787
1785 1788 if only_branches:
1786 1789 revbranch = get(rev)[5]['branch']
1787 1790 if revbranch not in only_branches:
1788 1791 continue
1789 1792
1790 1793 if df:
1791 1794 changes = get(rev)
1792 1795 if not df(changes[2][0]):
1793 1796 continue
1794 1797
1795 1798 if opts['keyword']:
1796 1799 changes = get(rev)
1797 1800 miss = 0
1798 1801 for k in [kw.lower() for kw in opts['keyword']]:
1799 1802 if not (k in changes[1].lower() or
1800 1803 k in changes[4].lower() or
1801 1804 k in " ".join(changes[3]).lower()):
1802 1805 miss = 1
1803 1806 break
1804 1807 if miss:
1805 1808 continue
1806 1809
1807 1810 copies = []
1808 1811 if opts.get('copies') and rev:
1809 1812 for fn in get(rev)[3]:
1810 1813 rename = getrenamed(fn, rev)
1811 1814 if rename:
1812 1815 copies.append((fn, rename[0]))
1813 1816 displayer.show(rev, changenode, copies=copies)
1814 1817 elif st == 'iter':
1815 1818 if count == limit: break
1816 1819 if displayer.flush(rev):
1817 1820 count += 1
1818 1821
1819 1822 def manifest(ui, repo, node=None, rev=None):
1820 1823 """output the current or given revision of the project manifest
1821 1824
1822 1825 Print a list of version controlled files for the given revision.
1823 1826 If no revision is given, the parent of the working directory is used,
1824 1827 or tip if no revision is checked out.
1825 1828
1826 1829 The manifest is the list of files being version controlled. If no revision
1827 1830 is given then the first parent of the working directory is used.
1828 1831
1829 1832 With -v flag, print file permissions, symlink and executable bits. With
1830 1833 --debug flag, print file revision hashes.
1831 1834 """
1832 1835
1833 1836 if rev and node:
1834 1837 raise util.Abort(_("please specify just one revision"))
1835 1838
1836 1839 if not node:
1837 1840 node = rev
1838 1841
1839 1842 m = repo.changectx(node).manifest()
1840 1843 files = m.keys()
1841 1844 files.sort()
1842 1845
1843 1846 for f in files:
1844 1847 if ui.debugflag:
1845 1848 ui.write("%40s " % hex(m[f]))
1846 1849 if ui.verbose:
1847 1850 type = m.execf(f) and "*" or m.linkf(f) and "@" or " "
1848 1851 perm = m.execf(f) and "755" or "644"
1849 1852 ui.write("%3s %1s " % (perm, type))
1850 1853 ui.write("%s\n" % f)
1851 1854
1852 1855 def merge(ui, repo, node=None, force=None, rev=None):
1853 1856 """merge working directory with another revision
1854 1857
1855 1858 Merge the contents of the current working directory and the
1856 1859 requested revision. Files that changed between either parent are
1857 1860 marked as changed for the next commit and a commit must be
1858 1861 performed before any further updates are allowed.
1859 1862
1860 1863 If no revision is specified, the working directory's parent is a
1861 1864 head revision, and the repository contains exactly one other head,
1862 1865 the other head is merged with by default. Otherwise, an explicit
1863 1866 revision to merge with must be provided.
1864 1867 """
1865 1868
1866 1869 if rev and node:
1867 1870 raise util.Abort(_("please specify just one revision"))
1868 1871 if not node:
1869 1872 node = rev
1870 1873
1871 1874 if not node:
1872 1875 heads = repo.heads()
1873 1876 if len(heads) > 2:
1874 1877 raise util.Abort(_('repo has %d heads - '
1875 1878 'please merge with an explicit rev') %
1876 1879 len(heads))
1877 1880 parent = repo.dirstate.parents()[0]
1878 1881 if len(heads) == 1:
1879 1882 msg = _('there is nothing to merge')
1880 1883 if parent != repo.lookup(repo.workingctx().branch()):
1881 1884 msg = _('%s - use "hg update" instead') % msg
1882 1885 raise util.Abort(msg)
1883 1886
1884 1887 if parent not in heads:
1885 1888 raise util.Abort(_('working dir not at a head rev - '
1886 1889 'use "hg update" or merge with an explicit rev'))
1887 1890 node = parent == heads[0] and heads[-1] or heads[0]
1888 1891 return hg.merge(repo, node, force=force)
1889 1892
1890 1893 def outgoing(ui, repo, dest=None, **opts):
1891 1894 """show changesets not found in destination
1892 1895
1893 1896 Show changesets not found in the specified destination repository or
1894 1897 the default push location. These are the changesets that would be pushed
1895 1898 if a push was requested.
1896 1899
1897 1900 See pull for valid destination format details.
1898 1901 """
1899 1902 limit = cmdutil.loglimit(opts)
1900 1903 dest, revs, checkout = hg.parseurl(
1901 1904 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1902 1905 cmdutil.setremoteconfig(ui, opts)
1903 1906 if revs:
1904 1907 revs = [repo.lookup(rev) for rev in revs]
1905 1908
1906 1909 other = hg.repository(ui, dest)
1907 1910 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
1908 1911 o = repo.findoutgoing(other, force=opts['force'])
1909 1912 if not o:
1910 1913 ui.status(_("no changes found\n"))
1911 1914 return 1
1912 1915 o = repo.changelog.nodesbetween(o, revs)[0]
1913 1916 if opts['newest_first']:
1914 1917 o.reverse()
1915 1918 displayer = cmdutil.show_changeset(ui, repo, opts)
1916 1919 count = 0
1917 1920 for n in o:
1918 1921 if count >= limit:
1919 1922 break
1920 1923 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1921 1924 if opts['no_merges'] and len(parents) == 2:
1922 1925 continue
1923 1926 count += 1
1924 1927 displayer.show(changenode=n)
1925 1928
1926 1929 def parents(ui, repo, file_=None, **opts):
1927 1930 """show the parents of the working dir or revision
1928 1931
1929 1932 Print the working directory's parent revisions. If a
1930 1933 revision is given via --rev, the parent of that revision
1931 1934 will be printed. If a file argument is given, revision in
1932 1935 which the file was last changed (before the working directory
1933 1936 revision or the argument to --rev if given) is printed.
1934 1937 """
1935 1938 rev = opts.get('rev')
1936 1939 if rev:
1937 1940 ctx = repo.changectx(rev)
1938 1941 else:
1939 1942 ctx = repo.workingctx()
1940 1943
1941 1944 if file_:
1942 1945 files, match, anypats = cmdutil.matchpats(repo, (file_,), opts)
1943 1946 if anypats or len(files) != 1:
1944 1947 raise util.Abort(_('can only specify an explicit file name'))
1945 1948 file_ = files[0]
1946 1949 filenodes = []
1947 1950 for cp in ctx.parents():
1948 1951 if not cp:
1949 1952 continue
1950 1953 try:
1951 1954 filenodes.append(cp.filenode(file_))
1952 1955 except revlog.LookupError:
1953 1956 pass
1954 1957 if not filenodes:
1955 1958 raise util.Abort(_("'%s' not found in manifest!") % file_)
1956 1959 fl = repo.file(file_)
1957 1960 p = [repo.lookup(fl.linkrev(fn)) for fn in filenodes]
1958 1961 else:
1959 1962 p = [cp.node() for cp in ctx.parents()]
1960 1963
1961 1964 displayer = cmdutil.show_changeset(ui, repo, opts)
1962 1965 for n in p:
1963 1966 if n != nullid:
1964 1967 displayer.show(changenode=n)
1965 1968
1966 1969 def paths(ui, repo, search=None):
1967 1970 """show definition of symbolic path names
1968 1971
1969 1972 Show definition of symbolic path name NAME. If no name is given, show
1970 1973 definition of available names.
1971 1974
1972 1975 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1973 1976 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1974 1977 """
1975 1978 if search:
1976 1979 for name, path in ui.configitems("paths"):
1977 1980 if name == search:
1978 1981 ui.write("%s\n" % util.hidepassword(path))
1979 1982 return
1980 1983 ui.warn(_("not found!\n"))
1981 1984 return 1
1982 1985 else:
1983 1986 for name, path in ui.configitems("paths"):
1984 1987 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
1985 1988
1986 1989 def postincoming(ui, repo, modheads, optupdate, checkout):
1987 1990 if modheads == 0:
1988 1991 return
1989 1992 if optupdate:
1990 1993 if modheads <= 1 or checkout:
1991 1994 return hg.update(repo, checkout)
1992 1995 else:
1993 1996 ui.status(_("not updating, since new heads added\n"))
1994 1997 if modheads > 1:
1995 1998 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1996 1999 else:
1997 2000 ui.status(_("(run 'hg update' to get a working copy)\n"))
1998 2001
1999 2002 def pull(ui, repo, source="default", **opts):
2000 2003 """pull changes from the specified source
2001 2004
2002 2005 Pull changes from a remote repository to a local one.
2003 2006
2004 2007 This finds all changes from the repository at the specified path
2005 2008 or URL and adds them to the local repository. By default, this
2006 2009 does not update the copy of the project in the working directory.
2007 2010
2008 2011 Valid URLs are of the form:
2009 2012
2010 2013 local/filesystem/path (or file://local/filesystem/path)
2011 2014 http://[user@]host[:port]/[path]
2012 2015 https://[user@]host[:port]/[path]
2013 2016 ssh://[user@]host[:port]/[path]
2014 2017 static-http://host[:port]/[path]
2015 2018
2016 2019 Paths in the local filesystem can either point to Mercurial
2017 2020 repositories or to bundle files (as created by 'hg bundle' or
2018 2021 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2019 2022 allows access to a Mercurial repository where you simply use a web
2020 2023 server to publish the .hg directory as static content.
2021 2024
2022 2025 An optional identifier after # indicates a particular branch, tag,
2023 2026 or changeset to pull.
2024 2027
2025 2028 Some notes about using SSH with Mercurial:
2026 2029 - SSH requires an accessible shell account on the destination machine
2027 2030 and a copy of hg in the remote path or specified with as remotecmd.
2028 2031 - path is relative to the remote user's home directory by default.
2029 2032 Use an extra slash at the start of a path to specify an absolute path:
2030 2033 ssh://example.com//tmp/repository
2031 2034 - Mercurial doesn't use its own compression via SSH; the right thing
2032 2035 to do is to configure it in your ~/.ssh/config, e.g.:
2033 2036 Host *.mylocalnetwork.example.com
2034 2037 Compression no
2035 2038 Host *
2036 2039 Compression yes
2037 2040 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2038 2041 with the --ssh command line option.
2039 2042 """
2040 2043 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
2041 2044 cmdutil.setremoteconfig(ui, opts)
2042 2045
2043 2046 other = hg.repository(ui, source)
2044 2047 ui.status(_('pulling from %s\n') % util.hidepassword(source))
2045 2048 if revs:
2046 2049 try:
2047 2050 revs = [other.lookup(rev) for rev in revs]
2048 2051 except NoCapability:
2049 2052 error = _("Other repository doesn't support revision lookup, "
2050 2053 "so a rev cannot be specified.")
2051 2054 raise util.Abort(error)
2052 2055
2053 2056 modheads = repo.pull(other, heads=revs, force=opts['force'])
2054 2057 return postincoming(ui, repo, modheads, opts['update'], checkout)
2055 2058
2056 2059 def push(ui, repo, dest=None, **opts):
2057 2060 """push changes to the specified destination
2058 2061
2059 2062 Push changes from the local repository to the given destination.
2060 2063
2061 2064 This is the symmetrical operation for pull. It helps to move
2062 2065 changes from the current repository to a different one. If the
2063 2066 destination is local this is identical to a pull in that directory
2064 2067 from the current one.
2065 2068
2066 2069 By default, push will refuse to run if it detects the result would
2067 2070 increase the number of remote heads. This generally indicates the
2068 2071 the client has forgotten to sync and merge before pushing.
2069 2072
2070 2073 Valid URLs are of the form:
2071 2074
2072 2075 local/filesystem/path (or file://local/filesystem/path)
2073 2076 ssh://[user@]host[:port]/[path]
2074 2077 http://[user@]host[:port]/[path]
2075 2078 https://[user@]host[:port]/[path]
2076 2079
2077 2080 An optional identifier after # indicates a particular branch, tag,
2078 2081 or changeset to push.
2079 2082
2080 2083 Look at the help text for the pull command for important details
2081 2084 about ssh:// URLs.
2082 2085
2083 2086 Pushing to http:// and https:// URLs is only possible, if this
2084 2087 feature is explicitly enabled on the remote Mercurial server.
2085 2088 """
2086 2089 dest, revs, checkout = hg.parseurl(
2087 2090 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2088 2091 cmdutil.setremoteconfig(ui, opts)
2089 2092
2090 2093 other = hg.repository(ui, dest)
2091 2094 ui.status('pushing to %s\n' % util.hidepassword(dest))
2092 2095 if revs:
2093 2096 revs = [repo.lookup(rev) for rev in revs]
2094 2097 r = repo.push(other, opts['force'], revs=revs)
2095 2098 return r == 0
2096 2099
2097 2100 def rawcommit(ui, repo, *pats, **opts):
2098 2101 """raw commit interface (DEPRECATED)
2099 2102
2100 2103 (DEPRECATED)
2101 2104 Lowlevel commit, for use in helper scripts.
2102 2105
2103 2106 This command is not intended to be used by normal users, as it is
2104 2107 primarily useful for importing from other SCMs.
2105 2108
2106 2109 This command is now deprecated and will be removed in a future
2107 2110 release, please use debugsetparents and commit instead.
2108 2111 """
2109 2112
2110 2113 ui.warn(_("(the rawcommit command is deprecated)\n"))
2111 2114
2112 2115 message = cmdutil.logmessage(opts)
2113 2116
2114 2117 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2115 2118 if opts['files']:
2116 2119 files += open(opts['files']).read().splitlines()
2117 2120
2118 2121 parents = [repo.lookup(p) for p in opts['parent']]
2119 2122
2120 2123 try:
2121 2124 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2122 2125 except ValueError, inst:
2123 2126 raise util.Abort(str(inst))
2124 2127
2125 2128 def recover(ui, repo):
2126 2129 """roll back an interrupted transaction
2127 2130
2128 2131 Recover from an interrupted commit or pull.
2129 2132
2130 2133 This command tries to fix the repository status after an interrupted
2131 2134 operation. It should only be necessary when Mercurial suggests it.
2132 2135 """
2133 2136 if repo.recover():
2134 2137 return hg.verify(repo)
2135 2138 return 1
2136 2139
2137 2140 def remove(ui, repo, *pats, **opts):
2138 2141 """remove the specified files on the next commit
2139 2142
2140 2143 Schedule the indicated files for removal from the repository.
2141 2144
2142 2145 This only removes files from the current branch, not from the entire
2143 2146 project history. -A can be used to remove only files that have already
2144 2147 been deleted, -f can be used to force deletion, and -Af can be used
2145 2148 to remove files from the next revision without deleting them.
2146 2149
2147 2150 The following table details the behavior of remove for different file
2148 2151 states (columns) and option combinations (rows). The file states are
2149 2152 Added, Clean, Modified and Missing (as reported by hg status). The
2150 2153 actions are Warn, Remove (from branch) and Delete (from disk).
2151 2154
2152 2155 A C M !
2153 2156 none W RD W R
2154 2157 -f R RD RD R
2155 2158 -A W W W R
2156 2159 -Af R R R R
2157 2160
2158 2161 This command schedules the files to be removed at the next commit.
2159 2162 To undo a remove before that, see hg revert.
2160 2163 """
2161 2164
2162 2165 after, force = opts.get('after'), opts.get('force')
2163 2166 if not pats and not after:
2164 2167 raise util.Abort(_('no files specified'))
2165 2168
2166 2169 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2167 2170 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2168 2171 modified, added, removed, deleted, unknown = mardu
2169 2172
2170 2173 remove, forget = [], []
2171 2174 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2172 2175
2173 2176 reason = None
2174 2177 if abs in removed or abs in unknown:
2175 2178 continue
2176 2179
2177 2180 # last column
2178 2181 elif abs in deleted:
2179 2182 remove.append(abs)
2180 2183
2181 2184 # rest of the third row
2182 2185 elif after and not force:
2183 2186 reason = _('still exists (use -f to force removal)')
2184 2187
2185 2188 # rest of the first column
2186 2189 elif abs in added:
2187 2190 if not force:
2188 2191 reason = _('has been marked for add (use -f to force removal)')
2189 2192 else:
2190 2193 forget.append(abs)
2191 2194
2192 2195 # rest of the third column
2193 2196 elif abs in modified:
2194 2197 if not force:
2195 2198 reason = _('is modified (use -f to force removal)')
2196 2199 else:
2197 2200 remove.append(abs)
2198 2201
2199 2202 # rest of the second column
2200 2203 elif not reason:
2201 2204 remove.append(abs)
2202 2205
2203 2206 if reason:
2204 2207 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2205 2208 elif ui.verbose or not exact:
2206 2209 ui.status(_('removing %s\n') % rel)
2207 2210
2208 2211 repo.forget(forget)
2209 2212 repo.remove(remove, unlink=not after)
2210 2213
2211 2214 def rename(ui, repo, *pats, **opts):
2212 2215 """rename files; equivalent of copy + remove
2213 2216
2214 2217 Mark dest as copies of sources; mark sources for deletion. If
2215 2218 dest is a directory, copies are put in that directory. If dest is
2216 2219 a file, there can only be one source.
2217 2220
2218 2221 By default, this command copies the contents of files as they
2219 2222 stand in the working directory. If invoked with --after, the
2220 2223 operation is recorded, but no copying is performed.
2221 2224
2222 2225 This command takes effect in the next commit. To undo a rename
2223 2226 before that, see hg revert.
2224 2227 """
2225 2228 wlock = repo.wlock(False)
2226 2229 try:
2227 2230 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2228 2231 finally:
2229 2232 del wlock
2230 2233
2231 2234 def revert(ui, repo, *pats, **opts):
2232 2235 """restore individual files or dirs to an earlier state
2233 2236
2234 2237 (use update -r to check out earlier revisions, revert does not
2235 2238 change the working dir parents)
2236 2239
2237 2240 With no revision specified, revert the named files or directories
2238 2241 to the contents they had in the parent of the working directory.
2239 2242 This restores the contents of the affected files to an unmodified
2240 2243 state and unschedules adds, removes, copies, and renames. If the
2241 2244 working directory has two parents, you must explicitly specify the
2242 2245 revision to revert to.
2243 2246
2244 2247 Using the -r option, revert the given files or directories to their
2245 2248 contents as of a specific revision. This can be helpful to "roll
2246 2249 back" some or all of an earlier change.
2247 2250 See 'hg help dates' for a list of formats valid for -d/--date.
2248 2251
2249 2252 Revert modifies the working directory. It does not commit any
2250 2253 changes, or change the parent of the working directory. If you
2251 2254 revert to a revision other than the parent of the working
2252 2255 directory, the reverted files will thus appear modified
2253 2256 afterwards.
2254 2257
2255 2258 If a file has been deleted, it is restored. If the executable
2256 2259 mode of a file was changed, it is reset.
2257 2260
2258 2261 If names are given, all files matching the names are reverted.
2259 2262 If no arguments are given, no files are reverted.
2260 2263
2261 2264 Modified files are saved with a .orig suffix before reverting.
2262 2265 To disable these backups, use --no-backup.
2263 2266 """
2264 2267
2265 2268 if opts["date"]:
2266 2269 if opts["rev"]:
2267 2270 raise util.Abort(_("you can't specify a revision and a date"))
2268 2271 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2269 2272
2270 2273 if not pats and not opts['all']:
2271 2274 raise util.Abort(_('no files or directories specified; '
2272 2275 'use --all to revert the whole repo'))
2273 2276
2274 2277 parent, p2 = repo.dirstate.parents()
2275 2278 if not opts['rev'] and p2 != nullid:
2276 2279 raise util.Abort(_('uncommitted merge - please provide a '
2277 2280 'specific revision'))
2278 2281 ctx = repo.changectx(opts['rev'])
2279 2282 node = ctx.node()
2280 2283 mf = ctx.manifest()
2281 2284 if node == parent:
2282 2285 pmf = mf
2283 2286 else:
2284 2287 pmf = None
2285 2288
2286 2289 # need all matching names in dirstate and manifest of target rev,
2287 2290 # so have to walk both. do not print errors if files exist in one
2288 2291 # but not other.
2289 2292
2290 2293 names = {}
2291 2294
2292 2295 wlock = repo.wlock()
2293 2296 try:
2294 2297 # walk dirstate.
2295 2298 files = []
2296 2299 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2297 2300 badmatch=mf.has_key):
2298 2301 names[abs] = (rel, exact)
2299 2302 if src != 'b':
2300 2303 files.append(abs)
2301 2304
2302 2305 # walk target manifest.
2303 2306
2304 2307 def badmatch(path):
2305 2308 if path in names:
2306 2309 return True
2307 2310 path_ = path + '/'
2308 2311 for f in names:
2309 2312 if f.startswith(path_):
2310 2313 return True
2311 2314 return False
2312 2315
2313 2316 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2314 2317 badmatch=badmatch):
2315 2318 if abs in names or src == 'b':
2316 2319 continue
2317 2320 names[abs] = (rel, exact)
2318 2321
2319 2322 changes = repo.status(files=files, match=names.has_key)[:4]
2320 2323 modified, added, removed, deleted = map(dict.fromkeys, changes)
2321 2324
2322 2325 # if f is a rename, also revert the source
2323 2326 cwd = repo.getcwd()
2324 2327 for f in added:
2325 2328 src = repo.dirstate.copied(f)
2326 2329 if src and src not in names and repo.dirstate[src] == 'r':
2327 2330 removed[src] = None
2328 2331 names[src] = (repo.pathto(src, cwd), True)
2329 2332
2330 2333 def removeforget(abs):
2331 2334 if repo.dirstate[abs] == 'a':
2332 2335 return _('forgetting %s\n')
2333 2336 return _('removing %s\n')
2334 2337
2335 2338 revert = ([], _('reverting %s\n'))
2336 2339 add = ([], _('adding %s\n'))
2337 2340 remove = ([], removeforget)
2338 2341 undelete = ([], _('undeleting %s\n'))
2339 2342
2340 2343 disptable = (
2341 2344 # dispatch table:
2342 2345 # file state
2343 2346 # action if in target manifest
2344 2347 # action if not in target manifest
2345 2348 # make backup if in target manifest
2346 2349 # make backup if not in target manifest
2347 2350 (modified, revert, remove, True, True),
2348 2351 (added, revert, remove, True, False),
2349 2352 (removed, undelete, None, False, False),
2350 2353 (deleted, revert, remove, False, False),
2351 2354 )
2352 2355
2353 2356 entries = names.items()
2354 2357 entries.sort()
2355 2358
2356 2359 for abs, (rel, exact) in entries:
2357 2360 mfentry = mf.get(abs)
2358 2361 target = repo.wjoin(abs)
2359 2362 def handle(xlist, dobackup):
2360 2363 xlist[0].append(abs)
2361 2364 if dobackup and not opts['no_backup'] and util.lexists(target):
2362 2365 bakname = "%s.orig" % rel
2363 2366 ui.note(_('saving current version of %s as %s\n') %
2364 2367 (rel, bakname))
2365 2368 if not opts.get('dry_run'):
2366 2369 util.copyfile(target, bakname)
2367 2370 if ui.verbose or not exact:
2368 2371 msg = xlist[1]
2369 2372 if not isinstance(msg, basestring):
2370 2373 msg = msg(abs)
2371 2374 ui.status(msg % rel)
2372 2375 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2373 2376 if abs not in table: continue
2374 2377 # file has changed in dirstate
2375 2378 if mfentry:
2376 2379 handle(hitlist, backuphit)
2377 2380 elif misslist is not None:
2378 2381 handle(misslist, backupmiss)
2379 2382 break
2380 2383 else:
2381 2384 if abs not in repo.dirstate:
2382 2385 if mfentry:
2383 2386 handle(add, True)
2384 2387 elif exact:
2385 2388 ui.warn(_('file not managed: %s\n') % rel)
2386 2389 continue
2387 2390 # file has not changed in dirstate
2388 2391 if node == parent:
2389 2392 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2390 2393 continue
2391 2394 if pmf is None:
2392 2395 # only need parent manifest in this unlikely case,
2393 2396 # so do not read by default
2394 2397 pmf = repo.changectx(parent).manifest()
2395 2398 if abs in pmf:
2396 2399 if mfentry:
2397 2400 # if version of file is same in parent and target
2398 2401 # manifests, do nothing
2399 2402 if (pmf[abs] != mfentry or
2400 2403 pmf.flags(abs) != mf.flags(abs)):
2401 2404 handle(revert, False)
2402 2405 else:
2403 2406 handle(remove, False)
2404 2407
2405 2408 if not opts.get('dry_run'):
2406 2409 def checkout(f):
2407 2410 fc = ctx[f]
2408 2411 repo.wwrite(f, fc.data(), fc.fileflags())
2409 2412
2410 2413 audit_path = util.path_auditor(repo.root)
2411 2414 for f in remove[0]:
2412 2415 if repo.dirstate[f] == 'a':
2413 2416 repo.dirstate.forget(f)
2414 2417 continue
2415 2418 audit_path(f)
2416 2419 try:
2417 2420 util.unlink(repo.wjoin(f))
2418 2421 except OSError:
2419 2422 pass
2420 2423 repo.dirstate.remove(f)
2421 2424
2422 2425 normal = None
2423 2426 if node == parent:
2424 2427 # We're reverting to our parent. If possible, we'd like status
2425 2428 # to report the file as clean. We have to use normallookup for
2426 2429 # merges to avoid losing information about merged/dirty files.
2427 2430 if p2 != nullid:
2428 2431 normal = repo.dirstate.normallookup
2429 2432 else:
2430 2433 normal = repo.dirstate.normal
2431 2434 for f in revert[0]:
2432 2435 checkout(f)
2433 2436 if normal:
2434 2437 normal(f)
2435 2438
2436 2439 for f in add[0]:
2437 2440 checkout(f)
2438 2441 repo.dirstate.add(f)
2439 2442
2440 2443 normal = repo.dirstate.normallookup
2441 2444 if node == parent and p2 == nullid:
2442 2445 normal = repo.dirstate.normal
2443 2446 for f in undelete[0]:
2444 2447 checkout(f)
2445 2448 normal(f)
2446 2449
2447 2450 finally:
2448 2451 del wlock
2449 2452
2450 2453 def rollback(ui, repo):
2451 2454 """roll back the last transaction
2452 2455
2453 2456 This command should be used with care. There is only one level of
2454 2457 rollback, and there is no way to undo a rollback. It will also
2455 2458 restore the dirstate at the time of the last transaction, losing
2456 2459 any dirstate changes since that time.
2457 2460
2458 2461 Transactions are used to encapsulate the effects of all commands
2459 2462 that create new changesets or propagate existing changesets into a
2460 2463 repository. For example, the following commands are transactional,
2461 2464 and their effects can be rolled back:
2462 2465
2463 2466 commit
2464 2467 import
2465 2468 pull
2466 2469 push (with this repository as destination)
2467 2470 unbundle
2468 2471
2469 2472 This command is not intended for use on public repositories. Once
2470 2473 changes are visible for pull by other users, rolling a transaction
2471 2474 back locally is ineffective (someone else may already have pulled
2472 2475 the changes). Furthermore, a race is possible with readers of the
2473 2476 repository; for example an in-progress pull from the repository
2474 2477 may fail if a rollback is performed.
2475 2478 """
2476 2479 repo.rollback()
2477 2480
2478 2481 def root(ui, repo):
2479 2482 """print the root (top) of the current working dir
2480 2483
2481 2484 Print the root directory of the current repository.
2482 2485 """
2483 2486 ui.write(repo.root + "\n")
2484 2487
2485 2488 def serve(ui, repo, **opts):
2486 2489 """export the repository via HTTP
2487 2490
2488 2491 Start a local HTTP repository browser and pull server.
2489 2492
2490 2493 By default, the server logs accesses to stdout and errors to
2491 2494 stderr. Use the "-A" and "-E" options to log to files.
2492 2495 """
2493 2496
2494 2497 if opts["stdio"]:
2495 2498 if repo is None:
2496 2499 raise RepoError(_("There is no Mercurial repository here"
2497 2500 " (.hg not found)"))
2498 2501 s = sshserver.sshserver(ui, repo)
2499 2502 s.serve_forever()
2500 2503
2501 2504 parentui = ui.parentui or ui
2502 2505 optlist = ("name templates style address port prefix ipv6"
2503 2506 " accesslog errorlog webdir_conf certificate")
2504 2507 for o in optlist.split():
2505 2508 if opts[o]:
2506 2509 parentui.setconfig("web", o, str(opts[o]))
2507 2510 if (repo is not None) and (repo.ui != parentui):
2508 2511 repo.ui.setconfig("web", o, str(opts[o]))
2509 2512
2510 2513 if repo is None and not ui.config("web", "webdir_conf"):
2511 2514 raise RepoError(_("There is no Mercurial repository here"
2512 2515 " (.hg not found)"))
2513 2516
2514 2517 class service:
2515 2518 def init(self):
2516 2519 util.set_signal_handler()
2517 2520 self.httpd = hgweb.server.create_server(parentui, repo)
2518 2521
2519 2522 if not ui.verbose: return
2520 2523
2521 2524 if self.httpd.prefix:
2522 2525 prefix = self.httpd.prefix.strip('/') + '/'
2523 2526 else:
2524 2527 prefix = ''
2525 2528
2526 2529 port = ':%d' % self.httpd.port
2527 2530 if port == ':80':
2528 2531 port = ''
2529 2532
2530 2533 ui.status(_('listening at http://%s%s/%s (%s:%d)\n') %
2531 2534 (self.httpd.fqaddr, port, prefix, self.httpd.addr, self.httpd.port))
2532 2535
2533 2536 def run(self):
2534 2537 self.httpd.serve_forever()
2535 2538
2536 2539 service = service()
2537 2540
2538 2541 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2539 2542
2540 2543 def status(ui, repo, *pats, **opts):
2541 2544 """show changed files in the working directory
2542 2545
2543 2546 Show status of files in the repository. If names are given, only
2544 2547 files that match are shown. Files that are clean or ignored or
2545 2548 source of a copy/move operation, are not listed unless -c (clean),
2546 2549 -i (ignored), -C (copies) or -A is given. Unless options described
2547 2550 with "show only ..." are given, the options -mardu are used.
2548 2551
2549 2552 Option -q/--quiet hides untracked (unknown and ignored) files
2550 2553 unless explicitly requested with -u/--unknown or -i/-ignored.
2551 2554
2552 2555 NOTE: status may appear to disagree with diff if permissions have
2553 2556 changed or a merge has occurred. The standard diff format does not
2554 2557 report permission changes and diff only reports changes relative
2555 2558 to one merge parent.
2556 2559
2557 2560 If one revision is given, it is used as the base revision.
2558 2561 If two revisions are given, the difference between them is shown.
2559 2562
2560 2563 The codes used to show the status of files are:
2561 2564 M = modified
2562 2565 A = added
2563 2566 R = removed
2564 2567 C = clean
2565 2568 ! = deleted, but still tracked
2566 2569 ? = not tracked
2567 2570 I = ignored
2568 2571 = the previous added file was copied from here
2569 2572 """
2570 2573
2571 2574 all = opts['all']
2572 2575 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2573 2576
2574 2577 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2575 2578 cwd = (pats and repo.getcwd()) or ''
2576 2579 modified, added, removed, deleted, unknown, ignored, clean = [
2577 2580 n for n in repo.status(node1=node1, node2=node2, files=files,
2578 2581 match=matchfn,
2579 2582 list_ignored=opts['ignored']
2580 2583 or all and not ui.quiet,
2581 2584 list_clean=opts['clean'] or all,
2582 2585 list_unknown=opts['unknown']
2583 2586 or not (ui.quiet or
2584 2587 opts['modified'] or
2585 2588 opts['added'] or
2586 2589 opts['removed'] or
2587 2590 opts['deleted'] or
2588 2591 opts['ignored']))]
2589 2592
2590 2593 changetypes = (('modified', 'M', modified),
2591 2594 ('added', 'A', added),
2592 2595 ('removed', 'R', removed),
2593 2596 ('deleted', '!', deleted),
2594 2597 ('unknown', '?', unknown),
2595 2598 ('ignored', 'I', ignored))
2596 2599
2597 2600 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2598 2601
2599 2602 copy = {}
2600 2603 showcopy = {}
2601 2604 if ((all or opts.get('copies')) and not opts.get('no_status')):
2602 2605 if opts.get('rev') == []:
2603 2606 # fast path, more correct with merge parents
2604 2607 showcopy = copy = repo.dirstate.copies().copy()
2605 2608 else:
2606 2609 ctxn = repo.changectx(nullid)
2607 2610 ctx1 = repo.changectx(node1)
2608 2611 ctx2 = repo.changectx(node2)
2609 2612 if node2 is None:
2610 2613 ctx2 = repo.workingctx()
2611 2614 copy, diverge = copies.copies(repo, ctx1, ctx2, ctxn)
2612 2615 for k, v in copy.items():
2613 2616 copy[v] = k
2614 2617
2615 2618 end = opts['print0'] and '\0' or '\n'
2616 2619
2617 2620 for opt, char, changes in ([ct for ct in explicit_changetypes
2618 2621 if all or opts[ct[0]]]
2619 2622 or changetypes):
2620 2623
2621 2624 if opts['no_status']:
2622 2625 format = "%%s%s" % end
2623 2626 else:
2624 2627 format = "%s %%s%s" % (char, end)
2625 2628
2626 2629 for f in changes:
2627 2630 ui.write(format % repo.pathto(f, cwd))
2628 2631 if f in copy and (f in added or f in showcopy):
2629 2632 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2630 2633
2631 2634 def tag(ui, repo, name1, *names, **opts):
2632 2635 """add one or more tags for the current or given revision
2633 2636
2634 2637 Name a particular revision using <name>.
2635 2638
2636 2639 Tags are used to name particular revisions of the repository and are
2637 2640 very useful to compare different revisions, to go back to significant
2638 2641 earlier versions or to mark branch points as releases, etc.
2639 2642
2640 2643 If no revision is given, the parent of the working directory is used,
2641 2644 or tip if no revision is checked out.
2642 2645
2643 2646 To facilitate version control, distribution, and merging of tags,
2644 2647 they are stored as a file named ".hgtags" which is managed
2645 2648 similarly to other project files and can be hand-edited if
2646 2649 necessary. The file '.hg/localtags' is used for local tags (not
2647 2650 shared among repositories).
2648 2651
2649 2652 See 'hg help dates' for a list of formats valid for -d/--date.
2650 2653 """
2651 2654
2652 2655 rev_ = None
2653 2656 names = (name1,) + names
2654 2657 if len(names) != len(dict.fromkeys(names)):
2655 2658 raise util.Abort(_('tag names must be unique'))
2656 2659 for n in names:
2657 2660 if n in ['tip', '.', 'null']:
2658 2661 raise util.Abort(_('the name \'%s\' is reserved') % n)
2659 2662 if opts['rev'] and opts['remove']:
2660 2663 raise util.Abort(_("--rev and --remove are incompatible"))
2661 2664 if opts['rev']:
2662 2665 rev_ = opts['rev']
2663 2666 message = opts['message']
2664 2667 if opts['remove']:
2665 2668 expectedtype = opts['local'] and 'local' or 'global'
2666 2669 for n in names:
2667 2670 if not repo.tagtype(n):
2668 2671 raise util.Abort(_('tag \'%s\' does not exist') % n)
2669 2672 if repo.tagtype(n) != expectedtype:
2670 2673 raise util.Abort(_('tag \'%s\' is not a %s tag') %
2671 2674 (n, expectedtype))
2672 2675 rev_ = nullid
2673 2676 if not message:
2674 2677 message = _('Removed tag %s') % ', '.join(names)
2675 2678 elif not opts['force']:
2676 2679 for n in names:
2677 2680 if n in repo.tags():
2678 2681 raise util.Abort(_('tag \'%s\' already exists '
2679 2682 '(use -f to force)') % n)
2680 2683 if not rev_ and repo.dirstate.parents()[1] != nullid:
2681 2684 raise util.Abort(_('uncommitted merge - please provide a '
2682 2685 'specific revision'))
2683 2686 r = repo.changectx(rev_).node()
2684 2687
2685 2688 if not message:
2686 2689 message = (_('Added tag %s for changeset %s') %
2687 2690 (', '.join(names), short(r)))
2688 2691
2689 2692 date = opts.get('date')
2690 2693 if date:
2691 2694 date = util.parsedate(date)
2692 2695
2693 2696 repo.tag(names, r, message, opts['local'], opts['user'], date)
2694 2697
2695 2698 def tags(ui, repo):
2696 2699 """list repository tags
2697 2700
2698 2701 List the repository tags.
2699 2702
2700 2703 This lists both regular and local tags. When the -v/--verbose switch
2701 2704 is used, a third column "local" is printed for local tags.
2702 2705 """
2703 2706
2704 2707 l = repo.tagslist()
2705 2708 l.reverse()
2706 2709 hexfunc = ui.debugflag and hex or short
2707 2710 tagtype = ""
2708 2711
2709 2712 for t, n in l:
2710 2713 if ui.quiet:
2711 2714 ui.write("%s\n" % t)
2712 2715 continue
2713 2716
2714 2717 try:
2715 2718 hn = hexfunc(n)
2716 2719 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2717 2720 except revlog.LookupError:
2718 2721 r = " ?:%s" % hn
2719 2722 else:
2720 2723 spaces = " " * (30 - util.locallen(t))
2721 2724 if ui.verbose:
2722 2725 if repo.tagtype(t) == 'local':
2723 2726 tagtype = " local"
2724 2727 else:
2725 2728 tagtype = ""
2726 2729 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2727 2730
2728 2731 def tip(ui, repo, **opts):
2729 2732 """show the tip revision
2730 2733
2731 2734 The tip revision (usually just called the tip) is the most
2732 2735 recently added changeset in the repository, the most recently
2733 2736 changed head.
2734 2737
2735 2738 If you have just made a commit, that commit will be the tip. If
2736 2739 you have just pulled changes from another repository, the tip of
2737 2740 that repository becomes the current tip. The "tip" tag is special
2738 2741 and cannot be renamed or assigned to a different changeset.
2739 2742 """
2740 2743 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2741 2744
2742 2745 def unbundle(ui, repo, fname1, *fnames, **opts):
2743 2746 """apply one or more changegroup files
2744 2747
2745 2748 Apply one or more compressed changegroup files generated by the
2746 2749 bundle command.
2747 2750 """
2748 2751 fnames = (fname1,) + fnames
2749 2752
2750 2753 lock = None
2751 2754 try:
2752 2755 lock = repo.lock()
2753 2756 for fname in fnames:
2754 2757 if os.path.exists(fname):
2755 2758 f = open(fname, "rb")
2756 2759 else:
2757 2760 f = urllib.urlopen(fname)
2758 2761 gen = changegroup.readbundle(f, fname)
2759 2762 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2760 2763 finally:
2761 2764 del lock
2762 2765
2763 2766 return postincoming(ui, repo, modheads, opts['update'], None)
2764 2767
2765 2768 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2766 2769 """update working directory
2767 2770
2768 2771 Update the working directory to the specified revision, or the
2769 2772 tip of the current branch if none is specified.
2770 2773
2771 2774 If the requested revision is a descendant of the working
2772 2775 directory, any outstanding changes in the working directory will
2773 2776 be merged into the result. If it is not directly descended but is
2774 2777 on the same named branch, update aborts with a suggestion to use
2775 2778 merge or update -C instead.
2776 2779
2777 2780 If the requested revision is on a different named branch and the
2778 2781 working directory is clean, update quietly switches branches.
2779 2782
2780 2783 See 'hg help dates' for a list of formats valid for --date.
2781 2784 """
2782 2785 if rev and node:
2783 2786 raise util.Abort(_("please specify just one revision"))
2784 2787
2785 2788 if not rev:
2786 2789 rev = node
2787 2790
2788 2791 if date:
2789 2792 if rev:
2790 2793 raise util.Abort(_("you can't specify a revision and a date"))
2791 2794 rev = cmdutil.finddate(ui, repo, date)
2792 2795
2793 2796 if clean:
2794 2797 return hg.clean(repo, rev)
2795 2798 else:
2796 2799 return hg.update(repo, rev)
2797 2800
2798 2801 def verify(ui, repo):
2799 2802 """verify the integrity of the repository
2800 2803
2801 2804 Verify the integrity of the current repository.
2802 2805
2803 2806 This will perform an extensive check of the repository's
2804 2807 integrity, validating the hashes and checksums of each entry in
2805 2808 the changelog, manifest, and tracked files, as well as the
2806 2809 integrity of their crosslinks and indices.
2807 2810 """
2808 2811 return hg.verify(repo)
2809 2812
2810 2813 def version_(ui):
2811 2814 """output version and copyright information"""
2812 2815 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2813 2816 % version.get_version())
2814 2817 ui.status(_(
2815 2818 "\nCopyright (C) 2005-2008 Matt Mackall <mpm@selenic.com> and others\n"
2816 2819 "This is free software; see the source for copying conditions. "
2817 2820 "There is NO\nwarranty; "
2818 2821 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2819 2822 ))
2820 2823
2821 2824 # Command options and aliases are listed here, alphabetically
2822 2825
2823 2826 globalopts = [
2824 2827 ('R', 'repository', '',
2825 2828 _('repository root directory or symbolic path name')),
2826 2829 ('', 'cwd', '', _('change working directory')),
2827 2830 ('y', 'noninteractive', None,
2828 2831 _('do not prompt, assume \'yes\' for any required answers')),
2829 2832 ('q', 'quiet', None, _('suppress output')),
2830 2833 ('v', 'verbose', None, _('enable additional output')),
2831 2834 ('', 'config', [], _('set/override config option')),
2832 2835 ('', 'debug', None, _('enable debugging output')),
2833 2836 ('', 'debugger', None, _('start debugger')),
2834 2837 ('', 'encoding', util._encoding, _('set the charset encoding')),
2835 2838 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2836 2839 ('', 'lsprof', None, _('print improved command execution profile')),
2837 2840 ('', 'traceback', None, _('print traceback on exception')),
2838 2841 ('', 'time', None, _('time how long the command takes')),
2839 2842 ('', 'profile', None, _('print command execution profile')),
2840 2843 ('', 'version', None, _('output version information and exit')),
2841 2844 ('h', 'help', None, _('display help and exit')),
2842 2845 ]
2843 2846
2844 2847 dryrunopts = [('n', 'dry-run', None,
2845 2848 _('do not perform actions, just print output'))]
2846 2849
2847 2850 remoteopts = [
2848 2851 ('e', 'ssh', '', _('specify ssh command to use')),
2849 2852 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2850 2853 ]
2851 2854
2852 2855 walkopts = [
2853 2856 ('I', 'include', [], _('include names matching the given patterns')),
2854 2857 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2855 2858 ]
2856 2859
2857 2860 commitopts = [
2858 2861 ('m', 'message', '', _('use <text> as commit message')),
2859 2862 ('l', 'logfile', '', _('read commit message from <file>')),
2860 2863 ]
2861 2864
2862 2865 commitopts2 = [
2863 2866 ('d', 'date', '', _('record datecode as commit date')),
2864 2867 ('u', 'user', '', _('record user as committer')),
2865 2868 ]
2866 2869
2867 2870 templateopts = [
2868 2871 ('', 'style', '', _('display using template map file')),
2869 2872 ('', 'template', '', _('display with template')),
2870 2873 ]
2871 2874
2872 2875 logopts = [
2873 2876 ('p', 'patch', None, _('show patch')),
2874 2877 ('l', 'limit', '', _('limit number of changes displayed')),
2875 2878 ('M', 'no-merges', None, _('do not show merges')),
2876 2879 ] + templateopts
2877 2880
2878 2881 table = {
2879 2882 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2880 2883 "addremove":
2881 2884 (addremove,
2882 2885 [('s', 'similarity', '',
2883 2886 _('guess renamed files by similarity (0<=s<=100)')),
2884 2887 ] + walkopts + dryrunopts,
2885 2888 _('hg addremove [OPTION]... [FILE]...')),
2886 2889 "^annotate|blame":
2887 2890 (annotate,
2888 2891 [('r', 'rev', '', _('annotate the specified revision')),
2889 2892 ('f', 'follow', None, _('follow file copies and renames')),
2890 2893 ('a', 'text', None, _('treat all files as text')),
2891 2894 ('u', 'user', None, _('list the author (long with -v)')),
2892 2895 ('d', 'date', None, _('list the date (short with -q)')),
2893 2896 ('n', 'number', None, _('list the revision number (default)')),
2894 2897 ('c', 'changeset', None, _('list the changeset')),
2895 2898 ('l', 'line-number', None,
2896 2899 _('show line number at the first appearance'))
2897 2900 ] + walkopts,
2898 2901 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
2899 2902 "archive":
2900 2903 (archive,
2901 2904 [('', 'no-decode', None, _('do not pass files through decoders')),
2902 2905 ('p', 'prefix', '', _('directory prefix for files in archive')),
2903 2906 ('r', 'rev', '', _('revision to distribute')),
2904 2907 ('t', 'type', '', _('type of distribution to create')),
2905 2908 ] + walkopts,
2906 2909 _('hg archive [OPTION]... DEST')),
2907 2910 "backout":
2908 2911 (backout,
2909 2912 [('', 'merge', None,
2910 2913 _('merge with old dirstate parent after backout')),
2911 2914 ('', 'parent', '', _('parent to choose when backing out merge')),
2912 2915 ('r', 'rev', '', _('revision to backout')),
2913 2916 ] + walkopts + commitopts + commitopts2,
2914 2917 _('hg backout [OPTION]... [-r] REV')),
2915 2918 "bisect":
2916 2919 (bisect,
2917 2920 [('r', 'reset', False, _('reset bisect state')),
2918 2921 ('g', 'good', False, _('mark changeset good')),
2919 2922 ('b', 'bad', False, _('mark changeset bad')),
2920 2923 ('s', 'skip', False, _('skip testing changeset')),
2921 2924 ('U', 'noupdate', False, _('do not update to target'))],
2922 2925 _("hg bisect [-gbsr] [REV]")),
2923 2926 "branch":
2924 2927 (branch,
2925 2928 [('f', 'force', None,
2926 2929 _('set branch name even if it shadows an existing branch'))],
2927 2930 _('hg branch [-f] [NAME]')),
2928 2931 "branches":
2929 2932 (branches,
2930 2933 [('a', 'active', False,
2931 2934 _('show only branches that have unmerged heads'))],
2932 2935 _('hg branches [-a]')),
2933 2936 "bundle":
2934 2937 (bundle,
2935 2938 [('f', 'force', None,
2936 2939 _('run even when remote repository is unrelated')),
2937 2940 ('r', 'rev', [],
2938 2941 _('a changeset up to which you would like to bundle')),
2939 2942 ('', 'base', [],
2940 2943 _('a base changeset to specify instead of a destination')),
2941 2944 ('a', 'all', None,
2942 2945 _('bundle all changesets in the repository')),
2943 2946 ] + remoteopts,
2944 2947 _('hg bundle [-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
2945 2948 "cat":
2946 2949 (cat,
2947 2950 [('o', 'output', '', _('print output to file with formatted name')),
2948 2951 ('r', 'rev', '', _('print the given revision')),
2949 2952 ('', 'decode', None, _('apply any matching decode filter')),
2950 2953 ] + walkopts,
2951 2954 _('hg cat [OPTION]... FILE...')),
2952 2955 "^clone":
2953 2956 (clone,
2954 2957 [('U', 'noupdate', None, _('do not update the new working directory')),
2955 2958 ('r', 'rev', [],
2956 2959 _('a changeset you would like to have after cloning')),
2957 2960 ('', 'pull', None, _('use pull protocol to copy metadata')),
2958 2961 ('', 'uncompressed', None,
2959 2962 _('use uncompressed transfer (fast over LAN)')),
2960 2963 ] + remoteopts,
2961 2964 _('hg clone [OPTION]... SOURCE [DEST]')),
2962 2965 "^commit|ci":
2963 2966 (commit,
2964 2967 [('A', 'addremove', None,
2965 2968 _('mark new/missing files as added/removed before committing')),
2966 2969 ] + walkopts + commitopts + commitopts2,
2967 2970 _('hg commit [OPTION]... [FILE]...')),
2968 2971 "copy|cp":
2969 2972 (copy,
2970 2973 [('A', 'after', None, _('record a copy that has already occurred')),
2971 2974 ('f', 'force', None,
2972 2975 _('forcibly copy over an existing managed file')),
2973 2976 ] + walkopts + dryrunopts,
2974 2977 _('hg copy [OPTION]... [SOURCE]... DEST')),
2975 2978 "debugancestor": (debugancestor, [],
2976 2979 _('hg debugancestor [INDEX] REV1 REV2')),
2977 2980 "debugcheckstate": (debugcheckstate, [], _('hg debugcheckstate')),
2978 2981 "debugcomplete":
2979 2982 (debugcomplete,
2980 2983 [('o', 'options', None, _('show the command options'))],
2981 2984 _('hg debugcomplete [-o] CMD')),
2982 2985 "debugdate":
2983 2986 (debugdate,
2984 2987 [('e', 'extended', None, _('try extended date formats'))],
2985 2988 _('hg debugdate [-e] DATE [RANGE]')),
2986 2989 "debugdata": (debugdata, [], _('hg debugdata FILE REV')),
2987 2990 "debugfsinfo": (debugfsinfo, [], _('hg debugfsinfo [PATH]')),
2988 2991 "debugindex": (debugindex, [], _('hg debugindex FILE')),
2989 2992 "debugindexdot": (debugindexdot, [], _('hg debugindexdot FILE')),
2990 2993 "debuginstall": (debuginstall, [], _('hg debuginstall')),
2991 2994 "debugrawcommit|rawcommit":
2992 2995 (rawcommit,
2993 2996 [('p', 'parent', [], _('parent')),
2994 2997 ('F', 'files', '', _('file list'))
2995 2998 ] + commitopts + commitopts2,
2996 2999 _('hg debugrawcommit [OPTION]... [FILE]...')),
2997 3000 "debugrebuildstate":
2998 3001 (debugrebuildstate,
2999 3002 [('r', 'rev', '', _('revision to rebuild to'))],
3000 3003 _('hg debugrebuildstate [-r REV] [REV]')),
3001 3004 "debugrename":
3002 3005 (debugrename,
3003 3006 [('r', 'rev', '', _('revision to debug'))],
3004 3007 _('hg debugrename [-r REV] FILE')),
3005 3008 "debugsetparents":
3006 3009 (debugsetparents,
3007 3010 [],
3008 3011 _('hg debugsetparents REV1 [REV2]')),
3009 3012 "debugstate":
3010 3013 (debugstate,
3011 3014 [('', 'nodates', None, _('do not display the saved mtime'))],
3012 3015 _('hg debugstate [OPTS]')),
3013 3016 "debugwalk": (debugwalk, walkopts, _('hg debugwalk [OPTION]... [FILE]...')),
3014 3017 "^diff":
3015 3018 (diff,
3016 3019 [('r', 'rev', [], _('revision')),
3017 3020 ('a', 'text', None, _('treat all files as text')),
3018 3021 ('p', 'show-function', None,
3019 3022 _('show which function each change is in')),
3020 3023 ('g', 'git', None, _('use git extended diff format')),
3021 3024 ('', 'nodates', None, _("don't include dates in diff headers")),
3022 3025 ('w', 'ignore-all-space', None,
3023 3026 _('ignore white space when comparing lines')),
3024 3027 ('b', 'ignore-space-change', None,
3025 3028 _('ignore changes in the amount of white space')),
3026 3029 ('B', 'ignore-blank-lines', None,
3027 3030 _('ignore changes whose lines are all blank')),
3028 3031 ('U', 'unified', 3,
3029 3032 _('number of lines of context to show'))
3030 3033 ] + walkopts,
3031 3034 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3032 3035 "^export":
3033 3036 (export,
3034 3037 [('o', 'output', '', _('print output to file with formatted name')),
3035 3038 ('a', 'text', None, _('treat all files as text')),
3036 3039 ('g', 'git', None, _('use git extended diff format')),
3037 3040 ('', 'nodates', None, _("don't include dates in diff headers")),
3038 3041 ('', 'switch-parent', None, _('diff against the second parent'))],
3039 3042 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
3040 3043 "grep":
3041 3044 (grep,
3042 3045 [('0', 'print0', None, _('end fields with NUL')),
3043 3046 ('', 'all', None, _('print all revisions that match')),
3044 3047 ('f', 'follow', None,
3045 3048 _('follow changeset history, or file history across copies and renames')),
3046 3049 ('i', 'ignore-case', None, _('ignore case when matching')),
3047 3050 ('l', 'files-with-matches', None,
3048 3051 _('print only filenames and revs that match')),
3049 3052 ('n', 'line-number', None, _('print matching line numbers')),
3050 3053 ('r', 'rev', [], _('search in given revision range')),
3051 3054 ('u', 'user', None, _('list the author (long with -v)')),
3052 3055 ('d', 'date', None, _('list the date (short with -q)')),
3053 3056 ] + walkopts,
3054 3057 _('hg grep [OPTION]... PATTERN [FILE]...')),
3055 3058 "heads":
3056 3059 (heads,
3057 3060 [('r', 'rev', '', _('show only heads which are descendants of rev')),
3058 3061 ] + templateopts,
3059 3062 _('hg heads [-r REV] [REV]...')),
3060 3063 "help": (help_, [], _('hg help [COMMAND]')),
3061 3064 "identify|id":
3062 3065 (identify,
3063 3066 [('r', 'rev', '', _('identify the specified rev')),
3064 3067 ('n', 'num', None, _('show local revision number')),
3065 3068 ('i', 'id', None, _('show global revision id')),
3066 3069 ('b', 'branch', None, _('show branch')),
3067 3070 ('t', 'tags', None, _('show tags'))],
3068 3071 _('hg identify [-nibt] [-r REV] [SOURCE]')),
3069 3072 "import|patch":
3070 3073 (import_,
3071 3074 [('p', 'strip', 1,
3072 3075 _('directory strip option for patch. This has the same\n'
3073 3076 'meaning as the corresponding patch option')),
3074 3077 ('b', 'base', '', _('base path')),
3075 3078 ('f', 'force', None,
3076 3079 _('skip check for outstanding uncommitted changes')),
3077 3080 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3078 3081 ('', 'exact', None,
3079 3082 _('apply patch to the nodes from which it was generated')),
3080 3083 ('', 'import-branch', None,
3081 3084 _('Use any branch information in patch (implied by --exact)'))] +
3082 3085 commitopts + commitopts2,
3083 3086 _('hg import [OPTION]... PATCH...')),
3084 3087 "incoming|in":
3085 3088 (incoming,
3086 3089 [('f', 'force', None,
3087 3090 _('run even when remote repository is unrelated')),
3088 3091 ('n', 'newest-first', None, _('show newest record first')),
3089 3092 ('', 'bundle', '', _('file to store the bundles into')),
3090 3093 ('r', 'rev', [],
3091 3094 _('a specific revision up to which you would like to pull')),
3092 3095 ] + logopts + remoteopts,
3093 3096 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
3094 3097 ' [--bundle FILENAME] [SOURCE]')),
3095 3098 "^init":
3096 3099 (init,
3097 3100 remoteopts,
3098 3101 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
3099 3102 "locate":
3100 3103 (locate,
3101 3104 [('r', 'rev', '', _('search the repository as it stood at rev')),
3102 3105 ('0', 'print0', None,
3103 3106 _('end filenames with NUL, for use with xargs')),
3104 3107 ('f', 'fullpath', None,
3105 3108 _('print complete paths from the filesystem root')),
3106 3109 ] + walkopts,
3107 3110 _('hg locate [OPTION]... [PATTERN]...')),
3108 3111 "^log|history":
3109 3112 (log,
3110 3113 [('f', 'follow', None,
3111 3114 _('follow changeset history, or file history across copies and renames')),
3112 3115 ('', 'follow-first', None,
3113 3116 _('only follow the first parent of merge changesets')),
3114 3117 ('d', 'date', '', _('show revs matching date spec')),
3115 3118 ('C', 'copies', None, _('show copied files')),
3116 3119 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3117 3120 ('r', 'rev', [], _('show the specified revision or range')),
3118 3121 ('', 'removed', None, _('include revs where files were removed')),
3119 3122 ('m', 'only-merges', None, _('show only merges')),
3120 3123 ('b', 'only-branch', [],
3121 3124 _('show only changesets within the given named branch')),
3122 3125 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3123 3126 ] + logopts + walkopts,
3124 3127 _('hg log [OPTION]... [FILE]')),
3125 3128 "manifest":
3126 3129 (manifest,
3127 3130 [('r', 'rev', '', _('revision to display'))],
3128 3131 _('hg manifest [-r REV]')),
3129 3132 "^merge":
3130 3133 (merge,
3131 3134 [('f', 'force', None, _('force a merge with outstanding changes')),
3132 3135 ('r', 'rev', '', _('revision to merge')),
3133 3136 ],
3134 3137 _('hg merge [-f] [[-r] REV]')),
3135 3138 "outgoing|out":
3136 3139 (outgoing,
3137 3140 [('f', 'force', None,
3138 3141 _('run even when remote repository is unrelated')),
3139 3142 ('r', 'rev', [],
3140 3143 _('a specific revision up to which you would like to push')),
3141 3144 ('n', 'newest-first', None, _('show newest record first')),
3142 3145 ] + logopts + remoteopts,
3143 3146 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3144 3147 "^parents":
3145 3148 (parents,
3146 3149 [('r', 'rev', '', _('show parents from the specified rev')),
3147 3150 ] + templateopts,
3148 3151 _('hg parents [-r REV] [FILE]')),
3149 3152 "paths": (paths, [], _('hg paths [NAME]')),
3150 3153 "^pull":
3151 3154 (pull,
3152 3155 [('u', 'update', None,
3153 3156 _('update to new tip if changesets were pulled')),
3154 3157 ('f', 'force', None,
3155 3158 _('run even when remote repository is unrelated')),
3156 3159 ('r', 'rev', [],
3157 3160 _('a specific revision up to which you would like to pull')),
3158 3161 ] + remoteopts,
3159 3162 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3160 3163 "^push":
3161 3164 (push,
3162 3165 [('f', 'force', None, _('force push')),
3163 3166 ('r', 'rev', [],
3164 3167 _('a specific revision up to which you would like to push')),
3165 3168 ] + remoteopts,
3166 3169 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3167 3170 "recover": (recover, [], _('hg recover')),
3168 3171 "^remove|rm":
3169 3172 (remove,
3170 3173 [('A', 'after', None, _('record delete for missing files')),
3171 3174 ('f', 'force', None,
3172 3175 _('remove (and delete) file even if added or modified')),
3173 3176 ] + walkopts,
3174 3177 _('hg remove [OPTION]... FILE...')),
3175 3178 "rename|mv":
3176 3179 (rename,
3177 3180 [('A', 'after', None, _('record a rename that has already occurred')),
3178 3181 ('f', 'force', None,
3179 3182 _('forcibly copy over an existing managed file')),
3180 3183 ] + walkopts + dryrunopts,
3181 3184 _('hg rename [OPTION]... SOURCE... DEST')),
3182 3185 "revert":
3183 3186 (revert,
3184 3187 [('a', 'all', None, _('revert all changes when no arguments given')),
3185 3188 ('d', 'date', '', _('tipmost revision matching date')),
3186 3189 ('r', 'rev', '', _('revision to revert to')),
3187 3190 ('', 'no-backup', None, _('do not save backup copies of files')),
3188 3191 ] + walkopts + dryrunopts,
3189 3192 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3190 3193 "rollback": (rollback, [], _('hg rollback')),
3191 3194 "root": (root, [], _('hg root')),
3192 3195 "^serve":
3193 3196 (serve,
3194 3197 [('A', 'accesslog', '', _('name of access log file to write to')),
3195 3198 ('d', 'daemon', None, _('run server in background')),
3196 3199 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3197 3200 ('E', 'errorlog', '', _('name of error log file to write to')),
3198 3201 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3199 3202 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3200 3203 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3201 3204 ('n', 'name', '',
3202 3205 _('name to show in web pages (default: working dir)')),
3203 3206 ('', 'webdir-conf', '', _('name of the webdir config file'
3204 3207 ' (serve more than one repo)')),
3205 3208 ('', 'pid-file', '', _('name of file to write process ID to')),
3206 3209 ('', 'stdio', None, _('for remote clients')),
3207 3210 ('t', 'templates', '', _('web templates to use')),
3208 3211 ('', 'style', '', _('template style to use')),
3209 3212 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3210 3213 ('', 'certificate', '', _('SSL certificate file'))],
3211 3214 _('hg serve [OPTION]...')),
3212 3215 "showconfig|debugconfig":
3213 3216 (showconfig,
3214 3217 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3215 3218 _('hg showconfig [-u] [NAME]...')),
3216 3219 "^status|st":
3217 3220 (status,
3218 3221 [('A', 'all', None, _('show status of all files')),
3219 3222 ('m', 'modified', None, _('show only modified files')),
3220 3223 ('a', 'added', None, _('show only added files')),
3221 3224 ('r', 'removed', None, _('show only removed files')),
3222 3225 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3223 3226 ('c', 'clean', None, _('show only files without changes')),
3224 3227 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3225 3228 ('i', 'ignored', None, _('show only ignored files')),
3226 3229 ('n', 'no-status', None, _('hide status prefix')),
3227 3230 ('C', 'copies', None, _('show source of copied files')),
3228 3231 ('0', 'print0', None,
3229 3232 _('end filenames with NUL, for use with xargs')),
3230 3233 ('', 'rev', [], _('show difference from revision')),
3231 3234 ] + walkopts,
3232 3235 _('hg status [OPTION]... [FILE]...')),
3233 3236 "tag":
3234 3237 (tag,
3235 3238 [('f', 'force', None, _('replace existing tag')),
3236 3239 ('l', 'local', None, _('make the tag local')),
3237 3240 ('r', 'rev', '', _('revision to tag')),
3238 3241 ('', 'remove', None, _('remove a tag')),
3239 3242 # -l/--local is already there, commitopts cannot be used
3240 3243 ('m', 'message', '', _('use <text> as commit message')),
3241 3244 ] + commitopts2,
3242 3245 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3243 3246 "tags": (tags, [], _('hg tags')),
3244 3247 "tip":
3245 3248 (tip,
3246 3249 [('p', 'patch', None, _('show patch')),
3247 3250 ] + templateopts,
3248 3251 _('hg tip [-p]')),
3249 3252 "unbundle":
3250 3253 (unbundle,
3251 3254 [('u', 'update', None,
3252 3255 _('update to new tip if changesets were unbundled'))],
3253 3256 _('hg unbundle [-u] FILE...')),
3254 3257 "^update|up|checkout|co":
3255 3258 (update,
3256 3259 [('C', 'clean', None, _('overwrite locally modified files')),
3257 3260 ('d', 'date', '', _('tipmost revision matching date')),
3258 3261 ('r', 'rev', '', _('revision'))],
3259 3262 _('hg update [-C] [-d DATE] [[-r] REV]')),
3260 3263 "verify": (verify, [], _('hg verify')),
3261 3264 "version": (version_, [], _('hg version')),
3262 3265 }
3263 3266
3264 3267 norepo = ("clone init version help debugcomplete debugdata"
3265 3268 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3266 3269 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,193 +1,233 b''
1 1 # copies.py - copy detection for Mercurial
2 2 #
3 3 # Copyright 2008 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from node import nullid, nullrev
9 9 from i18n import _
10 import util, ancestor
10 import util, heapq
11 11
12 12 def _nonoverlap(d1, d2, d3):
13 13 "Return list of elements in d1 not in d2 or d3"
14 14 l = [d for d in d1 if d not in d3 and d not in d2]
15 15 l.sort()
16 16 return l
17 17
18 18 def _dirname(f):
19 19 s = f.rfind("/")
20 20 if s == -1:
21 21 return ""
22 22 return f[:s]
23 23
24 24 def _dirs(files):
25 25 d = {}
26 26 for f in files:
27 27 f = _dirname(f)
28 28 while f not in d:
29 29 d[f] = True
30 30 f = _dirname(f)
31 31 return d
32 32
33 33 def _findoldnames(fctx, limit):
34 34 "find files that path was copied from, back to linkrev limit"
35 35 old = {}
36 36 seen = {}
37 37 orig = fctx.path()
38 visit = [fctx]
38 visit = [(fctx, 0)]
39 39 while visit:
40 fc = visit.pop()
40 fc, depth = visit.pop()
41 41 s = str(fc)
42 42 if s in seen:
43 43 continue
44 44 seen[s] = 1
45 45 if fc.path() != orig and fc.path() not in old:
46 old[fc.path()] = 1
46 old[fc.path()] = (depth, fc.path()) # remember depth
47 47 if fc.rev() < limit and fc.rev() is not None:
48 48 continue
49 visit += fc.parents()
49 visit += [(p, depth - 1) for p in fc.parents()]
50 50
51 old = old.keys()
51 # return old names sorted by depth
52 old = old.values()
52 53 old.sort()
53 return old
54 return [o[1] for o in old]
55
56 def _findlimit(repo, a, b):
57 "find the earliest revision that's an ancestor of a or b but not both"
58 # basic idea:
59 # - mark a and b with different sides
60 # - if a parent's children are all on the same side, the parent is
61 # on that side, otherwise it is on no side
62 # - walk the graph in topological order with the help of a heap;
63 # - add unseen parents to side map
64 # - clear side of any parent that has children on different sides
65 # - track number of interesting revs that might still be on a side
66 # - track the lowest interesting rev seen
67 # - quit when interesting revs is zero
68
69 cl = repo.changelog
70 working = cl.count() # pseudo rev for the working directory
71 if a is None:
72 a = working
73 if b is None:
74 b = working
54 75
55 def copies(repo, c1, c2, ca):
76 side = {a: -1, b: 1}
77 visit = [-a, -b]
78 heapq.heapify(visit)
79 interesting = len(visit)
80 limit = working
81
82 while interesting:
83 r = -heapq.heappop(visit)
84 if r == working:
85 parents = [cl.rev(p) for p in repo.dirstate.parents()]
86 else:
87 parents = cl.parentrevs(r)
88 for p in parents:
89 if p not in side:
90 # first time we see p; add it to visit
91 side[p] = side[r]
92 if side[p]:
93 interesting += 1
94 heapq.heappush(visit, -p)
95 elif side[p] and side[p] != side[r]:
96 # p was interesting but now we know better
97 side[p] = 0
98 interesting -= 1
99 if side[r]:
100 limit = r # lowest rev visited
101 interesting -= 1
102 return limit
103
104 def copies(repo, c1, c2, ca, checkdirs=False):
56 105 """
57 106 Find moves and copies between context c1 and c2
58 107 """
59 108 # avoid silly behavior for update from empty dir
60 if not c1 or not c2:
109 if not c1 or not c2 or c1 == c2:
61 110 return {}, {}
62 111
63 rev1, rev2 = c1.rev(), c2.rev()
64 if rev1 is None: # c1 is a workingctx
65 rev1 = c1.parents()[0].rev()
66 if rev2 is None: # c2 is a workingctx
67 rev2 = c2.parents()[0].rev()
68 pr = repo.changelog.parentrevs
69 def parents(rev):
70 return [p for p in pr(rev) if p != nullrev]
71 limit = min(ancestor.symmetricdifference(rev1, rev2, parents))
112 limit = _findlimit(repo, c1.rev(), c2.rev())
72 113 m1 = c1.manifest()
73 114 m2 = c2.manifest()
74 115 ma = ca.manifest()
75 116
76 117 def makectx(f, n):
77 118 if len(n) != 20: # in a working context?
78 119 if c1.rev() is None:
79 120 return c1.filectx(f)
80 121 return c2.filectx(f)
81 122 return repo.filectx(f, fileid=n)
82 123 ctx = util.cachefunc(makectx)
83 124
84 125 copy = {}
85 126 fullcopy = {}
86 127 diverge = {}
87 128
88 129 def checkcopies(f, m1, m2):
89 130 '''check possible copies of f from m1 to m2'''
90 131 c1 = ctx(f, m1[f])
91 132 for of in _findoldnames(c1, limit):
92 133 fullcopy[f] = of # remember for dir rename detection
93 134 if of in m2: # original file not in other manifest?
94 135 # if the original file is unchanged on the other branch,
95 136 # no merge needed
96 137 if m2[of] != ma.get(of):
97 138 c2 = ctx(of, m2[of])
98 139 ca = c1.ancestor(c2)
99 140 # related and named changed on only one side?
100 if ca and ca.path() == f or ca.path() == c2.path():
141 if ca and (ca.path() == f or ca.path() == c2.path()):
101 142 if c1 != ca or c2 != ca: # merge needed?
102 143 copy[f] = of
103 144 elif of in ma:
104 145 diverge.setdefault(of, []).append(f)
105 146
106 if not repo.ui.configbool("merge", "followcopies", True):
107 return {}, {}
108
109 147 repo.ui.debug(_(" searching for copies back to rev %d\n") % limit)
110 148
111 149 u1 = _nonoverlap(m1, m2, ma)
112 150 u2 = _nonoverlap(m2, m1, ma)
113 151
114 152 if u1:
115 153 repo.ui.debug(_(" unmatched files in local:\n %s\n")
116 154 % "\n ".join(u1))
117 155 if u2:
118 156 repo.ui.debug(_(" unmatched files in other:\n %s\n")
119 157 % "\n ".join(u2))
120 158
121 159 for f in u1:
122 160 checkcopies(f, m1, m2)
123 161 for f in u2:
124 162 checkcopies(f, m2, m1)
125 163
126 164 diverge2 = {}
127 165 for of, fl in diverge.items():
128 166 if len(fl) == 1:
129 167 del diverge[of] # not actually divergent
130 168 else:
131 169 diverge2.update(dict.fromkeys(fl)) # reverse map for below
132 170
133 171 if fullcopy:
134 172 repo.ui.debug(_(" all copies found (* = to merge, ! = divergent):\n"))
135 173 for f in fullcopy:
136 174 note = ""
137 175 if f in copy: note += "*"
138 176 if f in diverge2: note += "!"
139 177 repo.ui.debug(_(" %s -> %s %s\n") % (f, fullcopy[f], note))
140 178 del diverge2
141 179
142 if not fullcopy or not repo.ui.configbool("merge", "followdirs", True):
180 if not fullcopy or not checkdirs:
143 181 return copy, diverge
144 182
145 183 repo.ui.debug(_(" checking for directory renames\n"))
146 184
147 185 # generate a directory move map
148 186 d1, d2 = _dirs(m1), _dirs(m2)
149 187 invalid = {}
150 188 dirmove = {}
151 189
152 190 # examine each file copy for a potential directory move, which is
153 191 # when all the files in a directory are moved to a new directory
154 192 for dst, src in fullcopy.items():
155 193 dsrc, ddst = _dirname(src), _dirname(dst)
156 194 if dsrc in invalid:
157 195 # already seen to be uninteresting
158 196 continue
159 197 elif dsrc in d1 and ddst in d1:
160 198 # directory wasn't entirely moved locally
161 199 invalid[dsrc] = True
162 200 elif dsrc in d2 and ddst in d2:
163 201 # directory wasn't entirely moved remotely
164 202 invalid[dsrc] = True
165 203 elif dsrc in dirmove and dirmove[dsrc] != ddst:
166 204 # files from the same directory moved to two different places
167 205 invalid[dsrc] = True
168 206 else:
169 207 # looks good so far
170 208 dirmove[dsrc + "/"] = ddst + "/"
171 209
172 210 for i in invalid:
173 211 if i in dirmove:
174 212 del dirmove[i]
175 213 del d1, d2, invalid
176 214
177 215 if not dirmove:
178 216 return copy, diverge
179 217
180 218 for d in dirmove:
181 219 repo.ui.debug(_(" dir %s -> %s\n") % (d, dirmove[d]))
182 220
183 221 # check unaccounted nonoverlapping files against directory moves
184 222 for f in u1 + u2:
185 223 if f not in fullcopy:
186 224 for d in dirmove:
187 225 if f.startswith(d):
188 226 # new file added in a directory that was moved, move it
189 copy[f] = dirmove[d] + f[len(d):]
190 repo.ui.debug(_(" file %s -> %s\n") % (f, copy[f]))
227 df = dirmove[d] + f[len(d):]
228 if df not in copy:
229 copy[f] = df
230 repo.ui.debug(_(" file %s -> %s\n") % (f, copy[f]))
191 231 break
192 232
193 233 return copy, diverge
@@ -1,411 +1,413 b''
1 1 # merge.py - directory-level update/merge handling for Mercurial
2 2 #
3 3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms
6 6 # of the GNU General Public License, incorporated herein by reference.
7 7
8 8 from node import nullid, nullrev
9 9 from i18n import _
10 10 import errno, util, os, filemerge, copies
11 11
12 12 def _checkunknown(wctx, mctx):
13 13 "check for collisions between unknown files and files in mctx"
14 14 for f in wctx.unknown():
15 15 if f in mctx and mctx[f].cmp(wctx[f].data()):
16 16 raise util.Abort(_("untracked file in working directory differs"
17 17 " from file in requested revision: '%s'") % f)
18 18
19 19 def _checkcollision(mctx):
20 20 "check for case folding collisions in the destination context"
21 21 folded = {}
22 22 for fn in mctx:
23 23 fold = fn.lower()
24 24 if fold in folded:
25 25 raise util.Abort(_("case-folding collision between %s and %s")
26 26 % (fn, folded[fold]))
27 27 folded[fold] = fn
28 28
29 29 def _forgetremoved(wctx, mctx, branchmerge):
30 30 """
31 31 Forget removed files
32 32
33 33 If we're jumping between revisions (as opposed to merging), and if
34 34 neither the working directory nor the target rev has the file,
35 35 then we need to remove it from the dirstate, to prevent the
36 36 dirstate from listing the file when it is no longer in the
37 37 manifest.
38 38
39 39 If we're merging, and the other revision has removed a file
40 40 that is not present in the working directory, we need to mark it
41 41 as removed.
42 42 """
43 43
44 44 action = []
45 45 state = branchmerge and 'r' or 'f'
46 46 for f in wctx.deleted():
47 47 if f not in mctx:
48 48 action.append((f, state))
49 49
50 50 if not branchmerge:
51 51 for f in wctx.removed():
52 52 if f not in mctx:
53 53 action.append((f, "f"))
54 54
55 55 return action
56 56
57 57 def manifestmerge(repo, p1, p2, pa, overwrite, partial):
58 58 """
59 59 Merge p1 and p2 with ancestor ma and generate merge action list
60 60
61 61 overwrite = whether we clobber working files
62 62 partial = function to filter file lists
63 63 """
64 64
65 65 repo.ui.note(_("resolving manifests\n"))
66 66 repo.ui.debug(_(" overwrite %s partial %s\n") % (overwrite, bool(partial)))
67 67 repo.ui.debug(_(" ancestor %s local %s remote %s\n") % (pa, p1, p2))
68 68
69 69 m1 = p1.manifest()
70 70 m2 = p2.manifest()
71 71 ma = pa.manifest()
72 72 backwards = (pa == p2)
73 73 action = []
74 74 copy, copied, diverge = {}, {}, {}
75 75
76 76 def fmerge(f, f2=None, fa=None):
77 77 """merge flags"""
78 78 if not f2:
79 79 f2 = f
80 80 fa = f
81 81 a, m, n = ma.flags(fa), m1.flags(f), m2.flags(f2)
82 82 if m == n: # flags agree
83 83 return m # unchanged
84 84 if m and n: # flags are set but don't agree
85 85 if not a: # both differ from parent
86 86 r = repo.ui.prompt(
87 87 _(" conflicting flags for %s\n"
88 88 "(n)one, e(x)ec or sym(l)ink?") % f, "[nxl]", "n")
89 89 return r != "n" and r or ''
90 90 if m == a:
91 91 return n # changed from m to n
92 92 return m # changed from n to m
93 93 if m and m != a: # changed from a to m
94 94 return m
95 95 if n and n != a: # changed from a to n
96 96 return n
97 97 return '' # flag was cleared
98 98
99 99 def act(msg, m, f, *args):
100 100 repo.ui.debug(" %s: %s -> %s\n" % (f, msg, m))
101 101 action.append((f, m) + args)
102 102
103 103 if pa and not (backwards or overwrite):
104 copy, diverge = copies.copies(repo, p1, p2, pa)
104 if repo.ui.configbool("merge", "followcopies", True):
105 dirs = repo.ui.configbool("merge", "followdirs", True)
106 copy, diverge = copies.copies(repo, p1, p2, pa, dirs)
105 107 copied = dict.fromkeys(copy.values())
106 108 for of, fl in diverge.items():
107 109 act("divergent renames", "dr", of, fl)
108 110
109 111 # Compare manifests
110 112 for f, n in m1.iteritems():
111 113 if partial and not partial(f):
112 114 continue
113 115 if f in m2:
114 116 if overwrite or backwards:
115 117 rflags = m2.flags(f)
116 118 else:
117 119 rflags = fmerge(f)
118 120 # are files different?
119 121 if n != m2[f]:
120 122 a = ma.get(f, nullid)
121 123 # are we clobbering?
122 124 if overwrite:
123 125 act("clobbering", "g", f, rflags)
124 126 # or are we going back in time and clean?
125 127 elif backwards and not n[20:]:
126 128 act("reverting", "g", f, rflags)
127 129 # are both different from the ancestor?
128 130 elif n != a and m2[f] != a:
129 131 act("versions differ", "m", f, f, f, rflags, False)
130 132 # is remote's version newer?
131 133 elif m2[f] != a:
132 134 act("remote is newer", "g", f, rflags)
133 135 # local is newer, not overwrite, check mode bits
134 136 elif m1.flags(f) != rflags:
135 137 act("update permissions", "e", f, rflags)
136 138 # contents same, check mode bits
137 139 elif m1.flags(f) != rflags:
138 140 act("update permissions", "e", f, rflags)
139 141 elif f in copied:
140 142 continue
141 143 elif f in copy:
142 144 f2 = copy[f]
143 145 if f2 not in m2: # directory rename
144 146 act("remote renamed directory to " + f2, "d",
145 147 f, None, f2, m1.flags(f))
146 148 elif f2 in m1: # case 2 A,B/B/B
147 149 act("local copied to " + f2, "m",
148 150 f, f2, f, fmerge(f, f2, f2), False)
149 151 else: # case 4,21 A/B/B
150 152 act("local moved to " + f2, "m",
151 153 f, f2, f, fmerge(f, f2, f2), False)
152 154 elif f in ma:
153 155 if n != ma[f] and not overwrite:
154 156 if repo.ui.prompt(
155 157 _(" local changed %s which remote deleted\n"
156 158 "use (c)hanged version or (d)elete?") % f,
157 159 _("[cd]"), _("c")) == _("d"):
158 160 act("prompt delete", "r", f)
159 161 else:
160 162 act("other deleted", "r", f)
161 163 else:
162 164 # file is created on branch or in working directory
163 165 if (overwrite and n[20:] != "u") or (backwards and not n[20:]):
164 166 act("remote deleted", "r", f)
165 167
166 168 for f, n in m2.iteritems():
167 169 if partial and not partial(f):
168 170 continue
169 171 if f in m1:
170 172 continue
171 173 if f in copied:
172 174 continue
173 175 if f in copy:
174 176 f2 = copy[f]
175 177 if f2 not in m1: # directory rename
176 178 act("local renamed directory to " + f2, "d",
177 179 None, f, f2, m2.flags(f))
178 180 elif f2 in m2: # rename case 1, A/A,B/A
179 181 act("remote copied to " + f, "m",
180 182 f2, f, f, fmerge(f2, f, f2), False)
181 183 else: # case 3,20 A/B/A
182 184 act("remote moved to " + f, "m",
183 185 f2, f, f, fmerge(f2, f, f2), True)
184 186 elif f in ma:
185 187 if overwrite or backwards:
186 188 act("recreating", "g", f, m2.flags(f))
187 189 elif n != ma[f]:
188 190 if repo.ui.prompt(
189 191 _("remote changed %s which local deleted\n"
190 192 "use (c)hanged version or leave (d)eleted?") % f,
191 193 _("[cd]"), _("c")) == _("c"):
192 194 act("prompt recreating", "g", f, m2.flags(f))
193 195 else:
194 196 act("remote created", "g", f, m2.flags(f))
195 197
196 198 return action
197 199
198 200 def applyupdates(repo, action, wctx, mctx):
199 201 "apply the merge action list to the working directory"
200 202
201 203 updated, merged, removed, unresolved = 0, 0, 0, 0
202 204 action.sort()
203 205 # prescan for copy/renames
204 206 for a in action:
205 207 f, m = a[:2]
206 208 if m == 'm': # merge
207 209 f2, fd, flags, move = a[2:]
208 210 if f != fd:
209 211 repo.ui.debug(_("copying %s to %s\n") % (f, fd))
210 212 repo.wwrite(fd, repo.wread(f), flags)
211 213
212 214 audit_path = util.path_auditor(repo.root)
213 215
214 216 for a in action:
215 217 f, m = a[:2]
216 218 if f and f[0] == "/":
217 219 continue
218 220 if m == "r": # remove
219 221 repo.ui.note(_("removing %s\n") % f)
220 222 audit_path(f)
221 223 try:
222 224 util.unlink(repo.wjoin(f))
223 225 except OSError, inst:
224 226 if inst.errno != errno.ENOENT:
225 227 repo.ui.warn(_("update failed to remove %s: %s!\n") %
226 228 (f, inst.strerror))
227 229 removed += 1
228 230 elif m == "m": # merge
229 231 f2, fd, flags, move = a[2:]
230 232 r = filemerge.filemerge(repo, f, fd, f2, wctx, mctx)
231 233 if r > 0:
232 234 unresolved += 1
233 235 else:
234 236 if r is None:
235 237 updated += 1
236 238 else:
237 239 merged += 1
238 240 util.set_flags(repo.wjoin(fd), flags)
239 241 if f != fd and move and util.lexists(repo.wjoin(f)):
240 242 repo.ui.debug(_("removing %s\n") % f)
241 243 os.unlink(repo.wjoin(f))
242 244 elif m == "g": # get
243 245 flags = a[2]
244 246 repo.ui.note(_("getting %s\n") % f)
245 247 t = mctx.filectx(f).data()
246 248 repo.wwrite(f, t, flags)
247 249 updated += 1
248 250 elif m == "d": # directory rename
249 251 f2, fd, flags = a[2:]
250 252 if f:
251 253 repo.ui.note(_("moving %s to %s\n") % (f, fd))
252 254 t = wctx.filectx(f).data()
253 255 repo.wwrite(fd, t, flags)
254 256 util.unlink(repo.wjoin(f))
255 257 if f2:
256 258 repo.ui.note(_("getting %s to %s\n") % (f2, fd))
257 259 t = mctx.filectx(f2).data()
258 260 repo.wwrite(fd, t, flags)
259 261 updated += 1
260 262 elif m == "dr": # divergent renames
261 263 fl = a[2]
262 264 repo.ui.warn("warning: detected divergent renames of %s to:\n" % f)
263 265 for nf in fl:
264 266 repo.ui.warn(" %s\n" % nf)
265 267 elif m == "e": # exec
266 268 flags = a[2]
267 269 util.set_flags(repo.wjoin(f), flags)
268 270
269 271 return updated, merged, removed, unresolved
270 272
271 273 def recordupdates(repo, action, branchmerge):
272 274 "record merge actions to the dirstate"
273 275
274 276 for a in action:
275 277 f, m = a[:2]
276 278 if m == "r": # remove
277 279 if branchmerge:
278 280 repo.dirstate.remove(f)
279 281 else:
280 282 repo.dirstate.forget(f)
281 283 elif m == "f": # forget
282 284 repo.dirstate.forget(f)
283 285 elif m in "ge": # get or exec change
284 286 if branchmerge:
285 287 repo.dirstate.normaldirty(f)
286 288 else:
287 289 repo.dirstate.normal(f)
288 290 elif m == "m": # merge
289 291 f2, fd, flag, move = a[2:]
290 292 if branchmerge:
291 293 # We've done a branch merge, mark this file as merged
292 294 # so that we properly record the merger later
293 295 repo.dirstate.merge(fd)
294 296 if f != f2: # copy/rename
295 297 if move:
296 298 repo.dirstate.remove(f)
297 299 if f != fd:
298 300 repo.dirstate.copy(f, fd)
299 301 else:
300 302 repo.dirstate.copy(f2, fd)
301 303 else:
302 304 # We've update-merged a locally modified file, so
303 305 # we set the dirstate to emulate a normal checkout
304 306 # of that file some time in the past. Thus our
305 307 # merge will appear as a normal local file
306 308 # modification.
307 309 repo.dirstate.normallookup(fd)
308 310 if move:
309 311 repo.dirstate.forget(f)
310 312 elif m == "d": # directory rename
311 313 f2, fd, flag = a[2:]
312 314 if not f2 and f not in repo.dirstate:
313 315 # untracked file moved
314 316 continue
315 317 if branchmerge:
316 318 repo.dirstate.add(fd)
317 319 if f:
318 320 repo.dirstate.remove(f)
319 321 repo.dirstate.copy(f, fd)
320 322 if f2:
321 323 repo.dirstate.copy(f2, fd)
322 324 else:
323 325 repo.dirstate.normal(fd)
324 326 if f:
325 327 repo.dirstate.forget(f)
326 328
327 329 def update(repo, node, branchmerge, force, partial):
328 330 """
329 331 Perform a merge between the working directory and the given node
330 332
331 333 branchmerge = whether to merge between branches
332 334 force = whether to force branch merging or file overwriting
333 335 partial = a function to filter file lists (dirstate not updated)
334 336 """
335 337
336 338 wlock = repo.wlock()
337 339 try:
338 340 wc = repo.workingctx()
339 341 if node is None:
340 342 # tip of current branch
341 343 try:
342 344 node = repo.branchtags()[wc.branch()]
343 345 except KeyError:
344 346 if wc.branch() == "default": # no default branch!
345 347 node = repo.lookup("tip") # update to tip
346 348 else:
347 349 raise util.Abort(_("branch %s not found") % wc.branch())
348 350 overwrite = force and not branchmerge
349 351 pl = wc.parents()
350 352 p1, p2 = pl[0], repo.changectx(node)
351 353 pa = p1.ancestor(p2)
352 354 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
353 355 fastforward = False
354 356
355 357 ### check phase
356 358 if not overwrite and len(pl) > 1:
357 359 raise util.Abort(_("outstanding uncommitted merges"))
358 360 if branchmerge:
359 361 if pa == p2:
360 362 raise util.Abort(_("can't merge with ancestor"))
361 363 elif pa == p1:
362 364 if p1.branch() != p2.branch():
363 365 fastforward = True
364 366 else:
365 367 raise util.Abort(_("nothing to merge (use 'hg update'"
366 368 " or check 'hg heads')"))
367 369 if not force and (wc.files() or wc.deleted()):
368 370 raise util.Abort(_("outstanding uncommitted changes"))
369 371 elif not overwrite:
370 372 if pa == p1 or pa == p2: # linear
371 373 pass # all good
372 374 elif p1.branch() == p2.branch():
373 375 if wc.files() or wc.deleted():
374 376 raise util.Abort(_("crosses branches (use 'hg merge' or "
375 377 "'hg update -C' to discard changes)"))
376 378 raise util.Abort(_("crosses branches (use 'hg merge' "
377 379 "or 'hg update -C')"))
378 380 elif wc.files() or wc.deleted():
379 381 raise util.Abort(_("crosses named branches (use "
380 382 "'hg update -C' to discard changes)"))
381 383 else:
382 384 # Allow jumping branches if there are no changes
383 385 overwrite = True
384 386
385 387 ### calculate phase
386 388 action = []
387 389 if not force:
388 390 _checkunknown(wc, p2)
389 391 if not util.checkfolding(repo.path):
390 392 _checkcollision(p2)
391 393 action += _forgetremoved(wc, p2, branchmerge)
392 394 action += manifestmerge(repo, wc, p2, pa, overwrite, partial)
393 395
394 396 ### apply phase
395 397 if not branchmerge: # just jump to the new rev
396 398 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
397 399 if not partial:
398 400 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
399 401
400 402 stats = applyupdates(repo, action, wc, p2)
401 403
402 404 if not partial:
403 405 recordupdates(repo, action, branchmerge)
404 406 repo.dirstate.setparents(fp1, fp2)
405 407 if not branchmerge and not fastforward:
406 408 repo.dirstate.setbranch(p2.branch())
407 409 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
408 410
409 411 return stats
410 412 finally:
411 413 del wlock
@@ -1,94 +1,95 b''
1 1 # should complain
2 2 abort: please specify a revision to backout
3 3 abort: please specify just one revision
4 4 # basic operation
5 5 adding a
6 6 reverting a
7 7 changeset 2:2929462c3dff backs out changeset 1:a820f4f40a57
8 8 a
9 9 # file that was removed is recreated
10 10 adding a
11 11 adding a
12 12 changeset 2:de31bdc76c0d backs out changeset 1:76862dcce372
13 13 content
14 14 # backout of backout is as if nothing happened
15 15 removing a
16 16 changeset 3:7f6d0f120113 backs out changeset 2:de31bdc76c0d
17 17 cat: a: No such file or directory
18 18 # across branch
19 19 adding a
20 20 adding b
21 21 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
22 22 abort: cannot back out change on a different branch
23 23 adding c
24 24 created new head
25 25 abort: cannot back out change on a different branch
26 26 # backout with merge
27 27 adding a
28 28 reverting a
29 29 created new head
30 30 changeset 3:26b8ccb9ad91 backs out changeset 1:5a50a024c182
31 31 merging with changeset 3:26b8ccb9ad91
32 32 merging a
33 33 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
34 34 (branch merge, don't forget to commit)
35 35 line 1
36 36 line 2
37 37 line 3
38 38 # backout should not back out subsequent changesets
39 39 adding a
40 40 adding b
41 41 reverting a
42 42 created new head
43 43 changeset 3:3202beb76721 backs out changeset 1:22bca4c721e5
44 44 the backout changeset is a new head - do not forget to merge
45 45 (use "backout --merge" if you want to auto-merge)
46 46 b
47 47 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
48 48 adding a
49 49 adding b
50 50 adding c
51 51 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
52 52 adding d
53 53 created new head
54 54 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
55 55 (branch merge, don't forget to commit)
56 56 # backout of merge should fail
57 57 abort: cannot back out a merge changeset without --parent
58 58 # backout of merge with bad parent should fail
59 59 abort: cb9a9f314b8b is not a parent of b2f3bb92043e
60 60 # backout of non-merge with parent should fail
61 61 abort: cannot use --parent on non-merge changeset
62 62 # backout with valid parent should be ok
63 63 removing d
64 64 changeset 5:10e5328c8435 backs out changeset 4:b2f3bb92043e
65 65 rolling back last transaction
66 66 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
67 67 removing c
68 68 changeset 5:033590168430 backs out changeset 4:b2f3bb92043e
69 69 # named branches
70 70 adding default
71 71 marked working directory as branch branch1
72 72 adding file1
73 73 marked working directory as branch branch2
74 74 adding file2
75 75 removing file1
76 76 created new head
77 changeset 3:f1c642b1d8e5 backs out changeset 1:bf1602f437f3
77 changeset 3:d4e8f6db59fb backs out changeset 1:bf1602f437f3
78 78 the backout changeset is a new head - do not forget to merge
79 79 (use "backout --merge" if you want to auto-merge)
80 80 % on branch2 with branch1 not merged, so file1 should still exist:
81 81 45bbcd363bf0 (branch2)
82 82 C default
83 83 C file1
84 84 C file2
85 85 % on branch2 with branch1 merged, so file1 should be gone:
86 86 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
87 87 (branch merge, don't forget to commit)
88 21d4dc6f9a41 (branch2) tip
88 22149cdde76d (branch2) tip
89 89 C default
90 90 C file2
91 91 % on branch1, so no file1 and file2:
92 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
93 f1c642b1d8e5 (branch1)
92 1 files updated, 0 files merged, 1 files removed, 0 files unresolved
93 bf1602f437f3 (branch1)
94 94 C default
95 C file1
@@ -1,68 +1,76 b''
1 1 #!/bin/sh
2 2
3 3 add()
4 4 {
5 5 echo $2 >> $1
6 6 }
7 7
8 8 hg init t
9 9 cd t
10 10
11 11 # set up a boring main branch
12 12 add a a
13 13 hg add a
14 mkdir x
15 add x/x x
16 hg add x/x
14 17 hg ci -m0
15 18
16 19 add a m1
17 20 hg ci -m1
18 21
19 22 add a m2
23 add x/y y1
24 hg add x/y
20 25 hg ci -m2
21 26
22 27 show()
23 28 {
24 29 echo "- $2: $1"
25 30 hg st -C $1
26 31 echo
27 32 hg diff --git $1
28 33 echo
29 34 }
30 35
31 36 count=0
32 37 # make a new branch and get diff/status output
33 38 # $1 - first commit
34 39 # $2 - second commit
35 40 # $3 - working dir action
36 41 # $4 - test description
37 42 tb()
38 43 {
39 44 hg co -q -C 0
40 45
41 46 add a $count
42 47 count=`expr $count + 1`
43 48 hg ci -m "t0"
44 49 $1
45 50 hg ci -m "t1"
46 51 $2
47 52 hg ci -m "t2"
48 53 $3
49 54
50 55 echo "** $4 **"
51 56 echo "** $1 / $2 / $3"
52 57 show "" "working to parent"
53 58 show "--rev 0" "working to root"
54 59 show "--rev 2" "working to branch"
55 60 show "--rev 0 --rev ." "root to parent"
56 61 show "--rev . --rev 0" "parent to root"
57 62 show "--rev 2 --rev ." "branch to parent"
58 63 show "--rev . --rev 2" "parent to branch"
59 64 echo
60 65 }
61 66
67
62 68 tb "add a a1" "add a a2" "hg mv a b" "rename in working dir"
63 69 tb "add a a1" "add a a2" "hg cp a b" "copy in working dir"
64 70 tb "hg mv a b" "add b b1" "add b w" "single rename"
65 71 tb "hg cp a b" "add b b1" "add a w" "single copy"
66 72 tb "hg mv a b" "hg mv b c" "hg mv c d" "rename chain"
67 73 tb "hg cp a b" "hg cp b c" "hg cp c d" "copy chain"
68 74 tb "add a a1" "hg mv a b" "hg mv b a" "circular rename"
75
76 tb "hg mv x y" "add y/x x1" "add y/x x2" "directory move"
@@ -1,902 +1,1220 b''
1 1 created new head
2 2 ** rename in working dir **
3 3 ** add a a1 / add a a2 / hg mv a b
4 4 - working to parent:
5 5 A b
6 6 a
7 7 R a
8 8
9 9 diff --git a/a b/b
10 10 rename from a
11 11 rename to b
12 12
13 13 - working to root: --rev 0
14 14 A b
15 15 a
16 16 R a
17 17
18 18 diff --git a/a b/b
19 19 rename from a
20 20 rename to b
21 21 --- a/a
22 22 +++ b/b
23 23 @@ -1,1 +1,4 @@
24 24 a
25 25 +0
26 26 +a1
27 27 +a2
28 28
29 29 - working to branch: --rev 2
30 30 A b
31 31 a
32 32 R a
33 R x/y
33 34
34 35 diff --git a/a b/b
35 36 rename from a
36 37 rename to b
37 38 --- a/a
38 39 +++ b/b
39 40 @@ -1,3 +1,4 @@
40 41 a
41 42 -m1
42 43 -m2
43 44 +0
44 45 +a1
45 46 +a2
47 diff --git a/x/y b/x/y
48 deleted file mode 100644
49 --- a/x/y
50 +++ /dev/null
51 @@ -1,1 +0,0 @@
52 -y1
46 53
47 54 - root to parent: --rev 0 --rev .
48 55 M a
49 56
50 57 diff --git a/a b/a
51 58 --- a/a
52 59 +++ b/a
53 60 @@ -1,1 +1,4 @@
54 61 a
55 62 +0
56 63 +a1
57 64 +a2
58 65
59 66 - parent to root: --rev . --rev 0
60 67 M a
61 68
62 69 diff --git a/a b/a
63 70 --- a/a
64 71 +++ b/a
65 72 @@ -1,4 +1,1 @@
66 73 a
67 74 -0
68 75 -a1
69 76 -a2
70 77
71 78 - branch to parent: --rev 2 --rev .
72 79 M a
80 R x/y
73 81
74 82 diff --git a/a b/a
75 83 --- a/a
76 84 +++ b/a
77 85 @@ -1,3 +1,4 @@
78 86 a
79 87 -m1
80 88 -m2
81 89 +0
82 90 +a1
83 91 +a2
92 diff --git a/x/y b/x/y
93 deleted file mode 100644
94 --- a/x/y
95 +++ /dev/null
96 @@ -1,1 +0,0 @@
97 -y1
84 98
85 99 - parent to branch: --rev . --rev 2
86 100 M a
101 A x/y
87 102
88 103 diff --git a/a b/a
89 104 --- a/a
90 105 +++ b/a
91 106 @@ -1,4 +1,3 @@
92 107 a
93 108 -0
94 109 -a1
95 110 -a2
96 111 +m1
97 112 +m2
113 diff --git a/x/y b/x/y
114 new file mode 100644
115 --- /dev/null
116 +++ b/x/y
117 @@ -0,0 +1,1 @@
118 +y1
98 119
99 120
100 121 created new head
101 122 ** copy in working dir **
102 123 ** add a a1 / add a a2 / hg cp a b
103 124 - working to parent:
104 125 A b
105 126 a
106 127
107 128 diff --git a/a b/b
108 129 copy from a
109 130 copy to b
110 131
111 132 - working to root: --rev 0
112 133 M a
113 134 A b
114 135 a
115 136
116 137 diff --git a/a b/a
117 138 --- a/a
118 139 +++ b/a
119 140 @@ -1,1 +1,4 @@
120 141 a
121 142 +1
122 143 +a1
123 144 +a2
124 145 diff --git a/a b/b
125 146 copy from a
126 147 copy to b
127 148 --- a/a
128 149 +++ b/b
129 150 @@ -1,1 +1,4 @@
130 151 a
131 152 +1
132 153 +a1
133 154 +a2
134 155
135 156 - working to branch: --rev 2
136 157 M a
137 158 A b
138 159 a
160 R x/y
139 161
140 162 diff --git a/a b/a
141 163 --- a/a
142 164 +++ b/a
143 165 @@ -1,3 +1,4 @@
144 166 a
145 167 -m1
146 168 -m2
147 169 +1
148 170 +a1
149 171 +a2
150 172 diff --git a/a b/b
151 173 copy from a
152 174 copy to b
153 175 --- a/a
154 176 +++ b/b
155 177 @@ -1,3 +1,4 @@
156 178 a
157 179 -m1
158 180 -m2
159 181 +1
160 182 +a1
161 183 +a2
184 diff --git a/x/y b/x/y
185 deleted file mode 100644
186 --- a/x/y
187 +++ /dev/null
188 @@ -1,1 +0,0 @@
189 -y1
162 190
163 191 - root to parent: --rev 0 --rev .
164 192 M a
165 193
166 194 diff --git a/a b/a
167 195 --- a/a
168 196 +++ b/a
169 197 @@ -1,1 +1,4 @@
170 198 a
171 199 +1
172 200 +a1
173 201 +a2
174 202
175 203 - parent to root: --rev . --rev 0
176 204 M a
177 205
178 206 diff --git a/a b/a
179 207 --- a/a
180 208 +++ b/a
181 209 @@ -1,4 +1,1 @@
182 210 a
183 211 -1
184 212 -a1
185 213 -a2
186 214
187 215 - branch to parent: --rev 2 --rev .
188 216 M a
217 R x/y
189 218
190 219 diff --git a/a b/a
191 220 --- a/a
192 221 +++ b/a
193 222 @@ -1,3 +1,4 @@
194 223 a
195 224 -m1
196 225 -m2
197 226 +1
198 227 +a1
199 228 +a2
229 diff --git a/x/y b/x/y
230 deleted file mode 100644
231 --- a/x/y
232 +++ /dev/null
233 @@ -1,1 +0,0 @@
234 -y1
200 235
201 236 - parent to branch: --rev . --rev 2
202 237 M a
238 A x/y
203 239
204 240 diff --git a/a b/a
205 241 --- a/a
206 242 +++ b/a
207 243 @@ -1,4 +1,3 @@
208 244 a
209 245 -1
210 246 -a1
211 247 -a2
212 248 +m1
213 249 +m2
250 diff --git a/x/y b/x/y
251 new file mode 100644
252 --- /dev/null
253 +++ b/x/y
254 @@ -0,0 +1,1 @@
255 +y1
214 256
215 257
216 258 created new head
217 259 ** single rename **
218 260 ** hg mv a b / add b b1 / add b w
219 261 - working to parent:
220 262 M b
221 263
222 264 diff --git a/b b/b
223 265 --- a/b
224 266 +++ b/b
225 267 @@ -1,3 +1,4 @@
226 268 a
227 269 2
228 270 b1
229 271 +w
230 272
231 273 - working to root: --rev 0
232 274 A b
233 275 a
234 276 R a
235 277
236 278 diff --git a/a b/b
237 279 rename from a
238 280 rename to b
239 281 --- a/a
240 282 +++ b/b
241 283 @@ -1,1 +1,4 @@
242 284 a
243 285 +2
244 286 +b1
245 287 +w
246 288
247 289 - working to branch: --rev 2
248 290 A b
249 291 a
250 292 R a
293 R x/y
251 294
252 295 diff --git a/a b/b
253 296 rename from a
254 297 rename to b
255 298 --- a/a
256 299 +++ b/b
257 300 @@ -1,3 +1,4 @@
258 301 a
259 302 -m1
260 303 -m2
261 304 +2
262 305 +b1
263 306 +w
307 diff --git a/x/y b/x/y
308 deleted file mode 100644
309 --- a/x/y
310 +++ /dev/null
311 @@ -1,1 +0,0 @@
312 -y1
264 313
265 314 - root to parent: --rev 0 --rev .
266 315 A b
267 316 a
268 317 R a
269 318
270 319 diff --git a/a b/b
271 320 rename from a
272 321 rename to b
273 322 --- a/a
274 323 +++ b/b
275 324 @@ -1,1 +1,3 @@
276 325 a
277 326 +2
278 327 +b1
279 328
280 329 - parent to root: --rev . --rev 0
281 330 A a
282 331 b
283 332 R b
284 333
285 334 diff --git a/b b/a
286 335 rename from b
287 336 rename to a
288 337 --- a/b
289 338 +++ b/a
290 339 @@ -1,3 +1,1 @@
291 340 a
292 341 -2
293 342 -b1
294 343
295 344 - branch to parent: --rev 2 --rev .
296 345 A b
297 346 a
298 347 R a
348 R x/y
299 349
300 350 diff --git a/a b/b
301 351 rename from a
302 352 rename to b
303 353 --- a/a
304 354 +++ b/b
305 355 @@ -1,3 +1,3 @@
306 356 a
307 357 -m1
308 358 -m2
309 359 +2
310 360 +b1
361 diff --git a/x/y b/x/y
362 deleted file mode 100644
363 --- a/x/y
364 +++ /dev/null
365 @@ -1,1 +0,0 @@
366 -y1
311 367
312 368 - parent to branch: --rev . --rev 2
313 369 A a
314 370 b
371 A x/y
315 372 R b
316 373
317 374 diff --git a/b b/a
318 375 rename from b
319 376 rename to a
320 377 --- a/b
321 378 +++ b/a
322 379 @@ -1,3 +1,3 @@
323 380 a
324 381 -2
325 382 -b1
326 383 +m1
327 384 +m2
385 diff --git a/x/y b/x/y
386 new file mode 100644
387 --- /dev/null
388 +++ b/x/y
389 @@ -0,0 +1,1 @@
390 +y1
328 391
329 392
330 393 created new head
331 394 ** single copy **
332 395 ** hg cp a b / add b b1 / add a w
333 396 - working to parent:
334 397 M a
335 398
336 399 diff --git a/a b/a
337 400 --- a/a
338 401 +++ b/a
339 402 @@ -1,2 +1,3 @@
340 403 a
341 404 3
342 405 +w
343 406
344 407 - working to root: --rev 0
345 408 M a
346 409 A b
347 410 a
348 411
349 412 diff --git a/a b/a
350 413 --- a/a
351 414 +++ b/a
352 415 @@ -1,1 +1,3 @@
353 416 a
354 417 +3
355 418 +w
356 419 diff --git a/a b/b
357 420 copy from a
358 421 copy to b
359 422 --- a/a
360 423 +++ b/b
361 424 @@ -1,1 +1,3 @@
362 425 a
363 426 +3
364 427 +b1
365 428
366 429 - working to branch: --rev 2
367 430 M a
368 431 A b
369 432 a
433 R x/y
370 434
371 435 diff --git a/a b/a
372 436 --- a/a
373 437 +++ b/a
374 438 @@ -1,3 +1,3 @@
375 439 a
376 440 -m1
377 441 -m2
378 442 +3
379 443 +w
380 444 diff --git a/a b/b
381 445 copy from a
382 446 copy to b
383 447 --- a/a
384 448 +++ b/b
385 449 @@ -1,3 +1,3 @@
386 450 a
387 451 -m1
388 452 -m2
389 453 +3
390 454 +b1
455 diff --git a/x/y b/x/y
456 deleted file mode 100644
457 --- a/x/y
458 +++ /dev/null
459 @@ -1,1 +0,0 @@
460 -y1
391 461
392 462 - root to parent: --rev 0 --rev .
393 463 M a
394 464 A b
395 465 a
396 466
397 467 diff --git a/a b/a
398 468 --- a/a
399 469 +++ b/a
400 470 @@ -1,1 +1,2 @@
401 471 a
402 472 +3
403 473 diff --git a/a b/b
404 474 copy from a
405 475 copy to b
406 476 --- a/a
407 477 +++ b/b
408 478 @@ -1,1 +1,3 @@
409 479 a
410 480 +3
411 481 +b1
412 482
413 483 - parent to root: --rev . --rev 0
414 484 M a
415 485 R b
416 486
417 487 diff --git a/a b/a
418 488 --- a/a
419 489 +++ b/a
420 490 @@ -1,2 +1,1 @@
421 491 a
422 492 -3
423 493 diff --git a/b b/b
424 494 deleted file mode 100644
425 495 --- a/b
426 496 +++ /dev/null
427 497 @@ -1,3 +0,0 @@
428 498 -a
429 499 -3
430 500 -b1
431 501
432 502 - branch to parent: --rev 2 --rev .
433 503 M a
434 504 A b
435 505 a
506 R x/y
436 507
437 508 diff --git a/a b/a
438 509 --- a/a
439 510 +++ b/a
440 511 @@ -1,3 +1,2 @@
441 512 a
442 513 -m1
443 514 -m2
444 515 +3
445 516 diff --git a/a b/b
446 517 copy from a
447 518 copy to b
448 519 --- a/a
449 520 +++ b/b
450 521 @@ -1,3 +1,3 @@
451 522 a
452 523 -m1
453 524 -m2
454 525 +3
455 526 +b1
527 diff --git a/x/y b/x/y
528 deleted file mode 100644
529 --- a/x/y
530 +++ /dev/null
531 @@ -1,1 +0,0 @@
532 -y1
456 533
457 534 - parent to branch: --rev . --rev 2
458 535 M a
536 A x/y
459 537 R b
460 538
461 539 diff --git a/a b/a
462 540 --- a/a
463 541 +++ b/a
464 542 @@ -1,2 +1,3 @@
465 543 a
466 544 -3
467 545 +m1
468 546 +m2
469 547 diff --git a/b b/b
470 548 deleted file mode 100644
471 549 --- a/b
472 550 +++ /dev/null
473 551 @@ -1,3 +0,0 @@
474 552 -a
475 553 -3
476 554 -b1
555 diff --git a/x/y b/x/y
556 new file mode 100644
557 --- /dev/null
558 +++ b/x/y
559 @@ -0,0 +1,1 @@
560 +y1
477 561
478 562
479 563 created new head
480 564 ** rename chain **
481 565 ** hg mv a b / hg mv b c / hg mv c d
482 566 - working to parent:
483 567 A d
484 568 c
485 569 R c
486 570
487 571 diff --git a/c b/d
488 572 rename from c
489 573 rename to d
490 574
491 575 - working to root: --rev 0
492 576 A d
493 577 a
494 578 R a
495 579
496 580 diff --git a/a b/d
497 581 rename from a
498 582 rename to d
499 583 --- a/a
500 584 +++ b/d
501 585 @@ -1,1 +1,2 @@
502 586 a
503 587 +4
504 588
505 589 - working to branch: --rev 2
506 590 A d
507 591 a
508 592 R a
593 R x/y
509 594
510 595 diff --git a/a b/d
511 596 rename from a
512 597 rename to d
513 598 --- a/a
514 599 +++ b/d
515 600 @@ -1,3 +1,2 @@
516 601 a
517 602 -m1
518 603 -m2
519 604 +4
605 diff --git a/x/y b/x/y
606 deleted file mode 100644
607 --- a/x/y
608 +++ /dev/null
609 @@ -1,1 +0,0 @@
610 -y1
520 611
521 612 - root to parent: --rev 0 --rev .
522 613 A c
523 614 a
524 615 R a
525 616
526 617 diff --git a/a b/c
527 618 rename from a
528 619 rename to c
529 620 --- a/a
530 621 +++ b/c
531 622 @@ -1,1 +1,2 @@
532 623 a
533 624 +4
534 625
535 626 - parent to root: --rev . --rev 0
536 627 A a
537 628 c
538 629 R c
539 630
540 631 diff --git a/c b/a
541 632 rename from c
542 633 rename to a
543 634 --- a/c
544 635 +++ b/a
545 636 @@ -1,2 +1,1 @@
546 637 a
547 638 -4
548 639
549 640 - branch to parent: --rev 2 --rev .
550 641 A c
551 642 a
552 643 R a
644 R x/y
553 645
554 646 diff --git a/a b/c
555 647 rename from a
556 648 rename to c
557 649 --- a/a
558 650 +++ b/c
559 651 @@ -1,3 +1,2 @@
560 652 a
561 653 -m1
562 654 -m2
563 655 +4
656 diff --git a/x/y b/x/y
657 deleted file mode 100644
658 --- a/x/y
659 +++ /dev/null
660 @@ -1,1 +0,0 @@
661 -y1
564 662
565 663 - parent to branch: --rev . --rev 2
566 664 A a
567 665 c
666 A x/y
568 667 R c
569 668
570 669 diff --git a/c b/a
571 670 rename from c
572 671 rename to a
573 672 --- a/c
574 673 +++ b/a
575 674 @@ -1,2 +1,3 @@
576 675 a
577 676 -4
578 677 +m1
579 678 +m2
679 diff --git a/x/y b/x/y
680 new file mode 100644
681 --- /dev/null
682 +++ b/x/y
683 @@ -0,0 +1,1 @@
684 +y1
580 685
581 686
582 687 created new head
583 688 ** copy chain **
584 689 ** hg cp a b / hg cp b c / hg cp c d
585 690 - working to parent:
586 691 A d
587 692 c
588 693
589 694 diff --git a/c b/d
590 695 copy from c
591 696 copy to d
592 697
593 698 - working to root: --rev 0
594 699 M a
595 700 A b
596 701 a
597 702 A c
598 703 a
599 704 A d
600 705 a
601 706
602 707 diff --git a/a b/a
603 708 --- a/a
604 709 +++ b/a
605 710 @@ -1,1 +1,2 @@
606 711 a
607 712 +5
608 713 diff --git a/a b/b
609 714 copy from a
610 715 copy to b
611 716 --- a/a
612 717 +++ b/b
613 718 @@ -1,1 +1,2 @@
614 719 a
615 720 +5
616 721 diff --git a/a b/c
617 722 copy from a
618 723 copy to c
619 724 --- a/a
620 725 +++ b/c
621 726 @@ -1,1 +1,2 @@
622 727 a
623 728 +5
624 729 diff --git a/a b/d
625 730 copy from a
626 731 copy to d
627 732 --- a/a
628 733 +++ b/d
629 734 @@ -1,1 +1,2 @@
630 735 a
631 736 +5
632 737
633 738 - working to branch: --rev 2
634 739 M a
635 740 A b
636 741 a
637 742 A c
638 743 a
639 744 A d
640 745 a
746 R x/y
641 747
642 748 diff --git a/a b/a
643 749 --- a/a
644 750 +++ b/a
645 751 @@ -1,3 +1,2 @@
646 752 a
647 753 -m1
648 754 -m2
649 755 +5
650 756 diff --git a/a b/b
651 757 copy from a
652 758 copy to b
653 759 --- a/a
654 760 +++ b/b
655 761 @@ -1,3 +1,2 @@
656 762 a
657 763 -m1
658 764 -m2
659 765 +5
660 766 diff --git a/a b/c
661 767 copy from a
662 768 copy to c
663 769 --- a/a
664 770 +++ b/c
665 771 @@ -1,3 +1,2 @@
666 772 a
667 773 -m1
668 774 -m2
669 775 +5
670 776 diff --git a/a b/d
671 777 copy from a
672 778 copy to d
673 779 --- a/a
674 780 +++ b/d
675 781 @@ -1,3 +1,2 @@
676 782 a
677 783 -m1
678 784 -m2
679 785 +5
786 diff --git a/x/y b/x/y
787 deleted file mode 100644
788 --- a/x/y
789 +++ /dev/null
790 @@ -1,1 +0,0 @@
791 -y1
680 792
681 793 - root to parent: --rev 0 --rev .
682 794 M a
683 795 A b
684 796 a
685 797 A c
686 798 a
687 799
688 800 diff --git a/a b/a
689 801 --- a/a
690 802 +++ b/a
691 803 @@ -1,1 +1,2 @@
692 804 a
693 805 +5
694 806 diff --git a/a b/b
695 807 copy from a
696 808 copy to b
697 809 --- a/a
698 810 +++ b/b
699 811 @@ -1,1 +1,2 @@
700 812 a
701 813 +5
702 814 diff --git a/a b/c
703 815 copy from a
704 816 copy to c
705 817 --- a/a
706 818 +++ b/c
707 819 @@ -1,1 +1,2 @@
708 820 a
709 821 +5
710 822
711 823 - parent to root: --rev . --rev 0
712 824 M a
713 825 R b
714 826 R c
715 827
716 828 diff --git a/a b/a
717 829 --- a/a
718 830 +++ b/a
719 831 @@ -1,2 +1,1 @@
720 832 a
721 833 -5
722 834 diff --git a/b b/b
723 835 deleted file mode 100644
724 836 --- a/b
725 837 +++ /dev/null
726 838 @@ -1,2 +0,0 @@
727 839 -a
728 840 -5
729 841 diff --git a/c b/c
730 842 deleted file mode 100644
731 843 --- a/c
732 844 +++ /dev/null
733 845 @@ -1,2 +0,0 @@
734 846 -a
735 847 -5
736 848
737 849 - branch to parent: --rev 2 --rev .
738 850 M a
739 851 A b
740 852 a
741 853 A c
742 854 a
855 R x/y
743 856
744 857 diff --git a/a b/a
745 858 --- a/a
746 859 +++ b/a
747 860 @@ -1,3 +1,2 @@
748 861 a
749 862 -m1
750 863 -m2
751 864 +5
752 865 diff --git a/a b/b
753 866 copy from a
754 867 copy to b
755 868 --- a/a
756 869 +++ b/b
757 870 @@ -1,3 +1,2 @@
758 871 a
759 872 -m1
760 873 -m2
761 874 +5
762 875 diff --git a/a b/c
763 876 copy from a
764 877 copy to c
765 878 --- a/a
766 879 +++ b/c
767 880 @@ -1,3 +1,2 @@
768 881 a
769 882 -m1
770 883 -m2
771 884 +5
885 diff --git a/x/y b/x/y
886 deleted file mode 100644
887 --- a/x/y
888 +++ /dev/null
889 @@ -1,1 +0,0 @@
890 -y1
772 891
773 892 - parent to branch: --rev . --rev 2
774 893 M a
894 A x/y
775 895 R b
776 896 R c
777 897
778 898 diff --git a/a b/a
779 899 --- a/a
780 900 +++ b/a
781 901 @@ -1,2 +1,3 @@
782 902 a
783 903 -5
784 904 +m1
785 905 +m2
786 906 diff --git a/b b/b
787 907 deleted file mode 100644
788 908 --- a/b
789 909 +++ /dev/null
790 910 @@ -1,2 +0,0 @@
791 911 -a
792 912 -5
793 913 diff --git a/c b/c
794 914 deleted file mode 100644
795 915 --- a/c
796 916 +++ /dev/null
797 917 @@ -1,2 +0,0 @@
798 918 -a
799 919 -5
920 diff --git a/x/y b/x/y
921 new file mode 100644
922 --- /dev/null
923 +++ b/x/y
924 @@ -0,0 +1,1 @@
925 +y1
800 926
801 927
802 928 created new head
803 929 ** circular rename **
804 930 ** add a a1 / hg mv a b / hg mv b a
805 931 - working to parent:
806 932 A a
807 933 b
808 934 R b
809 935
810 936 diff --git a/b b/a
811 937 rename from b
812 938 rename to a
813 939
814 940 - working to root: --rev 0
815 941 M a
816 942
817 943 diff --git a/a b/a
818 944 --- a/a
819 945 +++ b/a
820 946 @@ -1,1 +1,3 @@
821 947 a
822 948 +6
823 949 +a1
824 950
825 951 - working to branch: --rev 2
826 952 M a
953 R x/y
827 954
828 955 diff --git a/a b/a
829 956 --- a/a
830 957 +++ b/a
831 958 @@ -1,3 +1,3 @@
832 959 a
833 960 -m1
834 961 -m2
835 962 +6
836 963 +a1
964 diff --git a/x/y b/x/y
965 deleted file mode 100644
966 --- a/x/y
967 +++ /dev/null
968 @@ -1,1 +0,0 @@
969 -y1
837 970
838 971 - root to parent: --rev 0 --rev .
839 972 A b
840 973 a
841 974 R a
842 975
843 976 diff --git a/a b/b
844 977 rename from a
845 978 rename to b
846 979 --- a/a
847 980 +++ b/b
848 981 @@ -1,1 +1,3 @@
849 982 a
850 983 +6
851 984 +a1
852 985
853 986 - parent to root: --rev . --rev 0
854 987 A a
855 988 b
856 989 R b
857 990
858 991 diff --git a/b b/a
859 992 rename from b
860 993 rename to a
861 994 --- a/b
862 995 +++ b/a
863 996 @@ -1,3 +1,1 @@
864 997 a
865 998 -6
866 999 -a1
867 1000
868 1001 - branch to parent: --rev 2 --rev .
869 1002 A b
870 1003 a
871 1004 R a
1005 R x/y
872 1006
873 1007 diff --git a/a b/b
874 1008 rename from a
875 1009 rename to b
876 1010 --- a/a
877 1011 +++ b/b
878 1012 @@ -1,3 +1,3 @@
879 1013 a
880 1014 -m1
881 1015 -m2
882 1016 +6
883 1017 +a1
1018 diff --git a/x/y b/x/y
1019 deleted file mode 100644
1020 --- a/x/y
1021 +++ /dev/null
1022 @@ -1,1 +0,0 @@
1023 -y1
884 1024
885 1025 - parent to branch: --rev . --rev 2
886 1026 A a
887 1027 b
1028 A x/y
888 1029 R b
889 1030
890 1031 diff --git a/b b/a
891 1032 rename from b
892 1033 rename to a
893 1034 --- a/b
894 1035 +++ b/a
895 1036 @@ -1,3 +1,3 @@
896 1037 a
897 1038 -6
898 1039 -a1
899 1040 +m1
900 1041 +m2
1042 diff --git a/x/y b/x/y
1043 new file mode 100644
1044 --- /dev/null
1045 +++ b/x/y
1046 @@ -0,0 +1,1 @@
1047 +y1
901 1048
902 1049
1050 created new head
1051 moving x/x to y/x
1052 ** directory move **
1053 ** hg mv x y / add y/x x1 / add y/x x2
1054 - working to parent:
1055 M y/x
1056
1057 diff --git a/y/x b/y/x
1058 --- a/y/x
1059 +++ b/y/x
1060 @@ -1,2 +1,3 @@
1061 x
1062 x1
1063 +x2
1064
1065 - working to root: --rev 0
1066 M a
1067 A y/x
1068 x/x
1069 R x/x
1070
1071 diff --git a/a b/a
1072 --- a/a
1073 +++ b/a
1074 @@ -1,1 +1,2 @@
1075 a
1076 +7
1077 diff --git a/x/x b/y/x
1078 rename from x/x
1079 rename to y/x
1080 --- a/x/x
1081 +++ b/y/x
1082 @@ -1,1 +1,3 @@
1083 x
1084 +x1
1085 +x2
1086
1087 - working to branch: --rev 2
1088 M a
1089 A y/x
1090 x/x
1091 R x/x
1092 R x/y
1093
1094 diff --git a/a b/a
1095 --- a/a
1096 +++ b/a
1097 @@ -1,3 +1,2 @@
1098 a
1099 -m1
1100 -m2
1101 +7
1102 diff --git a/x/y b/x/y
1103 deleted file mode 100644
1104 --- a/x/y
1105 +++ /dev/null
1106 @@ -1,1 +0,0 @@
1107 -y1
1108 diff --git a/x/x b/y/x
1109 rename from x/x
1110 rename to y/x
1111 --- a/x/x
1112 +++ b/y/x
1113 @@ -1,1 +1,3 @@
1114 x
1115 +x1
1116 +x2
1117
1118 - root to parent: --rev 0 --rev .
1119 M a
1120 A y/x
1121 x/x
1122 R x/x
1123
1124 diff --git a/a b/a
1125 --- a/a
1126 +++ b/a
1127 @@ -1,1 +1,2 @@
1128 a
1129 +7
1130 diff --git a/x/x b/y/x
1131 rename from x/x
1132 rename to y/x
1133 --- a/x/x
1134 +++ b/y/x
1135 @@ -1,1 +1,2 @@
1136 x
1137 +x1
1138
1139 - parent to root: --rev . --rev 0
1140 M a
1141 A x/x
1142 y/x
1143 R y/x
1144
1145 diff --git a/a b/a
1146 --- a/a
1147 +++ b/a
1148 @@ -1,2 +1,1 @@
1149 a
1150 -7
1151 diff --git a/y/x b/x/x
1152 rename from y/x
1153 rename to x/x
1154 --- a/y/x
1155 +++ b/x/x
1156 @@ -1,2 +1,1 @@
1157 x
1158 -x1
1159
1160 - branch to parent: --rev 2 --rev .
1161 M a
1162 A y/x
1163 x/x
1164 R x/x
1165 R x/y
1166
1167 diff --git a/a b/a
1168 --- a/a
1169 +++ b/a
1170 @@ -1,3 +1,2 @@
1171 a
1172 -m1
1173 -m2
1174 +7
1175 diff --git a/x/y b/x/y
1176 deleted file mode 100644
1177 --- a/x/y
1178 +++ /dev/null
1179 @@ -1,1 +0,0 @@
1180 -y1
1181 diff --git a/x/x b/y/x
1182 rename from x/x
1183 rename to y/x
1184 --- a/x/x
1185 +++ b/y/x
1186 @@ -1,1 +1,2 @@
1187 x
1188 +x1
1189
1190 - parent to branch: --rev . --rev 2
1191 M a
1192 A x/x
1193 y/x
1194 A x/y
1195 R y/x
1196
1197 diff --git a/a b/a
1198 --- a/a
1199 +++ b/a
1200 @@ -1,2 +1,3 @@
1201 a
1202 -7
1203 +m1
1204 +m2
1205 diff --git a/y/x b/x/x
1206 rename from y/x
1207 rename to x/x
1208 --- a/y/x
1209 +++ b/x/x
1210 @@ -1,2 +1,1 @@
1211 x
1212 -x1
1213 diff --git a/x/y b/x/y
1214 new file mode 100644
1215 --- /dev/null
1216 +++ b/x/y
1217 @@ -0,0 +1,1 @@
1218 +y1
1219
1220
General Comments 0
You need to be logged in to leave comments. Login now