##// END OF EJS Templates
Spelling fix: "commited" -> "committed"
mcmillen@cs.cmu.edu -
r1995:2da2d468 default
parent child Browse files
Show More
@@ -1,3309 +1,3309 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from demandload import demandload
8 from demandload import demandload
9 from node import *
9 from node import *
10 from i18n import gettext as _
10 from i18n import gettext as _
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
11 demandload(globals(), "os re sys signal shutil imp urllib pdb")
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
12 demandload(globals(), "fancyopts ui hg util lock revlog templater bundlerepo")
13 demandload(globals(), "fnmatch hgweb mdiff random signal tempfile time")
13 demandload(globals(), "fnmatch hgweb mdiff random signal tempfile time")
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
14 demandload(globals(), "traceback errno socket version struct atexit sets bz2")
15 demandload(globals(), "changegroup")
15 demandload(globals(), "changegroup")
16
16
17 class UnknownCommand(Exception):
17 class UnknownCommand(Exception):
18 """Exception raised if command is not in the command table."""
18 """Exception raised if command is not in the command table."""
19 class AmbiguousCommand(Exception):
19 class AmbiguousCommand(Exception):
20 """Exception raised if command shortcut matches more than one command."""
20 """Exception raised if command shortcut matches more than one command."""
21
21
22 def filterfiles(filters, files):
22 def filterfiles(filters, files):
23 l = [x for x in files if x in filters]
23 l = [x for x in files if x in filters]
24
24
25 for t in filters:
25 for t in filters:
26 if t and t[-1] != "/":
26 if t and t[-1] != "/":
27 t += "/"
27 t += "/"
28 l += [x for x in files if x.startswith(t)]
28 l += [x for x in files if x.startswith(t)]
29 return l
29 return l
30
30
31 def relpath(repo, args):
31 def relpath(repo, args):
32 cwd = repo.getcwd()
32 cwd = repo.getcwd()
33 if cwd:
33 if cwd:
34 return [util.normpath(os.path.join(cwd, x)) for x in args]
34 return [util.normpath(os.path.join(cwd, x)) for x in args]
35 return args
35 return args
36
36
37 def matchpats(repo, pats=[], opts={}, head=''):
37 def matchpats(repo, pats=[], opts={}, head=''):
38 cwd = repo.getcwd()
38 cwd = repo.getcwd()
39 if not pats and cwd:
39 if not pats and cwd:
40 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
40 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
41 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
41 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
42 cwd = ''
42 cwd = ''
43 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
43 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
44 opts.get('exclude'), head)
44 opts.get('exclude'), head)
45
45
46 def makewalk(repo, pats, opts, node=None, head=''):
46 def makewalk(repo, pats, opts, node=None, head=''):
47 files, matchfn, anypats = matchpats(repo, pats, opts, head)
47 files, matchfn, anypats = matchpats(repo, pats, opts, head)
48 exact = dict(zip(files, files))
48 exact = dict(zip(files, files))
49 def walk():
49 def walk():
50 for src, fn in repo.walk(node=node, files=files, match=matchfn):
50 for src, fn in repo.walk(node=node, files=files, match=matchfn):
51 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
51 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
52 return files, matchfn, walk()
52 return files, matchfn, walk()
53
53
54 def walk(repo, pats, opts, node=None, head=''):
54 def walk(repo, pats, opts, node=None, head=''):
55 files, matchfn, results = makewalk(repo, pats, opts, node, head)
55 files, matchfn, results = makewalk(repo, pats, opts, node, head)
56 for r in results:
56 for r in results:
57 yield r
57 yield r
58
58
59 def walkchangerevs(ui, repo, pats, opts):
59 def walkchangerevs(ui, repo, pats, opts):
60 '''Iterate over files and the revs they changed in.
60 '''Iterate over files and the revs they changed in.
61
61
62 Callers most commonly need to iterate backwards over the history
62 Callers most commonly need to iterate backwards over the history
63 it is interested in. Doing so has awful (quadratic-looking)
63 it is interested in. Doing so has awful (quadratic-looking)
64 performance, so we use iterators in a "windowed" way.
64 performance, so we use iterators in a "windowed" way.
65
65
66 We walk a window of revisions in the desired order. Within the
66 We walk a window of revisions in the desired order. Within the
67 window, we first walk forwards to gather data, then in the desired
67 window, we first walk forwards to gather data, then in the desired
68 order (usually backwards) to display it.
68 order (usually backwards) to display it.
69
69
70 This function returns an (iterator, getchange, matchfn) tuple. The
70 This function returns an (iterator, getchange, matchfn) tuple. The
71 getchange function returns the changelog entry for a numeric
71 getchange function returns the changelog entry for a numeric
72 revision. The iterator yields 3-tuples. They will be of one of
72 revision. The iterator yields 3-tuples. They will be of one of
73 the following forms:
73 the following forms:
74
74
75 "window", incrementing, lastrev: stepping through a window,
75 "window", incrementing, lastrev: stepping through a window,
76 positive if walking forwards through revs, last rev in the
76 positive if walking forwards through revs, last rev in the
77 sequence iterated over - use to reset state for the current window
77 sequence iterated over - use to reset state for the current window
78
78
79 "add", rev, fns: out-of-order traversal of the given file names
79 "add", rev, fns: out-of-order traversal of the given file names
80 fns, which changed during revision rev - use to gather data for
80 fns, which changed during revision rev - use to gather data for
81 possible display
81 possible display
82
82
83 "iter", rev, None: in-order traversal of the revs earlier iterated
83 "iter", rev, None: in-order traversal of the revs earlier iterated
84 over with "add" - use to display data'''
84 over with "add" - use to display data'''
85
85
86 def increasing_windows(start, end, windowsize=8, sizelimit=512):
86 def increasing_windows(start, end, windowsize=8, sizelimit=512):
87 if start < end:
87 if start < end:
88 while start < end:
88 while start < end:
89 yield start, min(windowsize, end-start)
89 yield start, min(windowsize, end-start)
90 start += windowsize
90 start += windowsize
91 if windowsize < sizelimit:
91 if windowsize < sizelimit:
92 windowsize *= 2
92 windowsize *= 2
93 else:
93 else:
94 while start > end:
94 while start > end:
95 yield start, min(windowsize, start-end-1)
95 yield start, min(windowsize, start-end-1)
96 start -= windowsize
96 start -= windowsize
97 if windowsize < sizelimit:
97 if windowsize < sizelimit:
98 windowsize *= 2
98 windowsize *= 2
99
99
100
100
101 files, matchfn, anypats = matchpats(repo, pats, opts)
101 files, matchfn, anypats = matchpats(repo, pats, opts)
102
102
103 if repo.changelog.count() == 0:
103 if repo.changelog.count() == 0:
104 return [], False, matchfn
104 return [], False, matchfn
105
105
106 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
106 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
107 wanted = {}
107 wanted = {}
108 slowpath = anypats
108 slowpath = anypats
109 fncache = {}
109 fncache = {}
110
110
111 chcache = {}
111 chcache = {}
112 def getchange(rev):
112 def getchange(rev):
113 ch = chcache.get(rev)
113 ch = chcache.get(rev)
114 if ch is None:
114 if ch is None:
115 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
115 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
116 return ch
116 return ch
117
117
118 if not slowpath and not files:
118 if not slowpath and not files:
119 # No files, no patterns. Display all revs.
119 # No files, no patterns. Display all revs.
120 wanted = dict(zip(revs, revs))
120 wanted = dict(zip(revs, revs))
121 if not slowpath:
121 if not slowpath:
122 # Only files, no patterns. Check the history of each file.
122 # Only files, no patterns. Check the history of each file.
123 def filerevgen(filelog):
123 def filerevgen(filelog):
124 for i, window in increasing_windows(filelog.count()-1, -1):
124 for i, window in increasing_windows(filelog.count()-1, -1):
125 revs = []
125 revs = []
126 for j in xrange(i - window, i + 1):
126 for j in xrange(i - window, i + 1):
127 revs.append(filelog.linkrev(filelog.node(j)))
127 revs.append(filelog.linkrev(filelog.node(j)))
128 revs.reverse()
128 revs.reverse()
129 for rev in revs:
129 for rev in revs:
130 yield rev
130 yield rev
131
131
132 minrev, maxrev = min(revs), max(revs)
132 minrev, maxrev = min(revs), max(revs)
133 for file_ in files:
133 for file_ in files:
134 filelog = repo.file(file_)
134 filelog = repo.file(file_)
135 # A zero count may be a directory or deleted file, so
135 # A zero count may be a directory or deleted file, so
136 # try to find matching entries on the slow path.
136 # try to find matching entries on the slow path.
137 if filelog.count() == 0:
137 if filelog.count() == 0:
138 slowpath = True
138 slowpath = True
139 break
139 break
140 for rev in filerevgen(filelog):
140 for rev in filerevgen(filelog):
141 if rev <= maxrev:
141 if rev <= maxrev:
142 if rev < minrev:
142 if rev < minrev:
143 break
143 break
144 fncache.setdefault(rev, [])
144 fncache.setdefault(rev, [])
145 fncache[rev].append(file_)
145 fncache[rev].append(file_)
146 wanted[rev] = 1
146 wanted[rev] = 1
147 if slowpath:
147 if slowpath:
148 # The slow path checks files modified in every changeset.
148 # The slow path checks files modified in every changeset.
149 def changerevgen():
149 def changerevgen():
150 for i, window in increasing_windows(repo.changelog.count()-1, -1):
150 for i, window in increasing_windows(repo.changelog.count()-1, -1):
151 for j in xrange(i - window, i + 1):
151 for j in xrange(i - window, i + 1):
152 yield j, getchange(j)[3]
152 yield j, getchange(j)[3]
153
153
154 for rev, changefiles in changerevgen():
154 for rev, changefiles in changerevgen():
155 matches = filter(matchfn, changefiles)
155 matches = filter(matchfn, changefiles)
156 if matches:
156 if matches:
157 fncache[rev] = matches
157 fncache[rev] = matches
158 wanted[rev] = 1
158 wanted[rev] = 1
159
159
160 def iterate():
160 def iterate():
161 for i, window in increasing_windows(0, len(revs)):
161 for i, window in increasing_windows(0, len(revs)):
162 yield 'window', revs[0] < revs[-1], revs[-1]
162 yield 'window', revs[0] < revs[-1], revs[-1]
163 nrevs = [rev for rev in revs[i:i+window]
163 nrevs = [rev for rev in revs[i:i+window]
164 if rev in wanted]
164 if rev in wanted]
165 srevs = list(nrevs)
165 srevs = list(nrevs)
166 srevs.sort()
166 srevs.sort()
167 for rev in srevs:
167 for rev in srevs:
168 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
168 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
169 yield 'add', rev, fns
169 yield 'add', rev, fns
170 for rev in nrevs:
170 for rev in nrevs:
171 yield 'iter', rev, None
171 yield 'iter', rev, None
172 return iterate(), getchange, matchfn
172 return iterate(), getchange, matchfn
173
173
174 revrangesep = ':'
174 revrangesep = ':'
175
175
176 def revrange(ui, repo, revs, revlog=None):
176 def revrange(ui, repo, revs, revlog=None):
177 """Yield revision as strings from a list of revision specifications."""
177 """Yield revision as strings from a list of revision specifications."""
178 if revlog is None:
178 if revlog is None:
179 revlog = repo.changelog
179 revlog = repo.changelog
180 revcount = revlog.count()
180 revcount = revlog.count()
181 def fix(val, defval):
181 def fix(val, defval):
182 if not val:
182 if not val:
183 return defval
183 return defval
184 try:
184 try:
185 num = int(val)
185 num = int(val)
186 if str(num) != val:
186 if str(num) != val:
187 raise ValueError
187 raise ValueError
188 if num < 0:
188 if num < 0:
189 num += revcount
189 num += revcount
190 if num < 0:
190 if num < 0:
191 num = 0
191 num = 0
192 elif num >= revcount:
192 elif num >= revcount:
193 raise ValueError
193 raise ValueError
194 except ValueError:
194 except ValueError:
195 try:
195 try:
196 num = repo.changelog.rev(repo.lookup(val))
196 num = repo.changelog.rev(repo.lookup(val))
197 except KeyError:
197 except KeyError:
198 try:
198 try:
199 num = revlog.rev(revlog.lookup(val))
199 num = revlog.rev(revlog.lookup(val))
200 except KeyError:
200 except KeyError:
201 raise util.Abort(_('invalid revision identifier %s'), val)
201 raise util.Abort(_('invalid revision identifier %s'), val)
202 return num
202 return num
203 seen = {}
203 seen = {}
204 for spec in revs:
204 for spec in revs:
205 if spec.find(revrangesep) >= 0:
205 if spec.find(revrangesep) >= 0:
206 start, end = spec.split(revrangesep, 1)
206 start, end = spec.split(revrangesep, 1)
207 start = fix(start, 0)
207 start = fix(start, 0)
208 end = fix(end, revcount - 1)
208 end = fix(end, revcount - 1)
209 step = start > end and -1 or 1
209 step = start > end and -1 or 1
210 for rev in xrange(start, end+step, step):
210 for rev in xrange(start, end+step, step):
211 if rev in seen:
211 if rev in seen:
212 continue
212 continue
213 seen[rev] = 1
213 seen[rev] = 1
214 yield str(rev)
214 yield str(rev)
215 else:
215 else:
216 rev = fix(spec, None)
216 rev = fix(spec, None)
217 if rev in seen:
217 if rev in seen:
218 continue
218 continue
219 seen[rev] = 1
219 seen[rev] = 1
220 yield str(rev)
220 yield str(rev)
221
221
222 def make_filename(repo, r, pat, node=None,
222 def make_filename(repo, r, pat, node=None,
223 total=None, seqno=None, revwidth=None, pathname=None):
223 total=None, seqno=None, revwidth=None, pathname=None):
224 node_expander = {
224 node_expander = {
225 'H': lambda: hex(node),
225 'H': lambda: hex(node),
226 'R': lambda: str(r.rev(node)),
226 'R': lambda: str(r.rev(node)),
227 'h': lambda: short(node),
227 'h': lambda: short(node),
228 }
228 }
229 expander = {
229 expander = {
230 '%': lambda: '%',
230 '%': lambda: '%',
231 'b': lambda: os.path.basename(repo.root),
231 'b': lambda: os.path.basename(repo.root),
232 }
232 }
233
233
234 try:
234 try:
235 if node:
235 if node:
236 expander.update(node_expander)
236 expander.update(node_expander)
237 if node and revwidth is not None:
237 if node and revwidth is not None:
238 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
238 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
239 if total is not None:
239 if total is not None:
240 expander['N'] = lambda: str(total)
240 expander['N'] = lambda: str(total)
241 if seqno is not None:
241 if seqno is not None:
242 expander['n'] = lambda: str(seqno)
242 expander['n'] = lambda: str(seqno)
243 if total is not None and seqno is not None:
243 if total is not None and seqno is not None:
244 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
244 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
245 if pathname is not None:
245 if pathname is not None:
246 expander['s'] = lambda: os.path.basename(pathname)
246 expander['s'] = lambda: os.path.basename(pathname)
247 expander['d'] = lambda: os.path.dirname(pathname) or '.'
247 expander['d'] = lambda: os.path.dirname(pathname) or '.'
248 expander['p'] = lambda: pathname
248 expander['p'] = lambda: pathname
249
249
250 newname = []
250 newname = []
251 patlen = len(pat)
251 patlen = len(pat)
252 i = 0
252 i = 0
253 while i < patlen:
253 while i < patlen:
254 c = pat[i]
254 c = pat[i]
255 if c == '%':
255 if c == '%':
256 i += 1
256 i += 1
257 c = pat[i]
257 c = pat[i]
258 c = expander[c]()
258 c = expander[c]()
259 newname.append(c)
259 newname.append(c)
260 i += 1
260 i += 1
261 return ''.join(newname)
261 return ''.join(newname)
262 except KeyError, inst:
262 except KeyError, inst:
263 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
263 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
264 inst.args[0])
264 inst.args[0])
265
265
266 def make_file(repo, r, pat, node=None,
266 def make_file(repo, r, pat, node=None,
267 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
267 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
268 if not pat or pat == '-':
268 if not pat or pat == '-':
269 return 'w' in mode and sys.stdout or sys.stdin
269 return 'w' in mode and sys.stdout or sys.stdin
270 if hasattr(pat, 'write') and 'w' in mode:
270 if hasattr(pat, 'write') and 'w' in mode:
271 return pat
271 return pat
272 if hasattr(pat, 'read') and 'r' in mode:
272 if hasattr(pat, 'read') and 'r' in mode:
273 return pat
273 return pat
274 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
274 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
275 pathname),
275 pathname),
276 mode)
276 mode)
277
277
278 def write_bundle(cg, filename=None, compress=True):
278 def write_bundle(cg, filename=None, compress=True):
279 """Write a bundle file and return its filename.
279 """Write a bundle file and return its filename.
280
280
281 Existing files will not be overwritten.
281 Existing files will not be overwritten.
282 If no filename is specified, a temporary file is created.
282 If no filename is specified, a temporary file is created.
283 bz2 compression can be turned off.
283 bz2 compression can be turned off.
284 The bundle file will be deleted in case of errors.
284 The bundle file will be deleted in case of errors.
285 """
285 """
286 class nocompress(object):
286 class nocompress(object):
287 def compress(self, x):
287 def compress(self, x):
288 return x
288 return x
289 def flush(self):
289 def flush(self):
290 return ""
290 return ""
291
291
292 fh = None
292 fh = None
293 cleanup = None
293 cleanup = None
294 try:
294 try:
295 if filename:
295 if filename:
296 if os.path.exists(filename):
296 if os.path.exists(filename):
297 raise util.Abort(_("file '%s' already exists"), filename)
297 raise util.Abort(_("file '%s' already exists"), filename)
298 fh = open(filename, "wb")
298 fh = open(filename, "wb")
299 else:
299 else:
300 fd, filename = tempfile.mkstemp(suffix=".hg", prefix="hg-bundle-")
300 fd, filename = tempfile.mkstemp(suffix=".hg", prefix="hg-bundle-")
301 fh = os.fdopen(fd, "wb")
301 fh = os.fdopen(fd, "wb")
302 cleanup = filename
302 cleanup = filename
303
303
304 if compress:
304 if compress:
305 fh.write("HG10")
305 fh.write("HG10")
306 z = bz2.BZ2Compressor(9)
306 z = bz2.BZ2Compressor(9)
307 else:
307 else:
308 fh.write("HG10UN")
308 fh.write("HG10UN")
309 z = nocompress()
309 z = nocompress()
310 # parse the changegroup data, otherwise we will block
310 # parse the changegroup data, otherwise we will block
311 # in case of sshrepo because we don't know the end of the stream
311 # in case of sshrepo because we don't know the end of the stream
312
312
313 # an empty chunkiter is the end of the changegroup
313 # an empty chunkiter is the end of the changegroup
314 empty = False
314 empty = False
315 while not empty:
315 while not empty:
316 empty = True
316 empty = True
317 for chunk in changegroup.chunkiter(cg):
317 for chunk in changegroup.chunkiter(cg):
318 empty = False
318 empty = False
319 fh.write(z.compress(changegroup.genchunk(chunk)))
319 fh.write(z.compress(changegroup.genchunk(chunk)))
320 fh.write(z.compress(changegroup.closechunk()))
320 fh.write(z.compress(changegroup.closechunk()))
321 fh.write(z.flush())
321 fh.write(z.flush())
322 cleanup = None
322 cleanup = None
323 return filename
323 return filename
324 finally:
324 finally:
325 if fh is not None:
325 if fh is not None:
326 fh.close()
326 fh.close()
327 if cleanup is not None:
327 if cleanup is not None:
328 os.unlink(cleanup)
328 os.unlink(cleanup)
329
329
330 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
330 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
331 changes=None, text=False, opts={}):
331 changes=None, text=False, opts={}):
332 if not node1:
332 if not node1:
333 node1 = repo.dirstate.parents()[0]
333 node1 = repo.dirstate.parents()[0]
334 # reading the data for node1 early allows it to play nicely
334 # reading the data for node1 early allows it to play nicely
335 # with repo.changes and the revlog cache.
335 # with repo.changes and the revlog cache.
336 change = repo.changelog.read(node1)
336 change = repo.changelog.read(node1)
337 mmap = repo.manifest.read(change[0])
337 mmap = repo.manifest.read(change[0])
338 date1 = util.datestr(change[2])
338 date1 = util.datestr(change[2])
339
339
340 if not changes:
340 if not changes:
341 changes = repo.changes(node1, node2, files, match=match)
341 changes = repo.changes(node1, node2, files, match=match)
342 modified, added, removed, deleted, unknown = changes
342 modified, added, removed, deleted, unknown = changes
343 if files:
343 if files:
344 modified, added, removed = map(lambda x: filterfiles(files, x),
344 modified, added, removed = map(lambda x: filterfiles(files, x),
345 (modified, added, removed))
345 (modified, added, removed))
346
346
347 if not modified and not added and not removed:
347 if not modified and not added and not removed:
348 return
348 return
349
349
350 if node2:
350 if node2:
351 change = repo.changelog.read(node2)
351 change = repo.changelog.read(node2)
352 mmap2 = repo.manifest.read(change[0])
352 mmap2 = repo.manifest.read(change[0])
353 date2 = util.datestr(change[2])
353 date2 = util.datestr(change[2])
354 def read(f):
354 def read(f):
355 return repo.file(f).read(mmap2[f])
355 return repo.file(f).read(mmap2[f])
356 else:
356 else:
357 date2 = util.datestr()
357 date2 = util.datestr()
358 def read(f):
358 def read(f):
359 return repo.wread(f)
359 return repo.wread(f)
360
360
361 if ui.quiet:
361 if ui.quiet:
362 r = None
362 r = None
363 else:
363 else:
364 hexfunc = ui.verbose and hex or short
364 hexfunc = ui.verbose and hex or short
365 r = [hexfunc(node) for node in [node1, node2] if node]
365 r = [hexfunc(node) for node in [node1, node2] if node]
366
366
367 diffopts = ui.diffopts()
367 diffopts = ui.diffopts()
368 showfunc = opts.get('show_function') or diffopts['showfunc']
368 showfunc = opts.get('show_function') or diffopts['showfunc']
369 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
369 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
370 for f in modified:
370 for f in modified:
371 to = None
371 to = None
372 if f in mmap:
372 if f in mmap:
373 to = repo.file(f).read(mmap[f])
373 to = repo.file(f).read(mmap[f])
374 tn = read(f)
374 tn = read(f)
375 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
375 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
376 showfunc=showfunc, ignorews=ignorews))
376 showfunc=showfunc, ignorews=ignorews))
377 for f in added:
377 for f in added:
378 to = None
378 to = None
379 tn = read(f)
379 tn = read(f)
380 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
380 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
381 showfunc=showfunc, ignorews=ignorews))
381 showfunc=showfunc, ignorews=ignorews))
382 for f in removed:
382 for f in removed:
383 to = repo.file(f).read(mmap[f])
383 to = repo.file(f).read(mmap[f])
384 tn = None
384 tn = None
385 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
385 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text,
386 showfunc=showfunc, ignorews=ignorews))
386 showfunc=showfunc, ignorews=ignorews))
387
387
388 def trimuser(ui, name, rev, revcache):
388 def trimuser(ui, name, rev, revcache):
389 """trim the name of the user who committed a change"""
389 """trim the name of the user who committed a change"""
390 user = revcache.get(rev)
390 user = revcache.get(rev)
391 if user is None:
391 if user is None:
392 user = revcache[rev] = ui.shortuser(name)
392 user = revcache[rev] = ui.shortuser(name)
393 return user
393 return user
394
394
395 class changeset_templater(object):
395 class changeset_templater(object):
396 '''use templater module to format changeset information.'''
396 '''use templater module to format changeset information.'''
397
397
398 def __init__(self, ui, repo, mapfile):
398 def __init__(self, ui, repo, mapfile):
399 self.t = templater.templater(mapfile, templater.common_filters,
399 self.t = templater.templater(mapfile, templater.common_filters,
400 cache={'parent': '{rev}:{node|short} ',
400 cache={'parent': '{rev}:{node|short} ',
401 'manifest': '{rev}:{node|short}'})
401 'manifest': '{rev}:{node|short}'})
402 self.ui = ui
402 self.ui = ui
403 self.repo = repo
403 self.repo = repo
404
404
405 def use_template(self, t):
405 def use_template(self, t):
406 '''set template string to use'''
406 '''set template string to use'''
407 self.t.cache['changeset'] = t
407 self.t.cache['changeset'] = t
408
408
409 def write(self, thing):
409 def write(self, thing):
410 '''write expanded template.
410 '''write expanded template.
411 uses in-order recursive traverse of iterators.'''
411 uses in-order recursive traverse of iterators.'''
412 for t in thing:
412 for t in thing:
413 if hasattr(t, '__iter__'):
413 if hasattr(t, '__iter__'):
414 self.write(t)
414 self.write(t)
415 else:
415 else:
416 self.ui.write(t)
416 self.ui.write(t)
417
417
418 def show(self, rev=0, changenode=None, brinfo=None):
418 def show(self, rev=0, changenode=None, brinfo=None):
419 '''show a single changeset or file revision'''
419 '''show a single changeset or file revision'''
420 log = self.repo.changelog
420 log = self.repo.changelog
421 if changenode is None:
421 if changenode is None:
422 changenode = log.node(rev)
422 changenode = log.node(rev)
423 elif not rev:
423 elif not rev:
424 rev = log.rev(changenode)
424 rev = log.rev(changenode)
425
425
426 changes = log.read(changenode)
426 changes = log.read(changenode)
427
427
428 def showlist(name, values, plural=None, **args):
428 def showlist(name, values, plural=None, **args):
429 '''expand set of values.
429 '''expand set of values.
430 name is name of key in template map.
430 name is name of key in template map.
431 values is list of strings or dicts.
431 values is list of strings or dicts.
432 plural is plural of name, if not simply name + 's'.
432 plural is plural of name, if not simply name + 's'.
433
433
434 expansion works like this, given name 'foo'.
434 expansion works like this, given name 'foo'.
435
435
436 if values is empty, expand 'no_foos'.
436 if values is empty, expand 'no_foos'.
437
437
438 if 'foo' not in template map, return values as a string,
438 if 'foo' not in template map, return values as a string,
439 joined by space.
439 joined by space.
440
440
441 expand 'start_foos'.
441 expand 'start_foos'.
442
442
443 for each value, expand 'foo'. if 'last_foo' in template
443 for each value, expand 'foo'. if 'last_foo' in template
444 map, expand it instead of 'foo' for last key.
444 map, expand it instead of 'foo' for last key.
445
445
446 expand 'end_foos'.
446 expand 'end_foos'.
447 '''
447 '''
448 if plural: names = plural
448 if plural: names = plural
449 else: names = name + 's'
449 else: names = name + 's'
450 if not values:
450 if not values:
451 noname = 'no_' + names
451 noname = 'no_' + names
452 if noname in self.t:
452 if noname in self.t:
453 yield self.t(noname, **args)
453 yield self.t(noname, **args)
454 return
454 return
455 if name not in self.t:
455 if name not in self.t:
456 if isinstance(values[0], str):
456 if isinstance(values[0], str):
457 yield ' '.join(values)
457 yield ' '.join(values)
458 else:
458 else:
459 for v in values:
459 for v in values:
460 yield dict(v, **args)
460 yield dict(v, **args)
461 return
461 return
462 startname = 'start_' + names
462 startname = 'start_' + names
463 if startname in self.t:
463 if startname in self.t:
464 yield self.t(startname, **args)
464 yield self.t(startname, **args)
465 vargs = args.copy()
465 vargs = args.copy()
466 def one(v, tag=name):
466 def one(v, tag=name):
467 try:
467 try:
468 vargs.update(v)
468 vargs.update(v)
469 except (AttributeError, ValueError):
469 except (AttributeError, ValueError):
470 try:
470 try:
471 for a, b in v:
471 for a, b in v:
472 vargs[a] = b
472 vargs[a] = b
473 except ValueError:
473 except ValueError:
474 vargs[name] = v
474 vargs[name] = v
475 return self.t(tag, **vargs)
475 return self.t(tag, **vargs)
476 lastname = 'last_' + name
476 lastname = 'last_' + name
477 if lastname in self.t:
477 if lastname in self.t:
478 last = values.pop()
478 last = values.pop()
479 else:
479 else:
480 last = None
480 last = None
481 for v in values:
481 for v in values:
482 yield one(v)
482 yield one(v)
483 if last is not None:
483 if last is not None:
484 yield one(last, tag=lastname)
484 yield one(last, tag=lastname)
485 endname = 'end_' + names
485 endname = 'end_' + names
486 if endname in self.t:
486 if endname in self.t:
487 yield self.t(endname, **args)
487 yield self.t(endname, **args)
488
488
489 if brinfo:
489 if brinfo:
490 def showbranches(**args):
490 def showbranches(**args):
491 if changenode in brinfo:
491 if changenode in brinfo:
492 for x in showlist('branch', brinfo[changenode],
492 for x in showlist('branch', brinfo[changenode],
493 plural='branches', **args):
493 plural='branches', **args):
494 yield x
494 yield x
495 else:
495 else:
496 showbranches = ''
496 showbranches = ''
497
497
498 if self.ui.debugflag:
498 if self.ui.debugflag:
499 def showmanifest(**args):
499 def showmanifest(**args):
500 args = args.copy()
500 args = args.copy()
501 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
501 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
502 node=hex(changes[0])))
502 node=hex(changes[0])))
503 yield self.t('manifest', **args)
503 yield self.t('manifest', **args)
504 else:
504 else:
505 showmanifest = ''
505 showmanifest = ''
506
506
507 def showparents(**args):
507 def showparents(**args):
508 parents = [[('rev', log.rev(p)), ('node', hex(p))]
508 parents = [[('rev', log.rev(p)), ('node', hex(p))]
509 for p in log.parents(changenode)
509 for p in log.parents(changenode)
510 if self.ui.debugflag or p != nullid]
510 if self.ui.debugflag or p != nullid]
511 if (not self.ui.debugflag and len(parents) == 1 and
511 if (not self.ui.debugflag and len(parents) == 1 and
512 parents[0][0][1] == rev - 1):
512 parents[0][0][1] == rev - 1):
513 return
513 return
514 for x in showlist('parent', parents, **args):
514 for x in showlist('parent', parents, **args):
515 yield x
515 yield x
516
516
517 def showtags(**args):
517 def showtags(**args):
518 for x in showlist('tag', self.repo.nodetags(changenode), **args):
518 for x in showlist('tag', self.repo.nodetags(changenode), **args):
519 yield x
519 yield x
520
520
521 if self.ui.debugflag:
521 if self.ui.debugflag:
522 files = self.repo.changes(log.parents(changenode)[0], changenode)
522 files = self.repo.changes(log.parents(changenode)[0], changenode)
523 def showfiles(**args):
523 def showfiles(**args):
524 for x in showlist('file', files[0], **args): yield x
524 for x in showlist('file', files[0], **args): yield x
525 def showadds(**args):
525 def showadds(**args):
526 for x in showlist('file_add', files[1], **args): yield x
526 for x in showlist('file_add', files[1], **args): yield x
527 def showdels(**args):
527 def showdels(**args):
528 for x in showlist('file_del', files[2], **args): yield x
528 for x in showlist('file_del', files[2], **args): yield x
529 else:
529 else:
530 def showfiles(**args):
530 def showfiles(**args):
531 for x in showlist('file', changes[3], **args): yield x
531 for x in showlist('file', changes[3], **args): yield x
532 showadds = ''
532 showadds = ''
533 showdels = ''
533 showdels = ''
534
534
535 props = {
535 props = {
536 'author': changes[1],
536 'author': changes[1],
537 'branches': showbranches,
537 'branches': showbranches,
538 'date': changes[2],
538 'date': changes[2],
539 'desc': changes[4],
539 'desc': changes[4],
540 'file_adds': showadds,
540 'file_adds': showadds,
541 'file_dels': showdels,
541 'file_dels': showdels,
542 'files': showfiles,
542 'files': showfiles,
543 'manifest': showmanifest,
543 'manifest': showmanifest,
544 'node': hex(changenode),
544 'node': hex(changenode),
545 'parents': showparents,
545 'parents': showparents,
546 'rev': rev,
546 'rev': rev,
547 'tags': showtags,
547 'tags': showtags,
548 }
548 }
549
549
550 try:
550 try:
551 if self.ui.debugflag and 'changeset_debug' in self.t:
551 if self.ui.debugflag and 'changeset_debug' in self.t:
552 key = 'changeset_debug'
552 key = 'changeset_debug'
553 elif self.ui.quiet and 'changeset_quiet' in self.t:
553 elif self.ui.quiet and 'changeset_quiet' in self.t:
554 key = 'changeset_quiet'
554 key = 'changeset_quiet'
555 elif self.ui.verbose and 'changeset_verbose' in self.t:
555 elif self.ui.verbose and 'changeset_verbose' in self.t:
556 key = 'changeset_verbose'
556 key = 'changeset_verbose'
557 else:
557 else:
558 key = 'changeset'
558 key = 'changeset'
559 self.write(self.t(key, **props))
559 self.write(self.t(key, **props))
560 except KeyError, inst:
560 except KeyError, inst:
561 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
561 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
562 inst.args[0]))
562 inst.args[0]))
563 except SyntaxError, inst:
563 except SyntaxError, inst:
564 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
564 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
565
565
566 class changeset_printer(object):
566 class changeset_printer(object):
567 '''show changeset information when templating not requested.'''
567 '''show changeset information when templating not requested.'''
568
568
569 def __init__(self, ui, repo):
569 def __init__(self, ui, repo):
570 self.ui = ui
570 self.ui = ui
571 self.repo = repo
571 self.repo = repo
572
572
573 def show(self, rev=0, changenode=None, brinfo=None):
573 def show(self, rev=0, changenode=None, brinfo=None):
574 '''show a single changeset or file revision'''
574 '''show a single changeset or file revision'''
575 log = self.repo.changelog
575 log = self.repo.changelog
576 if changenode is None:
576 if changenode is None:
577 changenode = log.node(rev)
577 changenode = log.node(rev)
578 elif not rev:
578 elif not rev:
579 rev = log.rev(changenode)
579 rev = log.rev(changenode)
580
580
581 if self.ui.quiet:
581 if self.ui.quiet:
582 self.ui.write("%d:%s\n" % (rev, short(changenode)))
582 self.ui.write("%d:%s\n" % (rev, short(changenode)))
583 return
583 return
584
584
585 changes = log.read(changenode)
585 changes = log.read(changenode)
586 date = util.datestr(changes[2])
586 date = util.datestr(changes[2])
587
587
588 parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p))
588 parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p))
589 for p in log.parents(changenode)
589 for p in log.parents(changenode)
590 if self.ui.debugflag or p != nullid]
590 if self.ui.debugflag or p != nullid]
591 if (not self.ui.debugflag and len(parents) == 1 and
591 if (not self.ui.debugflag and len(parents) == 1 and
592 parents[0][0] == rev-1):
592 parents[0][0] == rev-1):
593 parents = []
593 parents = []
594
594
595 if self.ui.verbose:
595 if self.ui.verbose:
596 self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
596 self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
597 else:
597 else:
598 self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
598 self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
599
599
600 for tag in self.repo.nodetags(changenode):
600 for tag in self.repo.nodetags(changenode):
601 self.ui.status(_("tag: %s\n") % tag)
601 self.ui.status(_("tag: %s\n") % tag)
602 for parent in parents:
602 for parent in parents:
603 self.ui.write(_("parent: %d:%s\n") % parent)
603 self.ui.write(_("parent: %d:%s\n") % parent)
604
604
605 if brinfo and changenode in brinfo:
605 if brinfo and changenode in brinfo:
606 br = brinfo[changenode]
606 br = brinfo[changenode]
607 self.ui.write(_("branch: %s\n") % " ".join(br))
607 self.ui.write(_("branch: %s\n") % " ".join(br))
608
608
609 self.ui.debug(_("manifest: %d:%s\n") %
609 self.ui.debug(_("manifest: %d:%s\n") %
610 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
610 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
611 self.ui.status(_("user: %s\n") % changes[1])
611 self.ui.status(_("user: %s\n") % changes[1])
612 self.ui.status(_("date: %s\n") % date)
612 self.ui.status(_("date: %s\n") % date)
613
613
614 if self.ui.debugflag:
614 if self.ui.debugflag:
615 files = self.repo.changes(log.parents(changenode)[0], changenode)
615 files = self.repo.changes(log.parents(changenode)[0], changenode)
616 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
616 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
617 files):
617 files):
618 if value:
618 if value:
619 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
619 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
620 else:
620 else:
621 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
621 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
622
622
623 description = changes[4].strip()
623 description = changes[4].strip()
624 if description:
624 if description:
625 if self.ui.verbose:
625 if self.ui.verbose:
626 self.ui.status(_("description:\n"))
626 self.ui.status(_("description:\n"))
627 self.ui.status(description)
627 self.ui.status(description)
628 self.ui.status("\n\n")
628 self.ui.status("\n\n")
629 else:
629 else:
630 self.ui.status(_("summary: %s\n") %
630 self.ui.status(_("summary: %s\n") %
631 description.splitlines()[0])
631 description.splitlines()[0])
632 self.ui.status("\n")
632 self.ui.status("\n")
633
633
634 def show_changeset(ui, repo, opts):
634 def show_changeset(ui, repo, opts):
635 '''show one changeset. uses template or regular display. caller
635 '''show one changeset. uses template or regular display. caller
636 can pass in 'style' and 'template' options in opts.'''
636 can pass in 'style' and 'template' options in opts.'''
637
637
638 tmpl = opts.get('template')
638 tmpl = opts.get('template')
639 if tmpl:
639 if tmpl:
640 tmpl = templater.parsestring(tmpl, quoted=False)
640 tmpl = templater.parsestring(tmpl, quoted=False)
641 else:
641 else:
642 tmpl = ui.config('ui', 'logtemplate')
642 tmpl = ui.config('ui', 'logtemplate')
643 if tmpl: tmpl = templater.parsestring(tmpl)
643 if tmpl: tmpl = templater.parsestring(tmpl)
644 mapfile = opts.get('style') or ui.config('ui', 'style')
644 mapfile = opts.get('style') or ui.config('ui', 'style')
645 if tmpl or mapfile:
645 if tmpl or mapfile:
646 if mapfile:
646 if mapfile:
647 if not os.path.isfile(mapfile):
647 if not os.path.isfile(mapfile):
648 mapname = templater.templatepath('map-cmdline.' + mapfile)
648 mapname = templater.templatepath('map-cmdline.' + mapfile)
649 if not mapname: mapname = templater.templatepath(mapfile)
649 if not mapname: mapname = templater.templatepath(mapfile)
650 if mapname: mapfile = mapname
650 if mapname: mapfile = mapname
651 try:
651 try:
652 t = changeset_templater(ui, repo, mapfile)
652 t = changeset_templater(ui, repo, mapfile)
653 except SyntaxError, inst:
653 except SyntaxError, inst:
654 raise util.Abort(inst.args[0])
654 raise util.Abort(inst.args[0])
655 if tmpl: t.use_template(tmpl)
655 if tmpl: t.use_template(tmpl)
656 return t
656 return t
657 return changeset_printer(ui, repo)
657 return changeset_printer(ui, repo)
658
658
659 def show_version(ui):
659 def show_version(ui):
660 """output version and copyright information"""
660 """output version and copyright information"""
661 ui.write(_("Mercurial Distributed SCM (version %s)\n")
661 ui.write(_("Mercurial Distributed SCM (version %s)\n")
662 % version.get_version())
662 % version.get_version())
663 ui.status(_(
663 ui.status(_(
664 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
664 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
665 "This is free software; see the source for copying conditions. "
665 "This is free software; see the source for copying conditions. "
666 "There is NO\nwarranty; "
666 "There is NO\nwarranty; "
667 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
667 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
668 ))
668 ))
669
669
670 def help_(ui, cmd=None, with_version=False):
670 def help_(ui, cmd=None, with_version=False):
671 """show help for a given command or all commands"""
671 """show help for a given command or all commands"""
672 option_lists = []
672 option_lists = []
673 if cmd and cmd != 'shortlist':
673 if cmd and cmd != 'shortlist':
674 if with_version:
674 if with_version:
675 show_version(ui)
675 show_version(ui)
676 ui.write('\n')
676 ui.write('\n')
677 aliases, i = find(cmd)
677 aliases, i = find(cmd)
678 # synopsis
678 # synopsis
679 ui.write("%s\n\n" % i[2])
679 ui.write("%s\n\n" % i[2])
680
680
681 # description
681 # description
682 doc = i[0].__doc__
682 doc = i[0].__doc__
683 if not doc:
683 if not doc:
684 doc = _("(No help text available)")
684 doc = _("(No help text available)")
685 if ui.quiet:
685 if ui.quiet:
686 doc = doc.splitlines(0)[0]
686 doc = doc.splitlines(0)[0]
687 ui.write("%s\n" % doc.rstrip())
687 ui.write("%s\n" % doc.rstrip())
688
688
689 if not ui.quiet:
689 if not ui.quiet:
690 # aliases
690 # aliases
691 if len(aliases) > 1:
691 if len(aliases) > 1:
692 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
692 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
693
693
694 # options
694 # options
695 if i[1]:
695 if i[1]:
696 option_lists.append(("options", i[1]))
696 option_lists.append(("options", i[1]))
697
697
698 else:
698 else:
699 # program name
699 # program name
700 if ui.verbose or with_version:
700 if ui.verbose or with_version:
701 show_version(ui)
701 show_version(ui)
702 else:
702 else:
703 ui.status(_("Mercurial Distributed SCM\n"))
703 ui.status(_("Mercurial Distributed SCM\n"))
704 ui.status('\n')
704 ui.status('\n')
705
705
706 # list of commands
706 # list of commands
707 if cmd == "shortlist":
707 if cmd == "shortlist":
708 ui.status(_('basic commands (use "hg help" '
708 ui.status(_('basic commands (use "hg help" '
709 'for the full list or option "-v" for details):\n\n'))
709 'for the full list or option "-v" for details):\n\n'))
710 elif ui.verbose:
710 elif ui.verbose:
711 ui.status(_('list of commands:\n\n'))
711 ui.status(_('list of commands:\n\n'))
712 else:
712 else:
713 ui.status(_('list of commands (use "hg help -v" '
713 ui.status(_('list of commands (use "hg help -v" '
714 'to show aliases and global options):\n\n'))
714 'to show aliases and global options):\n\n'))
715
715
716 h = {}
716 h = {}
717 cmds = {}
717 cmds = {}
718 for c, e in table.items():
718 for c, e in table.items():
719 f = c.split("|")[0]
719 f = c.split("|")[0]
720 if cmd == "shortlist" and not f.startswith("^"):
720 if cmd == "shortlist" and not f.startswith("^"):
721 continue
721 continue
722 f = f.lstrip("^")
722 f = f.lstrip("^")
723 if not ui.debugflag and f.startswith("debug"):
723 if not ui.debugflag and f.startswith("debug"):
724 continue
724 continue
725 doc = e[0].__doc__
725 doc = e[0].__doc__
726 if not doc:
726 if not doc:
727 doc = _("(No help text available)")
727 doc = _("(No help text available)")
728 h[f] = doc.splitlines(0)[0].rstrip()
728 h[f] = doc.splitlines(0)[0].rstrip()
729 cmds[f] = c.lstrip("^")
729 cmds[f] = c.lstrip("^")
730
730
731 fns = h.keys()
731 fns = h.keys()
732 fns.sort()
732 fns.sort()
733 m = max(map(len, fns))
733 m = max(map(len, fns))
734 for f in fns:
734 for f in fns:
735 if ui.verbose:
735 if ui.verbose:
736 commands = cmds[f].replace("|",", ")
736 commands = cmds[f].replace("|",", ")
737 ui.write(" %s:\n %s\n"%(commands, h[f]))
737 ui.write(" %s:\n %s\n"%(commands, h[f]))
738 else:
738 else:
739 ui.write(' %-*s %s\n' % (m, f, h[f]))
739 ui.write(' %-*s %s\n' % (m, f, h[f]))
740
740
741 # global options
741 # global options
742 if ui.verbose:
742 if ui.verbose:
743 option_lists.append(("global options", globalopts))
743 option_lists.append(("global options", globalopts))
744
744
745 # list all option lists
745 # list all option lists
746 opt_output = []
746 opt_output = []
747 for title, options in option_lists:
747 for title, options in option_lists:
748 opt_output.append(("\n%s:\n" % title, None))
748 opt_output.append(("\n%s:\n" % title, None))
749 for shortopt, longopt, default, desc in options:
749 for shortopt, longopt, default, desc in options:
750 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
750 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
751 longopt and " --%s" % longopt),
751 longopt and " --%s" % longopt),
752 "%s%s" % (desc,
752 "%s%s" % (desc,
753 default
753 default
754 and _(" (default: %s)") % default
754 and _(" (default: %s)") % default
755 or "")))
755 or "")))
756
756
757 if opt_output:
757 if opt_output:
758 opts_len = max([len(line[0]) for line in opt_output if line[1]])
758 opts_len = max([len(line[0]) for line in opt_output if line[1]])
759 for first, second in opt_output:
759 for first, second in opt_output:
760 if second:
760 if second:
761 ui.write(" %-*s %s\n" % (opts_len, first, second))
761 ui.write(" %-*s %s\n" % (opts_len, first, second))
762 else:
762 else:
763 ui.write("%s\n" % first)
763 ui.write("%s\n" % first)
764
764
765 # Commands start here, listed alphabetically
765 # Commands start here, listed alphabetically
766
766
767 def add(ui, repo, *pats, **opts):
767 def add(ui, repo, *pats, **opts):
768 """add the specified files on the next commit
768 """add the specified files on the next commit
769
769
770 Schedule files to be version controlled and added to the repository.
770 Schedule files to be version controlled and added to the repository.
771
771
772 The files will be added to the repository at the next commit.
772 The files will be added to the repository at the next commit.
773
773
774 If no names are given, add all files in the repository.
774 If no names are given, add all files in the repository.
775 """
775 """
776
776
777 names = []
777 names = []
778 for src, abs, rel, exact in walk(repo, pats, opts):
778 for src, abs, rel, exact in walk(repo, pats, opts):
779 if exact:
779 if exact:
780 if ui.verbose:
780 if ui.verbose:
781 ui.status(_('adding %s\n') % rel)
781 ui.status(_('adding %s\n') % rel)
782 names.append(abs)
782 names.append(abs)
783 elif repo.dirstate.state(abs) == '?':
783 elif repo.dirstate.state(abs) == '?':
784 ui.status(_('adding %s\n') % rel)
784 ui.status(_('adding %s\n') % rel)
785 names.append(abs)
785 names.append(abs)
786 repo.add(names)
786 repo.add(names)
787
787
788 def addremove(ui, repo, *pats, **opts):
788 def addremove(ui, repo, *pats, **opts):
789 """add all new files, delete all missing files
789 """add all new files, delete all missing files
790
790
791 Add all new files and remove all missing files from the repository.
791 Add all new files and remove all missing files from the repository.
792
792
793 New files are ignored if they match any of the patterns in .hgignore. As
793 New files are ignored if they match any of the patterns in .hgignore. As
794 with add, these changes take effect at the next commit.
794 with add, these changes take effect at the next commit.
795 """
795 """
796 return addremove_lock(ui, repo, pats, opts)
796 return addremove_lock(ui, repo, pats, opts)
797
797
798 def addremove_lock(ui, repo, pats, opts, wlock=None):
798 def addremove_lock(ui, repo, pats, opts, wlock=None):
799 add, remove = [], []
799 add, remove = [], []
800 for src, abs, rel, exact in walk(repo, pats, opts):
800 for src, abs, rel, exact in walk(repo, pats, opts):
801 if src == 'f' and repo.dirstate.state(abs) == '?':
801 if src == 'f' and repo.dirstate.state(abs) == '?':
802 add.append(abs)
802 add.append(abs)
803 if ui.verbose or not exact:
803 if ui.verbose or not exact:
804 ui.status(_('adding %s\n') % ((pats and rel) or abs))
804 ui.status(_('adding %s\n') % ((pats and rel) or abs))
805 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
805 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
806 remove.append(abs)
806 remove.append(abs)
807 if ui.verbose or not exact:
807 if ui.verbose or not exact:
808 ui.status(_('removing %s\n') % ((pats and rel) or abs))
808 ui.status(_('removing %s\n') % ((pats and rel) or abs))
809 repo.add(add, wlock=wlock)
809 repo.add(add, wlock=wlock)
810 repo.remove(remove, wlock=wlock)
810 repo.remove(remove, wlock=wlock)
811
811
812 def annotate(ui, repo, *pats, **opts):
812 def annotate(ui, repo, *pats, **opts):
813 """show changeset information per file line
813 """show changeset information per file line
814
814
815 List changes in files, showing the revision id responsible for each line
815 List changes in files, showing the revision id responsible for each line
816
816
817 This command is useful to discover who did a change or when a change took
817 This command is useful to discover who did a change or when a change took
818 place.
818 place.
819
819
820 Without the -a option, annotate will avoid processing files it
820 Without the -a option, annotate will avoid processing files it
821 detects as binary. With -a, annotate will generate an annotation
821 detects as binary. With -a, annotate will generate an annotation
822 anyway, probably with undesirable results.
822 anyway, probably with undesirable results.
823 """
823 """
824 def getnode(rev):
824 def getnode(rev):
825 return short(repo.changelog.node(rev))
825 return short(repo.changelog.node(rev))
826
826
827 ucache = {}
827 ucache = {}
828 def getname(rev):
828 def getname(rev):
829 cl = repo.changelog.read(repo.changelog.node(rev))
829 cl = repo.changelog.read(repo.changelog.node(rev))
830 return trimuser(ui, cl[1], rev, ucache)
830 return trimuser(ui, cl[1], rev, ucache)
831
831
832 dcache = {}
832 dcache = {}
833 def getdate(rev):
833 def getdate(rev):
834 datestr = dcache.get(rev)
834 datestr = dcache.get(rev)
835 if datestr is None:
835 if datestr is None:
836 cl = repo.changelog.read(repo.changelog.node(rev))
836 cl = repo.changelog.read(repo.changelog.node(rev))
837 datestr = dcache[rev] = util.datestr(cl[2])
837 datestr = dcache[rev] = util.datestr(cl[2])
838 return datestr
838 return datestr
839
839
840 if not pats:
840 if not pats:
841 raise util.Abort(_('at least one file name or pattern required'))
841 raise util.Abort(_('at least one file name or pattern required'))
842
842
843 opmap = [['user', getname], ['number', str], ['changeset', getnode],
843 opmap = [['user', getname], ['number', str], ['changeset', getnode],
844 ['date', getdate]]
844 ['date', getdate]]
845 if not opts['user'] and not opts['changeset'] and not opts['date']:
845 if not opts['user'] and not opts['changeset'] and not opts['date']:
846 opts['number'] = 1
846 opts['number'] = 1
847
847
848 if opts['rev']:
848 if opts['rev']:
849 node = repo.changelog.lookup(opts['rev'])
849 node = repo.changelog.lookup(opts['rev'])
850 else:
850 else:
851 node = repo.dirstate.parents()[0]
851 node = repo.dirstate.parents()[0]
852 change = repo.changelog.read(node)
852 change = repo.changelog.read(node)
853 mmap = repo.manifest.read(change[0])
853 mmap = repo.manifest.read(change[0])
854
854
855 for src, abs, rel, exact in walk(repo, pats, opts, node=node):
855 for src, abs, rel, exact in walk(repo, pats, opts, node=node):
856 f = repo.file(abs)
856 f = repo.file(abs)
857 if not opts['text'] and util.binary(f.read(mmap[abs])):
857 if not opts['text'] and util.binary(f.read(mmap[abs])):
858 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
858 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
859 continue
859 continue
860
860
861 lines = f.annotate(mmap[abs])
861 lines = f.annotate(mmap[abs])
862 pieces = []
862 pieces = []
863
863
864 for o, f in opmap:
864 for o, f in opmap:
865 if opts[o]:
865 if opts[o]:
866 l = [f(n) for n, dummy in lines]
866 l = [f(n) for n, dummy in lines]
867 if l:
867 if l:
868 m = max(map(len, l))
868 m = max(map(len, l))
869 pieces.append(["%*s" % (m, x) for x in l])
869 pieces.append(["%*s" % (m, x) for x in l])
870
870
871 if pieces:
871 if pieces:
872 for p, l in zip(zip(*pieces), lines):
872 for p, l in zip(zip(*pieces), lines):
873 ui.write("%s: %s" % (" ".join(p), l[1]))
873 ui.write("%s: %s" % (" ".join(p), l[1]))
874
874
875 def bundle(ui, repo, fname, dest="default-push", **opts):
875 def bundle(ui, repo, fname, dest="default-push", **opts):
876 """create a changegroup file
876 """create a changegroup file
877
877
878 Generate a compressed changegroup file collecting all changesets
878 Generate a compressed changegroup file collecting all changesets
879 not found in the other repository.
879 not found in the other repository.
880
880
881 This file can then be transferred using conventional means and
881 This file can then be transferred using conventional means and
882 applied to another repository with the unbundle command. This is
882 applied to another repository with the unbundle command. This is
883 useful when native push and pull are not available or when
883 useful when native push and pull are not available or when
884 exporting an entire repository is undesirable. The standard file
884 exporting an entire repository is undesirable. The standard file
885 extension is ".hg".
885 extension is ".hg".
886
886
887 Unlike import/export, this exactly preserves all changeset
887 Unlike import/export, this exactly preserves all changeset
888 contents including permissions, rename data, and revision history.
888 contents including permissions, rename data, and revision history.
889 """
889 """
890 dest = ui.expandpath(dest)
890 dest = ui.expandpath(dest)
891 other = hg.repository(ui, dest)
891 other = hg.repository(ui, dest)
892 o = repo.findoutgoing(other, force=opts['force'])
892 o = repo.findoutgoing(other, force=opts['force'])
893 cg = repo.changegroup(o, 'bundle')
893 cg = repo.changegroup(o, 'bundle')
894 write_bundle(cg, fname)
894 write_bundle(cg, fname)
895
895
896 def cat(ui, repo, file1, *pats, **opts):
896 def cat(ui, repo, file1, *pats, **opts):
897 """output the latest or given revisions of files
897 """output the latest or given revisions of files
898
898
899 Print the specified files as they were at the given revision.
899 Print the specified files as they were at the given revision.
900 If no revision is given then the tip is used.
900 If no revision is given then the tip is used.
901
901
902 Output may be to a file, in which case the name of the file is
902 Output may be to a file, in which case the name of the file is
903 given using a format string. The formatting rules are the same as
903 given using a format string. The formatting rules are the same as
904 for the export command, with the following additions:
904 for the export command, with the following additions:
905
905
906 %s basename of file being printed
906 %s basename of file being printed
907 %d dirname of file being printed, or '.' if in repo root
907 %d dirname of file being printed, or '.' if in repo root
908 %p root-relative path name of file being printed
908 %p root-relative path name of file being printed
909 """
909 """
910 mf = {}
910 mf = {}
911 rev = opts['rev']
911 rev = opts['rev']
912 if rev:
912 if rev:
913 node = repo.lookup(rev)
913 node = repo.lookup(rev)
914 else:
914 else:
915 node = repo.changelog.tip()
915 node = repo.changelog.tip()
916 change = repo.changelog.read(node)
916 change = repo.changelog.read(node)
917 mf = repo.manifest.read(change[0])
917 mf = repo.manifest.read(change[0])
918 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, node):
918 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, node):
919 r = repo.file(abs)
919 r = repo.file(abs)
920 n = mf[abs]
920 n = mf[abs]
921 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
921 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
922 fp.write(r.read(n))
922 fp.write(r.read(n))
923
923
924 def clone(ui, source, dest=None, **opts):
924 def clone(ui, source, dest=None, **opts):
925 """make a copy of an existing repository
925 """make a copy of an existing repository
926
926
927 Create a copy of an existing repository in a new directory.
927 Create a copy of an existing repository in a new directory.
928
928
929 If no destination directory name is specified, it defaults to the
929 If no destination directory name is specified, it defaults to the
930 basename of the source.
930 basename of the source.
931
931
932 The location of the source is added to the new repository's
932 The location of the source is added to the new repository's
933 .hg/hgrc file, as the default to be used for future pulls.
933 .hg/hgrc file, as the default to be used for future pulls.
934
934
935 For efficiency, hardlinks are used for cloning whenever the source
935 For efficiency, hardlinks are used for cloning whenever the source
936 and destination are on the same filesystem. Some filesystems,
936 and destination are on the same filesystem. Some filesystems,
937 such as AFS, implement hardlinking incorrectly, but do not report
937 such as AFS, implement hardlinking incorrectly, but do not report
938 errors. In these cases, use the --pull option to avoid
938 errors. In these cases, use the --pull option to avoid
939 hardlinking.
939 hardlinking.
940
940
941 See pull for valid source format details.
941 See pull for valid source format details.
942 """
942 """
943 if dest is None:
943 if dest is None:
944 dest = os.path.basename(os.path.normpath(source))
944 dest = os.path.basename(os.path.normpath(source))
945
945
946 if os.path.exists(dest):
946 if os.path.exists(dest):
947 raise util.Abort(_("destination '%s' already exists"), dest)
947 raise util.Abort(_("destination '%s' already exists"), dest)
948
948
949 dest = os.path.realpath(dest)
949 dest = os.path.realpath(dest)
950
950
951 class Dircleanup(object):
951 class Dircleanup(object):
952 def __init__(self, dir_):
952 def __init__(self, dir_):
953 self.rmtree = shutil.rmtree
953 self.rmtree = shutil.rmtree
954 self.dir_ = dir_
954 self.dir_ = dir_
955 os.mkdir(dir_)
955 os.mkdir(dir_)
956 def close(self):
956 def close(self):
957 self.dir_ = None
957 self.dir_ = None
958 def __del__(self):
958 def __del__(self):
959 if self.dir_:
959 if self.dir_:
960 self.rmtree(self.dir_, True)
960 self.rmtree(self.dir_, True)
961
961
962 if opts['ssh']:
962 if opts['ssh']:
963 ui.setconfig("ui", "ssh", opts['ssh'])
963 ui.setconfig("ui", "ssh", opts['ssh'])
964 if opts['remotecmd']:
964 if opts['remotecmd']:
965 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
965 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
966
966
967 source = ui.expandpath(source)
967 source = ui.expandpath(source)
968
968
969 d = Dircleanup(dest)
969 d = Dircleanup(dest)
970 abspath = source
970 abspath = source
971 other = hg.repository(ui, source)
971 other = hg.repository(ui, source)
972
972
973 copy = False
973 copy = False
974 if other.dev() != -1:
974 if other.dev() != -1:
975 abspath = os.path.abspath(source)
975 abspath = os.path.abspath(source)
976 if not opts['pull'] and not opts['rev']:
976 if not opts['pull'] and not opts['rev']:
977 copy = True
977 copy = True
978
978
979 if copy:
979 if copy:
980 try:
980 try:
981 # we use a lock here because if we race with commit, we
981 # we use a lock here because if we race with commit, we
982 # can end up with extra data in the cloned revlogs that's
982 # can end up with extra data in the cloned revlogs that's
983 # not pointed to by changesets, thus causing verify to
983 # not pointed to by changesets, thus causing verify to
984 # fail
984 # fail
985 l1 = other.lock()
985 l1 = other.lock()
986 except lock.LockException:
986 except lock.LockException:
987 copy = False
987 copy = False
988
988
989 if copy:
989 if copy:
990 # we lock here to avoid premature writing to the target
990 # we lock here to avoid premature writing to the target
991 os.mkdir(os.path.join(dest, ".hg"))
991 os.mkdir(os.path.join(dest, ".hg"))
992 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
992 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
993
993
994 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
994 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
995 for f in files.split():
995 for f in files.split():
996 src = os.path.join(source, ".hg", f)
996 src = os.path.join(source, ".hg", f)
997 dst = os.path.join(dest, ".hg", f)
997 dst = os.path.join(dest, ".hg", f)
998 try:
998 try:
999 util.copyfiles(src, dst)
999 util.copyfiles(src, dst)
1000 except OSError, inst:
1000 except OSError, inst:
1001 if inst.errno != errno.ENOENT:
1001 if inst.errno != errno.ENOENT:
1002 raise
1002 raise
1003
1003
1004 repo = hg.repository(ui, dest)
1004 repo = hg.repository(ui, dest)
1005
1005
1006 else:
1006 else:
1007 revs = None
1007 revs = None
1008 if opts['rev']:
1008 if opts['rev']:
1009 if not other.local():
1009 if not other.local():
1010 error = _("clone -r not supported yet for remote repositories.")
1010 error = _("clone -r not supported yet for remote repositories.")
1011 raise util.Abort(error)
1011 raise util.Abort(error)
1012 else:
1012 else:
1013 revs = [other.lookup(rev) for rev in opts['rev']]
1013 revs = [other.lookup(rev) for rev in opts['rev']]
1014 repo = hg.repository(ui, dest, create=1)
1014 repo = hg.repository(ui, dest, create=1)
1015 repo.pull(other, heads = revs)
1015 repo.pull(other, heads = revs)
1016
1016
1017 f = repo.opener("hgrc", "w", text=True)
1017 f = repo.opener("hgrc", "w", text=True)
1018 f.write("[paths]\n")
1018 f.write("[paths]\n")
1019 f.write("default = %s\n" % abspath)
1019 f.write("default = %s\n" % abspath)
1020 f.close()
1020 f.close()
1021
1021
1022 if not opts['noupdate']:
1022 if not opts['noupdate']:
1023 update(repo.ui, repo)
1023 update(repo.ui, repo)
1024
1024
1025 d.close()
1025 d.close()
1026
1026
1027 def commit(ui, repo, *pats, **opts):
1027 def commit(ui, repo, *pats, **opts):
1028 """commit the specified files or all outstanding changes
1028 """commit the specified files or all outstanding changes
1029
1029
1030 Commit changes to the given files into the repository.
1030 Commit changes to the given files into the repository.
1031
1031
1032 If a list of files is omitted, all changes reported by "hg status"
1032 If a list of files is omitted, all changes reported by "hg status"
1033 will be commited.
1033 will be committed.
1034
1034
1035 The HGEDITOR or EDITOR environment variables are used to start an
1035 The HGEDITOR or EDITOR environment variables are used to start an
1036 editor to add a commit comment.
1036 editor to add a commit comment.
1037 """
1037 """
1038 message = opts['message']
1038 message = opts['message']
1039 logfile = opts['logfile']
1039 logfile = opts['logfile']
1040
1040
1041 if message and logfile:
1041 if message and logfile:
1042 raise util.Abort(_('options --message and --logfile are mutually '
1042 raise util.Abort(_('options --message and --logfile are mutually '
1043 'exclusive'))
1043 'exclusive'))
1044 if not message and logfile:
1044 if not message and logfile:
1045 try:
1045 try:
1046 if logfile == '-':
1046 if logfile == '-':
1047 message = sys.stdin.read()
1047 message = sys.stdin.read()
1048 else:
1048 else:
1049 message = open(logfile).read()
1049 message = open(logfile).read()
1050 except IOError, inst:
1050 except IOError, inst:
1051 raise util.Abort(_("can't read commit message '%s': %s") %
1051 raise util.Abort(_("can't read commit message '%s': %s") %
1052 (logfile, inst.strerror))
1052 (logfile, inst.strerror))
1053
1053
1054 if opts['addremove']:
1054 if opts['addremove']:
1055 addremove(ui, repo, *pats, **opts)
1055 addremove(ui, repo, *pats, **opts)
1056 fns, match, anypats = matchpats(repo, pats, opts)
1056 fns, match, anypats = matchpats(repo, pats, opts)
1057 if pats:
1057 if pats:
1058 modified, added, removed, deleted, unknown = (
1058 modified, added, removed, deleted, unknown = (
1059 repo.changes(files=fns, match=match))
1059 repo.changes(files=fns, match=match))
1060 files = modified + added + removed
1060 files = modified + added + removed
1061 else:
1061 else:
1062 files = []
1062 files = []
1063 try:
1063 try:
1064 repo.commit(files, message, opts['user'], opts['date'], match)
1064 repo.commit(files, message, opts['user'], opts['date'], match)
1065 except ValueError, inst:
1065 except ValueError, inst:
1066 raise util.Abort(str(inst))
1066 raise util.Abort(str(inst))
1067
1067
1068 def docopy(ui, repo, pats, opts, wlock):
1068 def docopy(ui, repo, pats, opts, wlock):
1069 # called with the repo lock held
1069 # called with the repo lock held
1070 cwd = repo.getcwd()
1070 cwd = repo.getcwd()
1071 errors = 0
1071 errors = 0
1072 copied = []
1072 copied = []
1073 targets = {}
1073 targets = {}
1074
1074
1075 def okaytocopy(abs, rel, exact):
1075 def okaytocopy(abs, rel, exact):
1076 reasons = {'?': _('is not managed'),
1076 reasons = {'?': _('is not managed'),
1077 'a': _('has been marked for add'),
1077 'a': _('has been marked for add'),
1078 'r': _('has been marked for remove')}
1078 'r': _('has been marked for remove')}
1079 state = repo.dirstate.state(abs)
1079 state = repo.dirstate.state(abs)
1080 reason = reasons.get(state)
1080 reason = reasons.get(state)
1081 if reason:
1081 if reason:
1082 if state == 'a':
1082 if state == 'a':
1083 origsrc = repo.dirstate.copied(abs)
1083 origsrc = repo.dirstate.copied(abs)
1084 if origsrc is not None:
1084 if origsrc is not None:
1085 return origsrc
1085 return origsrc
1086 if exact:
1086 if exact:
1087 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
1087 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
1088 else:
1088 else:
1089 return abs
1089 return abs
1090
1090
1091 def copy(origsrc, abssrc, relsrc, target, exact):
1091 def copy(origsrc, abssrc, relsrc, target, exact):
1092 abstarget = util.canonpath(repo.root, cwd, target)
1092 abstarget = util.canonpath(repo.root, cwd, target)
1093 reltarget = util.pathto(cwd, abstarget)
1093 reltarget = util.pathto(cwd, abstarget)
1094 prevsrc = targets.get(abstarget)
1094 prevsrc = targets.get(abstarget)
1095 if prevsrc is not None:
1095 if prevsrc is not None:
1096 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1096 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1097 (reltarget, abssrc, prevsrc))
1097 (reltarget, abssrc, prevsrc))
1098 return
1098 return
1099 if (not opts['after'] and os.path.exists(reltarget) or
1099 if (not opts['after'] and os.path.exists(reltarget) or
1100 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
1100 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
1101 if not opts['force']:
1101 if not opts['force']:
1102 ui.warn(_('%s: not overwriting - file exists\n') %
1102 ui.warn(_('%s: not overwriting - file exists\n') %
1103 reltarget)
1103 reltarget)
1104 return
1104 return
1105 if not opts['after']:
1105 if not opts['after']:
1106 os.unlink(reltarget)
1106 os.unlink(reltarget)
1107 if opts['after']:
1107 if opts['after']:
1108 if not os.path.exists(reltarget):
1108 if not os.path.exists(reltarget):
1109 return
1109 return
1110 else:
1110 else:
1111 targetdir = os.path.dirname(reltarget) or '.'
1111 targetdir = os.path.dirname(reltarget) or '.'
1112 if not os.path.isdir(targetdir):
1112 if not os.path.isdir(targetdir):
1113 os.makedirs(targetdir)
1113 os.makedirs(targetdir)
1114 try:
1114 try:
1115 restore = repo.dirstate.state(abstarget) == 'r'
1115 restore = repo.dirstate.state(abstarget) == 'r'
1116 if restore:
1116 if restore:
1117 repo.undelete([abstarget], wlock)
1117 repo.undelete([abstarget], wlock)
1118 try:
1118 try:
1119 shutil.copyfile(relsrc, reltarget)
1119 shutil.copyfile(relsrc, reltarget)
1120 shutil.copymode(relsrc, reltarget)
1120 shutil.copymode(relsrc, reltarget)
1121 restore = False
1121 restore = False
1122 finally:
1122 finally:
1123 if restore:
1123 if restore:
1124 repo.remove([abstarget], wlock)
1124 repo.remove([abstarget], wlock)
1125 except shutil.Error, inst:
1125 except shutil.Error, inst:
1126 raise util.Abort(str(inst))
1126 raise util.Abort(str(inst))
1127 except IOError, inst:
1127 except IOError, inst:
1128 if inst.errno == errno.ENOENT:
1128 if inst.errno == errno.ENOENT:
1129 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1129 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1130 else:
1130 else:
1131 ui.warn(_('%s: cannot copy - %s\n') %
1131 ui.warn(_('%s: cannot copy - %s\n') %
1132 (relsrc, inst.strerror))
1132 (relsrc, inst.strerror))
1133 errors += 1
1133 errors += 1
1134 return
1134 return
1135 if ui.verbose or not exact:
1135 if ui.verbose or not exact:
1136 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1136 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1137 targets[abstarget] = abssrc
1137 targets[abstarget] = abssrc
1138 if abstarget != origsrc:
1138 if abstarget != origsrc:
1139 repo.copy(origsrc, abstarget, wlock)
1139 repo.copy(origsrc, abstarget, wlock)
1140 copied.append((abssrc, relsrc, exact))
1140 copied.append((abssrc, relsrc, exact))
1141
1141
1142 def targetpathfn(pat, dest, srcs):
1142 def targetpathfn(pat, dest, srcs):
1143 if os.path.isdir(pat):
1143 if os.path.isdir(pat):
1144 abspfx = util.canonpath(repo.root, cwd, pat)
1144 abspfx = util.canonpath(repo.root, cwd, pat)
1145 if destdirexists:
1145 if destdirexists:
1146 striplen = len(os.path.split(abspfx)[0])
1146 striplen = len(os.path.split(abspfx)[0])
1147 else:
1147 else:
1148 striplen = len(abspfx)
1148 striplen = len(abspfx)
1149 if striplen:
1149 if striplen:
1150 striplen += len(os.sep)
1150 striplen += len(os.sep)
1151 res = lambda p: os.path.join(dest, p[striplen:])
1151 res = lambda p: os.path.join(dest, p[striplen:])
1152 elif destdirexists:
1152 elif destdirexists:
1153 res = lambda p: os.path.join(dest, os.path.basename(p))
1153 res = lambda p: os.path.join(dest, os.path.basename(p))
1154 else:
1154 else:
1155 res = lambda p: dest
1155 res = lambda p: dest
1156 return res
1156 return res
1157
1157
1158 def targetpathafterfn(pat, dest, srcs):
1158 def targetpathafterfn(pat, dest, srcs):
1159 if util.patkind(pat, None)[0]:
1159 if util.patkind(pat, None)[0]:
1160 # a mercurial pattern
1160 # a mercurial pattern
1161 res = lambda p: os.path.join(dest, os.path.basename(p))
1161 res = lambda p: os.path.join(dest, os.path.basename(p))
1162 else:
1162 else:
1163 abspfx = util.canonpath(repo.root, cwd, pat)
1163 abspfx = util.canonpath(repo.root, cwd, pat)
1164 if len(abspfx) < len(srcs[0][0]):
1164 if len(abspfx) < len(srcs[0][0]):
1165 # A directory. Either the target path contains the last
1165 # A directory. Either the target path contains the last
1166 # component of the source path or it does not.
1166 # component of the source path or it does not.
1167 def evalpath(striplen):
1167 def evalpath(striplen):
1168 score = 0
1168 score = 0
1169 for s in srcs:
1169 for s in srcs:
1170 t = os.path.join(dest, s[0][striplen:])
1170 t = os.path.join(dest, s[0][striplen:])
1171 if os.path.exists(t):
1171 if os.path.exists(t):
1172 score += 1
1172 score += 1
1173 return score
1173 return score
1174
1174
1175 striplen = len(abspfx)
1175 striplen = len(abspfx)
1176 if striplen:
1176 if striplen:
1177 striplen += len(os.sep)
1177 striplen += len(os.sep)
1178 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1178 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1179 score = evalpath(striplen)
1179 score = evalpath(striplen)
1180 striplen1 = len(os.path.split(abspfx)[0])
1180 striplen1 = len(os.path.split(abspfx)[0])
1181 if striplen1:
1181 if striplen1:
1182 striplen1 += len(os.sep)
1182 striplen1 += len(os.sep)
1183 if evalpath(striplen1) > score:
1183 if evalpath(striplen1) > score:
1184 striplen = striplen1
1184 striplen = striplen1
1185 res = lambda p: os.path.join(dest, p[striplen:])
1185 res = lambda p: os.path.join(dest, p[striplen:])
1186 else:
1186 else:
1187 # a file
1187 # a file
1188 if destdirexists:
1188 if destdirexists:
1189 res = lambda p: os.path.join(dest, os.path.basename(p))
1189 res = lambda p: os.path.join(dest, os.path.basename(p))
1190 else:
1190 else:
1191 res = lambda p: dest
1191 res = lambda p: dest
1192 return res
1192 return res
1193
1193
1194
1194
1195 pats = list(pats)
1195 pats = list(pats)
1196 if not pats:
1196 if not pats:
1197 raise util.Abort(_('no source or destination specified'))
1197 raise util.Abort(_('no source or destination specified'))
1198 if len(pats) == 1:
1198 if len(pats) == 1:
1199 raise util.Abort(_('no destination specified'))
1199 raise util.Abort(_('no destination specified'))
1200 dest = pats.pop()
1200 dest = pats.pop()
1201 destdirexists = os.path.isdir(dest)
1201 destdirexists = os.path.isdir(dest)
1202 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1202 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1203 raise util.Abort(_('with multiple sources, destination must be an '
1203 raise util.Abort(_('with multiple sources, destination must be an '
1204 'existing directory'))
1204 'existing directory'))
1205 if opts['after']:
1205 if opts['after']:
1206 tfn = targetpathafterfn
1206 tfn = targetpathafterfn
1207 else:
1207 else:
1208 tfn = targetpathfn
1208 tfn = targetpathfn
1209 copylist = []
1209 copylist = []
1210 for pat in pats:
1210 for pat in pats:
1211 srcs = []
1211 srcs = []
1212 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
1212 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
1213 origsrc = okaytocopy(abssrc, relsrc, exact)
1213 origsrc = okaytocopy(abssrc, relsrc, exact)
1214 if origsrc:
1214 if origsrc:
1215 srcs.append((origsrc, abssrc, relsrc, exact))
1215 srcs.append((origsrc, abssrc, relsrc, exact))
1216 if not srcs:
1216 if not srcs:
1217 continue
1217 continue
1218 copylist.append((tfn(pat, dest, srcs), srcs))
1218 copylist.append((tfn(pat, dest, srcs), srcs))
1219 if not copylist:
1219 if not copylist:
1220 raise util.Abort(_('no files to copy'))
1220 raise util.Abort(_('no files to copy'))
1221
1221
1222 for targetpath, srcs in copylist:
1222 for targetpath, srcs in copylist:
1223 for origsrc, abssrc, relsrc, exact in srcs:
1223 for origsrc, abssrc, relsrc, exact in srcs:
1224 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1224 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1225
1225
1226 if errors:
1226 if errors:
1227 ui.warn(_('(consider using --after)\n'))
1227 ui.warn(_('(consider using --after)\n'))
1228 return errors, copied
1228 return errors, copied
1229
1229
1230 def copy(ui, repo, *pats, **opts):
1230 def copy(ui, repo, *pats, **opts):
1231 """mark files as copied for the next commit
1231 """mark files as copied for the next commit
1232
1232
1233 Mark dest as having copies of source files. If dest is a
1233 Mark dest as having copies of source files. If dest is a
1234 directory, copies are put in that directory. If dest is a file,
1234 directory, copies are put in that directory. If dest is a file,
1235 there can only be one source.
1235 there can only be one source.
1236
1236
1237 By default, this command copies the contents of files as they
1237 By default, this command copies the contents of files as they
1238 stand in the working directory. If invoked with --after, the
1238 stand in the working directory. If invoked with --after, the
1239 operation is recorded, but no copying is performed.
1239 operation is recorded, but no copying is performed.
1240
1240
1241 This command takes effect in the next commit.
1241 This command takes effect in the next commit.
1242
1242
1243 NOTE: This command should be treated as experimental. While it
1243 NOTE: This command should be treated as experimental. While it
1244 should properly record copied files, this information is not yet
1244 should properly record copied files, this information is not yet
1245 fully used by merge, nor fully reported by log.
1245 fully used by merge, nor fully reported by log.
1246 """
1246 """
1247 try:
1247 try:
1248 wlock = repo.wlock(0)
1248 wlock = repo.wlock(0)
1249 errs, copied = docopy(ui, repo, pats, opts, wlock)
1249 errs, copied = docopy(ui, repo, pats, opts, wlock)
1250 except lock.LockHeld, inst:
1250 except lock.LockHeld, inst:
1251 ui.warn(_("repository lock held by %s\n") % inst.args[0])
1251 ui.warn(_("repository lock held by %s\n") % inst.args[0])
1252 errs = 1
1252 errs = 1
1253 return errs
1253 return errs
1254
1254
1255 def debugancestor(ui, index, rev1, rev2):
1255 def debugancestor(ui, index, rev1, rev2):
1256 """find the ancestor revision of two revisions in a given index"""
1256 """find the ancestor revision of two revisions in a given index"""
1257 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "")
1257 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "")
1258 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1258 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1259 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1259 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1260
1260
1261 def debugcomplete(ui, cmd):
1261 def debugcomplete(ui, cmd):
1262 """returns the completion list associated with the given command"""
1262 """returns the completion list associated with the given command"""
1263 clist = findpossible(cmd).keys()
1263 clist = findpossible(cmd).keys()
1264 clist.sort()
1264 clist.sort()
1265 ui.write("%s\n" % " ".join(clist))
1265 ui.write("%s\n" % " ".join(clist))
1266
1266
1267 def debugrebuildstate(ui, repo, rev=None):
1267 def debugrebuildstate(ui, repo, rev=None):
1268 """rebuild the dirstate as it would look like for the given revision"""
1268 """rebuild the dirstate as it would look like for the given revision"""
1269 if not rev:
1269 if not rev:
1270 rev = repo.changelog.tip()
1270 rev = repo.changelog.tip()
1271 else:
1271 else:
1272 rev = repo.lookup(rev)
1272 rev = repo.lookup(rev)
1273 change = repo.changelog.read(rev)
1273 change = repo.changelog.read(rev)
1274 n = change[0]
1274 n = change[0]
1275 files = repo.manifest.readflags(n)
1275 files = repo.manifest.readflags(n)
1276 wlock = repo.wlock()
1276 wlock = repo.wlock()
1277 repo.dirstate.rebuild(rev, files.iteritems())
1277 repo.dirstate.rebuild(rev, files.iteritems())
1278
1278
1279 def debugcheckstate(ui, repo):
1279 def debugcheckstate(ui, repo):
1280 """validate the correctness of the current dirstate"""
1280 """validate the correctness of the current dirstate"""
1281 parent1, parent2 = repo.dirstate.parents()
1281 parent1, parent2 = repo.dirstate.parents()
1282 repo.dirstate.read()
1282 repo.dirstate.read()
1283 dc = repo.dirstate.map
1283 dc = repo.dirstate.map
1284 keys = dc.keys()
1284 keys = dc.keys()
1285 keys.sort()
1285 keys.sort()
1286 m1n = repo.changelog.read(parent1)[0]
1286 m1n = repo.changelog.read(parent1)[0]
1287 m2n = repo.changelog.read(parent2)[0]
1287 m2n = repo.changelog.read(parent2)[0]
1288 m1 = repo.manifest.read(m1n)
1288 m1 = repo.manifest.read(m1n)
1289 m2 = repo.manifest.read(m2n)
1289 m2 = repo.manifest.read(m2n)
1290 errors = 0
1290 errors = 0
1291 for f in dc:
1291 for f in dc:
1292 state = repo.dirstate.state(f)
1292 state = repo.dirstate.state(f)
1293 if state in "nr" and f not in m1:
1293 if state in "nr" and f not in m1:
1294 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1294 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1295 errors += 1
1295 errors += 1
1296 if state in "a" and f in m1:
1296 if state in "a" and f in m1:
1297 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1297 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1298 errors += 1
1298 errors += 1
1299 if state in "m" and f not in m1 and f not in m2:
1299 if state in "m" and f not in m1 and f not in m2:
1300 ui.warn(_("%s in state %s, but not in either manifest\n") %
1300 ui.warn(_("%s in state %s, but not in either manifest\n") %
1301 (f, state))
1301 (f, state))
1302 errors += 1
1302 errors += 1
1303 for f in m1:
1303 for f in m1:
1304 state = repo.dirstate.state(f)
1304 state = repo.dirstate.state(f)
1305 if state not in "nrm":
1305 if state not in "nrm":
1306 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1306 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1307 errors += 1
1307 errors += 1
1308 if errors:
1308 if errors:
1309 error = _(".hg/dirstate inconsistent with current parent's manifest")
1309 error = _(".hg/dirstate inconsistent with current parent's manifest")
1310 raise util.Abort(error)
1310 raise util.Abort(error)
1311
1311
1312 def debugconfig(ui, repo):
1312 def debugconfig(ui, repo):
1313 """show combined config settings from all hgrc files"""
1313 """show combined config settings from all hgrc files"""
1314 for section, name, value in ui.walkconfig():
1314 for section, name, value in ui.walkconfig():
1315 ui.write('%s.%s=%s\n' % (section, name, value))
1315 ui.write('%s.%s=%s\n' % (section, name, value))
1316
1316
1317 def debugsetparents(ui, repo, rev1, rev2=None):
1317 def debugsetparents(ui, repo, rev1, rev2=None):
1318 """manually set the parents of the current working directory
1318 """manually set the parents of the current working directory
1319
1319
1320 This is useful for writing repository conversion tools, but should
1320 This is useful for writing repository conversion tools, but should
1321 be used with care.
1321 be used with care.
1322 """
1322 """
1323
1323
1324 if not rev2:
1324 if not rev2:
1325 rev2 = hex(nullid)
1325 rev2 = hex(nullid)
1326
1326
1327 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1327 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1328
1328
1329 def debugstate(ui, repo):
1329 def debugstate(ui, repo):
1330 """show the contents of the current dirstate"""
1330 """show the contents of the current dirstate"""
1331 repo.dirstate.read()
1331 repo.dirstate.read()
1332 dc = repo.dirstate.map
1332 dc = repo.dirstate.map
1333 keys = dc.keys()
1333 keys = dc.keys()
1334 keys.sort()
1334 keys.sort()
1335 for file_ in keys:
1335 for file_ in keys:
1336 ui.write("%c %3o %10d %s %s\n"
1336 ui.write("%c %3o %10d %s %s\n"
1337 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1337 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1338 time.strftime("%x %X",
1338 time.strftime("%x %X",
1339 time.localtime(dc[file_][3])), file_))
1339 time.localtime(dc[file_][3])), file_))
1340 for f in repo.dirstate.copies:
1340 for f in repo.dirstate.copies:
1341 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1341 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1342
1342
1343 def debugdata(ui, file_, rev):
1343 def debugdata(ui, file_, rev):
1344 """dump the contents of an data file revision"""
1344 """dump the contents of an data file revision"""
1345 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1345 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1346 file_[:-2] + ".i", file_)
1346 file_[:-2] + ".i", file_)
1347 try:
1347 try:
1348 ui.write(r.revision(r.lookup(rev)))
1348 ui.write(r.revision(r.lookup(rev)))
1349 except KeyError:
1349 except KeyError:
1350 raise util.Abort(_('invalid revision identifier %s'), rev)
1350 raise util.Abort(_('invalid revision identifier %s'), rev)
1351
1351
1352 def debugindex(ui, file_):
1352 def debugindex(ui, file_):
1353 """dump the contents of an index file"""
1353 """dump the contents of an index file"""
1354 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1354 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1355 ui.write(" rev offset length base linkrev" +
1355 ui.write(" rev offset length base linkrev" +
1356 " nodeid p1 p2\n")
1356 " nodeid p1 p2\n")
1357 for i in range(r.count()):
1357 for i in range(r.count()):
1358 e = r.index[i]
1358 e = r.index[i]
1359 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1359 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1360 i, e[0], e[1], e[2], e[3],
1360 i, e[0], e[1], e[2], e[3],
1361 short(e[6]), short(e[4]), short(e[5])))
1361 short(e[6]), short(e[4]), short(e[5])))
1362
1362
1363 def debugindexdot(ui, file_):
1363 def debugindexdot(ui, file_):
1364 """dump an index DAG as a .dot file"""
1364 """dump an index DAG as a .dot file"""
1365 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1365 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "")
1366 ui.write("digraph G {\n")
1366 ui.write("digraph G {\n")
1367 for i in range(r.count()):
1367 for i in range(r.count()):
1368 e = r.index[i]
1368 e = r.index[i]
1369 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
1369 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
1370 if e[5] != nullid:
1370 if e[5] != nullid:
1371 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
1371 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
1372 ui.write("}\n")
1372 ui.write("}\n")
1373
1373
1374 def debugrename(ui, repo, file, rev=None):
1374 def debugrename(ui, repo, file, rev=None):
1375 """dump rename information"""
1375 """dump rename information"""
1376 r = repo.file(relpath(repo, [file])[0])
1376 r = repo.file(relpath(repo, [file])[0])
1377 if rev:
1377 if rev:
1378 try:
1378 try:
1379 # assume all revision numbers are for changesets
1379 # assume all revision numbers are for changesets
1380 n = repo.lookup(rev)
1380 n = repo.lookup(rev)
1381 change = repo.changelog.read(n)
1381 change = repo.changelog.read(n)
1382 m = repo.manifest.read(change[0])
1382 m = repo.manifest.read(change[0])
1383 n = m[relpath(repo, [file])[0]]
1383 n = m[relpath(repo, [file])[0]]
1384 except (hg.RepoError, KeyError):
1384 except (hg.RepoError, KeyError):
1385 n = r.lookup(rev)
1385 n = r.lookup(rev)
1386 else:
1386 else:
1387 n = r.tip()
1387 n = r.tip()
1388 m = r.renamed(n)
1388 m = r.renamed(n)
1389 if m:
1389 if m:
1390 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1390 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1391 else:
1391 else:
1392 ui.write(_("not renamed\n"))
1392 ui.write(_("not renamed\n"))
1393
1393
1394 def debugwalk(ui, repo, *pats, **opts):
1394 def debugwalk(ui, repo, *pats, **opts):
1395 """show how files match on given patterns"""
1395 """show how files match on given patterns"""
1396 items = list(walk(repo, pats, opts))
1396 items = list(walk(repo, pats, opts))
1397 if not items:
1397 if not items:
1398 return
1398 return
1399 fmt = '%%s %%-%ds %%-%ds %%s' % (
1399 fmt = '%%s %%-%ds %%-%ds %%s' % (
1400 max([len(abs) for (src, abs, rel, exact) in items]),
1400 max([len(abs) for (src, abs, rel, exact) in items]),
1401 max([len(rel) for (src, abs, rel, exact) in items]))
1401 max([len(rel) for (src, abs, rel, exact) in items]))
1402 for src, abs, rel, exact in items:
1402 for src, abs, rel, exact in items:
1403 line = fmt % (src, abs, rel, exact and 'exact' or '')
1403 line = fmt % (src, abs, rel, exact and 'exact' or '')
1404 ui.write("%s\n" % line.rstrip())
1404 ui.write("%s\n" % line.rstrip())
1405
1405
1406 def diff(ui, repo, *pats, **opts):
1406 def diff(ui, repo, *pats, **opts):
1407 """diff repository (or selected files)
1407 """diff repository (or selected files)
1408
1408
1409 Show differences between revisions for the specified files.
1409 Show differences between revisions for the specified files.
1410
1410
1411 Differences between files are shown using the unified diff format.
1411 Differences between files are shown using the unified diff format.
1412
1412
1413 When two revision arguments are given, then changes are shown
1413 When two revision arguments are given, then changes are shown
1414 between those revisions. If only one revision is specified then
1414 between those revisions. If only one revision is specified then
1415 that revision is compared to the working directory, and, when no
1415 that revision is compared to the working directory, and, when no
1416 revisions are specified, the working directory files are compared
1416 revisions are specified, the working directory files are compared
1417 to its parent.
1417 to its parent.
1418
1418
1419 Without the -a option, diff will avoid generating diffs of files
1419 Without the -a option, diff will avoid generating diffs of files
1420 it detects as binary. With -a, diff will generate a diff anyway,
1420 it detects as binary. With -a, diff will generate a diff anyway,
1421 probably with undesirable results.
1421 probably with undesirable results.
1422 """
1422 """
1423 node1, node2 = None, None
1423 node1, node2 = None, None
1424 revs = [repo.lookup(x) for x in opts['rev']]
1424 revs = [repo.lookup(x) for x in opts['rev']]
1425
1425
1426 if len(revs) > 0:
1426 if len(revs) > 0:
1427 node1 = revs[0]
1427 node1 = revs[0]
1428 if len(revs) > 1:
1428 if len(revs) > 1:
1429 node2 = revs[1]
1429 node2 = revs[1]
1430 if len(revs) > 2:
1430 if len(revs) > 2:
1431 raise util.Abort(_("too many revisions to diff"))
1431 raise util.Abort(_("too many revisions to diff"))
1432
1432
1433 fns, matchfn, anypats = matchpats(repo, pats, opts)
1433 fns, matchfn, anypats = matchpats(repo, pats, opts)
1434
1434
1435 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1435 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1436 text=opts['text'], opts=opts)
1436 text=opts['text'], opts=opts)
1437
1437
1438 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1438 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1439 node = repo.lookup(changeset)
1439 node = repo.lookup(changeset)
1440 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1440 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1441 if opts['switch_parent']:
1441 if opts['switch_parent']:
1442 parents.reverse()
1442 parents.reverse()
1443 prev = (parents and parents[0]) or nullid
1443 prev = (parents and parents[0]) or nullid
1444 change = repo.changelog.read(node)
1444 change = repo.changelog.read(node)
1445
1445
1446 fp = make_file(repo, repo.changelog, opts['output'],
1446 fp = make_file(repo, repo.changelog, opts['output'],
1447 node=node, total=total, seqno=seqno,
1447 node=node, total=total, seqno=seqno,
1448 revwidth=revwidth)
1448 revwidth=revwidth)
1449 if fp != sys.stdout:
1449 if fp != sys.stdout:
1450 ui.note("%s\n" % fp.name)
1450 ui.note("%s\n" % fp.name)
1451
1451
1452 fp.write("# HG changeset patch\n")
1452 fp.write("# HG changeset patch\n")
1453 fp.write("# User %s\n" % change[1])
1453 fp.write("# User %s\n" % change[1])
1454 fp.write("# Node ID %s\n" % hex(node))
1454 fp.write("# Node ID %s\n" % hex(node))
1455 fp.write("# Parent %s\n" % hex(prev))
1455 fp.write("# Parent %s\n" % hex(prev))
1456 if len(parents) > 1:
1456 if len(parents) > 1:
1457 fp.write("# Parent %s\n" % hex(parents[1]))
1457 fp.write("# Parent %s\n" % hex(parents[1]))
1458 fp.write(change[4].rstrip())
1458 fp.write(change[4].rstrip())
1459 fp.write("\n\n")
1459 fp.write("\n\n")
1460
1460
1461 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1461 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1462 if fp != sys.stdout:
1462 if fp != sys.stdout:
1463 fp.close()
1463 fp.close()
1464
1464
1465 def export(ui, repo, *changesets, **opts):
1465 def export(ui, repo, *changesets, **opts):
1466 """dump the header and diffs for one or more changesets
1466 """dump the header and diffs for one or more changesets
1467
1467
1468 Print the changeset header and diffs for one or more revisions.
1468 Print the changeset header and diffs for one or more revisions.
1469
1469
1470 The information shown in the changeset header is: author,
1470 The information shown in the changeset header is: author,
1471 changeset hash, parent and commit comment.
1471 changeset hash, parent and commit comment.
1472
1472
1473 Output may be to a file, in which case the name of the file is
1473 Output may be to a file, in which case the name of the file is
1474 given using a format string. The formatting rules are as follows:
1474 given using a format string. The formatting rules are as follows:
1475
1475
1476 %% literal "%" character
1476 %% literal "%" character
1477 %H changeset hash (40 bytes of hexadecimal)
1477 %H changeset hash (40 bytes of hexadecimal)
1478 %N number of patches being generated
1478 %N number of patches being generated
1479 %R changeset revision number
1479 %R changeset revision number
1480 %b basename of the exporting repository
1480 %b basename of the exporting repository
1481 %h short-form changeset hash (12 bytes of hexadecimal)
1481 %h short-form changeset hash (12 bytes of hexadecimal)
1482 %n zero-padded sequence number, starting at 1
1482 %n zero-padded sequence number, starting at 1
1483 %r zero-padded changeset revision number
1483 %r zero-padded changeset revision number
1484
1484
1485 Without the -a option, export will avoid generating diffs of files
1485 Without the -a option, export will avoid generating diffs of files
1486 it detects as binary. With -a, export will generate a diff anyway,
1486 it detects as binary. With -a, export will generate a diff anyway,
1487 probably with undesirable results.
1487 probably with undesirable results.
1488
1488
1489 With the --switch-parent option, the diff will be against the second
1489 With the --switch-parent option, the diff will be against the second
1490 parent. It can be useful to review a merge.
1490 parent. It can be useful to review a merge.
1491 """
1491 """
1492 if not changesets:
1492 if not changesets:
1493 raise util.Abort(_("export requires at least one changeset"))
1493 raise util.Abort(_("export requires at least one changeset"))
1494 seqno = 0
1494 seqno = 0
1495 revs = list(revrange(ui, repo, changesets))
1495 revs = list(revrange(ui, repo, changesets))
1496 total = len(revs)
1496 total = len(revs)
1497 revwidth = max(map(len, revs))
1497 revwidth = max(map(len, revs))
1498 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1498 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1499 ui.note(msg)
1499 ui.note(msg)
1500 for cset in revs:
1500 for cset in revs:
1501 seqno += 1
1501 seqno += 1
1502 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1502 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1503
1503
1504 def forget(ui, repo, *pats, **opts):
1504 def forget(ui, repo, *pats, **opts):
1505 """don't add the specified files on the next commit
1505 """don't add the specified files on the next commit
1506
1506
1507 Undo an 'hg add' scheduled for the next commit.
1507 Undo an 'hg add' scheduled for the next commit.
1508 """
1508 """
1509 forget = []
1509 forget = []
1510 for src, abs, rel, exact in walk(repo, pats, opts):
1510 for src, abs, rel, exact in walk(repo, pats, opts):
1511 if repo.dirstate.state(abs) == 'a':
1511 if repo.dirstate.state(abs) == 'a':
1512 forget.append(abs)
1512 forget.append(abs)
1513 if ui.verbose or not exact:
1513 if ui.verbose or not exact:
1514 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1514 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1515 repo.forget(forget)
1515 repo.forget(forget)
1516
1516
1517 def grep(ui, repo, pattern, *pats, **opts):
1517 def grep(ui, repo, pattern, *pats, **opts):
1518 """search for a pattern in specified files and revisions
1518 """search for a pattern in specified files and revisions
1519
1519
1520 Search revisions of files for a regular expression.
1520 Search revisions of files for a regular expression.
1521
1521
1522 This command behaves differently than Unix grep. It only accepts
1522 This command behaves differently than Unix grep. It only accepts
1523 Python/Perl regexps. It searches repository history, not the
1523 Python/Perl regexps. It searches repository history, not the
1524 working directory. It always prints the revision number in which
1524 working directory. It always prints the revision number in which
1525 a match appears.
1525 a match appears.
1526
1526
1527 By default, grep only prints output for the first revision of a
1527 By default, grep only prints output for the first revision of a
1528 file in which it finds a match. To get it to print every revision
1528 file in which it finds a match. To get it to print every revision
1529 that contains a change in match status ("-" for a match that
1529 that contains a change in match status ("-" for a match that
1530 becomes a non-match, or "+" for a non-match that becomes a match),
1530 becomes a non-match, or "+" for a non-match that becomes a match),
1531 use the --all flag.
1531 use the --all flag.
1532 """
1532 """
1533 reflags = 0
1533 reflags = 0
1534 if opts['ignore_case']:
1534 if opts['ignore_case']:
1535 reflags |= re.I
1535 reflags |= re.I
1536 regexp = re.compile(pattern, reflags)
1536 regexp = re.compile(pattern, reflags)
1537 sep, eol = ':', '\n'
1537 sep, eol = ':', '\n'
1538 if opts['print0']:
1538 if opts['print0']:
1539 sep = eol = '\0'
1539 sep = eol = '\0'
1540
1540
1541 fcache = {}
1541 fcache = {}
1542 def getfile(fn):
1542 def getfile(fn):
1543 if fn not in fcache:
1543 if fn not in fcache:
1544 fcache[fn] = repo.file(fn)
1544 fcache[fn] = repo.file(fn)
1545 return fcache[fn]
1545 return fcache[fn]
1546
1546
1547 def matchlines(body):
1547 def matchlines(body):
1548 begin = 0
1548 begin = 0
1549 linenum = 0
1549 linenum = 0
1550 while True:
1550 while True:
1551 match = regexp.search(body, begin)
1551 match = regexp.search(body, begin)
1552 if not match:
1552 if not match:
1553 break
1553 break
1554 mstart, mend = match.span()
1554 mstart, mend = match.span()
1555 linenum += body.count('\n', begin, mstart) + 1
1555 linenum += body.count('\n', begin, mstart) + 1
1556 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1556 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1557 lend = body.find('\n', mend)
1557 lend = body.find('\n', mend)
1558 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1558 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1559 begin = lend + 1
1559 begin = lend + 1
1560
1560
1561 class linestate(object):
1561 class linestate(object):
1562 def __init__(self, line, linenum, colstart, colend):
1562 def __init__(self, line, linenum, colstart, colend):
1563 self.line = line
1563 self.line = line
1564 self.linenum = linenum
1564 self.linenum = linenum
1565 self.colstart = colstart
1565 self.colstart = colstart
1566 self.colend = colend
1566 self.colend = colend
1567 def __eq__(self, other):
1567 def __eq__(self, other):
1568 return self.line == other.line
1568 return self.line == other.line
1569 def __hash__(self):
1569 def __hash__(self):
1570 return hash(self.line)
1570 return hash(self.line)
1571
1571
1572 matches = {}
1572 matches = {}
1573 def grepbody(fn, rev, body):
1573 def grepbody(fn, rev, body):
1574 matches[rev].setdefault(fn, {})
1574 matches[rev].setdefault(fn, {})
1575 m = matches[rev][fn]
1575 m = matches[rev][fn]
1576 for lnum, cstart, cend, line in matchlines(body):
1576 for lnum, cstart, cend, line in matchlines(body):
1577 s = linestate(line, lnum, cstart, cend)
1577 s = linestate(line, lnum, cstart, cend)
1578 m[s] = s
1578 m[s] = s
1579
1579
1580 # FIXME: prev isn't used, why ?
1580 # FIXME: prev isn't used, why ?
1581 prev = {}
1581 prev = {}
1582 ucache = {}
1582 ucache = {}
1583 def display(fn, rev, states, prevstates):
1583 def display(fn, rev, states, prevstates):
1584 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1584 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1585 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1585 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1586 counts = {'-': 0, '+': 0}
1586 counts = {'-': 0, '+': 0}
1587 filerevmatches = {}
1587 filerevmatches = {}
1588 for l in diff:
1588 for l in diff:
1589 if incrementing or not opts['all']:
1589 if incrementing or not opts['all']:
1590 change = ((l in prevstates) and '-') or '+'
1590 change = ((l in prevstates) and '-') or '+'
1591 r = rev
1591 r = rev
1592 else:
1592 else:
1593 change = ((l in states) and '-') or '+'
1593 change = ((l in states) and '-') or '+'
1594 r = prev[fn]
1594 r = prev[fn]
1595 cols = [fn, str(rev)]
1595 cols = [fn, str(rev)]
1596 if opts['line_number']:
1596 if opts['line_number']:
1597 cols.append(str(l.linenum))
1597 cols.append(str(l.linenum))
1598 if opts['all']:
1598 if opts['all']:
1599 cols.append(change)
1599 cols.append(change)
1600 if opts['user']:
1600 if opts['user']:
1601 cols.append(trimuser(ui, getchange(rev)[1], rev,
1601 cols.append(trimuser(ui, getchange(rev)[1], rev,
1602 ucache))
1602 ucache))
1603 if opts['files_with_matches']:
1603 if opts['files_with_matches']:
1604 c = (fn, rev)
1604 c = (fn, rev)
1605 if c in filerevmatches:
1605 if c in filerevmatches:
1606 continue
1606 continue
1607 filerevmatches[c] = 1
1607 filerevmatches[c] = 1
1608 else:
1608 else:
1609 cols.append(l.line)
1609 cols.append(l.line)
1610 ui.write(sep.join(cols), eol)
1610 ui.write(sep.join(cols), eol)
1611 counts[change] += 1
1611 counts[change] += 1
1612 return counts['+'], counts['-']
1612 return counts['+'], counts['-']
1613
1613
1614 fstate = {}
1614 fstate = {}
1615 skip = {}
1615 skip = {}
1616 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1616 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1617 count = 0
1617 count = 0
1618 incrementing = False
1618 incrementing = False
1619 for st, rev, fns in changeiter:
1619 for st, rev, fns in changeiter:
1620 if st == 'window':
1620 if st == 'window':
1621 incrementing = rev
1621 incrementing = rev
1622 matches.clear()
1622 matches.clear()
1623 elif st == 'add':
1623 elif st == 'add':
1624 change = repo.changelog.read(repo.lookup(str(rev)))
1624 change = repo.changelog.read(repo.lookup(str(rev)))
1625 mf = repo.manifest.read(change[0])
1625 mf = repo.manifest.read(change[0])
1626 matches[rev] = {}
1626 matches[rev] = {}
1627 for fn in fns:
1627 for fn in fns:
1628 if fn in skip:
1628 if fn in skip:
1629 continue
1629 continue
1630 fstate.setdefault(fn, {})
1630 fstate.setdefault(fn, {})
1631 try:
1631 try:
1632 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1632 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1633 except KeyError:
1633 except KeyError:
1634 pass
1634 pass
1635 elif st == 'iter':
1635 elif st == 'iter':
1636 states = matches[rev].items()
1636 states = matches[rev].items()
1637 states.sort()
1637 states.sort()
1638 for fn, m in states:
1638 for fn, m in states:
1639 if fn in skip:
1639 if fn in skip:
1640 continue
1640 continue
1641 if incrementing or not opts['all'] or fstate[fn]:
1641 if incrementing or not opts['all'] or fstate[fn]:
1642 pos, neg = display(fn, rev, m, fstate[fn])
1642 pos, neg = display(fn, rev, m, fstate[fn])
1643 count += pos + neg
1643 count += pos + neg
1644 if pos and not opts['all']:
1644 if pos and not opts['all']:
1645 skip[fn] = True
1645 skip[fn] = True
1646 fstate[fn] = m
1646 fstate[fn] = m
1647 prev[fn] = rev
1647 prev[fn] = rev
1648
1648
1649 if not incrementing:
1649 if not incrementing:
1650 fstate = fstate.items()
1650 fstate = fstate.items()
1651 fstate.sort()
1651 fstate.sort()
1652 for fn, state in fstate:
1652 for fn, state in fstate:
1653 if fn in skip:
1653 if fn in skip:
1654 continue
1654 continue
1655 display(fn, rev, {}, state)
1655 display(fn, rev, {}, state)
1656 return (count == 0 and 1) or 0
1656 return (count == 0 and 1) or 0
1657
1657
1658 def heads(ui, repo, **opts):
1658 def heads(ui, repo, **opts):
1659 """show current repository heads
1659 """show current repository heads
1660
1660
1661 Show all repository head changesets.
1661 Show all repository head changesets.
1662
1662
1663 Repository "heads" are changesets that don't have children
1663 Repository "heads" are changesets that don't have children
1664 changesets. They are where development generally takes place and
1664 changesets. They are where development generally takes place and
1665 are the usual targets for update and merge operations.
1665 are the usual targets for update and merge operations.
1666 """
1666 """
1667 if opts['rev']:
1667 if opts['rev']:
1668 heads = repo.heads(repo.lookup(opts['rev']))
1668 heads = repo.heads(repo.lookup(opts['rev']))
1669 else:
1669 else:
1670 heads = repo.heads()
1670 heads = repo.heads()
1671 br = None
1671 br = None
1672 if opts['branches']:
1672 if opts['branches']:
1673 br = repo.branchlookup(heads)
1673 br = repo.branchlookup(heads)
1674 displayer = show_changeset(ui, repo, opts)
1674 displayer = show_changeset(ui, repo, opts)
1675 for n in heads:
1675 for n in heads:
1676 displayer.show(changenode=n, brinfo=br)
1676 displayer.show(changenode=n, brinfo=br)
1677
1677
1678 def identify(ui, repo):
1678 def identify(ui, repo):
1679 """print information about the working copy
1679 """print information about the working copy
1680
1680
1681 Print a short summary of the current state of the repo.
1681 Print a short summary of the current state of the repo.
1682
1682
1683 This summary identifies the repository state using one or two parent
1683 This summary identifies the repository state using one or two parent
1684 hash identifiers, followed by a "+" if there are uncommitted changes
1684 hash identifiers, followed by a "+" if there are uncommitted changes
1685 in the working directory, followed by a list of tags for this revision.
1685 in the working directory, followed by a list of tags for this revision.
1686 """
1686 """
1687 parents = [p for p in repo.dirstate.parents() if p != nullid]
1687 parents = [p for p in repo.dirstate.parents() if p != nullid]
1688 if not parents:
1688 if not parents:
1689 ui.write(_("unknown\n"))
1689 ui.write(_("unknown\n"))
1690 return
1690 return
1691
1691
1692 hexfunc = ui.verbose and hex or short
1692 hexfunc = ui.verbose and hex or short
1693 modified, added, removed, deleted, unknown = repo.changes()
1693 modified, added, removed, deleted, unknown = repo.changes()
1694 output = ["%s%s" %
1694 output = ["%s%s" %
1695 ('+'.join([hexfunc(parent) for parent in parents]),
1695 ('+'.join([hexfunc(parent) for parent in parents]),
1696 (modified or added or removed or deleted) and "+" or "")]
1696 (modified or added or removed or deleted) and "+" or "")]
1697
1697
1698 if not ui.quiet:
1698 if not ui.quiet:
1699 # multiple tags for a single parent separated by '/'
1699 # multiple tags for a single parent separated by '/'
1700 parenttags = ['/'.join(tags)
1700 parenttags = ['/'.join(tags)
1701 for tags in map(repo.nodetags, parents) if tags]
1701 for tags in map(repo.nodetags, parents) if tags]
1702 # tags for multiple parents separated by ' + '
1702 # tags for multiple parents separated by ' + '
1703 if parenttags:
1703 if parenttags:
1704 output.append(' + '.join(parenttags))
1704 output.append(' + '.join(parenttags))
1705
1705
1706 ui.write("%s\n" % ' '.join(output))
1706 ui.write("%s\n" % ' '.join(output))
1707
1707
1708 def import_(ui, repo, patch1, *patches, **opts):
1708 def import_(ui, repo, patch1, *patches, **opts):
1709 """import an ordered set of patches
1709 """import an ordered set of patches
1710
1710
1711 Import a list of patches and commit them individually.
1711 Import a list of patches and commit them individually.
1712
1712
1713 If there are outstanding changes in the working directory, import
1713 If there are outstanding changes in the working directory, import
1714 will abort unless given the -f flag.
1714 will abort unless given the -f flag.
1715
1715
1716 If a patch looks like a mail message (its first line starts with
1716 If a patch looks like a mail message (its first line starts with
1717 "From " or looks like an RFC822 header), it will not be applied
1717 "From " or looks like an RFC822 header), it will not be applied
1718 unless the -f option is used. The importer neither parses nor
1718 unless the -f option is used. The importer neither parses nor
1719 discards mail headers, so use -f only to override the "mailness"
1719 discards mail headers, so use -f only to override the "mailness"
1720 safety check, not to import a real mail message.
1720 safety check, not to import a real mail message.
1721 """
1721 """
1722 patches = (patch1,) + patches
1722 patches = (patch1,) + patches
1723
1723
1724 if not opts['force']:
1724 if not opts['force']:
1725 modified, added, removed, deleted, unknown = repo.changes()
1725 modified, added, removed, deleted, unknown = repo.changes()
1726 if modified or added or removed or deleted:
1726 if modified or added or removed or deleted:
1727 raise util.Abort(_("outstanding uncommitted changes"))
1727 raise util.Abort(_("outstanding uncommitted changes"))
1728
1728
1729 d = opts["base"]
1729 d = opts["base"]
1730 strip = opts["strip"]
1730 strip = opts["strip"]
1731
1731
1732 mailre = re.compile(r'(?:From |[\w-]+:)')
1732 mailre = re.compile(r'(?:From |[\w-]+:)')
1733
1733
1734 # attempt to detect the start of a patch
1734 # attempt to detect the start of a patch
1735 # (this heuristic is borrowed from quilt)
1735 # (this heuristic is borrowed from quilt)
1736 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1736 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1737 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1737 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1738 '(---|\*\*\*)[ \t])')
1738 '(---|\*\*\*)[ \t])')
1739
1739
1740 for patch in patches:
1740 for patch in patches:
1741 ui.status(_("applying %s\n") % patch)
1741 ui.status(_("applying %s\n") % patch)
1742 pf = os.path.join(d, patch)
1742 pf = os.path.join(d, patch)
1743
1743
1744 message = []
1744 message = []
1745 user = None
1745 user = None
1746 hgpatch = False
1746 hgpatch = False
1747 for line in file(pf):
1747 for line in file(pf):
1748 line = line.rstrip()
1748 line = line.rstrip()
1749 if (not message and not hgpatch and
1749 if (not message and not hgpatch and
1750 mailre.match(line) and not opts['force']):
1750 mailre.match(line) and not opts['force']):
1751 if len(line) > 35:
1751 if len(line) > 35:
1752 line = line[:32] + '...'
1752 line = line[:32] + '...'
1753 raise util.Abort(_('first line looks like a '
1753 raise util.Abort(_('first line looks like a '
1754 'mail header: ') + line)
1754 'mail header: ') + line)
1755 if diffre.match(line):
1755 if diffre.match(line):
1756 break
1756 break
1757 elif hgpatch:
1757 elif hgpatch:
1758 # parse values when importing the result of an hg export
1758 # parse values when importing the result of an hg export
1759 if line.startswith("# User "):
1759 if line.startswith("# User "):
1760 user = line[7:]
1760 user = line[7:]
1761 ui.debug(_('User: %s\n') % user)
1761 ui.debug(_('User: %s\n') % user)
1762 elif not line.startswith("# ") and line:
1762 elif not line.startswith("# ") and line:
1763 message.append(line)
1763 message.append(line)
1764 hgpatch = False
1764 hgpatch = False
1765 elif line == '# HG changeset patch':
1765 elif line == '# HG changeset patch':
1766 hgpatch = True
1766 hgpatch = True
1767 message = [] # We may have collected garbage
1767 message = [] # We may have collected garbage
1768 else:
1768 else:
1769 message.append(line)
1769 message.append(line)
1770
1770
1771 # make sure message isn't empty
1771 # make sure message isn't empty
1772 if not message:
1772 if not message:
1773 message = _("imported patch %s\n") % patch
1773 message = _("imported patch %s\n") % patch
1774 else:
1774 else:
1775 message = "%s\n" % '\n'.join(message)
1775 message = "%s\n" % '\n'.join(message)
1776 ui.debug(_('message:\n%s\n') % message)
1776 ui.debug(_('message:\n%s\n') % message)
1777
1777
1778 files = util.patch(strip, pf, ui)
1778 files = util.patch(strip, pf, ui)
1779
1779
1780 if len(files) > 0:
1780 if len(files) > 0:
1781 addremove(ui, repo, *files)
1781 addremove(ui, repo, *files)
1782 repo.commit(files, message, user)
1782 repo.commit(files, message, user)
1783
1783
1784 def incoming(ui, repo, source="default", **opts):
1784 def incoming(ui, repo, source="default", **opts):
1785 """show new changesets found in source
1785 """show new changesets found in source
1786
1786
1787 Show new changesets found in the specified path/URL or the default
1787 Show new changesets found in the specified path/URL or the default
1788 pull location. These are the changesets that would be pulled if a pull
1788 pull location. These are the changesets that would be pulled if a pull
1789 was requested.
1789 was requested.
1790
1790
1791 For remote repository, using --bundle avoids downloading the changesets
1791 For remote repository, using --bundle avoids downloading the changesets
1792 twice if the incoming is followed by a pull.
1792 twice if the incoming is followed by a pull.
1793
1793
1794 See pull for valid source format details.
1794 See pull for valid source format details.
1795 """
1795 """
1796 source = ui.expandpath(source)
1796 source = ui.expandpath(source)
1797 if opts['ssh']:
1797 if opts['ssh']:
1798 ui.setconfig("ui", "ssh", opts['ssh'])
1798 ui.setconfig("ui", "ssh", opts['ssh'])
1799 if opts['remotecmd']:
1799 if opts['remotecmd']:
1800 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1800 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1801
1801
1802 other = hg.repository(ui, source)
1802 other = hg.repository(ui, source)
1803 incoming = repo.findincoming(other, force=opts["force"])
1803 incoming = repo.findincoming(other, force=opts["force"])
1804 if not incoming:
1804 if not incoming:
1805 ui.status(_("no changes found\n"))
1805 ui.status(_("no changes found\n"))
1806 return
1806 return
1807
1807
1808 cleanup = None
1808 cleanup = None
1809 try:
1809 try:
1810 fname = opts["bundle"]
1810 fname = opts["bundle"]
1811 if fname or not other.local():
1811 if fname or not other.local():
1812 # create a bundle (uncompressed if other repo is not local)
1812 # create a bundle (uncompressed if other repo is not local)
1813 cg = other.changegroup(incoming, "incoming")
1813 cg = other.changegroup(incoming, "incoming")
1814 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1814 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1815 # keep written bundle?
1815 # keep written bundle?
1816 if opts["bundle"]:
1816 if opts["bundle"]:
1817 cleanup = None
1817 cleanup = None
1818 if not other.local():
1818 if not other.local():
1819 # use the created uncompressed bundlerepo
1819 # use the created uncompressed bundlerepo
1820 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1820 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1821
1821
1822 o = other.changelog.nodesbetween(incoming)[0]
1822 o = other.changelog.nodesbetween(incoming)[0]
1823 if opts['newest_first']:
1823 if opts['newest_first']:
1824 o.reverse()
1824 o.reverse()
1825 displayer = show_changeset(ui, other, opts)
1825 displayer = show_changeset(ui, other, opts)
1826 for n in o:
1826 for n in o:
1827 parents = [p for p in other.changelog.parents(n) if p != nullid]
1827 parents = [p for p in other.changelog.parents(n) if p != nullid]
1828 if opts['no_merges'] and len(parents) == 2:
1828 if opts['no_merges'] and len(parents) == 2:
1829 continue
1829 continue
1830 displayer.show(changenode=n)
1830 displayer.show(changenode=n)
1831 if opts['patch']:
1831 if opts['patch']:
1832 prev = (parents and parents[0]) or nullid
1832 prev = (parents and parents[0]) or nullid
1833 dodiff(ui, ui, other, prev, n)
1833 dodiff(ui, ui, other, prev, n)
1834 ui.write("\n")
1834 ui.write("\n")
1835 finally:
1835 finally:
1836 if hasattr(other, 'close'):
1836 if hasattr(other, 'close'):
1837 other.close()
1837 other.close()
1838 if cleanup:
1838 if cleanup:
1839 os.unlink(cleanup)
1839 os.unlink(cleanup)
1840
1840
1841 def init(ui, dest="."):
1841 def init(ui, dest="."):
1842 """create a new repository in the given directory
1842 """create a new repository in the given directory
1843
1843
1844 Initialize a new repository in the given directory. If the given
1844 Initialize a new repository in the given directory. If the given
1845 directory does not exist, it is created.
1845 directory does not exist, it is created.
1846
1846
1847 If no directory is given, the current directory is used.
1847 If no directory is given, the current directory is used.
1848 """
1848 """
1849 if not os.path.exists(dest):
1849 if not os.path.exists(dest):
1850 os.mkdir(dest)
1850 os.mkdir(dest)
1851 hg.repository(ui, dest, create=1)
1851 hg.repository(ui, dest, create=1)
1852
1852
1853 def locate(ui, repo, *pats, **opts):
1853 def locate(ui, repo, *pats, **opts):
1854 """locate files matching specific patterns
1854 """locate files matching specific patterns
1855
1855
1856 Print all files under Mercurial control whose names match the
1856 Print all files under Mercurial control whose names match the
1857 given patterns.
1857 given patterns.
1858
1858
1859 This command searches the current directory and its
1859 This command searches the current directory and its
1860 subdirectories. To search an entire repository, move to the root
1860 subdirectories. To search an entire repository, move to the root
1861 of the repository.
1861 of the repository.
1862
1862
1863 If no patterns are given to match, this command prints all file
1863 If no patterns are given to match, this command prints all file
1864 names.
1864 names.
1865
1865
1866 If you want to feed the output of this command into the "xargs"
1866 If you want to feed the output of this command into the "xargs"
1867 command, use the "-0" option to both this command and "xargs".
1867 command, use the "-0" option to both this command and "xargs".
1868 This will avoid the problem of "xargs" treating single filenames
1868 This will avoid the problem of "xargs" treating single filenames
1869 that contain white space as multiple filenames.
1869 that contain white space as multiple filenames.
1870 """
1870 """
1871 end = opts['print0'] and '\0' or '\n'
1871 end = opts['print0'] and '\0' or '\n'
1872 rev = opts['rev']
1872 rev = opts['rev']
1873 if rev:
1873 if rev:
1874 node = repo.lookup(rev)
1874 node = repo.lookup(rev)
1875 else:
1875 else:
1876 node = None
1876 node = None
1877
1877
1878 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1878 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1879 head='(?:.*/|)'):
1879 head='(?:.*/|)'):
1880 if not node and repo.dirstate.state(abs) == '?':
1880 if not node and repo.dirstate.state(abs) == '?':
1881 continue
1881 continue
1882 if opts['fullpath']:
1882 if opts['fullpath']:
1883 ui.write(os.path.join(repo.root, abs), end)
1883 ui.write(os.path.join(repo.root, abs), end)
1884 else:
1884 else:
1885 ui.write(((pats and rel) or abs), end)
1885 ui.write(((pats and rel) or abs), end)
1886
1886
1887 def log(ui, repo, *pats, **opts):
1887 def log(ui, repo, *pats, **opts):
1888 """show revision history of entire repository or files
1888 """show revision history of entire repository or files
1889
1889
1890 Print the revision history of the specified files or the entire project.
1890 Print the revision history of the specified files or the entire project.
1891
1891
1892 By default this command outputs: changeset id and hash, tags,
1892 By default this command outputs: changeset id and hash, tags,
1893 non-trivial parents, user, date and time, and a summary for each
1893 non-trivial parents, user, date and time, and a summary for each
1894 commit. When the -v/--verbose switch is used, the list of changed
1894 commit. When the -v/--verbose switch is used, the list of changed
1895 files and full commit message is shown.
1895 files and full commit message is shown.
1896 """
1896 """
1897 class dui(object):
1897 class dui(object):
1898 # Implement and delegate some ui protocol. Save hunks of
1898 # Implement and delegate some ui protocol. Save hunks of
1899 # output for later display in the desired order.
1899 # output for later display in the desired order.
1900 def __init__(self, ui):
1900 def __init__(self, ui):
1901 self.ui = ui
1901 self.ui = ui
1902 self.hunk = {}
1902 self.hunk = {}
1903 def bump(self, rev):
1903 def bump(self, rev):
1904 self.rev = rev
1904 self.rev = rev
1905 self.hunk[rev] = []
1905 self.hunk[rev] = []
1906 def note(self, *args):
1906 def note(self, *args):
1907 if self.verbose:
1907 if self.verbose:
1908 self.write(*args)
1908 self.write(*args)
1909 def status(self, *args):
1909 def status(self, *args):
1910 if not self.quiet:
1910 if not self.quiet:
1911 self.write(*args)
1911 self.write(*args)
1912 def write(self, *args):
1912 def write(self, *args):
1913 self.hunk[self.rev].append(args)
1913 self.hunk[self.rev].append(args)
1914 def debug(self, *args):
1914 def debug(self, *args):
1915 if self.debugflag:
1915 if self.debugflag:
1916 self.write(*args)
1916 self.write(*args)
1917 def __getattr__(self, key):
1917 def __getattr__(self, key):
1918 return getattr(self.ui, key)
1918 return getattr(self.ui, key)
1919
1919
1920 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1920 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1921
1921
1922 if opts['limit']:
1922 if opts['limit']:
1923 try:
1923 try:
1924 limit = int(opts['limit'])
1924 limit = int(opts['limit'])
1925 except ValueError:
1925 except ValueError:
1926 raise util.Abort(_('limit must be a positive integer'))
1926 raise util.Abort(_('limit must be a positive integer'))
1927 if limit <= 0: raise util.Abort(_('limit must be positive'))
1927 if limit <= 0: raise util.Abort(_('limit must be positive'))
1928 else:
1928 else:
1929 limit = sys.maxint
1929 limit = sys.maxint
1930 count = 0
1930 count = 0
1931
1931
1932 displayer = show_changeset(ui, repo, opts)
1932 displayer = show_changeset(ui, repo, opts)
1933 for st, rev, fns in changeiter:
1933 for st, rev, fns in changeiter:
1934 if st == 'window':
1934 if st == 'window':
1935 du = dui(ui)
1935 du = dui(ui)
1936 displayer.ui = du
1936 displayer.ui = du
1937 elif st == 'add':
1937 elif st == 'add':
1938 du.bump(rev)
1938 du.bump(rev)
1939 changenode = repo.changelog.node(rev)
1939 changenode = repo.changelog.node(rev)
1940 parents = [p for p in repo.changelog.parents(changenode)
1940 parents = [p for p in repo.changelog.parents(changenode)
1941 if p != nullid]
1941 if p != nullid]
1942 if opts['no_merges'] and len(parents) == 2:
1942 if opts['no_merges'] and len(parents) == 2:
1943 continue
1943 continue
1944 if opts['only_merges'] and len(parents) != 2:
1944 if opts['only_merges'] and len(parents) != 2:
1945 continue
1945 continue
1946
1946
1947 if opts['keyword']:
1947 if opts['keyword']:
1948 changes = getchange(rev)
1948 changes = getchange(rev)
1949 miss = 0
1949 miss = 0
1950 for k in [kw.lower() for kw in opts['keyword']]:
1950 for k in [kw.lower() for kw in opts['keyword']]:
1951 if not (k in changes[1].lower() or
1951 if not (k in changes[1].lower() or
1952 k in changes[4].lower() or
1952 k in changes[4].lower() or
1953 k in " ".join(changes[3][:20]).lower()):
1953 k in " ".join(changes[3][:20]).lower()):
1954 miss = 1
1954 miss = 1
1955 break
1955 break
1956 if miss:
1956 if miss:
1957 continue
1957 continue
1958
1958
1959 br = None
1959 br = None
1960 if opts['branches']:
1960 if opts['branches']:
1961 br = repo.branchlookup([repo.changelog.node(rev)])
1961 br = repo.branchlookup([repo.changelog.node(rev)])
1962
1962
1963 displayer.show(rev, brinfo=br)
1963 displayer.show(rev, brinfo=br)
1964 if opts['patch']:
1964 if opts['patch']:
1965 prev = (parents and parents[0]) or nullid
1965 prev = (parents and parents[0]) or nullid
1966 dodiff(du, du, repo, prev, changenode, match=matchfn)
1966 dodiff(du, du, repo, prev, changenode, match=matchfn)
1967 du.write("\n\n")
1967 du.write("\n\n")
1968 elif st == 'iter':
1968 elif st == 'iter':
1969 if count == limit: break
1969 if count == limit: break
1970 if du.hunk[rev]:
1970 if du.hunk[rev]:
1971 count += 1
1971 count += 1
1972 for args in du.hunk[rev]:
1972 for args in du.hunk[rev]:
1973 ui.write(*args)
1973 ui.write(*args)
1974
1974
1975 def manifest(ui, repo, rev=None):
1975 def manifest(ui, repo, rev=None):
1976 """output the latest or given revision of the project manifest
1976 """output the latest or given revision of the project manifest
1977
1977
1978 Print a list of version controlled files for the given revision.
1978 Print a list of version controlled files for the given revision.
1979
1979
1980 The manifest is the list of files being version controlled. If no revision
1980 The manifest is the list of files being version controlled. If no revision
1981 is given then the tip is used.
1981 is given then the tip is used.
1982 """
1982 """
1983 if rev:
1983 if rev:
1984 try:
1984 try:
1985 # assume all revision numbers are for changesets
1985 # assume all revision numbers are for changesets
1986 n = repo.lookup(rev)
1986 n = repo.lookup(rev)
1987 change = repo.changelog.read(n)
1987 change = repo.changelog.read(n)
1988 n = change[0]
1988 n = change[0]
1989 except hg.RepoError:
1989 except hg.RepoError:
1990 n = repo.manifest.lookup(rev)
1990 n = repo.manifest.lookup(rev)
1991 else:
1991 else:
1992 n = repo.manifest.tip()
1992 n = repo.manifest.tip()
1993 m = repo.manifest.read(n)
1993 m = repo.manifest.read(n)
1994 mf = repo.manifest.readflags(n)
1994 mf = repo.manifest.readflags(n)
1995 files = m.keys()
1995 files = m.keys()
1996 files.sort()
1996 files.sort()
1997
1997
1998 for f in files:
1998 for f in files:
1999 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1999 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
2000
2000
2001 def outgoing(ui, repo, dest="default-push", **opts):
2001 def outgoing(ui, repo, dest="default-push", **opts):
2002 """show changesets not found in destination
2002 """show changesets not found in destination
2003
2003
2004 Show changesets not found in the specified destination repository or
2004 Show changesets not found in the specified destination repository or
2005 the default push location. These are the changesets that would be pushed
2005 the default push location. These are the changesets that would be pushed
2006 if a push was requested.
2006 if a push was requested.
2007
2007
2008 See pull for valid destination format details.
2008 See pull for valid destination format details.
2009 """
2009 """
2010 dest = ui.expandpath(dest)
2010 dest = ui.expandpath(dest)
2011 if opts['ssh']:
2011 if opts['ssh']:
2012 ui.setconfig("ui", "ssh", opts['ssh'])
2012 ui.setconfig("ui", "ssh", opts['ssh'])
2013 if opts['remotecmd']:
2013 if opts['remotecmd']:
2014 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2014 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2015
2015
2016 other = hg.repository(ui, dest)
2016 other = hg.repository(ui, dest)
2017 o = repo.findoutgoing(other, force=opts['force'])
2017 o = repo.findoutgoing(other, force=opts['force'])
2018 if not o:
2018 if not o:
2019 ui.status(_("no changes found\n"))
2019 ui.status(_("no changes found\n"))
2020 return
2020 return
2021 o = repo.changelog.nodesbetween(o)[0]
2021 o = repo.changelog.nodesbetween(o)[0]
2022 if opts['newest_first']:
2022 if opts['newest_first']:
2023 o.reverse()
2023 o.reverse()
2024 displayer = show_changeset(ui, repo, opts)
2024 displayer = show_changeset(ui, repo, opts)
2025 for n in o:
2025 for n in o:
2026 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2026 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2027 if opts['no_merges'] and len(parents) == 2:
2027 if opts['no_merges'] and len(parents) == 2:
2028 continue
2028 continue
2029 displayer.show(changenode=n)
2029 displayer.show(changenode=n)
2030 if opts['patch']:
2030 if opts['patch']:
2031 prev = (parents and parents[0]) or nullid
2031 prev = (parents and parents[0]) or nullid
2032 dodiff(ui, ui, repo, prev, n)
2032 dodiff(ui, ui, repo, prev, n)
2033 ui.write("\n")
2033 ui.write("\n")
2034
2034
2035 def parents(ui, repo, rev=None, branches=None, **opts):
2035 def parents(ui, repo, rev=None, branches=None, **opts):
2036 """show the parents of the working dir or revision
2036 """show the parents of the working dir or revision
2037
2037
2038 Print the working directory's parent revisions.
2038 Print the working directory's parent revisions.
2039 """
2039 """
2040 if rev:
2040 if rev:
2041 p = repo.changelog.parents(repo.lookup(rev))
2041 p = repo.changelog.parents(repo.lookup(rev))
2042 else:
2042 else:
2043 p = repo.dirstate.parents()
2043 p = repo.dirstate.parents()
2044
2044
2045 br = None
2045 br = None
2046 if branches is not None:
2046 if branches is not None:
2047 br = repo.branchlookup(p)
2047 br = repo.branchlookup(p)
2048 displayer = show_changeset(ui, repo, opts)
2048 displayer = show_changeset(ui, repo, opts)
2049 for n in p:
2049 for n in p:
2050 if n != nullid:
2050 if n != nullid:
2051 displayer.show(changenode=n, brinfo=br)
2051 displayer.show(changenode=n, brinfo=br)
2052
2052
2053 def paths(ui, repo, search=None):
2053 def paths(ui, repo, search=None):
2054 """show definition of symbolic path names
2054 """show definition of symbolic path names
2055
2055
2056 Show definition of symbolic path name NAME. If no name is given, show
2056 Show definition of symbolic path name NAME. If no name is given, show
2057 definition of available names.
2057 definition of available names.
2058
2058
2059 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2059 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2060 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2060 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2061 """
2061 """
2062 if search:
2062 if search:
2063 for name, path in ui.configitems("paths"):
2063 for name, path in ui.configitems("paths"):
2064 if name == search:
2064 if name == search:
2065 ui.write("%s\n" % path)
2065 ui.write("%s\n" % path)
2066 return
2066 return
2067 ui.warn(_("not found!\n"))
2067 ui.warn(_("not found!\n"))
2068 return 1
2068 return 1
2069 else:
2069 else:
2070 for name, path in ui.configitems("paths"):
2070 for name, path in ui.configitems("paths"):
2071 ui.write("%s = %s\n" % (name, path))
2071 ui.write("%s = %s\n" % (name, path))
2072
2072
2073 def pull(ui, repo, source="default", **opts):
2073 def pull(ui, repo, source="default", **opts):
2074 """pull changes from the specified source
2074 """pull changes from the specified source
2075
2075
2076 Pull changes from a remote repository to a local one.
2076 Pull changes from a remote repository to a local one.
2077
2077
2078 This finds all changes from the repository at the specified path
2078 This finds all changes from the repository at the specified path
2079 or URL and adds them to the local repository. By default, this
2079 or URL and adds them to the local repository. By default, this
2080 does not update the copy of the project in the working directory.
2080 does not update the copy of the project in the working directory.
2081
2081
2082 Valid URLs are of the form:
2082 Valid URLs are of the form:
2083
2083
2084 local/filesystem/path
2084 local/filesystem/path
2085 http://[user@]host[:port][/path]
2085 http://[user@]host[:port][/path]
2086 https://[user@]host[:port][/path]
2086 https://[user@]host[:port][/path]
2087 ssh://[user@]host[:port][/path]
2087 ssh://[user@]host[:port][/path]
2088
2088
2089 Some notes about using SSH with Mercurial:
2089 Some notes about using SSH with Mercurial:
2090 - SSH requires an accessible shell account on the destination machine
2090 - SSH requires an accessible shell account on the destination machine
2091 and a copy of hg in the remote path or specified with as remotecmd.
2091 and a copy of hg in the remote path or specified with as remotecmd.
2092 - /path is relative to the remote user's home directory by default.
2092 - /path is relative to the remote user's home directory by default.
2093 Use two slashes at the start of a path to specify an absolute path.
2093 Use two slashes at the start of a path to specify an absolute path.
2094 - Mercurial doesn't use its own compression via SSH; the right thing
2094 - Mercurial doesn't use its own compression via SSH; the right thing
2095 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2095 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2096 Host *.mylocalnetwork.example.com
2096 Host *.mylocalnetwork.example.com
2097 Compression off
2097 Compression off
2098 Host *
2098 Host *
2099 Compression on
2099 Compression on
2100 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2100 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2101 with the --ssh command line option.
2101 with the --ssh command line option.
2102 """
2102 """
2103 source = ui.expandpath(source)
2103 source = ui.expandpath(source)
2104 ui.status(_('pulling from %s\n') % (source))
2104 ui.status(_('pulling from %s\n') % (source))
2105
2105
2106 if opts['ssh']:
2106 if opts['ssh']:
2107 ui.setconfig("ui", "ssh", opts['ssh'])
2107 ui.setconfig("ui", "ssh", opts['ssh'])
2108 if opts['remotecmd']:
2108 if opts['remotecmd']:
2109 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2109 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2110
2110
2111 other = hg.repository(ui, source)
2111 other = hg.repository(ui, source)
2112 revs = None
2112 revs = None
2113 if opts['rev'] and not other.local():
2113 if opts['rev'] and not other.local():
2114 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2114 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2115 elif opts['rev']:
2115 elif opts['rev']:
2116 revs = [other.lookup(rev) for rev in opts['rev']]
2116 revs = [other.lookup(rev) for rev in opts['rev']]
2117 r = repo.pull(other, heads=revs, force=opts['force'])
2117 r = repo.pull(other, heads=revs, force=opts['force'])
2118 if not r:
2118 if not r:
2119 if opts['update']:
2119 if opts['update']:
2120 return update(ui, repo)
2120 return update(ui, repo)
2121 else:
2121 else:
2122 ui.status(_("(run 'hg update' to get a working copy)\n"))
2122 ui.status(_("(run 'hg update' to get a working copy)\n"))
2123
2123
2124 return r
2124 return r
2125
2125
2126 def push(ui, repo, dest="default-push", **opts):
2126 def push(ui, repo, dest="default-push", **opts):
2127 """push changes to the specified destination
2127 """push changes to the specified destination
2128
2128
2129 Push changes from the local repository to the given destination.
2129 Push changes from the local repository to the given destination.
2130
2130
2131 This is the symmetrical operation for pull. It helps to move
2131 This is the symmetrical operation for pull. It helps to move
2132 changes from the current repository to a different one. If the
2132 changes from the current repository to a different one. If the
2133 destination is local this is identical to a pull in that directory
2133 destination is local this is identical to a pull in that directory
2134 from the current one.
2134 from the current one.
2135
2135
2136 By default, push will refuse to run if it detects the result would
2136 By default, push will refuse to run if it detects the result would
2137 increase the number of remote heads. This generally indicates the
2137 increase the number of remote heads. This generally indicates the
2138 the client has forgotten to sync and merge before pushing.
2138 the client has forgotten to sync and merge before pushing.
2139
2139
2140 Valid URLs are of the form:
2140 Valid URLs are of the form:
2141
2141
2142 local/filesystem/path
2142 local/filesystem/path
2143 ssh://[user@]host[:port][/path]
2143 ssh://[user@]host[:port][/path]
2144
2144
2145 Look at the help text for the pull command for important details
2145 Look at the help text for the pull command for important details
2146 about ssh:// URLs.
2146 about ssh:// URLs.
2147 """
2147 """
2148 dest = ui.expandpath(dest)
2148 dest = ui.expandpath(dest)
2149 ui.status('pushing to %s\n' % (dest))
2149 ui.status('pushing to %s\n' % (dest))
2150
2150
2151 if opts['ssh']:
2151 if opts['ssh']:
2152 ui.setconfig("ui", "ssh", opts['ssh'])
2152 ui.setconfig("ui", "ssh", opts['ssh'])
2153 if opts['remotecmd']:
2153 if opts['remotecmd']:
2154 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2154 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2155
2155
2156 other = hg.repository(ui, dest)
2156 other = hg.repository(ui, dest)
2157 revs = None
2157 revs = None
2158 if opts['rev']:
2158 if opts['rev']:
2159 revs = [repo.lookup(rev) for rev in opts['rev']]
2159 revs = [repo.lookup(rev) for rev in opts['rev']]
2160 r = repo.push(other, opts['force'], revs=revs)
2160 r = repo.push(other, opts['force'], revs=revs)
2161 return r
2161 return r
2162
2162
2163 def rawcommit(ui, repo, *flist, **rc):
2163 def rawcommit(ui, repo, *flist, **rc):
2164 """raw commit interface (DEPRECATED)
2164 """raw commit interface (DEPRECATED)
2165
2165
2166 (DEPRECATED)
2166 (DEPRECATED)
2167 Lowlevel commit, for use in helper scripts.
2167 Lowlevel commit, for use in helper scripts.
2168
2168
2169 This command is not intended to be used by normal users, as it is
2169 This command is not intended to be used by normal users, as it is
2170 primarily useful for importing from other SCMs.
2170 primarily useful for importing from other SCMs.
2171
2171
2172 This command is now deprecated and will be removed in a future
2172 This command is now deprecated and will be removed in a future
2173 release, please use debugsetparents and commit instead.
2173 release, please use debugsetparents and commit instead.
2174 """
2174 """
2175
2175
2176 ui.warn(_("(the rawcommit command is deprecated)\n"))
2176 ui.warn(_("(the rawcommit command is deprecated)\n"))
2177
2177
2178 message = rc['message']
2178 message = rc['message']
2179 if not message and rc['logfile']:
2179 if not message and rc['logfile']:
2180 try:
2180 try:
2181 message = open(rc['logfile']).read()
2181 message = open(rc['logfile']).read()
2182 except IOError:
2182 except IOError:
2183 pass
2183 pass
2184 if not message and not rc['logfile']:
2184 if not message and not rc['logfile']:
2185 raise util.Abort(_("missing commit message"))
2185 raise util.Abort(_("missing commit message"))
2186
2186
2187 files = relpath(repo, list(flist))
2187 files = relpath(repo, list(flist))
2188 if rc['files']:
2188 if rc['files']:
2189 files += open(rc['files']).read().splitlines()
2189 files += open(rc['files']).read().splitlines()
2190
2190
2191 rc['parent'] = map(repo.lookup, rc['parent'])
2191 rc['parent'] = map(repo.lookup, rc['parent'])
2192
2192
2193 try:
2193 try:
2194 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2194 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2195 except ValueError, inst:
2195 except ValueError, inst:
2196 raise util.Abort(str(inst))
2196 raise util.Abort(str(inst))
2197
2197
2198 def recover(ui, repo):
2198 def recover(ui, repo):
2199 """roll back an interrupted transaction
2199 """roll back an interrupted transaction
2200
2200
2201 Recover from an interrupted commit or pull.
2201 Recover from an interrupted commit or pull.
2202
2202
2203 This command tries to fix the repository status after an interrupted
2203 This command tries to fix the repository status after an interrupted
2204 operation. It should only be necessary when Mercurial suggests it.
2204 operation. It should only be necessary when Mercurial suggests it.
2205 """
2205 """
2206 if repo.recover():
2206 if repo.recover():
2207 return repo.verify()
2207 return repo.verify()
2208 return False
2208 return False
2209
2209
2210 def remove(ui, repo, pat, *pats, **opts):
2210 def remove(ui, repo, pat, *pats, **opts):
2211 """remove the specified files on the next commit
2211 """remove the specified files on the next commit
2212
2212
2213 Schedule the indicated files for removal from the repository.
2213 Schedule the indicated files for removal from the repository.
2214
2214
2215 This command schedules the files to be removed at the next commit.
2215 This command schedules the files to be removed at the next commit.
2216 This only removes files from the current branch, not from the
2216 This only removes files from the current branch, not from the
2217 entire project history. If the files still exist in the working
2217 entire project history. If the files still exist in the working
2218 directory, they will be deleted from it.
2218 directory, they will be deleted from it.
2219 """
2219 """
2220 names = []
2220 names = []
2221 def okaytoremove(abs, rel, exact):
2221 def okaytoremove(abs, rel, exact):
2222 modified, added, removed, deleted, unknown = repo.changes(files=[abs])
2222 modified, added, removed, deleted, unknown = repo.changes(files=[abs])
2223 reason = None
2223 reason = None
2224 if modified and not opts['force']:
2224 if modified and not opts['force']:
2225 reason = _('is modified')
2225 reason = _('is modified')
2226 elif added:
2226 elif added:
2227 reason = _('has been marked for add')
2227 reason = _('has been marked for add')
2228 elif unknown:
2228 elif unknown:
2229 reason = _('is not managed')
2229 reason = _('is not managed')
2230 if reason:
2230 if reason:
2231 if exact:
2231 if exact:
2232 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2232 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2233 else:
2233 else:
2234 return True
2234 return True
2235 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
2235 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
2236 if okaytoremove(abs, rel, exact):
2236 if okaytoremove(abs, rel, exact):
2237 if ui.verbose or not exact:
2237 if ui.verbose or not exact:
2238 ui.status(_('removing %s\n') % rel)
2238 ui.status(_('removing %s\n') % rel)
2239 names.append(abs)
2239 names.append(abs)
2240 repo.remove(names, unlink=True)
2240 repo.remove(names, unlink=True)
2241
2241
2242 def rename(ui, repo, *pats, **opts):
2242 def rename(ui, repo, *pats, **opts):
2243 """rename files; equivalent of copy + remove
2243 """rename files; equivalent of copy + remove
2244
2244
2245 Mark dest as copies of sources; mark sources for deletion. If
2245 Mark dest as copies of sources; mark sources for deletion. If
2246 dest is a directory, copies are put in that directory. If dest is
2246 dest is a directory, copies are put in that directory. If dest is
2247 a file, there can only be one source.
2247 a file, there can only be one source.
2248
2248
2249 By default, this command copies the contents of files as they
2249 By default, this command copies the contents of files as they
2250 stand in the working directory. If invoked with --after, the
2250 stand in the working directory. If invoked with --after, the
2251 operation is recorded, but no copying is performed.
2251 operation is recorded, but no copying is performed.
2252
2252
2253 This command takes effect in the next commit.
2253 This command takes effect in the next commit.
2254
2254
2255 NOTE: This command should be treated as experimental. While it
2255 NOTE: This command should be treated as experimental. While it
2256 should properly record rename files, this information is not yet
2256 should properly record rename files, this information is not yet
2257 fully used by merge, nor fully reported by log.
2257 fully used by merge, nor fully reported by log.
2258 """
2258 """
2259 try:
2259 try:
2260 wlock = repo.wlock(0)
2260 wlock = repo.wlock(0)
2261 errs, copied = docopy(ui, repo, pats, opts, wlock)
2261 errs, copied = docopy(ui, repo, pats, opts, wlock)
2262 names = []
2262 names = []
2263 for abs, rel, exact in copied:
2263 for abs, rel, exact in copied:
2264 if ui.verbose or not exact:
2264 if ui.verbose or not exact:
2265 ui.status(_('removing %s\n') % rel)
2265 ui.status(_('removing %s\n') % rel)
2266 names.append(abs)
2266 names.append(abs)
2267 repo.remove(names, True, wlock)
2267 repo.remove(names, True, wlock)
2268 except lock.LockHeld, inst:
2268 except lock.LockHeld, inst:
2269 ui.warn(_("repository lock held by %s\n") % inst.args[0])
2269 ui.warn(_("repository lock held by %s\n") % inst.args[0])
2270 errs = 1
2270 errs = 1
2271 return errs
2271 return errs
2272
2272
2273 def revert(ui, repo, *pats, **opts):
2273 def revert(ui, repo, *pats, **opts):
2274 """revert modified files or dirs back to their unmodified states
2274 """revert modified files or dirs back to their unmodified states
2275
2275
2276 In its default mode, it reverts any uncommitted modifications made
2276 In its default mode, it reverts any uncommitted modifications made
2277 to the named files or directories. This restores the contents of
2277 to the named files or directories. This restores the contents of
2278 the affected files to an unmodified state.
2278 the affected files to an unmodified state.
2279
2279
2280 Using the -r option, it reverts the given files or directories to
2280 Using the -r option, it reverts the given files or directories to
2281 their state as of an earlier revision. This can be helpful to "roll
2281 their state as of an earlier revision. This can be helpful to "roll
2282 back" some or all of a change that should not have been committed.
2282 back" some or all of a change that should not have been committed.
2283
2283
2284 Revert modifies the working directory. It does not commit any
2284 Revert modifies the working directory. It does not commit any
2285 changes, or change the parent of the current working directory.
2285 changes, or change the parent of the current working directory.
2286
2286
2287 If a file has been deleted, it is recreated. If the executable
2287 If a file has been deleted, it is recreated. If the executable
2288 mode of a file was changed, it is reset.
2288 mode of a file was changed, it is reset.
2289
2289
2290 If names are given, all files matching the names are reverted.
2290 If names are given, all files matching the names are reverted.
2291
2291
2292 If no arguments are given, all files in the repository are reverted.
2292 If no arguments are given, all files in the repository are reverted.
2293 """
2293 """
2294 node = opts['rev'] and repo.lookup(opts['rev']) or \
2294 node = opts['rev'] and repo.lookup(opts['rev']) or \
2295 repo.dirstate.parents()[0]
2295 repo.dirstate.parents()[0]
2296
2296
2297 files, choose, anypats = matchpats(repo, pats, opts)
2297 files, choose, anypats = matchpats(repo, pats, opts)
2298 modified, added, removed, deleted, unknown = repo.changes(match=choose)
2298 modified, added, removed, deleted, unknown = repo.changes(match=choose)
2299 repo.forget(added)
2299 repo.forget(added)
2300 repo.undelete(removed)
2300 repo.undelete(removed)
2301
2301
2302 return repo.update(node, False, True, choose, False)
2302 return repo.update(node, False, True, choose, False)
2303
2303
2304 def root(ui, repo):
2304 def root(ui, repo):
2305 """print the root (top) of the current working dir
2305 """print the root (top) of the current working dir
2306
2306
2307 Print the root directory of the current repository.
2307 Print the root directory of the current repository.
2308 """
2308 """
2309 ui.write(repo.root + "\n")
2309 ui.write(repo.root + "\n")
2310
2310
2311 def serve(ui, repo, **opts):
2311 def serve(ui, repo, **opts):
2312 """export the repository via HTTP
2312 """export the repository via HTTP
2313
2313
2314 Start a local HTTP repository browser and pull server.
2314 Start a local HTTP repository browser and pull server.
2315
2315
2316 By default, the server logs accesses to stdout and errors to
2316 By default, the server logs accesses to stdout and errors to
2317 stderr. Use the "-A" and "-E" options to log to files.
2317 stderr. Use the "-A" and "-E" options to log to files.
2318 """
2318 """
2319
2319
2320 if opts["stdio"]:
2320 if opts["stdio"]:
2321 fin, fout = sys.stdin, sys.stdout
2321 fin, fout = sys.stdin, sys.stdout
2322 sys.stdout = sys.stderr
2322 sys.stdout = sys.stderr
2323
2323
2324 # Prevent insertion/deletion of CRs
2324 # Prevent insertion/deletion of CRs
2325 util.set_binary(fin)
2325 util.set_binary(fin)
2326 util.set_binary(fout)
2326 util.set_binary(fout)
2327
2327
2328 def getarg():
2328 def getarg():
2329 argline = fin.readline()[:-1]
2329 argline = fin.readline()[:-1]
2330 arg, l = argline.split()
2330 arg, l = argline.split()
2331 val = fin.read(int(l))
2331 val = fin.read(int(l))
2332 return arg, val
2332 return arg, val
2333 def respond(v):
2333 def respond(v):
2334 fout.write("%d\n" % len(v))
2334 fout.write("%d\n" % len(v))
2335 fout.write(v)
2335 fout.write(v)
2336 fout.flush()
2336 fout.flush()
2337
2337
2338 lock = None
2338 lock = None
2339
2339
2340 while 1:
2340 while 1:
2341 cmd = fin.readline()[:-1]
2341 cmd = fin.readline()[:-1]
2342 if cmd == '':
2342 if cmd == '':
2343 return
2343 return
2344 if cmd == "heads":
2344 if cmd == "heads":
2345 h = repo.heads()
2345 h = repo.heads()
2346 respond(" ".join(map(hex, h)) + "\n")
2346 respond(" ".join(map(hex, h)) + "\n")
2347 if cmd == "lock":
2347 if cmd == "lock":
2348 lock = repo.lock()
2348 lock = repo.lock()
2349 respond("")
2349 respond("")
2350 if cmd == "unlock":
2350 if cmd == "unlock":
2351 if lock:
2351 if lock:
2352 lock.release()
2352 lock.release()
2353 lock = None
2353 lock = None
2354 respond("")
2354 respond("")
2355 elif cmd == "branches":
2355 elif cmd == "branches":
2356 arg, nodes = getarg()
2356 arg, nodes = getarg()
2357 nodes = map(bin, nodes.split(" "))
2357 nodes = map(bin, nodes.split(" "))
2358 r = []
2358 r = []
2359 for b in repo.branches(nodes):
2359 for b in repo.branches(nodes):
2360 r.append(" ".join(map(hex, b)) + "\n")
2360 r.append(" ".join(map(hex, b)) + "\n")
2361 respond("".join(r))
2361 respond("".join(r))
2362 elif cmd == "between":
2362 elif cmd == "between":
2363 arg, pairs = getarg()
2363 arg, pairs = getarg()
2364 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
2364 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
2365 r = []
2365 r = []
2366 for b in repo.between(pairs):
2366 for b in repo.between(pairs):
2367 r.append(" ".join(map(hex, b)) + "\n")
2367 r.append(" ".join(map(hex, b)) + "\n")
2368 respond("".join(r))
2368 respond("".join(r))
2369 elif cmd == "changegroup":
2369 elif cmd == "changegroup":
2370 nodes = []
2370 nodes = []
2371 arg, roots = getarg()
2371 arg, roots = getarg()
2372 nodes = map(bin, roots.split(" "))
2372 nodes = map(bin, roots.split(" "))
2373
2373
2374 cg = repo.changegroup(nodes, 'serve')
2374 cg = repo.changegroup(nodes, 'serve')
2375 while 1:
2375 while 1:
2376 d = cg.read(4096)
2376 d = cg.read(4096)
2377 if not d:
2377 if not d:
2378 break
2378 break
2379 fout.write(d)
2379 fout.write(d)
2380
2380
2381 fout.flush()
2381 fout.flush()
2382
2382
2383 elif cmd == "addchangegroup":
2383 elif cmd == "addchangegroup":
2384 if not lock:
2384 if not lock:
2385 respond("not locked")
2385 respond("not locked")
2386 continue
2386 continue
2387 respond("")
2387 respond("")
2388
2388
2389 r = repo.addchangegroup(fin)
2389 r = repo.addchangegroup(fin)
2390 respond("")
2390 respond("")
2391
2391
2392 optlist = "name templates style address port ipv6 accesslog errorlog"
2392 optlist = "name templates style address port ipv6 accesslog errorlog"
2393 for o in optlist.split():
2393 for o in optlist.split():
2394 if opts[o]:
2394 if opts[o]:
2395 ui.setconfig("web", o, opts[o])
2395 ui.setconfig("web", o, opts[o])
2396
2396
2397 if opts['daemon'] and not opts['daemon_pipefds']:
2397 if opts['daemon'] and not opts['daemon_pipefds']:
2398 rfd, wfd = os.pipe()
2398 rfd, wfd = os.pipe()
2399 args = sys.argv[:]
2399 args = sys.argv[:]
2400 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2400 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2401 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2401 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2402 args[0], args)
2402 args[0], args)
2403 os.close(wfd)
2403 os.close(wfd)
2404 os.read(rfd, 1)
2404 os.read(rfd, 1)
2405 os._exit(0)
2405 os._exit(0)
2406
2406
2407 try:
2407 try:
2408 httpd = hgweb.create_server(repo)
2408 httpd = hgweb.create_server(repo)
2409 except socket.error, inst:
2409 except socket.error, inst:
2410 raise util.Abort(_('cannot start server: ') + inst.args[1])
2410 raise util.Abort(_('cannot start server: ') + inst.args[1])
2411
2411
2412 if ui.verbose:
2412 if ui.verbose:
2413 addr, port = httpd.socket.getsockname()
2413 addr, port = httpd.socket.getsockname()
2414 if addr == '0.0.0.0':
2414 if addr == '0.0.0.0':
2415 addr = socket.gethostname()
2415 addr = socket.gethostname()
2416 else:
2416 else:
2417 try:
2417 try:
2418 addr = socket.gethostbyaddr(addr)[0]
2418 addr = socket.gethostbyaddr(addr)[0]
2419 except socket.error:
2419 except socket.error:
2420 pass
2420 pass
2421 if port != 80:
2421 if port != 80:
2422 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2422 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2423 else:
2423 else:
2424 ui.status(_('listening at http://%s/\n') % addr)
2424 ui.status(_('listening at http://%s/\n') % addr)
2425
2425
2426 if opts['pid_file']:
2426 if opts['pid_file']:
2427 fp = open(opts['pid_file'], 'w')
2427 fp = open(opts['pid_file'], 'w')
2428 fp.write(str(os.getpid()))
2428 fp.write(str(os.getpid()))
2429 fp.close()
2429 fp.close()
2430
2430
2431 if opts['daemon_pipefds']:
2431 if opts['daemon_pipefds']:
2432 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2432 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2433 os.close(rfd)
2433 os.close(rfd)
2434 os.write(wfd, 'y')
2434 os.write(wfd, 'y')
2435 os.close(wfd)
2435 os.close(wfd)
2436 sys.stdout.flush()
2436 sys.stdout.flush()
2437 sys.stderr.flush()
2437 sys.stderr.flush()
2438 fd = os.open(util.nulldev, os.O_RDWR)
2438 fd = os.open(util.nulldev, os.O_RDWR)
2439 if fd != 0: os.dup2(fd, 0)
2439 if fd != 0: os.dup2(fd, 0)
2440 if fd != 1: os.dup2(fd, 1)
2440 if fd != 1: os.dup2(fd, 1)
2441 if fd != 2: os.dup2(fd, 2)
2441 if fd != 2: os.dup2(fd, 2)
2442 if fd not in (0, 1, 2): os.close(fd)
2442 if fd not in (0, 1, 2): os.close(fd)
2443
2443
2444 httpd.serve_forever()
2444 httpd.serve_forever()
2445
2445
2446 def status(ui, repo, *pats, **opts):
2446 def status(ui, repo, *pats, **opts):
2447 """show changed files in the working directory
2447 """show changed files in the working directory
2448
2448
2449 Show changed files in the repository. If names are
2449 Show changed files in the repository. If names are
2450 given, only files that match are shown.
2450 given, only files that match are shown.
2451
2451
2452 The codes used to show the status of files are:
2452 The codes used to show the status of files are:
2453 M = modified
2453 M = modified
2454 A = added
2454 A = added
2455 R = removed
2455 R = removed
2456 ! = deleted, but still tracked
2456 ! = deleted, but still tracked
2457 ? = not tracked
2457 ? = not tracked
2458 """
2458 """
2459
2459
2460 files, matchfn, anypats = matchpats(repo, pats, opts)
2460 files, matchfn, anypats = matchpats(repo, pats, opts)
2461 cwd = (pats and repo.getcwd()) or ''
2461 cwd = (pats and repo.getcwd()) or ''
2462 modified, added, removed, deleted, unknown = [
2462 modified, added, removed, deleted, unknown = [
2463 [util.pathto(cwd, x) for x in n]
2463 [util.pathto(cwd, x) for x in n]
2464 for n in repo.changes(files=files, match=matchfn)]
2464 for n in repo.changes(files=files, match=matchfn)]
2465
2465
2466 changetypes = [('modified', 'M', modified),
2466 changetypes = [('modified', 'M', modified),
2467 ('added', 'A', added),
2467 ('added', 'A', added),
2468 ('removed', 'R', removed),
2468 ('removed', 'R', removed),
2469 ('deleted', '!', deleted),
2469 ('deleted', '!', deleted),
2470 ('unknown', '?', unknown)]
2470 ('unknown', '?', unknown)]
2471
2471
2472 end = opts['print0'] and '\0' or '\n'
2472 end = opts['print0'] and '\0' or '\n'
2473
2473
2474 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2474 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2475 or changetypes):
2475 or changetypes):
2476 if opts['no_status']:
2476 if opts['no_status']:
2477 format = "%%s%s" % end
2477 format = "%%s%s" % end
2478 else:
2478 else:
2479 format = "%s %%s%s" % (char, end)
2479 format = "%s %%s%s" % (char, end)
2480
2480
2481 for f in changes:
2481 for f in changes:
2482 ui.write(format % f)
2482 ui.write(format % f)
2483
2483
2484 def tag(ui, repo, name, rev_=None, **opts):
2484 def tag(ui, repo, name, rev_=None, **opts):
2485 """add a tag for the current tip or a given revision
2485 """add a tag for the current tip or a given revision
2486
2486
2487 Name a particular revision using <name>.
2487 Name a particular revision using <name>.
2488
2488
2489 Tags are used to name particular revisions of the repository and are
2489 Tags are used to name particular revisions of the repository and are
2490 very useful to compare different revision, to go back to significant
2490 very useful to compare different revision, to go back to significant
2491 earlier versions or to mark branch points as releases, etc.
2491 earlier versions or to mark branch points as releases, etc.
2492
2492
2493 If no revision is given, the tip is used.
2493 If no revision is given, the tip is used.
2494
2494
2495 To facilitate version control, distribution, and merging of tags,
2495 To facilitate version control, distribution, and merging of tags,
2496 they are stored as a file named ".hgtags" which is managed
2496 they are stored as a file named ".hgtags" which is managed
2497 similarly to other project files and can be hand-edited if
2497 similarly to other project files and can be hand-edited if
2498 necessary. The file '.hg/localtags' is used for local tags (not
2498 necessary. The file '.hg/localtags' is used for local tags (not
2499 shared among repositories).
2499 shared among repositories).
2500 """
2500 """
2501 if name == "tip":
2501 if name == "tip":
2502 raise util.Abort(_("the name 'tip' is reserved"))
2502 raise util.Abort(_("the name 'tip' is reserved"))
2503 if rev_ is not None:
2503 if rev_ is not None:
2504 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2504 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2505 "please use 'hg tag [-r REV] NAME' instead\n"))
2505 "please use 'hg tag [-r REV] NAME' instead\n"))
2506 if opts['rev']:
2506 if opts['rev']:
2507 raise util.Abort(_("use only one form to specify the revision"))
2507 raise util.Abort(_("use only one form to specify the revision"))
2508 if opts['rev']:
2508 if opts['rev']:
2509 rev_ = opts['rev']
2509 rev_ = opts['rev']
2510 if rev_:
2510 if rev_:
2511 r = hex(repo.lookup(rev_))
2511 r = hex(repo.lookup(rev_))
2512 else:
2512 else:
2513 r = hex(repo.changelog.tip())
2513 r = hex(repo.changelog.tip())
2514
2514
2515 disallowed = (revrangesep, '\r', '\n')
2515 disallowed = (revrangesep, '\r', '\n')
2516 for c in disallowed:
2516 for c in disallowed:
2517 if name.find(c) >= 0:
2517 if name.find(c) >= 0:
2518 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2518 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2519
2519
2520 repo.hook('pretag', throw=True, node=r, tag=name,
2520 repo.hook('pretag', throw=True, node=r, tag=name,
2521 local=int(not not opts['local']))
2521 local=int(not not opts['local']))
2522
2522
2523 if opts['local']:
2523 if opts['local']:
2524 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2524 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2525 repo.hook('tag', node=r, tag=name, local=1)
2525 repo.hook('tag', node=r, tag=name, local=1)
2526 return
2526 return
2527
2527
2528 for x in repo.changes():
2528 for x in repo.changes():
2529 if ".hgtags" in x:
2529 if ".hgtags" in x:
2530 raise util.Abort(_("working copy of .hgtags is changed "
2530 raise util.Abort(_("working copy of .hgtags is changed "
2531 "(please commit .hgtags manually)"))
2531 "(please commit .hgtags manually)"))
2532
2532
2533 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2533 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2534 if repo.dirstate.state(".hgtags") == '?':
2534 if repo.dirstate.state(".hgtags") == '?':
2535 repo.add([".hgtags"])
2535 repo.add([".hgtags"])
2536
2536
2537 message = (opts['message'] or
2537 message = (opts['message'] or
2538 _("Added tag %s for changeset %s") % (name, r))
2538 _("Added tag %s for changeset %s") % (name, r))
2539 try:
2539 try:
2540 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2540 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2541 repo.hook('tag', node=r, tag=name, local=0)
2541 repo.hook('tag', node=r, tag=name, local=0)
2542 except ValueError, inst:
2542 except ValueError, inst:
2543 raise util.Abort(str(inst))
2543 raise util.Abort(str(inst))
2544
2544
2545 def tags(ui, repo):
2545 def tags(ui, repo):
2546 """list repository tags
2546 """list repository tags
2547
2547
2548 List the repository tags.
2548 List the repository tags.
2549
2549
2550 This lists both regular and local tags.
2550 This lists both regular and local tags.
2551 """
2551 """
2552
2552
2553 l = repo.tagslist()
2553 l = repo.tagslist()
2554 l.reverse()
2554 l.reverse()
2555 for t, n in l:
2555 for t, n in l:
2556 try:
2556 try:
2557 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2557 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2558 except KeyError:
2558 except KeyError:
2559 r = " ?:?"
2559 r = " ?:?"
2560 ui.write("%-30s %s\n" % (t, r))
2560 ui.write("%-30s %s\n" % (t, r))
2561
2561
2562 def tip(ui, repo, **opts):
2562 def tip(ui, repo, **opts):
2563 """show the tip revision
2563 """show the tip revision
2564
2564
2565 Show the tip revision.
2565 Show the tip revision.
2566 """
2566 """
2567 n = repo.changelog.tip()
2567 n = repo.changelog.tip()
2568 br = None
2568 br = None
2569 if opts['branches']:
2569 if opts['branches']:
2570 br = repo.branchlookup([n])
2570 br = repo.branchlookup([n])
2571 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2571 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2572 if opts['patch']:
2572 if opts['patch']:
2573 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2573 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2574
2574
2575 def unbundle(ui, repo, fname, **opts):
2575 def unbundle(ui, repo, fname, **opts):
2576 """apply a changegroup file
2576 """apply a changegroup file
2577
2577
2578 Apply a compressed changegroup file generated by the bundle
2578 Apply a compressed changegroup file generated by the bundle
2579 command.
2579 command.
2580 """
2580 """
2581 f = urllib.urlopen(fname)
2581 f = urllib.urlopen(fname)
2582
2582
2583 header = f.read(6)
2583 header = f.read(6)
2584 if not header.startswith("HG"):
2584 if not header.startswith("HG"):
2585 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2585 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2586 elif not header.startswith("HG10"):
2586 elif not header.startswith("HG10"):
2587 raise util.Abort(_("%s: unknown bundle version") % fname)
2587 raise util.Abort(_("%s: unknown bundle version") % fname)
2588 elif header == "HG10BZ":
2588 elif header == "HG10BZ":
2589 def generator(f):
2589 def generator(f):
2590 zd = bz2.BZ2Decompressor()
2590 zd = bz2.BZ2Decompressor()
2591 zd.decompress("BZ")
2591 zd.decompress("BZ")
2592 for chunk in f:
2592 for chunk in f:
2593 yield zd.decompress(chunk)
2593 yield zd.decompress(chunk)
2594 elif header == "HG10UN":
2594 elif header == "HG10UN":
2595 def generator(f):
2595 def generator(f):
2596 for chunk in f:
2596 for chunk in f:
2597 yield chunk
2597 yield chunk
2598 else:
2598 else:
2599 raise util.Abort(_("%s: unknown bundle compression type")
2599 raise util.Abort(_("%s: unknown bundle compression type")
2600 % fname)
2600 % fname)
2601 gen = generator(util.filechunkiter(f, 4096))
2601 gen = generator(util.filechunkiter(f, 4096))
2602 if repo.addchangegroup(util.chunkbuffer(gen)):
2602 if repo.addchangegroup(util.chunkbuffer(gen)):
2603 return 1
2603 return 1
2604
2604
2605 if opts['update']:
2605 if opts['update']:
2606 return update(ui, repo)
2606 return update(ui, repo)
2607 else:
2607 else:
2608 ui.status(_("(run 'hg update' to get a working copy)\n"))
2608 ui.status(_("(run 'hg update' to get a working copy)\n"))
2609
2609
2610 def undo(ui, repo):
2610 def undo(ui, repo):
2611 """undo the last commit or pull
2611 """undo the last commit or pull
2612
2612
2613 Roll back the last pull or commit transaction on the
2613 Roll back the last pull or commit transaction on the
2614 repository, restoring the project to its earlier state.
2614 repository, restoring the project to its earlier state.
2615
2615
2616 This command should be used with care. There is only one level of
2616 This command should be used with care. There is only one level of
2617 undo and there is no redo.
2617 undo and there is no redo.
2618
2618
2619 This command is not intended for use on public repositories. Once
2619 This command is not intended for use on public repositories. Once
2620 a change is visible for pull by other users, undoing it locally is
2620 a change is visible for pull by other users, undoing it locally is
2621 ineffective.
2621 ineffective.
2622 """
2622 """
2623 repo.undo()
2623 repo.undo()
2624
2624
2625 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2625 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2626 branch=None, **opts):
2626 branch=None, **opts):
2627 """update or merge working directory
2627 """update or merge working directory
2628
2628
2629 Update the working directory to the specified revision.
2629 Update the working directory to the specified revision.
2630
2630
2631 If there are no outstanding changes in the working directory and
2631 If there are no outstanding changes in the working directory and
2632 there is a linear relationship between the current version and the
2632 there is a linear relationship between the current version and the
2633 requested version, the result is the requested version.
2633 requested version, the result is the requested version.
2634
2634
2635 Otherwise the result is a merge between the contents of the
2635 Otherwise the result is a merge between the contents of the
2636 current working directory and the requested version. Files that
2636 current working directory and the requested version. Files that
2637 changed between either parent are marked as changed for the next
2637 changed between either parent are marked as changed for the next
2638 commit and a commit must be performed before any further updates
2638 commit and a commit must be performed before any further updates
2639 are allowed.
2639 are allowed.
2640
2640
2641 By default, update will refuse to run if doing so would require
2641 By default, update will refuse to run if doing so would require
2642 merging or discarding local changes.
2642 merging or discarding local changes.
2643 """
2643 """
2644 if branch:
2644 if branch:
2645 br = repo.branchlookup(branch=branch)
2645 br = repo.branchlookup(branch=branch)
2646 found = []
2646 found = []
2647 for x in br:
2647 for x in br:
2648 if branch in br[x]:
2648 if branch in br[x]:
2649 found.append(x)
2649 found.append(x)
2650 if len(found) > 1:
2650 if len(found) > 1:
2651 ui.warn(_("Found multiple heads for %s\n") % branch)
2651 ui.warn(_("Found multiple heads for %s\n") % branch)
2652 for x in found:
2652 for x in found:
2653 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br)
2653 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br)
2654 return 1
2654 return 1
2655 if len(found) == 1:
2655 if len(found) == 1:
2656 node = found[0]
2656 node = found[0]
2657 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2657 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2658 else:
2658 else:
2659 ui.warn(_("branch %s not found\n") % (branch))
2659 ui.warn(_("branch %s not found\n") % (branch))
2660 return 1
2660 return 1
2661 else:
2661 else:
2662 node = node and repo.lookup(node) or repo.changelog.tip()
2662 node = node and repo.lookup(node) or repo.changelog.tip()
2663 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2663 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2664
2664
2665 def verify(ui, repo):
2665 def verify(ui, repo):
2666 """verify the integrity of the repository
2666 """verify the integrity of the repository
2667
2667
2668 Verify the integrity of the current repository.
2668 Verify the integrity of the current repository.
2669
2669
2670 This will perform an extensive check of the repository's
2670 This will perform an extensive check of the repository's
2671 integrity, validating the hashes and checksums of each entry in
2671 integrity, validating the hashes and checksums of each entry in
2672 the changelog, manifest, and tracked files, as well as the
2672 the changelog, manifest, and tracked files, as well as the
2673 integrity of their crosslinks and indices.
2673 integrity of their crosslinks and indices.
2674 """
2674 """
2675 return repo.verify()
2675 return repo.verify()
2676
2676
2677 # Command options and aliases are listed here, alphabetically
2677 # Command options and aliases are listed here, alphabetically
2678
2678
2679 table = {
2679 table = {
2680 "^add":
2680 "^add":
2681 (add,
2681 (add,
2682 [('I', 'include', [], _('include names matching the given patterns')),
2682 [('I', 'include', [], _('include names matching the given patterns')),
2683 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2683 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2684 _('hg add [OPTION]... [FILE]...')),
2684 _('hg add [OPTION]... [FILE]...')),
2685 "addremove":
2685 "addremove":
2686 (addremove,
2686 (addremove,
2687 [('I', 'include', [], _('include names matching the given patterns')),
2687 [('I', 'include', [], _('include names matching the given patterns')),
2688 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2688 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2689 _('hg addremove [OPTION]... [FILE]...')),
2689 _('hg addremove [OPTION]... [FILE]...')),
2690 "^annotate":
2690 "^annotate":
2691 (annotate,
2691 (annotate,
2692 [('r', 'rev', '', _('annotate the specified revision')),
2692 [('r', 'rev', '', _('annotate the specified revision')),
2693 ('a', 'text', None, _('treat all files as text')),
2693 ('a', 'text', None, _('treat all files as text')),
2694 ('u', 'user', None, _('list the author')),
2694 ('u', 'user', None, _('list the author')),
2695 ('d', 'date', None, _('list the date')),
2695 ('d', 'date', None, _('list the date')),
2696 ('n', 'number', None, _('list the revision number (default)')),
2696 ('n', 'number', None, _('list the revision number (default)')),
2697 ('c', 'changeset', None, _('list the changeset')),
2697 ('c', 'changeset', None, _('list the changeset')),
2698 ('I', 'include', [], _('include names matching the given patterns')),
2698 ('I', 'include', [], _('include names matching the given patterns')),
2699 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2699 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2700 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2700 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2701 "bundle":
2701 "bundle":
2702 (bundle,
2702 (bundle,
2703 [('f', 'force', None,
2703 [('f', 'force', None,
2704 _('run even when remote repository is unrelated'))],
2704 _('run even when remote repository is unrelated'))],
2705 _('hg bundle FILE DEST')),
2705 _('hg bundle FILE DEST')),
2706 "cat":
2706 "cat":
2707 (cat,
2707 (cat,
2708 [('o', 'output', '', _('print output to file with formatted name')),
2708 [('o', 'output', '', _('print output to file with formatted name')),
2709 ('r', 'rev', '', _('print the given revision')),
2709 ('r', 'rev', '', _('print the given revision')),
2710 ('I', 'include', [], _('include names matching the given patterns')),
2710 ('I', 'include', [], _('include names matching the given patterns')),
2711 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2711 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2712 _('hg cat [OPTION]... FILE...')),
2712 _('hg cat [OPTION]... FILE...')),
2713 "^clone":
2713 "^clone":
2714 (clone,
2714 (clone,
2715 [('U', 'noupdate', None, _('do not update the new working directory')),
2715 [('U', 'noupdate', None, _('do not update the new working directory')),
2716 ('r', 'rev', [],
2716 ('r', 'rev', [],
2717 _('a changeset you would like to have after cloning')),
2717 _('a changeset you would like to have after cloning')),
2718 ('', 'pull', None, _('use pull protocol to copy metadata')),
2718 ('', 'pull', None, _('use pull protocol to copy metadata')),
2719 ('e', 'ssh', '', _('specify ssh command to use')),
2719 ('e', 'ssh', '', _('specify ssh command to use')),
2720 ('', 'remotecmd', '',
2720 ('', 'remotecmd', '',
2721 _('specify hg command to run on the remote side'))],
2721 _('specify hg command to run on the remote side'))],
2722 _('hg clone [OPTION]... SOURCE [DEST]')),
2722 _('hg clone [OPTION]... SOURCE [DEST]')),
2723 "^commit|ci":
2723 "^commit|ci":
2724 (commit,
2724 (commit,
2725 [('A', 'addremove', None, _('run addremove during commit')),
2725 [('A', 'addremove', None, _('run addremove during commit')),
2726 ('m', 'message', '', _('use <text> as commit message')),
2726 ('m', 'message', '', _('use <text> as commit message')),
2727 ('l', 'logfile', '', _('read the commit message from <file>')),
2727 ('l', 'logfile', '', _('read the commit message from <file>')),
2728 ('d', 'date', '', _('record datecode as commit date')),
2728 ('d', 'date', '', _('record datecode as commit date')),
2729 ('u', 'user', '', _('record user as commiter')),
2729 ('u', 'user', '', _('record user as commiter')),
2730 ('I', 'include', [], _('include names matching the given patterns')),
2730 ('I', 'include', [], _('include names matching the given patterns')),
2731 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2731 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2732 _('hg commit [OPTION]... [FILE]...')),
2732 _('hg commit [OPTION]... [FILE]...')),
2733 "copy|cp":
2733 "copy|cp":
2734 (copy,
2734 (copy,
2735 [('A', 'after', None, _('record a copy that has already occurred')),
2735 [('A', 'after', None, _('record a copy that has already occurred')),
2736 ('f', 'force', None,
2736 ('f', 'force', None,
2737 _('forcibly copy over an existing managed file')),
2737 _('forcibly copy over an existing managed file')),
2738 ('I', 'include', [], _('include names matching the given patterns')),
2738 ('I', 'include', [], _('include names matching the given patterns')),
2739 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2739 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2740 _('hg copy [OPTION]... [SOURCE]... DEST')),
2740 _('hg copy [OPTION]... [SOURCE]... DEST')),
2741 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2741 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2742 "debugcomplete": (debugcomplete, [], _('debugcomplete CMD')),
2742 "debugcomplete": (debugcomplete, [], _('debugcomplete CMD')),
2743 "debugrebuildstate":
2743 "debugrebuildstate":
2744 (debugrebuildstate,
2744 (debugrebuildstate,
2745 [('r', 'rev', '', _('revision to rebuild to'))],
2745 [('r', 'rev', '', _('revision to rebuild to'))],
2746 _('debugrebuildstate [-r REV] [REV]')),
2746 _('debugrebuildstate [-r REV] [REV]')),
2747 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2747 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2748 "debugconfig": (debugconfig, [], _('debugconfig')),
2748 "debugconfig": (debugconfig, [], _('debugconfig')),
2749 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2749 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2750 "debugstate": (debugstate, [], _('debugstate')),
2750 "debugstate": (debugstate, [], _('debugstate')),
2751 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2751 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2752 "debugindex": (debugindex, [], _('debugindex FILE')),
2752 "debugindex": (debugindex, [], _('debugindex FILE')),
2753 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2753 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2754 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2754 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2755 "debugwalk":
2755 "debugwalk":
2756 (debugwalk,
2756 (debugwalk,
2757 [('I', 'include', [], _('include names matching the given patterns')),
2757 [('I', 'include', [], _('include names matching the given patterns')),
2758 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2758 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2759 _('debugwalk [OPTION]... [FILE]...')),
2759 _('debugwalk [OPTION]... [FILE]...')),
2760 "^diff":
2760 "^diff":
2761 (diff,
2761 (diff,
2762 [('r', 'rev', [], _('revision')),
2762 [('r', 'rev', [], _('revision')),
2763 ('a', 'text', None, _('treat all files as text')),
2763 ('a', 'text', None, _('treat all files as text')),
2764 ('p', 'show-function', None,
2764 ('p', 'show-function', None,
2765 _('show which function each change is in')),
2765 _('show which function each change is in')),
2766 ('w', 'ignore-all-space', None,
2766 ('w', 'ignore-all-space', None,
2767 _('ignore white space when comparing lines')),
2767 _('ignore white space when comparing lines')),
2768 ('I', 'include', [], _('include names matching the given patterns')),
2768 ('I', 'include', [], _('include names matching the given patterns')),
2769 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2769 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2770 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2770 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2771 "^export":
2771 "^export":
2772 (export,
2772 (export,
2773 [('o', 'output', '', _('print output to file with formatted name')),
2773 [('o', 'output', '', _('print output to file with formatted name')),
2774 ('a', 'text', None, _('treat all files as text')),
2774 ('a', 'text', None, _('treat all files as text')),
2775 ('', 'switch-parent', None, _('diff against the second parent'))],
2775 ('', 'switch-parent', None, _('diff against the second parent'))],
2776 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2776 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2777 "forget":
2777 "forget":
2778 (forget,
2778 (forget,
2779 [('I', 'include', [], _('include names matching the given patterns')),
2779 [('I', 'include', [], _('include names matching the given patterns')),
2780 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2780 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2781 _('hg forget [OPTION]... FILE...')),
2781 _('hg forget [OPTION]... FILE...')),
2782 "grep":
2782 "grep":
2783 (grep,
2783 (grep,
2784 [('0', 'print0', None, _('end fields with NUL')),
2784 [('0', 'print0', None, _('end fields with NUL')),
2785 ('', 'all', None, _('print all revisions that match')),
2785 ('', 'all', None, _('print all revisions that match')),
2786 ('i', 'ignore-case', None, _('ignore case when matching')),
2786 ('i', 'ignore-case', None, _('ignore case when matching')),
2787 ('l', 'files-with-matches', None,
2787 ('l', 'files-with-matches', None,
2788 _('print only filenames and revs that match')),
2788 _('print only filenames and revs that match')),
2789 ('n', 'line-number', None, _('print matching line numbers')),
2789 ('n', 'line-number', None, _('print matching line numbers')),
2790 ('r', 'rev', [], _('search in given revision range')),
2790 ('r', 'rev', [], _('search in given revision range')),
2791 ('u', 'user', None, _('print user who committed change')),
2791 ('u', 'user', None, _('print user who committed change')),
2792 ('I', 'include', [], _('include names matching the given patterns')),
2792 ('I', 'include', [], _('include names matching the given patterns')),
2793 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2793 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2794 _('hg grep [OPTION]... PATTERN [FILE]...')),
2794 _('hg grep [OPTION]... PATTERN [FILE]...')),
2795 "heads":
2795 "heads":
2796 (heads,
2796 (heads,
2797 [('b', 'branches', None, _('show branches')),
2797 [('b', 'branches', None, _('show branches')),
2798 ('', 'style', '', _('display using template map file')),
2798 ('', 'style', '', _('display using template map file')),
2799 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2799 ('r', 'rev', '', _('show only heads which are descendants of rev')),
2800 ('', 'template', '', _('display with template'))],
2800 ('', 'template', '', _('display with template'))],
2801 _('hg heads [-b] [-r <rev>]')),
2801 _('hg heads [-b] [-r <rev>]')),
2802 "help": (help_, [], _('hg help [COMMAND]')),
2802 "help": (help_, [], _('hg help [COMMAND]')),
2803 "identify|id": (identify, [], _('hg identify')),
2803 "identify|id": (identify, [], _('hg identify')),
2804 "import|patch":
2804 "import|patch":
2805 (import_,
2805 (import_,
2806 [('p', 'strip', 1,
2806 [('p', 'strip', 1,
2807 _('directory strip option for patch. This has the same\n') +
2807 _('directory strip option for patch. This has the same\n') +
2808 _('meaning as the corresponding patch option')),
2808 _('meaning as the corresponding patch option')),
2809 ('b', 'base', '', _('base path')),
2809 ('b', 'base', '', _('base path')),
2810 ('f', 'force', None,
2810 ('f', 'force', None,
2811 _('skip check for outstanding uncommitted changes'))],
2811 _('skip check for outstanding uncommitted changes'))],
2812 _('hg import [-p NUM] [-b BASE] [-f] PATCH...')),
2812 _('hg import [-p NUM] [-b BASE] [-f] PATCH...')),
2813 "incoming|in": (incoming,
2813 "incoming|in": (incoming,
2814 [('M', 'no-merges', None, _('do not show merges')),
2814 [('M', 'no-merges', None, _('do not show merges')),
2815 ('f', 'force', None,
2815 ('f', 'force', None,
2816 _('run even when remote repository is unrelated')),
2816 _('run even when remote repository is unrelated')),
2817 ('', 'style', '', _('display using template map file')),
2817 ('', 'style', '', _('display using template map file')),
2818 ('n', 'newest-first', None, _('show newest record first')),
2818 ('n', 'newest-first', None, _('show newest record first')),
2819 ('', 'bundle', '', _('file to store the bundles into')),
2819 ('', 'bundle', '', _('file to store the bundles into')),
2820 ('p', 'patch', None, _('show patch')),
2820 ('p', 'patch', None, _('show patch')),
2821 ('', 'template', '', _('display with template')),
2821 ('', 'template', '', _('display with template')),
2822 ('e', 'ssh', '', _('specify ssh command to use')),
2822 ('e', 'ssh', '', _('specify ssh command to use')),
2823 ('', 'remotecmd', '',
2823 ('', 'remotecmd', '',
2824 _('specify hg command to run on the remote side'))],
2824 _('specify hg command to run on the remote side'))],
2825 _('hg incoming [-p] [-n] [-M] [--bundle FILENAME] [SOURCE]')),
2825 _('hg incoming [-p] [-n] [-M] [--bundle FILENAME] [SOURCE]')),
2826 "^init": (init, [], _('hg init [DEST]')),
2826 "^init": (init, [], _('hg init [DEST]')),
2827 "locate":
2827 "locate":
2828 (locate,
2828 (locate,
2829 [('r', 'rev', '', _('search the repository as it stood at rev')),
2829 [('r', 'rev', '', _('search the repository as it stood at rev')),
2830 ('0', 'print0', None,
2830 ('0', 'print0', None,
2831 _('end filenames with NUL, for use with xargs')),
2831 _('end filenames with NUL, for use with xargs')),
2832 ('f', 'fullpath', None,
2832 ('f', 'fullpath', None,
2833 _('print complete paths from the filesystem root')),
2833 _('print complete paths from the filesystem root')),
2834 ('I', 'include', [], _('include names matching the given patterns')),
2834 ('I', 'include', [], _('include names matching the given patterns')),
2835 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2835 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2836 _('hg locate [OPTION]... [PATTERN]...')),
2836 _('hg locate [OPTION]... [PATTERN]...')),
2837 "^log|history":
2837 "^log|history":
2838 (log,
2838 (log,
2839 [('b', 'branches', None, _('show branches')),
2839 [('b', 'branches', None, _('show branches')),
2840 ('k', 'keyword', [], _('search for a keyword')),
2840 ('k', 'keyword', [], _('search for a keyword')),
2841 ('l', 'limit', '', _('limit number of changes displayed')),
2841 ('l', 'limit', '', _('limit number of changes displayed')),
2842 ('r', 'rev', [], _('show the specified revision or range')),
2842 ('r', 'rev', [], _('show the specified revision or range')),
2843 ('M', 'no-merges', None, _('do not show merges')),
2843 ('M', 'no-merges', None, _('do not show merges')),
2844 ('', 'style', '', _('display using template map file')),
2844 ('', 'style', '', _('display using template map file')),
2845 ('m', 'only-merges', None, _('show only merges')),
2845 ('m', 'only-merges', None, _('show only merges')),
2846 ('p', 'patch', None, _('show patch')),
2846 ('p', 'patch', None, _('show patch')),
2847 ('', 'template', '', _('display with template')),
2847 ('', 'template', '', _('display with template')),
2848 ('I', 'include', [], _('include names matching the given patterns')),
2848 ('I', 'include', [], _('include names matching the given patterns')),
2849 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2849 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2850 _('hg log [OPTION]... [FILE]')),
2850 _('hg log [OPTION]... [FILE]')),
2851 "manifest": (manifest, [], _('hg manifest [REV]')),
2851 "manifest": (manifest, [], _('hg manifest [REV]')),
2852 "outgoing|out": (outgoing,
2852 "outgoing|out": (outgoing,
2853 [('M', 'no-merges', None, _('do not show merges')),
2853 [('M', 'no-merges', None, _('do not show merges')),
2854 ('f', 'force', None,
2854 ('f', 'force', None,
2855 _('run even when remote repository is unrelated')),
2855 _('run even when remote repository is unrelated')),
2856 ('p', 'patch', None, _('show patch')),
2856 ('p', 'patch', None, _('show patch')),
2857 ('', 'style', '', _('display using template map file')),
2857 ('', 'style', '', _('display using template map file')),
2858 ('n', 'newest-first', None, _('show newest record first')),
2858 ('n', 'newest-first', None, _('show newest record first')),
2859 ('', 'template', '', _('display with template')),
2859 ('', 'template', '', _('display with template')),
2860 ('e', 'ssh', '', _('specify ssh command to use')),
2860 ('e', 'ssh', '', _('specify ssh command to use')),
2861 ('', 'remotecmd', '',
2861 ('', 'remotecmd', '',
2862 _('specify hg command to run on the remote side'))],
2862 _('specify hg command to run on the remote side'))],
2863 _('hg outgoing [-M] [-p] [-n] [DEST]')),
2863 _('hg outgoing [-M] [-p] [-n] [DEST]')),
2864 "^parents":
2864 "^parents":
2865 (parents,
2865 (parents,
2866 [('b', 'branches', None, _('show branches')),
2866 [('b', 'branches', None, _('show branches')),
2867 ('', 'style', '', _('display using template map file')),
2867 ('', 'style', '', _('display using template map file')),
2868 ('', 'template', '', _('display with template'))],
2868 ('', 'template', '', _('display with template'))],
2869 _('hg parents [-b] [REV]')),
2869 _('hg parents [-b] [REV]')),
2870 "paths": (paths, [], _('hg paths [NAME]')),
2870 "paths": (paths, [], _('hg paths [NAME]')),
2871 "^pull":
2871 "^pull":
2872 (pull,
2872 (pull,
2873 [('u', 'update', None,
2873 [('u', 'update', None,
2874 _('update the working directory to tip after pull')),
2874 _('update the working directory to tip after pull')),
2875 ('e', 'ssh', '', _('specify ssh command to use')),
2875 ('e', 'ssh', '', _('specify ssh command to use')),
2876 ('f', 'force', None,
2876 ('f', 'force', None,
2877 _('run even when remote repository is unrelated')),
2877 _('run even when remote repository is unrelated')),
2878 ('r', 'rev', [], _('a specific revision you would like to pull')),
2878 ('r', 'rev', [], _('a specific revision you would like to pull')),
2879 ('', 'remotecmd', '',
2879 ('', 'remotecmd', '',
2880 _('specify hg command to run on the remote side'))],
2880 _('specify hg command to run on the remote side'))],
2881 _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')),
2881 _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')),
2882 "^push":
2882 "^push":
2883 (push,
2883 (push,
2884 [('f', 'force', None, _('force push')),
2884 [('f', 'force', None, _('force push')),
2885 ('e', 'ssh', '', _('specify ssh command to use')),
2885 ('e', 'ssh', '', _('specify ssh command to use')),
2886 ('r', 'rev', [], _('a specific revision you would like to push')),
2886 ('r', 'rev', [], _('a specific revision you would like to push')),
2887 ('', 'remotecmd', '',
2887 ('', 'remotecmd', '',
2888 _('specify hg command to run on the remote side'))],
2888 _('specify hg command to run on the remote side'))],
2889 _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')),
2889 _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')),
2890 "debugrawcommit|rawcommit":
2890 "debugrawcommit|rawcommit":
2891 (rawcommit,
2891 (rawcommit,
2892 [('p', 'parent', [], _('parent')),
2892 [('p', 'parent', [], _('parent')),
2893 ('d', 'date', '', _('date code')),
2893 ('d', 'date', '', _('date code')),
2894 ('u', 'user', '', _('user')),
2894 ('u', 'user', '', _('user')),
2895 ('F', 'files', '', _('file list')),
2895 ('F', 'files', '', _('file list')),
2896 ('m', 'message', '', _('commit message')),
2896 ('m', 'message', '', _('commit message')),
2897 ('l', 'logfile', '', _('commit message file'))],
2897 ('l', 'logfile', '', _('commit message file'))],
2898 _('hg debugrawcommit [OPTION]... [FILE]...')),
2898 _('hg debugrawcommit [OPTION]... [FILE]...')),
2899 "recover": (recover, [], _('hg recover')),
2899 "recover": (recover, [], _('hg recover')),
2900 "^remove|rm":
2900 "^remove|rm":
2901 (remove,
2901 (remove,
2902 [('f', 'force', None, _('remove file even if modified')),
2902 [('f', 'force', None, _('remove file even if modified')),
2903 ('I', 'include', [], _('include names matching the given patterns')),
2903 ('I', 'include', [], _('include names matching the given patterns')),
2904 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2904 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2905 _('hg remove [OPTION]... FILE...')),
2905 _('hg remove [OPTION]... FILE...')),
2906 "rename|mv":
2906 "rename|mv":
2907 (rename,
2907 (rename,
2908 [('A', 'after', None, _('record a rename that has already occurred')),
2908 [('A', 'after', None, _('record a rename that has already occurred')),
2909 ('f', 'force', None,
2909 ('f', 'force', None,
2910 _('forcibly copy over an existing managed file')),
2910 _('forcibly copy over an existing managed file')),
2911 ('I', 'include', [], _('include names matching the given patterns')),
2911 ('I', 'include', [], _('include names matching the given patterns')),
2912 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2912 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2913 _('hg rename [OPTION]... SOURCE... DEST')),
2913 _('hg rename [OPTION]... SOURCE... DEST')),
2914 "^revert":
2914 "^revert":
2915 (revert,
2915 (revert,
2916 [('r', 'rev', '', _('revision to revert to')),
2916 [('r', 'rev', '', _('revision to revert to')),
2917 ('I', 'include', [], _('include names matching the given patterns')),
2917 ('I', 'include', [], _('include names matching the given patterns')),
2918 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2918 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2919 _('hg revert [-r REV] [NAME]...')),
2919 _('hg revert [-r REV] [NAME]...')),
2920 "root": (root, [], _('hg root')),
2920 "root": (root, [], _('hg root')),
2921 "^serve":
2921 "^serve":
2922 (serve,
2922 (serve,
2923 [('A', 'accesslog', '', _('name of access log file to write to')),
2923 [('A', 'accesslog', '', _('name of access log file to write to')),
2924 ('d', 'daemon', None, _('run server in background')),
2924 ('d', 'daemon', None, _('run server in background')),
2925 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2925 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
2926 ('E', 'errorlog', '', _('name of error log file to write to')),
2926 ('E', 'errorlog', '', _('name of error log file to write to')),
2927 ('p', 'port', 0, _('port to use (default: 8000)')),
2927 ('p', 'port', 0, _('port to use (default: 8000)')),
2928 ('a', 'address', '', _('address to use')),
2928 ('a', 'address', '', _('address to use')),
2929 ('n', 'name', '',
2929 ('n', 'name', '',
2930 _('name to show in web pages (default: working dir)')),
2930 _('name to show in web pages (default: working dir)')),
2931 ('', 'pid-file', '', _('name of file to write process ID to')),
2931 ('', 'pid-file', '', _('name of file to write process ID to')),
2932 ('', 'stdio', None, _('for remote clients')),
2932 ('', 'stdio', None, _('for remote clients')),
2933 ('t', 'templates', '', _('web templates to use')),
2933 ('t', 'templates', '', _('web templates to use')),
2934 ('', 'style', '', _('template style to use')),
2934 ('', 'style', '', _('template style to use')),
2935 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2935 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2936 _('hg serve [OPTION]...')),
2936 _('hg serve [OPTION]...')),
2937 "^status|st":
2937 "^status|st":
2938 (status,
2938 (status,
2939 [('m', 'modified', None, _('show only modified files')),
2939 [('m', 'modified', None, _('show only modified files')),
2940 ('a', 'added', None, _('show only added files')),
2940 ('a', 'added', None, _('show only added files')),
2941 ('r', 'removed', None, _('show only removed files')),
2941 ('r', 'removed', None, _('show only removed files')),
2942 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2942 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
2943 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2943 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2944 ('n', 'no-status', None, _('hide status prefix')),
2944 ('n', 'no-status', None, _('hide status prefix')),
2945 ('0', 'print0', None,
2945 ('0', 'print0', None,
2946 _('end filenames with NUL, for use with xargs')),
2946 _('end filenames with NUL, for use with xargs')),
2947 ('I', 'include', [], _('include names matching the given patterns')),
2947 ('I', 'include', [], _('include names matching the given patterns')),
2948 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2948 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2949 _('hg status [OPTION]... [FILE]...')),
2949 _('hg status [OPTION]... [FILE]...')),
2950 "tag":
2950 "tag":
2951 (tag,
2951 (tag,
2952 [('l', 'local', None, _('make the tag local')),
2952 [('l', 'local', None, _('make the tag local')),
2953 ('m', 'message', '', _('message for tag commit log entry')),
2953 ('m', 'message', '', _('message for tag commit log entry')),
2954 ('d', 'date', '', _('record datecode as commit date')),
2954 ('d', 'date', '', _('record datecode as commit date')),
2955 ('u', 'user', '', _('record user as commiter')),
2955 ('u', 'user', '', _('record user as commiter')),
2956 ('r', 'rev', '', _('revision to tag'))],
2956 ('r', 'rev', '', _('revision to tag'))],
2957 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2957 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
2958 "tags": (tags, [], _('hg tags')),
2958 "tags": (tags, [], _('hg tags')),
2959 "tip":
2959 "tip":
2960 (tip,
2960 (tip,
2961 [('b', 'branches', None, _('show branches')),
2961 [('b', 'branches', None, _('show branches')),
2962 ('', 'style', '', _('display using template map file')),
2962 ('', 'style', '', _('display using template map file')),
2963 ('p', 'patch', None, _('show patch')),
2963 ('p', 'patch', None, _('show patch')),
2964 ('', 'template', '', _('display with template'))],
2964 ('', 'template', '', _('display with template'))],
2965 _('hg tip [-b] [-p]')),
2965 _('hg tip [-b] [-p]')),
2966 "unbundle":
2966 "unbundle":
2967 (unbundle,
2967 (unbundle,
2968 [('u', 'update', None,
2968 [('u', 'update', None,
2969 _('update the working directory to tip after unbundle'))],
2969 _('update the working directory to tip after unbundle'))],
2970 _('hg unbundle [-u] FILE')),
2970 _('hg unbundle [-u] FILE')),
2971 "undo": (undo, [], _('hg undo')),
2971 "undo": (undo, [], _('hg undo')),
2972 "^update|up|checkout|co":
2972 "^update|up|checkout|co":
2973 (update,
2973 (update,
2974 [('b', 'branch', '', _('checkout the head of a specific branch')),
2974 [('b', 'branch', '', _('checkout the head of a specific branch')),
2975 ('', 'style', '', _('display using template map file')),
2975 ('', 'style', '', _('display using template map file')),
2976 ('m', 'merge', None, _('allow merging of branches')),
2976 ('m', 'merge', None, _('allow merging of branches')),
2977 ('C', 'clean', None, _('overwrite locally modified files')),
2977 ('C', 'clean', None, _('overwrite locally modified files')),
2978 ('f', 'force', None, _('force a merge with outstanding changes')),
2978 ('f', 'force', None, _('force a merge with outstanding changes')),
2979 ('', 'template', '', _('display with template'))],
2979 ('', 'template', '', _('display with template'))],
2980 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
2980 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
2981 "verify": (verify, [], _('hg verify')),
2981 "verify": (verify, [], _('hg verify')),
2982 "version": (show_version, [], _('hg version')),
2982 "version": (show_version, [], _('hg version')),
2983 }
2983 }
2984
2984
2985 globalopts = [
2985 globalopts = [
2986 ('R', 'repository', '',
2986 ('R', 'repository', '',
2987 _('repository root directory or symbolic path name')),
2987 _('repository root directory or symbolic path name')),
2988 ('', 'cwd', '', _('change working directory')),
2988 ('', 'cwd', '', _('change working directory')),
2989 ('y', 'noninteractive', None,
2989 ('y', 'noninteractive', None,
2990 _('do not prompt, assume \'yes\' for any required answers')),
2990 _('do not prompt, assume \'yes\' for any required answers')),
2991 ('q', 'quiet', None, _('suppress output')),
2991 ('q', 'quiet', None, _('suppress output')),
2992 ('v', 'verbose', None, _('enable additional output')),
2992 ('v', 'verbose', None, _('enable additional output')),
2993 ('', 'debug', None, _('enable debugging output')),
2993 ('', 'debug', None, _('enable debugging output')),
2994 ('', 'debugger', None, _('start debugger')),
2994 ('', 'debugger', None, _('start debugger')),
2995 ('', 'traceback', None, _('print traceback on exception')),
2995 ('', 'traceback', None, _('print traceback on exception')),
2996 ('', 'time', None, _('time how long the command takes')),
2996 ('', 'time', None, _('time how long the command takes')),
2997 ('', 'profile', None, _('print command execution profile')),
2997 ('', 'profile', None, _('print command execution profile')),
2998 ('', 'version', None, _('output version information and exit')),
2998 ('', 'version', None, _('output version information and exit')),
2999 ('h', 'help', None, _('display help and exit')),
2999 ('h', 'help', None, _('display help and exit')),
3000 ]
3000 ]
3001
3001
3002 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3002 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3003 " debugindex debugindexdot")
3003 " debugindex debugindexdot")
3004 optionalrepo = ("paths debugconfig")
3004 optionalrepo = ("paths debugconfig")
3005
3005
3006 def findpossible(cmd):
3006 def findpossible(cmd):
3007 """
3007 """
3008 Return cmd -> (aliases, command table entry)
3008 Return cmd -> (aliases, command table entry)
3009 for each matching command
3009 for each matching command
3010 """
3010 """
3011 choice = {}
3011 choice = {}
3012 debugchoice = {}
3012 debugchoice = {}
3013 for e in table.keys():
3013 for e in table.keys():
3014 aliases = e.lstrip("^").split("|")
3014 aliases = e.lstrip("^").split("|")
3015 if cmd in aliases:
3015 if cmd in aliases:
3016 choice[cmd] = (aliases, table[e])
3016 choice[cmd] = (aliases, table[e])
3017 continue
3017 continue
3018 for a in aliases:
3018 for a in aliases:
3019 if a.startswith(cmd):
3019 if a.startswith(cmd):
3020 if aliases[0].startswith("debug"):
3020 if aliases[0].startswith("debug"):
3021 debugchoice[a] = (aliases, table[e])
3021 debugchoice[a] = (aliases, table[e])
3022 else:
3022 else:
3023 choice[a] = (aliases, table[e])
3023 choice[a] = (aliases, table[e])
3024 break
3024 break
3025
3025
3026 if not choice and debugchoice:
3026 if not choice and debugchoice:
3027 choice = debugchoice
3027 choice = debugchoice
3028
3028
3029 return choice
3029 return choice
3030
3030
3031 def find(cmd):
3031 def find(cmd):
3032 """Return (aliases, command table entry) for command string."""
3032 """Return (aliases, command table entry) for command string."""
3033 choice = findpossible(cmd)
3033 choice = findpossible(cmd)
3034
3034
3035 if choice.has_key(cmd):
3035 if choice.has_key(cmd):
3036 return choice[cmd]
3036 return choice[cmd]
3037
3037
3038 if len(choice) > 1:
3038 if len(choice) > 1:
3039 clist = choice.keys()
3039 clist = choice.keys()
3040 clist.sort()
3040 clist.sort()
3041 raise AmbiguousCommand(cmd, clist)
3041 raise AmbiguousCommand(cmd, clist)
3042
3042
3043 if choice:
3043 if choice:
3044 return choice.values()[0]
3044 return choice.values()[0]
3045
3045
3046 raise UnknownCommand(cmd)
3046 raise UnknownCommand(cmd)
3047
3047
3048 class SignalInterrupt(Exception):
3048 class SignalInterrupt(Exception):
3049 """Exception raised on SIGTERM and SIGHUP."""
3049 """Exception raised on SIGTERM and SIGHUP."""
3050
3050
3051 def catchterm(*args):
3051 def catchterm(*args):
3052 raise SignalInterrupt
3052 raise SignalInterrupt
3053
3053
3054 def run():
3054 def run():
3055 sys.exit(dispatch(sys.argv[1:]))
3055 sys.exit(dispatch(sys.argv[1:]))
3056
3056
3057 class ParseError(Exception):
3057 class ParseError(Exception):
3058 """Exception raised on errors in parsing the command line."""
3058 """Exception raised on errors in parsing the command line."""
3059
3059
3060 def parse(ui, args):
3060 def parse(ui, args):
3061 options = {}
3061 options = {}
3062 cmdoptions = {}
3062 cmdoptions = {}
3063
3063
3064 try:
3064 try:
3065 args = fancyopts.fancyopts(args, globalopts, options)
3065 args = fancyopts.fancyopts(args, globalopts, options)
3066 except fancyopts.getopt.GetoptError, inst:
3066 except fancyopts.getopt.GetoptError, inst:
3067 raise ParseError(None, inst)
3067 raise ParseError(None, inst)
3068
3068
3069 if args:
3069 if args:
3070 cmd, args = args[0], args[1:]
3070 cmd, args = args[0], args[1:]
3071 aliases, i = find(cmd)
3071 aliases, i = find(cmd)
3072 cmd = aliases[0]
3072 cmd = aliases[0]
3073 defaults = ui.config("defaults", cmd)
3073 defaults = ui.config("defaults", cmd)
3074 if defaults:
3074 if defaults:
3075 args = defaults.split() + args
3075 args = defaults.split() + args
3076 c = list(i[1])
3076 c = list(i[1])
3077 else:
3077 else:
3078 cmd = None
3078 cmd = None
3079 c = []
3079 c = []
3080
3080
3081 # combine global options into local
3081 # combine global options into local
3082 for o in globalopts:
3082 for o in globalopts:
3083 c.append((o[0], o[1], options[o[1]], o[3]))
3083 c.append((o[0], o[1], options[o[1]], o[3]))
3084
3084
3085 try:
3085 try:
3086 args = fancyopts.fancyopts(args, c, cmdoptions)
3086 args = fancyopts.fancyopts(args, c, cmdoptions)
3087 except fancyopts.getopt.GetoptError, inst:
3087 except fancyopts.getopt.GetoptError, inst:
3088 raise ParseError(cmd, inst)
3088 raise ParseError(cmd, inst)
3089
3089
3090 # separate global options back out
3090 # separate global options back out
3091 for o in globalopts:
3091 for o in globalopts:
3092 n = o[1]
3092 n = o[1]
3093 options[n] = cmdoptions[n]
3093 options[n] = cmdoptions[n]
3094 del cmdoptions[n]
3094 del cmdoptions[n]
3095
3095
3096 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3096 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3097
3097
3098 def dispatch(args):
3098 def dispatch(args):
3099 signal.signal(signal.SIGTERM, catchterm)
3099 signal.signal(signal.SIGTERM, catchterm)
3100 try:
3100 try:
3101 signal.signal(signal.SIGHUP, catchterm)
3101 signal.signal(signal.SIGHUP, catchterm)
3102 except AttributeError:
3102 except AttributeError:
3103 pass
3103 pass
3104
3104
3105 try:
3105 try:
3106 u = ui.ui()
3106 u = ui.ui()
3107 except util.Abort, inst:
3107 except util.Abort, inst:
3108 sys.stderr.write(_("abort: %s\n") % inst)
3108 sys.stderr.write(_("abort: %s\n") % inst)
3109 sys.exit(1)
3109 sys.exit(1)
3110
3110
3111 external = []
3111 external = []
3112 for x in u.extensions():
3112 for x in u.extensions():
3113 def on_exception(exc, inst):
3113 def on_exception(exc, inst):
3114 u.warn(_("*** failed to import extension %s\n") % x[1])
3114 u.warn(_("*** failed to import extension %s\n") % x[1])
3115 u.warn("%s\n" % inst)
3115 u.warn("%s\n" % inst)
3116 if "--traceback" in sys.argv[1:]:
3116 if "--traceback" in sys.argv[1:]:
3117 traceback.print_exc()
3117 traceback.print_exc()
3118 if x[1]:
3118 if x[1]:
3119 try:
3119 try:
3120 mod = imp.load_source(x[0], x[1])
3120 mod = imp.load_source(x[0], x[1])
3121 except Exception, inst:
3121 except Exception, inst:
3122 on_exception(Exception, inst)
3122 on_exception(Exception, inst)
3123 continue
3123 continue
3124 else:
3124 else:
3125 def importh(name):
3125 def importh(name):
3126 mod = __import__(name)
3126 mod = __import__(name)
3127 components = name.split('.')
3127 components = name.split('.')
3128 for comp in components[1:]:
3128 for comp in components[1:]:
3129 mod = getattr(mod, comp)
3129 mod = getattr(mod, comp)
3130 return mod
3130 return mod
3131 try:
3131 try:
3132 try:
3132 try:
3133 mod = importh("hgext." + x[0])
3133 mod = importh("hgext." + x[0])
3134 except ImportError:
3134 except ImportError:
3135 mod = importh(x[0])
3135 mod = importh(x[0])
3136 except Exception, inst:
3136 except Exception, inst:
3137 on_exception(Exception, inst)
3137 on_exception(Exception, inst)
3138 continue
3138 continue
3139
3139
3140 external.append(mod)
3140 external.append(mod)
3141 for x in external:
3141 for x in external:
3142 cmdtable = getattr(x, 'cmdtable', {})
3142 cmdtable = getattr(x, 'cmdtable', {})
3143 for t in cmdtable:
3143 for t in cmdtable:
3144 if t in table:
3144 if t in table:
3145 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
3145 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
3146 table.update(cmdtable)
3146 table.update(cmdtable)
3147
3147
3148 try:
3148 try:
3149 cmd, func, args, options, cmdoptions = parse(u, args)
3149 cmd, func, args, options, cmdoptions = parse(u, args)
3150 if options["time"]:
3150 if options["time"]:
3151 def get_times():
3151 def get_times():
3152 t = os.times()
3152 t = os.times()
3153 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3153 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3154 t = (t[0], t[1], t[2], t[3], time.clock())
3154 t = (t[0], t[1], t[2], t[3], time.clock())
3155 return t
3155 return t
3156 s = get_times()
3156 s = get_times()
3157 def print_time():
3157 def print_time():
3158 t = get_times()
3158 t = get_times()
3159 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3159 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3160 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3160 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3161 atexit.register(print_time)
3161 atexit.register(print_time)
3162
3162
3163 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3163 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3164 not options["noninteractive"])
3164 not options["noninteractive"])
3165
3165
3166 # enter the debugger before command execution
3166 # enter the debugger before command execution
3167 if options['debugger']:
3167 if options['debugger']:
3168 pdb.set_trace()
3168 pdb.set_trace()
3169
3169
3170 try:
3170 try:
3171 if options['cwd']:
3171 if options['cwd']:
3172 try:
3172 try:
3173 os.chdir(options['cwd'])
3173 os.chdir(options['cwd'])
3174 except OSError, inst:
3174 except OSError, inst:
3175 raise util.Abort('%s: %s' %
3175 raise util.Abort('%s: %s' %
3176 (options['cwd'], inst.strerror))
3176 (options['cwd'], inst.strerror))
3177
3177
3178 path = u.expandpath(options["repository"]) or ""
3178 path = u.expandpath(options["repository"]) or ""
3179 repo = path and hg.repository(u, path=path) or None
3179 repo = path and hg.repository(u, path=path) or None
3180
3180
3181 if options['help']:
3181 if options['help']:
3182 help_(u, cmd, options['version'])
3182 help_(u, cmd, options['version'])
3183 sys.exit(0)
3183 sys.exit(0)
3184 elif options['version']:
3184 elif options['version']:
3185 show_version(u)
3185 show_version(u)
3186 sys.exit(0)
3186 sys.exit(0)
3187 elif not cmd:
3187 elif not cmd:
3188 help_(u, 'shortlist')
3188 help_(u, 'shortlist')
3189 sys.exit(0)
3189 sys.exit(0)
3190
3190
3191 if cmd not in norepo.split():
3191 if cmd not in norepo.split():
3192 try:
3192 try:
3193 if not repo:
3193 if not repo:
3194 repo = hg.repository(u, path=path)
3194 repo = hg.repository(u, path=path)
3195 u = repo.ui
3195 u = repo.ui
3196 for x in external:
3196 for x in external:
3197 if hasattr(x, 'reposetup'):
3197 if hasattr(x, 'reposetup'):
3198 x.reposetup(u, repo)
3198 x.reposetup(u, repo)
3199 except hg.RepoError:
3199 except hg.RepoError:
3200 if cmd not in optionalrepo.split():
3200 if cmd not in optionalrepo.split():
3201 raise
3201 raise
3202 d = lambda: func(u, repo, *args, **cmdoptions)
3202 d = lambda: func(u, repo, *args, **cmdoptions)
3203 else:
3203 else:
3204 d = lambda: func(u, *args, **cmdoptions)
3204 d = lambda: func(u, *args, **cmdoptions)
3205
3205
3206 try:
3206 try:
3207 if options['profile']:
3207 if options['profile']:
3208 import hotshot, hotshot.stats
3208 import hotshot, hotshot.stats
3209 prof = hotshot.Profile("hg.prof")
3209 prof = hotshot.Profile("hg.prof")
3210 try:
3210 try:
3211 try:
3211 try:
3212 return prof.runcall(d)
3212 return prof.runcall(d)
3213 except:
3213 except:
3214 try:
3214 try:
3215 u.warn(_('exception raised - generating '
3215 u.warn(_('exception raised - generating '
3216 'profile anyway\n'))
3216 'profile anyway\n'))
3217 except:
3217 except:
3218 pass
3218 pass
3219 raise
3219 raise
3220 finally:
3220 finally:
3221 prof.close()
3221 prof.close()
3222 stats = hotshot.stats.load("hg.prof")
3222 stats = hotshot.stats.load("hg.prof")
3223 stats.strip_dirs()
3223 stats.strip_dirs()
3224 stats.sort_stats('time', 'calls')
3224 stats.sort_stats('time', 'calls')
3225 stats.print_stats(40)
3225 stats.print_stats(40)
3226 else:
3226 else:
3227 return d()
3227 return d()
3228 finally:
3228 finally:
3229 u.flush()
3229 u.flush()
3230 except:
3230 except:
3231 # enter the debugger when we hit an exception
3231 # enter the debugger when we hit an exception
3232 if options['debugger']:
3232 if options['debugger']:
3233 pdb.post_mortem(sys.exc_info()[2])
3233 pdb.post_mortem(sys.exc_info()[2])
3234 if options['traceback']:
3234 if options['traceback']:
3235 traceback.print_exc()
3235 traceback.print_exc()
3236 raise
3236 raise
3237 except ParseError, inst:
3237 except ParseError, inst:
3238 if inst.args[0]:
3238 if inst.args[0]:
3239 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3239 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3240 help_(u, inst.args[0])
3240 help_(u, inst.args[0])
3241 else:
3241 else:
3242 u.warn(_("hg: %s\n") % inst.args[1])
3242 u.warn(_("hg: %s\n") % inst.args[1])
3243 help_(u, 'shortlist')
3243 help_(u, 'shortlist')
3244 sys.exit(-1)
3244 sys.exit(-1)
3245 except AmbiguousCommand, inst:
3245 except AmbiguousCommand, inst:
3246 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3246 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3247 (inst.args[0], " ".join(inst.args[1])))
3247 (inst.args[0], " ".join(inst.args[1])))
3248 sys.exit(1)
3248 sys.exit(1)
3249 except UnknownCommand, inst:
3249 except UnknownCommand, inst:
3250 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3250 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3251 help_(u, 'shortlist')
3251 help_(u, 'shortlist')
3252 sys.exit(1)
3252 sys.exit(1)
3253 except hg.RepoError, inst:
3253 except hg.RepoError, inst:
3254 u.warn(_("abort: "), inst, "!\n")
3254 u.warn(_("abort: "), inst, "!\n")
3255 except revlog.RevlogError, inst:
3255 except revlog.RevlogError, inst:
3256 u.warn(_("abort: "), inst, "!\n")
3256 u.warn(_("abort: "), inst, "!\n")
3257 except SignalInterrupt:
3257 except SignalInterrupt:
3258 u.warn(_("killed!\n"))
3258 u.warn(_("killed!\n"))
3259 except KeyboardInterrupt:
3259 except KeyboardInterrupt:
3260 try:
3260 try:
3261 u.warn(_("interrupted!\n"))
3261 u.warn(_("interrupted!\n"))
3262 except IOError, inst:
3262 except IOError, inst:
3263 if inst.errno == errno.EPIPE:
3263 if inst.errno == errno.EPIPE:
3264 if u.debugflag:
3264 if u.debugflag:
3265 u.warn(_("\nbroken pipe\n"))
3265 u.warn(_("\nbroken pipe\n"))
3266 else:
3266 else:
3267 raise
3267 raise
3268 except IOError, inst:
3268 except IOError, inst:
3269 if hasattr(inst, "code"):
3269 if hasattr(inst, "code"):
3270 u.warn(_("abort: %s\n") % inst)
3270 u.warn(_("abort: %s\n") % inst)
3271 elif hasattr(inst, "reason"):
3271 elif hasattr(inst, "reason"):
3272 u.warn(_("abort: error: %s\n") % inst.reason[1])
3272 u.warn(_("abort: error: %s\n") % inst.reason[1])
3273 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3273 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3274 if u.debugflag:
3274 if u.debugflag:
3275 u.warn(_("broken pipe\n"))
3275 u.warn(_("broken pipe\n"))
3276 elif getattr(inst, "strerror", None):
3276 elif getattr(inst, "strerror", None):
3277 if getattr(inst, "filename", None):
3277 if getattr(inst, "filename", None):
3278 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3278 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3279 else:
3279 else:
3280 u.warn(_("abort: %s\n") % inst.strerror)
3280 u.warn(_("abort: %s\n") % inst.strerror)
3281 else:
3281 else:
3282 raise
3282 raise
3283 except OSError, inst:
3283 except OSError, inst:
3284 if hasattr(inst, "filename"):
3284 if hasattr(inst, "filename"):
3285 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3285 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3286 else:
3286 else:
3287 u.warn(_("abort: %s\n") % inst.strerror)
3287 u.warn(_("abort: %s\n") % inst.strerror)
3288 except util.Abort, inst:
3288 except util.Abort, inst:
3289 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3289 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3290 sys.exit(1)
3290 sys.exit(1)
3291 except TypeError, inst:
3291 except TypeError, inst:
3292 # was this an argument error?
3292 # was this an argument error?
3293 tb = traceback.extract_tb(sys.exc_info()[2])
3293 tb = traceback.extract_tb(sys.exc_info()[2])
3294 if len(tb) > 2: # no
3294 if len(tb) > 2: # no
3295 raise
3295 raise
3296 u.debug(inst, "\n")
3296 u.debug(inst, "\n")
3297 u.warn(_("%s: invalid arguments\n") % cmd)
3297 u.warn(_("%s: invalid arguments\n") % cmd)
3298 help_(u, cmd)
3298 help_(u, cmd)
3299 except SystemExit:
3299 except SystemExit:
3300 # don't catch this in the catch-all below
3300 # don't catch this in the catch-all below
3301 raise
3301 raise
3302 except:
3302 except:
3303 u.warn(_("** unknown exception encountered, details follow\n"))
3303 u.warn(_("** unknown exception encountered, details follow\n"))
3304 u.warn(_("** report bug details to mercurial@selenic.com\n"))
3304 u.warn(_("** report bug details to mercurial@selenic.com\n"))
3305 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3305 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3306 % version.get_version())
3306 % version.get_version())
3307 raise
3307 raise
3308
3308
3309 sys.exit(-1)
3309 sys.exit(-1)
@@ -1,1904 +1,1904 b''
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import os, util
8 import os, util
9 import filelog, manifest, changelog, dirstate, repo
9 import filelog, manifest, changelog, dirstate, repo
10 from node import *
10 from node import *
11 from i18n import gettext as _
11 from i18n import gettext as _
12 from demandload import *
12 from demandload import *
13 demandload(globals(), "re lock transaction tempfile stat mdiff errno ui")
13 demandload(globals(), "re lock transaction tempfile stat mdiff errno ui")
14 demandload(globals(), "changegroup")
14 demandload(globals(), "changegroup")
15
15
16 class localrepository(object):
16 class localrepository(object):
17 def __del__(self):
17 def __del__(self):
18 self.transhandle = None
18 self.transhandle = None
19 def __init__(self, parentui, path=None, create=0):
19 def __init__(self, parentui, path=None, create=0):
20 if not path:
20 if not path:
21 p = os.getcwd()
21 p = os.getcwd()
22 while not os.path.isdir(os.path.join(p, ".hg")):
22 while not os.path.isdir(os.path.join(p, ".hg")):
23 oldp = p
23 oldp = p
24 p = os.path.dirname(p)
24 p = os.path.dirname(p)
25 if p == oldp:
25 if p == oldp:
26 raise repo.RepoError(_("no repo found"))
26 raise repo.RepoError(_("no repo found"))
27 path = p
27 path = p
28 self.path = os.path.join(path, ".hg")
28 self.path = os.path.join(path, ".hg")
29
29
30 if not create and not os.path.isdir(self.path):
30 if not create and not os.path.isdir(self.path):
31 raise repo.RepoError(_("repository %s not found") % path)
31 raise repo.RepoError(_("repository %s not found") % path)
32
32
33 self.root = os.path.abspath(path)
33 self.root = os.path.abspath(path)
34 self.ui = ui.ui(parentui=parentui)
34 self.ui = ui.ui(parentui=parentui)
35 self.opener = util.opener(self.path)
35 self.opener = util.opener(self.path)
36 self.wopener = util.opener(self.root)
36 self.wopener = util.opener(self.root)
37 self.manifest = manifest.manifest(self.opener)
37 self.manifest = manifest.manifest(self.opener)
38 self.changelog = changelog.changelog(self.opener)
38 self.changelog = changelog.changelog(self.opener)
39 self.tagscache = None
39 self.tagscache = None
40 self.nodetagscache = None
40 self.nodetagscache = None
41 self.encodepats = None
41 self.encodepats = None
42 self.decodepats = None
42 self.decodepats = None
43 self.transhandle = None
43 self.transhandle = None
44
44
45 if create:
45 if create:
46 os.mkdir(self.path)
46 os.mkdir(self.path)
47 os.mkdir(self.join("data"))
47 os.mkdir(self.join("data"))
48
48
49 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
49 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
50 try:
50 try:
51 self.ui.readconfig(self.join("hgrc"), self.root)
51 self.ui.readconfig(self.join("hgrc"), self.root)
52 except IOError:
52 except IOError:
53 pass
53 pass
54
54
55 def hook(self, name, throw=False, **args):
55 def hook(self, name, throw=False, **args):
56 def runhook(name, cmd):
56 def runhook(name, cmd):
57 self.ui.note(_("running hook %s: %s\n") % (name, cmd))
57 self.ui.note(_("running hook %s: %s\n") % (name, cmd))
58 env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()] +
58 env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()] +
59 [(k.upper(), v) for k, v in args.iteritems()])
59 [(k.upper(), v) for k, v in args.iteritems()])
60 r = util.system(cmd, environ=env, cwd=self.root)
60 r = util.system(cmd, environ=env, cwd=self.root)
61 if r:
61 if r:
62 desc, r = util.explain_exit(r)
62 desc, r = util.explain_exit(r)
63 if throw:
63 if throw:
64 raise util.Abort(_('%s hook %s') % (name, desc))
64 raise util.Abort(_('%s hook %s') % (name, desc))
65 self.ui.warn(_('error: %s hook %s\n') % (name, desc))
65 self.ui.warn(_('error: %s hook %s\n') % (name, desc))
66 return False
66 return False
67 return True
67 return True
68
68
69 r = True
69 r = True
70 hooks = [(hname, cmd) for hname, cmd in self.ui.configitems("hooks")
70 hooks = [(hname, cmd) for hname, cmd in self.ui.configitems("hooks")
71 if hname.split(".", 1)[0] == name and cmd]
71 if hname.split(".", 1)[0] == name and cmd]
72 hooks.sort()
72 hooks.sort()
73 for hname, cmd in hooks:
73 for hname, cmd in hooks:
74 r = runhook(hname, cmd) and r
74 r = runhook(hname, cmd) and r
75 return r
75 return r
76
76
77 def tags(self):
77 def tags(self):
78 '''return a mapping of tag to node'''
78 '''return a mapping of tag to node'''
79 if not self.tagscache:
79 if not self.tagscache:
80 self.tagscache = {}
80 self.tagscache = {}
81
81
82 def parsetag(line, context):
82 def parsetag(line, context):
83 if not line:
83 if not line:
84 return
84 return
85 s = l.split(" ", 1)
85 s = l.split(" ", 1)
86 if len(s) != 2:
86 if len(s) != 2:
87 self.ui.warn(_("%s: ignoring invalid tag\n") % context)
87 self.ui.warn(_("%s: ignoring invalid tag\n") % context)
88 return
88 return
89 node, key = s
89 node, key = s
90 try:
90 try:
91 bin_n = bin(node)
91 bin_n = bin(node)
92 except TypeError:
92 except TypeError:
93 self.ui.warn(_("%s: ignoring invalid tag\n") % context)
93 self.ui.warn(_("%s: ignoring invalid tag\n") % context)
94 return
94 return
95 if bin_n not in self.changelog.nodemap:
95 if bin_n not in self.changelog.nodemap:
96 self.ui.warn(_("%s: ignoring invalid tag\n") % context)
96 self.ui.warn(_("%s: ignoring invalid tag\n") % context)
97 return
97 return
98 self.tagscache[key.strip()] = bin_n
98 self.tagscache[key.strip()] = bin_n
99
99
100 # read each head of the tags file, ending with the tip
100 # read each head of the tags file, ending with the tip
101 # and add each tag found to the map, with "newer" ones
101 # and add each tag found to the map, with "newer" ones
102 # taking precedence
102 # taking precedence
103 fl = self.file(".hgtags")
103 fl = self.file(".hgtags")
104 h = fl.heads()
104 h = fl.heads()
105 h.reverse()
105 h.reverse()
106 for r in h:
106 for r in h:
107 count = 0
107 count = 0
108 for l in fl.read(r).splitlines():
108 for l in fl.read(r).splitlines():
109 count += 1
109 count += 1
110 parsetag(l, ".hgtags:%d" % count)
110 parsetag(l, ".hgtags:%d" % count)
111
111
112 try:
112 try:
113 f = self.opener("localtags")
113 f = self.opener("localtags")
114 count = 0
114 count = 0
115 for l in f:
115 for l in f:
116 count += 1
116 count += 1
117 parsetag(l, "localtags:%d" % count)
117 parsetag(l, "localtags:%d" % count)
118 except IOError:
118 except IOError:
119 pass
119 pass
120
120
121 self.tagscache['tip'] = self.changelog.tip()
121 self.tagscache['tip'] = self.changelog.tip()
122
122
123 return self.tagscache
123 return self.tagscache
124
124
125 def tagslist(self):
125 def tagslist(self):
126 '''return a list of tags ordered by revision'''
126 '''return a list of tags ordered by revision'''
127 l = []
127 l = []
128 for t, n in self.tags().items():
128 for t, n in self.tags().items():
129 try:
129 try:
130 r = self.changelog.rev(n)
130 r = self.changelog.rev(n)
131 except:
131 except:
132 r = -2 # sort to the beginning of the list if unknown
132 r = -2 # sort to the beginning of the list if unknown
133 l.append((r, t, n))
133 l.append((r, t, n))
134 l.sort()
134 l.sort()
135 return [(t, n) for r, t, n in l]
135 return [(t, n) for r, t, n in l]
136
136
137 def nodetags(self, node):
137 def nodetags(self, node):
138 '''return the tags associated with a node'''
138 '''return the tags associated with a node'''
139 if not self.nodetagscache:
139 if not self.nodetagscache:
140 self.nodetagscache = {}
140 self.nodetagscache = {}
141 for t, n in self.tags().items():
141 for t, n in self.tags().items():
142 self.nodetagscache.setdefault(n, []).append(t)
142 self.nodetagscache.setdefault(n, []).append(t)
143 return self.nodetagscache.get(node, [])
143 return self.nodetagscache.get(node, [])
144
144
145 def lookup(self, key):
145 def lookup(self, key):
146 try:
146 try:
147 return self.tags()[key]
147 return self.tags()[key]
148 except KeyError:
148 except KeyError:
149 try:
149 try:
150 return self.changelog.lookup(key)
150 return self.changelog.lookup(key)
151 except:
151 except:
152 raise repo.RepoError(_("unknown revision '%s'") % key)
152 raise repo.RepoError(_("unknown revision '%s'") % key)
153
153
154 def dev(self):
154 def dev(self):
155 return os.stat(self.path).st_dev
155 return os.stat(self.path).st_dev
156
156
157 def local(self):
157 def local(self):
158 return True
158 return True
159
159
160 def join(self, f):
160 def join(self, f):
161 return os.path.join(self.path, f)
161 return os.path.join(self.path, f)
162
162
163 def wjoin(self, f):
163 def wjoin(self, f):
164 return os.path.join(self.root, f)
164 return os.path.join(self.root, f)
165
165
166 def file(self, f):
166 def file(self, f):
167 if f[0] == '/':
167 if f[0] == '/':
168 f = f[1:]
168 f = f[1:]
169 return filelog.filelog(self.opener, f)
169 return filelog.filelog(self.opener, f)
170
170
171 def getcwd(self):
171 def getcwd(self):
172 return self.dirstate.getcwd()
172 return self.dirstate.getcwd()
173
173
174 def wfile(self, f, mode='r'):
174 def wfile(self, f, mode='r'):
175 return self.wopener(f, mode)
175 return self.wopener(f, mode)
176
176
177 def wread(self, filename):
177 def wread(self, filename):
178 if self.encodepats == None:
178 if self.encodepats == None:
179 l = []
179 l = []
180 for pat, cmd in self.ui.configitems("encode"):
180 for pat, cmd in self.ui.configitems("encode"):
181 mf = util.matcher(self.root, "", [pat], [], [])[1]
181 mf = util.matcher(self.root, "", [pat], [], [])[1]
182 l.append((mf, cmd))
182 l.append((mf, cmd))
183 self.encodepats = l
183 self.encodepats = l
184
184
185 data = self.wopener(filename, 'r').read()
185 data = self.wopener(filename, 'r').read()
186
186
187 for mf, cmd in self.encodepats:
187 for mf, cmd in self.encodepats:
188 if mf(filename):
188 if mf(filename):
189 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
189 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
190 data = util.filter(data, cmd)
190 data = util.filter(data, cmd)
191 break
191 break
192
192
193 return data
193 return data
194
194
195 def wwrite(self, filename, data, fd=None):
195 def wwrite(self, filename, data, fd=None):
196 if self.decodepats == None:
196 if self.decodepats == None:
197 l = []
197 l = []
198 for pat, cmd in self.ui.configitems("decode"):
198 for pat, cmd in self.ui.configitems("decode"):
199 mf = util.matcher(self.root, "", [pat], [], [])[1]
199 mf = util.matcher(self.root, "", [pat], [], [])[1]
200 l.append((mf, cmd))
200 l.append((mf, cmd))
201 self.decodepats = l
201 self.decodepats = l
202
202
203 for mf, cmd in self.decodepats:
203 for mf, cmd in self.decodepats:
204 if mf(filename):
204 if mf(filename):
205 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
205 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
206 data = util.filter(data, cmd)
206 data = util.filter(data, cmd)
207 break
207 break
208
208
209 if fd:
209 if fd:
210 return fd.write(data)
210 return fd.write(data)
211 return self.wopener(filename, 'w').write(data)
211 return self.wopener(filename, 'w').write(data)
212
212
213 def transaction(self):
213 def transaction(self):
214 tr = self.transhandle
214 tr = self.transhandle
215 if tr != None and tr.running():
215 if tr != None and tr.running():
216 return tr.nest()
216 return tr.nest()
217
217
218 # save dirstate for undo
218 # save dirstate for undo
219 try:
219 try:
220 ds = self.opener("dirstate").read()
220 ds = self.opener("dirstate").read()
221 except IOError:
221 except IOError:
222 ds = ""
222 ds = ""
223 self.opener("journal.dirstate", "w").write(ds)
223 self.opener("journal.dirstate", "w").write(ds)
224
224
225 tr = transaction.transaction(self.ui.warn, self.opener,
225 tr = transaction.transaction(self.ui.warn, self.opener,
226 self.join("journal"),
226 self.join("journal"),
227 aftertrans(self.path))
227 aftertrans(self.path))
228 self.transhandle = tr
228 self.transhandle = tr
229 return tr
229 return tr
230
230
231 def recover(self):
231 def recover(self):
232 l = self.lock()
232 l = self.lock()
233 if os.path.exists(self.join("journal")):
233 if os.path.exists(self.join("journal")):
234 self.ui.status(_("rolling back interrupted transaction\n"))
234 self.ui.status(_("rolling back interrupted transaction\n"))
235 transaction.rollback(self.opener, self.join("journal"))
235 transaction.rollback(self.opener, self.join("journal"))
236 self.reload()
236 self.reload()
237 return True
237 return True
238 else:
238 else:
239 self.ui.warn(_("no interrupted transaction available\n"))
239 self.ui.warn(_("no interrupted transaction available\n"))
240 return False
240 return False
241
241
242 def undo(self, wlock=None):
242 def undo(self, wlock=None):
243 if not wlock:
243 if not wlock:
244 wlock = self.wlock()
244 wlock = self.wlock()
245 l = self.lock()
245 l = self.lock()
246 if os.path.exists(self.join("undo")):
246 if os.path.exists(self.join("undo")):
247 self.ui.status(_("rolling back last transaction\n"))
247 self.ui.status(_("rolling back last transaction\n"))
248 transaction.rollback(self.opener, self.join("undo"))
248 transaction.rollback(self.opener, self.join("undo"))
249 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
249 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
250 self.reload()
250 self.reload()
251 self.wreload()
251 self.wreload()
252 else:
252 else:
253 self.ui.warn(_("no undo information available\n"))
253 self.ui.warn(_("no undo information available\n"))
254
254
255 def wreload(self):
255 def wreload(self):
256 self.dirstate.read()
256 self.dirstate.read()
257
257
258 def reload(self):
258 def reload(self):
259 self.changelog.load()
259 self.changelog.load()
260 self.manifest.load()
260 self.manifest.load()
261 self.tagscache = None
261 self.tagscache = None
262 self.nodetagscache = None
262 self.nodetagscache = None
263
263
264 def do_lock(self, lockname, wait, releasefn=None, acquirefn=None):
264 def do_lock(self, lockname, wait, releasefn=None, acquirefn=None):
265 try:
265 try:
266 l = lock.lock(self.join(lockname), 0, releasefn)
266 l = lock.lock(self.join(lockname), 0, releasefn)
267 except lock.LockHeld, inst:
267 except lock.LockHeld, inst:
268 if not wait:
268 if not wait:
269 raise inst
269 raise inst
270 self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0])
270 self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0])
271 try:
271 try:
272 # default to 600 seconds timeout
272 # default to 600 seconds timeout
273 l = lock.lock(self.join(lockname),
273 l = lock.lock(self.join(lockname),
274 int(self.ui.config("ui", "timeout") or 600),
274 int(self.ui.config("ui", "timeout") or 600),
275 releasefn)
275 releasefn)
276 except lock.LockHeld, inst:
276 except lock.LockHeld, inst:
277 raise util.Abort(_("timeout while waiting for "
277 raise util.Abort(_("timeout while waiting for "
278 "lock held by %s") % inst.args[0])
278 "lock held by %s") % inst.args[0])
279 if acquirefn:
279 if acquirefn:
280 acquirefn()
280 acquirefn()
281 return l
281 return l
282
282
283 def lock(self, wait=1):
283 def lock(self, wait=1):
284 return self.do_lock("lock", wait, acquirefn=self.reload)
284 return self.do_lock("lock", wait, acquirefn=self.reload)
285
285
286 def wlock(self, wait=1):
286 def wlock(self, wait=1):
287 return self.do_lock("wlock", wait,
287 return self.do_lock("wlock", wait,
288 self.dirstate.write,
288 self.dirstate.write,
289 self.wreload)
289 self.wreload)
290
290
291 def checkfilemerge(self, filename, text, filelog, manifest1, manifest2):
291 def checkfilemerge(self, filename, text, filelog, manifest1, manifest2):
292 "determine whether a new filenode is needed"
292 "determine whether a new filenode is needed"
293 fp1 = manifest1.get(filename, nullid)
293 fp1 = manifest1.get(filename, nullid)
294 fp2 = manifest2.get(filename, nullid)
294 fp2 = manifest2.get(filename, nullid)
295
295
296 if fp2 != nullid:
296 if fp2 != nullid:
297 # is one parent an ancestor of the other?
297 # is one parent an ancestor of the other?
298 fpa = filelog.ancestor(fp1, fp2)
298 fpa = filelog.ancestor(fp1, fp2)
299 if fpa == fp1:
299 if fpa == fp1:
300 fp1, fp2 = fp2, nullid
300 fp1, fp2 = fp2, nullid
301 elif fpa == fp2:
301 elif fpa == fp2:
302 fp2 = nullid
302 fp2 = nullid
303
303
304 # is the file unmodified from the parent? report existing entry
304 # is the file unmodified from the parent? report existing entry
305 if fp2 == nullid and text == filelog.read(fp1):
305 if fp2 == nullid and text == filelog.read(fp1):
306 return (fp1, None, None)
306 return (fp1, None, None)
307
307
308 return (None, fp1, fp2)
308 return (None, fp1, fp2)
309
309
310 def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None):
310 def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None):
311 orig_parent = self.dirstate.parents()[0] or nullid
311 orig_parent = self.dirstate.parents()[0] or nullid
312 p1 = p1 or self.dirstate.parents()[0] or nullid
312 p1 = p1 or self.dirstate.parents()[0] or nullid
313 p2 = p2 or self.dirstate.parents()[1] or nullid
313 p2 = p2 or self.dirstate.parents()[1] or nullid
314 c1 = self.changelog.read(p1)
314 c1 = self.changelog.read(p1)
315 c2 = self.changelog.read(p2)
315 c2 = self.changelog.read(p2)
316 m1 = self.manifest.read(c1[0])
316 m1 = self.manifest.read(c1[0])
317 mf1 = self.manifest.readflags(c1[0])
317 mf1 = self.manifest.readflags(c1[0])
318 m2 = self.manifest.read(c2[0])
318 m2 = self.manifest.read(c2[0])
319 changed = []
319 changed = []
320
320
321 if orig_parent == p1:
321 if orig_parent == p1:
322 update_dirstate = 1
322 update_dirstate = 1
323 else:
323 else:
324 update_dirstate = 0
324 update_dirstate = 0
325
325
326 if not wlock:
326 if not wlock:
327 wlock = self.wlock()
327 wlock = self.wlock()
328 l = self.lock()
328 l = self.lock()
329 tr = self.transaction()
329 tr = self.transaction()
330 mm = m1.copy()
330 mm = m1.copy()
331 mfm = mf1.copy()
331 mfm = mf1.copy()
332 linkrev = self.changelog.count()
332 linkrev = self.changelog.count()
333 for f in files:
333 for f in files:
334 try:
334 try:
335 t = self.wread(f)
335 t = self.wread(f)
336 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
336 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
337 r = self.file(f)
337 r = self.file(f)
338 mfm[f] = tm
338 mfm[f] = tm
339
339
340 (entry, fp1, fp2) = self.checkfilemerge(f, t, r, m1, m2)
340 (entry, fp1, fp2) = self.checkfilemerge(f, t, r, m1, m2)
341 if entry:
341 if entry:
342 mm[f] = entry
342 mm[f] = entry
343 continue
343 continue
344
344
345 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
345 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
346 changed.append(f)
346 changed.append(f)
347 if update_dirstate:
347 if update_dirstate:
348 self.dirstate.update([f], "n")
348 self.dirstate.update([f], "n")
349 except IOError:
349 except IOError:
350 try:
350 try:
351 del mm[f]
351 del mm[f]
352 del mfm[f]
352 del mfm[f]
353 if update_dirstate:
353 if update_dirstate:
354 self.dirstate.forget([f])
354 self.dirstate.forget([f])
355 except:
355 except:
356 # deleted from p2?
356 # deleted from p2?
357 pass
357 pass
358
358
359 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
359 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
360 user = user or self.ui.username()
360 user = user or self.ui.username()
361 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
361 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
362 tr.close()
362 tr.close()
363 if update_dirstate:
363 if update_dirstate:
364 self.dirstate.setparents(n, nullid)
364 self.dirstate.setparents(n, nullid)
365
365
366 def commit(self, files=None, text="", user=None, date=None,
366 def commit(self, files=None, text="", user=None, date=None,
367 match=util.always, force=False, lock=None, wlock=None):
367 match=util.always, force=False, lock=None, wlock=None):
368 commit = []
368 commit = []
369 remove = []
369 remove = []
370 changed = []
370 changed = []
371
371
372 if files:
372 if files:
373 for f in files:
373 for f in files:
374 s = self.dirstate.state(f)
374 s = self.dirstate.state(f)
375 if s in 'nmai':
375 if s in 'nmai':
376 commit.append(f)
376 commit.append(f)
377 elif s == 'r':
377 elif s == 'r':
378 remove.append(f)
378 remove.append(f)
379 else:
379 else:
380 self.ui.warn(_("%s not tracked!\n") % f)
380 self.ui.warn(_("%s not tracked!\n") % f)
381 else:
381 else:
382 modified, added, removed, deleted, unknown = self.changes(match=match)
382 modified, added, removed, deleted, unknown = self.changes(match=match)
383 commit = modified + added
383 commit = modified + added
384 remove = removed
384 remove = removed
385
385
386 p1, p2 = self.dirstate.parents()
386 p1, p2 = self.dirstate.parents()
387 c1 = self.changelog.read(p1)
387 c1 = self.changelog.read(p1)
388 c2 = self.changelog.read(p2)
388 c2 = self.changelog.read(p2)
389 m1 = self.manifest.read(c1[0])
389 m1 = self.manifest.read(c1[0])
390 mf1 = self.manifest.readflags(c1[0])
390 mf1 = self.manifest.readflags(c1[0])
391 m2 = self.manifest.read(c2[0])
391 m2 = self.manifest.read(c2[0])
392
392
393 if not commit and not remove and not force and p2 == nullid:
393 if not commit and not remove and not force and p2 == nullid:
394 self.ui.status(_("nothing changed\n"))
394 self.ui.status(_("nothing changed\n"))
395 return None
395 return None
396
396
397 xp1 = hex(p1)
397 xp1 = hex(p1)
398 if p2 == nullid: xp2 = ''
398 if p2 == nullid: xp2 = ''
399 else: xp2 = hex(p2)
399 else: xp2 = hex(p2)
400
400
401 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
401 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
402
402
403 if not wlock:
403 if not wlock:
404 wlock = self.wlock()
404 wlock = self.wlock()
405 if not lock:
405 if not lock:
406 lock = self.lock()
406 lock = self.lock()
407 tr = self.transaction()
407 tr = self.transaction()
408
408
409 # check in files
409 # check in files
410 new = {}
410 new = {}
411 linkrev = self.changelog.count()
411 linkrev = self.changelog.count()
412 commit.sort()
412 commit.sort()
413 for f in commit:
413 for f in commit:
414 self.ui.note(f + "\n")
414 self.ui.note(f + "\n")
415 try:
415 try:
416 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
416 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
417 t = self.wread(f)
417 t = self.wread(f)
418 except IOError:
418 except IOError:
419 self.ui.warn(_("trouble committing %s!\n") % f)
419 self.ui.warn(_("trouble committing %s!\n") % f)
420 raise
420 raise
421
421
422 r = self.file(f)
422 r = self.file(f)
423
423
424 meta = {}
424 meta = {}
425 cp = self.dirstate.copied(f)
425 cp = self.dirstate.copied(f)
426 if cp:
426 if cp:
427 meta["copy"] = cp
427 meta["copy"] = cp
428 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
428 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
429 self.ui.debug(_(" %s: copy %s:%s\n") % (f, cp, meta["copyrev"]))
429 self.ui.debug(_(" %s: copy %s:%s\n") % (f, cp, meta["copyrev"]))
430 fp1, fp2 = nullid, nullid
430 fp1, fp2 = nullid, nullid
431 else:
431 else:
432 entry, fp1, fp2 = self.checkfilemerge(f, t, r, m1, m2)
432 entry, fp1, fp2 = self.checkfilemerge(f, t, r, m1, m2)
433 if entry:
433 if entry:
434 new[f] = entry
434 new[f] = entry
435 continue
435 continue
436
436
437 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
437 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
438 # remember what we've added so that we can later calculate
438 # remember what we've added so that we can later calculate
439 # the files to pull from a set of changesets
439 # the files to pull from a set of changesets
440 changed.append(f)
440 changed.append(f)
441
441
442 # update manifest
442 # update manifest
443 m1 = m1.copy()
443 m1 = m1.copy()
444 m1.update(new)
444 m1.update(new)
445 for f in remove:
445 for f in remove:
446 if f in m1:
446 if f in m1:
447 del m1[f]
447 del m1[f]
448 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
448 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
449 (new, remove))
449 (new, remove))
450
450
451 # add changeset
451 # add changeset
452 new = new.keys()
452 new = new.keys()
453 new.sort()
453 new.sort()
454
454
455 user = user or self.ui.username()
455 user = user or self.ui.username()
456 if not text:
456 if not text:
457 edittext = [""]
457 edittext = [""]
458 if p2 != nullid:
458 if p2 != nullid:
459 edittext.append("HG: branch merge")
459 edittext.append("HG: branch merge")
460 edittext.extend(["HG: changed %s" % f for f in changed])
460 edittext.extend(["HG: changed %s" % f for f in changed])
461 edittext.extend(["HG: removed %s" % f for f in remove])
461 edittext.extend(["HG: removed %s" % f for f in remove])
462 if not changed and not remove:
462 if not changed and not remove:
463 edittext.append("HG: no files changed")
463 edittext.append("HG: no files changed")
464 edittext.append("")
464 edittext.append("")
465 # run editor in the repository root
465 # run editor in the repository root
466 olddir = os.getcwd()
466 olddir = os.getcwd()
467 os.chdir(self.root)
467 os.chdir(self.root)
468 edittext = self.ui.edit("\n".join(edittext), user)
468 edittext = self.ui.edit("\n".join(edittext), user)
469 os.chdir(olddir)
469 os.chdir(olddir)
470 if not edittext.rstrip():
470 if not edittext.rstrip():
471 return None
471 return None
472 text = edittext
472 text = edittext
473
473
474 n = self.changelog.add(mn, changed + remove, text, tr, p1, p2, user, date)
474 n = self.changelog.add(mn, changed + remove, text, tr, p1, p2, user, date)
475 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
475 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
476 parent2=xp2)
476 parent2=xp2)
477 tr.close()
477 tr.close()
478
478
479 self.dirstate.setparents(n)
479 self.dirstate.setparents(n)
480 self.dirstate.update(new, "n")
480 self.dirstate.update(new, "n")
481 self.dirstate.forget(remove)
481 self.dirstate.forget(remove)
482
482
483 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
483 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
484 return n
484 return n
485
485
486 def walk(self, node=None, files=[], match=util.always):
486 def walk(self, node=None, files=[], match=util.always):
487 if node:
487 if node:
488 fdict = dict.fromkeys(files)
488 fdict = dict.fromkeys(files)
489 for fn in self.manifest.read(self.changelog.read(node)[0]):
489 for fn in self.manifest.read(self.changelog.read(node)[0]):
490 fdict.pop(fn, None)
490 fdict.pop(fn, None)
491 if match(fn):
491 if match(fn):
492 yield 'm', fn
492 yield 'm', fn
493 for fn in fdict:
493 for fn in fdict:
494 self.ui.warn(_('%s: No such file in rev %s\n') % (
494 self.ui.warn(_('%s: No such file in rev %s\n') % (
495 util.pathto(self.getcwd(), fn), short(node)))
495 util.pathto(self.getcwd(), fn), short(node)))
496 else:
496 else:
497 for src, fn in self.dirstate.walk(files, match):
497 for src, fn in self.dirstate.walk(files, match):
498 yield src, fn
498 yield src, fn
499
499
500 def changes(self, node1=None, node2=None, files=[], match=util.always,
500 def changes(self, node1=None, node2=None, files=[], match=util.always,
501 wlock=None):
501 wlock=None):
502 """return changes between two nodes or node and working directory
502 """return changes between two nodes or node and working directory
503
503
504 If node1 is None, use the first dirstate parent instead.
504 If node1 is None, use the first dirstate parent instead.
505 If node2 is None, compare node1 with working directory.
505 If node2 is None, compare node1 with working directory.
506 """
506 """
507
507
508 def fcmp(fn, mf):
508 def fcmp(fn, mf):
509 t1 = self.wread(fn)
509 t1 = self.wread(fn)
510 t2 = self.file(fn).read(mf.get(fn, nullid))
510 t2 = self.file(fn).read(mf.get(fn, nullid))
511 return cmp(t1, t2)
511 return cmp(t1, t2)
512
512
513 def mfmatches(node):
513 def mfmatches(node):
514 change = self.changelog.read(node)
514 change = self.changelog.read(node)
515 mf = dict(self.manifest.read(change[0]))
515 mf = dict(self.manifest.read(change[0]))
516 for fn in mf.keys():
516 for fn in mf.keys():
517 if not match(fn):
517 if not match(fn):
518 del mf[fn]
518 del mf[fn]
519 return mf
519 return mf
520
520
521 if node1:
521 if node1:
522 # read the manifest from node1 before the manifest from node2,
522 # read the manifest from node1 before the manifest from node2,
523 # so that we'll hit the manifest cache if we're going through
523 # so that we'll hit the manifest cache if we're going through
524 # all the revisions in parent->child order.
524 # all the revisions in parent->child order.
525 mf1 = mfmatches(node1)
525 mf1 = mfmatches(node1)
526
526
527 # are we comparing the working directory?
527 # are we comparing the working directory?
528 if not node2:
528 if not node2:
529 if not wlock:
529 if not wlock:
530 try:
530 try:
531 wlock = self.wlock(wait=0)
531 wlock = self.wlock(wait=0)
532 except lock.LockException:
532 except lock.LockException:
533 wlock = None
533 wlock = None
534 lookup, modified, added, removed, deleted, unknown = (
534 lookup, modified, added, removed, deleted, unknown = (
535 self.dirstate.changes(files, match))
535 self.dirstate.changes(files, match))
536
536
537 # are we comparing working dir against its parent?
537 # are we comparing working dir against its parent?
538 if not node1:
538 if not node1:
539 if lookup:
539 if lookup:
540 # do a full compare of any files that might have changed
540 # do a full compare of any files that might have changed
541 mf2 = mfmatches(self.dirstate.parents()[0])
541 mf2 = mfmatches(self.dirstate.parents()[0])
542 for f in lookup:
542 for f in lookup:
543 if fcmp(f, mf2):
543 if fcmp(f, mf2):
544 modified.append(f)
544 modified.append(f)
545 elif wlock is not None:
545 elif wlock is not None:
546 self.dirstate.update([f], "n")
546 self.dirstate.update([f], "n")
547 else:
547 else:
548 # we are comparing working dir against non-parent
548 # we are comparing working dir against non-parent
549 # generate a pseudo-manifest for the working dir
549 # generate a pseudo-manifest for the working dir
550 mf2 = mfmatches(self.dirstate.parents()[0])
550 mf2 = mfmatches(self.dirstate.parents()[0])
551 for f in lookup + modified + added:
551 for f in lookup + modified + added:
552 mf2[f] = ""
552 mf2[f] = ""
553 for f in removed:
553 for f in removed:
554 if f in mf2:
554 if f in mf2:
555 del mf2[f]
555 del mf2[f]
556 else:
556 else:
557 # we are comparing two revisions
557 # we are comparing two revisions
558 deleted, unknown = [], []
558 deleted, unknown = [], []
559 mf2 = mfmatches(node2)
559 mf2 = mfmatches(node2)
560
560
561 if node1:
561 if node1:
562 # flush lists from dirstate before comparing manifests
562 # flush lists from dirstate before comparing manifests
563 modified, added = [], []
563 modified, added = [], []
564
564
565 for fn in mf2:
565 for fn in mf2:
566 if mf1.has_key(fn):
566 if mf1.has_key(fn):
567 if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
567 if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
568 modified.append(fn)
568 modified.append(fn)
569 del mf1[fn]
569 del mf1[fn]
570 else:
570 else:
571 added.append(fn)
571 added.append(fn)
572
572
573 removed = mf1.keys()
573 removed = mf1.keys()
574
574
575 # sort and return results:
575 # sort and return results:
576 for l in modified, added, removed, deleted, unknown:
576 for l in modified, added, removed, deleted, unknown:
577 l.sort()
577 l.sort()
578 return (modified, added, removed, deleted, unknown)
578 return (modified, added, removed, deleted, unknown)
579
579
580 def add(self, list, wlock=None):
580 def add(self, list, wlock=None):
581 if not wlock:
581 if not wlock:
582 wlock = self.wlock()
582 wlock = self.wlock()
583 for f in list:
583 for f in list:
584 p = self.wjoin(f)
584 p = self.wjoin(f)
585 if not os.path.exists(p):
585 if not os.path.exists(p):
586 self.ui.warn(_("%s does not exist!\n") % f)
586 self.ui.warn(_("%s does not exist!\n") % f)
587 elif not os.path.isfile(p):
587 elif not os.path.isfile(p):
588 self.ui.warn(_("%s not added: only files supported currently\n")
588 self.ui.warn(_("%s not added: only files supported currently\n")
589 % f)
589 % f)
590 elif self.dirstate.state(f) in 'an':
590 elif self.dirstate.state(f) in 'an':
591 self.ui.warn(_("%s already tracked!\n") % f)
591 self.ui.warn(_("%s already tracked!\n") % f)
592 else:
592 else:
593 self.dirstate.update([f], "a")
593 self.dirstate.update([f], "a")
594
594
595 def forget(self, list, wlock=None):
595 def forget(self, list, wlock=None):
596 if not wlock:
596 if not wlock:
597 wlock = self.wlock()
597 wlock = self.wlock()
598 for f in list:
598 for f in list:
599 if self.dirstate.state(f) not in 'ai':
599 if self.dirstate.state(f) not in 'ai':
600 self.ui.warn(_("%s not added!\n") % f)
600 self.ui.warn(_("%s not added!\n") % f)
601 else:
601 else:
602 self.dirstate.forget([f])
602 self.dirstate.forget([f])
603
603
604 def remove(self, list, unlink=False, wlock=None):
604 def remove(self, list, unlink=False, wlock=None):
605 if unlink:
605 if unlink:
606 for f in list:
606 for f in list:
607 try:
607 try:
608 util.unlink(self.wjoin(f))
608 util.unlink(self.wjoin(f))
609 except OSError, inst:
609 except OSError, inst:
610 if inst.errno != errno.ENOENT:
610 if inst.errno != errno.ENOENT:
611 raise
611 raise
612 if not wlock:
612 if not wlock:
613 wlock = self.wlock()
613 wlock = self.wlock()
614 for f in list:
614 for f in list:
615 p = self.wjoin(f)
615 p = self.wjoin(f)
616 if os.path.exists(p):
616 if os.path.exists(p):
617 self.ui.warn(_("%s still exists!\n") % f)
617 self.ui.warn(_("%s still exists!\n") % f)
618 elif self.dirstate.state(f) == 'a':
618 elif self.dirstate.state(f) == 'a':
619 self.dirstate.forget([f])
619 self.dirstate.forget([f])
620 elif f not in self.dirstate:
620 elif f not in self.dirstate:
621 self.ui.warn(_("%s not tracked!\n") % f)
621 self.ui.warn(_("%s not tracked!\n") % f)
622 else:
622 else:
623 self.dirstate.update([f], "r")
623 self.dirstate.update([f], "r")
624
624
625 def undelete(self, list, wlock=None):
625 def undelete(self, list, wlock=None):
626 p = self.dirstate.parents()[0]
626 p = self.dirstate.parents()[0]
627 mn = self.changelog.read(p)[0]
627 mn = self.changelog.read(p)[0]
628 mf = self.manifest.readflags(mn)
628 mf = self.manifest.readflags(mn)
629 m = self.manifest.read(mn)
629 m = self.manifest.read(mn)
630 if not wlock:
630 if not wlock:
631 wlock = self.wlock()
631 wlock = self.wlock()
632 for f in list:
632 for f in list:
633 if self.dirstate.state(f) not in "r":
633 if self.dirstate.state(f) not in "r":
634 self.ui.warn("%s not removed!\n" % f)
634 self.ui.warn("%s not removed!\n" % f)
635 else:
635 else:
636 t = self.file(f).read(m[f])
636 t = self.file(f).read(m[f])
637 self.wwrite(f, t)
637 self.wwrite(f, t)
638 util.set_exec(self.wjoin(f), mf[f])
638 util.set_exec(self.wjoin(f), mf[f])
639 self.dirstate.update([f], "n")
639 self.dirstate.update([f], "n")
640
640
641 def copy(self, source, dest, wlock=None):
641 def copy(self, source, dest, wlock=None):
642 p = self.wjoin(dest)
642 p = self.wjoin(dest)
643 if not os.path.exists(p):
643 if not os.path.exists(p):
644 self.ui.warn(_("%s does not exist!\n") % dest)
644 self.ui.warn(_("%s does not exist!\n") % dest)
645 elif not os.path.isfile(p):
645 elif not os.path.isfile(p):
646 self.ui.warn(_("copy failed: %s is not a file\n") % dest)
646 self.ui.warn(_("copy failed: %s is not a file\n") % dest)
647 else:
647 else:
648 if not wlock:
648 if not wlock:
649 wlock = self.wlock()
649 wlock = self.wlock()
650 if self.dirstate.state(dest) == '?':
650 if self.dirstate.state(dest) == '?':
651 self.dirstate.update([dest], "a")
651 self.dirstate.update([dest], "a")
652 self.dirstate.copy(source, dest)
652 self.dirstate.copy(source, dest)
653
653
654 def heads(self, start=None):
654 def heads(self, start=None):
655 heads = self.changelog.heads(start)
655 heads = self.changelog.heads(start)
656 # sort the output in rev descending order
656 # sort the output in rev descending order
657 heads = [(-self.changelog.rev(h), h) for h in heads]
657 heads = [(-self.changelog.rev(h), h) for h in heads]
658 heads.sort()
658 heads.sort()
659 return [n for (r, n) in heads]
659 return [n for (r, n) in heads]
660
660
661 # branchlookup returns a dict giving a list of branches for
661 # branchlookup returns a dict giving a list of branches for
662 # each head. A branch is defined as the tag of a node or
662 # each head. A branch is defined as the tag of a node or
663 # the branch of the node's parents. If a node has multiple
663 # the branch of the node's parents. If a node has multiple
664 # branch tags, tags are eliminated if they are visible from other
664 # branch tags, tags are eliminated if they are visible from other
665 # branch tags.
665 # branch tags.
666 #
666 #
667 # So, for this graph: a->b->c->d->e
667 # So, for this graph: a->b->c->d->e
668 # \ /
668 # \ /
669 # aa -----/
669 # aa -----/
670 # a has tag 2.6.12
670 # a has tag 2.6.12
671 # d has tag 2.6.13
671 # d has tag 2.6.13
672 # e would have branch tags for 2.6.12 and 2.6.13. Because the node
672 # e would have branch tags for 2.6.12 and 2.6.13. Because the node
673 # for 2.6.12 can be reached from the node 2.6.13, that is eliminated
673 # for 2.6.12 can be reached from the node 2.6.13, that is eliminated
674 # from the list.
674 # from the list.
675 #
675 #
676 # It is possible that more than one head will have the same branch tag.
676 # It is possible that more than one head will have the same branch tag.
677 # callers need to check the result for multiple heads under the same
677 # callers need to check the result for multiple heads under the same
678 # branch tag if that is a problem for them (ie checkout of a specific
678 # branch tag if that is a problem for them (ie checkout of a specific
679 # branch).
679 # branch).
680 #
680 #
681 # passing in a specific branch will limit the depth of the search
681 # passing in a specific branch will limit the depth of the search
682 # through the parents. It won't limit the branches returned in the
682 # through the parents. It won't limit the branches returned in the
683 # result though.
683 # result though.
684 def branchlookup(self, heads=None, branch=None):
684 def branchlookup(self, heads=None, branch=None):
685 if not heads:
685 if not heads:
686 heads = self.heads()
686 heads = self.heads()
687 headt = [ h for h in heads ]
687 headt = [ h for h in heads ]
688 chlog = self.changelog
688 chlog = self.changelog
689 branches = {}
689 branches = {}
690 merges = []
690 merges = []
691 seenmerge = {}
691 seenmerge = {}
692
692
693 # traverse the tree once for each head, recording in the branches
693 # traverse the tree once for each head, recording in the branches
694 # dict which tags are visible from this head. The branches
694 # dict which tags are visible from this head. The branches
695 # dict also records which tags are visible from each tag
695 # dict also records which tags are visible from each tag
696 # while we traverse.
696 # while we traverse.
697 while headt or merges:
697 while headt or merges:
698 if merges:
698 if merges:
699 n, found = merges.pop()
699 n, found = merges.pop()
700 visit = [n]
700 visit = [n]
701 else:
701 else:
702 h = headt.pop()
702 h = headt.pop()
703 visit = [h]
703 visit = [h]
704 found = [h]
704 found = [h]
705 seen = {}
705 seen = {}
706 while visit:
706 while visit:
707 n = visit.pop()
707 n = visit.pop()
708 if n in seen:
708 if n in seen:
709 continue
709 continue
710 pp = chlog.parents(n)
710 pp = chlog.parents(n)
711 tags = self.nodetags(n)
711 tags = self.nodetags(n)
712 if tags:
712 if tags:
713 for x in tags:
713 for x in tags:
714 if x == 'tip':
714 if x == 'tip':
715 continue
715 continue
716 for f in found:
716 for f in found:
717 branches.setdefault(f, {})[n] = 1
717 branches.setdefault(f, {})[n] = 1
718 branches.setdefault(n, {})[n] = 1
718 branches.setdefault(n, {})[n] = 1
719 break
719 break
720 if n not in found:
720 if n not in found:
721 found.append(n)
721 found.append(n)
722 if branch in tags:
722 if branch in tags:
723 continue
723 continue
724 seen[n] = 1
724 seen[n] = 1
725 if pp[1] != nullid and n not in seenmerge:
725 if pp[1] != nullid and n not in seenmerge:
726 merges.append((pp[1], [x for x in found]))
726 merges.append((pp[1], [x for x in found]))
727 seenmerge[n] = 1
727 seenmerge[n] = 1
728 if pp[0] != nullid:
728 if pp[0] != nullid:
729 visit.append(pp[0])
729 visit.append(pp[0])
730 # traverse the branches dict, eliminating branch tags from each
730 # traverse the branches dict, eliminating branch tags from each
731 # head that are visible from another branch tag for that head.
731 # head that are visible from another branch tag for that head.
732 out = {}
732 out = {}
733 viscache = {}
733 viscache = {}
734 for h in heads:
734 for h in heads:
735 def visible(node):
735 def visible(node):
736 if node in viscache:
736 if node in viscache:
737 return viscache[node]
737 return viscache[node]
738 ret = {}
738 ret = {}
739 visit = [node]
739 visit = [node]
740 while visit:
740 while visit:
741 x = visit.pop()
741 x = visit.pop()
742 if x in viscache:
742 if x in viscache:
743 ret.update(viscache[x])
743 ret.update(viscache[x])
744 elif x not in ret:
744 elif x not in ret:
745 ret[x] = 1
745 ret[x] = 1
746 if x in branches:
746 if x in branches:
747 visit[len(visit):] = branches[x].keys()
747 visit[len(visit):] = branches[x].keys()
748 viscache[node] = ret
748 viscache[node] = ret
749 return ret
749 return ret
750 if h not in branches:
750 if h not in branches:
751 continue
751 continue
752 # O(n^2), but somewhat limited. This only searches the
752 # O(n^2), but somewhat limited. This only searches the
753 # tags visible from a specific head, not all the tags in the
753 # tags visible from a specific head, not all the tags in the
754 # whole repo.
754 # whole repo.
755 for b in branches[h]:
755 for b in branches[h]:
756 vis = False
756 vis = False
757 for bb in branches[h].keys():
757 for bb in branches[h].keys():
758 if b != bb:
758 if b != bb:
759 if b in visible(bb):
759 if b in visible(bb):
760 vis = True
760 vis = True
761 break
761 break
762 if not vis:
762 if not vis:
763 l = out.setdefault(h, [])
763 l = out.setdefault(h, [])
764 l[len(l):] = self.nodetags(b)
764 l[len(l):] = self.nodetags(b)
765 return out
765 return out
766
766
767 def branches(self, nodes):
767 def branches(self, nodes):
768 if not nodes:
768 if not nodes:
769 nodes = [self.changelog.tip()]
769 nodes = [self.changelog.tip()]
770 b = []
770 b = []
771 for n in nodes:
771 for n in nodes:
772 t = n
772 t = n
773 while n:
773 while n:
774 p = self.changelog.parents(n)
774 p = self.changelog.parents(n)
775 if p[1] != nullid or p[0] == nullid:
775 if p[1] != nullid or p[0] == nullid:
776 b.append((t, n, p[0], p[1]))
776 b.append((t, n, p[0], p[1]))
777 break
777 break
778 n = p[0]
778 n = p[0]
779 return b
779 return b
780
780
781 def between(self, pairs):
781 def between(self, pairs):
782 r = []
782 r = []
783
783
784 for top, bottom in pairs:
784 for top, bottom in pairs:
785 n, l, i = top, [], 0
785 n, l, i = top, [], 0
786 f = 1
786 f = 1
787
787
788 while n != bottom:
788 while n != bottom:
789 p = self.changelog.parents(n)[0]
789 p = self.changelog.parents(n)[0]
790 if i == f:
790 if i == f:
791 l.append(n)
791 l.append(n)
792 f = f * 2
792 f = f * 2
793 n = p
793 n = p
794 i += 1
794 i += 1
795
795
796 r.append(l)
796 r.append(l)
797
797
798 return r
798 return r
799
799
800 def findincoming(self, remote, base=None, heads=None, force=False):
800 def findincoming(self, remote, base=None, heads=None, force=False):
801 m = self.changelog.nodemap
801 m = self.changelog.nodemap
802 search = []
802 search = []
803 fetch = {}
803 fetch = {}
804 seen = {}
804 seen = {}
805 seenbranch = {}
805 seenbranch = {}
806 if base == None:
806 if base == None:
807 base = {}
807 base = {}
808
808
809 # assume we're closer to the tip than the root
809 # assume we're closer to the tip than the root
810 # and start by examining the heads
810 # and start by examining the heads
811 self.ui.status(_("searching for changes\n"))
811 self.ui.status(_("searching for changes\n"))
812
812
813 if not heads:
813 if not heads:
814 heads = remote.heads()
814 heads = remote.heads()
815
815
816 unknown = []
816 unknown = []
817 for h in heads:
817 for h in heads:
818 if h not in m:
818 if h not in m:
819 unknown.append(h)
819 unknown.append(h)
820 else:
820 else:
821 base[h] = 1
821 base[h] = 1
822
822
823 if not unknown:
823 if not unknown:
824 return []
824 return []
825
825
826 rep = {}
826 rep = {}
827 reqcnt = 0
827 reqcnt = 0
828
828
829 # search through remote branches
829 # search through remote branches
830 # a 'branch' here is a linear segment of history, with four parts:
830 # a 'branch' here is a linear segment of history, with four parts:
831 # head, root, first parent, second parent
831 # head, root, first parent, second parent
832 # (a branch always has two parents (or none) by definition)
832 # (a branch always has two parents (or none) by definition)
833 unknown = remote.branches(unknown)
833 unknown = remote.branches(unknown)
834 while unknown:
834 while unknown:
835 r = []
835 r = []
836 while unknown:
836 while unknown:
837 n = unknown.pop(0)
837 n = unknown.pop(0)
838 if n[0] in seen:
838 if n[0] in seen:
839 continue
839 continue
840
840
841 self.ui.debug(_("examining %s:%s\n")
841 self.ui.debug(_("examining %s:%s\n")
842 % (short(n[0]), short(n[1])))
842 % (short(n[0]), short(n[1])))
843 if n[0] == nullid:
843 if n[0] == nullid:
844 break
844 break
845 if n in seenbranch:
845 if n in seenbranch:
846 self.ui.debug(_("branch already found\n"))
846 self.ui.debug(_("branch already found\n"))
847 continue
847 continue
848 if n[1] and n[1] in m: # do we know the base?
848 if n[1] and n[1] in m: # do we know the base?
849 self.ui.debug(_("found incomplete branch %s:%s\n")
849 self.ui.debug(_("found incomplete branch %s:%s\n")
850 % (short(n[0]), short(n[1])))
850 % (short(n[0]), short(n[1])))
851 search.append(n) # schedule branch range for scanning
851 search.append(n) # schedule branch range for scanning
852 seenbranch[n] = 1
852 seenbranch[n] = 1
853 else:
853 else:
854 if n[1] not in seen and n[1] not in fetch:
854 if n[1] not in seen and n[1] not in fetch:
855 if n[2] in m and n[3] in m:
855 if n[2] in m and n[3] in m:
856 self.ui.debug(_("found new changeset %s\n") %
856 self.ui.debug(_("found new changeset %s\n") %
857 short(n[1]))
857 short(n[1]))
858 fetch[n[1]] = 1 # earliest unknown
858 fetch[n[1]] = 1 # earliest unknown
859 base[n[2]] = 1 # latest known
859 base[n[2]] = 1 # latest known
860 continue
860 continue
861
861
862 for a in n[2:4]:
862 for a in n[2:4]:
863 if a not in rep:
863 if a not in rep:
864 r.append(a)
864 r.append(a)
865 rep[a] = 1
865 rep[a] = 1
866
866
867 seen[n[0]] = 1
867 seen[n[0]] = 1
868
868
869 if r:
869 if r:
870 reqcnt += 1
870 reqcnt += 1
871 self.ui.debug(_("request %d: %s\n") %
871 self.ui.debug(_("request %d: %s\n") %
872 (reqcnt, " ".join(map(short, r))))
872 (reqcnt, " ".join(map(short, r))))
873 for p in range(0, len(r), 10):
873 for p in range(0, len(r), 10):
874 for b in remote.branches(r[p:p+10]):
874 for b in remote.branches(r[p:p+10]):
875 self.ui.debug(_("received %s:%s\n") %
875 self.ui.debug(_("received %s:%s\n") %
876 (short(b[0]), short(b[1])))
876 (short(b[0]), short(b[1])))
877 if b[0] in m:
877 if b[0] in m:
878 self.ui.debug(_("found base node %s\n")
878 self.ui.debug(_("found base node %s\n")
879 % short(b[0]))
879 % short(b[0]))
880 base[b[0]] = 1
880 base[b[0]] = 1
881 elif b[0] not in seen:
881 elif b[0] not in seen:
882 unknown.append(b)
882 unknown.append(b)
883
883
884 # do binary search on the branches we found
884 # do binary search on the branches we found
885 while search:
885 while search:
886 n = search.pop(0)
886 n = search.pop(0)
887 reqcnt += 1
887 reqcnt += 1
888 l = remote.between([(n[0], n[1])])[0]
888 l = remote.between([(n[0], n[1])])[0]
889 l.append(n[1])
889 l.append(n[1])
890 p = n[0]
890 p = n[0]
891 f = 1
891 f = 1
892 for i in l:
892 for i in l:
893 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
893 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
894 if i in m:
894 if i in m:
895 if f <= 2:
895 if f <= 2:
896 self.ui.debug(_("found new branch changeset %s\n") %
896 self.ui.debug(_("found new branch changeset %s\n") %
897 short(p))
897 short(p))
898 fetch[p] = 1
898 fetch[p] = 1
899 base[i] = 1
899 base[i] = 1
900 else:
900 else:
901 self.ui.debug(_("narrowed branch search to %s:%s\n")
901 self.ui.debug(_("narrowed branch search to %s:%s\n")
902 % (short(p), short(i)))
902 % (short(p), short(i)))
903 search.append((p, i))
903 search.append((p, i))
904 break
904 break
905 p, f = i, f * 2
905 p, f = i, f * 2
906
906
907 # sanity check our fetch list
907 # sanity check our fetch list
908 for f in fetch.keys():
908 for f in fetch.keys():
909 if f in m:
909 if f in m:
910 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
910 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
911
911
912 if base.keys() == [nullid]:
912 if base.keys() == [nullid]:
913 if force:
913 if force:
914 self.ui.warn(_("warning: repository is unrelated\n"))
914 self.ui.warn(_("warning: repository is unrelated\n"))
915 else:
915 else:
916 raise util.Abort(_("repository is unrelated"))
916 raise util.Abort(_("repository is unrelated"))
917
917
918 self.ui.note(_("found new changesets starting at ") +
918 self.ui.note(_("found new changesets starting at ") +
919 " ".join([short(f) for f in fetch]) + "\n")
919 " ".join([short(f) for f in fetch]) + "\n")
920
920
921 self.ui.debug(_("%d total queries\n") % reqcnt)
921 self.ui.debug(_("%d total queries\n") % reqcnt)
922
922
923 return fetch.keys()
923 return fetch.keys()
924
924
925 def findoutgoing(self, remote, base=None, heads=None, force=False):
925 def findoutgoing(self, remote, base=None, heads=None, force=False):
926 if base == None:
926 if base == None:
927 base = {}
927 base = {}
928 self.findincoming(remote, base, heads, force=force)
928 self.findincoming(remote, base, heads, force=force)
929
929
930 self.ui.debug(_("common changesets up to ")
930 self.ui.debug(_("common changesets up to ")
931 + " ".join(map(short, base.keys())) + "\n")
931 + " ".join(map(short, base.keys())) + "\n")
932
932
933 remain = dict.fromkeys(self.changelog.nodemap)
933 remain = dict.fromkeys(self.changelog.nodemap)
934
934
935 # prune everything remote has from the tree
935 # prune everything remote has from the tree
936 del remain[nullid]
936 del remain[nullid]
937 remove = base.keys()
937 remove = base.keys()
938 while remove:
938 while remove:
939 n = remove.pop(0)
939 n = remove.pop(0)
940 if n in remain:
940 if n in remain:
941 del remain[n]
941 del remain[n]
942 for p in self.changelog.parents(n):
942 for p in self.changelog.parents(n):
943 remove.append(p)
943 remove.append(p)
944
944
945 # find every node whose parents have been pruned
945 # find every node whose parents have been pruned
946 subset = []
946 subset = []
947 for n in remain:
947 for n in remain:
948 p1, p2 = self.changelog.parents(n)
948 p1, p2 = self.changelog.parents(n)
949 if p1 not in remain and p2 not in remain:
949 if p1 not in remain and p2 not in remain:
950 subset.append(n)
950 subset.append(n)
951
951
952 # this is the set of all roots we have to push
952 # this is the set of all roots we have to push
953 return subset
953 return subset
954
954
955 def pull(self, remote, heads=None, force=False):
955 def pull(self, remote, heads=None, force=False):
956 l = self.lock()
956 l = self.lock()
957
957
958 # if we have an empty repo, fetch everything
958 # if we have an empty repo, fetch everything
959 if self.changelog.tip() == nullid:
959 if self.changelog.tip() == nullid:
960 self.ui.status(_("requesting all changes\n"))
960 self.ui.status(_("requesting all changes\n"))
961 fetch = [nullid]
961 fetch = [nullid]
962 else:
962 else:
963 fetch = self.findincoming(remote, force=force)
963 fetch = self.findincoming(remote, force=force)
964
964
965 if not fetch:
965 if not fetch:
966 self.ui.status(_("no changes found\n"))
966 self.ui.status(_("no changes found\n"))
967 return 1
967 return 1
968
968
969 if heads is None:
969 if heads is None:
970 cg = remote.changegroup(fetch, 'pull')
970 cg = remote.changegroup(fetch, 'pull')
971 else:
971 else:
972 cg = remote.changegroupsubset(fetch, heads, 'pull')
972 cg = remote.changegroupsubset(fetch, heads, 'pull')
973 return self.addchangegroup(cg)
973 return self.addchangegroup(cg)
974
974
975 def push(self, remote, force=False, revs=None):
975 def push(self, remote, force=False, revs=None):
976 lock = remote.lock()
976 lock = remote.lock()
977
977
978 base = {}
978 base = {}
979 heads = remote.heads()
979 heads = remote.heads()
980 inc = self.findincoming(remote, base, heads, force=force)
980 inc = self.findincoming(remote, base, heads, force=force)
981 if not force and inc:
981 if not force and inc:
982 self.ui.warn(_("abort: unsynced remote changes!\n"))
982 self.ui.warn(_("abort: unsynced remote changes!\n"))
983 self.ui.status(_("(did you forget to sync? use push -f to force)\n"))
983 self.ui.status(_("(did you forget to sync? use push -f to force)\n"))
984 return 1
984 return 1
985
985
986 update = self.findoutgoing(remote, base)
986 update = self.findoutgoing(remote, base)
987 if revs is not None:
987 if revs is not None:
988 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
988 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
989 else:
989 else:
990 bases, heads = update, self.changelog.heads()
990 bases, heads = update, self.changelog.heads()
991
991
992 if not bases:
992 if not bases:
993 self.ui.status(_("no changes found\n"))
993 self.ui.status(_("no changes found\n"))
994 return 1
994 return 1
995 elif not force:
995 elif not force:
996 if len(bases) < len(heads):
996 if len(bases) < len(heads):
997 self.ui.warn(_("abort: push creates new remote branches!\n"))
997 self.ui.warn(_("abort: push creates new remote branches!\n"))
998 self.ui.status(_("(did you forget to merge?"
998 self.ui.status(_("(did you forget to merge?"
999 " use push -f to force)\n"))
999 " use push -f to force)\n"))
1000 return 1
1000 return 1
1001
1001
1002 if revs is None:
1002 if revs is None:
1003 cg = self.changegroup(update, 'push')
1003 cg = self.changegroup(update, 'push')
1004 else:
1004 else:
1005 cg = self.changegroupsubset(update, revs, 'push')
1005 cg = self.changegroupsubset(update, revs, 'push')
1006 return remote.addchangegroup(cg)
1006 return remote.addchangegroup(cg)
1007
1007
1008 def changegroupsubset(self, bases, heads, source):
1008 def changegroupsubset(self, bases, heads, source):
1009 """This function generates a changegroup consisting of all the nodes
1009 """This function generates a changegroup consisting of all the nodes
1010 that are descendents of any of the bases, and ancestors of any of
1010 that are descendents of any of the bases, and ancestors of any of
1011 the heads.
1011 the heads.
1012
1012
1013 It is fairly complex as determining which filenodes and which
1013 It is fairly complex as determining which filenodes and which
1014 manifest nodes need to be included for the changeset to be complete
1014 manifest nodes need to be included for the changeset to be complete
1015 is non-trivial.
1015 is non-trivial.
1016
1016
1017 Another wrinkle is doing the reverse, figuring out which changeset in
1017 Another wrinkle is doing the reverse, figuring out which changeset in
1018 the changegroup a particular filenode or manifestnode belongs to."""
1018 the changegroup a particular filenode or manifestnode belongs to."""
1019
1019
1020 self.hook('preoutgoing', throw=True, source=source)
1020 self.hook('preoutgoing', throw=True, source=source)
1021
1021
1022 # Set up some initial variables
1022 # Set up some initial variables
1023 # Make it easy to refer to self.changelog
1023 # Make it easy to refer to self.changelog
1024 cl = self.changelog
1024 cl = self.changelog
1025 # msng is short for missing - compute the list of changesets in this
1025 # msng is short for missing - compute the list of changesets in this
1026 # changegroup.
1026 # changegroup.
1027 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1027 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1028 # Some bases may turn out to be superfluous, and some heads may be
1028 # Some bases may turn out to be superfluous, and some heads may be
1029 # too. nodesbetween will return the minimal set of bases and heads
1029 # too. nodesbetween will return the minimal set of bases and heads
1030 # necessary to re-create the changegroup.
1030 # necessary to re-create the changegroup.
1031
1031
1032 # Known heads are the list of heads that it is assumed the recipient
1032 # Known heads are the list of heads that it is assumed the recipient
1033 # of this changegroup will know about.
1033 # of this changegroup will know about.
1034 knownheads = {}
1034 knownheads = {}
1035 # We assume that all parents of bases are known heads.
1035 # We assume that all parents of bases are known heads.
1036 for n in bases:
1036 for n in bases:
1037 for p in cl.parents(n):
1037 for p in cl.parents(n):
1038 if p != nullid:
1038 if p != nullid:
1039 knownheads[p] = 1
1039 knownheads[p] = 1
1040 knownheads = knownheads.keys()
1040 knownheads = knownheads.keys()
1041 if knownheads:
1041 if knownheads:
1042 # Now that we know what heads are known, we can compute which
1042 # Now that we know what heads are known, we can compute which
1043 # changesets are known. The recipient must know about all
1043 # changesets are known. The recipient must know about all
1044 # changesets required to reach the known heads from the null
1044 # changesets required to reach the known heads from the null
1045 # changeset.
1045 # changeset.
1046 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1046 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1047 junk = None
1047 junk = None
1048 # Transform the list into an ersatz set.
1048 # Transform the list into an ersatz set.
1049 has_cl_set = dict.fromkeys(has_cl_set)
1049 has_cl_set = dict.fromkeys(has_cl_set)
1050 else:
1050 else:
1051 # If there were no known heads, the recipient cannot be assumed to
1051 # If there were no known heads, the recipient cannot be assumed to
1052 # know about any changesets.
1052 # know about any changesets.
1053 has_cl_set = {}
1053 has_cl_set = {}
1054
1054
1055 # Make it easy to refer to self.manifest
1055 # Make it easy to refer to self.manifest
1056 mnfst = self.manifest
1056 mnfst = self.manifest
1057 # We don't know which manifests are missing yet
1057 # We don't know which manifests are missing yet
1058 msng_mnfst_set = {}
1058 msng_mnfst_set = {}
1059 # Nor do we know which filenodes are missing.
1059 # Nor do we know which filenodes are missing.
1060 msng_filenode_set = {}
1060 msng_filenode_set = {}
1061
1061
1062 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1062 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1063 junk = None
1063 junk = None
1064
1064
1065 # A changeset always belongs to itself, so the changenode lookup
1065 # A changeset always belongs to itself, so the changenode lookup
1066 # function for a changenode is identity.
1066 # function for a changenode is identity.
1067 def identity(x):
1067 def identity(x):
1068 return x
1068 return x
1069
1069
1070 # A function generating function. Sets up an environment for the
1070 # A function generating function. Sets up an environment for the
1071 # inner function.
1071 # inner function.
1072 def cmp_by_rev_func(revlog):
1072 def cmp_by_rev_func(revlog):
1073 # Compare two nodes by their revision number in the environment's
1073 # Compare two nodes by their revision number in the environment's
1074 # revision history. Since the revision number both represents the
1074 # revision history. Since the revision number both represents the
1075 # most efficient order to read the nodes in, and represents a
1075 # most efficient order to read the nodes in, and represents a
1076 # topological sorting of the nodes, this function is often useful.
1076 # topological sorting of the nodes, this function is often useful.
1077 def cmp_by_rev(a, b):
1077 def cmp_by_rev(a, b):
1078 return cmp(revlog.rev(a), revlog.rev(b))
1078 return cmp(revlog.rev(a), revlog.rev(b))
1079 return cmp_by_rev
1079 return cmp_by_rev
1080
1080
1081 # If we determine that a particular file or manifest node must be a
1081 # If we determine that a particular file or manifest node must be a
1082 # node that the recipient of the changegroup will already have, we can
1082 # node that the recipient of the changegroup will already have, we can
1083 # also assume the recipient will have all the parents. This function
1083 # also assume the recipient will have all the parents. This function
1084 # prunes them from the set of missing nodes.
1084 # prunes them from the set of missing nodes.
1085 def prune_parents(revlog, hasset, msngset):
1085 def prune_parents(revlog, hasset, msngset):
1086 haslst = hasset.keys()
1086 haslst = hasset.keys()
1087 haslst.sort(cmp_by_rev_func(revlog))
1087 haslst.sort(cmp_by_rev_func(revlog))
1088 for node in haslst:
1088 for node in haslst:
1089 parentlst = [p for p in revlog.parents(node) if p != nullid]
1089 parentlst = [p for p in revlog.parents(node) if p != nullid]
1090 while parentlst:
1090 while parentlst:
1091 n = parentlst.pop()
1091 n = parentlst.pop()
1092 if n not in hasset:
1092 if n not in hasset:
1093 hasset[n] = 1
1093 hasset[n] = 1
1094 p = [p for p in revlog.parents(n) if p != nullid]
1094 p = [p for p in revlog.parents(n) if p != nullid]
1095 parentlst.extend(p)
1095 parentlst.extend(p)
1096 for n in hasset:
1096 for n in hasset:
1097 msngset.pop(n, None)
1097 msngset.pop(n, None)
1098
1098
1099 # This is a function generating function used to set up an environment
1099 # This is a function generating function used to set up an environment
1100 # for the inner function to execute in.
1100 # for the inner function to execute in.
1101 def manifest_and_file_collector(changedfileset):
1101 def manifest_and_file_collector(changedfileset):
1102 # This is an information gathering function that gathers
1102 # This is an information gathering function that gathers
1103 # information from each changeset node that goes out as part of
1103 # information from each changeset node that goes out as part of
1104 # the changegroup. The information gathered is a list of which
1104 # the changegroup. The information gathered is a list of which
1105 # manifest nodes are potentially required (the recipient may
1105 # manifest nodes are potentially required (the recipient may
1106 # already have them) and total list of all files which were
1106 # already have them) and total list of all files which were
1107 # changed in any changeset in the changegroup.
1107 # changed in any changeset in the changegroup.
1108 #
1108 #
1109 # We also remember the first changenode we saw any manifest
1109 # We also remember the first changenode we saw any manifest
1110 # referenced by so we can later determine which changenode 'owns'
1110 # referenced by so we can later determine which changenode 'owns'
1111 # the manifest.
1111 # the manifest.
1112 def collect_manifests_and_files(clnode):
1112 def collect_manifests_and_files(clnode):
1113 c = cl.read(clnode)
1113 c = cl.read(clnode)
1114 for f in c[3]:
1114 for f in c[3]:
1115 # This is to make sure we only have one instance of each
1115 # This is to make sure we only have one instance of each
1116 # filename string for each filename.
1116 # filename string for each filename.
1117 changedfileset.setdefault(f, f)
1117 changedfileset.setdefault(f, f)
1118 msng_mnfst_set.setdefault(c[0], clnode)
1118 msng_mnfst_set.setdefault(c[0], clnode)
1119 return collect_manifests_and_files
1119 return collect_manifests_and_files
1120
1120
1121 # Figure out which manifest nodes (of the ones we think might be part
1121 # Figure out which manifest nodes (of the ones we think might be part
1122 # of the changegroup) the recipient must know about and remove them
1122 # of the changegroup) the recipient must know about and remove them
1123 # from the changegroup.
1123 # from the changegroup.
1124 def prune_manifests():
1124 def prune_manifests():
1125 has_mnfst_set = {}
1125 has_mnfst_set = {}
1126 for n in msng_mnfst_set:
1126 for n in msng_mnfst_set:
1127 # If a 'missing' manifest thinks it belongs to a changenode
1127 # If a 'missing' manifest thinks it belongs to a changenode
1128 # the recipient is assumed to have, obviously the recipient
1128 # the recipient is assumed to have, obviously the recipient
1129 # must have that manifest.
1129 # must have that manifest.
1130 linknode = cl.node(mnfst.linkrev(n))
1130 linknode = cl.node(mnfst.linkrev(n))
1131 if linknode in has_cl_set:
1131 if linknode in has_cl_set:
1132 has_mnfst_set[n] = 1
1132 has_mnfst_set[n] = 1
1133 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1133 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1134
1134
1135 # Use the information collected in collect_manifests_and_files to say
1135 # Use the information collected in collect_manifests_and_files to say
1136 # which changenode any manifestnode belongs to.
1136 # which changenode any manifestnode belongs to.
1137 def lookup_manifest_link(mnfstnode):
1137 def lookup_manifest_link(mnfstnode):
1138 return msng_mnfst_set[mnfstnode]
1138 return msng_mnfst_set[mnfstnode]
1139
1139
1140 # A function generating function that sets up the initial environment
1140 # A function generating function that sets up the initial environment
1141 # the inner function.
1141 # the inner function.
1142 def filenode_collector(changedfiles):
1142 def filenode_collector(changedfiles):
1143 next_rev = [0]
1143 next_rev = [0]
1144 # This gathers information from each manifestnode included in the
1144 # This gathers information from each manifestnode included in the
1145 # changegroup about which filenodes the manifest node references
1145 # changegroup about which filenodes the manifest node references
1146 # so we can include those in the changegroup too.
1146 # so we can include those in the changegroup too.
1147 #
1147 #
1148 # It also remembers which changenode each filenode belongs to. It
1148 # It also remembers which changenode each filenode belongs to. It
1149 # does this by assuming the a filenode belongs to the changenode
1149 # does this by assuming the a filenode belongs to the changenode
1150 # the first manifest that references it belongs to.
1150 # the first manifest that references it belongs to.
1151 def collect_msng_filenodes(mnfstnode):
1151 def collect_msng_filenodes(mnfstnode):
1152 r = mnfst.rev(mnfstnode)
1152 r = mnfst.rev(mnfstnode)
1153 if r == next_rev[0]:
1153 if r == next_rev[0]:
1154 # If the last rev we looked at was the one just previous,
1154 # If the last rev we looked at was the one just previous,
1155 # we only need to see a diff.
1155 # we only need to see a diff.
1156 delta = mdiff.patchtext(mnfst.delta(mnfstnode))
1156 delta = mdiff.patchtext(mnfst.delta(mnfstnode))
1157 # For each line in the delta
1157 # For each line in the delta
1158 for dline in delta.splitlines():
1158 for dline in delta.splitlines():
1159 # get the filename and filenode for that line
1159 # get the filename and filenode for that line
1160 f, fnode = dline.split('\0')
1160 f, fnode = dline.split('\0')
1161 fnode = bin(fnode[:40])
1161 fnode = bin(fnode[:40])
1162 f = changedfiles.get(f, None)
1162 f = changedfiles.get(f, None)
1163 # And if the file is in the list of files we care
1163 # And if the file is in the list of files we care
1164 # about.
1164 # about.
1165 if f is not None:
1165 if f is not None:
1166 # Get the changenode this manifest belongs to
1166 # Get the changenode this manifest belongs to
1167 clnode = msng_mnfst_set[mnfstnode]
1167 clnode = msng_mnfst_set[mnfstnode]
1168 # Create the set of filenodes for the file if
1168 # Create the set of filenodes for the file if
1169 # there isn't one already.
1169 # there isn't one already.
1170 ndset = msng_filenode_set.setdefault(f, {})
1170 ndset = msng_filenode_set.setdefault(f, {})
1171 # And set the filenode's changelog node to the
1171 # And set the filenode's changelog node to the
1172 # manifest's if it hasn't been set already.
1172 # manifest's if it hasn't been set already.
1173 ndset.setdefault(fnode, clnode)
1173 ndset.setdefault(fnode, clnode)
1174 else:
1174 else:
1175 # Otherwise we need a full manifest.
1175 # Otherwise we need a full manifest.
1176 m = mnfst.read(mnfstnode)
1176 m = mnfst.read(mnfstnode)
1177 # For every file in we care about.
1177 # For every file in we care about.
1178 for f in changedfiles:
1178 for f in changedfiles:
1179 fnode = m.get(f, None)
1179 fnode = m.get(f, None)
1180 # If it's in the manifest
1180 # If it's in the manifest
1181 if fnode is not None:
1181 if fnode is not None:
1182 # See comments above.
1182 # See comments above.
1183 clnode = msng_mnfst_set[mnfstnode]
1183 clnode = msng_mnfst_set[mnfstnode]
1184 ndset = msng_filenode_set.setdefault(f, {})
1184 ndset = msng_filenode_set.setdefault(f, {})
1185 ndset.setdefault(fnode, clnode)
1185 ndset.setdefault(fnode, clnode)
1186 # Remember the revision we hope to see next.
1186 # Remember the revision we hope to see next.
1187 next_rev[0] = r + 1
1187 next_rev[0] = r + 1
1188 return collect_msng_filenodes
1188 return collect_msng_filenodes
1189
1189
1190 # We have a list of filenodes we think we need for a file, lets remove
1190 # We have a list of filenodes we think we need for a file, lets remove
1191 # all those we now the recipient must have.
1191 # all those we now the recipient must have.
1192 def prune_filenodes(f, filerevlog):
1192 def prune_filenodes(f, filerevlog):
1193 msngset = msng_filenode_set[f]
1193 msngset = msng_filenode_set[f]
1194 hasset = {}
1194 hasset = {}
1195 # If a 'missing' filenode thinks it belongs to a changenode we
1195 # If a 'missing' filenode thinks it belongs to a changenode we
1196 # assume the recipient must have, then the recipient must have
1196 # assume the recipient must have, then the recipient must have
1197 # that filenode.
1197 # that filenode.
1198 for n in msngset:
1198 for n in msngset:
1199 clnode = cl.node(filerevlog.linkrev(n))
1199 clnode = cl.node(filerevlog.linkrev(n))
1200 if clnode in has_cl_set:
1200 if clnode in has_cl_set:
1201 hasset[n] = 1
1201 hasset[n] = 1
1202 prune_parents(filerevlog, hasset, msngset)
1202 prune_parents(filerevlog, hasset, msngset)
1203
1203
1204 # A function generator function that sets up the a context for the
1204 # A function generator function that sets up the a context for the
1205 # inner function.
1205 # inner function.
1206 def lookup_filenode_link_func(fname):
1206 def lookup_filenode_link_func(fname):
1207 msngset = msng_filenode_set[fname]
1207 msngset = msng_filenode_set[fname]
1208 # Lookup the changenode the filenode belongs to.
1208 # Lookup the changenode the filenode belongs to.
1209 def lookup_filenode_link(fnode):
1209 def lookup_filenode_link(fnode):
1210 return msngset[fnode]
1210 return msngset[fnode]
1211 return lookup_filenode_link
1211 return lookup_filenode_link
1212
1212
1213 # Now that we have all theses utility functions to help out and
1213 # Now that we have all theses utility functions to help out and
1214 # logically divide up the task, generate the group.
1214 # logically divide up the task, generate the group.
1215 def gengroup():
1215 def gengroup():
1216 # The set of changed files starts empty.
1216 # The set of changed files starts empty.
1217 changedfiles = {}
1217 changedfiles = {}
1218 # Create a changenode group generator that will call our functions
1218 # Create a changenode group generator that will call our functions
1219 # back to lookup the owning changenode and collect information.
1219 # back to lookup the owning changenode and collect information.
1220 group = cl.group(msng_cl_lst, identity,
1220 group = cl.group(msng_cl_lst, identity,
1221 manifest_and_file_collector(changedfiles))
1221 manifest_and_file_collector(changedfiles))
1222 for chnk in group:
1222 for chnk in group:
1223 yield chnk
1223 yield chnk
1224
1224
1225 # The list of manifests has been collected by the generator
1225 # The list of manifests has been collected by the generator
1226 # calling our functions back.
1226 # calling our functions back.
1227 prune_manifests()
1227 prune_manifests()
1228 msng_mnfst_lst = msng_mnfst_set.keys()
1228 msng_mnfst_lst = msng_mnfst_set.keys()
1229 # Sort the manifestnodes by revision number.
1229 # Sort the manifestnodes by revision number.
1230 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1230 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1231 # Create a generator for the manifestnodes that calls our lookup
1231 # Create a generator for the manifestnodes that calls our lookup
1232 # and data collection functions back.
1232 # and data collection functions back.
1233 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1233 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1234 filenode_collector(changedfiles))
1234 filenode_collector(changedfiles))
1235 for chnk in group:
1235 for chnk in group:
1236 yield chnk
1236 yield chnk
1237
1237
1238 # These are no longer needed, dereference and toss the memory for
1238 # These are no longer needed, dereference and toss the memory for
1239 # them.
1239 # them.
1240 msng_mnfst_lst = None
1240 msng_mnfst_lst = None
1241 msng_mnfst_set.clear()
1241 msng_mnfst_set.clear()
1242
1242
1243 changedfiles = changedfiles.keys()
1243 changedfiles = changedfiles.keys()
1244 changedfiles.sort()
1244 changedfiles.sort()
1245 # Go through all our files in order sorted by name.
1245 # Go through all our files in order sorted by name.
1246 for fname in changedfiles:
1246 for fname in changedfiles:
1247 filerevlog = self.file(fname)
1247 filerevlog = self.file(fname)
1248 # Toss out the filenodes that the recipient isn't really
1248 # Toss out the filenodes that the recipient isn't really
1249 # missing.
1249 # missing.
1250 if msng_filenode_set.has_key(fname):
1250 if msng_filenode_set.has_key(fname):
1251 prune_filenodes(fname, filerevlog)
1251 prune_filenodes(fname, filerevlog)
1252 msng_filenode_lst = msng_filenode_set[fname].keys()
1252 msng_filenode_lst = msng_filenode_set[fname].keys()
1253 else:
1253 else:
1254 msng_filenode_lst = []
1254 msng_filenode_lst = []
1255 # If any filenodes are left, generate the group for them,
1255 # If any filenodes are left, generate the group for them,
1256 # otherwise don't bother.
1256 # otherwise don't bother.
1257 if len(msng_filenode_lst) > 0:
1257 if len(msng_filenode_lst) > 0:
1258 yield changegroup.genchunk(fname)
1258 yield changegroup.genchunk(fname)
1259 # Sort the filenodes by their revision #
1259 # Sort the filenodes by their revision #
1260 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1260 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1261 # Create a group generator and only pass in a changenode
1261 # Create a group generator and only pass in a changenode
1262 # lookup function as we need to collect no information
1262 # lookup function as we need to collect no information
1263 # from filenodes.
1263 # from filenodes.
1264 group = filerevlog.group(msng_filenode_lst,
1264 group = filerevlog.group(msng_filenode_lst,
1265 lookup_filenode_link_func(fname))
1265 lookup_filenode_link_func(fname))
1266 for chnk in group:
1266 for chnk in group:
1267 yield chnk
1267 yield chnk
1268 if msng_filenode_set.has_key(fname):
1268 if msng_filenode_set.has_key(fname):
1269 # Don't need this anymore, toss it to free memory.
1269 # Don't need this anymore, toss it to free memory.
1270 del msng_filenode_set[fname]
1270 del msng_filenode_set[fname]
1271 # Signal that no more groups are left.
1271 # Signal that no more groups are left.
1272 yield changegroup.closechunk()
1272 yield changegroup.closechunk()
1273
1273
1274 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1274 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1275
1275
1276 return util.chunkbuffer(gengroup())
1276 return util.chunkbuffer(gengroup())
1277
1277
1278 def changegroup(self, basenodes, source):
1278 def changegroup(self, basenodes, source):
1279 """Generate a changegroup of all nodes that we have that a recipient
1279 """Generate a changegroup of all nodes that we have that a recipient
1280 doesn't.
1280 doesn't.
1281
1281
1282 This is much easier than the previous function as we can assume that
1282 This is much easier than the previous function as we can assume that
1283 the recipient has any changenode we aren't sending them."""
1283 the recipient has any changenode we aren't sending them."""
1284
1284
1285 self.hook('preoutgoing', throw=True, source=source)
1285 self.hook('preoutgoing', throw=True, source=source)
1286
1286
1287 cl = self.changelog
1287 cl = self.changelog
1288 nodes = cl.nodesbetween(basenodes, None)[0]
1288 nodes = cl.nodesbetween(basenodes, None)[0]
1289 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1289 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1290
1290
1291 def identity(x):
1291 def identity(x):
1292 return x
1292 return x
1293
1293
1294 def gennodelst(revlog):
1294 def gennodelst(revlog):
1295 for r in xrange(0, revlog.count()):
1295 for r in xrange(0, revlog.count()):
1296 n = revlog.node(r)
1296 n = revlog.node(r)
1297 if revlog.linkrev(n) in revset:
1297 if revlog.linkrev(n) in revset:
1298 yield n
1298 yield n
1299
1299
1300 def changed_file_collector(changedfileset):
1300 def changed_file_collector(changedfileset):
1301 def collect_changed_files(clnode):
1301 def collect_changed_files(clnode):
1302 c = cl.read(clnode)
1302 c = cl.read(clnode)
1303 for fname in c[3]:
1303 for fname in c[3]:
1304 changedfileset[fname] = 1
1304 changedfileset[fname] = 1
1305 return collect_changed_files
1305 return collect_changed_files
1306
1306
1307 def lookuprevlink_func(revlog):
1307 def lookuprevlink_func(revlog):
1308 def lookuprevlink(n):
1308 def lookuprevlink(n):
1309 return cl.node(revlog.linkrev(n))
1309 return cl.node(revlog.linkrev(n))
1310 return lookuprevlink
1310 return lookuprevlink
1311
1311
1312 def gengroup():
1312 def gengroup():
1313 # construct a list of all changed files
1313 # construct a list of all changed files
1314 changedfiles = {}
1314 changedfiles = {}
1315
1315
1316 for chnk in cl.group(nodes, identity,
1316 for chnk in cl.group(nodes, identity,
1317 changed_file_collector(changedfiles)):
1317 changed_file_collector(changedfiles)):
1318 yield chnk
1318 yield chnk
1319 changedfiles = changedfiles.keys()
1319 changedfiles = changedfiles.keys()
1320 changedfiles.sort()
1320 changedfiles.sort()
1321
1321
1322 mnfst = self.manifest
1322 mnfst = self.manifest
1323 nodeiter = gennodelst(mnfst)
1323 nodeiter = gennodelst(mnfst)
1324 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1324 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1325 yield chnk
1325 yield chnk
1326
1326
1327 for fname in changedfiles:
1327 for fname in changedfiles:
1328 filerevlog = self.file(fname)
1328 filerevlog = self.file(fname)
1329 nodeiter = gennodelst(filerevlog)
1329 nodeiter = gennodelst(filerevlog)
1330 nodeiter = list(nodeiter)
1330 nodeiter = list(nodeiter)
1331 if nodeiter:
1331 if nodeiter:
1332 yield changegroup.genchunk(fname)
1332 yield changegroup.genchunk(fname)
1333 lookup = lookuprevlink_func(filerevlog)
1333 lookup = lookuprevlink_func(filerevlog)
1334 for chnk in filerevlog.group(nodeiter, lookup):
1334 for chnk in filerevlog.group(nodeiter, lookup):
1335 yield chnk
1335 yield chnk
1336
1336
1337 yield changegroup.closechunk()
1337 yield changegroup.closechunk()
1338 self.hook('outgoing', node=hex(nodes[0]), source=source)
1338 self.hook('outgoing', node=hex(nodes[0]), source=source)
1339
1339
1340 return util.chunkbuffer(gengroup())
1340 return util.chunkbuffer(gengroup())
1341
1341
1342 def addchangegroup(self, source):
1342 def addchangegroup(self, source):
1343
1343
1344 def csmap(x):
1344 def csmap(x):
1345 self.ui.debug(_("add changeset %s\n") % short(x))
1345 self.ui.debug(_("add changeset %s\n") % short(x))
1346 return self.changelog.count()
1346 return self.changelog.count()
1347
1347
1348 def revmap(x):
1348 def revmap(x):
1349 return self.changelog.rev(x)
1349 return self.changelog.rev(x)
1350
1350
1351 if not source:
1351 if not source:
1352 return
1352 return
1353
1353
1354 self.hook('prechangegroup', throw=True)
1354 self.hook('prechangegroup', throw=True)
1355
1355
1356 changesets = files = revisions = 0
1356 changesets = files = revisions = 0
1357
1357
1358 tr = self.transaction()
1358 tr = self.transaction()
1359
1359
1360 oldheads = len(self.changelog.heads())
1360 oldheads = len(self.changelog.heads())
1361
1361
1362 # pull off the changeset group
1362 # pull off the changeset group
1363 self.ui.status(_("adding changesets\n"))
1363 self.ui.status(_("adding changesets\n"))
1364 co = self.changelog.tip()
1364 co = self.changelog.tip()
1365 chunkiter = changegroup.chunkiter(source)
1365 chunkiter = changegroup.chunkiter(source)
1366 cn = self.changelog.addgroup(chunkiter, csmap, tr, 1) # unique
1366 cn = self.changelog.addgroup(chunkiter, csmap, tr, 1) # unique
1367 cnr, cor = map(self.changelog.rev, (cn, co))
1367 cnr, cor = map(self.changelog.rev, (cn, co))
1368 if cn == nullid:
1368 if cn == nullid:
1369 cnr = cor
1369 cnr = cor
1370 changesets = cnr - cor
1370 changesets = cnr - cor
1371
1371
1372 # pull off the manifest group
1372 # pull off the manifest group
1373 self.ui.status(_("adding manifests\n"))
1373 self.ui.status(_("adding manifests\n"))
1374 mm = self.manifest.tip()
1374 mm = self.manifest.tip()
1375 chunkiter = changegroup.chunkiter(source)
1375 chunkiter = changegroup.chunkiter(source)
1376 mo = self.manifest.addgroup(chunkiter, revmap, tr)
1376 mo = self.manifest.addgroup(chunkiter, revmap, tr)
1377
1377
1378 # process the files
1378 # process the files
1379 self.ui.status(_("adding file changes\n"))
1379 self.ui.status(_("adding file changes\n"))
1380 while 1:
1380 while 1:
1381 f = changegroup.getchunk(source)
1381 f = changegroup.getchunk(source)
1382 if not f:
1382 if not f:
1383 break
1383 break
1384 self.ui.debug(_("adding %s revisions\n") % f)
1384 self.ui.debug(_("adding %s revisions\n") % f)
1385 fl = self.file(f)
1385 fl = self.file(f)
1386 o = fl.count()
1386 o = fl.count()
1387 chunkiter = changegroup.chunkiter(source)
1387 chunkiter = changegroup.chunkiter(source)
1388 n = fl.addgroup(chunkiter, revmap, tr)
1388 n = fl.addgroup(chunkiter, revmap, tr)
1389 revisions += fl.count() - o
1389 revisions += fl.count() - o
1390 files += 1
1390 files += 1
1391
1391
1392 newheads = len(self.changelog.heads())
1392 newheads = len(self.changelog.heads())
1393 heads = ""
1393 heads = ""
1394 if oldheads and newheads > oldheads:
1394 if oldheads and newheads > oldheads:
1395 heads = _(" (+%d heads)") % (newheads - oldheads)
1395 heads = _(" (+%d heads)") % (newheads - oldheads)
1396
1396
1397 self.ui.status(_("added %d changesets"
1397 self.ui.status(_("added %d changesets"
1398 " with %d changes to %d files%s\n")
1398 " with %d changes to %d files%s\n")
1399 % (changesets, revisions, files, heads))
1399 % (changesets, revisions, files, heads))
1400
1400
1401 self.hook('pretxnchangegroup', throw=True,
1401 self.hook('pretxnchangegroup', throw=True,
1402 node=hex(self.changelog.node(cor+1)))
1402 node=hex(self.changelog.node(cor+1)))
1403
1403
1404 tr.close()
1404 tr.close()
1405
1405
1406 if changesets > 0:
1406 if changesets > 0:
1407 self.hook("changegroup", node=hex(self.changelog.node(cor+1)))
1407 self.hook("changegroup", node=hex(self.changelog.node(cor+1)))
1408
1408
1409 for i in range(cor + 1, cnr + 1):
1409 for i in range(cor + 1, cnr + 1):
1410 self.hook("incoming", node=hex(self.changelog.node(i)))
1410 self.hook("incoming", node=hex(self.changelog.node(i)))
1411
1411
1412 def update(self, node, allow=False, force=False, choose=None,
1412 def update(self, node, allow=False, force=False, choose=None,
1413 moddirstate=True, forcemerge=False, wlock=None):
1413 moddirstate=True, forcemerge=False, wlock=None):
1414 pl = self.dirstate.parents()
1414 pl = self.dirstate.parents()
1415 if not force and pl[1] != nullid:
1415 if not force and pl[1] != nullid:
1416 self.ui.warn(_("aborting: outstanding uncommitted merges\n"))
1416 self.ui.warn(_("aborting: outstanding uncommitted merges\n"))
1417 return 1
1417 return 1
1418
1418
1419 err = False
1419 err = False
1420
1420
1421 p1, p2 = pl[0], node
1421 p1, p2 = pl[0], node
1422 pa = self.changelog.ancestor(p1, p2)
1422 pa = self.changelog.ancestor(p1, p2)
1423 m1n = self.changelog.read(p1)[0]
1423 m1n = self.changelog.read(p1)[0]
1424 m2n = self.changelog.read(p2)[0]
1424 m2n = self.changelog.read(p2)[0]
1425 man = self.manifest.ancestor(m1n, m2n)
1425 man = self.manifest.ancestor(m1n, m2n)
1426 m1 = self.manifest.read(m1n)
1426 m1 = self.manifest.read(m1n)
1427 mf1 = self.manifest.readflags(m1n)
1427 mf1 = self.manifest.readflags(m1n)
1428 m2 = self.manifest.read(m2n).copy()
1428 m2 = self.manifest.read(m2n).copy()
1429 mf2 = self.manifest.readflags(m2n)
1429 mf2 = self.manifest.readflags(m2n)
1430 ma = self.manifest.read(man)
1430 ma = self.manifest.read(man)
1431 mfa = self.manifest.readflags(man)
1431 mfa = self.manifest.readflags(man)
1432
1432
1433 modified, added, removed, deleted, unknown = self.changes()
1433 modified, added, removed, deleted, unknown = self.changes()
1434
1434
1435 # is this a jump, or a merge? i.e. is there a linear path
1435 # is this a jump, or a merge? i.e. is there a linear path
1436 # from p1 to p2?
1436 # from p1 to p2?
1437 linear_path = (pa == p1 or pa == p2)
1437 linear_path = (pa == p1 or pa == p2)
1438
1438
1439 if allow and linear_path:
1439 if allow and linear_path:
1440 raise util.Abort(_("there is nothing to merge, "
1440 raise util.Abort(_("there is nothing to merge, "
1441 "just use 'hg update'"))
1441 "just use 'hg update'"))
1442 if allow and not forcemerge:
1442 if allow and not forcemerge:
1443 if modified or added or removed:
1443 if modified or added or removed:
1444 raise util.Abort(_("outstanding uncommited changes"))
1444 raise util.Abort(_("outstanding uncommitted changes"))
1445 if not forcemerge and not force:
1445 if not forcemerge and not force:
1446 for f in unknown:
1446 for f in unknown:
1447 if f in m2:
1447 if f in m2:
1448 t1 = self.wread(f)
1448 t1 = self.wread(f)
1449 t2 = self.file(f).read(m2[f])
1449 t2 = self.file(f).read(m2[f])
1450 if cmp(t1, t2) != 0:
1450 if cmp(t1, t2) != 0:
1451 raise util.Abort(_("'%s' already exists in the working"
1451 raise util.Abort(_("'%s' already exists in the working"
1452 " dir and differs from remote") % f)
1452 " dir and differs from remote") % f)
1453
1453
1454 # resolve the manifest to determine which files
1454 # resolve the manifest to determine which files
1455 # we care about merging
1455 # we care about merging
1456 self.ui.note(_("resolving manifests\n"))
1456 self.ui.note(_("resolving manifests\n"))
1457 self.ui.debug(_(" force %s allow %s moddirstate %s linear %s\n") %
1457 self.ui.debug(_(" force %s allow %s moddirstate %s linear %s\n") %
1458 (force, allow, moddirstate, linear_path))
1458 (force, allow, moddirstate, linear_path))
1459 self.ui.debug(_(" ancestor %s local %s remote %s\n") %
1459 self.ui.debug(_(" ancestor %s local %s remote %s\n") %
1460 (short(man), short(m1n), short(m2n)))
1460 (short(man), short(m1n), short(m2n)))
1461
1461
1462 merge = {}
1462 merge = {}
1463 get = {}
1463 get = {}
1464 remove = []
1464 remove = []
1465
1465
1466 # construct a working dir manifest
1466 # construct a working dir manifest
1467 mw = m1.copy()
1467 mw = m1.copy()
1468 mfw = mf1.copy()
1468 mfw = mf1.copy()
1469 umap = dict.fromkeys(unknown)
1469 umap = dict.fromkeys(unknown)
1470
1470
1471 for f in added + modified + unknown:
1471 for f in added + modified + unknown:
1472 mw[f] = ""
1472 mw[f] = ""
1473 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1473 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1474
1474
1475 if moddirstate and not wlock:
1475 if moddirstate and not wlock:
1476 wlock = self.wlock()
1476 wlock = self.wlock()
1477
1477
1478 for f in deleted + removed:
1478 for f in deleted + removed:
1479 if f in mw:
1479 if f in mw:
1480 del mw[f]
1480 del mw[f]
1481
1481
1482 # If we're jumping between revisions (as opposed to merging),
1482 # If we're jumping between revisions (as opposed to merging),
1483 # and if neither the working directory nor the target rev has
1483 # and if neither the working directory nor the target rev has
1484 # the file, then we need to remove it from the dirstate, to
1484 # the file, then we need to remove it from the dirstate, to
1485 # prevent the dirstate from listing the file when it is no
1485 # prevent the dirstate from listing the file when it is no
1486 # longer in the manifest.
1486 # longer in the manifest.
1487 if moddirstate and linear_path and f not in m2:
1487 if moddirstate and linear_path and f not in m2:
1488 self.dirstate.forget((f,))
1488 self.dirstate.forget((f,))
1489
1489
1490 # Compare manifests
1490 # Compare manifests
1491 for f, n in mw.iteritems():
1491 for f, n in mw.iteritems():
1492 if choose and not choose(f):
1492 if choose and not choose(f):
1493 continue
1493 continue
1494 if f in m2:
1494 if f in m2:
1495 s = 0
1495 s = 0
1496
1496
1497 # is the wfile new since m1, and match m2?
1497 # is the wfile new since m1, and match m2?
1498 if f not in m1:
1498 if f not in m1:
1499 t1 = self.wread(f)
1499 t1 = self.wread(f)
1500 t2 = self.file(f).read(m2[f])
1500 t2 = self.file(f).read(m2[f])
1501 if cmp(t1, t2) == 0:
1501 if cmp(t1, t2) == 0:
1502 n = m2[f]
1502 n = m2[f]
1503 del t1, t2
1503 del t1, t2
1504
1504
1505 # are files different?
1505 # are files different?
1506 if n != m2[f]:
1506 if n != m2[f]:
1507 a = ma.get(f, nullid)
1507 a = ma.get(f, nullid)
1508 # are both different from the ancestor?
1508 # are both different from the ancestor?
1509 if n != a and m2[f] != a:
1509 if n != a and m2[f] != a:
1510 self.ui.debug(_(" %s versions differ, resolve\n") % f)
1510 self.ui.debug(_(" %s versions differ, resolve\n") % f)
1511 # merge executable bits
1511 # merge executable bits
1512 # "if we changed or they changed, change in merge"
1512 # "if we changed or they changed, change in merge"
1513 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1513 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1514 mode = ((a^b) | (a^c)) ^ a
1514 mode = ((a^b) | (a^c)) ^ a
1515 merge[f] = (m1.get(f, nullid), m2[f], mode)
1515 merge[f] = (m1.get(f, nullid), m2[f], mode)
1516 s = 1
1516 s = 1
1517 # are we clobbering?
1517 # are we clobbering?
1518 # is remote's version newer?
1518 # is remote's version newer?
1519 # or are we going back in time?
1519 # or are we going back in time?
1520 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1520 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1521 self.ui.debug(_(" remote %s is newer, get\n") % f)
1521 self.ui.debug(_(" remote %s is newer, get\n") % f)
1522 get[f] = m2[f]
1522 get[f] = m2[f]
1523 s = 1
1523 s = 1
1524 elif f in umap:
1524 elif f in umap:
1525 # this unknown file is the same as the checkout
1525 # this unknown file is the same as the checkout
1526 get[f] = m2[f]
1526 get[f] = m2[f]
1527
1527
1528 if not s and mfw[f] != mf2[f]:
1528 if not s and mfw[f] != mf2[f]:
1529 if force:
1529 if force:
1530 self.ui.debug(_(" updating permissions for %s\n") % f)
1530 self.ui.debug(_(" updating permissions for %s\n") % f)
1531 util.set_exec(self.wjoin(f), mf2[f])
1531 util.set_exec(self.wjoin(f), mf2[f])
1532 else:
1532 else:
1533 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1533 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1534 mode = ((a^b) | (a^c)) ^ a
1534 mode = ((a^b) | (a^c)) ^ a
1535 if mode != b:
1535 if mode != b:
1536 self.ui.debug(_(" updating permissions for %s\n")
1536 self.ui.debug(_(" updating permissions for %s\n")
1537 % f)
1537 % f)
1538 util.set_exec(self.wjoin(f), mode)
1538 util.set_exec(self.wjoin(f), mode)
1539 del m2[f]
1539 del m2[f]
1540 elif f in ma:
1540 elif f in ma:
1541 if n != ma[f]:
1541 if n != ma[f]:
1542 r = _("d")
1542 r = _("d")
1543 if not force and (linear_path or allow):
1543 if not force and (linear_path or allow):
1544 r = self.ui.prompt(
1544 r = self.ui.prompt(
1545 (_(" local changed %s which remote deleted\n") % f) +
1545 (_(" local changed %s which remote deleted\n") % f) +
1546 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1546 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1547 if r == _("d"):
1547 if r == _("d"):
1548 remove.append(f)
1548 remove.append(f)
1549 else:
1549 else:
1550 self.ui.debug(_("other deleted %s\n") % f)
1550 self.ui.debug(_("other deleted %s\n") % f)
1551 remove.append(f) # other deleted it
1551 remove.append(f) # other deleted it
1552 else:
1552 else:
1553 # file is created on branch or in working directory
1553 # file is created on branch or in working directory
1554 if force and f not in umap:
1554 if force and f not in umap:
1555 self.ui.debug(_("remote deleted %s, clobbering\n") % f)
1555 self.ui.debug(_("remote deleted %s, clobbering\n") % f)
1556 remove.append(f)
1556 remove.append(f)
1557 elif n == m1.get(f, nullid): # same as parent
1557 elif n == m1.get(f, nullid): # same as parent
1558 if p2 == pa: # going backwards?
1558 if p2 == pa: # going backwards?
1559 self.ui.debug(_("remote deleted %s\n") % f)
1559 self.ui.debug(_("remote deleted %s\n") % f)
1560 remove.append(f)
1560 remove.append(f)
1561 else:
1561 else:
1562 self.ui.debug(_("local modified %s, keeping\n") % f)
1562 self.ui.debug(_("local modified %s, keeping\n") % f)
1563 else:
1563 else:
1564 self.ui.debug(_("working dir created %s, keeping\n") % f)
1564 self.ui.debug(_("working dir created %s, keeping\n") % f)
1565
1565
1566 for f, n in m2.iteritems():
1566 for f, n in m2.iteritems():
1567 if choose and not choose(f):
1567 if choose and not choose(f):
1568 continue
1568 continue
1569 if f[0] == "/":
1569 if f[0] == "/":
1570 continue
1570 continue
1571 if f in ma and n != ma[f]:
1571 if f in ma and n != ma[f]:
1572 r = _("k")
1572 r = _("k")
1573 if not force and (linear_path or allow):
1573 if not force and (linear_path or allow):
1574 r = self.ui.prompt(
1574 r = self.ui.prompt(
1575 (_("remote changed %s which local deleted\n") % f) +
1575 (_("remote changed %s which local deleted\n") % f) +
1576 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1576 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1577 if r == _("k"):
1577 if r == _("k"):
1578 get[f] = n
1578 get[f] = n
1579 elif f not in ma:
1579 elif f not in ma:
1580 self.ui.debug(_("remote created %s\n") % f)
1580 self.ui.debug(_("remote created %s\n") % f)
1581 get[f] = n
1581 get[f] = n
1582 else:
1582 else:
1583 if force or p2 == pa: # going backwards?
1583 if force or p2 == pa: # going backwards?
1584 self.ui.debug(_("local deleted %s, recreating\n") % f)
1584 self.ui.debug(_("local deleted %s, recreating\n") % f)
1585 get[f] = n
1585 get[f] = n
1586 else:
1586 else:
1587 self.ui.debug(_("local deleted %s\n") % f)
1587 self.ui.debug(_("local deleted %s\n") % f)
1588
1588
1589 del mw, m1, m2, ma
1589 del mw, m1, m2, ma
1590
1590
1591 if force:
1591 if force:
1592 for f in merge:
1592 for f in merge:
1593 get[f] = merge[f][1]
1593 get[f] = merge[f][1]
1594 merge = {}
1594 merge = {}
1595
1595
1596 if linear_path or force:
1596 if linear_path or force:
1597 # we don't need to do any magic, just jump to the new rev
1597 # we don't need to do any magic, just jump to the new rev
1598 branch_merge = False
1598 branch_merge = False
1599 p1, p2 = p2, nullid
1599 p1, p2 = p2, nullid
1600 else:
1600 else:
1601 if not allow:
1601 if not allow:
1602 self.ui.status(_("this update spans a branch"
1602 self.ui.status(_("this update spans a branch"
1603 " affecting the following files:\n"))
1603 " affecting the following files:\n"))
1604 fl = merge.keys() + get.keys()
1604 fl = merge.keys() + get.keys()
1605 fl.sort()
1605 fl.sort()
1606 for f in fl:
1606 for f in fl:
1607 cf = ""
1607 cf = ""
1608 if f in merge:
1608 if f in merge:
1609 cf = _(" (resolve)")
1609 cf = _(" (resolve)")
1610 self.ui.status(" %s%s\n" % (f, cf))
1610 self.ui.status(" %s%s\n" % (f, cf))
1611 self.ui.warn(_("aborting update spanning branches!\n"))
1611 self.ui.warn(_("aborting update spanning branches!\n"))
1612 self.ui.status(_("(use update -m to merge across branches"
1612 self.ui.status(_("(use update -m to merge across branches"
1613 " or -C to lose changes)\n"))
1613 " or -C to lose changes)\n"))
1614 return 1
1614 return 1
1615 branch_merge = True
1615 branch_merge = True
1616
1616
1617 # get the files we don't need to change
1617 # get the files we don't need to change
1618 files = get.keys()
1618 files = get.keys()
1619 files.sort()
1619 files.sort()
1620 for f in files:
1620 for f in files:
1621 if f[0] == "/":
1621 if f[0] == "/":
1622 continue
1622 continue
1623 self.ui.note(_("getting %s\n") % f)
1623 self.ui.note(_("getting %s\n") % f)
1624 t = self.file(f).read(get[f])
1624 t = self.file(f).read(get[f])
1625 self.wwrite(f, t)
1625 self.wwrite(f, t)
1626 util.set_exec(self.wjoin(f), mf2[f])
1626 util.set_exec(self.wjoin(f), mf2[f])
1627 if moddirstate:
1627 if moddirstate:
1628 if branch_merge:
1628 if branch_merge:
1629 self.dirstate.update([f], 'n', st_mtime=-1)
1629 self.dirstate.update([f], 'n', st_mtime=-1)
1630 else:
1630 else:
1631 self.dirstate.update([f], 'n')
1631 self.dirstate.update([f], 'n')
1632
1632
1633 # merge the tricky bits
1633 # merge the tricky bits
1634 failedmerge = []
1634 failedmerge = []
1635 files = merge.keys()
1635 files = merge.keys()
1636 files.sort()
1636 files.sort()
1637 xp1 = hex(p1)
1637 xp1 = hex(p1)
1638 xp2 = hex(p2)
1638 xp2 = hex(p2)
1639 for f in files:
1639 for f in files:
1640 self.ui.status(_("merging %s\n") % f)
1640 self.ui.status(_("merging %s\n") % f)
1641 my, other, flag = merge[f]
1641 my, other, flag = merge[f]
1642 ret = self.merge3(f, my, other, xp1, xp2)
1642 ret = self.merge3(f, my, other, xp1, xp2)
1643 if ret:
1643 if ret:
1644 err = True
1644 err = True
1645 failedmerge.append(f)
1645 failedmerge.append(f)
1646 util.set_exec(self.wjoin(f), flag)
1646 util.set_exec(self.wjoin(f), flag)
1647 if moddirstate:
1647 if moddirstate:
1648 if branch_merge:
1648 if branch_merge:
1649 # We've done a branch merge, mark this file as merged
1649 # We've done a branch merge, mark this file as merged
1650 # so that we properly record the merger later
1650 # so that we properly record the merger later
1651 self.dirstate.update([f], 'm')
1651 self.dirstate.update([f], 'm')
1652 else:
1652 else:
1653 # We've update-merged a locally modified file, so
1653 # We've update-merged a locally modified file, so
1654 # we set the dirstate to emulate a normal checkout
1654 # we set the dirstate to emulate a normal checkout
1655 # of that file some time in the past. Thus our
1655 # of that file some time in the past. Thus our
1656 # merge will appear as a normal local file
1656 # merge will appear as a normal local file
1657 # modification.
1657 # modification.
1658 f_len = len(self.file(f).read(other))
1658 f_len = len(self.file(f).read(other))
1659 self.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
1659 self.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
1660
1660
1661 remove.sort()
1661 remove.sort()
1662 for f in remove:
1662 for f in remove:
1663 self.ui.note(_("removing %s\n") % f)
1663 self.ui.note(_("removing %s\n") % f)
1664 util.audit_path(f)
1664 util.audit_path(f)
1665 try:
1665 try:
1666 util.unlink(self.wjoin(f))
1666 util.unlink(self.wjoin(f))
1667 except OSError, inst:
1667 except OSError, inst:
1668 if inst.errno != errno.ENOENT:
1668 if inst.errno != errno.ENOENT:
1669 self.ui.warn(_("update failed to remove %s: %s!\n") %
1669 self.ui.warn(_("update failed to remove %s: %s!\n") %
1670 (f, inst.strerror))
1670 (f, inst.strerror))
1671 if moddirstate:
1671 if moddirstate:
1672 if branch_merge:
1672 if branch_merge:
1673 self.dirstate.update(remove, 'r')
1673 self.dirstate.update(remove, 'r')
1674 else:
1674 else:
1675 self.dirstate.forget(remove)
1675 self.dirstate.forget(remove)
1676
1676
1677 if moddirstate:
1677 if moddirstate:
1678 self.dirstate.setparents(p1, p2)
1678 self.dirstate.setparents(p1, p2)
1679
1679
1680 stat = ((len(get), _("updated")),
1680 stat = ((len(get), _("updated")),
1681 (len(merge) - len(failedmerge), _("merged")),
1681 (len(merge) - len(failedmerge), _("merged")),
1682 (len(remove), _("removed")),
1682 (len(remove), _("removed")),
1683 (len(failedmerge), _("unresolved")))
1683 (len(failedmerge), _("unresolved")))
1684 note = ", ".join([_("%d files %s") % s for s in stat])
1684 note = ", ".join([_("%d files %s") % s for s in stat])
1685 self.ui.note("%s\n" % note)
1685 self.ui.note("%s\n" % note)
1686 if moddirstate and branch_merge:
1686 if moddirstate and branch_merge:
1687 self.ui.note(_("(branch merge, don't forget to commit)\n"))
1687 self.ui.note(_("(branch merge, don't forget to commit)\n"))
1688
1688
1689 return err
1689 return err
1690
1690
1691 def merge3(self, fn, my, other, p1, p2):
1691 def merge3(self, fn, my, other, p1, p2):
1692 """perform a 3-way merge in the working directory"""
1692 """perform a 3-way merge in the working directory"""
1693
1693
1694 def temp(prefix, node):
1694 def temp(prefix, node):
1695 pre = "%s~%s." % (os.path.basename(fn), prefix)
1695 pre = "%s~%s." % (os.path.basename(fn), prefix)
1696 (fd, name) = tempfile.mkstemp("", pre)
1696 (fd, name) = tempfile.mkstemp("", pre)
1697 f = os.fdopen(fd, "wb")
1697 f = os.fdopen(fd, "wb")
1698 self.wwrite(fn, fl.read(node), f)
1698 self.wwrite(fn, fl.read(node), f)
1699 f.close()
1699 f.close()
1700 return name
1700 return name
1701
1701
1702 fl = self.file(fn)
1702 fl = self.file(fn)
1703 base = fl.ancestor(my, other)
1703 base = fl.ancestor(my, other)
1704 a = self.wjoin(fn)
1704 a = self.wjoin(fn)
1705 b = temp("base", base)
1705 b = temp("base", base)
1706 c = temp("other", other)
1706 c = temp("other", other)
1707
1707
1708 self.ui.note(_("resolving %s\n") % fn)
1708 self.ui.note(_("resolving %s\n") % fn)
1709 self.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
1709 self.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
1710 (fn, short(my), short(other), short(base)))
1710 (fn, short(my), short(other), short(base)))
1711
1711
1712 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1712 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1713 or "hgmerge")
1713 or "hgmerge")
1714 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=self.root,
1714 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=self.root,
1715 environ={'HG_FILE': fn,
1715 environ={'HG_FILE': fn,
1716 'HG_MY_NODE': p1,
1716 'HG_MY_NODE': p1,
1717 'HG_OTHER_NODE': p2,
1717 'HG_OTHER_NODE': p2,
1718 'HG_FILE_MY_NODE': hex(my),
1718 'HG_FILE_MY_NODE': hex(my),
1719 'HG_FILE_OTHER_NODE': hex(other),
1719 'HG_FILE_OTHER_NODE': hex(other),
1720 'HG_FILE_BASE_NODE': hex(base)})
1720 'HG_FILE_BASE_NODE': hex(base)})
1721 if r:
1721 if r:
1722 self.ui.warn(_("merging %s failed!\n") % fn)
1722 self.ui.warn(_("merging %s failed!\n") % fn)
1723
1723
1724 os.unlink(b)
1724 os.unlink(b)
1725 os.unlink(c)
1725 os.unlink(c)
1726 return r
1726 return r
1727
1727
1728 def verify(self):
1728 def verify(self):
1729 filelinkrevs = {}
1729 filelinkrevs = {}
1730 filenodes = {}
1730 filenodes = {}
1731 changesets = revisions = files = 0
1731 changesets = revisions = files = 0
1732 errors = [0]
1732 errors = [0]
1733 neededmanifests = {}
1733 neededmanifests = {}
1734
1734
1735 def err(msg):
1735 def err(msg):
1736 self.ui.warn(msg + "\n")
1736 self.ui.warn(msg + "\n")
1737 errors[0] += 1
1737 errors[0] += 1
1738
1738
1739 def checksize(obj, name):
1739 def checksize(obj, name):
1740 d = obj.checksize()
1740 d = obj.checksize()
1741 if d[0]:
1741 if d[0]:
1742 err(_("%s data length off by %d bytes") % (name, d[0]))
1742 err(_("%s data length off by %d bytes") % (name, d[0]))
1743 if d[1]:
1743 if d[1]:
1744 err(_("%s index contains %d extra bytes") % (name, d[1]))
1744 err(_("%s index contains %d extra bytes") % (name, d[1]))
1745
1745
1746 seen = {}
1746 seen = {}
1747 self.ui.status(_("checking changesets\n"))
1747 self.ui.status(_("checking changesets\n"))
1748 checksize(self.changelog, "changelog")
1748 checksize(self.changelog, "changelog")
1749
1749
1750 for i in range(self.changelog.count()):
1750 for i in range(self.changelog.count()):
1751 changesets += 1
1751 changesets += 1
1752 n = self.changelog.node(i)
1752 n = self.changelog.node(i)
1753 l = self.changelog.linkrev(n)
1753 l = self.changelog.linkrev(n)
1754 if l != i:
1754 if l != i:
1755 err(_("incorrect link (%d) for changeset revision %d") %(l, i))
1755 err(_("incorrect link (%d) for changeset revision %d") %(l, i))
1756 if n in seen:
1756 if n in seen:
1757 err(_("duplicate changeset at revision %d") % i)
1757 err(_("duplicate changeset at revision %d") % i)
1758 seen[n] = 1
1758 seen[n] = 1
1759
1759
1760 for p in self.changelog.parents(n):
1760 for p in self.changelog.parents(n):
1761 if p not in self.changelog.nodemap:
1761 if p not in self.changelog.nodemap:
1762 err(_("changeset %s has unknown parent %s") %
1762 err(_("changeset %s has unknown parent %s") %
1763 (short(n), short(p)))
1763 (short(n), short(p)))
1764 try:
1764 try:
1765 changes = self.changelog.read(n)
1765 changes = self.changelog.read(n)
1766 except KeyboardInterrupt:
1766 except KeyboardInterrupt:
1767 self.ui.warn(_("interrupted"))
1767 self.ui.warn(_("interrupted"))
1768 raise
1768 raise
1769 except Exception, inst:
1769 except Exception, inst:
1770 err(_("unpacking changeset %s: %s") % (short(n), inst))
1770 err(_("unpacking changeset %s: %s") % (short(n), inst))
1771 continue
1771 continue
1772
1772
1773 neededmanifests[changes[0]] = n
1773 neededmanifests[changes[0]] = n
1774
1774
1775 for f in changes[3]:
1775 for f in changes[3]:
1776 filelinkrevs.setdefault(f, []).append(i)
1776 filelinkrevs.setdefault(f, []).append(i)
1777
1777
1778 seen = {}
1778 seen = {}
1779 self.ui.status(_("checking manifests\n"))
1779 self.ui.status(_("checking manifests\n"))
1780 checksize(self.manifest, "manifest")
1780 checksize(self.manifest, "manifest")
1781
1781
1782 for i in range(self.manifest.count()):
1782 for i in range(self.manifest.count()):
1783 n = self.manifest.node(i)
1783 n = self.manifest.node(i)
1784 l = self.manifest.linkrev(n)
1784 l = self.manifest.linkrev(n)
1785
1785
1786 if l < 0 or l >= self.changelog.count():
1786 if l < 0 or l >= self.changelog.count():
1787 err(_("bad manifest link (%d) at revision %d") % (l, i))
1787 err(_("bad manifest link (%d) at revision %d") % (l, i))
1788
1788
1789 if n in neededmanifests:
1789 if n in neededmanifests:
1790 del neededmanifests[n]
1790 del neededmanifests[n]
1791
1791
1792 if n in seen:
1792 if n in seen:
1793 err(_("duplicate manifest at revision %d") % i)
1793 err(_("duplicate manifest at revision %d") % i)
1794
1794
1795 seen[n] = 1
1795 seen[n] = 1
1796
1796
1797 for p in self.manifest.parents(n):
1797 for p in self.manifest.parents(n):
1798 if p not in self.manifest.nodemap:
1798 if p not in self.manifest.nodemap:
1799 err(_("manifest %s has unknown parent %s") %
1799 err(_("manifest %s has unknown parent %s") %
1800 (short(n), short(p)))
1800 (short(n), short(p)))
1801
1801
1802 try:
1802 try:
1803 delta = mdiff.patchtext(self.manifest.delta(n))
1803 delta = mdiff.patchtext(self.manifest.delta(n))
1804 except KeyboardInterrupt:
1804 except KeyboardInterrupt:
1805 self.ui.warn(_("interrupted"))
1805 self.ui.warn(_("interrupted"))
1806 raise
1806 raise
1807 except Exception, inst:
1807 except Exception, inst:
1808 err(_("unpacking manifest %s: %s") % (short(n), inst))
1808 err(_("unpacking manifest %s: %s") % (short(n), inst))
1809 continue
1809 continue
1810
1810
1811 try:
1811 try:
1812 ff = [ l.split('\0') for l in delta.splitlines() ]
1812 ff = [ l.split('\0') for l in delta.splitlines() ]
1813 for f, fn in ff:
1813 for f, fn in ff:
1814 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1814 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1815 except (ValueError, TypeError), inst:
1815 except (ValueError, TypeError), inst:
1816 err(_("broken delta in manifest %s: %s") % (short(n), inst))
1816 err(_("broken delta in manifest %s: %s") % (short(n), inst))
1817
1817
1818 self.ui.status(_("crosschecking files in changesets and manifests\n"))
1818 self.ui.status(_("crosschecking files in changesets and manifests\n"))
1819
1819
1820 for m, c in neededmanifests.items():
1820 for m, c in neededmanifests.items():
1821 err(_("Changeset %s refers to unknown manifest %s") %
1821 err(_("Changeset %s refers to unknown manifest %s") %
1822 (short(m), short(c)))
1822 (short(m), short(c)))
1823 del neededmanifests
1823 del neededmanifests
1824
1824
1825 for f in filenodes:
1825 for f in filenodes:
1826 if f not in filelinkrevs:
1826 if f not in filelinkrevs:
1827 err(_("file %s in manifest but not in changesets") % f)
1827 err(_("file %s in manifest but not in changesets") % f)
1828
1828
1829 for f in filelinkrevs:
1829 for f in filelinkrevs:
1830 if f not in filenodes:
1830 if f not in filenodes:
1831 err(_("file %s in changeset but not in manifest") % f)
1831 err(_("file %s in changeset but not in manifest") % f)
1832
1832
1833 self.ui.status(_("checking files\n"))
1833 self.ui.status(_("checking files\n"))
1834 ff = filenodes.keys()
1834 ff = filenodes.keys()
1835 ff.sort()
1835 ff.sort()
1836 for f in ff:
1836 for f in ff:
1837 if f == "/dev/null":
1837 if f == "/dev/null":
1838 continue
1838 continue
1839 files += 1
1839 files += 1
1840 if not f:
1840 if not f:
1841 err(_("file without name in manifest %s") % short(n))
1841 err(_("file without name in manifest %s") % short(n))
1842 continue
1842 continue
1843 fl = self.file(f)
1843 fl = self.file(f)
1844 checksize(fl, f)
1844 checksize(fl, f)
1845
1845
1846 nodes = {nullid: 1}
1846 nodes = {nullid: 1}
1847 seen = {}
1847 seen = {}
1848 for i in range(fl.count()):
1848 for i in range(fl.count()):
1849 revisions += 1
1849 revisions += 1
1850 n = fl.node(i)
1850 n = fl.node(i)
1851
1851
1852 if n in seen:
1852 if n in seen:
1853 err(_("%s: duplicate revision %d") % (f, i))
1853 err(_("%s: duplicate revision %d") % (f, i))
1854 if n not in filenodes[f]:
1854 if n not in filenodes[f]:
1855 err(_("%s: %d:%s not in manifests") % (f, i, short(n)))
1855 err(_("%s: %d:%s not in manifests") % (f, i, short(n)))
1856 else:
1856 else:
1857 del filenodes[f][n]
1857 del filenodes[f][n]
1858
1858
1859 flr = fl.linkrev(n)
1859 flr = fl.linkrev(n)
1860 if flr not in filelinkrevs.get(f, []):
1860 if flr not in filelinkrevs.get(f, []):
1861 err(_("%s:%s points to unexpected changeset %d")
1861 err(_("%s:%s points to unexpected changeset %d")
1862 % (f, short(n), flr))
1862 % (f, short(n), flr))
1863 else:
1863 else:
1864 filelinkrevs[f].remove(flr)
1864 filelinkrevs[f].remove(flr)
1865
1865
1866 # verify contents
1866 # verify contents
1867 try:
1867 try:
1868 t = fl.read(n)
1868 t = fl.read(n)
1869 except KeyboardInterrupt:
1869 except KeyboardInterrupt:
1870 self.ui.warn(_("interrupted"))
1870 self.ui.warn(_("interrupted"))
1871 raise
1871 raise
1872 except Exception, inst:
1872 except Exception, inst:
1873 err(_("unpacking file %s %s: %s") % (f, short(n), inst))
1873 err(_("unpacking file %s %s: %s") % (f, short(n), inst))
1874
1874
1875 # verify parents
1875 # verify parents
1876 (p1, p2) = fl.parents(n)
1876 (p1, p2) = fl.parents(n)
1877 if p1 not in nodes:
1877 if p1 not in nodes:
1878 err(_("file %s:%s unknown parent 1 %s") %
1878 err(_("file %s:%s unknown parent 1 %s") %
1879 (f, short(n), short(p1)))
1879 (f, short(n), short(p1)))
1880 if p2 not in nodes:
1880 if p2 not in nodes:
1881 err(_("file %s:%s unknown parent 2 %s") %
1881 err(_("file %s:%s unknown parent 2 %s") %
1882 (f, short(n), short(p1)))
1882 (f, short(n), short(p1)))
1883 nodes[n] = 1
1883 nodes[n] = 1
1884
1884
1885 # cross-check
1885 # cross-check
1886 for node in filenodes[f]:
1886 for node in filenodes[f]:
1887 err(_("node %s in manifests not in %s") % (hex(node), f))
1887 err(_("node %s in manifests not in %s") % (hex(node), f))
1888
1888
1889 self.ui.status(_("%d files, %d changesets, %d total revisions\n") %
1889 self.ui.status(_("%d files, %d changesets, %d total revisions\n") %
1890 (files, changesets, revisions))
1890 (files, changesets, revisions))
1891
1891
1892 if errors[0]:
1892 if errors[0]:
1893 self.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
1893 self.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
1894 return 1
1894 return 1
1895
1895
1896 # used to avoid circular references so destructors work
1896 # used to avoid circular references so destructors work
1897 def aftertrans(base):
1897 def aftertrans(base):
1898 p = base
1898 p = base
1899 def a():
1899 def a():
1900 util.rename(os.path.join(p, "journal"), os.path.join(p, "undo"))
1900 util.rename(os.path.join(p, "journal"), os.path.join(p, "undo"))
1901 util.rename(os.path.join(p, "journal.dirstate"),
1901 util.rename(os.path.join(p, "journal.dirstate"),
1902 os.path.join(p, "undo.dirstate"))
1902 os.path.join(p, "undo.dirstate"))
1903 return a
1903 return a
1904
1904
@@ -1,19 +1,19 b''
1 %% no merges expected
1 %% no merges expected
2 %% merge should fail
2 %% merge should fail
3 abort: 'b' already exists in the working dir and differs from remote
3 abort: 'b' already exists in the working dir and differs from remote
4 %% merge of b expected
4 %% merge of b expected
5 merging for b
5 merging for b
6 merging b
6 merging b
7 %%
7 %%
8 Contents of b should be "this is file b1"
8 Contents of b should be "this is file b1"
9 This is file b1
9 This is file b1
10 %% merge fails
10 %% merge fails
11 abort: outstanding uncommited changes
11 abort: outstanding uncommitted changes
12 %% merge expected!
12 %% merge expected!
13 merging for b
13 merging for b
14 merging b
14 merging b
15 %% merge of b should fail
15 %% merge of b should fail
16 abort: outstanding uncommited changes
16 abort: outstanding uncommitted changes
17 %% merge of b expected
17 %% merge of b expected
18 merging for b
18 merging for b
19 merging b
19 merging b
@@ -1,138 +1,138 b''
1 adding a
1 adding a
2 diff -r 33aaa84a386b a
2 diff -r 33aaa84a386b a
3 --- a/a
3 --- a/a
4 +++ b/a
4 +++ b/a
5 @@ -1,1 +1,1 @@ a
5 @@ -1,1 +1,1 @@ a
6 -a
6 -a
7 +abc
7 +abc
8 adding b
8 adding b
9 M a
9 M a
10 changeset: 0:33aaa84a386b
10 changeset: 0:33aaa84a386b
11 user: test
11 user: test
12 date: Mon Jan 12 13:46:40 1970 +0000
12 date: Mon Jan 12 13:46:40 1970 +0000
13 summary: 1
13 summary: 1
14
14
15 resolving manifests
15 resolving manifests
16 force None allow None moddirstate True linear True
16 force None allow None moddirstate True linear True
17 ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e
17 ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e
18 a versions differ, resolve
18 a versions differ, resolve
19 remote created b
19 remote created b
20 getting b
20 getting b
21 merging a
21 merging a
22 resolving a
22 resolving a
23 file a: my b789fdd96dc2 other d730145abbf9 ancestor b789fdd96dc2
23 file a: my b789fdd96dc2 other d730145abbf9 ancestor b789fdd96dc2
24 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
24 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
25 changeset: 1:802f095af299
25 changeset: 1:802f095af299
26 tag: tip
26 tag: tip
27 user: test
27 user: test
28 date: Mon Jan 12 13:46:40 1970 +0000
28 date: Mon Jan 12 13:46:40 1970 +0000
29 summary: 2
29 summary: 2
30
30
31 resolving manifests
31 resolving manifests
32 force None allow None moddirstate True linear True
32 force None allow None moddirstate True linear True
33 ancestor a0c8bcbbb45c local 1165e8bd193e remote a0c8bcbbb45c
33 ancestor a0c8bcbbb45c local 1165e8bd193e remote a0c8bcbbb45c
34 remote deleted b
34 remote deleted b
35 removing b
35 removing b
36 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
36 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
37 changeset: 0:33aaa84a386b
37 changeset: 0:33aaa84a386b
38 user: test
38 user: test
39 date: Mon Jan 12 13:46:40 1970 +0000
39 date: Mon Jan 12 13:46:40 1970 +0000
40 summary: 1
40 summary: 1
41
41
42 abort: there is nothing to merge, just use 'hg update'
42 abort: there is nothing to merge, just use 'hg update'
43 failed
43 failed
44 changeset: 0:33aaa84a386b
44 changeset: 0:33aaa84a386b
45 user: test
45 user: test
46 date: Mon Jan 12 13:46:40 1970 +0000
46 date: Mon Jan 12 13:46:40 1970 +0000
47 summary: 1
47 summary: 1
48
48
49 resolving manifests
49 resolving manifests
50 force None allow None moddirstate True linear True
50 force None allow None moddirstate True linear True
51 ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e
51 ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e
52 a versions differ, resolve
52 a versions differ, resolve
53 remote created b
53 remote created b
54 getting b
54 getting b
55 merging a
55 merging a
56 resolving a
56 resolving a
57 file a: my b789fdd96dc2 other d730145abbf9 ancestor b789fdd96dc2
57 file a: my b789fdd96dc2 other d730145abbf9 ancestor b789fdd96dc2
58 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
58 1 files updated, 1 files merged, 0 files removed, 0 files unresolved
59 changeset: 1:802f095af299
59 changeset: 1:802f095af299
60 tag: tip
60 tag: tip
61 user: test
61 user: test
62 date: Mon Jan 12 13:46:40 1970 +0000
62 date: Mon Jan 12 13:46:40 1970 +0000
63 summary: 2
63 summary: 2
64
64
65 changeset: 1:802f095af299cde27a85b2f056aef3829870956c
65 changeset: 1:802f095af299cde27a85b2f056aef3829870956c
66 tag: tip
66 tag: tip
67 user: test
67 user: test
68 date: Mon Jan 12 13:46:40 1970 +0000
68 date: Mon Jan 12 13:46:40 1970 +0000
69 files: a b
69 files: a b
70 description:
70 description:
71 2
71 2
72
72
73
73
74 changeset: 0:33aaa84a386bd609094aeb21a97c09436c482ef1
74 changeset: 0:33aaa84a386bd609094aeb21a97c09436c482ef1
75 user: test
75 user: test
76 date: Mon Jan 12 13:46:40 1970 +0000
76 date: Mon Jan 12 13:46:40 1970 +0000
77 files: a
77 files: a
78 description:
78 description:
79 1
79 1
80
80
81
81
82 diff -r 802f095af299 a
82 diff -r 802f095af299 a
83 --- a/a
83 --- a/a
84 +++ b/a
84 +++ b/a
85 @@ -1,1 +1,1 @@ a2
85 @@ -1,1 +1,1 @@ a2
86 -a2
86 -a2
87 +abc
87 +abc
88 adding b
88 adding b
89 M a
89 M a
90 changeset: 1:802f095af299
90 changeset: 1:802f095af299
91 user: test
91 user: test
92 date: Mon Jan 12 13:46:40 1970 +0000
92 date: Mon Jan 12 13:46:40 1970 +0000
93 summary: 2
93 summary: 2
94
94
95 resolving manifests
95 resolving manifests
96 force None allow None moddirstate True linear False
96 force None allow None moddirstate True linear False
97 ancestor a0c8bcbbb45c local 1165e8bd193e remote 4096f2872392
97 ancestor a0c8bcbbb45c local 1165e8bd193e remote 4096f2872392
98 a versions differ, resolve
98 a versions differ, resolve
99 b versions differ, resolve
99 b versions differ, resolve
100 this update spans a branch affecting the following files:
100 this update spans a branch affecting the following files:
101 a (resolve)
101 a (resolve)
102 b (resolve)
102 b (resolve)
103 aborting update spanning branches!
103 aborting update spanning branches!
104 (use update -m to merge across branches or -C to lose changes)
104 (use update -m to merge across branches or -C to lose changes)
105 failed
105 failed
106 abort: outstanding uncommited changes
106 abort: outstanding uncommitted changes
107 failed
107 failed
108 resolving manifests
108 resolving manifests
109 force None allow 1 moddirstate True linear False
109 force None allow 1 moddirstate True linear False
110 ancestor a0c8bcbbb45c local 1165e8bd193e remote 4096f2872392
110 ancestor a0c8bcbbb45c local 1165e8bd193e remote 4096f2872392
111 a versions differ, resolve
111 a versions differ, resolve
112 b versions differ, resolve
112 b versions differ, resolve
113 merging a
113 merging a
114 resolving a
114 resolving a
115 file a: my d730145abbf9 other 13e0d5f949fa ancestor b789fdd96dc2
115 file a: my d730145abbf9 other 13e0d5f949fa ancestor b789fdd96dc2
116 merging b
116 merging b
117 resolving b
117 resolving b
118 file b: my 1e88685f5dde other 61de8c7723ca ancestor 000000000000
118 file b: my 1e88685f5dde other 61de8c7723ca ancestor 000000000000
119 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
119 0 files updated, 2 files merged, 0 files removed, 0 files unresolved
120 (branch merge, don't forget to commit)
120 (branch merge, don't forget to commit)
121 changeset: 1:802f095af299
121 changeset: 1:802f095af299
122 user: test
122 user: test
123 date: Mon Jan 12 13:46:40 1970 +0000
123 date: Mon Jan 12 13:46:40 1970 +0000
124 summary: 2
124 summary: 2
125
125
126 changeset: 2:030602aee63d
126 changeset: 2:030602aee63d
127 tag: tip
127 tag: tip
128 parent: 0:33aaa84a386b
128 parent: 0:33aaa84a386b
129 user: test
129 user: test
130 date: Mon Jan 12 13:46:40 1970 +0000
130 date: Mon Jan 12 13:46:40 1970 +0000
131 summary: 3
131 summary: 3
132
132
133 diff -r 802f095af299 a
133 diff -r 802f095af299 a
134 --- a/a
134 --- a/a
135 +++ b/a
135 +++ b/a
136 @@ -1,1 +1,1 @@ a2
136 @@ -1,1 +1,1 @@ a2
137 -a2
137 -a2
138 +abc
138 +abc
General Comments 0
You need to be logged in to leave comments. Login now