##// END OF EJS Templates
Added ability to clone from a local repository to a (new) remote one....
Sean Meiners -
r2549:e1831f06 default
parent child Browse files
Show More
@@ -1,3554 +1,3570 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 mdiff random signal tempfile time")
13 demandload(globals(), "fnmatch 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(), "archival cStringIO changegroup email.Parser")
15 demandload(globals(), "archival cStringIO changegroup email.Parser")
16 demandload(globals(), "hgweb.server sshserver")
16 demandload(globals(), "hgweb.server sshserver")
17
17
18 class UnknownCommand(Exception):
18 class UnknownCommand(Exception):
19 """Exception raised if command is not in the command table."""
19 """Exception raised if command is not in the command table."""
20 class AmbiguousCommand(Exception):
20 class AmbiguousCommand(Exception):
21 """Exception raised if command shortcut matches more than one command."""
21 """Exception raised if command shortcut matches more than one command."""
22
22
23 def bail_if_changed(repo):
23 def bail_if_changed(repo):
24 modified, added, removed, deleted, unknown = repo.changes()
24 modified, added, removed, deleted, unknown = repo.changes()
25 if modified or added or removed or deleted:
25 if modified or added or removed or deleted:
26 raise util.Abort(_("outstanding uncommitted changes"))
26 raise util.Abort(_("outstanding uncommitted changes"))
27
27
28 def filterfiles(filters, files):
28 def filterfiles(filters, files):
29 l = [x for x in files if x in filters]
29 l = [x for x in files if x in filters]
30
30
31 for t in filters:
31 for t in filters:
32 if t and t[-1] != "/":
32 if t and t[-1] != "/":
33 t += "/"
33 t += "/"
34 l += [x for x in files if x.startswith(t)]
34 l += [x for x in files if x.startswith(t)]
35 return l
35 return l
36
36
37 def relpath(repo, args):
37 def relpath(repo, args):
38 cwd = repo.getcwd()
38 cwd = repo.getcwd()
39 if cwd:
39 if cwd:
40 return [util.normpath(os.path.join(cwd, x)) for x in args]
40 return [util.normpath(os.path.join(cwd, x)) for x in args]
41 return args
41 return args
42
42
43 def matchpats(repo, pats=[], opts={}, head=''):
43 def matchpats(repo, pats=[], opts={}, head=''):
44 cwd = repo.getcwd()
44 cwd = repo.getcwd()
45 if not pats and cwd:
45 if not pats and cwd:
46 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
46 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
47 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
47 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
48 cwd = ''
48 cwd = ''
49 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
49 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
50 opts.get('exclude'), head)
50 opts.get('exclude'), head)
51
51
52 def makewalk(repo, pats, opts, node=None, head='', badmatch=None):
52 def makewalk(repo, pats, opts, node=None, head='', badmatch=None):
53 files, matchfn, anypats = matchpats(repo, pats, opts, head)
53 files, matchfn, anypats = matchpats(repo, pats, opts, head)
54 exact = dict(zip(files, files))
54 exact = dict(zip(files, files))
55 def walk():
55 def walk():
56 for src, fn in repo.walk(node=node, files=files, match=matchfn,
56 for src, fn in repo.walk(node=node, files=files, match=matchfn,
57 badmatch=badmatch):
57 badmatch=badmatch):
58 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
58 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
59 return files, matchfn, walk()
59 return files, matchfn, walk()
60
60
61 def walk(repo, pats, opts, node=None, head='', badmatch=None):
61 def walk(repo, pats, opts, node=None, head='', badmatch=None):
62 files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch)
62 files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch)
63 for r in results:
63 for r in results:
64 yield r
64 yield r
65
65
66 def walkchangerevs(ui, repo, pats, opts):
66 def walkchangerevs(ui, repo, pats, opts):
67 '''Iterate over files and the revs they changed in.
67 '''Iterate over files and the revs they changed in.
68
68
69 Callers most commonly need to iterate backwards over the history
69 Callers most commonly need to iterate backwards over the history
70 it is interested in. Doing so has awful (quadratic-looking)
70 it is interested in. Doing so has awful (quadratic-looking)
71 performance, so we use iterators in a "windowed" way.
71 performance, so we use iterators in a "windowed" way.
72
72
73 We walk a window of revisions in the desired order. Within the
73 We walk a window of revisions in the desired order. Within the
74 window, we first walk forwards to gather data, then in the desired
74 window, we first walk forwards to gather data, then in the desired
75 order (usually backwards) to display it.
75 order (usually backwards) to display it.
76
76
77 This function returns an (iterator, getchange, matchfn) tuple. The
77 This function returns an (iterator, getchange, matchfn) tuple. The
78 getchange function returns the changelog entry for a numeric
78 getchange function returns the changelog entry for a numeric
79 revision. The iterator yields 3-tuples. They will be of one of
79 revision. The iterator yields 3-tuples. They will be of one of
80 the following forms:
80 the following forms:
81
81
82 "window", incrementing, lastrev: stepping through a window,
82 "window", incrementing, lastrev: stepping through a window,
83 positive if walking forwards through revs, last rev in the
83 positive if walking forwards through revs, last rev in the
84 sequence iterated over - use to reset state for the current window
84 sequence iterated over - use to reset state for the current window
85
85
86 "add", rev, fns: out-of-order traversal of the given file names
86 "add", rev, fns: out-of-order traversal of the given file names
87 fns, which changed during revision rev - use to gather data for
87 fns, which changed during revision rev - use to gather data for
88 possible display
88 possible display
89
89
90 "iter", rev, None: in-order traversal of the revs earlier iterated
90 "iter", rev, None: in-order traversal of the revs earlier iterated
91 over with "add" - use to display data'''
91 over with "add" - use to display data'''
92
92
93 def increasing_windows(start, end, windowsize=8, sizelimit=512):
93 def increasing_windows(start, end, windowsize=8, sizelimit=512):
94 if start < end:
94 if start < end:
95 while start < end:
95 while start < end:
96 yield start, min(windowsize, end-start)
96 yield start, min(windowsize, end-start)
97 start += windowsize
97 start += windowsize
98 if windowsize < sizelimit:
98 if windowsize < sizelimit:
99 windowsize *= 2
99 windowsize *= 2
100 else:
100 else:
101 while start > end:
101 while start > end:
102 yield start, min(windowsize, start-end-1)
102 yield start, min(windowsize, start-end-1)
103 start -= windowsize
103 start -= windowsize
104 if windowsize < sizelimit:
104 if windowsize < sizelimit:
105 windowsize *= 2
105 windowsize *= 2
106
106
107
107
108 files, matchfn, anypats = matchpats(repo, pats, opts)
108 files, matchfn, anypats = matchpats(repo, pats, opts)
109
109
110 if repo.changelog.count() == 0:
110 if repo.changelog.count() == 0:
111 return [], False, matchfn
111 return [], False, matchfn
112
112
113 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
113 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
114 wanted = {}
114 wanted = {}
115 slowpath = anypats
115 slowpath = anypats
116 fncache = {}
116 fncache = {}
117
117
118 chcache = {}
118 chcache = {}
119 def getchange(rev):
119 def getchange(rev):
120 ch = chcache.get(rev)
120 ch = chcache.get(rev)
121 if ch is None:
121 if ch is None:
122 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
122 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
123 return ch
123 return ch
124
124
125 if not slowpath and not files:
125 if not slowpath and not files:
126 # No files, no patterns. Display all revs.
126 # No files, no patterns. Display all revs.
127 wanted = dict(zip(revs, revs))
127 wanted = dict(zip(revs, revs))
128 if not slowpath:
128 if not slowpath:
129 # Only files, no patterns. Check the history of each file.
129 # Only files, no patterns. Check the history of each file.
130 def filerevgen(filelog):
130 def filerevgen(filelog):
131 for i, window in increasing_windows(filelog.count()-1, -1):
131 for i, window in increasing_windows(filelog.count()-1, -1):
132 revs = []
132 revs = []
133 for j in xrange(i - window, i + 1):
133 for j in xrange(i - window, i + 1):
134 revs.append(filelog.linkrev(filelog.node(j)))
134 revs.append(filelog.linkrev(filelog.node(j)))
135 revs.reverse()
135 revs.reverse()
136 for rev in revs:
136 for rev in revs:
137 yield rev
137 yield rev
138
138
139 minrev, maxrev = min(revs), max(revs)
139 minrev, maxrev = min(revs), max(revs)
140 for file_ in files:
140 for file_ in files:
141 filelog = repo.file(file_)
141 filelog = repo.file(file_)
142 # A zero count may be a directory or deleted file, so
142 # A zero count may be a directory or deleted file, so
143 # try to find matching entries on the slow path.
143 # try to find matching entries on the slow path.
144 if filelog.count() == 0:
144 if filelog.count() == 0:
145 slowpath = True
145 slowpath = True
146 break
146 break
147 for rev in filerevgen(filelog):
147 for rev in filerevgen(filelog):
148 if rev <= maxrev:
148 if rev <= maxrev:
149 if rev < minrev:
149 if rev < minrev:
150 break
150 break
151 fncache.setdefault(rev, [])
151 fncache.setdefault(rev, [])
152 fncache[rev].append(file_)
152 fncache[rev].append(file_)
153 wanted[rev] = 1
153 wanted[rev] = 1
154 if slowpath:
154 if slowpath:
155 # The slow path checks files modified in every changeset.
155 # The slow path checks files modified in every changeset.
156 def changerevgen():
156 def changerevgen():
157 for i, window in increasing_windows(repo.changelog.count()-1, -1):
157 for i, window in increasing_windows(repo.changelog.count()-1, -1):
158 for j in xrange(i - window, i + 1):
158 for j in xrange(i - window, i + 1):
159 yield j, getchange(j)[3]
159 yield j, getchange(j)[3]
160
160
161 for rev, changefiles in changerevgen():
161 for rev, changefiles in changerevgen():
162 matches = filter(matchfn, changefiles)
162 matches = filter(matchfn, changefiles)
163 if matches:
163 if matches:
164 fncache[rev] = matches
164 fncache[rev] = matches
165 wanted[rev] = 1
165 wanted[rev] = 1
166
166
167 def iterate():
167 def iterate():
168 for i, window in increasing_windows(0, len(revs)):
168 for i, window in increasing_windows(0, len(revs)):
169 yield 'window', revs[0] < revs[-1], revs[-1]
169 yield 'window', revs[0] < revs[-1], revs[-1]
170 nrevs = [rev for rev in revs[i:i+window]
170 nrevs = [rev for rev in revs[i:i+window]
171 if rev in wanted]
171 if rev in wanted]
172 srevs = list(nrevs)
172 srevs = list(nrevs)
173 srevs.sort()
173 srevs.sort()
174 for rev in srevs:
174 for rev in srevs:
175 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
175 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
176 yield 'add', rev, fns
176 yield 'add', rev, fns
177 for rev in nrevs:
177 for rev in nrevs:
178 yield 'iter', rev, None
178 yield 'iter', rev, None
179 return iterate(), getchange, matchfn
179 return iterate(), getchange, matchfn
180
180
181 revrangesep = ':'
181 revrangesep = ':'
182
182
183 def revfix(repo, val, defval):
183 def revfix(repo, val, defval):
184 '''turn user-level id of changeset into rev number.
184 '''turn user-level id of changeset into rev number.
185 user-level id can be tag, changeset, rev number, or negative rev
185 user-level id can be tag, changeset, rev number, or negative rev
186 number relative to number of revs (-1 is tip, etc).'''
186 number relative to number of revs (-1 is tip, etc).'''
187 if not val:
187 if not val:
188 return defval
188 return defval
189 try:
189 try:
190 num = int(val)
190 num = int(val)
191 if str(num) != val:
191 if str(num) != val:
192 raise ValueError
192 raise ValueError
193 if num < 0:
193 if num < 0:
194 num += repo.changelog.count()
194 num += repo.changelog.count()
195 if num < 0:
195 if num < 0:
196 num = 0
196 num = 0
197 elif num >= repo.changelog.count():
197 elif num >= repo.changelog.count():
198 raise ValueError
198 raise ValueError
199 except ValueError:
199 except ValueError:
200 try:
200 try:
201 num = repo.changelog.rev(repo.lookup(val))
201 num = repo.changelog.rev(repo.lookup(val))
202 except KeyError:
202 except KeyError:
203 raise util.Abort(_('invalid revision identifier %s'), val)
203 raise util.Abort(_('invalid revision identifier %s'), val)
204 return num
204 return num
205
205
206 def revpair(ui, repo, revs):
206 def revpair(ui, repo, revs):
207 '''return pair of nodes, given list of revisions. second item can
207 '''return pair of nodes, given list of revisions. second item can
208 be None, meaning use working dir.'''
208 be None, meaning use working dir.'''
209 if not revs:
209 if not revs:
210 return repo.dirstate.parents()[0], None
210 return repo.dirstate.parents()[0], None
211 end = None
211 end = None
212 if len(revs) == 1:
212 if len(revs) == 1:
213 start = revs[0]
213 start = revs[0]
214 if revrangesep in start:
214 if revrangesep in start:
215 start, end = start.split(revrangesep, 1)
215 start, end = start.split(revrangesep, 1)
216 start = revfix(repo, start, 0)
216 start = revfix(repo, start, 0)
217 end = revfix(repo, end, repo.changelog.count() - 1)
217 end = revfix(repo, end, repo.changelog.count() - 1)
218 else:
218 else:
219 start = revfix(repo, start, None)
219 start = revfix(repo, start, None)
220 elif len(revs) == 2:
220 elif len(revs) == 2:
221 if revrangesep in revs[0] or revrangesep in revs[1]:
221 if revrangesep in revs[0] or revrangesep in revs[1]:
222 raise util.Abort(_('too many revisions specified'))
222 raise util.Abort(_('too many revisions specified'))
223 start = revfix(repo, revs[0], None)
223 start = revfix(repo, revs[0], None)
224 end = revfix(repo, revs[1], None)
224 end = revfix(repo, revs[1], None)
225 else:
225 else:
226 raise util.Abort(_('too many revisions specified'))
226 raise util.Abort(_('too many revisions specified'))
227 if end is not None: end = repo.lookup(str(end))
227 if end is not None: end = repo.lookup(str(end))
228 return repo.lookup(str(start)), end
228 return repo.lookup(str(start)), end
229
229
230 def revrange(ui, repo, revs):
230 def revrange(ui, repo, revs):
231 """Yield revision as strings from a list of revision specifications."""
231 """Yield revision as strings from a list of revision specifications."""
232 seen = {}
232 seen = {}
233 for spec in revs:
233 for spec in revs:
234 if spec.find(revrangesep) >= 0:
234 if spec.find(revrangesep) >= 0:
235 start, end = spec.split(revrangesep, 1)
235 start, end = spec.split(revrangesep, 1)
236 start = revfix(repo, start, 0)
236 start = revfix(repo, start, 0)
237 end = revfix(repo, end, repo.changelog.count() - 1)
237 end = revfix(repo, end, repo.changelog.count() - 1)
238 step = start > end and -1 or 1
238 step = start > end and -1 or 1
239 for rev in xrange(start, end+step, step):
239 for rev in xrange(start, end+step, step):
240 if rev in seen:
240 if rev in seen:
241 continue
241 continue
242 seen[rev] = 1
242 seen[rev] = 1
243 yield str(rev)
243 yield str(rev)
244 else:
244 else:
245 rev = revfix(repo, spec, None)
245 rev = revfix(repo, spec, None)
246 if rev in seen:
246 if rev in seen:
247 continue
247 continue
248 seen[rev] = 1
248 seen[rev] = 1
249 yield str(rev)
249 yield str(rev)
250
250
251 def make_filename(repo, r, pat, node=None,
251 def make_filename(repo, r, pat, node=None,
252 total=None, seqno=None, revwidth=None, pathname=None):
252 total=None, seqno=None, revwidth=None, pathname=None):
253 node_expander = {
253 node_expander = {
254 'H': lambda: hex(node),
254 'H': lambda: hex(node),
255 'R': lambda: str(r.rev(node)),
255 'R': lambda: str(r.rev(node)),
256 'h': lambda: short(node),
256 'h': lambda: short(node),
257 }
257 }
258 expander = {
258 expander = {
259 '%': lambda: '%',
259 '%': lambda: '%',
260 'b': lambda: os.path.basename(repo.root),
260 'b': lambda: os.path.basename(repo.root),
261 }
261 }
262
262
263 try:
263 try:
264 if node:
264 if node:
265 expander.update(node_expander)
265 expander.update(node_expander)
266 if node and revwidth is not None:
266 if node and revwidth is not None:
267 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
267 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
268 if total is not None:
268 if total is not None:
269 expander['N'] = lambda: str(total)
269 expander['N'] = lambda: str(total)
270 if seqno is not None:
270 if seqno is not None:
271 expander['n'] = lambda: str(seqno)
271 expander['n'] = lambda: str(seqno)
272 if total is not None and seqno is not None:
272 if total is not None and seqno is not None:
273 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
273 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
274 if pathname is not None:
274 if pathname is not None:
275 expander['s'] = lambda: os.path.basename(pathname)
275 expander['s'] = lambda: os.path.basename(pathname)
276 expander['d'] = lambda: os.path.dirname(pathname) or '.'
276 expander['d'] = lambda: os.path.dirname(pathname) or '.'
277 expander['p'] = lambda: pathname
277 expander['p'] = lambda: pathname
278
278
279 newname = []
279 newname = []
280 patlen = len(pat)
280 patlen = len(pat)
281 i = 0
281 i = 0
282 while i < patlen:
282 while i < patlen:
283 c = pat[i]
283 c = pat[i]
284 if c == '%':
284 if c == '%':
285 i += 1
285 i += 1
286 c = pat[i]
286 c = pat[i]
287 c = expander[c]()
287 c = expander[c]()
288 newname.append(c)
288 newname.append(c)
289 i += 1
289 i += 1
290 return ''.join(newname)
290 return ''.join(newname)
291 except KeyError, inst:
291 except KeyError, inst:
292 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
292 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
293 inst.args[0])
293 inst.args[0])
294
294
295 def make_file(repo, r, pat, node=None,
295 def make_file(repo, r, pat, node=None,
296 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
296 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
297 if not pat or pat == '-':
297 if not pat or pat == '-':
298 return 'w' in mode and sys.stdout or sys.stdin
298 return 'w' in mode and sys.stdout or sys.stdin
299 if hasattr(pat, 'write') and 'w' in mode:
299 if hasattr(pat, 'write') and 'w' in mode:
300 return pat
300 return pat
301 if hasattr(pat, 'read') and 'r' in mode:
301 if hasattr(pat, 'read') and 'r' in mode:
302 return pat
302 return pat
303 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
303 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
304 pathname),
304 pathname),
305 mode)
305 mode)
306
306
307 def write_bundle(cg, filename=None, compress=True):
307 def write_bundle(cg, filename=None, compress=True):
308 """Write a bundle file and return its filename.
308 """Write a bundle file and return its filename.
309
309
310 Existing files will not be overwritten.
310 Existing files will not be overwritten.
311 If no filename is specified, a temporary file is created.
311 If no filename is specified, a temporary file is created.
312 bz2 compression can be turned off.
312 bz2 compression can be turned off.
313 The bundle file will be deleted in case of errors.
313 The bundle file will be deleted in case of errors.
314 """
314 """
315 class nocompress(object):
315 class nocompress(object):
316 def compress(self, x):
316 def compress(self, x):
317 return x
317 return x
318 def flush(self):
318 def flush(self):
319 return ""
319 return ""
320
320
321 fh = None
321 fh = None
322 cleanup = None
322 cleanup = None
323 try:
323 try:
324 if filename:
324 if filename:
325 if os.path.exists(filename):
325 if os.path.exists(filename):
326 raise util.Abort(_("file '%s' already exists"), filename)
326 raise util.Abort(_("file '%s' already exists"), filename)
327 fh = open(filename, "wb")
327 fh = open(filename, "wb")
328 else:
328 else:
329 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
329 fd, filename = tempfile.mkstemp(prefix="hg-bundle-", suffix=".hg")
330 fh = os.fdopen(fd, "wb")
330 fh = os.fdopen(fd, "wb")
331 cleanup = filename
331 cleanup = filename
332
332
333 if compress:
333 if compress:
334 fh.write("HG10")
334 fh.write("HG10")
335 z = bz2.BZ2Compressor(9)
335 z = bz2.BZ2Compressor(9)
336 else:
336 else:
337 fh.write("HG10UN")
337 fh.write("HG10UN")
338 z = nocompress()
338 z = nocompress()
339 # parse the changegroup data, otherwise we will block
339 # parse the changegroup data, otherwise we will block
340 # in case of sshrepo because we don't know the end of the stream
340 # in case of sshrepo because we don't know the end of the stream
341
341
342 # an empty chunkiter is the end of the changegroup
342 # an empty chunkiter is the end of the changegroup
343 empty = False
343 empty = False
344 while not empty:
344 while not empty:
345 empty = True
345 empty = True
346 for chunk in changegroup.chunkiter(cg):
346 for chunk in changegroup.chunkiter(cg):
347 empty = False
347 empty = False
348 fh.write(z.compress(changegroup.genchunk(chunk)))
348 fh.write(z.compress(changegroup.genchunk(chunk)))
349 fh.write(z.compress(changegroup.closechunk()))
349 fh.write(z.compress(changegroup.closechunk()))
350 fh.write(z.flush())
350 fh.write(z.flush())
351 cleanup = None
351 cleanup = None
352 return filename
352 return filename
353 finally:
353 finally:
354 if fh is not None:
354 if fh is not None:
355 fh.close()
355 fh.close()
356 if cleanup is not None:
356 if cleanup is not None:
357 os.unlink(cleanup)
357 os.unlink(cleanup)
358
358
359 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
359 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
360 changes=None, text=False, opts={}):
360 changes=None, text=False, opts={}):
361 if not node1:
361 if not node1:
362 node1 = repo.dirstate.parents()[0]
362 node1 = repo.dirstate.parents()[0]
363 # reading the data for node1 early allows it to play nicely
363 # reading the data for node1 early allows it to play nicely
364 # with repo.changes and the revlog cache.
364 # with repo.changes and the revlog cache.
365 change = repo.changelog.read(node1)
365 change = repo.changelog.read(node1)
366 mmap = repo.manifest.read(change[0])
366 mmap = repo.manifest.read(change[0])
367 date1 = util.datestr(change[2])
367 date1 = util.datestr(change[2])
368
368
369 if not changes:
369 if not changes:
370 changes = repo.changes(node1, node2, files, match=match)
370 changes = repo.changes(node1, node2, files, match=match)
371 modified, added, removed, deleted, unknown = changes
371 modified, added, removed, deleted, unknown = changes
372 if files:
372 if files:
373 modified, added, removed = map(lambda x: filterfiles(files, x),
373 modified, added, removed = map(lambda x: filterfiles(files, x),
374 (modified, added, removed))
374 (modified, added, removed))
375
375
376 if not modified and not added and not removed:
376 if not modified and not added and not removed:
377 return
377 return
378
378
379 if node2:
379 if node2:
380 change = repo.changelog.read(node2)
380 change = repo.changelog.read(node2)
381 mmap2 = repo.manifest.read(change[0])
381 mmap2 = repo.manifest.read(change[0])
382 _date2 = util.datestr(change[2])
382 _date2 = util.datestr(change[2])
383 def date2(f):
383 def date2(f):
384 return _date2
384 return _date2
385 def read(f):
385 def read(f):
386 return repo.file(f).read(mmap2[f])
386 return repo.file(f).read(mmap2[f])
387 else:
387 else:
388 tz = util.makedate()[1]
388 tz = util.makedate()[1]
389 _date2 = util.datestr()
389 _date2 = util.datestr()
390 def date2(f):
390 def date2(f):
391 try:
391 try:
392 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
392 return util.datestr((os.lstat(repo.wjoin(f)).st_mtime, tz))
393 except OSError, err:
393 except OSError, err:
394 if err.errno != errno.ENOENT: raise
394 if err.errno != errno.ENOENT: raise
395 return _date2
395 return _date2
396 def read(f):
396 def read(f):
397 return repo.wread(f)
397 return repo.wread(f)
398
398
399 if ui.quiet:
399 if ui.quiet:
400 r = None
400 r = None
401 else:
401 else:
402 hexfunc = ui.verbose and hex or short
402 hexfunc = ui.verbose and hex or short
403 r = [hexfunc(node) for node in [node1, node2] if node]
403 r = [hexfunc(node) for node in [node1, node2] if node]
404
404
405 diffopts = ui.diffopts()
405 diffopts = ui.diffopts()
406 showfunc = opts.get('show_function') or diffopts['showfunc']
406 showfunc = opts.get('show_function') or diffopts['showfunc']
407 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
407 ignorews = opts.get('ignore_all_space') or diffopts['ignorews']
408 for f in modified:
408 for f in modified:
409 to = None
409 to = None
410 if f in mmap:
410 if f in mmap:
411 to = repo.file(f).read(mmap[f])
411 to = repo.file(f).read(mmap[f])
412 tn = read(f)
412 tn = read(f)
413 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
413 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
414 showfunc=showfunc, ignorews=ignorews))
414 showfunc=showfunc, ignorews=ignorews))
415 for f in added:
415 for f in added:
416 to = None
416 to = None
417 tn = read(f)
417 tn = read(f)
418 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
418 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
419 showfunc=showfunc, ignorews=ignorews))
419 showfunc=showfunc, ignorews=ignorews))
420 for f in removed:
420 for f in removed:
421 to = repo.file(f).read(mmap[f])
421 to = repo.file(f).read(mmap[f])
422 tn = None
422 tn = None
423 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
423 fp.write(mdiff.unidiff(to, date1, tn, date2(f), f, r, text=text,
424 showfunc=showfunc, ignorews=ignorews))
424 showfunc=showfunc, ignorews=ignorews))
425
425
426 def trimuser(ui, name, rev, revcache):
426 def trimuser(ui, name, rev, revcache):
427 """trim the name of the user who committed a change"""
427 """trim the name of the user who committed a change"""
428 user = revcache.get(rev)
428 user = revcache.get(rev)
429 if user is None:
429 if user is None:
430 user = revcache[rev] = ui.shortuser(name)
430 user = revcache[rev] = ui.shortuser(name)
431 return user
431 return user
432
432
433 class changeset_printer(object):
433 class changeset_printer(object):
434 '''show changeset information when templating not requested.'''
434 '''show changeset information when templating not requested.'''
435
435
436 def __init__(self, ui, repo):
436 def __init__(self, ui, repo):
437 self.ui = ui
437 self.ui = ui
438 self.repo = repo
438 self.repo = repo
439
439
440 def show(self, rev=0, changenode=None, brinfo=None):
440 def show(self, rev=0, changenode=None, brinfo=None):
441 '''show a single changeset or file revision'''
441 '''show a single changeset or file revision'''
442 log = self.repo.changelog
442 log = self.repo.changelog
443 if changenode is None:
443 if changenode is None:
444 changenode = log.node(rev)
444 changenode = log.node(rev)
445 elif not rev:
445 elif not rev:
446 rev = log.rev(changenode)
446 rev = log.rev(changenode)
447
447
448 if self.ui.quiet:
448 if self.ui.quiet:
449 self.ui.write("%d:%s\n" % (rev, short(changenode)))
449 self.ui.write("%d:%s\n" % (rev, short(changenode)))
450 return
450 return
451
451
452 changes = log.read(changenode)
452 changes = log.read(changenode)
453 date = util.datestr(changes[2])
453 date = util.datestr(changes[2])
454
454
455 parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p))
455 parents = [(log.rev(p), self.ui.verbose and hex(p) or short(p))
456 for p in log.parents(changenode)
456 for p in log.parents(changenode)
457 if self.ui.debugflag or p != nullid]
457 if self.ui.debugflag or p != nullid]
458 if (not self.ui.debugflag and len(parents) == 1 and
458 if (not self.ui.debugflag and len(parents) == 1 and
459 parents[0][0] == rev-1):
459 parents[0][0] == rev-1):
460 parents = []
460 parents = []
461
461
462 if self.ui.verbose:
462 if self.ui.verbose:
463 self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
463 self.ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
464 else:
464 else:
465 self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
465 self.ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
466
466
467 for tag in self.repo.nodetags(changenode):
467 for tag in self.repo.nodetags(changenode):
468 self.ui.status(_("tag: %s\n") % tag)
468 self.ui.status(_("tag: %s\n") % tag)
469 for parent in parents:
469 for parent in parents:
470 self.ui.write(_("parent: %d:%s\n") % parent)
470 self.ui.write(_("parent: %d:%s\n") % parent)
471
471
472 if brinfo and changenode in brinfo:
472 if brinfo and changenode in brinfo:
473 br = brinfo[changenode]
473 br = brinfo[changenode]
474 self.ui.write(_("branch: %s\n") % " ".join(br))
474 self.ui.write(_("branch: %s\n") % " ".join(br))
475
475
476 self.ui.debug(_("manifest: %d:%s\n") %
476 self.ui.debug(_("manifest: %d:%s\n") %
477 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
477 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
478 self.ui.status(_("user: %s\n") % changes[1])
478 self.ui.status(_("user: %s\n") % changes[1])
479 self.ui.status(_("date: %s\n") % date)
479 self.ui.status(_("date: %s\n") % date)
480
480
481 if self.ui.debugflag:
481 if self.ui.debugflag:
482 files = self.repo.changes(log.parents(changenode)[0], changenode)
482 files = self.repo.changes(log.parents(changenode)[0], changenode)
483 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
483 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
484 files):
484 files):
485 if value:
485 if value:
486 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
486 self.ui.note("%-12s %s\n" % (key, " ".join(value)))
487 else:
487 else:
488 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
488 self.ui.note(_("files: %s\n") % " ".join(changes[3]))
489
489
490 description = changes[4].strip()
490 description = changes[4].strip()
491 if description:
491 if description:
492 if self.ui.verbose:
492 if self.ui.verbose:
493 self.ui.status(_("description:\n"))
493 self.ui.status(_("description:\n"))
494 self.ui.status(description)
494 self.ui.status(description)
495 self.ui.status("\n\n")
495 self.ui.status("\n\n")
496 else:
496 else:
497 self.ui.status(_("summary: %s\n") %
497 self.ui.status(_("summary: %s\n") %
498 description.splitlines()[0])
498 description.splitlines()[0])
499 self.ui.status("\n")
499 self.ui.status("\n")
500
500
501 def show_changeset(ui, repo, opts):
501 def show_changeset(ui, repo, opts):
502 '''show one changeset. uses template or regular display. caller
502 '''show one changeset. uses template or regular display. caller
503 can pass in 'style' and 'template' options in opts.'''
503 can pass in 'style' and 'template' options in opts.'''
504
504
505 tmpl = opts.get('template')
505 tmpl = opts.get('template')
506 if tmpl:
506 if tmpl:
507 tmpl = templater.parsestring(tmpl, quoted=False)
507 tmpl = templater.parsestring(tmpl, quoted=False)
508 else:
508 else:
509 tmpl = ui.config('ui', 'logtemplate')
509 tmpl = ui.config('ui', 'logtemplate')
510 if tmpl: tmpl = templater.parsestring(tmpl)
510 if tmpl: tmpl = templater.parsestring(tmpl)
511 mapfile = opts.get('style') or ui.config('ui', 'style')
511 mapfile = opts.get('style') or ui.config('ui', 'style')
512 if tmpl or mapfile:
512 if tmpl or mapfile:
513 if mapfile:
513 if mapfile:
514 if not os.path.isfile(mapfile):
514 if not os.path.isfile(mapfile):
515 mapname = templater.templatepath('map-cmdline.' + mapfile)
515 mapname = templater.templatepath('map-cmdline.' + mapfile)
516 if not mapname: mapname = templater.templatepath(mapfile)
516 if not mapname: mapname = templater.templatepath(mapfile)
517 if mapname: mapfile = mapname
517 if mapname: mapfile = mapname
518 try:
518 try:
519 t = templater.changeset_templater(ui, repo, mapfile)
519 t = templater.changeset_templater(ui, repo, mapfile)
520 except SyntaxError, inst:
520 except SyntaxError, inst:
521 raise util.Abort(inst.args[0])
521 raise util.Abort(inst.args[0])
522 if tmpl: t.use_template(tmpl)
522 if tmpl: t.use_template(tmpl)
523 return t
523 return t
524 return changeset_printer(ui, repo)
524 return changeset_printer(ui, repo)
525
525
526 def show_version(ui):
526 def show_version(ui):
527 """output version and copyright information"""
527 """output version and copyright information"""
528 ui.write(_("Mercurial Distributed SCM (version %s)\n")
528 ui.write(_("Mercurial Distributed SCM (version %s)\n")
529 % version.get_version())
529 % version.get_version())
530 ui.status(_(
530 ui.status(_(
531 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
531 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
532 "This is free software; see the source for copying conditions. "
532 "This is free software; see the source for copying conditions. "
533 "There is NO\nwarranty; "
533 "There is NO\nwarranty; "
534 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
534 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
535 ))
535 ))
536
536
537 def help_(ui, cmd=None, with_version=False):
537 def help_(ui, cmd=None, with_version=False):
538 """show help for a given command or all commands"""
538 """show help for a given command or all commands"""
539 option_lists = []
539 option_lists = []
540 if cmd and cmd != 'shortlist':
540 if cmd and cmd != 'shortlist':
541 if with_version:
541 if with_version:
542 show_version(ui)
542 show_version(ui)
543 ui.write('\n')
543 ui.write('\n')
544 aliases, i = find(cmd)
544 aliases, i = find(cmd)
545 # synopsis
545 # synopsis
546 ui.write("%s\n\n" % i[2])
546 ui.write("%s\n\n" % i[2])
547
547
548 # description
548 # description
549 doc = i[0].__doc__
549 doc = i[0].__doc__
550 if not doc:
550 if not doc:
551 doc = _("(No help text available)")
551 doc = _("(No help text available)")
552 if ui.quiet:
552 if ui.quiet:
553 doc = doc.splitlines(0)[0]
553 doc = doc.splitlines(0)[0]
554 ui.write("%s\n" % doc.rstrip())
554 ui.write("%s\n" % doc.rstrip())
555
555
556 if not ui.quiet:
556 if not ui.quiet:
557 # aliases
557 # aliases
558 if len(aliases) > 1:
558 if len(aliases) > 1:
559 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
559 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
560
560
561 # options
561 # options
562 if i[1]:
562 if i[1]:
563 option_lists.append(("options", i[1]))
563 option_lists.append(("options", i[1]))
564
564
565 else:
565 else:
566 # program name
566 # program name
567 if ui.verbose or with_version:
567 if ui.verbose or with_version:
568 show_version(ui)
568 show_version(ui)
569 else:
569 else:
570 ui.status(_("Mercurial Distributed SCM\n"))
570 ui.status(_("Mercurial Distributed SCM\n"))
571 ui.status('\n')
571 ui.status('\n')
572
572
573 # list of commands
573 # list of commands
574 if cmd == "shortlist":
574 if cmd == "shortlist":
575 ui.status(_('basic commands (use "hg help" '
575 ui.status(_('basic commands (use "hg help" '
576 'for the full list or option "-v" for details):\n\n'))
576 'for the full list or option "-v" for details):\n\n'))
577 elif ui.verbose:
577 elif ui.verbose:
578 ui.status(_('list of commands:\n\n'))
578 ui.status(_('list of commands:\n\n'))
579 else:
579 else:
580 ui.status(_('list of commands (use "hg help -v" '
580 ui.status(_('list of commands (use "hg help -v" '
581 'to show aliases and global options):\n\n'))
581 'to show aliases and global options):\n\n'))
582
582
583 h = {}
583 h = {}
584 cmds = {}
584 cmds = {}
585 for c, e in table.items():
585 for c, e in table.items():
586 f = c.split("|")[0]
586 f = c.split("|")[0]
587 if cmd == "shortlist" and not f.startswith("^"):
587 if cmd == "shortlist" and not f.startswith("^"):
588 continue
588 continue
589 f = f.lstrip("^")
589 f = f.lstrip("^")
590 if not ui.debugflag and f.startswith("debug"):
590 if not ui.debugflag and f.startswith("debug"):
591 continue
591 continue
592 doc = e[0].__doc__
592 doc = e[0].__doc__
593 if not doc:
593 if not doc:
594 doc = _("(No help text available)")
594 doc = _("(No help text available)")
595 h[f] = doc.splitlines(0)[0].rstrip()
595 h[f] = doc.splitlines(0)[0].rstrip()
596 cmds[f] = c.lstrip("^")
596 cmds[f] = c.lstrip("^")
597
597
598 fns = h.keys()
598 fns = h.keys()
599 fns.sort()
599 fns.sort()
600 m = max(map(len, fns))
600 m = max(map(len, fns))
601 for f in fns:
601 for f in fns:
602 if ui.verbose:
602 if ui.verbose:
603 commands = cmds[f].replace("|",", ")
603 commands = cmds[f].replace("|",", ")
604 ui.write(" %s:\n %s\n"%(commands, h[f]))
604 ui.write(" %s:\n %s\n"%(commands, h[f]))
605 else:
605 else:
606 ui.write(' %-*s %s\n' % (m, f, h[f]))
606 ui.write(' %-*s %s\n' % (m, f, h[f]))
607
607
608 # global options
608 # global options
609 if ui.verbose:
609 if ui.verbose:
610 option_lists.append(("global options", globalopts))
610 option_lists.append(("global options", globalopts))
611
611
612 # list all option lists
612 # list all option lists
613 opt_output = []
613 opt_output = []
614 for title, options in option_lists:
614 for title, options in option_lists:
615 opt_output.append(("\n%s:\n" % title, None))
615 opt_output.append(("\n%s:\n" % title, None))
616 for shortopt, longopt, default, desc in options:
616 for shortopt, longopt, default, desc in options:
617 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
617 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
618 longopt and " --%s" % longopt),
618 longopt and " --%s" % longopt),
619 "%s%s" % (desc,
619 "%s%s" % (desc,
620 default
620 default
621 and _(" (default: %s)") % default
621 and _(" (default: %s)") % default
622 or "")))
622 or "")))
623
623
624 if opt_output:
624 if opt_output:
625 opts_len = max([len(line[0]) for line in opt_output if line[1]])
625 opts_len = max([len(line[0]) for line in opt_output if line[1]])
626 for first, second in opt_output:
626 for first, second in opt_output:
627 if second:
627 if second:
628 ui.write(" %-*s %s\n" % (opts_len, first, second))
628 ui.write(" %-*s %s\n" % (opts_len, first, second))
629 else:
629 else:
630 ui.write("%s\n" % first)
630 ui.write("%s\n" % first)
631
631
632 # Commands start here, listed alphabetically
632 # Commands start here, listed alphabetically
633
633
634 def add(ui, repo, *pats, **opts):
634 def add(ui, repo, *pats, **opts):
635 """add the specified files on the next commit
635 """add the specified files on the next commit
636
636
637 Schedule files to be version controlled and added to the repository.
637 Schedule files to be version controlled and added to the repository.
638
638
639 The files will be added to the repository at the next commit.
639 The files will be added to the repository at the next commit.
640
640
641 If no names are given, add all files in the repository.
641 If no names are given, add all files in the repository.
642 """
642 """
643
643
644 names = []
644 names = []
645 for src, abs, rel, exact in walk(repo, pats, opts):
645 for src, abs, rel, exact in walk(repo, pats, opts):
646 if exact:
646 if exact:
647 if ui.verbose:
647 if ui.verbose:
648 ui.status(_('adding %s\n') % rel)
648 ui.status(_('adding %s\n') % rel)
649 names.append(abs)
649 names.append(abs)
650 elif repo.dirstate.state(abs) == '?':
650 elif repo.dirstate.state(abs) == '?':
651 ui.status(_('adding %s\n') % rel)
651 ui.status(_('adding %s\n') % rel)
652 names.append(abs)
652 names.append(abs)
653 if not opts.get('dry_run'):
653 if not opts.get('dry_run'):
654 repo.add(names)
654 repo.add(names)
655
655
656 def addremove(ui, repo, *pats, **opts):
656 def addremove(ui, repo, *pats, **opts):
657 """add all new files, delete all missing files (DEPRECATED)
657 """add all new files, delete all missing files (DEPRECATED)
658
658
659 (DEPRECATED)
659 (DEPRECATED)
660 Add all new files and remove all missing files from the repository.
660 Add all new files and remove all missing files from the repository.
661
661
662 New files are ignored if they match any of the patterns in .hgignore. As
662 New files are ignored if they match any of the patterns in .hgignore. As
663 with add, these changes take effect at the next commit.
663 with add, these changes take effect at the next commit.
664
664
665 This command is now deprecated and will be removed in a future
665 This command is now deprecated and will be removed in a future
666 release. Please use add and remove --after instead.
666 release. Please use add and remove --after instead.
667 """
667 """
668 ui.warn(_('(the addremove command is deprecated; use add and remove '
668 ui.warn(_('(the addremove command is deprecated; use add and remove '
669 '--after instead)\n'))
669 '--after instead)\n'))
670 return addremove_lock(ui, repo, pats, opts)
670 return addremove_lock(ui, repo, pats, opts)
671
671
672 def addremove_lock(ui, repo, pats, opts, wlock=None):
672 def addremove_lock(ui, repo, pats, opts, wlock=None):
673 add, remove = [], []
673 add, remove = [], []
674 for src, abs, rel, exact in walk(repo, pats, opts):
674 for src, abs, rel, exact in walk(repo, pats, opts):
675 if src == 'f' and repo.dirstate.state(abs) == '?':
675 if src == 'f' and repo.dirstate.state(abs) == '?':
676 add.append(abs)
676 add.append(abs)
677 if ui.verbose or not exact:
677 if ui.verbose or not exact:
678 ui.status(_('adding %s\n') % ((pats and rel) or abs))
678 ui.status(_('adding %s\n') % ((pats and rel) or abs))
679 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
679 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
680 remove.append(abs)
680 remove.append(abs)
681 if ui.verbose or not exact:
681 if ui.verbose or not exact:
682 ui.status(_('removing %s\n') % ((pats and rel) or abs))
682 ui.status(_('removing %s\n') % ((pats and rel) or abs))
683 if not opts.get('dry_run'):
683 if not opts.get('dry_run'):
684 repo.add(add, wlock=wlock)
684 repo.add(add, wlock=wlock)
685 repo.remove(remove, wlock=wlock)
685 repo.remove(remove, wlock=wlock)
686
686
687 def annotate(ui, repo, *pats, **opts):
687 def annotate(ui, repo, *pats, **opts):
688 """show changeset information per file line
688 """show changeset information per file line
689
689
690 List changes in files, showing the revision id responsible for each line
690 List changes in files, showing the revision id responsible for each line
691
691
692 This command is useful to discover who did a change or when a change took
692 This command is useful to discover who did a change or when a change took
693 place.
693 place.
694
694
695 Without the -a option, annotate will avoid processing files it
695 Without the -a option, annotate will avoid processing files it
696 detects as binary. With -a, annotate will generate an annotation
696 detects as binary. With -a, annotate will generate an annotation
697 anyway, probably with undesirable results.
697 anyway, probably with undesirable results.
698 """
698 """
699 def getnode(rev):
699 def getnode(rev):
700 return short(repo.changelog.node(rev))
700 return short(repo.changelog.node(rev))
701
701
702 ucache = {}
702 ucache = {}
703 def getname(rev):
703 def getname(rev):
704 cl = repo.changelog.read(repo.changelog.node(rev))
704 cl = repo.changelog.read(repo.changelog.node(rev))
705 return trimuser(ui, cl[1], rev, ucache)
705 return trimuser(ui, cl[1], rev, ucache)
706
706
707 dcache = {}
707 dcache = {}
708 def getdate(rev):
708 def getdate(rev):
709 datestr = dcache.get(rev)
709 datestr = dcache.get(rev)
710 if datestr is None:
710 if datestr is None:
711 cl = repo.changelog.read(repo.changelog.node(rev))
711 cl = repo.changelog.read(repo.changelog.node(rev))
712 datestr = dcache[rev] = util.datestr(cl[2])
712 datestr = dcache[rev] = util.datestr(cl[2])
713 return datestr
713 return datestr
714
714
715 if not pats:
715 if not pats:
716 raise util.Abort(_('at least one file name or pattern required'))
716 raise util.Abort(_('at least one file name or pattern required'))
717
717
718 opmap = [['user', getname], ['number', str], ['changeset', getnode],
718 opmap = [['user', getname], ['number', str], ['changeset', getnode],
719 ['date', getdate]]
719 ['date', getdate]]
720 if not opts['user'] and not opts['changeset'] and not opts['date']:
720 if not opts['user'] and not opts['changeset'] and not opts['date']:
721 opts['number'] = 1
721 opts['number'] = 1
722
722
723 if opts['rev']:
723 if opts['rev']:
724 node = repo.changelog.lookup(opts['rev'])
724 node = repo.changelog.lookup(opts['rev'])
725 else:
725 else:
726 node = repo.dirstate.parents()[0]
726 node = repo.dirstate.parents()[0]
727 change = repo.changelog.read(node)
727 change = repo.changelog.read(node)
728 mmap = repo.manifest.read(change[0])
728 mmap = repo.manifest.read(change[0])
729
729
730 for src, abs, rel, exact in walk(repo, pats, opts, node=node):
730 for src, abs, rel, exact in walk(repo, pats, opts, node=node):
731 f = repo.file(abs)
731 f = repo.file(abs)
732 if not opts['text'] and util.binary(f.read(mmap[abs])):
732 if not opts['text'] and util.binary(f.read(mmap[abs])):
733 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
733 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
734 continue
734 continue
735
735
736 lines = f.annotate(mmap[abs])
736 lines = f.annotate(mmap[abs])
737 pieces = []
737 pieces = []
738
738
739 for o, f in opmap:
739 for o, f in opmap:
740 if opts[o]:
740 if opts[o]:
741 l = [f(n) for n, dummy in lines]
741 l = [f(n) for n, dummy in lines]
742 if l:
742 if l:
743 m = max(map(len, l))
743 m = max(map(len, l))
744 pieces.append(["%*s" % (m, x) for x in l])
744 pieces.append(["%*s" % (m, x) for x in l])
745
745
746 if pieces:
746 if pieces:
747 for p, l in zip(zip(*pieces), lines):
747 for p, l in zip(zip(*pieces), lines):
748 ui.write("%s: %s" % (" ".join(p), l[1]))
748 ui.write("%s: %s" % (" ".join(p), l[1]))
749
749
750 def archive(ui, repo, dest, **opts):
750 def archive(ui, repo, dest, **opts):
751 '''create unversioned archive of a repository revision
751 '''create unversioned archive of a repository revision
752
752
753 By default, the revision used is the parent of the working
753 By default, the revision used is the parent of the working
754 directory; use "-r" to specify a different revision.
754 directory; use "-r" to specify a different revision.
755
755
756 To specify the type of archive to create, use "-t". Valid
756 To specify the type of archive to create, use "-t". Valid
757 types are:
757 types are:
758
758
759 "files" (default): a directory full of files
759 "files" (default): a directory full of files
760 "tar": tar archive, uncompressed
760 "tar": tar archive, uncompressed
761 "tbz2": tar archive, compressed using bzip2
761 "tbz2": tar archive, compressed using bzip2
762 "tgz": tar archive, compressed using gzip
762 "tgz": tar archive, compressed using gzip
763 "uzip": zip archive, uncompressed
763 "uzip": zip archive, uncompressed
764 "zip": zip archive, compressed using deflate
764 "zip": zip archive, compressed using deflate
765
765
766 The exact name of the destination archive or directory is given
766 The exact name of the destination archive or directory is given
767 using a format string; see "hg help export" for details.
767 using a format string; see "hg help export" for details.
768
768
769 Each member added to an archive file has a directory prefix
769 Each member added to an archive file has a directory prefix
770 prepended. Use "-p" to specify a format string for the prefix.
770 prepended. Use "-p" to specify a format string for the prefix.
771 The default is the basename of the archive, with suffixes removed.
771 The default is the basename of the archive, with suffixes removed.
772 '''
772 '''
773
773
774 if opts['rev']:
774 if opts['rev']:
775 node = repo.lookup(opts['rev'])
775 node = repo.lookup(opts['rev'])
776 else:
776 else:
777 node, p2 = repo.dirstate.parents()
777 node, p2 = repo.dirstate.parents()
778 if p2 != nullid:
778 if p2 != nullid:
779 raise util.Abort(_('uncommitted merge - please provide a '
779 raise util.Abort(_('uncommitted merge - please provide a '
780 'specific revision'))
780 'specific revision'))
781
781
782 dest = make_filename(repo, repo.changelog, dest, node)
782 dest = make_filename(repo, repo.changelog, dest, node)
783 if os.path.realpath(dest) == repo.root:
783 if os.path.realpath(dest) == repo.root:
784 raise util.Abort(_('repository root cannot be destination'))
784 raise util.Abort(_('repository root cannot be destination'))
785 dummy, matchfn, dummy = matchpats(repo, [], opts)
785 dummy, matchfn, dummy = matchpats(repo, [], opts)
786 kind = opts.get('type') or 'files'
786 kind = opts.get('type') or 'files'
787 prefix = opts['prefix']
787 prefix = opts['prefix']
788 if dest == '-':
788 if dest == '-':
789 if kind == 'files':
789 if kind == 'files':
790 raise util.Abort(_('cannot archive plain files to stdout'))
790 raise util.Abort(_('cannot archive plain files to stdout'))
791 dest = sys.stdout
791 dest = sys.stdout
792 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
792 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
793 prefix = make_filename(repo, repo.changelog, prefix, node)
793 prefix = make_filename(repo, repo.changelog, prefix, node)
794 archival.archive(repo, dest, node, kind, not opts['no_decode'],
794 archival.archive(repo, dest, node, kind, not opts['no_decode'],
795 matchfn, prefix)
795 matchfn, prefix)
796
796
797 def backout(ui, repo, rev, **opts):
797 def backout(ui, repo, rev, **opts):
798 '''reverse effect of earlier changeset
798 '''reverse effect of earlier changeset
799
799
800 Commit the backed out changes as a new changeset. The new
800 Commit the backed out changes as a new changeset. The new
801 changeset is a child of the backed out changeset.
801 changeset is a child of the backed out changeset.
802
802
803 If you back out a changeset other than the tip, a new head is
803 If you back out a changeset other than the tip, a new head is
804 created. This head is the parent of the working directory. If
804 created. This head is the parent of the working directory. If
805 you back out an old changeset, your working directory will appear
805 you back out an old changeset, your working directory will appear
806 old after the backout. You should merge the backout changeset
806 old after the backout. You should merge the backout changeset
807 with another head.
807 with another head.
808
808
809 The --merge option remembers the parent of the working directory
809 The --merge option remembers the parent of the working directory
810 before starting the backout, then merges the new head with that
810 before starting the backout, then merges the new head with that
811 changeset afterwards. This saves you from doing the merge by
811 changeset afterwards. This saves you from doing the merge by
812 hand. The result of this merge is not committed, as for a normal
812 hand. The result of this merge is not committed, as for a normal
813 merge.'''
813 merge.'''
814
814
815 bail_if_changed(repo)
815 bail_if_changed(repo)
816 op1, op2 = repo.dirstate.parents()
816 op1, op2 = repo.dirstate.parents()
817 if op2 != nullid:
817 if op2 != nullid:
818 raise util.Abort(_('outstanding uncommitted merge'))
818 raise util.Abort(_('outstanding uncommitted merge'))
819 node = repo.lookup(rev)
819 node = repo.lookup(rev)
820 parent, p2 = repo.changelog.parents(node)
820 parent, p2 = repo.changelog.parents(node)
821 if parent == nullid:
821 if parent == nullid:
822 raise util.Abort(_('cannot back out a change with no parents'))
822 raise util.Abort(_('cannot back out a change with no parents'))
823 if p2 != nullid:
823 if p2 != nullid:
824 raise util.Abort(_('cannot back out a merge'))
824 raise util.Abort(_('cannot back out a merge'))
825 repo.update(node, force=True, show_stats=False)
825 repo.update(node, force=True, show_stats=False)
826 revert_opts = opts.copy()
826 revert_opts = opts.copy()
827 revert_opts['rev'] = hex(parent)
827 revert_opts['rev'] = hex(parent)
828 revert(ui, repo, **revert_opts)
828 revert(ui, repo, **revert_opts)
829 commit_opts = opts.copy()
829 commit_opts = opts.copy()
830 commit_opts['addremove'] = False
830 commit_opts['addremove'] = False
831 if not commit_opts['message'] and not commit_opts['logfile']:
831 if not commit_opts['message'] and not commit_opts['logfile']:
832 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
832 commit_opts['message'] = _("Backed out changeset %s") % (hex(node))
833 commit_opts['force_editor'] = True
833 commit_opts['force_editor'] = True
834 commit(ui, repo, **commit_opts)
834 commit(ui, repo, **commit_opts)
835 def nice(node):
835 def nice(node):
836 return '%d:%s' % (repo.changelog.rev(node), short(node))
836 return '%d:%s' % (repo.changelog.rev(node), short(node))
837 ui.status(_('changeset %s backs out changeset %s\n') %
837 ui.status(_('changeset %s backs out changeset %s\n') %
838 (nice(repo.changelog.tip()), nice(node)))
838 (nice(repo.changelog.tip()), nice(node)))
839 if op1 != node:
839 if op1 != node:
840 if opts['merge']:
840 if opts['merge']:
841 ui.status(_('merging with changeset %s\n') % nice(op1))
841 ui.status(_('merging with changeset %s\n') % nice(op1))
842 doupdate(ui, repo, hex(op1), **opts)
842 doupdate(ui, repo, hex(op1), **opts)
843 else:
843 else:
844 ui.status(_('the backout changeset is a new head - '
844 ui.status(_('the backout changeset is a new head - '
845 'do not forget to merge\n'))
845 'do not forget to merge\n'))
846 ui.status(_('(use "backout -m" if you want to auto-merge)\n'))
846 ui.status(_('(use "backout -m" if you want to auto-merge)\n'))
847
847
848 def bundle(ui, repo, fname, dest=None, **opts):
848 def bundle(ui, repo, fname, dest=None, **opts):
849 """create a changegroup file
849 """create a changegroup file
850
850
851 Generate a compressed changegroup file collecting all changesets
851 Generate a compressed changegroup file collecting all changesets
852 not found in the other repository.
852 not found in the other repository.
853
853
854 This file can then be transferred using conventional means and
854 This file can then be transferred using conventional means and
855 applied to another repository with the unbundle command. This is
855 applied to another repository with the unbundle command. This is
856 useful when native push and pull are not available or when
856 useful when native push and pull are not available or when
857 exporting an entire repository is undesirable. The standard file
857 exporting an entire repository is undesirable. The standard file
858 extension is ".hg".
858 extension is ".hg".
859
859
860 Unlike import/export, this exactly preserves all changeset
860 Unlike import/export, this exactly preserves all changeset
861 contents including permissions, rename data, and revision history.
861 contents including permissions, rename data, and revision history.
862 """
862 """
863 dest = ui.expandpath(dest or 'default-push', dest or 'default')
863 dest = ui.expandpath(dest or 'default-push', dest or 'default')
864 other = hg.repository(ui, dest)
864 other = hg.repository(ui, dest)
865 o = repo.findoutgoing(other, force=opts['force'])
865 o = repo.findoutgoing(other, force=opts['force'])
866 cg = repo.changegroup(o, 'bundle')
866 cg = repo.changegroup(o, 'bundle')
867 write_bundle(cg, fname)
867 write_bundle(cg, fname)
868
868
869 def cat(ui, repo, file1, *pats, **opts):
869 def cat(ui, repo, file1, *pats, **opts):
870 """output the latest or given revisions of files
870 """output the latest or given revisions of files
871
871
872 Print the specified files as they were at the given revision.
872 Print the specified files as they were at the given revision.
873 If no revision is given then the tip is used.
873 If no revision is given then the tip is used.
874
874
875 Output may be to a file, in which case the name of the file is
875 Output may be to a file, in which case the name of the file is
876 given using a format string. The formatting rules are the same as
876 given using a format string. The formatting rules are the same as
877 for the export command, with the following additions:
877 for the export command, with the following additions:
878
878
879 %s basename of file being printed
879 %s basename of file being printed
880 %d dirname of file being printed, or '.' if in repo root
880 %d dirname of file being printed, or '.' if in repo root
881 %p root-relative path name of file being printed
881 %p root-relative path name of file being printed
882 """
882 """
883 mf = {}
883 mf = {}
884 rev = opts['rev']
884 rev = opts['rev']
885 if rev:
885 if rev:
886 node = repo.lookup(rev)
886 node = repo.lookup(rev)
887 else:
887 else:
888 node = repo.changelog.tip()
888 node = repo.changelog.tip()
889 change = repo.changelog.read(node)
889 change = repo.changelog.read(node)
890 mf = repo.manifest.read(change[0])
890 mf = repo.manifest.read(change[0])
891 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, node):
891 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts, node):
892 r = repo.file(abs)
892 r = repo.file(abs)
893 n = mf[abs]
893 n = mf[abs]
894 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
894 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
895 fp.write(r.read(n))
895 fp.write(r.read(n))
896
896
897 def clone(ui, source, dest=None, **opts):
897 def clone(ui, source, dest=None, **opts):
898 """make a copy of an existing repository
898 """make a copy of an existing repository
899
899
900 Create a copy of an existing repository in a new directory.
900 Create a copy of an existing repository in a new directory.
901
901
902 If no destination directory name is specified, it defaults to the
902 If no destination directory name is specified, it defaults to the
903 basename of the source.
903 basename of the source.
904
904
905 The location of the source is added to the new repository's
905 The location of the source is added to the new repository's
906 .hg/hgrc file, as the default to be used for future pulls.
906 .hg/hgrc file, as the default to be used for future pulls.
907
907
908 For efficiency, hardlinks are used for cloning whenever the source
908 For efficiency, hardlinks are used for cloning whenever the source
909 and destination are on the same filesystem. Some filesystems,
909 and destination are on the same filesystem. Some filesystems,
910 such as AFS, implement hardlinking incorrectly, but do not report
910 such as AFS, implement hardlinking incorrectly, but do not report
911 errors. In these cases, use the --pull option to avoid
911 errors. In these cases, use the --pull option to avoid
912 hardlinking.
912 hardlinking.
913
913
914 See pull for valid source format details.
914 See pull for valid source format details.
915 """
915 """
916 if dest is None:
916 if dest is None:
917 dest = os.path.basename(os.path.normpath(source))
917 dest = os.path.basename(os.path.normpath(source))
918
918
919 if os.path.exists(dest):
919 if os.path.exists(dest):
920 raise util.Abort(_("destination '%s' already exists"), dest)
920 raise util.Abort(_("destination '%s' already exists"), dest)
921
921
922 dest = os.path.realpath(dest)
923
924 class Dircleanup(object):
922 class Dircleanup(object):
925 def __init__(self, dir_):
923 def __init__(self, dir_):
926 self.rmtree = shutil.rmtree
924 self.rmtree = shutil.rmtree
927 self.dir_ = dir_
925 self.dir_ = dir_
928 os.mkdir(dir_)
929 def close(self):
926 def close(self):
930 self.dir_ = None
927 self.dir_ = None
931 def __del__(self):
928 def __del__(self):
932 if self.dir_:
929 if self.dir_:
933 self.rmtree(self.dir_, True)
930 self.rmtree(self.dir_, True)
934
931
935 if opts['ssh']:
932 if opts['ssh']:
936 ui.setconfig("ui", "ssh", opts['ssh'])
933 ui.setconfig("ui", "ssh", opts['ssh'])
937 if opts['remotecmd']:
934 if opts['remotecmd']:
938 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
935 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
939
936
940 source = ui.expandpath(source)
937 source = ui.expandpath(source)
941
938 src_repo = hg.repository(ui, source)
942 d = Dircleanup(dest)
939
940 dest_repo = None
941 try:
942 dest_repo = hg.repository(ui, dest)
943 raise util.Abort(_("destination '%s' already exists." % dest))
944 except hg.RepoError:
945 dest_repo = hg.repository(ui, dest, create=1)
946
947 dest_path = None
948 d = None
949 if dest_repo.local():
950 dest_path = os.path.realpath(dest)
951 d = Dircleanup(dest_path)
952
943 abspath = source
953 abspath = source
944 other = hg.repository(ui, source)
945
946 copy = False
954 copy = False
947 if other.dev() != -1:
955 if src_repo.local() and dest_repo.local():
948 abspath = os.path.abspath(source)
956 abspath = os.path.abspath(source)
949 if not opts['pull'] and not opts['rev']:
957 if not opts['pull'] and not opts['rev']:
950 copy = True
958 copy = True
951
959
952 if copy:
960 if copy:
953 try:
961 try:
954 # we use a lock here because if we race with commit, we
962 # we use a lock here because if we race with commit, we
955 # can end up with extra data in the cloned revlogs that's
963 # can end up with extra data in the cloned revlogs that's
956 # not pointed to by changesets, thus causing verify to
964 # not pointed to by changesets, thus causing verify to
957 # fail
965 # fail
958 l1 = other.lock()
966 l1 = src_repo.lock()
959 except lock.LockException:
967 except lock.LockException:
960 copy = False
968 copy = False
961
969
962 if copy:
970 if copy:
963 # we lock here to avoid premature writing to the target
971 # we lock here to avoid premature writing to the target
964 os.mkdir(os.path.join(dest, ".hg"))
972 l2 = lock.lock(os.path.join(dest_path, ".hg", "lock"))
965 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
973
966
974 # we need to remove the (empty) data dir in dest so copyfiles can do it's work
975 os.rmdir( os.path.join(dest_path, ".hg", "data") )
967 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
976 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
968 for f in files.split():
977 for f in files.split():
969 src = os.path.join(source, ".hg", f)
978 src = os.path.join(source, ".hg", f)
970 dst = os.path.join(dest, ".hg", f)
979 dst = os.path.join(dest_path, ".hg", f)
971 try:
980 try:
972 util.copyfiles(src, dst)
981 util.copyfiles(src, dst)
973 except OSError, inst:
982 except OSError, inst:
974 if inst.errno != errno.ENOENT:
983 if inst.errno != errno.ENOENT:
975 raise
984 raise
976
985
977 repo = hg.repository(ui, dest)
986 # we need to re-init the repo after manually copying the data into it
987 dest_repo = hg.repository(ui, dest)
978
988
979 else:
989 else:
980 revs = None
990 revs = None
981 if opts['rev']:
991 if opts['rev']:
982 if not other.local():
992 if not src_repo.local():
983 error = _("clone -r not supported yet for remote repositories.")
993 error = _("clone -r not supported yet for remote repositories.")
984 raise util.Abort(error)
994 raise util.Abort(error)
985 else:
995 else:
986 revs = [other.lookup(rev) for rev in opts['rev']]
996 revs = [src_repo.lookup(rev) for rev in opts['rev']]
987 repo = hg.repository(ui, dest, create=1)
997
988 repo.pull(other, heads = revs)
998 if dest_repo.local():
989
999 dest_repo.pull(src_repo, heads = revs)
990 f = repo.opener("hgrc", "w", text=True)
1000 elif src_repo.local():
1001 src_repo.push(dest_repo, revs = revs)
1002 else:
1003 error = _("clone from remote to remote not supported.")
1004 raise util.Abort(error)
1005
1006 if dest_repo.local():
1007 f = dest_repo.opener("hgrc", "w", text=True)
991 f.write("[paths]\n")
1008 f.write("[paths]\n")
992 f.write("default = %s\n" % abspath)
1009 f.write("default = %s\n" % abspath)
993 f.close()
1010 f.close()
994
1011
995 if not opts['noupdate']:
1012 if not opts['noupdate']:
996 doupdate(repo.ui, repo)
1013 doupdate(dest_repo.ui, dest_repo)
997
1014
1015 if d:
998 d.close()
1016 d.close()
999
1017
1000 def commit(ui, repo, *pats, **opts):
1018 def commit(ui, repo, *pats, **opts):
1001 """commit the specified files or all outstanding changes
1019 """commit the specified files or all outstanding changes
1002
1020
1003 Commit changes to the given files into the repository.
1021 Commit changes to the given files into the repository.
1004
1022
1005 If a list of files is omitted, all changes reported by "hg status"
1023 If a list of files is omitted, all changes reported by "hg status"
1006 will be committed.
1024 will be committed.
1007
1025
1008 If no commit message is specified, the editor configured in your hgrc
1026 If no commit message is specified, the editor configured in your hgrc
1009 or in the EDITOR environment variable is started to enter a message.
1027 or in the EDITOR environment variable is started to enter a message.
1010 """
1028 """
1011 message = opts['message']
1029 message = opts['message']
1012 logfile = opts['logfile']
1030 logfile = opts['logfile']
1013
1031
1014 if message and logfile:
1032 if message and logfile:
1015 raise util.Abort(_('options --message and --logfile are mutually '
1033 raise util.Abort(_('options --message and --logfile are mutually '
1016 'exclusive'))
1034 'exclusive'))
1017 if not message and logfile:
1035 if not message and logfile:
1018 try:
1036 try:
1019 if logfile == '-':
1037 if logfile == '-':
1020 message = sys.stdin.read()
1038 message = sys.stdin.read()
1021 else:
1039 else:
1022 message = open(logfile).read()
1040 message = open(logfile).read()
1023 except IOError, inst:
1041 except IOError, inst:
1024 raise util.Abort(_("can't read commit message '%s': %s") %
1042 raise util.Abort(_("can't read commit message '%s': %s") %
1025 (logfile, inst.strerror))
1043 (logfile, inst.strerror))
1026
1044
1027 if opts['addremove']:
1045 if opts['addremove']:
1028 addremove_lock(ui, repo, pats, opts)
1046 addremove_lock(ui, repo, pats, opts)
1029 fns, match, anypats = matchpats(repo, pats, opts)
1047 fns, match, anypats = matchpats(repo, pats, opts)
1030 if pats:
1048 if pats:
1031 modified, added, removed, deleted, unknown = (
1049 modified, added, removed, deleted, unknown = (
1032 repo.changes(files=fns, match=match))
1050 repo.changes(files=fns, match=match))
1033 files = modified + added + removed
1051 files = modified + added + removed
1034 else:
1052 else:
1035 files = []
1053 files = []
1036 try:
1054 try:
1037 repo.commit(files, message, opts['user'], opts['date'], match,
1055 repo.commit(files, message, opts['user'], opts['date'], match,
1038 force_editor=opts.get('force_editor'))
1056 force_editor=opts.get('force_editor'))
1039 except ValueError, inst:
1057 except ValueError, inst:
1040 raise util.Abort(str(inst))
1058 raise util.Abort(str(inst))
1041
1059
1042 def docopy(ui, repo, pats, opts, wlock):
1060 def docopy(ui, repo, pats, opts, wlock):
1043 # called with the repo lock held
1061 # called with the repo lock held
1044 cwd = repo.getcwd()
1062 cwd = repo.getcwd()
1045 errors = 0
1063 errors = 0
1046 copied = []
1064 copied = []
1047 targets = {}
1065 targets = {}
1048
1066
1049 def okaytocopy(abs, rel, exact):
1067 def okaytocopy(abs, rel, exact):
1050 reasons = {'?': _('is not managed'),
1068 reasons = {'?': _('is not managed'),
1051 'a': _('has been marked for add'),
1069 'a': _('has been marked for add'),
1052 'r': _('has been marked for remove')}
1070 'r': _('has been marked for remove')}
1053 state = repo.dirstate.state(abs)
1071 state = repo.dirstate.state(abs)
1054 reason = reasons.get(state)
1072 reason = reasons.get(state)
1055 if reason:
1073 if reason:
1056 if state == 'a':
1074 if state == 'a':
1057 origsrc = repo.dirstate.copied(abs)
1075 origsrc = repo.dirstate.copied(abs)
1058 if origsrc is not None:
1076 if origsrc is not None:
1059 return origsrc
1077 return origsrc
1060 if exact:
1078 if exact:
1061 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
1079 ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
1062 else:
1080 else:
1063 return abs
1081 return abs
1064
1082
1065 def copy(origsrc, abssrc, relsrc, target, exact):
1083 def copy(origsrc, abssrc, relsrc, target, exact):
1066 abstarget = util.canonpath(repo.root, cwd, target)
1084 abstarget = util.canonpath(repo.root, cwd, target)
1067 reltarget = util.pathto(cwd, abstarget)
1085 reltarget = util.pathto(cwd, abstarget)
1068 prevsrc = targets.get(abstarget)
1086 prevsrc = targets.get(abstarget)
1069 if prevsrc is not None:
1087 if prevsrc is not None:
1070 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1088 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
1071 (reltarget, abssrc, prevsrc))
1089 (reltarget, abssrc, prevsrc))
1072 return
1090 return
1073 if (not opts['after'] and os.path.exists(reltarget) or
1091 if (not opts['after'] and os.path.exists(reltarget) or
1074 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
1092 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
1075 if not opts['force']:
1093 if not opts['force']:
1076 ui.warn(_('%s: not overwriting - file exists\n') %
1094 ui.warn(_('%s: not overwriting - file exists\n') %
1077 reltarget)
1095 reltarget)
1078 return
1096 return
1079 if not opts['after'] and not opts.get('dry_run'):
1097 if not opts['after'] and not opts.get('dry_run'):
1080 os.unlink(reltarget)
1098 os.unlink(reltarget)
1081 if opts['after']:
1099 if opts['after']:
1082 if not os.path.exists(reltarget):
1100 if not os.path.exists(reltarget):
1083 return
1101 return
1084 else:
1102 else:
1085 targetdir = os.path.dirname(reltarget) or '.'
1103 targetdir = os.path.dirname(reltarget) or '.'
1086 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
1104 if not os.path.isdir(targetdir) and not opts.get('dry_run'):
1087 os.makedirs(targetdir)
1105 os.makedirs(targetdir)
1088 try:
1106 try:
1089 restore = repo.dirstate.state(abstarget) == 'r'
1107 restore = repo.dirstate.state(abstarget) == 'r'
1090 if restore and not opts.get('dry_run'):
1108 if restore and not opts.get('dry_run'):
1091 repo.undelete([abstarget], wlock)
1109 repo.undelete([abstarget], wlock)
1092 try:
1110 try:
1093 if not opts.get('dry_run'):
1111 if not opts.get('dry_run'):
1094 shutil.copyfile(relsrc, reltarget)
1112 shutil.copyfile(relsrc, reltarget)
1095 shutil.copymode(relsrc, reltarget)
1113 shutil.copymode(relsrc, reltarget)
1096 restore = False
1114 restore = False
1097 finally:
1115 finally:
1098 if restore:
1116 if restore:
1099 repo.remove([abstarget], wlock)
1117 repo.remove([abstarget], wlock)
1100 except shutil.Error, inst:
1118 except shutil.Error, inst:
1101 raise util.Abort(str(inst))
1119 raise util.Abort(str(inst))
1102 except IOError, inst:
1120 except IOError, inst:
1103 if inst.errno == errno.ENOENT:
1121 if inst.errno == errno.ENOENT:
1104 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1122 ui.warn(_('%s: deleted in working copy\n') % relsrc)
1105 else:
1123 else:
1106 ui.warn(_('%s: cannot copy - %s\n') %
1124 ui.warn(_('%s: cannot copy - %s\n') %
1107 (relsrc, inst.strerror))
1125 (relsrc, inst.strerror))
1108 errors += 1
1126 errors += 1
1109 return
1127 return
1110 if ui.verbose or not exact:
1128 if ui.verbose or not exact:
1111 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1129 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
1112 targets[abstarget] = abssrc
1130 targets[abstarget] = abssrc
1113 if abstarget != origsrc and not opts.get('dry_run'):
1131 if abstarget != origsrc and not opts.get('dry_run'):
1114 repo.copy(origsrc, abstarget, wlock)
1132 repo.copy(origsrc, abstarget, wlock)
1115 copied.append((abssrc, relsrc, exact))
1133 copied.append((abssrc, relsrc, exact))
1116
1134
1117 def targetpathfn(pat, dest, srcs):
1135 def targetpathfn(pat, dest, srcs):
1118 if os.path.isdir(pat):
1136 if os.path.isdir(pat):
1119 abspfx = util.canonpath(repo.root, cwd, pat)
1137 abspfx = util.canonpath(repo.root, cwd, pat)
1120 if destdirexists:
1138 if destdirexists:
1121 striplen = len(os.path.split(abspfx)[0])
1139 striplen = len(os.path.split(abspfx)[0])
1122 else:
1140 else:
1123 striplen = len(abspfx)
1141 striplen = len(abspfx)
1124 if striplen:
1142 if striplen:
1125 striplen += len(os.sep)
1143 striplen += len(os.sep)
1126 res = lambda p: os.path.join(dest, p[striplen:])
1144 res = lambda p: os.path.join(dest, p[striplen:])
1127 elif destdirexists:
1145 elif destdirexists:
1128 res = lambda p: os.path.join(dest, os.path.basename(p))
1146 res = lambda p: os.path.join(dest, os.path.basename(p))
1129 else:
1147 else:
1130 res = lambda p: dest
1148 res = lambda p: dest
1131 return res
1149 return res
1132
1150
1133 def targetpathafterfn(pat, dest, srcs):
1151 def targetpathafterfn(pat, dest, srcs):
1134 if util.patkind(pat, None)[0]:
1152 if util.patkind(pat, None)[0]:
1135 # a mercurial pattern
1153 # a mercurial pattern
1136 res = lambda p: os.path.join(dest, os.path.basename(p))
1154 res = lambda p: os.path.join(dest, os.path.basename(p))
1137 else:
1155 else:
1138 abspfx = util.canonpath(repo.root, cwd, pat)
1156 abspfx = util.canonpath(repo.root, cwd, pat)
1139 if len(abspfx) < len(srcs[0][0]):
1157 if len(abspfx) < len(srcs[0][0]):
1140 # A directory. Either the target path contains the last
1158 # A directory. Either the target path contains the last
1141 # component of the source path or it does not.
1159 # component of the source path or it does not.
1142 def evalpath(striplen):
1160 def evalpath(striplen):
1143 score = 0
1161 score = 0
1144 for s in srcs:
1162 for s in srcs:
1145 t = os.path.join(dest, s[0][striplen:])
1163 t = os.path.join(dest, s[0][striplen:])
1146 if os.path.exists(t):
1164 if os.path.exists(t):
1147 score += 1
1165 score += 1
1148 return score
1166 return score
1149
1167
1150 striplen = len(abspfx)
1168 striplen = len(abspfx)
1151 if striplen:
1169 if striplen:
1152 striplen += len(os.sep)
1170 striplen += len(os.sep)
1153 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1171 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
1154 score = evalpath(striplen)
1172 score = evalpath(striplen)
1155 striplen1 = len(os.path.split(abspfx)[0])
1173 striplen1 = len(os.path.split(abspfx)[0])
1156 if striplen1:
1174 if striplen1:
1157 striplen1 += len(os.sep)
1175 striplen1 += len(os.sep)
1158 if evalpath(striplen1) > score:
1176 if evalpath(striplen1) > score:
1159 striplen = striplen1
1177 striplen = striplen1
1160 res = lambda p: os.path.join(dest, p[striplen:])
1178 res = lambda p: os.path.join(dest, p[striplen:])
1161 else:
1179 else:
1162 # a file
1180 # a file
1163 if destdirexists:
1181 if destdirexists:
1164 res = lambda p: os.path.join(dest, os.path.basename(p))
1182 res = lambda p: os.path.join(dest, os.path.basename(p))
1165 else:
1183 else:
1166 res = lambda p: dest
1184 res = lambda p: dest
1167 return res
1185 return res
1168
1186
1169
1187
1170 pats = list(pats)
1188 pats = list(pats)
1171 if not pats:
1189 if not pats:
1172 raise util.Abort(_('no source or destination specified'))
1190 raise util.Abort(_('no source or destination specified'))
1173 if len(pats) == 1:
1191 if len(pats) == 1:
1174 raise util.Abort(_('no destination specified'))
1192 raise util.Abort(_('no destination specified'))
1175 dest = pats.pop()
1193 dest = pats.pop()
1176 destdirexists = os.path.isdir(dest)
1194 destdirexists = os.path.isdir(dest)
1177 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1195 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
1178 raise util.Abort(_('with multiple sources, destination must be an '
1196 raise util.Abort(_('with multiple sources, destination must be an '
1179 'existing directory'))
1197 'existing directory'))
1180 if opts['after']:
1198 if opts['after']:
1181 tfn = targetpathafterfn
1199 tfn = targetpathafterfn
1182 else:
1200 else:
1183 tfn = targetpathfn
1201 tfn = targetpathfn
1184 copylist = []
1202 copylist = []
1185 for pat in pats:
1203 for pat in pats:
1186 srcs = []
1204 srcs = []
1187 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
1205 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
1188 origsrc = okaytocopy(abssrc, relsrc, exact)
1206 origsrc = okaytocopy(abssrc, relsrc, exact)
1189 if origsrc:
1207 if origsrc:
1190 srcs.append((origsrc, abssrc, relsrc, exact))
1208 srcs.append((origsrc, abssrc, relsrc, exact))
1191 if not srcs:
1209 if not srcs:
1192 continue
1210 continue
1193 copylist.append((tfn(pat, dest, srcs), srcs))
1211 copylist.append((tfn(pat, dest, srcs), srcs))
1194 if not copylist:
1212 if not copylist:
1195 raise util.Abort(_('no files to copy'))
1213 raise util.Abort(_('no files to copy'))
1196
1214
1197 for targetpath, srcs in copylist:
1215 for targetpath, srcs in copylist:
1198 for origsrc, abssrc, relsrc, exact in srcs:
1216 for origsrc, abssrc, relsrc, exact in srcs:
1199 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1217 copy(origsrc, abssrc, relsrc, targetpath(abssrc), exact)
1200
1218
1201 if errors:
1219 if errors:
1202 ui.warn(_('(consider using --after)\n'))
1220 ui.warn(_('(consider using --after)\n'))
1203 return errors, copied
1221 return errors, copied
1204
1222
1205 def copy(ui, repo, *pats, **opts):
1223 def copy(ui, repo, *pats, **opts):
1206 """mark files as copied for the next commit
1224 """mark files as copied for the next commit
1207
1225
1208 Mark dest as having copies of source files. If dest is a
1226 Mark dest as having copies of source files. If dest is a
1209 directory, copies are put in that directory. If dest is a file,
1227 directory, copies are put in that directory. If dest is a file,
1210 there can only be one source.
1228 there can only be one source.
1211
1229
1212 By default, this command copies the contents of files as they
1230 By default, this command copies the contents of files as they
1213 stand in the working directory. If invoked with --after, the
1231 stand in the working directory. If invoked with --after, the
1214 operation is recorded, but no copying is performed.
1232 operation is recorded, but no copying is performed.
1215
1233
1216 This command takes effect in the next commit.
1234 This command takes effect in the next commit.
1217
1235
1218 NOTE: This command should be treated as experimental. While it
1236 NOTE: This command should be treated as experimental. While it
1219 should properly record copied files, this information is not yet
1237 should properly record copied files, this information is not yet
1220 fully used by merge, nor fully reported by log.
1238 fully used by merge, nor fully reported by log.
1221 """
1239 """
1222 wlock = repo.wlock(0)
1240 wlock = repo.wlock(0)
1223 errs, copied = docopy(ui, repo, pats, opts, wlock)
1241 errs, copied = docopy(ui, repo, pats, opts, wlock)
1224 return errs
1242 return errs
1225
1243
1226 def debugancestor(ui, index, rev1, rev2):
1244 def debugancestor(ui, index, rev1, rev2):
1227 """find the ancestor revision of two revisions in a given index"""
1245 """find the ancestor revision of two revisions in a given index"""
1228 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1246 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0)
1229 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1247 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
1230 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1248 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1231
1249
1232 def debugcomplete(ui, cmd='', **opts):
1250 def debugcomplete(ui, cmd='', **opts):
1233 """returns the completion list associated with the given command"""
1251 """returns the completion list associated with the given command"""
1234
1252
1235 if opts['options']:
1253 if opts['options']:
1236 options = []
1254 options = []
1237 otables = [globalopts]
1255 otables = [globalopts]
1238 if cmd:
1256 if cmd:
1239 aliases, entry = find(cmd)
1257 aliases, entry = find(cmd)
1240 otables.append(entry[1])
1258 otables.append(entry[1])
1241 for t in otables:
1259 for t in otables:
1242 for o in t:
1260 for o in t:
1243 if o[0]:
1261 if o[0]:
1244 options.append('-%s' % o[0])
1262 options.append('-%s' % o[0])
1245 options.append('--%s' % o[1])
1263 options.append('--%s' % o[1])
1246 ui.write("%s\n" % "\n".join(options))
1264 ui.write("%s\n" % "\n".join(options))
1247 return
1265 return
1248
1266
1249 clist = findpossible(cmd).keys()
1267 clist = findpossible(cmd).keys()
1250 clist.sort()
1268 clist.sort()
1251 ui.write("%s\n" % "\n".join(clist))
1269 ui.write("%s\n" % "\n".join(clist))
1252
1270
1253 def debugrebuildstate(ui, repo, rev=None):
1271 def debugrebuildstate(ui, repo, rev=None):
1254 """rebuild the dirstate as it would look like for the given revision"""
1272 """rebuild the dirstate as it would look like for the given revision"""
1255 if not rev:
1273 if not rev:
1256 rev = repo.changelog.tip()
1274 rev = repo.changelog.tip()
1257 else:
1275 else:
1258 rev = repo.lookup(rev)
1276 rev = repo.lookup(rev)
1259 change = repo.changelog.read(rev)
1277 change = repo.changelog.read(rev)
1260 n = change[0]
1278 n = change[0]
1261 files = repo.manifest.readflags(n)
1279 files = repo.manifest.readflags(n)
1262 wlock = repo.wlock()
1280 wlock = repo.wlock()
1263 repo.dirstate.rebuild(rev, files.iteritems())
1281 repo.dirstate.rebuild(rev, files.iteritems())
1264
1282
1265 def debugcheckstate(ui, repo):
1283 def debugcheckstate(ui, repo):
1266 """validate the correctness of the current dirstate"""
1284 """validate the correctness of the current dirstate"""
1267 parent1, parent2 = repo.dirstate.parents()
1285 parent1, parent2 = repo.dirstate.parents()
1268 repo.dirstate.read()
1286 repo.dirstate.read()
1269 dc = repo.dirstate.map
1287 dc = repo.dirstate.map
1270 keys = dc.keys()
1288 keys = dc.keys()
1271 keys.sort()
1289 keys.sort()
1272 m1n = repo.changelog.read(parent1)[0]
1290 m1n = repo.changelog.read(parent1)[0]
1273 m2n = repo.changelog.read(parent2)[0]
1291 m2n = repo.changelog.read(parent2)[0]
1274 m1 = repo.manifest.read(m1n)
1292 m1 = repo.manifest.read(m1n)
1275 m2 = repo.manifest.read(m2n)
1293 m2 = repo.manifest.read(m2n)
1276 errors = 0
1294 errors = 0
1277 for f in dc:
1295 for f in dc:
1278 state = repo.dirstate.state(f)
1296 state = repo.dirstate.state(f)
1279 if state in "nr" and f not in m1:
1297 if state in "nr" and f not in m1:
1280 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1298 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1281 errors += 1
1299 errors += 1
1282 if state in "a" and f in m1:
1300 if state in "a" and f in m1:
1283 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1301 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1284 errors += 1
1302 errors += 1
1285 if state in "m" and f not in m1 and f not in m2:
1303 if state in "m" and f not in m1 and f not in m2:
1286 ui.warn(_("%s in state %s, but not in either manifest\n") %
1304 ui.warn(_("%s in state %s, but not in either manifest\n") %
1287 (f, state))
1305 (f, state))
1288 errors += 1
1306 errors += 1
1289 for f in m1:
1307 for f in m1:
1290 state = repo.dirstate.state(f)
1308 state = repo.dirstate.state(f)
1291 if state not in "nrm":
1309 if state not in "nrm":
1292 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1310 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1293 errors += 1
1311 errors += 1
1294 if errors:
1312 if errors:
1295 error = _(".hg/dirstate inconsistent with current parent's manifest")
1313 error = _(".hg/dirstate inconsistent with current parent's manifest")
1296 raise util.Abort(error)
1314 raise util.Abort(error)
1297
1315
1298 def debugconfig(ui, repo, *values):
1316 def debugconfig(ui, repo, *values):
1299 """show combined config settings from all hgrc files
1317 """show combined config settings from all hgrc files
1300
1318
1301 With no args, print names and values of all config items.
1319 With no args, print names and values of all config items.
1302
1320
1303 With one arg of the form section.name, print just the value of
1321 With one arg of the form section.name, print just the value of
1304 that config item.
1322 that config item.
1305
1323
1306 With multiple args, print names and values of all config items
1324 With multiple args, print names and values of all config items
1307 with matching section names."""
1325 with matching section names."""
1308
1326
1309 if values:
1327 if values:
1310 if len([v for v in values if '.' in v]) > 1:
1328 if len([v for v in values if '.' in v]) > 1:
1311 raise util.Abort(_('only one config item permitted'))
1329 raise util.Abort(_('only one config item permitted'))
1312 for section, name, value in ui.walkconfig():
1330 for section, name, value in ui.walkconfig():
1313 sectname = section + '.' + name
1331 sectname = section + '.' + name
1314 if values:
1332 if values:
1315 for v in values:
1333 for v in values:
1316 if v == section:
1334 if v == section:
1317 ui.write('%s=%s\n' % (sectname, value))
1335 ui.write('%s=%s\n' % (sectname, value))
1318 elif v == sectname:
1336 elif v == sectname:
1319 ui.write(value, '\n')
1337 ui.write(value, '\n')
1320 else:
1338 else:
1321 ui.write('%s=%s\n' % (sectname, value))
1339 ui.write('%s=%s\n' % (sectname, value))
1322
1340
1323 def debugsetparents(ui, repo, rev1, rev2=None):
1341 def debugsetparents(ui, repo, rev1, rev2=None):
1324 """manually set the parents of the current working directory
1342 """manually set the parents of the current working directory
1325
1343
1326 This is useful for writing repository conversion tools, but should
1344 This is useful for writing repository conversion tools, but should
1327 be used with care.
1345 be used with care.
1328 """
1346 """
1329
1347
1330 if not rev2:
1348 if not rev2:
1331 rev2 = hex(nullid)
1349 rev2 = hex(nullid)
1332
1350
1333 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1351 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1334
1352
1335 def debugstate(ui, repo):
1353 def debugstate(ui, repo):
1336 """show the contents of the current dirstate"""
1354 """show the contents of the current dirstate"""
1337 repo.dirstate.read()
1355 repo.dirstate.read()
1338 dc = repo.dirstate.map
1356 dc = repo.dirstate.map
1339 keys = dc.keys()
1357 keys = dc.keys()
1340 keys.sort()
1358 keys.sort()
1341 for file_ in keys:
1359 for file_ in keys:
1342 ui.write("%c %3o %10d %s %s\n"
1360 ui.write("%c %3o %10d %s %s\n"
1343 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1361 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1344 time.strftime("%x %X",
1362 time.strftime("%x %X",
1345 time.localtime(dc[file_][3])), file_))
1363 time.localtime(dc[file_][3])), file_))
1346 for f in repo.dirstate.copies:
1364 for f in repo.dirstate.copies:
1347 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1365 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1348
1366
1349 def debugdata(ui, file_, rev):
1367 def debugdata(ui, file_, rev):
1350 """dump the contents of an data file revision"""
1368 """dump the contents of an data file revision"""
1351 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1369 r = revlog.revlog(util.opener(os.getcwd(), audit=False),
1352 file_[:-2] + ".i", file_, 0)
1370 file_[:-2] + ".i", file_, 0)
1353 try:
1371 try:
1354 ui.write(r.revision(r.lookup(rev)))
1372 ui.write(r.revision(r.lookup(rev)))
1355 except KeyError:
1373 except KeyError:
1356 raise util.Abort(_('invalid revision identifier %s'), rev)
1374 raise util.Abort(_('invalid revision identifier %s'), rev)
1357
1375
1358 def debugindex(ui, file_):
1376 def debugindex(ui, file_):
1359 """dump the contents of an index file"""
1377 """dump the contents of an index file"""
1360 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1378 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1361 ui.write(" rev offset length base linkrev" +
1379 ui.write(" rev offset length base linkrev" +
1362 " nodeid p1 p2\n")
1380 " nodeid p1 p2\n")
1363 for i in range(r.count()):
1381 for i in range(r.count()):
1364 node = r.node(i)
1382 node = r.node(i)
1365 pp = r.parents(node)
1383 pp = r.parents(node)
1366 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1384 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1367 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1385 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
1368 short(node), short(pp[0]), short(pp[1])))
1386 short(node), short(pp[0]), short(pp[1])))
1369
1387
1370 def debugindexdot(ui, file_):
1388 def debugindexdot(ui, file_):
1371 """dump an index DAG as a .dot file"""
1389 """dump an index DAG as a .dot file"""
1372 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1390 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0)
1373 ui.write("digraph G {\n")
1391 ui.write("digraph G {\n")
1374 for i in range(r.count()):
1392 for i in range(r.count()):
1375 node = r.node(i)
1393 node = r.node(i)
1376 pp = r.parents(node)
1394 pp = r.parents(node)
1377 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1395 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
1378 if pp[1] != nullid:
1396 if pp[1] != nullid:
1379 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1397 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
1380 ui.write("}\n")
1398 ui.write("}\n")
1381
1399
1382 def debugrename(ui, repo, file, rev=None):
1400 def debugrename(ui, repo, file, rev=None):
1383 """dump rename information"""
1401 """dump rename information"""
1384 r = repo.file(relpath(repo, [file])[0])
1402 r = repo.file(relpath(repo, [file])[0])
1385 if rev:
1403 if rev:
1386 try:
1404 try:
1387 # assume all revision numbers are for changesets
1405 # assume all revision numbers are for changesets
1388 n = repo.lookup(rev)
1406 n = repo.lookup(rev)
1389 change = repo.changelog.read(n)
1407 change = repo.changelog.read(n)
1390 m = repo.manifest.read(change[0])
1408 m = repo.manifest.read(change[0])
1391 n = m[relpath(repo, [file])[0]]
1409 n = m[relpath(repo, [file])[0]]
1392 except (hg.RepoError, KeyError):
1410 except (hg.RepoError, KeyError):
1393 n = r.lookup(rev)
1411 n = r.lookup(rev)
1394 else:
1412 else:
1395 n = r.tip()
1413 n = r.tip()
1396 m = r.renamed(n)
1414 m = r.renamed(n)
1397 if m:
1415 if m:
1398 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1416 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1399 else:
1417 else:
1400 ui.write(_("not renamed\n"))
1418 ui.write(_("not renamed\n"))
1401
1419
1402 def debugwalk(ui, repo, *pats, **opts):
1420 def debugwalk(ui, repo, *pats, **opts):
1403 """show how files match on given patterns"""
1421 """show how files match on given patterns"""
1404 items = list(walk(repo, pats, opts))
1422 items = list(walk(repo, pats, opts))
1405 if not items:
1423 if not items:
1406 return
1424 return
1407 fmt = '%%s %%-%ds %%-%ds %%s' % (
1425 fmt = '%%s %%-%ds %%-%ds %%s' % (
1408 max([len(abs) for (src, abs, rel, exact) in items]),
1426 max([len(abs) for (src, abs, rel, exact) in items]),
1409 max([len(rel) for (src, abs, rel, exact) in items]))
1427 max([len(rel) for (src, abs, rel, exact) in items]))
1410 for src, abs, rel, exact in items:
1428 for src, abs, rel, exact in items:
1411 line = fmt % (src, abs, rel, exact and 'exact' or '')
1429 line = fmt % (src, abs, rel, exact and 'exact' or '')
1412 ui.write("%s\n" % line.rstrip())
1430 ui.write("%s\n" % line.rstrip())
1413
1431
1414 def diff(ui, repo, *pats, **opts):
1432 def diff(ui, repo, *pats, **opts):
1415 """diff repository (or selected files)
1433 """diff repository (or selected files)
1416
1434
1417 Show differences between revisions for the specified files.
1435 Show differences between revisions for the specified files.
1418
1436
1419 Differences between files are shown using the unified diff format.
1437 Differences between files are shown using the unified diff format.
1420
1438
1421 When two revision arguments are given, then changes are shown
1439 When two revision arguments are given, then changes are shown
1422 between those revisions. If only one revision is specified then
1440 between those revisions. If only one revision is specified then
1423 that revision is compared to the working directory, and, when no
1441 that revision is compared to the working directory, and, when no
1424 revisions are specified, the working directory files are compared
1442 revisions are specified, the working directory files are compared
1425 to its parent.
1443 to its parent.
1426
1444
1427 Without the -a option, diff will avoid generating diffs of files
1445 Without the -a option, diff will avoid generating diffs of files
1428 it detects as binary. With -a, diff will generate a diff anyway,
1446 it detects as binary. With -a, diff will generate a diff anyway,
1429 probably with undesirable results.
1447 probably with undesirable results.
1430 """
1448 """
1431 node1, node2 = revpair(ui, repo, opts['rev'])
1449 node1, node2 = revpair(ui, repo, opts['rev'])
1432
1450
1433 fns, matchfn, anypats = matchpats(repo, pats, opts)
1451 fns, matchfn, anypats = matchpats(repo, pats, opts)
1434
1452
1435 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1453 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1436 text=opts['text'], opts=opts)
1454 text=opts['text'], opts=opts)
1437
1455
1438 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1456 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1439 node = repo.lookup(changeset)
1457 node = repo.lookup(changeset)
1440 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1458 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1441 if opts['switch_parent']:
1459 if opts['switch_parent']:
1442 parents.reverse()
1460 parents.reverse()
1443 prev = (parents and parents[0]) or nullid
1461 prev = (parents and parents[0]) or nullid
1444 change = repo.changelog.read(node)
1462 change = repo.changelog.read(node)
1445
1463
1446 fp = make_file(repo, repo.changelog, opts['output'],
1464 fp = make_file(repo, repo.changelog, opts['output'],
1447 node=node, total=total, seqno=seqno,
1465 node=node, total=total, seqno=seqno,
1448 revwidth=revwidth)
1466 revwidth=revwidth)
1449 if fp != sys.stdout:
1467 if fp != sys.stdout:
1450 ui.note("%s\n" % fp.name)
1468 ui.note("%s\n" % fp.name)
1451
1469
1452 fp.write("# HG changeset patch\n")
1470 fp.write("# HG changeset patch\n")
1453 fp.write("# User %s\n" % change[1])
1471 fp.write("# User %s\n" % change[1])
1454 fp.write("# Date %d %d\n" % change[2])
1472 fp.write("# Date %d %d\n" % change[2])
1455 fp.write("# Node ID %s\n" % hex(node))
1473 fp.write("# Node ID %s\n" % hex(node))
1456 fp.write("# Parent %s\n" % hex(prev))
1474 fp.write("# Parent %s\n" % hex(prev))
1457 if len(parents) > 1:
1475 if len(parents) > 1:
1458 fp.write("# Parent %s\n" % hex(parents[1]))
1476 fp.write("# Parent %s\n" % hex(parents[1]))
1459 fp.write(change[4].rstrip())
1477 fp.write(change[4].rstrip())
1460 fp.write("\n\n")
1478 fp.write("\n\n")
1461
1479
1462 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1480 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1463 if fp != sys.stdout:
1481 if fp != sys.stdout:
1464 fp.close()
1482 fp.close()
1465
1483
1466 def export(ui, repo, *changesets, **opts):
1484 def export(ui, repo, *changesets, **opts):
1467 """dump the header and diffs for one or more changesets
1485 """dump the header and diffs for one or more changesets
1468
1486
1469 Print the changeset header and diffs for one or more revisions.
1487 Print the changeset header and diffs for one or more revisions.
1470
1488
1471 The information shown in the changeset header is: author,
1489 The information shown in the changeset header is: author,
1472 changeset hash, parent and commit comment.
1490 changeset hash, parent and commit comment.
1473
1491
1474 Output may be to a file, in which case the name of the file is
1492 Output may be to a file, in which case the name of the file is
1475 given using a format string. The formatting rules are as follows:
1493 given using a format string. The formatting rules are as follows:
1476
1494
1477 %% literal "%" character
1495 %% literal "%" character
1478 %H changeset hash (40 bytes of hexadecimal)
1496 %H changeset hash (40 bytes of hexadecimal)
1479 %N number of patches being generated
1497 %N number of patches being generated
1480 %R changeset revision number
1498 %R changeset revision number
1481 %b basename of the exporting repository
1499 %b basename of the exporting repository
1482 %h short-form changeset hash (12 bytes of hexadecimal)
1500 %h short-form changeset hash (12 bytes of hexadecimal)
1483 %n zero-padded sequence number, starting at 1
1501 %n zero-padded sequence number, starting at 1
1484 %r zero-padded changeset revision number
1502 %r zero-padded changeset revision number
1485
1503
1486 Without the -a option, export will avoid generating diffs of files
1504 Without the -a option, export will avoid generating diffs of files
1487 it detects as binary. With -a, export will generate a diff anyway,
1505 it detects as binary. With -a, export will generate a diff anyway,
1488 probably with undesirable results.
1506 probably with undesirable results.
1489
1507
1490 With the --switch-parent option, the diff will be against the second
1508 With the --switch-parent option, the diff will be against the second
1491 parent. It can be useful to review a merge.
1509 parent. It can be useful to review a merge.
1492 """
1510 """
1493 if not changesets:
1511 if not changesets:
1494 raise util.Abort(_("export requires at least one changeset"))
1512 raise util.Abort(_("export requires at least one changeset"))
1495 seqno = 0
1513 seqno = 0
1496 revs = list(revrange(ui, repo, changesets))
1514 revs = list(revrange(ui, repo, changesets))
1497 total = len(revs)
1515 total = len(revs)
1498 revwidth = max(map(len, revs))
1516 revwidth = max(map(len, revs))
1499 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1517 msg = len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n")
1500 ui.note(msg)
1518 ui.note(msg)
1501 for cset in revs:
1519 for cset in revs:
1502 seqno += 1
1520 seqno += 1
1503 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1521 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1504
1522
1505 def forget(ui, repo, *pats, **opts):
1523 def forget(ui, repo, *pats, **opts):
1506 """don't add the specified files on the next commit (DEPRECATED)
1524 """don't add the specified files on the next commit (DEPRECATED)
1507
1525
1508 (DEPRECATED)
1526 (DEPRECATED)
1509 Undo an 'hg add' scheduled for the next commit.
1527 Undo an 'hg add' scheduled for the next commit.
1510
1528
1511 This command is now deprecated and will be removed in a future
1529 This command is now deprecated and will be removed in a future
1512 release. Please use revert instead.
1530 release. Please use revert instead.
1513 """
1531 """
1514 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1532 ui.warn(_("(the forget command is deprecated; use revert instead)\n"))
1515 forget = []
1533 forget = []
1516 for src, abs, rel, exact in walk(repo, pats, opts):
1534 for src, abs, rel, exact in walk(repo, pats, opts):
1517 if repo.dirstate.state(abs) == 'a':
1535 if repo.dirstate.state(abs) == 'a':
1518 forget.append(abs)
1536 forget.append(abs)
1519 if ui.verbose or not exact:
1537 if ui.verbose or not exact:
1520 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1538 ui.status(_('forgetting %s\n') % ((pats and rel) or abs))
1521 repo.forget(forget)
1539 repo.forget(forget)
1522
1540
1523 def grep(ui, repo, pattern, *pats, **opts):
1541 def grep(ui, repo, pattern, *pats, **opts):
1524 """search for a pattern in specified files and revisions
1542 """search for a pattern in specified files and revisions
1525
1543
1526 Search revisions of files for a regular expression.
1544 Search revisions of files for a regular expression.
1527
1545
1528 This command behaves differently than Unix grep. It only accepts
1546 This command behaves differently than Unix grep. It only accepts
1529 Python/Perl regexps. It searches repository history, not the
1547 Python/Perl regexps. It searches repository history, not the
1530 working directory. It always prints the revision number in which
1548 working directory. It always prints the revision number in which
1531 a match appears.
1549 a match appears.
1532
1550
1533 By default, grep only prints output for the first revision of a
1551 By default, grep only prints output for the first revision of a
1534 file in which it finds a match. To get it to print every revision
1552 file in which it finds a match. To get it to print every revision
1535 that contains a change in match status ("-" for a match that
1553 that contains a change in match status ("-" for a match that
1536 becomes a non-match, or "+" for a non-match that becomes a match),
1554 becomes a non-match, or "+" for a non-match that becomes a match),
1537 use the --all flag.
1555 use the --all flag.
1538 """
1556 """
1539 reflags = 0
1557 reflags = 0
1540 if opts['ignore_case']:
1558 if opts['ignore_case']:
1541 reflags |= re.I
1559 reflags |= re.I
1542 regexp = re.compile(pattern, reflags)
1560 regexp = re.compile(pattern, reflags)
1543 sep, eol = ':', '\n'
1561 sep, eol = ':', '\n'
1544 if opts['print0']:
1562 if opts['print0']:
1545 sep = eol = '\0'
1563 sep = eol = '\0'
1546
1564
1547 fcache = {}
1565 fcache = {}
1548 def getfile(fn):
1566 def getfile(fn):
1549 if fn not in fcache:
1567 if fn not in fcache:
1550 fcache[fn] = repo.file(fn)
1568 fcache[fn] = repo.file(fn)
1551 return fcache[fn]
1569 return fcache[fn]
1552
1570
1553 def matchlines(body):
1571 def matchlines(body):
1554 begin = 0
1572 begin = 0
1555 linenum = 0
1573 linenum = 0
1556 while True:
1574 while True:
1557 match = regexp.search(body, begin)
1575 match = regexp.search(body, begin)
1558 if not match:
1576 if not match:
1559 break
1577 break
1560 mstart, mend = match.span()
1578 mstart, mend = match.span()
1561 linenum += body.count('\n', begin, mstart) + 1
1579 linenum += body.count('\n', begin, mstart) + 1
1562 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1580 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1563 lend = body.find('\n', mend)
1581 lend = body.find('\n', mend)
1564 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1582 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1565 begin = lend + 1
1583 begin = lend + 1
1566
1584
1567 class linestate(object):
1585 class linestate(object):
1568 def __init__(self, line, linenum, colstart, colend):
1586 def __init__(self, line, linenum, colstart, colend):
1569 self.line = line
1587 self.line = line
1570 self.linenum = linenum
1588 self.linenum = linenum
1571 self.colstart = colstart
1589 self.colstart = colstart
1572 self.colend = colend
1590 self.colend = colend
1573 def __eq__(self, other):
1591 def __eq__(self, other):
1574 return self.line == other.line
1592 return self.line == other.line
1575 def __hash__(self):
1593 def __hash__(self):
1576 return hash(self.line)
1594 return hash(self.line)
1577
1595
1578 matches = {}
1596 matches = {}
1579 def grepbody(fn, rev, body):
1597 def grepbody(fn, rev, body):
1580 matches[rev].setdefault(fn, {})
1598 matches[rev].setdefault(fn, {})
1581 m = matches[rev][fn]
1599 m = matches[rev][fn]
1582 for lnum, cstart, cend, line in matchlines(body):
1600 for lnum, cstart, cend, line in matchlines(body):
1583 s = linestate(line, lnum, cstart, cend)
1601 s = linestate(line, lnum, cstart, cend)
1584 m[s] = s
1602 m[s] = s
1585
1603
1586 # FIXME: prev isn't used, why ?
1604 # FIXME: prev isn't used, why ?
1587 prev = {}
1605 prev = {}
1588 ucache = {}
1606 ucache = {}
1589 def display(fn, rev, states, prevstates):
1607 def display(fn, rev, states, prevstates):
1590 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1608 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1591 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1609 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1592 counts = {'-': 0, '+': 0}
1610 counts = {'-': 0, '+': 0}
1593 filerevmatches = {}
1611 filerevmatches = {}
1594 for l in diff:
1612 for l in diff:
1595 if incrementing or not opts['all']:
1613 if incrementing or not opts['all']:
1596 change = ((l in prevstates) and '-') or '+'
1614 change = ((l in prevstates) and '-') or '+'
1597 r = rev
1615 r = rev
1598 else:
1616 else:
1599 change = ((l in states) and '-') or '+'
1617 change = ((l in states) and '-') or '+'
1600 r = prev[fn]
1618 r = prev[fn]
1601 cols = [fn, str(rev)]
1619 cols = [fn, str(rev)]
1602 if opts['line_number']:
1620 if opts['line_number']:
1603 cols.append(str(l.linenum))
1621 cols.append(str(l.linenum))
1604 if opts['all']:
1622 if opts['all']:
1605 cols.append(change)
1623 cols.append(change)
1606 if opts['user']:
1624 if opts['user']:
1607 cols.append(trimuser(ui, getchange(rev)[1], rev,
1625 cols.append(trimuser(ui, getchange(rev)[1], rev,
1608 ucache))
1626 ucache))
1609 if opts['files_with_matches']:
1627 if opts['files_with_matches']:
1610 c = (fn, rev)
1628 c = (fn, rev)
1611 if c in filerevmatches:
1629 if c in filerevmatches:
1612 continue
1630 continue
1613 filerevmatches[c] = 1
1631 filerevmatches[c] = 1
1614 else:
1632 else:
1615 cols.append(l.line)
1633 cols.append(l.line)
1616 ui.write(sep.join(cols), eol)
1634 ui.write(sep.join(cols), eol)
1617 counts[change] += 1
1635 counts[change] += 1
1618 return counts['+'], counts['-']
1636 return counts['+'], counts['-']
1619
1637
1620 fstate = {}
1638 fstate = {}
1621 skip = {}
1639 skip = {}
1622 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1640 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1623 count = 0
1641 count = 0
1624 incrementing = False
1642 incrementing = False
1625 for st, rev, fns in changeiter:
1643 for st, rev, fns in changeiter:
1626 if st == 'window':
1644 if st == 'window':
1627 incrementing = rev
1645 incrementing = rev
1628 matches.clear()
1646 matches.clear()
1629 elif st == 'add':
1647 elif st == 'add':
1630 change = repo.changelog.read(repo.lookup(str(rev)))
1648 change = repo.changelog.read(repo.lookup(str(rev)))
1631 mf = repo.manifest.read(change[0])
1649 mf = repo.manifest.read(change[0])
1632 matches[rev] = {}
1650 matches[rev] = {}
1633 for fn in fns:
1651 for fn in fns:
1634 if fn in skip:
1652 if fn in skip:
1635 continue
1653 continue
1636 fstate.setdefault(fn, {})
1654 fstate.setdefault(fn, {})
1637 try:
1655 try:
1638 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1656 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1639 except KeyError:
1657 except KeyError:
1640 pass
1658 pass
1641 elif st == 'iter':
1659 elif st == 'iter':
1642 states = matches[rev].items()
1660 states = matches[rev].items()
1643 states.sort()
1661 states.sort()
1644 for fn, m in states:
1662 for fn, m in states:
1645 if fn in skip:
1663 if fn in skip:
1646 continue
1664 continue
1647 if incrementing or not opts['all'] or fstate[fn]:
1665 if incrementing or not opts['all'] or fstate[fn]:
1648 pos, neg = display(fn, rev, m, fstate[fn])
1666 pos, neg = display(fn, rev, m, fstate[fn])
1649 count += pos + neg
1667 count += pos + neg
1650 if pos and not opts['all']:
1668 if pos and not opts['all']:
1651 skip[fn] = True
1669 skip[fn] = True
1652 fstate[fn] = m
1670 fstate[fn] = m
1653 prev[fn] = rev
1671 prev[fn] = rev
1654
1672
1655 if not incrementing:
1673 if not incrementing:
1656 fstate = fstate.items()
1674 fstate = fstate.items()
1657 fstate.sort()
1675 fstate.sort()
1658 for fn, state in fstate:
1676 for fn, state in fstate:
1659 if fn in skip:
1677 if fn in skip:
1660 continue
1678 continue
1661 display(fn, rev, {}, state)
1679 display(fn, rev, {}, state)
1662 return (count == 0 and 1) or 0
1680 return (count == 0 and 1) or 0
1663
1681
1664 def heads(ui, repo, **opts):
1682 def heads(ui, repo, **opts):
1665 """show current repository heads
1683 """show current repository heads
1666
1684
1667 Show all repository head changesets.
1685 Show all repository head changesets.
1668
1686
1669 Repository "heads" are changesets that don't have children
1687 Repository "heads" are changesets that don't have children
1670 changesets. They are where development generally takes place and
1688 changesets. They are where development generally takes place and
1671 are the usual targets for update and merge operations.
1689 are the usual targets for update and merge operations.
1672 """
1690 """
1673 if opts['rev']:
1691 if opts['rev']:
1674 heads = repo.heads(repo.lookup(opts['rev']))
1692 heads = repo.heads(repo.lookup(opts['rev']))
1675 else:
1693 else:
1676 heads = repo.heads()
1694 heads = repo.heads()
1677 br = None
1695 br = None
1678 if opts['branches']:
1696 if opts['branches']:
1679 br = repo.branchlookup(heads)
1697 br = repo.branchlookup(heads)
1680 displayer = show_changeset(ui, repo, opts)
1698 displayer = show_changeset(ui, repo, opts)
1681 for n in heads:
1699 for n in heads:
1682 displayer.show(changenode=n, brinfo=br)
1700 displayer.show(changenode=n, brinfo=br)
1683
1701
1684 def identify(ui, repo):
1702 def identify(ui, repo):
1685 """print information about the working copy
1703 """print information about the working copy
1686
1704
1687 Print a short summary of the current state of the repo.
1705 Print a short summary of the current state of the repo.
1688
1706
1689 This summary identifies the repository state using one or two parent
1707 This summary identifies the repository state using one or two parent
1690 hash identifiers, followed by a "+" if there are uncommitted changes
1708 hash identifiers, followed by a "+" if there are uncommitted changes
1691 in the working directory, followed by a list of tags for this revision.
1709 in the working directory, followed by a list of tags for this revision.
1692 """
1710 """
1693 parents = [p for p in repo.dirstate.parents() if p != nullid]
1711 parents = [p for p in repo.dirstate.parents() if p != nullid]
1694 if not parents:
1712 if not parents:
1695 ui.write(_("unknown\n"))
1713 ui.write(_("unknown\n"))
1696 return
1714 return
1697
1715
1698 hexfunc = ui.verbose and hex or short
1716 hexfunc = ui.verbose and hex or short
1699 modified, added, removed, deleted, unknown = repo.changes()
1717 modified, added, removed, deleted, unknown = repo.changes()
1700 output = ["%s%s" %
1718 output = ["%s%s" %
1701 ('+'.join([hexfunc(parent) for parent in parents]),
1719 ('+'.join([hexfunc(parent) for parent in parents]),
1702 (modified or added or removed or deleted) and "+" or "")]
1720 (modified or added or removed or deleted) and "+" or "")]
1703
1721
1704 if not ui.quiet:
1722 if not ui.quiet:
1705 # multiple tags for a single parent separated by '/'
1723 # multiple tags for a single parent separated by '/'
1706 parenttags = ['/'.join(tags)
1724 parenttags = ['/'.join(tags)
1707 for tags in map(repo.nodetags, parents) if tags]
1725 for tags in map(repo.nodetags, parents) if tags]
1708 # tags for multiple parents separated by ' + '
1726 # tags for multiple parents separated by ' + '
1709 if parenttags:
1727 if parenttags:
1710 output.append(' + '.join(parenttags))
1728 output.append(' + '.join(parenttags))
1711
1729
1712 ui.write("%s\n" % ' '.join(output))
1730 ui.write("%s\n" % ' '.join(output))
1713
1731
1714 def import_(ui, repo, patch1, *patches, **opts):
1732 def import_(ui, repo, patch1, *patches, **opts):
1715 """import an ordered set of patches
1733 """import an ordered set of patches
1716
1734
1717 Import a list of patches and commit them individually.
1735 Import a list of patches and commit them individually.
1718
1736
1719 If there are outstanding changes in the working directory, import
1737 If there are outstanding changes in the working directory, import
1720 will abort unless given the -f flag.
1738 will abort unless given the -f flag.
1721
1739
1722 You can import a patch straight from a mail message. Even patches
1740 You can import a patch straight from a mail message. Even patches
1723 as attachments work (body part must be type text/plain or
1741 as attachments work (body part must be type text/plain or
1724 text/x-patch to be used). From and Subject headers of email
1742 text/x-patch to be used). From and Subject headers of email
1725 message are used as default committer and commit message. All
1743 message are used as default committer and commit message. All
1726 text/plain body parts before first diff are added to commit
1744 text/plain body parts before first diff are added to commit
1727 message.
1745 message.
1728
1746
1729 If imported patch was generated by hg export, user and description
1747 If imported patch was generated by hg export, user and description
1730 from patch override values from message headers and body. Values
1748 from patch override values from message headers and body. Values
1731 given on command line with -m and -u override these.
1749 given on command line with -m and -u override these.
1732
1750
1733 To read a patch from standard input, use patch name "-".
1751 To read a patch from standard input, use patch name "-".
1734 """
1752 """
1735 patches = (patch1,) + patches
1753 patches = (patch1,) + patches
1736
1754
1737 if not opts['force']:
1755 if not opts['force']:
1738 bail_if_changed(repo)
1756 bail_if_changed(repo)
1739
1757
1740 d = opts["base"]
1758 d = opts["base"]
1741 strip = opts["strip"]
1759 strip = opts["strip"]
1742
1760
1743 mailre = re.compile(r'(?:From |[\w-]+:)')
1761 mailre = re.compile(r'(?:From |[\w-]+:)')
1744
1762
1745 # attempt to detect the start of a patch
1763 # attempt to detect the start of a patch
1746 # (this heuristic is borrowed from quilt)
1764 # (this heuristic is borrowed from quilt)
1747 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1765 diffre = re.compile(r'^(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1748 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1766 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1749 '(---|\*\*\*)[ \t])', re.MULTILINE)
1767 '(---|\*\*\*)[ \t])', re.MULTILINE)
1750
1768
1751 for patch in patches:
1769 for patch in patches:
1752 pf = os.path.join(d, patch)
1770 pf = os.path.join(d, patch)
1753
1771
1754 message = None
1772 message = None
1755 user = None
1773 user = None
1756 date = None
1774 date = None
1757 hgpatch = False
1775 hgpatch = False
1758
1776
1759 p = email.Parser.Parser()
1777 p = email.Parser.Parser()
1760 if pf == '-':
1778 if pf == '-':
1761 msg = p.parse(sys.stdin)
1779 msg = p.parse(sys.stdin)
1762 ui.status(_("applying patch from stdin\n"))
1780 ui.status(_("applying patch from stdin\n"))
1763 else:
1781 else:
1764 msg = p.parse(file(pf))
1782 msg = p.parse(file(pf))
1765 ui.status(_("applying %s\n") % patch)
1783 ui.status(_("applying %s\n") % patch)
1766
1784
1767 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
1785 fd, tmpname = tempfile.mkstemp(prefix='hg-patch-')
1768 tmpfp = os.fdopen(fd, 'w')
1786 tmpfp = os.fdopen(fd, 'w')
1769 try:
1787 try:
1770 message = msg['Subject']
1788 message = msg['Subject']
1771 if message:
1789 if message:
1772 message = message.replace('\n\t', ' ')
1790 message = message.replace('\n\t', ' ')
1773 ui.debug('Subject: %s\n' % message)
1791 ui.debug('Subject: %s\n' % message)
1774 user = msg['From']
1792 user = msg['From']
1775 if user:
1793 if user:
1776 ui.debug('From: %s\n' % user)
1794 ui.debug('From: %s\n' % user)
1777 diffs_seen = 0
1795 diffs_seen = 0
1778 ok_types = ('text/plain', 'text/x-patch')
1796 ok_types = ('text/plain', 'text/x-patch')
1779 for part in msg.walk():
1797 for part in msg.walk():
1780 content_type = part.get_content_type()
1798 content_type = part.get_content_type()
1781 ui.debug('Content-Type: %s\n' % content_type)
1799 ui.debug('Content-Type: %s\n' % content_type)
1782 if content_type not in ok_types:
1800 if content_type not in ok_types:
1783 continue
1801 continue
1784 payload = part.get_payload(decode=True)
1802 payload = part.get_payload(decode=True)
1785 m = diffre.search(payload)
1803 m = diffre.search(payload)
1786 if m:
1804 if m:
1787 ui.debug(_('found patch at byte %d\n') % m.start(0))
1805 ui.debug(_('found patch at byte %d\n') % m.start(0))
1788 diffs_seen += 1
1806 diffs_seen += 1
1789 hgpatch = False
1807 hgpatch = False
1790 fp = cStringIO.StringIO()
1808 fp = cStringIO.StringIO()
1791 if message:
1809 if message:
1792 fp.write(message)
1810 fp.write(message)
1793 fp.write('\n')
1811 fp.write('\n')
1794 for line in payload[:m.start(0)].splitlines():
1812 for line in payload[:m.start(0)].splitlines():
1795 if line.startswith('# HG changeset patch'):
1813 if line.startswith('# HG changeset patch'):
1796 ui.debug(_('patch generated by hg export\n'))
1814 ui.debug(_('patch generated by hg export\n'))
1797 hgpatch = True
1815 hgpatch = True
1798 # drop earlier commit message content
1816 # drop earlier commit message content
1799 fp.seek(0)
1817 fp.seek(0)
1800 fp.truncate()
1818 fp.truncate()
1801 elif hgpatch:
1819 elif hgpatch:
1802 if line.startswith('# User '):
1820 if line.startswith('# User '):
1803 user = line[7:]
1821 user = line[7:]
1804 ui.debug('From: %s\n' % user)
1822 ui.debug('From: %s\n' % user)
1805 elif line.startswith("# Date "):
1823 elif line.startswith("# Date "):
1806 date = line[7:]
1824 date = line[7:]
1807 if not line.startswith('# '):
1825 if not line.startswith('# '):
1808 fp.write(line)
1826 fp.write(line)
1809 fp.write('\n')
1827 fp.write('\n')
1810 message = fp.getvalue()
1828 message = fp.getvalue()
1811 if tmpfp:
1829 if tmpfp:
1812 tmpfp.write(payload)
1830 tmpfp.write(payload)
1813 if not payload.endswith('\n'):
1831 if not payload.endswith('\n'):
1814 tmpfp.write('\n')
1832 tmpfp.write('\n')
1815 elif not diffs_seen and message and content_type == 'text/plain':
1833 elif not diffs_seen and message and content_type == 'text/plain':
1816 message += '\n' + payload
1834 message += '\n' + payload
1817
1835
1818 if opts['message']:
1836 if opts['message']:
1819 # pickup the cmdline msg
1837 # pickup the cmdline msg
1820 message = opts['message']
1838 message = opts['message']
1821 elif message:
1839 elif message:
1822 # pickup the patch msg
1840 # pickup the patch msg
1823 message = message.strip()
1841 message = message.strip()
1824 else:
1842 else:
1825 # launch the editor
1843 # launch the editor
1826 message = None
1844 message = None
1827 ui.debug(_('message:\n%s\n') % message)
1845 ui.debug(_('message:\n%s\n') % message)
1828
1846
1829 tmpfp.close()
1847 tmpfp.close()
1830 if not diffs_seen:
1848 if not diffs_seen:
1831 raise util.Abort(_('no diffs found'))
1849 raise util.Abort(_('no diffs found'))
1832
1850
1833 files = util.patch(strip, tmpname, ui)
1851 files = util.patch(strip, tmpname, ui)
1834 if len(files) > 0:
1852 if len(files) > 0:
1835 addremove_lock(ui, repo, files, {})
1853 addremove_lock(ui, repo, files, {})
1836 repo.commit(files, message, user, date)
1854 repo.commit(files, message, user, date)
1837 finally:
1855 finally:
1838 os.unlink(tmpname)
1856 os.unlink(tmpname)
1839
1857
1840 def incoming(ui, repo, source="default", **opts):
1858 def incoming(ui, repo, source="default", **opts):
1841 """show new changesets found in source
1859 """show new changesets found in source
1842
1860
1843 Show new changesets found in the specified path/URL or the default
1861 Show new changesets found in the specified path/URL or the default
1844 pull location. These are the changesets that would be pulled if a pull
1862 pull location. These are the changesets that would be pulled if a pull
1845 was requested.
1863 was requested.
1846
1864
1847 For remote repository, using --bundle avoids downloading the changesets
1865 For remote repository, using --bundle avoids downloading the changesets
1848 twice if the incoming is followed by a pull.
1866 twice if the incoming is followed by a pull.
1849
1867
1850 See pull for valid source format details.
1868 See pull for valid source format details.
1851 """
1869 """
1852 source = ui.expandpath(source)
1870 source = ui.expandpath(source)
1853 if opts['ssh']:
1871 if opts['ssh']:
1854 ui.setconfig("ui", "ssh", opts['ssh'])
1872 ui.setconfig("ui", "ssh", opts['ssh'])
1855 if opts['remotecmd']:
1873 if opts['remotecmd']:
1856 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1874 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1857
1875
1858 other = hg.repository(ui, source)
1876 other = hg.repository(ui, source)
1859 incoming = repo.findincoming(other, force=opts["force"])
1877 incoming = repo.findincoming(other, force=opts["force"])
1860 if not incoming:
1878 if not incoming:
1861 ui.status(_("no changes found\n"))
1879 ui.status(_("no changes found\n"))
1862 return
1880 return
1863
1881
1864 cleanup = None
1882 cleanup = None
1865 try:
1883 try:
1866 fname = opts["bundle"]
1884 fname = opts["bundle"]
1867 if fname or not other.local():
1885 if fname or not other.local():
1868 # create a bundle (uncompressed if other repo is not local)
1886 # create a bundle (uncompressed if other repo is not local)
1869 cg = other.changegroup(incoming, "incoming")
1887 cg = other.changegroup(incoming, "incoming")
1870 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1888 fname = cleanup = write_bundle(cg, fname, compress=other.local())
1871 # keep written bundle?
1889 # keep written bundle?
1872 if opts["bundle"]:
1890 if opts["bundle"]:
1873 cleanup = None
1891 cleanup = None
1874 if not other.local():
1892 if not other.local():
1875 # use the created uncompressed bundlerepo
1893 # use the created uncompressed bundlerepo
1876 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1894 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1877
1895
1878 revs = None
1896 revs = None
1879 if opts['rev']:
1897 if opts['rev']:
1880 revs = [other.lookup(rev) for rev in opts['rev']]
1898 revs = [other.lookup(rev) for rev in opts['rev']]
1881 o = other.changelog.nodesbetween(incoming, revs)[0]
1899 o = other.changelog.nodesbetween(incoming, revs)[0]
1882 if opts['newest_first']:
1900 if opts['newest_first']:
1883 o.reverse()
1901 o.reverse()
1884 displayer = show_changeset(ui, other, opts)
1902 displayer = show_changeset(ui, other, opts)
1885 for n in o:
1903 for n in o:
1886 parents = [p for p in other.changelog.parents(n) if p != nullid]
1904 parents = [p for p in other.changelog.parents(n) if p != nullid]
1887 if opts['no_merges'] and len(parents) == 2:
1905 if opts['no_merges'] and len(parents) == 2:
1888 continue
1906 continue
1889 displayer.show(changenode=n)
1907 displayer.show(changenode=n)
1890 if opts['patch']:
1908 if opts['patch']:
1891 prev = (parents and parents[0]) or nullid
1909 prev = (parents and parents[0]) or nullid
1892 dodiff(ui, ui, other, prev, n)
1910 dodiff(ui, ui, other, prev, n)
1893 ui.write("\n")
1911 ui.write("\n")
1894 finally:
1912 finally:
1895 if hasattr(other, 'close'):
1913 if hasattr(other, 'close'):
1896 other.close()
1914 other.close()
1897 if cleanup:
1915 if cleanup:
1898 os.unlink(cleanup)
1916 os.unlink(cleanup)
1899
1917
1900 def init(ui, dest="."):
1918 def init(ui, dest="."):
1901 """create a new repository in the given directory
1919 """create a new repository in the given directory
1902
1920
1903 Initialize a new repository in the given directory. If the given
1921 Initialize a new repository in the given directory. If the given
1904 directory does not exist, it is created.
1922 directory does not exist, it is created.
1905
1923
1906 If no directory is given, the current directory is used.
1924 If no directory is given, the current directory is used.
1907 """
1925 """
1908 if not os.path.exists(dest):
1909 os.mkdir(dest)
1910 hg.repository(ui, dest, create=1)
1926 hg.repository(ui, dest, create=1)
1911
1927
1912 def locate(ui, repo, *pats, **opts):
1928 def locate(ui, repo, *pats, **opts):
1913 """locate files matching specific patterns
1929 """locate files matching specific patterns
1914
1930
1915 Print all files under Mercurial control whose names match the
1931 Print all files under Mercurial control whose names match the
1916 given patterns.
1932 given patterns.
1917
1933
1918 This command searches the current directory and its
1934 This command searches the current directory and its
1919 subdirectories. To search an entire repository, move to the root
1935 subdirectories. To search an entire repository, move to the root
1920 of the repository.
1936 of the repository.
1921
1937
1922 If no patterns are given to match, this command prints all file
1938 If no patterns are given to match, this command prints all file
1923 names.
1939 names.
1924
1940
1925 If you want to feed the output of this command into the "xargs"
1941 If you want to feed the output of this command into the "xargs"
1926 command, use the "-0" option to both this command and "xargs".
1942 command, use the "-0" option to both this command and "xargs".
1927 This will avoid the problem of "xargs" treating single filenames
1943 This will avoid the problem of "xargs" treating single filenames
1928 that contain white space as multiple filenames.
1944 that contain white space as multiple filenames.
1929 """
1945 """
1930 end = opts['print0'] and '\0' or '\n'
1946 end = opts['print0'] and '\0' or '\n'
1931 rev = opts['rev']
1947 rev = opts['rev']
1932 if rev:
1948 if rev:
1933 node = repo.lookup(rev)
1949 node = repo.lookup(rev)
1934 else:
1950 else:
1935 node = None
1951 node = None
1936
1952
1937 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1953 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
1938 head='(?:.*/|)'):
1954 head='(?:.*/|)'):
1939 if not node and repo.dirstate.state(abs) == '?':
1955 if not node and repo.dirstate.state(abs) == '?':
1940 continue
1956 continue
1941 if opts['fullpath']:
1957 if opts['fullpath']:
1942 ui.write(os.path.join(repo.root, abs), end)
1958 ui.write(os.path.join(repo.root, abs), end)
1943 else:
1959 else:
1944 ui.write(((pats and rel) or abs), end)
1960 ui.write(((pats and rel) or abs), end)
1945
1961
1946 def log(ui, repo, *pats, **opts):
1962 def log(ui, repo, *pats, **opts):
1947 """show revision history of entire repository or files
1963 """show revision history of entire repository or files
1948
1964
1949 Print the revision history of the specified files or the entire project.
1965 Print the revision history of the specified files or the entire project.
1950
1966
1951 By default this command outputs: changeset id and hash, tags,
1967 By default this command outputs: changeset id and hash, tags,
1952 non-trivial parents, user, date and time, and a summary for each
1968 non-trivial parents, user, date and time, and a summary for each
1953 commit. When the -v/--verbose switch is used, the list of changed
1969 commit. When the -v/--verbose switch is used, the list of changed
1954 files and full commit message is shown.
1970 files and full commit message is shown.
1955 """
1971 """
1956 class dui(object):
1972 class dui(object):
1957 # Implement and delegate some ui protocol. Save hunks of
1973 # Implement and delegate some ui protocol. Save hunks of
1958 # output for later display in the desired order.
1974 # output for later display in the desired order.
1959 def __init__(self, ui):
1975 def __init__(self, ui):
1960 self.ui = ui
1976 self.ui = ui
1961 self.hunk = {}
1977 self.hunk = {}
1962 self.header = {}
1978 self.header = {}
1963 def bump(self, rev):
1979 def bump(self, rev):
1964 self.rev = rev
1980 self.rev = rev
1965 self.hunk[rev] = []
1981 self.hunk[rev] = []
1966 self.header[rev] = []
1982 self.header[rev] = []
1967 def note(self, *args):
1983 def note(self, *args):
1968 if self.verbose:
1984 if self.verbose:
1969 self.write(*args)
1985 self.write(*args)
1970 def status(self, *args):
1986 def status(self, *args):
1971 if not self.quiet:
1987 if not self.quiet:
1972 self.write(*args)
1988 self.write(*args)
1973 def write(self, *args):
1989 def write(self, *args):
1974 self.hunk[self.rev].append(args)
1990 self.hunk[self.rev].append(args)
1975 def write_header(self, *args):
1991 def write_header(self, *args):
1976 self.header[self.rev].append(args)
1992 self.header[self.rev].append(args)
1977 def debug(self, *args):
1993 def debug(self, *args):
1978 if self.debugflag:
1994 if self.debugflag:
1979 self.write(*args)
1995 self.write(*args)
1980 def __getattr__(self, key):
1996 def __getattr__(self, key):
1981 return getattr(self.ui, key)
1997 return getattr(self.ui, key)
1982
1998
1983 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1999 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1984
2000
1985 if opts['limit']:
2001 if opts['limit']:
1986 try:
2002 try:
1987 limit = int(opts['limit'])
2003 limit = int(opts['limit'])
1988 except ValueError:
2004 except ValueError:
1989 raise util.Abort(_('limit must be a positive integer'))
2005 raise util.Abort(_('limit must be a positive integer'))
1990 if limit <= 0: raise util.Abort(_('limit must be positive'))
2006 if limit <= 0: raise util.Abort(_('limit must be positive'))
1991 else:
2007 else:
1992 limit = sys.maxint
2008 limit = sys.maxint
1993 count = 0
2009 count = 0
1994
2010
1995 displayer = show_changeset(ui, repo, opts)
2011 displayer = show_changeset(ui, repo, opts)
1996 for st, rev, fns in changeiter:
2012 for st, rev, fns in changeiter:
1997 if st == 'window':
2013 if st == 'window':
1998 du = dui(ui)
2014 du = dui(ui)
1999 displayer.ui = du
2015 displayer.ui = du
2000 elif st == 'add':
2016 elif st == 'add':
2001 du.bump(rev)
2017 du.bump(rev)
2002 changenode = repo.changelog.node(rev)
2018 changenode = repo.changelog.node(rev)
2003 parents = [p for p in repo.changelog.parents(changenode)
2019 parents = [p for p in repo.changelog.parents(changenode)
2004 if p != nullid]
2020 if p != nullid]
2005 if opts['no_merges'] and len(parents) == 2:
2021 if opts['no_merges'] and len(parents) == 2:
2006 continue
2022 continue
2007 if opts['only_merges'] and len(parents) != 2:
2023 if opts['only_merges'] and len(parents) != 2:
2008 continue
2024 continue
2009
2025
2010 if opts['keyword']:
2026 if opts['keyword']:
2011 changes = getchange(rev)
2027 changes = getchange(rev)
2012 miss = 0
2028 miss = 0
2013 for k in [kw.lower() for kw in opts['keyword']]:
2029 for k in [kw.lower() for kw in opts['keyword']]:
2014 if not (k in changes[1].lower() or
2030 if not (k in changes[1].lower() or
2015 k in changes[4].lower() or
2031 k in changes[4].lower() or
2016 k in " ".join(changes[3][:20]).lower()):
2032 k in " ".join(changes[3][:20]).lower()):
2017 miss = 1
2033 miss = 1
2018 break
2034 break
2019 if miss:
2035 if miss:
2020 continue
2036 continue
2021
2037
2022 br = None
2038 br = None
2023 if opts['branches']:
2039 if opts['branches']:
2024 br = repo.branchlookup([repo.changelog.node(rev)])
2040 br = repo.branchlookup([repo.changelog.node(rev)])
2025
2041
2026 displayer.show(rev, brinfo=br)
2042 displayer.show(rev, brinfo=br)
2027 if opts['patch']:
2043 if opts['patch']:
2028 prev = (parents and parents[0]) or nullid
2044 prev = (parents and parents[0]) or nullid
2029 dodiff(du, du, repo, prev, changenode, match=matchfn)
2045 dodiff(du, du, repo, prev, changenode, match=matchfn)
2030 du.write("\n\n")
2046 du.write("\n\n")
2031 elif st == 'iter':
2047 elif st == 'iter':
2032 if count == limit: break
2048 if count == limit: break
2033 if du.header[rev]:
2049 if du.header[rev]:
2034 for args in du.header[rev]:
2050 for args in du.header[rev]:
2035 ui.write_header(*args)
2051 ui.write_header(*args)
2036 if du.hunk[rev]:
2052 if du.hunk[rev]:
2037 count += 1
2053 count += 1
2038 for args in du.hunk[rev]:
2054 for args in du.hunk[rev]:
2039 ui.write(*args)
2055 ui.write(*args)
2040
2056
2041 def manifest(ui, repo, rev=None):
2057 def manifest(ui, repo, rev=None):
2042 """output the latest or given revision of the project manifest
2058 """output the latest or given revision of the project manifest
2043
2059
2044 Print a list of version controlled files for the given revision.
2060 Print a list of version controlled files for the given revision.
2045
2061
2046 The manifest is the list of files being version controlled. If no revision
2062 The manifest is the list of files being version controlled. If no revision
2047 is given then the tip is used.
2063 is given then the tip is used.
2048 """
2064 """
2049 if rev:
2065 if rev:
2050 try:
2066 try:
2051 # assume all revision numbers are for changesets
2067 # assume all revision numbers are for changesets
2052 n = repo.lookup(rev)
2068 n = repo.lookup(rev)
2053 change = repo.changelog.read(n)
2069 change = repo.changelog.read(n)
2054 n = change[0]
2070 n = change[0]
2055 except hg.RepoError:
2071 except hg.RepoError:
2056 n = repo.manifest.lookup(rev)
2072 n = repo.manifest.lookup(rev)
2057 else:
2073 else:
2058 n = repo.manifest.tip()
2074 n = repo.manifest.tip()
2059 m = repo.manifest.read(n)
2075 m = repo.manifest.read(n)
2060 mf = repo.manifest.readflags(n)
2076 mf = repo.manifest.readflags(n)
2061 files = m.keys()
2077 files = m.keys()
2062 files.sort()
2078 files.sort()
2063
2079
2064 for f in files:
2080 for f in files:
2065 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
2081 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
2066
2082
2067 def merge(ui, repo, node=None, **opts):
2083 def merge(ui, repo, node=None, **opts):
2068 """Merge working directory with another revision
2084 """Merge working directory with another revision
2069
2085
2070 Merge the contents of the current working directory and the
2086 Merge the contents of the current working directory and the
2071 requested revision. Files that changed between either parent are
2087 requested revision. Files that changed between either parent are
2072 marked as changed for the next commit and a commit must be
2088 marked as changed for the next commit and a commit must be
2073 performed before any further updates are allowed.
2089 performed before any further updates are allowed.
2074 """
2090 """
2075 return doupdate(ui, repo, node=node, merge=True, **opts)
2091 return doupdate(ui, repo, node=node, merge=True, **opts)
2076
2092
2077 def outgoing(ui, repo, dest=None, **opts):
2093 def outgoing(ui, repo, dest=None, **opts):
2078 """show changesets not found in destination
2094 """show changesets not found in destination
2079
2095
2080 Show changesets not found in the specified destination repository or
2096 Show changesets not found in the specified destination repository or
2081 the default push location. These are the changesets that would be pushed
2097 the default push location. These are the changesets that would be pushed
2082 if a push was requested.
2098 if a push was requested.
2083
2099
2084 See pull for valid destination format details.
2100 See pull for valid destination format details.
2085 """
2101 """
2086 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2102 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2087 if opts['ssh']:
2103 if opts['ssh']:
2088 ui.setconfig("ui", "ssh", opts['ssh'])
2104 ui.setconfig("ui", "ssh", opts['ssh'])
2089 if opts['remotecmd']:
2105 if opts['remotecmd']:
2090 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2106 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2091 revs = None
2107 revs = None
2092 if opts['rev']:
2108 if opts['rev']:
2093 revs = [repo.lookup(rev) for rev in opts['rev']]
2109 revs = [repo.lookup(rev) for rev in opts['rev']]
2094
2110
2095 other = hg.repository(ui, dest)
2111 other = hg.repository(ui, dest)
2096 o = repo.findoutgoing(other, force=opts['force'])
2112 o = repo.findoutgoing(other, force=opts['force'])
2097 if not o:
2113 if not o:
2098 ui.status(_("no changes found\n"))
2114 ui.status(_("no changes found\n"))
2099 return
2115 return
2100 o = repo.changelog.nodesbetween(o, revs)[0]
2116 o = repo.changelog.nodesbetween(o, revs)[0]
2101 if opts['newest_first']:
2117 if opts['newest_first']:
2102 o.reverse()
2118 o.reverse()
2103 displayer = show_changeset(ui, repo, opts)
2119 displayer = show_changeset(ui, repo, opts)
2104 for n in o:
2120 for n in o:
2105 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2121 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2106 if opts['no_merges'] and len(parents) == 2:
2122 if opts['no_merges'] and len(parents) == 2:
2107 continue
2123 continue
2108 displayer.show(changenode=n)
2124 displayer.show(changenode=n)
2109 if opts['patch']:
2125 if opts['patch']:
2110 prev = (parents and parents[0]) or nullid
2126 prev = (parents and parents[0]) or nullid
2111 dodiff(ui, ui, repo, prev, n)
2127 dodiff(ui, ui, repo, prev, n)
2112 ui.write("\n")
2128 ui.write("\n")
2113
2129
2114 def parents(ui, repo, rev=None, branches=None, **opts):
2130 def parents(ui, repo, rev=None, branches=None, **opts):
2115 """show the parents of the working dir or revision
2131 """show the parents of the working dir or revision
2116
2132
2117 Print the working directory's parent revisions.
2133 Print the working directory's parent revisions.
2118 """
2134 """
2119 if rev:
2135 if rev:
2120 p = repo.changelog.parents(repo.lookup(rev))
2136 p = repo.changelog.parents(repo.lookup(rev))
2121 else:
2137 else:
2122 p = repo.dirstate.parents()
2138 p = repo.dirstate.parents()
2123
2139
2124 br = None
2140 br = None
2125 if branches is not None:
2141 if branches is not None:
2126 br = repo.branchlookup(p)
2142 br = repo.branchlookup(p)
2127 displayer = show_changeset(ui, repo, opts)
2143 displayer = show_changeset(ui, repo, opts)
2128 for n in p:
2144 for n in p:
2129 if n != nullid:
2145 if n != nullid:
2130 displayer.show(changenode=n, brinfo=br)
2146 displayer.show(changenode=n, brinfo=br)
2131
2147
2132 def paths(ui, repo, search=None):
2148 def paths(ui, repo, search=None):
2133 """show definition of symbolic path names
2149 """show definition of symbolic path names
2134
2150
2135 Show definition of symbolic path name NAME. If no name is given, show
2151 Show definition of symbolic path name NAME. If no name is given, show
2136 definition of available names.
2152 definition of available names.
2137
2153
2138 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2154 Path names are defined in the [paths] section of /etc/mercurial/hgrc
2139 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2155 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2140 """
2156 """
2141 if search:
2157 if search:
2142 for name, path in ui.configitems("paths"):
2158 for name, path in ui.configitems("paths"):
2143 if name == search:
2159 if name == search:
2144 ui.write("%s\n" % path)
2160 ui.write("%s\n" % path)
2145 return
2161 return
2146 ui.warn(_("not found!\n"))
2162 ui.warn(_("not found!\n"))
2147 return 1
2163 return 1
2148 else:
2164 else:
2149 for name, path in ui.configitems("paths"):
2165 for name, path in ui.configitems("paths"):
2150 ui.write("%s = %s\n" % (name, path))
2166 ui.write("%s = %s\n" % (name, path))
2151
2167
2152 def postincoming(ui, repo, modheads, optupdate):
2168 def postincoming(ui, repo, modheads, optupdate):
2153 if modheads == 0:
2169 if modheads == 0:
2154 return
2170 return
2155 if optupdate:
2171 if optupdate:
2156 if modheads == 1:
2172 if modheads == 1:
2157 return doupdate(ui, repo)
2173 return doupdate(ui, repo)
2158 else:
2174 else:
2159 ui.status(_("not updating, since new heads added\n"))
2175 ui.status(_("not updating, since new heads added\n"))
2160 if modheads > 1:
2176 if modheads > 1:
2161 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2177 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2162 else:
2178 else:
2163 ui.status(_("(run 'hg update' to get a working copy)\n"))
2179 ui.status(_("(run 'hg update' to get a working copy)\n"))
2164
2180
2165 def pull(ui, repo, source="default", **opts):
2181 def pull(ui, repo, source="default", **opts):
2166 """pull changes from the specified source
2182 """pull changes from the specified source
2167
2183
2168 Pull changes from a remote repository to a local one.
2184 Pull changes from a remote repository to a local one.
2169
2185
2170 This finds all changes from the repository at the specified path
2186 This finds all changes from the repository at the specified path
2171 or URL and adds them to the local repository. By default, this
2187 or URL and adds them to the local repository. By default, this
2172 does not update the copy of the project in the working directory.
2188 does not update the copy of the project in the working directory.
2173
2189
2174 Valid URLs are of the form:
2190 Valid URLs are of the form:
2175
2191
2176 local/filesystem/path
2192 local/filesystem/path
2177 http://[user@]host[:port][/path]
2193 http://[user@]host[:port][/path]
2178 https://[user@]host[:port][/path]
2194 https://[user@]host[:port][/path]
2179 ssh://[user@]host[:port][/path]
2195 ssh://[user@]host[:port][/path]
2180
2196
2181 Some notes about using SSH with Mercurial:
2197 Some notes about using SSH with Mercurial:
2182 - SSH requires an accessible shell account on the destination machine
2198 - SSH requires an accessible shell account on the destination machine
2183 and a copy of hg in the remote path or specified with as remotecmd.
2199 and a copy of hg in the remote path or specified with as remotecmd.
2184 - /path is relative to the remote user's home directory by default.
2200 - /path is relative to the remote user's home directory by default.
2185 Use two slashes at the start of a path to specify an absolute path.
2201 Use two slashes at the start of a path to specify an absolute path.
2186 - Mercurial doesn't use its own compression via SSH; the right thing
2202 - Mercurial doesn't use its own compression via SSH; the right thing
2187 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2203 to do is to configure it in your ~/.ssh/ssh_config, e.g.:
2188 Host *.mylocalnetwork.example.com
2204 Host *.mylocalnetwork.example.com
2189 Compression off
2205 Compression off
2190 Host *
2206 Host *
2191 Compression on
2207 Compression on
2192 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2208 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2193 with the --ssh command line option.
2209 with the --ssh command line option.
2194 """
2210 """
2195 source = ui.expandpath(source)
2211 source = ui.expandpath(source)
2196
2212
2197 if opts['ssh']:
2213 if opts['ssh']:
2198 ui.setconfig("ui", "ssh", opts['ssh'])
2214 ui.setconfig("ui", "ssh", opts['ssh'])
2199 if opts['remotecmd']:
2215 if opts['remotecmd']:
2200 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2216 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2201
2217
2202 other = hg.repository(ui, source)
2218 other = hg.repository(ui, source)
2203 ui.status(_('pulling from %s\n') % (source))
2219 ui.status(_('pulling from %s\n') % (source))
2204 revs = None
2220 revs = None
2205 if opts['rev'] and not other.local():
2221 if opts['rev'] and not other.local():
2206 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2222 raise util.Abort(_("pull -r doesn't work for remote repositories yet"))
2207 elif opts['rev']:
2223 elif opts['rev']:
2208 revs = [other.lookup(rev) for rev in opts['rev']]
2224 revs = [other.lookup(rev) for rev in opts['rev']]
2209 modheads = repo.pull(other, heads=revs, force=opts['force'])
2225 modheads = repo.pull(other, heads=revs, force=opts['force'])
2210 return postincoming(ui, repo, modheads, opts['update'])
2226 return postincoming(ui, repo, modheads, opts['update'])
2211
2227
2212 def push(ui, repo, dest=None, **opts):
2228 def push(ui, repo, dest=None, **opts):
2213 """push changes to the specified destination
2229 """push changes to the specified destination
2214
2230
2215 Push changes from the local repository to the given destination.
2231 Push changes from the local repository to the given destination.
2216
2232
2217 This is the symmetrical operation for pull. It helps to move
2233 This is the symmetrical operation for pull. It helps to move
2218 changes from the current repository to a different one. If the
2234 changes from the current repository to a different one. If the
2219 destination is local this is identical to a pull in that directory
2235 destination is local this is identical to a pull in that directory
2220 from the current one.
2236 from the current one.
2221
2237
2222 By default, push will refuse to run if it detects the result would
2238 By default, push will refuse to run if it detects the result would
2223 increase the number of remote heads. This generally indicates the
2239 increase the number of remote heads. This generally indicates the
2224 the client has forgotten to sync and merge before pushing.
2240 the client has forgotten to sync and merge before pushing.
2225
2241
2226 Valid URLs are of the form:
2242 Valid URLs are of the form:
2227
2243
2228 local/filesystem/path
2244 local/filesystem/path
2229 ssh://[user@]host[:port][/path]
2245 ssh://[user@]host[:port][/path]
2230
2246
2231 Look at the help text for the pull command for important details
2247 Look at the help text for the pull command for important details
2232 about ssh:// URLs.
2248 about ssh:// URLs.
2233 """
2249 """
2234 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2250 dest = ui.expandpath(dest or 'default-push', dest or 'default')
2235
2251
2236 if opts['ssh']:
2252 if opts['ssh']:
2237 ui.setconfig("ui", "ssh", opts['ssh'])
2253 ui.setconfig("ui", "ssh", opts['ssh'])
2238 if opts['remotecmd']:
2254 if opts['remotecmd']:
2239 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2255 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
2240
2256
2241 other = hg.repository(ui, dest)
2257 other = hg.repository(ui, dest)
2242 ui.status('pushing to %s\n' % (dest))
2258 ui.status('pushing to %s\n' % (dest))
2243 revs = None
2259 revs = None
2244 if opts['rev']:
2260 if opts['rev']:
2245 revs = [repo.lookup(rev) for rev in opts['rev']]
2261 revs = [repo.lookup(rev) for rev in opts['rev']]
2246 r = repo.push(other, opts['force'], revs=revs)
2262 r = repo.push(other, opts['force'], revs=revs)
2247 return r == 0
2263 return r == 0
2248
2264
2249 def rawcommit(ui, repo, *flist, **rc):
2265 def rawcommit(ui, repo, *flist, **rc):
2250 """raw commit interface (DEPRECATED)
2266 """raw commit interface (DEPRECATED)
2251
2267
2252 (DEPRECATED)
2268 (DEPRECATED)
2253 Lowlevel commit, for use in helper scripts.
2269 Lowlevel commit, for use in helper scripts.
2254
2270
2255 This command is not intended to be used by normal users, as it is
2271 This command is not intended to be used by normal users, as it is
2256 primarily useful for importing from other SCMs.
2272 primarily useful for importing from other SCMs.
2257
2273
2258 This command is now deprecated and will be removed in a future
2274 This command is now deprecated and will be removed in a future
2259 release, please use debugsetparents and commit instead.
2275 release, please use debugsetparents and commit instead.
2260 """
2276 """
2261
2277
2262 ui.warn(_("(the rawcommit command is deprecated)\n"))
2278 ui.warn(_("(the rawcommit command is deprecated)\n"))
2263
2279
2264 message = rc['message']
2280 message = rc['message']
2265 if not message and rc['logfile']:
2281 if not message and rc['logfile']:
2266 try:
2282 try:
2267 message = open(rc['logfile']).read()
2283 message = open(rc['logfile']).read()
2268 except IOError:
2284 except IOError:
2269 pass
2285 pass
2270 if not message and not rc['logfile']:
2286 if not message and not rc['logfile']:
2271 raise util.Abort(_("missing commit message"))
2287 raise util.Abort(_("missing commit message"))
2272
2288
2273 files = relpath(repo, list(flist))
2289 files = relpath(repo, list(flist))
2274 if rc['files']:
2290 if rc['files']:
2275 files += open(rc['files']).read().splitlines()
2291 files += open(rc['files']).read().splitlines()
2276
2292
2277 rc['parent'] = map(repo.lookup, rc['parent'])
2293 rc['parent'] = map(repo.lookup, rc['parent'])
2278
2294
2279 try:
2295 try:
2280 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2296 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
2281 except ValueError, inst:
2297 except ValueError, inst:
2282 raise util.Abort(str(inst))
2298 raise util.Abort(str(inst))
2283
2299
2284 def recover(ui, repo):
2300 def recover(ui, repo):
2285 """roll back an interrupted transaction
2301 """roll back an interrupted transaction
2286
2302
2287 Recover from an interrupted commit or pull.
2303 Recover from an interrupted commit or pull.
2288
2304
2289 This command tries to fix the repository status after an interrupted
2305 This command tries to fix the repository status after an interrupted
2290 operation. It should only be necessary when Mercurial suggests it.
2306 operation. It should only be necessary when Mercurial suggests it.
2291 """
2307 """
2292 if repo.recover():
2308 if repo.recover():
2293 return repo.verify()
2309 return repo.verify()
2294 return 1
2310 return 1
2295
2311
2296 def remove(ui, repo, *pats, **opts):
2312 def remove(ui, repo, *pats, **opts):
2297 """remove the specified files on the next commit
2313 """remove the specified files on the next commit
2298
2314
2299 Schedule the indicated files for removal from the repository.
2315 Schedule the indicated files for removal from the repository.
2300
2316
2301 This command schedules the files to be removed at the next commit.
2317 This command schedules the files to be removed at the next commit.
2302 This only removes files from the current branch, not from the
2318 This only removes files from the current branch, not from the
2303 entire project history. If the files still exist in the working
2319 entire project history. If the files still exist in the working
2304 directory, they will be deleted from it. If invoked with --after,
2320 directory, they will be deleted from it. If invoked with --after,
2305 files that have been manually deleted are marked as removed.
2321 files that have been manually deleted are marked as removed.
2306
2322
2307 Modified files and added files are not removed by default. To
2323 Modified files and added files are not removed by default. To
2308 remove them, use the -f/--force option.
2324 remove them, use the -f/--force option.
2309 """
2325 """
2310 names = []
2326 names = []
2311 if not opts['after'] and not pats:
2327 if not opts['after'] and not pats:
2312 raise util.Abort(_('no files specified'))
2328 raise util.Abort(_('no files specified'))
2313 files, matchfn, anypats = matchpats(repo, pats, opts)
2329 files, matchfn, anypats = matchpats(repo, pats, opts)
2314 exact = dict.fromkeys(files)
2330 exact = dict.fromkeys(files)
2315 mardu = map(dict.fromkeys, repo.changes(files=files, match=matchfn))
2331 mardu = map(dict.fromkeys, repo.changes(files=files, match=matchfn))
2316 modified, added, removed, deleted, unknown = mardu
2332 modified, added, removed, deleted, unknown = mardu
2317 remove, forget = [], []
2333 remove, forget = [], []
2318 for src, abs, rel, exact in walk(repo, pats, opts):
2334 for src, abs, rel, exact in walk(repo, pats, opts):
2319 reason = None
2335 reason = None
2320 if abs not in deleted and opts['after']:
2336 if abs not in deleted and opts['after']:
2321 reason = _('is still present')
2337 reason = _('is still present')
2322 elif abs in modified and not opts['force']:
2338 elif abs in modified and not opts['force']:
2323 reason = _('is modified (use -f to force removal)')
2339 reason = _('is modified (use -f to force removal)')
2324 elif abs in added:
2340 elif abs in added:
2325 if opts['force']:
2341 if opts['force']:
2326 forget.append(abs)
2342 forget.append(abs)
2327 continue
2343 continue
2328 reason = _('has been marked for add (use -f to force removal)')
2344 reason = _('has been marked for add (use -f to force removal)')
2329 elif abs in unknown:
2345 elif abs in unknown:
2330 reason = _('is not managed')
2346 reason = _('is not managed')
2331 elif abs in removed:
2347 elif abs in removed:
2332 continue
2348 continue
2333 if reason:
2349 if reason:
2334 if exact:
2350 if exact:
2335 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2351 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2336 else:
2352 else:
2337 if ui.verbose or not exact:
2353 if ui.verbose or not exact:
2338 ui.status(_('removing %s\n') % rel)
2354 ui.status(_('removing %s\n') % rel)
2339 remove.append(abs)
2355 remove.append(abs)
2340 repo.forget(forget)
2356 repo.forget(forget)
2341 repo.remove(remove, unlink=not opts['after'])
2357 repo.remove(remove, unlink=not opts['after'])
2342
2358
2343 def rename(ui, repo, *pats, **opts):
2359 def rename(ui, repo, *pats, **opts):
2344 """rename files; equivalent of copy + remove
2360 """rename files; equivalent of copy + remove
2345
2361
2346 Mark dest as copies of sources; mark sources for deletion. If
2362 Mark dest as copies of sources; mark sources for deletion. If
2347 dest is a directory, copies are put in that directory. If dest is
2363 dest is a directory, copies are put in that directory. If dest is
2348 a file, there can only be one source.
2364 a file, there can only be one source.
2349
2365
2350 By default, this command copies the contents of files as they
2366 By default, this command copies the contents of files as they
2351 stand in the working directory. If invoked with --after, the
2367 stand in the working directory. If invoked with --after, the
2352 operation is recorded, but no copying is performed.
2368 operation is recorded, but no copying is performed.
2353
2369
2354 This command takes effect in the next commit.
2370 This command takes effect in the next commit.
2355
2371
2356 NOTE: This command should be treated as experimental. While it
2372 NOTE: This command should be treated as experimental. While it
2357 should properly record rename files, this information is not yet
2373 should properly record rename files, this information is not yet
2358 fully used by merge, nor fully reported by log.
2374 fully used by merge, nor fully reported by log.
2359 """
2375 """
2360 wlock = repo.wlock(0)
2376 wlock = repo.wlock(0)
2361 errs, copied = docopy(ui, repo, pats, opts, wlock)
2377 errs, copied = docopy(ui, repo, pats, opts, wlock)
2362 names = []
2378 names = []
2363 for abs, rel, exact in copied:
2379 for abs, rel, exact in copied:
2364 if ui.verbose or not exact:
2380 if ui.verbose or not exact:
2365 ui.status(_('removing %s\n') % rel)
2381 ui.status(_('removing %s\n') % rel)
2366 names.append(abs)
2382 names.append(abs)
2367 if not opts.get('dry_run'):
2383 if not opts.get('dry_run'):
2368 repo.remove(names, True, wlock)
2384 repo.remove(names, True, wlock)
2369 return errs
2385 return errs
2370
2386
2371 def revert(ui, repo, *pats, **opts):
2387 def revert(ui, repo, *pats, **opts):
2372 """revert files or dirs to their states as of some revision
2388 """revert files or dirs to their states as of some revision
2373
2389
2374 With no revision specified, revert the named files or directories
2390 With no revision specified, revert the named files or directories
2375 to the contents they had in the parent of the working directory.
2391 to the contents they had in the parent of the working directory.
2376 This restores the contents of the affected files to an unmodified
2392 This restores the contents of the affected files to an unmodified
2377 state. If the working directory has two parents, you must
2393 state. If the working directory has two parents, you must
2378 explicitly specify the revision to revert to.
2394 explicitly specify the revision to revert to.
2379
2395
2380 Modified files are saved with a .orig suffix before reverting.
2396 Modified files are saved with a .orig suffix before reverting.
2381 To disable these backups, use --no-backup.
2397 To disable these backups, use --no-backup.
2382
2398
2383 Using the -r option, revert the given files or directories to
2399 Using the -r option, revert the given files or directories to
2384 their contents as of a specific revision. This can be helpful to"roll
2400 their contents as of a specific revision. This can be helpful to"roll
2385 back" some or all of a change that should not have been committed.
2401 back" some or all of a change that should not have been committed.
2386
2402
2387 Revert modifies the working directory. It does not commit any
2403 Revert modifies the working directory. It does not commit any
2388 changes, or change the parent of the working directory. If you
2404 changes, or change the parent of the working directory. If you
2389 revert to a revision other than the parent of the working
2405 revert to a revision other than the parent of the working
2390 directory, the reverted files will thus appear modified
2406 directory, the reverted files will thus appear modified
2391 afterwards.
2407 afterwards.
2392
2408
2393 If a file has been deleted, it is recreated. If the executable
2409 If a file has been deleted, it is recreated. If the executable
2394 mode of a file was changed, it is reset.
2410 mode of a file was changed, it is reset.
2395
2411
2396 If names are given, all files matching the names are reverted.
2412 If names are given, all files matching the names are reverted.
2397
2413
2398 If no arguments are given, all files in the repository are reverted.
2414 If no arguments are given, all files in the repository are reverted.
2399 """
2415 """
2400 parent, p2 = repo.dirstate.parents()
2416 parent, p2 = repo.dirstate.parents()
2401 if opts['rev']:
2417 if opts['rev']:
2402 node = repo.lookup(opts['rev'])
2418 node = repo.lookup(opts['rev'])
2403 elif p2 != nullid:
2419 elif p2 != nullid:
2404 raise util.Abort(_('working dir has two parents; '
2420 raise util.Abort(_('working dir has two parents; '
2405 'you must specify the revision to revert to'))
2421 'you must specify the revision to revert to'))
2406 else:
2422 else:
2407 node = parent
2423 node = parent
2408 mf = repo.manifest.read(repo.changelog.read(node)[0])
2424 mf = repo.manifest.read(repo.changelog.read(node)[0])
2409 if node == parent:
2425 if node == parent:
2410 pmf = mf
2426 pmf = mf
2411 else:
2427 else:
2412 pmf = None
2428 pmf = None
2413
2429
2414 wlock = repo.wlock()
2430 wlock = repo.wlock()
2415
2431
2416 # need all matching names in dirstate and manifest of target rev,
2432 # need all matching names in dirstate and manifest of target rev,
2417 # so have to walk both. do not print errors if files exist in one
2433 # so have to walk both. do not print errors if files exist in one
2418 # but not other.
2434 # but not other.
2419
2435
2420 names = {}
2436 names = {}
2421 target_only = {}
2437 target_only = {}
2422
2438
2423 # walk dirstate.
2439 # walk dirstate.
2424
2440
2425 for src, abs, rel, exact in walk(repo, pats, opts, badmatch=mf.has_key):
2441 for src, abs, rel, exact in walk(repo, pats, opts, badmatch=mf.has_key):
2426 names[abs] = (rel, exact)
2442 names[abs] = (rel, exact)
2427 if src == 'b':
2443 if src == 'b':
2428 target_only[abs] = True
2444 target_only[abs] = True
2429
2445
2430 # walk target manifest.
2446 # walk target manifest.
2431
2447
2432 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
2448 for src, abs, rel, exact in walk(repo, pats, opts, node=node,
2433 badmatch=names.has_key):
2449 badmatch=names.has_key):
2434 if abs in names: continue
2450 if abs in names: continue
2435 names[abs] = (rel, exact)
2451 names[abs] = (rel, exact)
2436 target_only[abs] = True
2452 target_only[abs] = True
2437
2453
2438 changes = repo.changes(match=names.has_key, wlock=wlock)
2454 changes = repo.changes(match=names.has_key, wlock=wlock)
2439 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2455 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2440
2456
2441 revert = ([], _('reverting %s\n'))
2457 revert = ([], _('reverting %s\n'))
2442 add = ([], _('adding %s\n'))
2458 add = ([], _('adding %s\n'))
2443 remove = ([], _('removing %s\n'))
2459 remove = ([], _('removing %s\n'))
2444 forget = ([], _('forgetting %s\n'))
2460 forget = ([], _('forgetting %s\n'))
2445 undelete = ([], _('undeleting %s\n'))
2461 undelete = ([], _('undeleting %s\n'))
2446 update = {}
2462 update = {}
2447
2463
2448 disptable = (
2464 disptable = (
2449 # dispatch table:
2465 # dispatch table:
2450 # file state
2466 # file state
2451 # action if in target manifest
2467 # action if in target manifest
2452 # action if not in target manifest
2468 # action if not in target manifest
2453 # make backup if in target manifest
2469 # make backup if in target manifest
2454 # make backup if not in target manifest
2470 # make backup if not in target manifest
2455 (modified, revert, remove, True, True),
2471 (modified, revert, remove, True, True),
2456 (added, revert, forget, True, False),
2472 (added, revert, forget, True, False),
2457 (removed, undelete, None, False, False),
2473 (removed, undelete, None, False, False),
2458 (deleted, revert, remove, False, False),
2474 (deleted, revert, remove, False, False),
2459 (unknown, add, None, True, False),
2475 (unknown, add, None, True, False),
2460 (target_only, add, None, False, False),
2476 (target_only, add, None, False, False),
2461 )
2477 )
2462
2478
2463 entries = names.items()
2479 entries = names.items()
2464 entries.sort()
2480 entries.sort()
2465
2481
2466 for abs, (rel, exact) in entries:
2482 for abs, (rel, exact) in entries:
2467 mfentry = mf.get(abs)
2483 mfentry = mf.get(abs)
2468 def handle(xlist, dobackup):
2484 def handle(xlist, dobackup):
2469 xlist[0].append(abs)
2485 xlist[0].append(abs)
2470 update[abs] = 1
2486 update[abs] = 1
2471 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2487 if dobackup and not opts['no_backup'] and os.path.exists(rel):
2472 bakname = "%s.orig" % rel
2488 bakname = "%s.orig" % rel
2473 ui.note(_('saving current version of %s as %s\n') %
2489 ui.note(_('saving current version of %s as %s\n') %
2474 (rel, bakname))
2490 (rel, bakname))
2475 if not opts.get('dry_run'):
2491 if not opts.get('dry_run'):
2476 shutil.copyfile(rel, bakname)
2492 shutil.copyfile(rel, bakname)
2477 shutil.copymode(rel, bakname)
2493 shutil.copymode(rel, bakname)
2478 if ui.verbose or not exact:
2494 if ui.verbose or not exact:
2479 ui.status(xlist[1] % rel)
2495 ui.status(xlist[1] % rel)
2480 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2496 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2481 if abs not in table: continue
2497 if abs not in table: continue
2482 # file has changed in dirstate
2498 # file has changed in dirstate
2483 if mfentry:
2499 if mfentry:
2484 handle(hitlist, backuphit)
2500 handle(hitlist, backuphit)
2485 elif misslist is not None:
2501 elif misslist is not None:
2486 handle(misslist, backupmiss)
2502 handle(misslist, backupmiss)
2487 else:
2503 else:
2488 if exact: ui.warn(_('file not managed: %s\n' % rel))
2504 if exact: ui.warn(_('file not managed: %s\n' % rel))
2489 break
2505 break
2490 else:
2506 else:
2491 # file has not changed in dirstate
2507 # file has not changed in dirstate
2492 if node == parent:
2508 if node == parent:
2493 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2509 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2494 continue
2510 continue
2495 if pmf is None:
2511 if pmf is None:
2496 # only need parent manifest in this unlikely case,
2512 # only need parent manifest in this unlikely case,
2497 # so do not read by default
2513 # so do not read by default
2498 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2514 pmf = repo.manifest.read(repo.changelog.read(parent)[0])
2499 if abs in pmf:
2515 if abs in pmf:
2500 if mfentry:
2516 if mfentry:
2501 # if version of file is same in parent and target
2517 # if version of file is same in parent and target
2502 # manifests, do nothing
2518 # manifests, do nothing
2503 if pmf[abs] != mfentry:
2519 if pmf[abs] != mfentry:
2504 handle(revert, False)
2520 handle(revert, False)
2505 else:
2521 else:
2506 handle(remove, False)
2522 handle(remove, False)
2507
2523
2508 if not opts.get('dry_run'):
2524 if not opts.get('dry_run'):
2509 repo.dirstate.forget(forget[0])
2525 repo.dirstate.forget(forget[0])
2510 r = repo.update(node, False, True, update.has_key, False, wlock=wlock,
2526 r = repo.update(node, False, True, update.has_key, False, wlock=wlock,
2511 show_stats=False)
2527 show_stats=False)
2512 repo.dirstate.update(add[0], 'a')
2528 repo.dirstate.update(add[0], 'a')
2513 repo.dirstate.update(undelete[0], 'n')
2529 repo.dirstate.update(undelete[0], 'n')
2514 repo.dirstate.update(remove[0], 'r')
2530 repo.dirstate.update(remove[0], 'r')
2515 return r
2531 return r
2516
2532
2517 def rollback(ui, repo):
2533 def rollback(ui, repo):
2518 """roll back the last transaction in this repository
2534 """roll back the last transaction in this repository
2519
2535
2520 Roll back the last transaction in this repository, restoring the
2536 Roll back the last transaction in this repository, restoring the
2521 project to its state prior to the transaction.
2537 project to its state prior to the transaction.
2522
2538
2523 Transactions are used to encapsulate the effects of all commands
2539 Transactions are used to encapsulate the effects of all commands
2524 that create new changesets or propagate existing changesets into a
2540 that create new changesets or propagate existing changesets into a
2525 repository. For example, the following commands are transactional,
2541 repository. For example, the following commands are transactional,
2526 and their effects can be rolled back:
2542 and their effects can be rolled back:
2527
2543
2528 commit
2544 commit
2529 import
2545 import
2530 pull
2546 pull
2531 push (with this repository as destination)
2547 push (with this repository as destination)
2532 unbundle
2548 unbundle
2533
2549
2534 This command should be used with care. There is only one level of
2550 This command should be used with care. There is only one level of
2535 rollback, and there is no way to undo a rollback.
2551 rollback, and there is no way to undo a rollback.
2536
2552
2537 This command is not intended for use on public repositories. Once
2553 This command is not intended for use on public repositories. Once
2538 changes are visible for pull by other users, rolling a transaction
2554 changes are visible for pull by other users, rolling a transaction
2539 back locally is ineffective (someone else may already have pulled
2555 back locally is ineffective (someone else may already have pulled
2540 the changes). Furthermore, a race is possible with readers of the
2556 the changes). Furthermore, a race is possible with readers of the
2541 repository; for example an in-progress pull from the repository
2557 repository; for example an in-progress pull from the repository
2542 may fail if a rollback is performed.
2558 may fail if a rollback is performed.
2543 """
2559 """
2544 repo.rollback()
2560 repo.rollback()
2545
2561
2546 def root(ui, repo):
2562 def root(ui, repo):
2547 """print the root (top) of the current working dir
2563 """print the root (top) of the current working dir
2548
2564
2549 Print the root directory of the current repository.
2565 Print the root directory of the current repository.
2550 """
2566 """
2551 ui.write(repo.root + "\n")
2567 ui.write(repo.root + "\n")
2552
2568
2553 def serve(ui, repo, **opts):
2569 def serve(ui, repo, **opts):
2554 """export the repository via HTTP
2570 """export the repository via HTTP
2555
2571
2556 Start a local HTTP repository browser and pull server.
2572 Start a local HTTP repository browser and pull server.
2557
2573
2558 By default, the server logs accesses to stdout and errors to
2574 By default, the server logs accesses to stdout and errors to
2559 stderr. Use the "-A" and "-E" options to log to files.
2575 stderr. Use the "-A" and "-E" options to log to files.
2560 """
2576 """
2561
2577
2562 if opts["stdio"]:
2578 if opts["stdio"]:
2563 if repo is None:
2579 if repo is None:
2564 raise hg.RepoError(_('no repo found'))
2580 raise hg.RepoError(_('no repo found'))
2565 s = sshserver.sshserver(ui, repo)
2581 s = sshserver.sshserver(ui, repo)
2566 s.serve_forever()
2582 s.serve_forever()
2567
2583
2568 optlist = ("name templates style address port ipv6"
2584 optlist = ("name templates style address port ipv6"
2569 " accesslog errorlog webdir_conf")
2585 " accesslog errorlog webdir_conf")
2570 for o in optlist.split():
2586 for o in optlist.split():
2571 if opts[o]:
2587 if opts[o]:
2572 ui.setconfig("web", o, opts[o])
2588 ui.setconfig("web", o, opts[o])
2573
2589
2574 if repo is None and not ui.config("web", "webdir_conf"):
2590 if repo is None and not ui.config("web", "webdir_conf"):
2575 raise hg.RepoError(_('no repo found'))
2591 raise hg.RepoError(_('no repo found'))
2576
2592
2577 if opts['daemon'] and not opts['daemon_pipefds']:
2593 if opts['daemon'] and not opts['daemon_pipefds']:
2578 rfd, wfd = os.pipe()
2594 rfd, wfd = os.pipe()
2579 args = sys.argv[:]
2595 args = sys.argv[:]
2580 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2596 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
2581 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2597 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
2582 args[0], args)
2598 args[0], args)
2583 os.close(wfd)
2599 os.close(wfd)
2584 os.read(rfd, 1)
2600 os.read(rfd, 1)
2585 os._exit(0)
2601 os._exit(0)
2586
2602
2587 try:
2603 try:
2588 httpd = hgweb.server.create_server(ui, repo)
2604 httpd = hgweb.server.create_server(ui, repo)
2589 except socket.error, inst:
2605 except socket.error, inst:
2590 raise util.Abort(_('cannot start server: ') + inst.args[1])
2606 raise util.Abort(_('cannot start server: ') + inst.args[1])
2591
2607
2592 if ui.verbose:
2608 if ui.verbose:
2593 addr, port = httpd.socket.getsockname()
2609 addr, port = httpd.socket.getsockname()
2594 if addr == '0.0.0.0':
2610 if addr == '0.0.0.0':
2595 addr = socket.gethostname()
2611 addr = socket.gethostname()
2596 else:
2612 else:
2597 try:
2613 try:
2598 addr = socket.gethostbyaddr(addr)[0]
2614 addr = socket.gethostbyaddr(addr)[0]
2599 except socket.error:
2615 except socket.error:
2600 pass
2616 pass
2601 if port != 80:
2617 if port != 80:
2602 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2618 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
2603 else:
2619 else:
2604 ui.status(_('listening at http://%s/\n') % addr)
2620 ui.status(_('listening at http://%s/\n') % addr)
2605
2621
2606 if opts['pid_file']:
2622 if opts['pid_file']:
2607 fp = open(opts['pid_file'], 'w')
2623 fp = open(opts['pid_file'], 'w')
2608 fp.write(str(os.getpid()))
2624 fp.write(str(os.getpid()))
2609 fp.close()
2625 fp.close()
2610
2626
2611 if opts['daemon_pipefds']:
2627 if opts['daemon_pipefds']:
2612 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2628 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
2613 os.close(rfd)
2629 os.close(rfd)
2614 os.write(wfd, 'y')
2630 os.write(wfd, 'y')
2615 os.close(wfd)
2631 os.close(wfd)
2616 sys.stdout.flush()
2632 sys.stdout.flush()
2617 sys.stderr.flush()
2633 sys.stderr.flush()
2618 fd = os.open(util.nulldev, os.O_RDWR)
2634 fd = os.open(util.nulldev, os.O_RDWR)
2619 if fd != 0: os.dup2(fd, 0)
2635 if fd != 0: os.dup2(fd, 0)
2620 if fd != 1: os.dup2(fd, 1)
2636 if fd != 1: os.dup2(fd, 1)
2621 if fd != 2: os.dup2(fd, 2)
2637 if fd != 2: os.dup2(fd, 2)
2622 if fd not in (0, 1, 2): os.close(fd)
2638 if fd not in (0, 1, 2): os.close(fd)
2623
2639
2624 httpd.serve_forever()
2640 httpd.serve_forever()
2625
2641
2626 def status(ui, repo, *pats, **opts):
2642 def status(ui, repo, *pats, **opts):
2627 """show changed files in the working directory
2643 """show changed files in the working directory
2628
2644
2629 Show changed files in the repository. If names are
2645 Show changed files in the repository. If names are
2630 given, only files that match are shown.
2646 given, only files that match are shown.
2631
2647
2632 The codes used to show the status of files are:
2648 The codes used to show the status of files are:
2633 M = modified
2649 M = modified
2634 A = added
2650 A = added
2635 R = removed
2651 R = removed
2636 ! = deleted, but still tracked
2652 ! = deleted, but still tracked
2637 ? = not tracked
2653 ? = not tracked
2638 I = ignored (not shown by default)
2654 I = ignored (not shown by default)
2639 """
2655 """
2640
2656
2641 show_ignored = opts['ignored'] and True or False
2657 show_ignored = opts['ignored'] and True or False
2642 files, matchfn, anypats = matchpats(repo, pats, opts)
2658 files, matchfn, anypats = matchpats(repo, pats, opts)
2643 cwd = (pats and repo.getcwd()) or ''
2659 cwd = (pats and repo.getcwd()) or ''
2644 modified, added, removed, deleted, unknown, ignored = [
2660 modified, added, removed, deleted, unknown, ignored = [
2645 [util.pathto(cwd, x) for x in n]
2661 [util.pathto(cwd, x) for x in n]
2646 for n in repo.changes(files=files, match=matchfn,
2662 for n in repo.changes(files=files, match=matchfn,
2647 show_ignored=show_ignored)]
2663 show_ignored=show_ignored)]
2648
2664
2649 changetypes = [('modified', 'M', modified),
2665 changetypes = [('modified', 'M', modified),
2650 ('added', 'A', added),
2666 ('added', 'A', added),
2651 ('removed', 'R', removed),
2667 ('removed', 'R', removed),
2652 ('deleted', '!', deleted),
2668 ('deleted', '!', deleted),
2653 ('unknown', '?', unknown),
2669 ('unknown', '?', unknown),
2654 ('ignored', 'I', ignored)]
2670 ('ignored', 'I', ignored)]
2655
2671
2656 end = opts['print0'] and '\0' or '\n'
2672 end = opts['print0'] and '\0' or '\n'
2657
2673
2658 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2674 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2659 or changetypes):
2675 or changetypes):
2660 if opts['no_status']:
2676 if opts['no_status']:
2661 format = "%%s%s" % end
2677 format = "%%s%s" % end
2662 else:
2678 else:
2663 format = "%s %%s%s" % (char, end)
2679 format = "%s %%s%s" % (char, end)
2664
2680
2665 for f in changes:
2681 for f in changes:
2666 ui.write(format % f)
2682 ui.write(format % f)
2667
2683
2668 def tag(ui, repo, name, rev_=None, **opts):
2684 def tag(ui, repo, name, rev_=None, **opts):
2669 """add a tag for the current tip or a given revision
2685 """add a tag for the current tip or a given revision
2670
2686
2671 Name a particular revision using <name>.
2687 Name a particular revision using <name>.
2672
2688
2673 Tags are used to name particular revisions of the repository and are
2689 Tags are used to name particular revisions of the repository and are
2674 very useful to compare different revision, to go back to significant
2690 very useful to compare different revision, to go back to significant
2675 earlier versions or to mark branch points as releases, etc.
2691 earlier versions or to mark branch points as releases, etc.
2676
2692
2677 If no revision is given, the tip is used.
2693 If no revision is given, the tip is used.
2678
2694
2679 To facilitate version control, distribution, and merging of tags,
2695 To facilitate version control, distribution, and merging of tags,
2680 they are stored as a file named ".hgtags" which is managed
2696 they are stored as a file named ".hgtags" which is managed
2681 similarly to other project files and can be hand-edited if
2697 similarly to other project files and can be hand-edited if
2682 necessary. The file '.hg/localtags' is used for local tags (not
2698 necessary. The file '.hg/localtags' is used for local tags (not
2683 shared among repositories).
2699 shared among repositories).
2684 """
2700 """
2685 if name == "tip":
2701 if name == "tip":
2686 raise util.Abort(_("the name 'tip' is reserved"))
2702 raise util.Abort(_("the name 'tip' is reserved"))
2687 if rev_ is not None:
2703 if rev_ is not None:
2688 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2704 ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
2689 "please use 'hg tag [-r REV] NAME' instead\n"))
2705 "please use 'hg tag [-r REV] NAME' instead\n"))
2690 if opts['rev']:
2706 if opts['rev']:
2691 raise util.Abort(_("use only one form to specify the revision"))
2707 raise util.Abort(_("use only one form to specify the revision"))
2692 if opts['rev']:
2708 if opts['rev']:
2693 rev_ = opts['rev']
2709 rev_ = opts['rev']
2694 if rev_:
2710 if rev_:
2695 r = hex(repo.lookup(rev_))
2711 r = hex(repo.lookup(rev_))
2696 else:
2712 else:
2697 r = hex(repo.changelog.tip())
2713 r = hex(repo.changelog.tip())
2698
2714
2699 disallowed = (revrangesep, '\r', '\n')
2715 disallowed = (revrangesep, '\r', '\n')
2700 for c in disallowed:
2716 for c in disallowed:
2701 if name.find(c) >= 0:
2717 if name.find(c) >= 0:
2702 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2718 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2703
2719
2704 repo.hook('pretag', throw=True, node=r, tag=name,
2720 repo.hook('pretag', throw=True, node=r, tag=name,
2705 local=int(not not opts['local']))
2721 local=int(not not opts['local']))
2706
2722
2707 if opts['local']:
2723 if opts['local']:
2708 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2724 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2709 repo.hook('tag', node=r, tag=name, local=1)
2725 repo.hook('tag', node=r, tag=name, local=1)
2710 return
2726 return
2711
2727
2712 for x in repo.changes():
2728 for x in repo.changes():
2713 if ".hgtags" in x:
2729 if ".hgtags" in x:
2714 raise util.Abort(_("working copy of .hgtags is changed "
2730 raise util.Abort(_("working copy of .hgtags is changed "
2715 "(please commit .hgtags manually)"))
2731 "(please commit .hgtags manually)"))
2716
2732
2717 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2733 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2718 if repo.dirstate.state(".hgtags") == '?':
2734 if repo.dirstate.state(".hgtags") == '?':
2719 repo.add([".hgtags"])
2735 repo.add([".hgtags"])
2720
2736
2721 message = (opts['message'] or
2737 message = (opts['message'] or
2722 _("Added tag %s for changeset %s") % (name, r))
2738 _("Added tag %s for changeset %s") % (name, r))
2723 try:
2739 try:
2724 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2740 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2725 repo.hook('tag', node=r, tag=name, local=0)
2741 repo.hook('tag', node=r, tag=name, local=0)
2726 except ValueError, inst:
2742 except ValueError, inst:
2727 raise util.Abort(str(inst))
2743 raise util.Abort(str(inst))
2728
2744
2729 def tags(ui, repo):
2745 def tags(ui, repo):
2730 """list repository tags
2746 """list repository tags
2731
2747
2732 List the repository tags.
2748 List the repository tags.
2733
2749
2734 This lists both regular and local tags.
2750 This lists both regular and local tags.
2735 """
2751 """
2736
2752
2737 l = repo.tagslist()
2753 l = repo.tagslist()
2738 l.reverse()
2754 l.reverse()
2739 for t, n in l:
2755 for t, n in l:
2740 try:
2756 try:
2741 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2757 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2742 except KeyError:
2758 except KeyError:
2743 r = " ?:?"
2759 r = " ?:?"
2744 if ui.quiet:
2760 if ui.quiet:
2745 ui.write("%s\n" % t)
2761 ui.write("%s\n" % t)
2746 else:
2762 else:
2747 ui.write("%-30s %s\n" % (t, r))
2763 ui.write("%-30s %s\n" % (t, r))
2748
2764
2749 def tip(ui, repo, **opts):
2765 def tip(ui, repo, **opts):
2750 """show the tip revision
2766 """show the tip revision
2751
2767
2752 Show the tip revision.
2768 Show the tip revision.
2753 """
2769 """
2754 n = repo.changelog.tip()
2770 n = repo.changelog.tip()
2755 br = None
2771 br = None
2756 if opts['branches']:
2772 if opts['branches']:
2757 br = repo.branchlookup([n])
2773 br = repo.branchlookup([n])
2758 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2774 show_changeset(ui, repo, opts).show(changenode=n, brinfo=br)
2759 if opts['patch']:
2775 if opts['patch']:
2760 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2776 dodiff(ui, ui, repo, repo.changelog.parents(n)[0], n)
2761
2777
2762 def unbundle(ui, repo, fname, **opts):
2778 def unbundle(ui, repo, fname, **opts):
2763 """apply a changegroup file
2779 """apply a changegroup file
2764
2780
2765 Apply a compressed changegroup file generated by the bundle
2781 Apply a compressed changegroup file generated by the bundle
2766 command.
2782 command.
2767 """
2783 """
2768 f = urllib.urlopen(fname)
2784 f = urllib.urlopen(fname)
2769
2785
2770 header = f.read(6)
2786 header = f.read(6)
2771 if not header.startswith("HG"):
2787 if not header.startswith("HG"):
2772 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2788 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2773 elif not header.startswith("HG10"):
2789 elif not header.startswith("HG10"):
2774 raise util.Abort(_("%s: unknown bundle version") % fname)
2790 raise util.Abort(_("%s: unknown bundle version") % fname)
2775 elif header == "HG10BZ":
2791 elif header == "HG10BZ":
2776 def generator(f):
2792 def generator(f):
2777 zd = bz2.BZ2Decompressor()
2793 zd = bz2.BZ2Decompressor()
2778 zd.decompress("BZ")
2794 zd.decompress("BZ")
2779 for chunk in f:
2795 for chunk in f:
2780 yield zd.decompress(chunk)
2796 yield zd.decompress(chunk)
2781 elif header == "HG10UN":
2797 elif header == "HG10UN":
2782 def generator(f):
2798 def generator(f):
2783 for chunk in f:
2799 for chunk in f:
2784 yield chunk
2800 yield chunk
2785 else:
2801 else:
2786 raise util.Abort(_("%s: unknown bundle compression type")
2802 raise util.Abort(_("%s: unknown bundle compression type")
2787 % fname)
2803 % fname)
2788 gen = generator(util.filechunkiter(f, 4096))
2804 gen = generator(util.filechunkiter(f, 4096))
2789 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle')
2805 modheads = repo.addchangegroup(util.chunkbuffer(gen), 'unbundle')
2790 return postincoming(ui, repo, modheads, opts['update'])
2806 return postincoming(ui, repo, modheads, opts['update'])
2791
2807
2792 def undo(ui, repo):
2808 def undo(ui, repo):
2793 """undo the last commit or pull (DEPRECATED)
2809 """undo the last commit or pull (DEPRECATED)
2794
2810
2795 (DEPRECATED)
2811 (DEPRECATED)
2796 This command is now deprecated and will be removed in a future
2812 This command is now deprecated and will be removed in a future
2797 release. Please use the rollback command instead. For usage
2813 release. Please use the rollback command instead. For usage
2798 instructions, see the rollback command.
2814 instructions, see the rollback command.
2799 """
2815 """
2800 ui.warn(_('(the undo command is deprecated; use rollback instead)\n'))
2816 ui.warn(_('(the undo command is deprecated; use rollback instead)\n'))
2801 repo.rollback()
2817 repo.rollback()
2802
2818
2803 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2819 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2804 branch=None, **opts):
2820 branch=None, **opts):
2805 """update or merge working directory
2821 """update or merge working directory
2806
2822
2807 Update the working directory to the specified revision.
2823 Update the working directory to the specified revision.
2808
2824
2809 If there are no outstanding changes in the working directory and
2825 If there are no outstanding changes in the working directory and
2810 there is a linear relationship between the current version and the
2826 there is a linear relationship between the current version and the
2811 requested version, the result is the requested version.
2827 requested version, the result is the requested version.
2812
2828
2813 To merge the working directory with another revision, use the
2829 To merge the working directory with another revision, use the
2814 merge command.
2830 merge command.
2815
2831
2816 By default, update will refuse to run if doing so would require
2832 By default, update will refuse to run if doing so would require
2817 merging or discarding local changes.
2833 merging or discarding local changes.
2818 """
2834 """
2819 if merge:
2835 if merge:
2820 ui.warn(_('(the -m/--merge option is deprecated; '
2836 ui.warn(_('(the -m/--merge option is deprecated; '
2821 'use the merge command instead)\n'))
2837 'use the merge command instead)\n'))
2822 return doupdate(ui, repo, node, merge, clean, force, branch, **opts)
2838 return doupdate(ui, repo, node, merge, clean, force, branch, **opts)
2823
2839
2824 def doupdate(ui, repo, node=None, merge=False, clean=False, force=None,
2840 def doupdate(ui, repo, node=None, merge=False, clean=False, force=None,
2825 branch=None, **opts):
2841 branch=None, **opts):
2826 if branch:
2842 if branch:
2827 br = repo.branchlookup(branch=branch)
2843 br = repo.branchlookup(branch=branch)
2828 found = []
2844 found = []
2829 for x in br:
2845 for x in br:
2830 if branch in br[x]:
2846 if branch in br[x]:
2831 found.append(x)
2847 found.append(x)
2832 if len(found) > 1:
2848 if len(found) > 1:
2833 ui.warn(_("Found multiple heads for %s\n") % branch)
2849 ui.warn(_("Found multiple heads for %s\n") % branch)
2834 for x in found:
2850 for x in found:
2835 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br)
2851 show_changeset(ui, repo, opts).show(changenode=x, brinfo=br)
2836 return 1
2852 return 1
2837 if len(found) == 1:
2853 if len(found) == 1:
2838 node = found[0]
2854 node = found[0]
2839 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2855 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2840 else:
2856 else:
2841 ui.warn(_("branch %s not found\n") % (branch))
2857 ui.warn(_("branch %s not found\n") % (branch))
2842 return 1
2858 return 1
2843 else:
2859 else:
2844 node = node and repo.lookup(node) or repo.changelog.tip()
2860 node = node and repo.lookup(node) or repo.changelog.tip()
2845 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2861 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2846
2862
2847 def verify(ui, repo):
2863 def verify(ui, repo):
2848 """verify the integrity of the repository
2864 """verify the integrity of the repository
2849
2865
2850 Verify the integrity of the current repository.
2866 Verify the integrity of the current repository.
2851
2867
2852 This will perform an extensive check of the repository's
2868 This will perform an extensive check of the repository's
2853 integrity, validating the hashes and checksums of each entry in
2869 integrity, validating the hashes and checksums of each entry in
2854 the changelog, manifest, and tracked files, as well as the
2870 the changelog, manifest, and tracked files, as well as the
2855 integrity of their crosslinks and indices.
2871 integrity of their crosslinks and indices.
2856 """
2872 """
2857 return repo.verify()
2873 return repo.verify()
2858
2874
2859 # Command options and aliases are listed here, alphabetically
2875 # Command options and aliases are listed here, alphabetically
2860
2876
2861 table = {
2877 table = {
2862 "^add":
2878 "^add":
2863 (add,
2879 (add,
2864 [('I', 'include', [], _('include names matching the given patterns')),
2880 [('I', 'include', [], _('include names matching the given patterns')),
2865 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2881 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2866 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2882 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2867 _('hg add [OPTION]... [FILE]...')),
2883 _('hg add [OPTION]... [FILE]...')),
2868 "debugaddremove|addremove":
2884 "debugaddremove|addremove":
2869 (addremove,
2885 (addremove,
2870 [('I', 'include', [], _('include names matching the given patterns')),
2886 [('I', 'include', [], _('include names matching the given patterns')),
2871 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2887 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2872 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2888 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2873 _('hg addremove [OPTION]... [FILE]...')),
2889 _('hg addremove [OPTION]... [FILE]...')),
2874 "^annotate":
2890 "^annotate":
2875 (annotate,
2891 (annotate,
2876 [('r', 'rev', '', _('annotate the specified revision')),
2892 [('r', 'rev', '', _('annotate the specified revision')),
2877 ('a', 'text', None, _('treat all files as text')),
2893 ('a', 'text', None, _('treat all files as text')),
2878 ('u', 'user', None, _('list the author')),
2894 ('u', 'user', None, _('list the author')),
2879 ('d', 'date', None, _('list the date')),
2895 ('d', 'date', None, _('list the date')),
2880 ('n', 'number', None, _('list the revision number (default)')),
2896 ('n', 'number', None, _('list the revision number (default)')),
2881 ('c', 'changeset', None, _('list the changeset')),
2897 ('c', 'changeset', None, _('list the changeset')),
2882 ('I', 'include', [], _('include names matching the given patterns')),
2898 ('I', 'include', [], _('include names matching the given patterns')),
2883 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2899 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2884 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2900 _('hg annotate [-r REV] [-a] [-u] [-d] [-n] [-c] FILE...')),
2885 "archive":
2901 "archive":
2886 (archive,
2902 (archive,
2887 [('', 'no-decode', None, _('do not pass files through decoders')),
2903 [('', 'no-decode', None, _('do not pass files through decoders')),
2888 ('p', 'prefix', '', _('directory prefix for files in archive')),
2904 ('p', 'prefix', '', _('directory prefix for files in archive')),
2889 ('r', 'rev', '', _('revision to distribute')),
2905 ('r', 'rev', '', _('revision to distribute')),
2890 ('t', 'type', '', _('type of distribution to create')),
2906 ('t', 'type', '', _('type of distribution to create')),
2891 ('I', 'include', [], _('include names matching the given patterns')),
2907 ('I', 'include', [], _('include names matching the given patterns')),
2892 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2908 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2893 _('hg archive [OPTION]... DEST')),
2909 _('hg archive [OPTION]... DEST')),
2894 "backout":
2910 "backout":
2895 (backout,
2911 (backout,
2896 [('', 'merge', None,
2912 [('', 'merge', None,
2897 _('merge with old dirstate parent after backout')),
2913 _('merge with old dirstate parent after backout')),
2898 ('m', 'message', '', _('use <text> as commit message')),
2914 ('m', 'message', '', _('use <text> as commit message')),
2899 ('l', 'logfile', '', _('read commit message from <file>')),
2915 ('l', 'logfile', '', _('read commit message from <file>')),
2900 ('d', 'date', '', _('record datecode as commit date')),
2916 ('d', 'date', '', _('record datecode as commit date')),
2901 ('u', 'user', '', _('record user as committer')),
2917 ('u', 'user', '', _('record user as committer')),
2902 ('I', 'include', [], _('include names matching the given patterns')),
2918 ('I', 'include', [], _('include names matching the given patterns')),
2903 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2919 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2904 _('hg backout [OPTION]... REV')),
2920 _('hg backout [OPTION]... REV')),
2905 "bundle":
2921 "bundle":
2906 (bundle,
2922 (bundle,
2907 [('f', 'force', None,
2923 [('f', 'force', None,
2908 _('run even when remote repository is unrelated'))],
2924 _('run even when remote repository is unrelated'))],
2909 _('hg bundle FILE DEST')),
2925 _('hg bundle FILE DEST')),
2910 "cat":
2926 "cat":
2911 (cat,
2927 (cat,
2912 [('o', 'output', '', _('print output to file with formatted name')),
2928 [('o', 'output', '', _('print output to file with formatted name')),
2913 ('r', 'rev', '', _('print the given revision')),
2929 ('r', 'rev', '', _('print the given revision')),
2914 ('I', 'include', [], _('include names matching the given patterns')),
2930 ('I', 'include', [], _('include names matching the given patterns')),
2915 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2931 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2916 _('hg cat [OPTION]... FILE...')),
2932 _('hg cat [OPTION]... FILE...')),
2917 "^clone":
2933 "^clone":
2918 (clone,
2934 (clone,
2919 [('U', 'noupdate', None, _('do not update the new working directory')),
2935 [('U', 'noupdate', None, _('do not update the new working directory')),
2920 ('r', 'rev', [],
2936 ('r', 'rev', [],
2921 _('a changeset you would like to have after cloning')),
2937 _('a changeset you would like to have after cloning')),
2922 ('', 'pull', None, _('use pull protocol to copy metadata')),
2938 ('', 'pull', None, _('use pull protocol to copy metadata')),
2923 ('e', 'ssh', '', _('specify ssh command to use')),
2939 ('e', 'ssh', '', _('specify ssh command to use')),
2924 ('', 'remotecmd', '',
2940 ('', 'remotecmd', '',
2925 _('specify hg command to run on the remote side'))],
2941 _('specify hg command to run on the remote side'))],
2926 _('hg clone [OPTION]... SOURCE [DEST]')),
2942 _('hg clone [OPTION]... SOURCE [DEST]')),
2927 "^commit|ci":
2943 "^commit|ci":
2928 (commit,
2944 (commit,
2929 [('A', 'addremove', None,
2945 [('A', 'addremove', None,
2930 _('mark new/missing files as added/removed before committing')),
2946 _('mark new/missing files as added/removed before committing')),
2931 ('m', 'message', '', _('use <text> as commit message')),
2947 ('m', 'message', '', _('use <text> as commit message')),
2932 ('l', 'logfile', '', _('read the commit message from <file>')),
2948 ('l', 'logfile', '', _('read the commit message from <file>')),
2933 ('d', 'date', '', _('record datecode as commit date')),
2949 ('d', 'date', '', _('record datecode as commit date')),
2934 ('u', 'user', '', _('record user as commiter')),
2950 ('u', 'user', '', _('record user as commiter')),
2935 ('I', 'include', [], _('include names matching the given patterns')),
2951 ('I', 'include', [], _('include names matching the given patterns')),
2936 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2952 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2937 _('hg commit [OPTION]... [FILE]...')),
2953 _('hg commit [OPTION]... [FILE]...')),
2938 "copy|cp":
2954 "copy|cp":
2939 (copy,
2955 (copy,
2940 [('A', 'after', None, _('record a copy that has already occurred')),
2956 [('A', 'after', None, _('record a copy that has already occurred')),
2941 ('f', 'force', None,
2957 ('f', 'force', None,
2942 _('forcibly copy over an existing managed file')),
2958 _('forcibly copy over an existing managed file')),
2943 ('I', 'include', [], _('include names matching the given patterns')),
2959 ('I', 'include', [], _('include names matching the given patterns')),
2944 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2960 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2945 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2961 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
2946 _('hg copy [OPTION]... [SOURCE]... DEST')),
2962 _('hg copy [OPTION]... [SOURCE]... DEST')),
2947 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2963 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2948 "debugcomplete":
2964 "debugcomplete":
2949 (debugcomplete,
2965 (debugcomplete,
2950 [('o', 'options', None, _('show the command options'))],
2966 [('o', 'options', None, _('show the command options'))],
2951 _('debugcomplete [-o] CMD')),
2967 _('debugcomplete [-o] CMD')),
2952 "debugrebuildstate":
2968 "debugrebuildstate":
2953 (debugrebuildstate,
2969 (debugrebuildstate,
2954 [('r', 'rev', '', _('revision to rebuild to'))],
2970 [('r', 'rev', '', _('revision to rebuild to'))],
2955 _('debugrebuildstate [-r REV] [REV]')),
2971 _('debugrebuildstate [-r REV] [REV]')),
2956 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2972 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2957 "debugconfig": (debugconfig, [], _('debugconfig [NAME]...')),
2973 "debugconfig": (debugconfig, [], _('debugconfig [NAME]...')),
2958 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2974 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2959 "debugstate": (debugstate, [], _('debugstate')),
2975 "debugstate": (debugstate, [], _('debugstate')),
2960 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2976 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2961 "debugindex": (debugindex, [], _('debugindex FILE')),
2977 "debugindex": (debugindex, [], _('debugindex FILE')),
2962 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2978 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2963 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2979 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2964 "debugwalk":
2980 "debugwalk":
2965 (debugwalk,
2981 (debugwalk,
2966 [('I', 'include', [], _('include names matching the given patterns')),
2982 [('I', 'include', [], _('include names matching the given patterns')),
2967 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2983 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2968 _('debugwalk [OPTION]... [FILE]...')),
2984 _('debugwalk [OPTION]... [FILE]...')),
2969 "^diff":
2985 "^diff":
2970 (diff,
2986 (diff,
2971 [('r', 'rev', [], _('revision')),
2987 [('r', 'rev', [], _('revision')),
2972 ('a', 'text', None, _('treat all files as text')),
2988 ('a', 'text', None, _('treat all files as text')),
2973 ('p', 'show-function', None,
2989 ('p', 'show-function', None,
2974 _('show which function each change is in')),
2990 _('show which function each change is in')),
2975 ('w', 'ignore-all-space', None,
2991 ('w', 'ignore-all-space', None,
2976 _('ignore white space when comparing lines')),
2992 _('ignore white space when comparing lines')),
2977 ('I', 'include', [], _('include names matching the given patterns')),
2993 ('I', 'include', [], _('include names matching the given patterns')),
2978 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2994 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2979 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2995 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2980 "^export":
2996 "^export":
2981 (export,
2997 (export,
2982 [('o', 'output', '', _('print output to file with formatted name')),
2998 [('o', 'output', '', _('print output to file with formatted name')),
2983 ('a', 'text', None, _('treat all files as text')),
2999 ('a', 'text', None, _('treat all files as text')),
2984 ('', 'switch-parent', None, _('diff against the second parent'))],
3000 ('', 'switch-parent', None, _('diff against the second parent'))],
2985 _('hg export [-a] [-o OUTFILESPEC] REV...')),
3001 _('hg export [-a] [-o OUTFILESPEC] REV...')),
2986 "debugforget|forget":
3002 "debugforget|forget":
2987 (forget,
3003 (forget,
2988 [('I', 'include', [], _('include names matching the given patterns')),
3004 [('I', 'include', [], _('include names matching the given patterns')),
2989 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3005 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2990 _('hg forget [OPTION]... FILE...')),
3006 _('hg forget [OPTION]... FILE...')),
2991 "grep":
3007 "grep":
2992 (grep,
3008 (grep,
2993 [('0', 'print0', None, _('end fields with NUL')),
3009 [('0', 'print0', None, _('end fields with NUL')),
2994 ('', 'all', None, _('print all revisions that match')),
3010 ('', 'all', None, _('print all revisions that match')),
2995 ('i', 'ignore-case', None, _('ignore case when matching')),
3011 ('i', 'ignore-case', None, _('ignore case when matching')),
2996 ('l', 'files-with-matches', None,
3012 ('l', 'files-with-matches', None,
2997 _('print only filenames and revs that match')),
3013 _('print only filenames and revs that match')),
2998 ('n', 'line-number', None, _('print matching line numbers')),
3014 ('n', 'line-number', None, _('print matching line numbers')),
2999 ('r', 'rev', [], _('search in given revision range')),
3015 ('r', 'rev', [], _('search in given revision range')),
3000 ('u', 'user', None, _('print user who committed change')),
3016 ('u', 'user', None, _('print user who committed change')),
3001 ('I', 'include', [], _('include names matching the given patterns')),
3017 ('I', 'include', [], _('include names matching the given patterns')),
3002 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3018 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3003 _('hg grep [OPTION]... PATTERN [FILE]...')),
3019 _('hg grep [OPTION]... PATTERN [FILE]...')),
3004 "heads":
3020 "heads":
3005 (heads,
3021 (heads,
3006 [('b', 'branches', None, _('show branches')),
3022 [('b', 'branches', None, _('show branches')),
3007 ('', 'style', '', _('display using template map file')),
3023 ('', 'style', '', _('display using template map file')),
3008 ('r', 'rev', '', _('show only heads which are descendants of rev')),
3024 ('r', 'rev', '', _('show only heads which are descendants of rev')),
3009 ('', 'template', '', _('display with template'))],
3025 ('', 'template', '', _('display with template'))],
3010 _('hg heads [-b] [-r <rev>]')),
3026 _('hg heads [-b] [-r <rev>]')),
3011 "help": (help_, [], _('hg help [COMMAND]')),
3027 "help": (help_, [], _('hg help [COMMAND]')),
3012 "identify|id": (identify, [], _('hg identify')),
3028 "identify|id": (identify, [], _('hg identify')),
3013 "import|patch":
3029 "import|patch":
3014 (import_,
3030 (import_,
3015 [('p', 'strip', 1,
3031 [('p', 'strip', 1,
3016 _('directory strip option for patch. This has the same\n'
3032 _('directory strip option for patch. This has the same\n'
3017 'meaning as the corresponding patch option')),
3033 'meaning as the corresponding patch option')),
3018 ('m', 'message', '', _('use <text> as commit message')),
3034 ('m', 'message', '', _('use <text> as commit message')),
3019 ('b', 'base', '', _('base path')),
3035 ('b', 'base', '', _('base path')),
3020 ('f', 'force', None,
3036 ('f', 'force', None,
3021 _('skip check for outstanding uncommitted changes'))],
3037 _('skip check for outstanding uncommitted changes'))],
3022 _('hg import [-p NUM] [-b BASE] [-m MESSAGE] [-f] PATCH...')),
3038 _('hg import [-p NUM] [-b BASE] [-m MESSAGE] [-f] PATCH...')),
3023 "incoming|in": (incoming,
3039 "incoming|in": (incoming,
3024 [('M', 'no-merges', None, _('do not show merges')),
3040 [('M', 'no-merges', None, _('do not show merges')),
3025 ('f', 'force', None,
3041 ('f', 'force', None,
3026 _('run even when remote repository is unrelated')),
3042 _('run even when remote repository is unrelated')),
3027 ('', 'style', '', _('display using template map file')),
3043 ('', 'style', '', _('display using template map file')),
3028 ('n', 'newest-first', None, _('show newest record first')),
3044 ('n', 'newest-first', None, _('show newest record first')),
3029 ('', 'bundle', '', _('file to store the bundles into')),
3045 ('', 'bundle', '', _('file to store the bundles into')),
3030 ('p', 'patch', None, _('show patch')),
3046 ('p', 'patch', None, _('show patch')),
3031 ('r', 'rev', [], _('a specific revision you would like to pull')),
3047 ('r', 'rev', [], _('a specific revision you would like to pull')),
3032 ('', 'template', '', _('display with template')),
3048 ('', 'template', '', _('display with template')),
3033 ('e', 'ssh', '', _('specify ssh command to use')),
3049 ('e', 'ssh', '', _('specify ssh command to use')),
3034 ('', 'remotecmd', '',
3050 ('', 'remotecmd', '',
3035 _('specify hg command to run on the remote side'))],
3051 _('specify hg command to run on the remote side'))],
3036 _('hg incoming [-p] [-n] [-M] [-r REV]...'
3052 _('hg incoming [-p] [-n] [-M] [-r REV]...'
3037 ' [--bundle FILENAME] [SOURCE]')),
3053 ' [--bundle FILENAME] [SOURCE]')),
3038 "^init": (init, [], _('hg init [DEST]')),
3054 "^init": (init, [], _('hg init [DEST]')),
3039 "locate":
3055 "locate":
3040 (locate,
3056 (locate,
3041 [('r', 'rev', '', _('search the repository as it stood at rev')),
3057 [('r', 'rev', '', _('search the repository as it stood at rev')),
3042 ('0', 'print0', None,
3058 ('0', 'print0', None,
3043 _('end filenames with NUL, for use with xargs')),
3059 _('end filenames with NUL, for use with xargs')),
3044 ('f', 'fullpath', None,
3060 ('f', 'fullpath', None,
3045 _('print complete paths from the filesystem root')),
3061 _('print complete paths from the filesystem root')),
3046 ('I', 'include', [], _('include names matching the given patterns')),
3062 ('I', 'include', [], _('include names matching the given patterns')),
3047 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3063 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3048 _('hg locate [OPTION]... [PATTERN]...')),
3064 _('hg locate [OPTION]... [PATTERN]...')),
3049 "^log|history":
3065 "^log|history":
3050 (log,
3066 (log,
3051 [('b', 'branches', None, _('show branches')),
3067 [('b', 'branches', None, _('show branches')),
3052 ('k', 'keyword', [], _('search for a keyword')),
3068 ('k', 'keyword', [], _('search for a keyword')),
3053 ('l', 'limit', '', _('limit number of changes displayed')),
3069 ('l', 'limit', '', _('limit number of changes displayed')),
3054 ('r', 'rev', [], _('show the specified revision or range')),
3070 ('r', 'rev', [], _('show the specified revision or range')),
3055 ('M', 'no-merges', None, _('do not show merges')),
3071 ('M', 'no-merges', None, _('do not show merges')),
3056 ('', 'style', '', _('display using template map file')),
3072 ('', 'style', '', _('display using template map file')),
3057 ('m', 'only-merges', None, _('show only merges')),
3073 ('m', 'only-merges', None, _('show only merges')),
3058 ('p', 'patch', None, _('show patch')),
3074 ('p', 'patch', None, _('show patch')),
3059 ('', 'template', '', _('display with template')),
3075 ('', 'template', '', _('display with template')),
3060 ('I', 'include', [], _('include names matching the given patterns')),
3076 ('I', 'include', [], _('include names matching the given patterns')),
3061 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3077 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3062 _('hg log [OPTION]... [FILE]')),
3078 _('hg log [OPTION]... [FILE]')),
3063 "manifest": (manifest, [], _('hg manifest [REV]')),
3079 "manifest": (manifest, [], _('hg manifest [REV]')),
3064 "merge":
3080 "merge":
3065 (merge,
3081 (merge,
3066 [('b', 'branch', '', _('merge with head of a specific branch')),
3082 [('b', 'branch', '', _('merge with head of a specific branch')),
3067 ('f', 'force', None, _('force a merge with outstanding changes'))],
3083 ('f', 'force', None, _('force a merge with outstanding changes'))],
3068 _('hg merge [-b TAG] [-f] [REV]')),
3084 _('hg merge [-b TAG] [-f] [REV]')),
3069 "outgoing|out": (outgoing,
3085 "outgoing|out": (outgoing,
3070 [('M', 'no-merges', None, _('do not show merges')),
3086 [('M', 'no-merges', None, _('do not show merges')),
3071 ('f', 'force', None,
3087 ('f', 'force', None,
3072 _('run even when remote repository is unrelated')),
3088 _('run even when remote repository is unrelated')),
3073 ('p', 'patch', None, _('show patch')),
3089 ('p', 'patch', None, _('show patch')),
3074 ('', 'style', '', _('display using template map file')),
3090 ('', 'style', '', _('display using template map file')),
3075 ('r', 'rev', [], _('a specific revision you would like to push')),
3091 ('r', 'rev', [], _('a specific revision you would like to push')),
3076 ('n', 'newest-first', None, _('show newest record first')),
3092 ('n', 'newest-first', None, _('show newest record first')),
3077 ('', 'template', '', _('display with template')),
3093 ('', 'template', '', _('display with template')),
3078 ('e', 'ssh', '', _('specify ssh command to use')),
3094 ('e', 'ssh', '', _('specify ssh command to use')),
3079 ('', 'remotecmd', '',
3095 ('', 'remotecmd', '',
3080 _('specify hg command to run on the remote side'))],
3096 _('specify hg command to run on the remote side'))],
3081 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
3097 _('hg outgoing [-M] [-p] [-n] [-r REV]... [DEST]')),
3082 "^parents":
3098 "^parents":
3083 (parents,
3099 (parents,
3084 [('b', 'branches', None, _('show branches')),
3100 [('b', 'branches', None, _('show branches')),
3085 ('', 'style', '', _('display using template map file')),
3101 ('', 'style', '', _('display using template map file')),
3086 ('', 'template', '', _('display with template'))],
3102 ('', 'template', '', _('display with template'))],
3087 _('hg parents [-b] [REV]')),
3103 _('hg parents [-b] [REV]')),
3088 "paths": (paths, [], _('hg paths [NAME]')),
3104 "paths": (paths, [], _('hg paths [NAME]')),
3089 "^pull":
3105 "^pull":
3090 (pull,
3106 (pull,
3091 [('u', 'update', None,
3107 [('u', 'update', None,
3092 _('update the working directory to tip after pull')),
3108 _('update the working directory to tip after pull')),
3093 ('e', 'ssh', '', _('specify ssh command to use')),
3109 ('e', 'ssh', '', _('specify ssh command to use')),
3094 ('f', 'force', None,
3110 ('f', 'force', None,
3095 _('run even when remote repository is unrelated')),
3111 _('run even when remote repository is unrelated')),
3096 ('r', 'rev', [], _('a specific revision you would like to pull')),
3112 ('r', 'rev', [], _('a specific revision you would like to pull')),
3097 ('', 'remotecmd', '',
3113 ('', 'remotecmd', '',
3098 _('specify hg command to run on the remote side'))],
3114 _('specify hg command to run on the remote side'))],
3099 _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')),
3115 _('hg pull [-u] [-e FILE] [-r REV]... [--remotecmd FILE] [SOURCE]')),
3100 "^push":
3116 "^push":
3101 (push,
3117 (push,
3102 [('f', 'force', None, _('force push')),
3118 [('f', 'force', None, _('force push')),
3103 ('e', 'ssh', '', _('specify ssh command to use')),
3119 ('e', 'ssh', '', _('specify ssh command to use')),
3104 ('r', 'rev', [], _('a specific revision you would like to push')),
3120 ('r', 'rev', [], _('a specific revision you would like to push')),
3105 ('', 'remotecmd', '',
3121 ('', 'remotecmd', '',
3106 _('specify hg command to run on the remote side'))],
3122 _('specify hg command to run on the remote side'))],
3107 _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')),
3123 _('hg push [-f] [-e FILE] [-r REV]... [--remotecmd FILE] [DEST]')),
3108 "debugrawcommit|rawcommit":
3124 "debugrawcommit|rawcommit":
3109 (rawcommit,
3125 (rawcommit,
3110 [('p', 'parent', [], _('parent')),
3126 [('p', 'parent', [], _('parent')),
3111 ('d', 'date', '', _('date code')),
3127 ('d', 'date', '', _('date code')),
3112 ('u', 'user', '', _('user')),
3128 ('u', 'user', '', _('user')),
3113 ('F', 'files', '', _('file list')),
3129 ('F', 'files', '', _('file list')),
3114 ('m', 'message', '', _('commit message')),
3130 ('m', 'message', '', _('commit message')),
3115 ('l', 'logfile', '', _('commit message file'))],
3131 ('l', 'logfile', '', _('commit message file'))],
3116 _('hg debugrawcommit [OPTION]... [FILE]...')),
3132 _('hg debugrawcommit [OPTION]... [FILE]...')),
3117 "recover": (recover, [], _('hg recover')),
3133 "recover": (recover, [], _('hg recover')),
3118 "^remove|rm":
3134 "^remove|rm":
3119 (remove,
3135 (remove,
3120 [('A', 'after', None, _('record remove that has already occurred')),
3136 [('A', 'after', None, _('record remove that has already occurred')),
3121 ('f', 'force', None, _('remove file even if modified')),
3137 ('f', 'force', None, _('remove file even if modified')),
3122 ('I', 'include', [], _('include names matching the given patterns')),
3138 ('I', 'include', [], _('include names matching the given patterns')),
3123 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3139 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3124 _('hg remove [OPTION]... FILE...')),
3140 _('hg remove [OPTION]... FILE...')),
3125 "rename|mv":
3141 "rename|mv":
3126 (rename,
3142 (rename,
3127 [('A', 'after', None, _('record a rename that has already occurred')),
3143 [('A', 'after', None, _('record a rename that has already occurred')),
3128 ('f', 'force', None,
3144 ('f', 'force', None,
3129 _('forcibly copy over an existing managed file')),
3145 _('forcibly copy over an existing managed file')),
3130 ('I', 'include', [], _('include names matching the given patterns')),
3146 ('I', 'include', [], _('include names matching the given patterns')),
3131 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3147 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3132 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3148 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3133 _('hg rename [OPTION]... SOURCE... DEST')),
3149 _('hg rename [OPTION]... SOURCE... DEST')),
3134 "^revert":
3150 "^revert":
3135 (revert,
3151 (revert,
3136 [('r', 'rev', '', _('revision to revert to')),
3152 [('r', 'rev', '', _('revision to revert to')),
3137 ('', 'no-backup', None, _('do not save backup copies of files')),
3153 ('', 'no-backup', None, _('do not save backup copies of files')),
3138 ('I', 'include', [], _('include names matching given patterns')),
3154 ('I', 'include', [], _('include names matching given patterns')),
3139 ('X', 'exclude', [], _('exclude names matching given patterns')),
3155 ('X', 'exclude', [], _('exclude names matching given patterns')),
3140 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3156 ('n', 'dry-run', None, _('do not perform actions, just print output'))],
3141 _('hg revert [-r REV] [NAME]...')),
3157 _('hg revert [-r REV] [NAME]...')),
3142 "rollback": (rollback, [], _('hg rollback')),
3158 "rollback": (rollback, [], _('hg rollback')),
3143 "root": (root, [], _('hg root')),
3159 "root": (root, [], _('hg root')),
3144 "^serve":
3160 "^serve":
3145 (serve,
3161 (serve,
3146 [('A', 'accesslog', '', _('name of access log file to write to')),
3162 [('A', 'accesslog', '', _('name of access log file to write to')),
3147 ('d', 'daemon', None, _('run server in background')),
3163 ('d', 'daemon', None, _('run server in background')),
3148 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3164 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3149 ('E', 'errorlog', '', _('name of error log file to write to')),
3165 ('E', 'errorlog', '', _('name of error log file to write to')),
3150 ('p', 'port', 0, _('port to use (default: 8000)')),
3166 ('p', 'port', 0, _('port to use (default: 8000)')),
3151 ('a', 'address', '', _('address to use')),
3167 ('a', 'address', '', _('address to use')),
3152 ('n', 'name', '',
3168 ('n', 'name', '',
3153 _('name to show in web pages (default: working dir)')),
3169 _('name to show in web pages (default: working dir)')),
3154 ('', 'webdir-conf', '', _('name of the webdir config file'
3170 ('', 'webdir-conf', '', _('name of the webdir config file'
3155 ' (serve more than one repo)')),
3171 ' (serve more than one repo)')),
3156 ('', 'pid-file', '', _('name of file to write process ID to')),
3172 ('', 'pid-file', '', _('name of file to write process ID to')),
3157 ('', 'stdio', None, _('for remote clients')),
3173 ('', 'stdio', None, _('for remote clients')),
3158 ('t', 'templates', '', _('web templates to use')),
3174 ('t', 'templates', '', _('web templates to use')),
3159 ('', 'style', '', _('template style to use')),
3175 ('', 'style', '', _('template style to use')),
3160 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3176 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
3161 _('hg serve [OPTION]...')),
3177 _('hg serve [OPTION]...')),
3162 "^status|st":
3178 "^status|st":
3163 (status,
3179 (status,
3164 [('m', 'modified', None, _('show only modified files')),
3180 [('m', 'modified', None, _('show only modified files')),
3165 ('a', 'added', None, _('show only added files')),
3181 ('a', 'added', None, _('show only added files')),
3166 ('r', 'removed', None, _('show only removed files')),
3182 ('r', 'removed', None, _('show only removed files')),
3167 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3183 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3168 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3184 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3169 ('i', 'ignored', None, _('show ignored files')),
3185 ('i', 'ignored', None, _('show ignored files')),
3170 ('n', 'no-status', None, _('hide status prefix')),
3186 ('n', 'no-status', None, _('hide status prefix')),
3171 ('0', 'print0', None,
3187 ('0', 'print0', None,
3172 _('end filenames with NUL, for use with xargs')),
3188 _('end filenames with NUL, for use with xargs')),
3173 ('I', 'include', [], _('include names matching the given patterns')),
3189 ('I', 'include', [], _('include names matching the given patterns')),
3174 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3190 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
3175 _('hg status [OPTION]... [FILE]...')),
3191 _('hg status [OPTION]... [FILE]...')),
3176 "tag":
3192 "tag":
3177 (tag,
3193 (tag,
3178 [('l', 'local', None, _('make the tag local')),
3194 [('l', 'local', None, _('make the tag local')),
3179 ('m', 'message', '', _('message for tag commit log entry')),
3195 ('m', 'message', '', _('message for tag commit log entry')),
3180 ('d', 'date', '', _('record datecode as commit date')),
3196 ('d', 'date', '', _('record datecode as commit date')),
3181 ('u', 'user', '', _('record user as commiter')),
3197 ('u', 'user', '', _('record user as commiter')),
3182 ('r', 'rev', '', _('revision to tag'))],
3198 ('r', 'rev', '', _('revision to tag'))],
3183 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3199 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
3184 "tags": (tags, [], _('hg tags')),
3200 "tags": (tags, [], _('hg tags')),
3185 "tip":
3201 "tip":
3186 (tip,
3202 (tip,
3187 [('b', 'branches', None, _('show branches')),
3203 [('b', 'branches', None, _('show branches')),
3188 ('', 'style', '', _('display using template map file')),
3204 ('', 'style', '', _('display using template map file')),
3189 ('p', 'patch', None, _('show patch')),
3205 ('p', 'patch', None, _('show patch')),
3190 ('', 'template', '', _('display with template'))],
3206 ('', 'template', '', _('display with template'))],
3191 _('hg tip [-b] [-p]')),
3207 _('hg tip [-b] [-p]')),
3192 "unbundle":
3208 "unbundle":
3193 (unbundle,
3209 (unbundle,
3194 [('u', 'update', None,
3210 [('u', 'update', None,
3195 _('update the working directory to tip after unbundle'))],
3211 _('update the working directory to tip after unbundle'))],
3196 _('hg unbundle [-u] FILE')),
3212 _('hg unbundle [-u] FILE')),
3197 "debugundo|undo": (undo, [], _('hg undo')),
3213 "debugundo|undo": (undo, [], _('hg undo')),
3198 "^update|up|checkout|co":
3214 "^update|up|checkout|co":
3199 (update,
3215 (update,
3200 [('b', 'branch', '', _('checkout the head of a specific branch')),
3216 [('b', 'branch', '', _('checkout the head of a specific branch')),
3201 ('m', 'merge', None, _('allow merging of branches (DEPRECATED)')),
3217 ('m', 'merge', None, _('allow merging of branches (DEPRECATED)')),
3202 ('C', 'clean', None, _('overwrite locally modified files')),
3218 ('C', 'clean', None, _('overwrite locally modified files')),
3203 ('f', 'force', None, _('force a merge with outstanding changes'))],
3219 ('f', 'force', None, _('force a merge with outstanding changes'))],
3204 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
3220 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
3205 "verify": (verify, [], _('hg verify')),
3221 "verify": (verify, [], _('hg verify')),
3206 "version": (show_version, [], _('hg version')),
3222 "version": (show_version, [], _('hg version')),
3207 }
3223 }
3208
3224
3209 globalopts = [
3225 globalopts = [
3210 ('R', 'repository', '',
3226 ('R', 'repository', '',
3211 _('repository root directory or symbolic path name')),
3227 _('repository root directory or symbolic path name')),
3212 ('', 'cwd', '', _('change working directory')),
3228 ('', 'cwd', '', _('change working directory')),
3213 ('y', 'noninteractive', None,
3229 ('y', 'noninteractive', None,
3214 _('do not prompt, assume \'yes\' for any required answers')),
3230 _('do not prompt, assume \'yes\' for any required answers')),
3215 ('q', 'quiet', None, _('suppress output')),
3231 ('q', 'quiet', None, _('suppress output')),
3216 ('v', 'verbose', None, _('enable additional output')),
3232 ('v', 'verbose', None, _('enable additional output')),
3217 ('', 'config', [], _('set/override config option')),
3233 ('', 'config', [], _('set/override config option')),
3218 ('', 'debug', None, _('enable debugging output')),
3234 ('', 'debug', None, _('enable debugging output')),
3219 ('', 'debugger', None, _('start debugger')),
3235 ('', 'debugger', None, _('start debugger')),
3220 ('', 'lsprof', None, _('print improved command execution profile')),
3236 ('', 'lsprof', None, _('print improved command execution profile')),
3221 ('', 'traceback', None, _('print traceback on exception')),
3237 ('', 'traceback', None, _('print traceback on exception')),
3222 ('', 'time', None, _('time how long the command takes')),
3238 ('', 'time', None, _('time how long the command takes')),
3223 ('', 'profile', None, _('print command execution profile')),
3239 ('', 'profile', None, _('print command execution profile')),
3224 ('', 'version', None, _('output version information and exit')),
3240 ('', 'version', None, _('output version information and exit')),
3225 ('h', 'help', None, _('display help and exit')),
3241 ('h', 'help', None, _('display help and exit')),
3226 ]
3242 ]
3227
3243
3228 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3244 norepo = ("clone init version help debugancestor debugcomplete debugdata"
3229 " debugindex debugindexdot")
3245 " debugindex debugindexdot")
3230 optionalrepo = ("paths serve debugconfig")
3246 optionalrepo = ("paths serve debugconfig")
3231
3247
3232 def findpossible(cmd):
3248 def findpossible(cmd):
3233 """
3249 """
3234 Return cmd -> (aliases, command table entry)
3250 Return cmd -> (aliases, command table entry)
3235 for each matching command.
3251 for each matching command.
3236 Return debug commands (or their aliases) only if no normal command matches.
3252 Return debug commands (or their aliases) only if no normal command matches.
3237 """
3253 """
3238 choice = {}
3254 choice = {}
3239 debugchoice = {}
3255 debugchoice = {}
3240 for e in table.keys():
3256 for e in table.keys():
3241 aliases = e.lstrip("^").split("|")
3257 aliases = e.lstrip("^").split("|")
3242 found = None
3258 found = None
3243 if cmd in aliases:
3259 if cmd in aliases:
3244 found = cmd
3260 found = cmd
3245 else:
3261 else:
3246 for a in aliases:
3262 for a in aliases:
3247 if a.startswith(cmd):
3263 if a.startswith(cmd):
3248 found = a
3264 found = a
3249 break
3265 break
3250 if found is not None:
3266 if found is not None:
3251 if aliases[0].startswith("debug"):
3267 if aliases[0].startswith("debug"):
3252 debugchoice[found] = (aliases, table[e])
3268 debugchoice[found] = (aliases, table[e])
3253 else:
3269 else:
3254 choice[found] = (aliases, table[e])
3270 choice[found] = (aliases, table[e])
3255
3271
3256 if not choice and debugchoice:
3272 if not choice and debugchoice:
3257 choice = debugchoice
3273 choice = debugchoice
3258
3274
3259 return choice
3275 return choice
3260
3276
3261 def find(cmd):
3277 def find(cmd):
3262 """Return (aliases, command table entry) for command string."""
3278 """Return (aliases, command table entry) for command string."""
3263 choice = findpossible(cmd)
3279 choice = findpossible(cmd)
3264
3280
3265 if choice.has_key(cmd):
3281 if choice.has_key(cmd):
3266 return choice[cmd]
3282 return choice[cmd]
3267
3283
3268 if len(choice) > 1:
3284 if len(choice) > 1:
3269 clist = choice.keys()
3285 clist = choice.keys()
3270 clist.sort()
3286 clist.sort()
3271 raise AmbiguousCommand(cmd, clist)
3287 raise AmbiguousCommand(cmd, clist)
3272
3288
3273 if choice:
3289 if choice:
3274 return choice.values()[0]
3290 return choice.values()[0]
3275
3291
3276 raise UnknownCommand(cmd)
3292 raise UnknownCommand(cmd)
3277
3293
3278 def catchterm(*args):
3294 def catchterm(*args):
3279 raise util.SignalInterrupt
3295 raise util.SignalInterrupt
3280
3296
3281 def run():
3297 def run():
3282 sys.exit(dispatch(sys.argv[1:]))
3298 sys.exit(dispatch(sys.argv[1:]))
3283
3299
3284 class ParseError(Exception):
3300 class ParseError(Exception):
3285 """Exception raised on errors in parsing the command line."""
3301 """Exception raised on errors in parsing the command line."""
3286
3302
3287 def parse(ui, args):
3303 def parse(ui, args):
3288 options = {}
3304 options = {}
3289 cmdoptions = {}
3305 cmdoptions = {}
3290
3306
3291 try:
3307 try:
3292 args = fancyopts.fancyopts(args, globalopts, options)
3308 args = fancyopts.fancyopts(args, globalopts, options)
3293 except fancyopts.getopt.GetoptError, inst:
3309 except fancyopts.getopt.GetoptError, inst:
3294 raise ParseError(None, inst)
3310 raise ParseError(None, inst)
3295
3311
3296 if args:
3312 if args:
3297 cmd, args = args[0], args[1:]
3313 cmd, args = args[0], args[1:]
3298 aliases, i = find(cmd)
3314 aliases, i = find(cmd)
3299 cmd = aliases[0]
3315 cmd = aliases[0]
3300 defaults = ui.config("defaults", cmd)
3316 defaults = ui.config("defaults", cmd)
3301 if defaults:
3317 if defaults:
3302 args = defaults.split() + args
3318 args = defaults.split() + args
3303 c = list(i[1])
3319 c = list(i[1])
3304 else:
3320 else:
3305 cmd = None
3321 cmd = None
3306 c = []
3322 c = []
3307
3323
3308 # combine global options into local
3324 # combine global options into local
3309 for o in globalopts:
3325 for o in globalopts:
3310 c.append((o[0], o[1], options[o[1]], o[3]))
3326 c.append((o[0], o[1], options[o[1]], o[3]))
3311
3327
3312 try:
3328 try:
3313 args = fancyopts.fancyopts(args, c, cmdoptions)
3329 args = fancyopts.fancyopts(args, c, cmdoptions)
3314 except fancyopts.getopt.GetoptError, inst:
3330 except fancyopts.getopt.GetoptError, inst:
3315 raise ParseError(cmd, inst)
3331 raise ParseError(cmd, inst)
3316
3332
3317 # separate global options back out
3333 # separate global options back out
3318 for o in globalopts:
3334 for o in globalopts:
3319 n = o[1]
3335 n = o[1]
3320 options[n] = cmdoptions[n]
3336 options[n] = cmdoptions[n]
3321 del cmdoptions[n]
3337 del cmdoptions[n]
3322
3338
3323 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3339 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
3324
3340
3325 def dispatch(args):
3341 def dispatch(args):
3326 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3342 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
3327 num = getattr(signal, name, None)
3343 num = getattr(signal, name, None)
3328 if num: signal.signal(num, catchterm)
3344 if num: signal.signal(num, catchterm)
3329
3345
3330 try:
3346 try:
3331 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3347 u = ui.ui(traceback='--traceback' in sys.argv[1:])
3332 except util.Abort, inst:
3348 except util.Abort, inst:
3333 sys.stderr.write(_("abort: %s\n") % inst)
3349 sys.stderr.write(_("abort: %s\n") % inst)
3334 return -1
3350 return -1
3335
3351
3336 external = []
3352 external = []
3337 for x in u.extensions():
3353 for x in u.extensions():
3338 try:
3354 try:
3339 if x[1]:
3355 if x[1]:
3340 # the module will be loaded in sys.modules
3356 # the module will be loaded in sys.modules
3341 # choose an unique name so that it doesn't
3357 # choose an unique name so that it doesn't
3342 # conflicts with other modules
3358 # conflicts with other modules
3343 module_name = "hgext_%s" % x[0].replace('.', '_')
3359 module_name = "hgext_%s" % x[0].replace('.', '_')
3344 mod = imp.load_source(module_name, x[1])
3360 mod = imp.load_source(module_name, x[1])
3345 else:
3361 else:
3346 def importh(name):
3362 def importh(name):
3347 mod = __import__(name)
3363 mod = __import__(name)
3348 components = name.split('.')
3364 components = name.split('.')
3349 for comp in components[1:]:
3365 for comp in components[1:]:
3350 mod = getattr(mod, comp)
3366 mod = getattr(mod, comp)
3351 return mod
3367 return mod
3352 try:
3368 try:
3353 mod = importh("hgext." + x[0])
3369 mod = importh("hgext." + x[0])
3354 except ImportError:
3370 except ImportError:
3355 mod = importh(x[0])
3371 mod = importh(x[0])
3356 external.append(mod)
3372 external.append(mod)
3357 except (util.SignalInterrupt, KeyboardInterrupt):
3373 except (util.SignalInterrupt, KeyboardInterrupt):
3358 raise
3374 raise
3359 except Exception, inst:
3375 except Exception, inst:
3360 u.warn(_("*** failed to import extension %s: %s\n") % (x[0], inst))
3376 u.warn(_("*** failed to import extension %s: %s\n") % (x[0], inst))
3361 if u.print_exc():
3377 if u.print_exc():
3362 return 1
3378 return 1
3363
3379
3364 for x in external:
3380 for x in external:
3365 uisetup = getattr(x, 'uisetup', None)
3381 uisetup = getattr(x, 'uisetup', None)
3366 if uisetup:
3382 if uisetup:
3367 uisetup(u)
3383 uisetup(u)
3368 cmdtable = getattr(x, 'cmdtable', {})
3384 cmdtable = getattr(x, 'cmdtable', {})
3369 for t in cmdtable:
3385 for t in cmdtable:
3370 if t in table:
3386 if t in table:
3371 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
3387 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
3372 table.update(cmdtable)
3388 table.update(cmdtable)
3373
3389
3374 try:
3390 try:
3375 cmd, func, args, options, cmdoptions = parse(u, args)
3391 cmd, func, args, options, cmdoptions = parse(u, args)
3376 if options["time"]:
3392 if options["time"]:
3377 def get_times():
3393 def get_times():
3378 t = os.times()
3394 t = os.times()
3379 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3395 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
3380 t = (t[0], t[1], t[2], t[3], time.clock())
3396 t = (t[0], t[1], t[2], t[3], time.clock())
3381 return t
3397 return t
3382 s = get_times()
3398 s = get_times()
3383 def print_time():
3399 def print_time():
3384 t = get_times()
3400 t = get_times()
3385 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3401 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
3386 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3402 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
3387 atexit.register(print_time)
3403 atexit.register(print_time)
3388
3404
3389 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3405 u.updateopts(options["verbose"], options["debug"], options["quiet"],
3390 not options["noninteractive"], options["traceback"],
3406 not options["noninteractive"], options["traceback"],
3391 options["config"])
3407 options["config"])
3392
3408
3393 # enter the debugger before command execution
3409 # enter the debugger before command execution
3394 if options['debugger']:
3410 if options['debugger']:
3395 pdb.set_trace()
3411 pdb.set_trace()
3396
3412
3397 try:
3413 try:
3398 if options['cwd']:
3414 if options['cwd']:
3399 try:
3415 try:
3400 os.chdir(options['cwd'])
3416 os.chdir(options['cwd'])
3401 except OSError, inst:
3417 except OSError, inst:
3402 raise util.Abort('%s: %s' %
3418 raise util.Abort('%s: %s' %
3403 (options['cwd'], inst.strerror))
3419 (options['cwd'], inst.strerror))
3404
3420
3405 path = u.expandpath(options["repository"]) or ""
3421 path = u.expandpath(options["repository"]) or ""
3406 repo = path and hg.repository(u, path=path) or None
3422 repo = path and hg.repository(u, path=path) or None
3407
3423
3408 if options['help']:
3424 if options['help']:
3409 return help_(u, cmd, options['version'])
3425 return help_(u, cmd, options['version'])
3410 elif options['version']:
3426 elif options['version']:
3411 return show_version(u)
3427 return show_version(u)
3412 elif not cmd:
3428 elif not cmd:
3413 return help_(u, 'shortlist')
3429 return help_(u, 'shortlist')
3414
3430
3415 if cmd not in norepo.split():
3431 if cmd not in norepo.split():
3416 try:
3432 try:
3417 if not repo:
3433 if not repo:
3418 repo = hg.repository(u, path=path)
3434 repo = hg.repository(u, path=path)
3419 u = repo.ui
3435 u = repo.ui
3420 for x in external:
3436 for x in external:
3421 if hasattr(x, 'reposetup'):
3437 if hasattr(x, 'reposetup'):
3422 x.reposetup(u, repo)
3438 x.reposetup(u, repo)
3423 except hg.RepoError:
3439 except hg.RepoError:
3424 if cmd not in optionalrepo.split():
3440 if cmd not in optionalrepo.split():
3425 raise
3441 raise
3426 d = lambda: func(u, repo, *args, **cmdoptions)
3442 d = lambda: func(u, repo, *args, **cmdoptions)
3427 else:
3443 else:
3428 d = lambda: func(u, *args, **cmdoptions)
3444 d = lambda: func(u, *args, **cmdoptions)
3429
3445
3430 try:
3446 try:
3431 if options['profile']:
3447 if options['profile']:
3432 import hotshot, hotshot.stats
3448 import hotshot, hotshot.stats
3433 prof = hotshot.Profile("hg.prof")
3449 prof = hotshot.Profile("hg.prof")
3434 try:
3450 try:
3435 try:
3451 try:
3436 return prof.runcall(d)
3452 return prof.runcall(d)
3437 except:
3453 except:
3438 try:
3454 try:
3439 u.warn(_('exception raised - generating '
3455 u.warn(_('exception raised - generating '
3440 'profile anyway\n'))
3456 'profile anyway\n'))
3441 except:
3457 except:
3442 pass
3458 pass
3443 raise
3459 raise
3444 finally:
3460 finally:
3445 prof.close()
3461 prof.close()
3446 stats = hotshot.stats.load("hg.prof")
3462 stats = hotshot.stats.load("hg.prof")
3447 stats.strip_dirs()
3463 stats.strip_dirs()
3448 stats.sort_stats('time', 'calls')
3464 stats.sort_stats('time', 'calls')
3449 stats.print_stats(40)
3465 stats.print_stats(40)
3450 elif options['lsprof']:
3466 elif options['lsprof']:
3451 try:
3467 try:
3452 from mercurial import lsprof
3468 from mercurial import lsprof
3453 except ImportError:
3469 except ImportError:
3454 raise util.Abort(_(
3470 raise util.Abort(_(
3455 'lsprof not available - install from '
3471 'lsprof not available - install from '
3456 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3472 'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'))
3457 p = lsprof.Profiler()
3473 p = lsprof.Profiler()
3458 p.enable(subcalls=True)
3474 p.enable(subcalls=True)
3459 try:
3475 try:
3460 return d()
3476 return d()
3461 finally:
3477 finally:
3462 p.disable()
3478 p.disable()
3463 stats = lsprof.Stats(p.getstats())
3479 stats = lsprof.Stats(p.getstats())
3464 stats.sort()
3480 stats.sort()
3465 stats.pprint(top=10, file=sys.stderr, climit=5)
3481 stats.pprint(top=10, file=sys.stderr, climit=5)
3466 else:
3482 else:
3467 return d()
3483 return d()
3468 finally:
3484 finally:
3469 u.flush()
3485 u.flush()
3470 except:
3486 except:
3471 # enter the debugger when we hit an exception
3487 # enter the debugger when we hit an exception
3472 if options['debugger']:
3488 if options['debugger']:
3473 pdb.post_mortem(sys.exc_info()[2])
3489 pdb.post_mortem(sys.exc_info()[2])
3474 u.print_exc()
3490 u.print_exc()
3475 raise
3491 raise
3476 except ParseError, inst:
3492 except ParseError, inst:
3477 if inst.args[0]:
3493 if inst.args[0]:
3478 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3494 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
3479 help_(u, inst.args[0])
3495 help_(u, inst.args[0])
3480 else:
3496 else:
3481 u.warn(_("hg: %s\n") % inst.args[1])
3497 u.warn(_("hg: %s\n") % inst.args[1])
3482 help_(u, 'shortlist')
3498 help_(u, 'shortlist')
3483 except AmbiguousCommand, inst:
3499 except AmbiguousCommand, inst:
3484 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3500 u.warn(_("hg: command '%s' is ambiguous:\n %s\n") %
3485 (inst.args[0], " ".join(inst.args[1])))
3501 (inst.args[0], " ".join(inst.args[1])))
3486 except UnknownCommand, inst:
3502 except UnknownCommand, inst:
3487 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3503 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
3488 help_(u, 'shortlist')
3504 help_(u, 'shortlist')
3489 except hg.RepoError, inst:
3505 except hg.RepoError, inst:
3490 u.warn(_("abort: %s!\n") % inst)
3506 u.warn(_("abort: %s!\n") % inst)
3491 except lock.LockHeld, inst:
3507 except lock.LockHeld, inst:
3492 if inst.errno == errno.ETIMEDOUT:
3508 if inst.errno == errno.ETIMEDOUT:
3493 reason = _('timed out waiting for lock held by %s') % inst.locker
3509 reason = _('timed out waiting for lock held by %s') % inst.locker
3494 else:
3510 else:
3495 reason = _('lock held by %s') % inst.locker
3511 reason = _('lock held by %s') % inst.locker
3496 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3512 u.warn(_("abort: %s: %s\n") % (inst.desc or inst.filename, reason))
3497 except lock.LockUnavailable, inst:
3513 except lock.LockUnavailable, inst:
3498 u.warn(_("abort: could not lock %s: %s\n") %
3514 u.warn(_("abort: could not lock %s: %s\n") %
3499 (inst.desc or inst.filename, inst.strerror))
3515 (inst.desc or inst.filename, inst.strerror))
3500 except revlog.RevlogError, inst:
3516 except revlog.RevlogError, inst:
3501 u.warn(_("abort: "), inst, "!\n")
3517 u.warn(_("abort: "), inst, "!\n")
3502 except util.SignalInterrupt:
3518 except util.SignalInterrupt:
3503 u.warn(_("killed!\n"))
3519 u.warn(_("killed!\n"))
3504 except KeyboardInterrupt:
3520 except KeyboardInterrupt:
3505 try:
3521 try:
3506 u.warn(_("interrupted!\n"))
3522 u.warn(_("interrupted!\n"))
3507 except IOError, inst:
3523 except IOError, inst:
3508 if inst.errno == errno.EPIPE:
3524 if inst.errno == errno.EPIPE:
3509 if u.debugflag:
3525 if u.debugflag:
3510 u.warn(_("\nbroken pipe\n"))
3526 u.warn(_("\nbroken pipe\n"))
3511 else:
3527 else:
3512 raise
3528 raise
3513 except IOError, inst:
3529 except IOError, inst:
3514 if hasattr(inst, "code"):
3530 if hasattr(inst, "code"):
3515 u.warn(_("abort: %s\n") % inst)
3531 u.warn(_("abort: %s\n") % inst)
3516 elif hasattr(inst, "reason"):
3532 elif hasattr(inst, "reason"):
3517 u.warn(_("abort: error: %s\n") % inst.reason[1])
3533 u.warn(_("abort: error: %s\n") % inst.reason[1])
3518 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3534 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
3519 if u.debugflag:
3535 if u.debugflag:
3520 u.warn(_("broken pipe\n"))
3536 u.warn(_("broken pipe\n"))
3521 elif getattr(inst, "strerror", None):
3537 elif getattr(inst, "strerror", None):
3522 if getattr(inst, "filename", None):
3538 if getattr(inst, "filename", None):
3523 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3539 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
3524 else:
3540 else:
3525 u.warn(_("abort: %s\n") % inst.strerror)
3541 u.warn(_("abort: %s\n") % inst.strerror)
3526 else:
3542 else:
3527 raise
3543 raise
3528 except OSError, inst:
3544 except OSError, inst:
3529 if hasattr(inst, "filename"):
3545 if hasattr(inst, "filename"):
3530 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3546 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
3531 else:
3547 else:
3532 u.warn(_("abort: %s\n") % inst.strerror)
3548 u.warn(_("abort: %s\n") % inst.strerror)
3533 except util.Abort, inst:
3549 except util.Abort, inst:
3534 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3550 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
3535 except TypeError, inst:
3551 except TypeError, inst:
3536 # was this an argument error?
3552 # was this an argument error?
3537 tb = traceback.extract_tb(sys.exc_info()[2])
3553 tb = traceback.extract_tb(sys.exc_info()[2])
3538 if len(tb) > 2: # no
3554 if len(tb) > 2: # no
3539 raise
3555 raise
3540 u.debug(inst, "\n")
3556 u.debug(inst, "\n")
3541 u.warn(_("%s: invalid arguments\n") % cmd)
3557 u.warn(_("%s: invalid arguments\n") % cmd)
3542 help_(u, cmd)
3558 help_(u, cmd)
3543 except SystemExit, inst:
3559 except SystemExit, inst:
3544 # Commands shouldn't sys.exit directly, but give a return code.
3560 # Commands shouldn't sys.exit directly, but give a return code.
3545 # Just in case catch this and and pass exit code to caller.
3561 # Just in case catch this and and pass exit code to caller.
3546 return inst.code
3562 return inst.code
3547 except:
3563 except:
3548 u.warn(_("** unknown exception encountered, details follow\n"))
3564 u.warn(_("** unknown exception encountered, details follow\n"))
3549 u.warn(_("** report bug details to mercurial@selenic.com\n"))
3565 u.warn(_("** report bug details to mercurial@selenic.com\n"))
3550 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3566 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
3551 % version.get_version())
3567 % version.get_version())
3552 raise
3568 raise
3553
3569
3554 return -1
3570 return -1
@@ -1,70 +1,73 b''
1 # hg.py - repository classes for mercurial
1 # hg.py - repository classes 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 node import *
8 from node import *
9 from repo import *
9 from repo import *
10 from demandload import *
10 from demandload import *
11 from i18n import gettext as _
11 from i18n import gettext as _
12 demandload(globals(), "localrepo bundlerepo httprepo sshrepo statichttprepo")
12 demandload(globals(), "localrepo bundlerepo httprepo sshrepo statichttprepo")
13 demandload(globals(), "os util")
13 demandload(globals(), "os util")
14
14
15 def bundle(ui, path):
15 def bundle(ui, path):
16 if path.startswith('bundle://'):
16 if path.startswith('bundle://'):
17 path = path[9:]
17 path = path[9:]
18 else:
18 else:
19 path = path[7:]
19 path = path[7:]
20 s = path.split("+", 1)
20 s = path.split("+", 1)
21 if len(s) == 1:
21 if len(s) == 1:
22 repopath, bundlename = "", s[0]
22 repopath, bundlename = "", s[0]
23 else:
23 else:
24 repopath, bundlename = s
24 repopath, bundlename = s
25 return bundlerepo.bundlerepository(ui, repopath, bundlename)
25 return bundlerepo.bundlerepository(ui, repopath, bundlename)
26
26
27 def hg(ui, path):
27 def hg(ui, path):
28 ui.warn(_("hg:// syntax is deprecated, please use http:// instead\n"))
28 ui.warn(_("hg:// syntax is deprecated, please use http:// instead\n"))
29 return httprepo.httprepository(ui, path.replace("hg://", "http://"))
29 return httprepo.httprepository(ui, path.replace("hg://", "http://"))
30
30
31 def local_(ui, path, create=0):
31 def local_(ui, path, create=0):
32 if path.startswith('file:'):
32 if path.startswith('file:'):
33 path = path[5:]
33 path = path[5:]
34 return localrepo.localrepository(ui, path, create)
34 return localrepo.localrepository(ui, path, create)
35
35
36 def ssh_(ui, path, create=0):
37 return sshrepo.sshrepository(ui, path, create)
38
36 def old_http(ui, path):
39 def old_http(ui, path):
37 ui.warn(_("old-http:// syntax is deprecated, "
40 ui.warn(_("old-http:// syntax is deprecated, "
38 "please use static-http:// instead\n"))
41 "please use static-http:// instead\n"))
39 return statichttprepo.statichttprepository(
42 return statichttprepo.statichttprepository(
40 ui, path.replace("old-http://", "http://"))
43 ui, path.replace("old-http://", "http://"))
41
44
42 def static_http(ui, path):
45 def static_http(ui, path):
43 return statichttprepo.statichttprepository(
46 return statichttprepo.statichttprepository(
44 ui, path.replace("static-http://", "http://"))
47 ui, path.replace("static-http://", "http://"))
45
48
46 schemes = {
49 schemes = {
47 'bundle': bundle,
50 'bundle': bundle,
48 'file': local_,
51 'file': local_,
49 'hg': hg,
52 'hg': hg,
50 'http': lambda ui, path: httprepo.httprepository(ui, path),
53 'http': lambda ui, path: httprepo.httprepository(ui, path),
51 'https': lambda ui, path: httprepo.httpsrepository(ui, path),
54 'https': lambda ui, path: httprepo.httpsrepository(ui, path),
52 'old-http': old_http,
55 'old-http': old_http,
53 'ssh': lambda ui, path: sshrepo.sshrepository(ui, path),
56 'ssh': ssh_,
54 'static-http': static_http,
57 'static-http': static_http,
55 }
58 }
56
59
57 def repository(ui, path=None, create=0):
60 def repository(ui, path=None, create=0):
58 if not path: path = ''
61 if not path: path = ''
59 scheme = path
62 scheme = path
60 if scheme:
63 if scheme:
61 c = scheme.find(':')
64 c = scheme.find(':')
62 scheme = c >= 0 and scheme[:c]
65 scheme = c >= 0 and scheme[:c]
63 try:
66 try:
64 ctor = schemes.get(scheme) or schemes['file']
67 ctor = schemes.get(scheme) or schemes['file']
65 if create:
68 if create:
66 return ctor(ui, path, create)
69 return ctor(ui, path, create)
67 return ctor(ui, path)
70 return ctor(ui, path)
68 except TypeError:
71 except TypeError:
69 raise util.Abort(_('cannot create new repository over "%s" protocol') %
72 raise util.Abort(_('cannot create new repository over "%s" protocol') %
70 scheme)
73 scheme)
@@ -1,2152 +1,2154 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 from node import *
8 from node import *
9 from i18n import gettext as _
9 from i18n import gettext as _
10 from demandload import *
10 from demandload import *
11 demandload(globals(), "appendfile changegroup")
11 demandload(globals(), "appendfile changegroup")
12 demandload(globals(), "changelog dirstate filelog manifest repo")
12 demandload(globals(), "changelog dirstate filelog manifest repo")
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(), "os revlog util")
14 demandload(globals(), "os revlog util")
15
15
16 class localrepository(object):
16 class localrepository(object):
17 capabilities = ()
17 capabilities = ()
18
18
19 def __del__(self):
19 def __del__(self):
20 self.transhandle = None
20 self.transhandle = None
21 def __init__(self, parentui, path=None, create=0):
21 def __init__(self, parentui, path=None, create=0):
22 if not path:
22 if not path:
23 p = os.getcwd()
23 p = os.getcwd()
24 while not os.path.isdir(os.path.join(p, ".hg")):
24 while not os.path.isdir(os.path.join(p, ".hg")):
25 oldp = p
25 oldp = p
26 p = os.path.dirname(p)
26 p = os.path.dirname(p)
27 if p == oldp:
27 if p == oldp:
28 raise repo.RepoError(_("no repo found"))
28 raise repo.RepoError(_("no repo found"))
29 path = p
29 path = p
30 self.path = os.path.join(path, ".hg")
30 self.path = os.path.join(path, ".hg")
31
31
32 if not create and not os.path.isdir(self.path):
32 if not create and not os.path.isdir(self.path):
33 raise repo.RepoError(_("repository %s not found") % path)
33 raise repo.RepoError(_("repository %s not found") % path)
34
34
35 self.root = os.path.abspath(path)
35 self.root = os.path.abspath(path)
36 self.origroot = path
36 self.origroot = path
37 self.ui = ui.ui(parentui=parentui)
37 self.ui = ui.ui(parentui=parentui)
38 self.opener = util.opener(self.path)
38 self.opener = util.opener(self.path)
39 self.wopener = util.opener(self.root)
39 self.wopener = util.opener(self.root)
40
40
41 try:
41 try:
42 self.ui.readconfig(self.join("hgrc"), self.root)
42 self.ui.readconfig(self.join("hgrc"), self.root)
43 except IOError:
43 except IOError:
44 pass
44 pass
45
45
46 v = self.ui.revlogopts
46 v = self.ui.revlogopts
47 self.revlogversion = int(v.get('format', revlog.REVLOG_DEFAULT_FORMAT))
47 self.revlogversion = int(v.get('format', revlog.REVLOG_DEFAULT_FORMAT))
48 self.revlogv1 = self.revlogversion != revlog.REVLOGV0
48 self.revlogv1 = self.revlogversion != revlog.REVLOGV0
49 fl = v.get('flags', None)
49 fl = v.get('flags', None)
50 flags = 0
50 flags = 0
51 if fl != None:
51 if fl != None:
52 for x in fl.split():
52 for x in fl.split():
53 flags |= revlog.flagstr(x)
53 flags |= revlog.flagstr(x)
54 elif self.revlogv1:
54 elif self.revlogv1:
55 flags = revlog.REVLOG_DEFAULT_FLAGS
55 flags = revlog.REVLOG_DEFAULT_FLAGS
56
56
57 v = self.revlogversion | flags
57 v = self.revlogversion | flags
58 self.manifest = manifest.manifest(self.opener, v)
58 self.manifest = manifest.manifest(self.opener, v)
59 self.changelog = changelog.changelog(self.opener, v)
59 self.changelog = changelog.changelog(self.opener, v)
60
60
61 # the changelog might not have the inline index flag
61 # the changelog might not have the inline index flag
62 # on. If the format of the changelog is the same as found in
62 # on. If the format of the changelog is the same as found in
63 # .hgrc, apply any flags found in the .hgrc as well.
63 # .hgrc, apply any flags found in the .hgrc as well.
64 # Otherwise, just version from the changelog
64 # Otherwise, just version from the changelog
65 v = self.changelog.version
65 v = self.changelog.version
66 if v == self.revlogversion:
66 if v == self.revlogversion:
67 v |= flags
67 v |= flags
68 self.revlogversion = v
68 self.revlogversion = v
69
69
70 self.tagscache = None
70 self.tagscache = None
71 self.nodetagscache = None
71 self.nodetagscache = None
72 self.encodepats = None
72 self.encodepats = None
73 self.decodepats = None
73 self.decodepats = None
74 self.transhandle = None
74 self.transhandle = None
75
75
76 if create:
76 if create:
77 if not os.path.exists(path):
78 os.mkdir(path)
77 os.mkdir(self.path)
79 os.mkdir(self.path)
78 os.mkdir(self.join("data"))
80 os.mkdir(self.join("data"))
79
81
80 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
82 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
81
83
82 def hook(self, name, throw=False, **args):
84 def hook(self, name, throw=False, **args):
83 def callhook(hname, funcname):
85 def callhook(hname, funcname):
84 '''call python hook. hook is callable object, looked up as
86 '''call python hook. hook is callable object, looked up as
85 name in python module. if callable returns "true", hook
87 name in python module. if callable returns "true", hook
86 fails, else passes. if hook raises exception, treated as
88 fails, else passes. if hook raises exception, treated as
87 hook failure. exception propagates if throw is "true".
89 hook failure. exception propagates if throw is "true".
88
90
89 reason for "true" meaning "hook failed" is so that
91 reason for "true" meaning "hook failed" is so that
90 unmodified commands (e.g. mercurial.commands.update) can
92 unmodified commands (e.g. mercurial.commands.update) can
91 be run as hooks without wrappers to convert return values.'''
93 be run as hooks without wrappers to convert return values.'''
92
94
93 self.ui.note(_("calling hook %s: %s\n") % (hname, funcname))
95 self.ui.note(_("calling hook %s: %s\n") % (hname, funcname))
94 d = funcname.rfind('.')
96 d = funcname.rfind('.')
95 if d == -1:
97 if d == -1:
96 raise util.Abort(_('%s hook is invalid ("%s" not in a module)')
98 raise util.Abort(_('%s hook is invalid ("%s" not in a module)')
97 % (hname, funcname))
99 % (hname, funcname))
98 modname = funcname[:d]
100 modname = funcname[:d]
99 try:
101 try:
100 obj = __import__(modname)
102 obj = __import__(modname)
101 except ImportError:
103 except ImportError:
102 raise util.Abort(_('%s hook is invalid '
104 raise util.Abort(_('%s hook is invalid '
103 '(import of "%s" failed)') %
105 '(import of "%s" failed)') %
104 (hname, modname))
106 (hname, modname))
105 try:
107 try:
106 for p in funcname.split('.')[1:]:
108 for p in funcname.split('.')[1:]:
107 obj = getattr(obj, p)
109 obj = getattr(obj, p)
108 except AttributeError, err:
110 except AttributeError, err:
109 raise util.Abort(_('%s hook is invalid '
111 raise util.Abort(_('%s hook is invalid '
110 '("%s" is not defined)') %
112 '("%s" is not defined)') %
111 (hname, funcname))
113 (hname, funcname))
112 if not callable(obj):
114 if not callable(obj):
113 raise util.Abort(_('%s hook is invalid '
115 raise util.Abort(_('%s hook is invalid '
114 '("%s" is not callable)') %
116 '("%s" is not callable)') %
115 (hname, funcname))
117 (hname, funcname))
116 try:
118 try:
117 r = obj(ui=self.ui, repo=self, hooktype=name, **args)
119 r = obj(ui=self.ui, repo=self, hooktype=name, **args)
118 except (KeyboardInterrupt, util.SignalInterrupt):
120 except (KeyboardInterrupt, util.SignalInterrupt):
119 raise
121 raise
120 except Exception, exc:
122 except Exception, exc:
121 if isinstance(exc, util.Abort):
123 if isinstance(exc, util.Abort):
122 self.ui.warn(_('error: %s hook failed: %s\n') %
124 self.ui.warn(_('error: %s hook failed: %s\n') %
123 (hname, exc.args[0] % exc.args[1:]))
125 (hname, exc.args[0] % exc.args[1:]))
124 else:
126 else:
125 self.ui.warn(_('error: %s hook raised an exception: '
127 self.ui.warn(_('error: %s hook raised an exception: '
126 '%s\n') % (hname, exc))
128 '%s\n') % (hname, exc))
127 if throw:
129 if throw:
128 raise
130 raise
129 self.ui.print_exc()
131 self.ui.print_exc()
130 return True
132 return True
131 if r:
133 if r:
132 if throw:
134 if throw:
133 raise util.Abort(_('%s hook failed') % hname)
135 raise util.Abort(_('%s hook failed') % hname)
134 self.ui.warn(_('warning: %s hook failed\n') % hname)
136 self.ui.warn(_('warning: %s hook failed\n') % hname)
135 return r
137 return r
136
138
137 def runhook(name, cmd):
139 def runhook(name, cmd):
138 self.ui.note(_("running hook %s: %s\n") % (name, cmd))
140 self.ui.note(_("running hook %s: %s\n") % (name, cmd))
139 env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()])
141 env = dict([('HG_' + k.upper(), v) for k, v in args.iteritems()])
140 r = util.system(cmd, environ=env, cwd=self.root)
142 r = util.system(cmd, environ=env, cwd=self.root)
141 if r:
143 if r:
142 desc, r = util.explain_exit(r)
144 desc, r = util.explain_exit(r)
143 if throw:
145 if throw:
144 raise util.Abort(_('%s hook %s') % (name, desc))
146 raise util.Abort(_('%s hook %s') % (name, desc))
145 self.ui.warn(_('warning: %s hook %s\n') % (name, desc))
147 self.ui.warn(_('warning: %s hook %s\n') % (name, desc))
146 return r
148 return r
147
149
148 r = False
150 r = False
149 hooks = [(hname, cmd) for hname, cmd in self.ui.configitems("hooks")
151 hooks = [(hname, cmd) for hname, cmd in self.ui.configitems("hooks")
150 if hname.split(".", 1)[0] == name and cmd]
152 if hname.split(".", 1)[0] == name and cmd]
151 hooks.sort()
153 hooks.sort()
152 for hname, cmd in hooks:
154 for hname, cmd in hooks:
153 if cmd.startswith('python:'):
155 if cmd.startswith('python:'):
154 r = callhook(hname, cmd[7:].strip()) or r
156 r = callhook(hname, cmd[7:].strip()) or r
155 else:
157 else:
156 r = runhook(hname, cmd) or r
158 r = runhook(hname, cmd) or r
157 return r
159 return r
158
160
159 def tags(self):
161 def tags(self):
160 '''return a mapping of tag to node'''
162 '''return a mapping of tag to node'''
161 if not self.tagscache:
163 if not self.tagscache:
162 self.tagscache = {}
164 self.tagscache = {}
163
165
164 def parsetag(line, context):
166 def parsetag(line, context):
165 if not line:
167 if not line:
166 return
168 return
167 s = l.split(" ", 1)
169 s = l.split(" ", 1)
168 if len(s) != 2:
170 if len(s) != 2:
169 self.ui.warn(_("%s: cannot parse entry\n") % context)
171 self.ui.warn(_("%s: cannot parse entry\n") % context)
170 return
172 return
171 node, key = s
173 node, key = s
172 key = key.strip()
174 key = key.strip()
173 try:
175 try:
174 bin_n = bin(node)
176 bin_n = bin(node)
175 except TypeError:
177 except TypeError:
176 self.ui.warn(_("%s: node '%s' is not well formed\n") %
178 self.ui.warn(_("%s: node '%s' is not well formed\n") %
177 (context, node))
179 (context, node))
178 return
180 return
179 if bin_n not in self.changelog.nodemap:
181 if bin_n not in self.changelog.nodemap:
180 self.ui.warn(_("%s: tag '%s' refers to unknown node\n") %
182 self.ui.warn(_("%s: tag '%s' refers to unknown node\n") %
181 (context, key))
183 (context, key))
182 return
184 return
183 self.tagscache[key] = bin_n
185 self.tagscache[key] = bin_n
184
186
185 # read the tags file from each head, ending with the tip,
187 # read the tags file from each head, ending with the tip,
186 # and add each tag found to the map, with "newer" ones
188 # and add each tag found to the map, with "newer" ones
187 # taking precedence
189 # taking precedence
188 heads = self.heads()
190 heads = self.heads()
189 heads.reverse()
191 heads.reverse()
190 fl = self.file(".hgtags")
192 fl = self.file(".hgtags")
191 for node in heads:
193 for node in heads:
192 change = self.changelog.read(node)
194 change = self.changelog.read(node)
193 rev = self.changelog.rev(node)
195 rev = self.changelog.rev(node)
194 fn, ff = self.manifest.find(change[0], '.hgtags')
196 fn, ff = self.manifest.find(change[0], '.hgtags')
195 if fn is None: continue
197 if fn is None: continue
196 count = 0
198 count = 0
197 for l in fl.read(fn).splitlines():
199 for l in fl.read(fn).splitlines():
198 count += 1
200 count += 1
199 parsetag(l, _(".hgtags (rev %d:%s), line %d") %
201 parsetag(l, _(".hgtags (rev %d:%s), line %d") %
200 (rev, short(node), count))
202 (rev, short(node), count))
201 try:
203 try:
202 f = self.opener("localtags")
204 f = self.opener("localtags")
203 count = 0
205 count = 0
204 for l in f:
206 for l in f:
205 count += 1
207 count += 1
206 parsetag(l, _("localtags, line %d") % count)
208 parsetag(l, _("localtags, line %d") % count)
207 except IOError:
209 except IOError:
208 pass
210 pass
209
211
210 self.tagscache['tip'] = self.changelog.tip()
212 self.tagscache['tip'] = self.changelog.tip()
211
213
212 return self.tagscache
214 return self.tagscache
213
215
214 def tagslist(self):
216 def tagslist(self):
215 '''return a list of tags ordered by revision'''
217 '''return a list of tags ordered by revision'''
216 l = []
218 l = []
217 for t, n in self.tags().items():
219 for t, n in self.tags().items():
218 try:
220 try:
219 r = self.changelog.rev(n)
221 r = self.changelog.rev(n)
220 except:
222 except:
221 r = -2 # sort to the beginning of the list if unknown
223 r = -2 # sort to the beginning of the list if unknown
222 l.append((r, t, n))
224 l.append((r, t, n))
223 l.sort()
225 l.sort()
224 return [(t, n) for r, t, n in l]
226 return [(t, n) for r, t, n in l]
225
227
226 def nodetags(self, node):
228 def nodetags(self, node):
227 '''return the tags associated with a node'''
229 '''return the tags associated with a node'''
228 if not self.nodetagscache:
230 if not self.nodetagscache:
229 self.nodetagscache = {}
231 self.nodetagscache = {}
230 for t, n in self.tags().items():
232 for t, n in self.tags().items():
231 self.nodetagscache.setdefault(n, []).append(t)
233 self.nodetagscache.setdefault(n, []).append(t)
232 return self.nodetagscache.get(node, [])
234 return self.nodetagscache.get(node, [])
233
235
234 def lookup(self, key):
236 def lookup(self, key):
235 try:
237 try:
236 return self.tags()[key]
238 return self.tags()[key]
237 except KeyError:
239 except KeyError:
238 try:
240 try:
239 return self.changelog.lookup(key)
241 return self.changelog.lookup(key)
240 except:
242 except:
241 raise repo.RepoError(_("unknown revision '%s'") % key)
243 raise repo.RepoError(_("unknown revision '%s'") % key)
242
244
243 def dev(self):
245 def dev(self):
244 return os.lstat(self.path).st_dev
246 return os.lstat(self.path).st_dev
245
247
246 def local(self):
248 def local(self):
247 return True
249 return True
248
250
249 def join(self, f):
251 def join(self, f):
250 return os.path.join(self.path, f)
252 return os.path.join(self.path, f)
251
253
252 def wjoin(self, f):
254 def wjoin(self, f):
253 return os.path.join(self.root, f)
255 return os.path.join(self.root, f)
254
256
255 def file(self, f):
257 def file(self, f):
256 if f[0] == '/':
258 if f[0] == '/':
257 f = f[1:]
259 f = f[1:]
258 return filelog.filelog(self.opener, f, self.revlogversion)
260 return filelog.filelog(self.opener, f, self.revlogversion)
259
261
260 def getcwd(self):
262 def getcwd(self):
261 return self.dirstate.getcwd()
263 return self.dirstate.getcwd()
262
264
263 def wfile(self, f, mode='r'):
265 def wfile(self, f, mode='r'):
264 return self.wopener(f, mode)
266 return self.wopener(f, mode)
265
267
266 def wread(self, filename):
268 def wread(self, filename):
267 if self.encodepats == None:
269 if self.encodepats == None:
268 l = []
270 l = []
269 for pat, cmd in self.ui.configitems("encode"):
271 for pat, cmd in self.ui.configitems("encode"):
270 mf = util.matcher(self.root, "", [pat], [], [])[1]
272 mf = util.matcher(self.root, "", [pat], [], [])[1]
271 l.append((mf, cmd))
273 l.append((mf, cmd))
272 self.encodepats = l
274 self.encodepats = l
273
275
274 data = self.wopener(filename, 'r').read()
276 data = self.wopener(filename, 'r').read()
275
277
276 for mf, cmd in self.encodepats:
278 for mf, cmd in self.encodepats:
277 if mf(filename):
279 if mf(filename):
278 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
280 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
279 data = util.filter(data, cmd)
281 data = util.filter(data, cmd)
280 break
282 break
281
283
282 return data
284 return data
283
285
284 def wwrite(self, filename, data, fd=None):
286 def wwrite(self, filename, data, fd=None):
285 if self.decodepats == None:
287 if self.decodepats == None:
286 l = []
288 l = []
287 for pat, cmd in self.ui.configitems("decode"):
289 for pat, cmd in self.ui.configitems("decode"):
288 mf = util.matcher(self.root, "", [pat], [], [])[1]
290 mf = util.matcher(self.root, "", [pat], [], [])[1]
289 l.append((mf, cmd))
291 l.append((mf, cmd))
290 self.decodepats = l
292 self.decodepats = l
291
293
292 for mf, cmd in self.decodepats:
294 for mf, cmd in self.decodepats:
293 if mf(filename):
295 if mf(filename):
294 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
296 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
295 data = util.filter(data, cmd)
297 data = util.filter(data, cmd)
296 break
298 break
297
299
298 if fd:
300 if fd:
299 return fd.write(data)
301 return fd.write(data)
300 return self.wopener(filename, 'w').write(data)
302 return self.wopener(filename, 'w').write(data)
301
303
302 def transaction(self):
304 def transaction(self):
303 tr = self.transhandle
305 tr = self.transhandle
304 if tr != None and tr.running():
306 if tr != None and tr.running():
305 return tr.nest()
307 return tr.nest()
306
308
307 # save dirstate for rollback
309 # save dirstate for rollback
308 try:
310 try:
309 ds = self.opener("dirstate").read()
311 ds = self.opener("dirstate").read()
310 except IOError:
312 except IOError:
311 ds = ""
313 ds = ""
312 self.opener("journal.dirstate", "w").write(ds)
314 self.opener("journal.dirstate", "w").write(ds)
313
315
314 tr = transaction.transaction(self.ui.warn, self.opener,
316 tr = transaction.transaction(self.ui.warn, self.opener,
315 self.join("journal"),
317 self.join("journal"),
316 aftertrans(self.path))
318 aftertrans(self.path))
317 self.transhandle = tr
319 self.transhandle = tr
318 return tr
320 return tr
319
321
320 def recover(self):
322 def recover(self):
321 l = self.lock()
323 l = self.lock()
322 if os.path.exists(self.join("journal")):
324 if os.path.exists(self.join("journal")):
323 self.ui.status(_("rolling back interrupted transaction\n"))
325 self.ui.status(_("rolling back interrupted transaction\n"))
324 transaction.rollback(self.opener, self.join("journal"))
326 transaction.rollback(self.opener, self.join("journal"))
325 self.reload()
327 self.reload()
326 return True
328 return True
327 else:
329 else:
328 self.ui.warn(_("no interrupted transaction available\n"))
330 self.ui.warn(_("no interrupted transaction available\n"))
329 return False
331 return False
330
332
331 def rollback(self, wlock=None):
333 def rollback(self, wlock=None):
332 if not wlock:
334 if not wlock:
333 wlock = self.wlock()
335 wlock = self.wlock()
334 l = self.lock()
336 l = self.lock()
335 if os.path.exists(self.join("undo")):
337 if os.path.exists(self.join("undo")):
336 self.ui.status(_("rolling back last transaction\n"))
338 self.ui.status(_("rolling back last transaction\n"))
337 transaction.rollback(self.opener, self.join("undo"))
339 transaction.rollback(self.opener, self.join("undo"))
338 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
340 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
339 self.reload()
341 self.reload()
340 self.wreload()
342 self.wreload()
341 else:
343 else:
342 self.ui.warn(_("no rollback information available\n"))
344 self.ui.warn(_("no rollback information available\n"))
343
345
344 def wreload(self):
346 def wreload(self):
345 self.dirstate.read()
347 self.dirstate.read()
346
348
347 def reload(self):
349 def reload(self):
348 self.changelog.load()
350 self.changelog.load()
349 self.manifest.load()
351 self.manifest.load()
350 self.tagscache = None
352 self.tagscache = None
351 self.nodetagscache = None
353 self.nodetagscache = None
352
354
353 def do_lock(self, lockname, wait, releasefn=None, acquirefn=None,
355 def do_lock(self, lockname, wait, releasefn=None, acquirefn=None,
354 desc=None):
356 desc=None):
355 try:
357 try:
356 l = lock.lock(self.join(lockname), 0, releasefn, desc=desc)
358 l = lock.lock(self.join(lockname), 0, releasefn, desc=desc)
357 except lock.LockHeld, inst:
359 except lock.LockHeld, inst:
358 if not wait:
360 if not wait:
359 raise
361 raise
360 self.ui.warn(_("waiting for lock on %s held by %s\n") %
362 self.ui.warn(_("waiting for lock on %s held by %s\n") %
361 (desc, inst.args[0]))
363 (desc, inst.args[0]))
362 # default to 600 seconds timeout
364 # default to 600 seconds timeout
363 l = lock.lock(self.join(lockname),
365 l = lock.lock(self.join(lockname),
364 int(self.ui.config("ui", "timeout") or 600),
366 int(self.ui.config("ui", "timeout") or 600),
365 releasefn, desc=desc)
367 releasefn, desc=desc)
366 if acquirefn:
368 if acquirefn:
367 acquirefn()
369 acquirefn()
368 return l
370 return l
369
371
370 def lock(self, wait=1):
372 def lock(self, wait=1):
371 return self.do_lock("lock", wait, acquirefn=self.reload,
373 return self.do_lock("lock", wait, acquirefn=self.reload,
372 desc=_('repository %s') % self.origroot)
374 desc=_('repository %s') % self.origroot)
373
375
374 def wlock(self, wait=1):
376 def wlock(self, wait=1):
375 return self.do_lock("wlock", wait, self.dirstate.write,
377 return self.do_lock("wlock", wait, self.dirstate.write,
376 self.wreload,
378 self.wreload,
377 desc=_('working directory of %s') % self.origroot)
379 desc=_('working directory of %s') % self.origroot)
378
380
379 def checkfilemerge(self, filename, text, filelog, manifest1, manifest2):
381 def checkfilemerge(self, filename, text, filelog, manifest1, manifest2):
380 "determine whether a new filenode is needed"
382 "determine whether a new filenode is needed"
381 fp1 = manifest1.get(filename, nullid)
383 fp1 = manifest1.get(filename, nullid)
382 fp2 = manifest2.get(filename, nullid)
384 fp2 = manifest2.get(filename, nullid)
383
385
384 if fp2 != nullid:
386 if fp2 != nullid:
385 # is one parent an ancestor of the other?
387 # is one parent an ancestor of the other?
386 fpa = filelog.ancestor(fp1, fp2)
388 fpa = filelog.ancestor(fp1, fp2)
387 if fpa == fp1:
389 if fpa == fp1:
388 fp1, fp2 = fp2, nullid
390 fp1, fp2 = fp2, nullid
389 elif fpa == fp2:
391 elif fpa == fp2:
390 fp2 = nullid
392 fp2 = nullid
391
393
392 # is the file unmodified from the parent? report existing entry
394 # is the file unmodified from the parent? report existing entry
393 if fp2 == nullid and text == filelog.read(fp1):
395 if fp2 == nullid and text == filelog.read(fp1):
394 return (fp1, None, None)
396 return (fp1, None, None)
395
397
396 return (None, fp1, fp2)
398 return (None, fp1, fp2)
397
399
398 def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None):
400 def rawcommit(self, files, text, user, date, p1=None, p2=None, wlock=None):
399 orig_parent = self.dirstate.parents()[0] or nullid
401 orig_parent = self.dirstate.parents()[0] or nullid
400 p1 = p1 or self.dirstate.parents()[0] or nullid
402 p1 = p1 or self.dirstate.parents()[0] or nullid
401 p2 = p2 or self.dirstate.parents()[1] or nullid
403 p2 = p2 or self.dirstate.parents()[1] or nullid
402 c1 = self.changelog.read(p1)
404 c1 = self.changelog.read(p1)
403 c2 = self.changelog.read(p2)
405 c2 = self.changelog.read(p2)
404 m1 = self.manifest.read(c1[0])
406 m1 = self.manifest.read(c1[0])
405 mf1 = self.manifest.readflags(c1[0])
407 mf1 = self.manifest.readflags(c1[0])
406 m2 = self.manifest.read(c2[0])
408 m2 = self.manifest.read(c2[0])
407 changed = []
409 changed = []
408
410
409 if orig_parent == p1:
411 if orig_parent == p1:
410 update_dirstate = 1
412 update_dirstate = 1
411 else:
413 else:
412 update_dirstate = 0
414 update_dirstate = 0
413
415
414 if not wlock:
416 if not wlock:
415 wlock = self.wlock()
417 wlock = self.wlock()
416 l = self.lock()
418 l = self.lock()
417 tr = self.transaction()
419 tr = self.transaction()
418 mm = m1.copy()
420 mm = m1.copy()
419 mfm = mf1.copy()
421 mfm = mf1.copy()
420 linkrev = self.changelog.count()
422 linkrev = self.changelog.count()
421 for f in files:
423 for f in files:
422 try:
424 try:
423 t = self.wread(f)
425 t = self.wread(f)
424 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
426 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
425 r = self.file(f)
427 r = self.file(f)
426 mfm[f] = tm
428 mfm[f] = tm
427
429
428 (entry, fp1, fp2) = self.checkfilemerge(f, t, r, m1, m2)
430 (entry, fp1, fp2) = self.checkfilemerge(f, t, r, m1, m2)
429 if entry:
431 if entry:
430 mm[f] = entry
432 mm[f] = entry
431 continue
433 continue
432
434
433 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
435 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
434 changed.append(f)
436 changed.append(f)
435 if update_dirstate:
437 if update_dirstate:
436 self.dirstate.update([f], "n")
438 self.dirstate.update([f], "n")
437 except IOError:
439 except IOError:
438 try:
440 try:
439 del mm[f]
441 del mm[f]
440 del mfm[f]
442 del mfm[f]
441 if update_dirstate:
443 if update_dirstate:
442 self.dirstate.forget([f])
444 self.dirstate.forget([f])
443 except:
445 except:
444 # deleted from p2?
446 # deleted from p2?
445 pass
447 pass
446
448
447 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
449 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
448 user = user or self.ui.username()
450 user = user or self.ui.username()
449 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
451 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
450 tr.close()
452 tr.close()
451 if update_dirstate:
453 if update_dirstate:
452 self.dirstate.setparents(n, nullid)
454 self.dirstate.setparents(n, nullid)
453
455
454 def commit(self, files=None, text="", user=None, date=None,
456 def commit(self, files=None, text="", user=None, date=None,
455 match=util.always, force=False, lock=None, wlock=None,
457 match=util.always, force=False, lock=None, wlock=None,
456 force_editor=False):
458 force_editor=False):
457 commit = []
459 commit = []
458 remove = []
460 remove = []
459 changed = []
461 changed = []
460
462
461 if files:
463 if files:
462 for f in files:
464 for f in files:
463 s = self.dirstate.state(f)
465 s = self.dirstate.state(f)
464 if s in 'nmai':
466 if s in 'nmai':
465 commit.append(f)
467 commit.append(f)
466 elif s == 'r':
468 elif s == 'r':
467 remove.append(f)
469 remove.append(f)
468 else:
470 else:
469 self.ui.warn(_("%s not tracked!\n") % f)
471 self.ui.warn(_("%s not tracked!\n") % f)
470 else:
472 else:
471 modified, added, removed, deleted, unknown = self.changes(match=match)
473 modified, added, removed, deleted, unknown = self.changes(match=match)
472 commit = modified + added
474 commit = modified + added
473 remove = removed
475 remove = removed
474
476
475 p1, p2 = self.dirstate.parents()
477 p1, p2 = self.dirstate.parents()
476 c1 = self.changelog.read(p1)
478 c1 = self.changelog.read(p1)
477 c2 = self.changelog.read(p2)
479 c2 = self.changelog.read(p2)
478 m1 = self.manifest.read(c1[0])
480 m1 = self.manifest.read(c1[0])
479 mf1 = self.manifest.readflags(c1[0])
481 mf1 = self.manifest.readflags(c1[0])
480 m2 = self.manifest.read(c2[0])
482 m2 = self.manifest.read(c2[0])
481
483
482 if not commit and not remove and not force and p2 == nullid:
484 if not commit and not remove and not force and p2 == nullid:
483 self.ui.status(_("nothing changed\n"))
485 self.ui.status(_("nothing changed\n"))
484 return None
486 return None
485
487
486 xp1 = hex(p1)
488 xp1 = hex(p1)
487 if p2 == nullid: xp2 = ''
489 if p2 == nullid: xp2 = ''
488 else: xp2 = hex(p2)
490 else: xp2 = hex(p2)
489
491
490 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
492 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
491
493
492 if not wlock:
494 if not wlock:
493 wlock = self.wlock()
495 wlock = self.wlock()
494 if not lock:
496 if not lock:
495 lock = self.lock()
497 lock = self.lock()
496 tr = self.transaction()
498 tr = self.transaction()
497
499
498 # check in files
500 # check in files
499 new = {}
501 new = {}
500 linkrev = self.changelog.count()
502 linkrev = self.changelog.count()
501 commit.sort()
503 commit.sort()
502 for f in commit:
504 for f in commit:
503 self.ui.note(f + "\n")
505 self.ui.note(f + "\n")
504 try:
506 try:
505 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
507 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
506 t = self.wread(f)
508 t = self.wread(f)
507 except IOError:
509 except IOError:
508 self.ui.warn(_("trouble committing %s!\n") % f)
510 self.ui.warn(_("trouble committing %s!\n") % f)
509 raise
511 raise
510
512
511 r = self.file(f)
513 r = self.file(f)
512
514
513 meta = {}
515 meta = {}
514 cp = self.dirstate.copied(f)
516 cp = self.dirstate.copied(f)
515 if cp:
517 if cp:
516 meta["copy"] = cp
518 meta["copy"] = cp
517 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
519 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
518 self.ui.debug(_(" %s: copy %s:%s\n") % (f, cp, meta["copyrev"]))
520 self.ui.debug(_(" %s: copy %s:%s\n") % (f, cp, meta["copyrev"]))
519 fp1, fp2 = nullid, nullid
521 fp1, fp2 = nullid, nullid
520 else:
522 else:
521 entry, fp1, fp2 = self.checkfilemerge(f, t, r, m1, m2)
523 entry, fp1, fp2 = self.checkfilemerge(f, t, r, m1, m2)
522 if entry:
524 if entry:
523 new[f] = entry
525 new[f] = entry
524 continue
526 continue
525
527
526 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
528 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
527 # remember what we've added so that we can later calculate
529 # remember what we've added so that we can later calculate
528 # the files to pull from a set of changesets
530 # the files to pull from a set of changesets
529 changed.append(f)
531 changed.append(f)
530
532
531 # update manifest
533 # update manifest
532 m1 = m1.copy()
534 m1 = m1.copy()
533 m1.update(new)
535 m1.update(new)
534 for f in remove:
536 for f in remove:
535 if f in m1:
537 if f in m1:
536 del m1[f]
538 del m1[f]
537 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
539 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
538 (new, remove))
540 (new, remove))
539
541
540 # add changeset
542 # add changeset
541 new = new.keys()
543 new = new.keys()
542 new.sort()
544 new.sort()
543
545
544 user = user or self.ui.username()
546 user = user or self.ui.username()
545 if not text or force_editor:
547 if not text or force_editor:
546 edittext = []
548 edittext = []
547 if text:
549 if text:
548 edittext.append(text)
550 edittext.append(text)
549 edittext.append("")
551 edittext.append("")
550 if p2 != nullid:
552 if p2 != nullid:
551 edittext.append("HG: branch merge")
553 edittext.append("HG: branch merge")
552 edittext.extend(["HG: changed %s" % f for f in changed])
554 edittext.extend(["HG: changed %s" % f for f in changed])
553 edittext.extend(["HG: removed %s" % f for f in remove])
555 edittext.extend(["HG: removed %s" % f for f in remove])
554 if not changed and not remove:
556 if not changed and not remove:
555 edittext.append("HG: no files changed")
557 edittext.append("HG: no files changed")
556 edittext.append("")
558 edittext.append("")
557 # run editor in the repository root
559 # run editor in the repository root
558 olddir = os.getcwd()
560 olddir = os.getcwd()
559 os.chdir(self.root)
561 os.chdir(self.root)
560 text = self.ui.edit("\n".join(edittext), user)
562 text = self.ui.edit("\n".join(edittext), user)
561 os.chdir(olddir)
563 os.chdir(olddir)
562
564
563 lines = [line.rstrip() for line in text.rstrip().splitlines()]
565 lines = [line.rstrip() for line in text.rstrip().splitlines()]
564 while lines and not lines[0]:
566 while lines and not lines[0]:
565 del lines[0]
567 del lines[0]
566 if not lines:
568 if not lines:
567 return None
569 return None
568 text = '\n'.join(lines)
570 text = '\n'.join(lines)
569 n = self.changelog.add(mn, changed + remove, text, tr, p1, p2, user, date)
571 n = self.changelog.add(mn, changed + remove, text, tr, p1, p2, user, date)
570 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
572 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
571 parent2=xp2)
573 parent2=xp2)
572 tr.close()
574 tr.close()
573
575
574 self.dirstate.setparents(n)
576 self.dirstate.setparents(n)
575 self.dirstate.update(new, "n")
577 self.dirstate.update(new, "n")
576 self.dirstate.forget(remove)
578 self.dirstate.forget(remove)
577
579
578 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
580 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
579 return n
581 return n
580
582
581 def walk(self, node=None, files=[], match=util.always, badmatch=None):
583 def walk(self, node=None, files=[], match=util.always, badmatch=None):
582 if node:
584 if node:
583 fdict = dict.fromkeys(files)
585 fdict = dict.fromkeys(files)
584 for fn in self.manifest.read(self.changelog.read(node)[0]):
586 for fn in self.manifest.read(self.changelog.read(node)[0]):
585 fdict.pop(fn, None)
587 fdict.pop(fn, None)
586 if match(fn):
588 if match(fn):
587 yield 'm', fn
589 yield 'm', fn
588 for fn in fdict:
590 for fn in fdict:
589 if badmatch and badmatch(fn):
591 if badmatch and badmatch(fn):
590 if match(fn):
592 if match(fn):
591 yield 'b', fn
593 yield 'b', fn
592 else:
594 else:
593 self.ui.warn(_('%s: No such file in rev %s\n') % (
595 self.ui.warn(_('%s: No such file in rev %s\n') % (
594 util.pathto(self.getcwd(), fn), short(node)))
596 util.pathto(self.getcwd(), fn), short(node)))
595 else:
597 else:
596 for src, fn in self.dirstate.walk(files, match, badmatch=badmatch):
598 for src, fn in self.dirstate.walk(files, match, badmatch=badmatch):
597 yield src, fn
599 yield src, fn
598
600
599 def changes(self, node1=None, node2=None, files=[], match=util.always,
601 def changes(self, node1=None, node2=None, files=[], match=util.always,
600 wlock=None, show_ignored=None):
602 wlock=None, show_ignored=None):
601 """return changes between two nodes or node and working directory
603 """return changes between two nodes or node and working directory
602
604
603 If node1 is None, use the first dirstate parent instead.
605 If node1 is None, use the first dirstate parent instead.
604 If node2 is None, compare node1 with working directory.
606 If node2 is None, compare node1 with working directory.
605 """
607 """
606
608
607 def fcmp(fn, mf):
609 def fcmp(fn, mf):
608 t1 = self.wread(fn)
610 t1 = self.wread(fn)
609 t2 = self.file(fn).read(mf.get(fn, nullid))
611 t2 = self.file(fn).read(mf.get(fn, nullid))
610 return cmp(t1, t2)
612 return cmp(t1, t2)
611
613
612 def mfmatches(node):
614 def mfmatches(node):
613 change = self.changelog.read(node)
615 change = self.changelog.read(node)
614 mf = dict(self.manifest.read(change[0]))
616 mf = dict(self.manifest.read(change[0]))
615 for fn in mf.keys():
617 for fn in mf.keys():
616 if not match(fn):
618 if not match(fn):
617 del mf[fn]
619 del mf[fn]
618 return mf
620 return mf
619
621
620 modified, added, removed, deleted, unknown, ignored = [],[],[],[],[],[]
622 modified, added, removed, deleted, unknown, ignored = [],[],[],[],[],[]
621 compareworking = False
623 compareworking = False
622 if not node1 or (not node2 and node1 == self.dirstate.parents()[0]):
624 if not node1 or (not node2 and node1 == self.dirstate.parents()[0]):
623 compareworking = True
625 compareworking = True
624
626
625 if not compareworking:
627 if not compareworking:
626 # read the manifest from node1 before the manifest from node2,
628 # read the manifest from node1 before the manifest from node2,
627 # so that we'll hit the manifest cache if we're going through
629 # so that we'll hit the manifest cache if we're going through
628 # all the revisions in parent->child order.
630 # all the revisions in parent->child order.
629 mf1 = mfmatches(node1)
631 mf1 = mfmatches(node1)
630
632
631 # are we comparing the working directory?
633 # are we comparing the working directory?
632 if not node2:
634 if not node2:
633 if not wlock:
635 if not wlock:
634 try:
636 try:
635 wlock = self.wlock(wait=0)
637 wlock = self.wlock(wait=0)
636 except lock.LockException:
638 except lock.LockException:
637 wlock = None
639 wlock = None
638 lookup, modified, added, removed, deleted, unknown, ignored = (
640 lookup, modified, added, removed, deleted, unknown, ignored = (
639 self.dirstate.changes(files, match, show_ignored))
641 self.dirstate.changes(files, match, show_ignored))
640
642
641 # are we comparing working dir against its parent?
643 # are we comparing working dir against its parent?
642 if compareworking:
644 if compareworking:
643 if lookup:
645 if lookup:
644 # do a full compare of any files that might have changed
646 # do a full compare of any files that might have changed
645 mf2 = mfmatches(self.dirstate.parents()[0])
647 mf2 = mfmatches(self.dirstate.parents()[0])
646 for f in lookup:
648 for f in lookup:
647 if fcmp(f, mf2):
649 if fcmp(f, mf2):
648 modified.append(f)
650 modified.append(f)
649 elif wlock is not None:
651 elif wlock is not None:
650 self.dirstate.update([f], "n")
652 self.dirstate.update([f], "n")
651 else:
653 else:
652 # we are comparing working dir against non-parent
654 # we are comparing working dir against non-parent
653 # generate a pseudo-manifest for the working dir
655 # generate a pseudo-manifest for the working dir
654 mf2 = mfmatches(self.dirstate.parents()[0])
656 mf2 = mfmatches(self.dirstate.parents()[0])
655 for f in lookup + modified + added:
657 for f in lookup + modified + added:
656 mf2[f] = ""
658 mf2[f] = ""
657 for f in removed:
659 for f in removed:
658 if f in mf2:
660 if f in mf2:
659 del mf2[f]
661 del mf2[f]
660 else:
662 else:
661 # we are comparing two revisions
663 # we are comparing two revisions
662 deleted, unknown, ignored = [], [], []
664 deleted, unknown, ignored = [], [], []
663 mf2 = mfmatches(node2)
665 mf2 = mfmatches(node2)
664
666
665 if not compareworking:
667 if not compareworking:
666 # flush lists from dirstate before comparing manifests
668 # flush lists from dirstate before comparing manifests
667 modified, added = [], []
669 modified, added = [], []
668
670
669 # make sure to sort the files so we talk to the disk in a
671 # make sure to sort the files so we talk to the disk in a
670 # reasonable order
672 # reasonable order
671 mf2keys = mf2.keys()
673 mf2keys = mf2.keys()
672 mf2keys.sort()
674 mf2keys.sort()
673 for fn in mf2keys:
675 for fn in mf2keys:
674 if mf1.has_key(fn):
676 if mf1.has_key(fn):
675 if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
677 if mf1[fn] != mf2[fn] and (mf2[fn] != "" or fcmp(fn, mf1)):
676 modified.append(fn)
678 modified.append(fn)
677 del mf1[fn]
679 del mf1[fn]
678 else:
680 else:
679 added.append(fn)
681 added.append(fn)
680
682
681 removed = mf1.keys()
683 removed = mf1.keys()
682
684
683 # sort and return results:
685 # sort and return results:
684 for l in modified, added, removed, deleted, unknown, ignored:
686 for l in modified, added, removed, deleted, unknown, ignored:
685 l.sort()
687 l.sort()
686 if show_ignored is None:
688 if show_ignored is None:
687 return (modified, added, removed, deleted, unknown)
689 return (modified, added, removed, deleted, unknown)
688 else:
690 else:
689 return (modified, added, removed, deleted, unknown, ignored)
691 return (modified, added, removed, deleted, unknown, ignored)
690
692
691 def add(self, list, wlock=None):
693 def add(self, list, wlock=None):
692 if not wlock:
694 if not wlock:
693 wlock = self.wlock()
695 wlock = self.wlock()
694 for f in list:
696 for f in list:
695 p = self.wjoin(f)
697 p = self.wjoin(f)
696 if not os.path.exists(p):
698 if not os.path.exists(p):
697 self.ui.warn(_("%s does not exist!\n") % f)
699 self.ui.warn(_("%s does not exist!\n") % f)
698 elif not os.path.isfile(p):
700 elif not os.path.isfile(p):
699 self.ui.warn(_("%s not added: only files supported currently\n")
701 self.ui.warn(_("%s not added: only files supported currently\n")
700 % f)
702 % f)
701 elif self.dirstate.state(f) in 'an':
703 elif self.dirstate.state(f) in 'an':
702 self.ui.warn(_("%s already tracked!\n") % f)
704 self.ui.warn(_("%s already tracked!\n") % f)
703 else:
705 else:
704 self.dirstate.update([f], "a")
706 self.dirstate.update([f], "a")
705
707
706 def forget(self, list, wlock=None):
708 def forget(self, list, wlock=None):
707 if not wlock:
709 if not wlock:
708 wlock = self.wlock()
710 wlock = self.wlock()
709 for f in list:
711 for f in list:
710 if self.dirstate.state(f) not in 'ai':
712 if self.dirstate.state(f) not in 'ai':
711 self.ui.warn(_("%s not added!\n") % f)
713 self.ui.warn(_("%s not added!\n") % f)
712 else:
714 else:
713 self.dirstate.forget([f])
715 self.dirstate.forget([f])
714
716
715 def remove(self, list, unlink=False, wlock=None):
717 def remove(self, list, unlink=False, wlock=None):
716 if unlink:
718 if unlink:
717 for f in list:
719 for f in list:
718 try:
720 try:
719 util.unlink(self.wjoin(f))
721 util.unlink(self.wjoin(f))
720 except OSError, inst:
722 except OSError, inst:
721 if inst.errno != errno.ENOENT:
723 if inst.errno != errno.ENOENT:
722 raise
724 raise
723 if not wlock:
725 if not wlock:
724 wlock = self.wlock()
726 wlock = self.wlock()
725 for f in list:
727 for f in list:
726 p = self.wjoin(f)
728 p = self.wjoin(f)
727 if os.path.exists(p):
729 if os.path.exists(p):
728 self.ui.warn(_("%s still exists!\n") % f)
730 self.ui.warn(_("%s still exists!\n") % f)
729 elif self.dirstate.state(f) == 'a':
731 elif self.dirstate.state(f) == 'a':
730 self.dirstate.forget([f])
732 self.dirstate.forget([f])
731 elif f not in self.dirstate:
733 elif f not in self.dirstate:
732 self.ui.warn(_("%s not tracked!\n") % f)
734 self.ui.warn(_("%s not tracked!\n") % f)
733 else:
735 else:
734 self.dirstate.update([f], "r")
736 self.dirstate.update([f], "r")
735
737
736 def undelete(self, list, wlock=None):
738 def undelete(self, list, wlock=None):
737 p = self.dirstate.parents()[0]
739 p = self.dirstate.parents()[0]
738 mn = self.changelog.read(p)[0]
740 mn = self.changelog.read(p)[0]
739 mf = self.manifest.readflags(mn)
741 mf = self.manifest.readflags(mn)
740 m = self.manifest.read(mn)
742 m = self.manifest.read(mn)
741 if not wlock:
743 if not wlock:
742 wlock = self.wlock()
744 wlock = self.wlock()
743 for f in list:
745 for f in list:
744 if self.dirstate.state(f) not in "r":
746 if self.dirstate.state(f) not in "r":
745 self.ui.warn("%s not removed!\n" % f)
747 self.ui.warn("%s not removed!\n" % f)
746 else:
748 else:
747 t = self.file(f).read(m[f])
749 t = self.file(f).read(m[f])
748 self.wwrite(f, t)
750 self.wwrite(f, t)
749 util.set_exec(self.wjoin(f), mf[f])
751 util.set_exec(self.wjoin(f), mf[f])
750 self.dirstate.update([f], "n")
752 self.dirstate.update([f], "n")
751
753
752 def copy(self, source, dest, wlock=None):
754 def copy(self, source, dest, wlock=None):
753 p = self.wjoin(dest)
755 p = self.wjoin(dest)
754 if not os.path.exists(p):
756 if not os.path.exists(p):
755 self.ui.warn(_("%s does not exist!\n") % dest)
757 self.ui.warn(_("%s does not exist!\n") % dest)
756 elif not os.path.isfile(p):
758 elif not os.path.isfile(p):
757 self.ui.warn(_("copy failed: %s is not a file\n") % dest)
759 self.ui.warn(_("copy failed: %s is not a file\n") % dest)
758 else:
760 else:
759 if not wlock:
761 if not wlock:
760 wlock = self.wlock()
762 wlock = self.wlock()
761 if self.dirstate.state(dest) == '?':
763 if self.dirstate.state(dest) == '?':
762 self.dirstate.update([dest], "a")
764 self.dirstate.update([dest], "a")
763 self.dirstate.copy(source, dest)
765 self.dirstate.copy(source, dest)
764
766
765 def heads(self, start=None):
767 def heads(self, start=None):
766 heads = self.changelog.heads(start)
768 heads = self.changelog.heads(start)
767 # sort the output in rev descending order
769 # sort the output in rev descending order
768 heads = [(-self.changelog.rev(h), h) for h in heads]
770 heads = [(-self.changelog.rev(h), h) for h in heads]
769 heads.sort()
771 heads.sort()
770 return [n for (r, n) in heads]
772 return [n for (r, n) in heads]
771
773
772 # branchlookup returns a dict giving a list of branches for
774 # branchlookup returns a dict giving a list of branches for
773 # each head. A branch is defined as the tag of a node or
775 # each head. A branch is defined as the tag of a node or
774 # the branch of the node's parents. If a node has multiple
776 # the branch of the node's parents. If a node has multiple
775 # branch tags, tags are eliminated if they are visible from other
777 # branch tags, tags are eliminated if they are visible from other
776 # branch tags.
778 # branch tags.
777 #
779 #
778 # So, for this graph: a->b->c->d->e
780 # So, for this graph: a->b->c->d->e
779 # \ /
781 # \ /
780 # aa -----/
782 # aa -----/
781 # a has tag 2.6.12
783 # a has tag 2.6.12
782 # d has tag 2.6.13
784 # d has tag 2.6.13
783 # e would have branch tags for 2.6.12 and 2.6.13. Because the node
785 # e would have branch tags for 2.6.12 and 2.6.13. Because the node
784 # for 2.6.12 can be reached from the node 2.6.13, that is eliminated
786 # for 2.6.12 can be reached from the node 2.6.13, that is eliminated
785 # from the list.
787 # from the list.
786 #
788 #
787 # It is possible that more than one head will have the same branch tag.
789 # It is possible that more than one head will have the same branch tag.
788 # callers need to check the result for multiple heads under the same
790 # callers need to check the result for multiple heads under the same
789 # branch tag if that is a problem for them (ie checkout of a specific
791 # branch tag if that is a problem for them (ie checkout of a specific
790 # branch).
792 # branch).
791 #
793 #
792 # passing in a specific branch will limit the depth of the search
794 # passing in a specific branch will limit the depth of the search
793 # through the parents. It won't limit the branches returned in the
795 # through the parents. It won't limit the branches returned in the
794 # result though.
796 # result though.
795 def branchlookup(self, heads=None, branch=None):
797 def branchlookup(self, heads=None, branch=None):
796 if not heads:
798 if not heads:
797 heads = self.heads()
799 heads = self.heads()
798 headt = [ h for h in heads ]
800 headt = [ h for h in heads ]
799 chlog = self.changelog
801 chlog = self.changelog
800 branches = {}
802 branches = {}
801 merges = []
803 merges = []
802 seenmerge = {}
804 seenmerge = {}
803
805
804 # traverse the tree once for each head, recording in the branches
806 # traverse the tree once for each head, recording in the branches
805 # dict which tags are visible from this head. The branches
807 # dict which tags are visible from this head. The branches
806 # dict also records which tags are visible from each tag
808 # dict also records which tags are visible from each tag
807 # while we traverse.
809 # while we traverse.
808 while headt or merges:
810 while headt or merges:
809 if merges:
811 if merges:
810 n, found = merges.pop()
812 n, found = merges.pop()
811 visit = [n]
813 visit = [n]
812 else:
814 else:
813 h = headt.pop()
815 h = headt.pop()
814 visit = [h]
816 visit = [h]
815 found = [h]
817 found = [h]
816 seen = {}
818 seen = {}
817 while visit:
819 while visit:
818 n = visit.pop()
820 n = visit.pop()
819 if n in seen:
821 if n in seen:
820 continue
822 continue
821 pp = chlog.parents(n)
823 pp = chlog.parents(n)
822 tags = self.nodetags(n)
824 tags = self.nodetags(n)
823 if tags:
825 if tags:
824 for x in tags:
826 for x in tags:
825 if x == 'tip':
827 if x == 'tip':
826 continue
828 continue
827 for f in found:
829 for f in found:
828 branches.setdefault(f, {})[n] = 1
830 branches.setdefault(f, {})[n] = 1
829 branches.setdefault(n, {})[n] = 1
831 branches.setdefault(n, {})[n] = 1
830 break
832 break
831 if n not in found:
833 if n not in found:
832 found.append(n)
834 found.append(n)
833 if branch in tags:
835 if branch in tags:
834 continue
836 continue
835 seen[n] = 1
837 seen[n] = 1
836 if pp[1] != nullid and n not in seenmerge:
838 if pp[1] != nullid and n not in seenmerge:
837 merges.append((pp[1], [x for x in found]))
839 merges.append((pp[1], [x for x in found]))
838 seenmerge[n] = 1
840 seenmerge[n] = 1
839 if pp[0] != nullid:
841 if pp[0] != nullid:
840 visit.append(pp[0])
842 visit.append(pp[0])
841 # traverse the branches dict, eliminating branch tags from each
843 # traverse the branches dict, eliminating branch tags from each
842 # head that are visible from another branch tag for that head.
844 # head that are visible from another branch tag for that head.
843 out = {}
845 out = {}
844 viscache = {}
846 viscache = {}
845 for h in heads:
847 for h in heads:
846 def visible(node):
848 def visible(node):
847 if node in viscache:
849 if node in viscache:
848 return viscache[node]
850 return viscache[node]
849 ret = {}
851 ret = {}
850 visit = [node]
852 visit = [node]
851 while visit:
853 while visit:
852 x = visit.pop()
854 x = visit.pop()
853 if x in viscache:
855 if x in viscache:
854 ret.update(viscache[x])
856 ret.update(viscache[x])
855 elif x not in ret:
857 elif x not in ret:
856 ret[x] = 1
858 ret[x] = 1
857 if x in branches:
859 if x in branches:
858 visit[len(visit):] = branches[x].keys()
860 visit[len(visit):] = branches[x].keys()
859 viscache[node] = ret
861 viscache[node] = ret
860 return ret
862 return ret
861 if h not in branches:
863 if h not in branches:
862 continue
864 continue
863 # O(n^2), but somewhat limited. This only searches the
865 # O(n^2), but somewhat limited. This only searches the
864 # tags visible from a specific head, not all the tags in the
866 # tags visible from a specific head, not all the tags in the
865 # whole repo.
867 # whole repo.
866 for b in branches[h]:
868 for b in branches[h]:
867 vis = False
869 vis = False
868 for bb in branches[h].keys():
870 for bb in branches[h].keys():
869 if b != bb:
871 if b != bb:
870 if b in visible(bb):
872 if b in visible(bb):
871 vis = True
873 vis = True
872 break
874 break
873 if not vis:
875 if not vis:
874 l = out.setdefault(h, [])
876 l = out.setdefault(h, [])
875 l[len(l):] = self.nodetags(b)
877 l[len(l):] = self.nodetags(b)
876 return out
878 return out
877
879
878 def branches(self, nodes):
880 def branches(self, nodes):
879 if not nodes:
881 if not nodes:
880 nodes = [self.changelog.tip()]
882 nodes = [self.changelog.tip()]
881 b = []
883 b = []
882 for n in nodes:
884 for n in nodes:
883 t = n
885 t = n
884 while 1:
886 while 1:
885 p = self.changelog.parents(n)
887 p = self.changelog.parents(n)
886 if p[1] != nullid or p[0] == nullid:
888 if p[1] != nullid or p[0] == nullid:
887 b.append((t, n, p[0], p[1]))
889 b.append((t, n, p[0], p[1]))
888 break
890 break
889 n = p[0]
891 n = p[0]
890 return b
892 return b
891
893
892 def between(self, pairs):
894 def between(self, pairs):
893 r = []
895 r = []
894
896
895 for top, bottom in pairs:
897 for top, bottom in pairs:
896 n, l, i = top, [], 0
898 n, l, i = top, [], 0
897 f = 1
899 f = 1
898
900
899 while n != bottom:
901 while n != bottom:
900 p = self.changelog.parents(n)[0]
902 p = self.changelog.parents(n)[0]
901 if i == f:
903 if i == f:
902 l.append(n)
904 l.append(n)
903 f = f * 2
905 f = f * 2
904 n = p
906 n = p
905 i += 1
907 i += 1
906
908
907 r.append(l)
909 r.append(l)
908
910
909 return r
911 return r
910
912
911 def findincoming(self, remote, base=None, heads=None, force=False):
913 def findincoming(self, remote, base=None, heads=None, force=False):
912 """Return list of roots of the subsets of missing nodes from remote
914 """Return list of roots of the subsets of missing nodes from remote
913
915
914 If base dict is specified, assume that these nodes and their parents
916 If base dict is specified, assume that these nodes and their parents
915 exist on the remote side and that no child of a node of base exists
917 exist on the remote side and that no child of a node of base exists
916 in both remote and self.
918 in both remote and self.
917 Furthermore base will be updated to include the nodes that exists
919 Furthermore base will be updated to include the nodes that exists
918 in self and remote but no children exists in self and remote.
920 in self and remote but no children exists in self and remote.
919 If a list of heads is specified, return only nodes which are heads
921 If a list of heads is specified, return only nodes which are heads
920 or ancestors of these heads.
922 or ancestors of these heads.
921
923
922 All the ancestors of base are in self and in remote.
924 All the ancestors of base are in self and in remote.
923 All the descendants of the list returned are missing in self.
925 All the descendants of the list returned are missing in self.
924 (and so we know that the rest of the nodes are missing in remote, see
926 (and so we know that the rest of the nodes are missing in remote, see
925 outgoing)
927 outgoing)
926 """
928 """
927 m = self.changelog.nodemap
929 m = self.changelog.nodemap
928 search = []
930 search = []
929 fetch = {}
931 fetch = {}
930 seen = {}
932 seen = {}
931 seenbranch = {}
933 seenbranch = {}
932 if base == None:
934 if base == None:
933 base = {}
935 base = {}
934
936
935 if not heads:
937 if not heads:
936 heads = remote.heads()
938 heads = remote.heads()
937
939
938 if self.changelog.tip() == nullid:
940 if self.changelog.tip() == nullid:
939 base[nullid] = 1
941 base[nullid] = 1
940 if heads != [nullid]:
942 if heads != [nullid]:
941 return [nullid]
943 return [nullid]
942 return []
944 return []
943
945
944 # assume we're closer to the tip than the root
946 # assume we're closer to the tip than the root
945 # and start by examining the heads
947 # and start by examining the heads
946 self.ui.status(_("searching for changes\n"))
948 self.ui.status(_("searching for changes\n"))
947
949
948 unknown = []
950 unknown = []
949 for h in heads:
951 for h in heads:
950 if h not in m:
952 if h not in m:
951 unknown.append(h)
953 unknown.append(h)
952 else:
954 else:
953 base[h] = 1
955 base[h] = 1
954
956
955 if not unknown:
957 if not unknown:
956 return []
958 return []
957
959
958 req = dict.fromkeys(unknown)
960 req = dict.fromkeys(unknown)
959 reqcnt = 0
961 reqcnt = 0
960
962
961 # search through remote branches
963 # search through remote branches
962 # a 'branch' here is a linear segment of history, with four parts:
964 # a 'branch' here is a linear segment of history, with four parts:
963 # head, root, first parent, second parent
965 # head, root, first parent, second parent
964 # (a branch always has two parents (or none) by definition)
966 # (a branch always has two parents (or none) by definition)
965 unknown = remote.branches(unknown)
967 unknown = remote.branches(unknown)
966 while unknown:
968 while unknown:
967 r = []
969 r = []
968 while unknown:
970 while unknown:
969 n = unknown.pop(0)
971 n = unknown.pop(0)
970 if n[0] in seen:
972 if n[0] in seen:
971 continue
973 continue
972
974
973 self.ui.debug(_("examining %s:%s\n")
975 self.ui.debug(_("examining %s:%s\n")
974 % (short(n[0]), short(n[1])))
976 % (short(n[0]), short(n[1])))
975 if n[0] == nullid: # found the end of the branch
977 if n[0] == nullid: # found the end of the branch
976 pass
978 pass
977 elif n in seenbranch:
979 elif n in seenbranch:
978 self.ui.debug(_("branch already found\n"))
980 self.ui.debug(_("branch already found\n"))
979 continue
981 continue
980 elif n[1] and n[1] in m: # do we know the base?
982 elif n[1] and n[1] in m: # do we know the base?
981 self.ui.debug(_("found incomplete branch %s:%s\n")
983 self.ui.debug(_("found incomplete branch %s:%s\n")
982 % (short(n[0]), short(n[1])))
984 % (short(n[0]), short(n[1])))
983 search.append(n) # schedule branch range for scanning
985 search.append(n) # schedule branch range for scanning
984 seenbranch[n] = 1
986 seenbranch[n] = 1
985 else:
987 else:
986 if n[1] not in seen and n[1] not in fetch:
988 if n[1] not in seen and n[1] not in fetch:
987 if n[2] in m and n[3] in m:
989 if n[2] in m and n[3] in m:
988 self.ui.debug(_("found new changeset %s\n") %
990 self.ui.debug(_("found new changeset %s\n") %
989 short(n[1]))
991 short(n[1]))
990 fetch[n[1]] = 1 # earliest unknown
992 fetch[n[1]] = 1 # earliest unknown
991 for p in n[2:4]:
993 for p in n[2:4]:
992 if p in m:
994 if p in m:
993 base[p] = 1 # latest known
995 base[p] = 1 # latest known
994
996
995 for p in n[2:4]:
997 for p in n[2:4]:
996 if p not in req and p not in m:
998 if p not in req and p not in m:
997 r.append(p)
999 r.append(p)
998 req[p] = 1
1000 req[p] = 1
999 seen[n[0]] = 1
1001 seen[n[0]] = 1
1000
1002
1001 if r:
1003 if r:
1002 reqcnt += 1
1004 reqcnt += 1
1003 self.ui.debug(_("request %d: %s\n") %
1005 self.ui.debug(_("request %d: %s\n") %
1004 (reqcnt, " ".join(map(short, r))))
1006 (reqcnt, " ".join(map(short, r))))
1005 for p in range(0, len(r), 10):
1007 for p in range(0, len(r), 10):
1006 for b in remote.branches(r[p:p+10]):
1008 for b in remote.branches(r[p:p+10]):
1007 self.ui.debug(_("received %s:%s\n") %
1009 self.ui.debug(_("received %s:%s\n") %
1008 (short(b[0]), short(b[1])))
1010 (short(b[0]), short(b[1])))
1009 unknown.append(b)
1011 unknown.append(b)
1010
1012
1011 # do binary search on the branches we found
1013 # do binary search on the branches we found
1012 while search:
1014 while search:
1013 n = search.pop(0)
1015 n = search.pop(0)
1014 reqcnt += 1
1016 reqcnt += 1
1015 l = remote.between([(n[0], n[1])])[0]
1017 l = remote.between([(n[0], n[1])])[0]
1016 l.append(n[1])
1018 l.append(n[1])
1017 p = n[0]
1019 p = n[0]
1018 f = 1
1020 f = 1
1019 for i in l:
1021 for i in l:
1020 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
1022 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
1021 if i in m:
1023 if i in m:
1022 if f <= 2:
1024 if f <= 2:
1023 self.ui.debug(_("found new branch changeset %s\n") %
1025 self.ui.debug(_("found new branch changeset %s\n") %
1024 short(p))
1026 short(p))
1025 fetch[p] = 1
1027 fetch[p] = 1
1026 base[i] = 1
1028 base[i] = 1
1027 else:
1029 else:
1028 self.ui.debug(_("narrowed branch search to %s:%s\n")
1030 self.ui.debug(_("narrowed branch search to %s:%s\n")
1029 % (short(p), short(i)))
1031 % (short(p), short(i)))
1030 search.append((p, i))
1032 search.append((p, i))
1031 break
1033 break
1032 p, f = i, f * 2
1034 p, f = i, f * 2
1033
1035
1034 # sanity check our fetch list
1036 # sanity check our fetch list
1035 for f in fetch.keys():
1037 for f in fetch.keys():
1036 if f in m:
1038 if f in m:
1037 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
1039 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
1038
1040
1039 if base.keys() == [nullid]:
1041 if base.keys() == [nullid]:
1040 if force:
1042 if force:
1041 self.ui.warn(_("warning: repository is unrelated\n"))
1043 self.ui.warn(_("warning: repository is unrelated\n"))
1042 else:
1044 else:
1043 raise util.Abort(_("repository is unrelated"))
1045 raise util.Abort(_("repository is unrelated"))
1044
1046
1045 self.ui.note(_("found new changesets starting at ") +
1047 self.ui.note(_("found new changesets starting at ") +
1046 " ".join([short(f) for f in fetch]) + "\n")
1048 " ".join([short(f) for f in fetch]) + "\n")
1047
1049
1048 self.ui.debug(_("%d total queries\n") % reqcnt)
1050 self.ui.debug(_("%d total queries\n") % reqcnt)
1049
1051
1050 return fetch.keys()
1052 return fetch.keys()
1051
1053
1052 def findoutgoing(self, remote, base=None, heads=None, force=False):
1054 def findoutgoing(self, remote, base=None, heads=None, force=False):
1053 """Return list of nodes that are roots of subsets not in remote
1055 """Return list of nodes that are roots of subsets not in remote
1054
1056
1055 If base dict is specified, assume that these nodes and their parents
1057 If base dict is specified, assume that these nodes and their parents
1056 exist on the remote side.
1058 exist on the remote side.
1057 If a list of heads is specified, return only nodes which are heads
1059 If a list of heads is specified, return only nodes which are heads
1058 or ancestors of these heads, and return a second element which
1060 or ancestors of these heads, and return a second element which
1059 contains all remote heads which get new children.
1061 contains all remote heads which get new children.
1060 """
1062 """
1061 if base == None:
1063 if base == None:
1062 base = {}
1064 base = {}
1063 self.findincoming(remote, base, heads, force=force)
1065 self.findincoming(remote, base, heads, force=force)
1064
1066
1065 self.ui.debug(_("common changesets up to ")
1067 self.ui.debug(_("common changesets up to ")
1066 + " ".join(map(short, base.keys())) + "\n")
1068 + " ".join(map(short, base.keys())) + "\n")
1067
1069
1068 remain = dict.fromkeys(self.changelog.nodemap)
1070 remain = dict.fromkeys(self.changelog.nodemap)
1069
1071
1070 # prune everything remote has from the tree
1072 # prune everything remote has from the tree
1071 del remain[nullid]
1073 del remain[nullid]
1072 remove = base.keys()
1074 remove = base.keys()
1073 while remove:
1075 while remove:
1074 n = remove.pop(0)
1076 n = remove.pop(0)
1075 if n in remain:
1077 if n in remain:
1076 del remain[n]
1078 del remain[n]
1077 for p in self.changelog.parents(n):
1079 for p in self.changelog.parents(n):
1078 remove.append(p)
1080 remove.append(p)
1079
1081
1080 # find every node whose parents have been pruned
1082 # find every node whose parents have been pruned
1081 subset = []
1083 subset = []
1082 # find every remote head that will get new children
1084 # find every remote head that will get new children
1083 updated_heads = {}
1085 updated_heads = {}
1084 for n in remain:
1086 for n in remain:
1085 p1, p2 = self.changelog.parents(n)
1087 p1, p2 = self.changelog.parents(n)
1086 if p1 not in remain and p2 not in remain:
1088 if p1 not in remain and p2 not in remain:
1087 subset.append(n)
1089 subset.append(n)
1088 if heads:
1090 if heads:
1089 if p1 in heads:
1091 if p1 in heads:
1090 updated_heads[p1] = True
1092 updated_heads[p1] = True
1091 if p2 in heads:
1093 if p2 in heads:
1092 updated_heads[p2] = True
1094 updated_heads[p2] = True
1093
1095
1094 # this is the set of all roots we have to push
1096 # this is the set of all roots we have to push
1095 if heads:
1097 if heads:
1096 return subset, updated_heads.keys()
1098 return subset, updated_heads.keys()
1097 else:
1099 else:
1098 return subset
1100 return subset
1099
1101
1100 def pull(self, remote, heads=None, force=False):
1102 def pull(self, remote, heads=None, force=False):
1101 l = self.lock()
1103 l = self.lock()
1102
1104
1103 fetch = self.findincoming(remote, force=force)
1105 fetch = self.findincoming(remote, force=force)
1104 if fetch == [nullid]:
1106 if fetch == [nullid]:
1105 self.ui.status(_("requesting all changes\n"))
1107 self.ui.status(_("requesting all changes\n"))
1106
1108
1107 if not fetch:
1109 if not fetch:
1108 self.ui.status(_("no changes found\n"))
1110 self.ui.status(_("no changes found\n"))
1109 return 0
1111 return 0
1110
1112
1111 if heads is None:
1113 if heads is None:
1112 cg = remote.changegroup(fetch, 'pull')
1114 cg = remote.changegroup(fetch, 'pull')
1113 else:
1115 else:
1114 cg = remote.changegroupsubset(fetch, heads, 'pull')
1116 cg = remote.changegroupsubset(fetch, heads, 'pull')
1115 return self.addchangegroup(cg, 'pull')
1117 return self.addchangegroup(cg, 'pull')
1116
1118
1117 def push(self, remote, force=False, revs=None):
1119 def push(self, remote, force=False, revs=None):
1118 # there are two ways to push to remote repo:
1120 # there are two ways to push to remote repo:
1119 #
1121 #
1120 # addchangegroup assumes local user can lock remote
1122 # addchangegroup assumes local user can lock remote
1121 # repo (local filesystem, old ssh servers).
1123 # repo (local filesystem, old ssh servers).
1122 #
1124 #
1123 # unbundle assumes local user cannot lock remote repo (new ssh
1125 # unbundle assumes local user cannot lock remote repo (new ssh
1124 # servers, http servers).
1126 # servers, http servers).
1125
1127
1126 if 'unbundle' in remote.capabilities:
1128 if 'unbundle' in remote.capabilities:
1127 return self.push_unbundle(remote, force, revs)
1129 return self.push_unbundle(remote, force, revs)
1128 return self.push_addchangegroup(remote, force, revs)
1130 return self.push_addchangegroup(remote, force, revs)
1129
1131
1130 def prepush(self, remote, force, revs):
1132 def prepush(self, remote, force, revs):
1131 base = {}
1133 base = {}
1132 remote_heads = remote.heads()
1134 remote_heads = remote.heads()
1133 inc = self.findincoming(remote, base, remote_heads, force=force)
1135 inc = self.findincoming(remote, base, remote_heads, force=force)
1134 if not force and inc:
1136 if not force and inc:
1135 self.ui.warn(_("abort: unsynced remote changes!\n"))
1137 self.ui.warn(_("abort: unsynced remote changes!\n"))
1136 self.ui.status(_("(did you forget to sync?"
1138 self.ui.status(_("(did you forget to sync?"
1137 " use push -f to force)\n"))
1139 " use push -f to force)\n"))
1138 return None, 1
1140 return None, 1
1139
1141
1140 update, updated_heads = self.findoutgoing(remote, base, remote_heads)
1142 update, updated_heads = self.findoutgoing(remote, base, remote_heads)
1141 if revs is not None:
1143 if revs is not None:
1142 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
1144 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
1143 else:
1145 else:
1144 bases, heads = update, self.changelog.heads()
1146 bases, heads = update, self.changelog.heads()
1145
1147
1146 if not bases:
1148 if not bases:
1147 self.ui.status(_("no changes found\n"))
1149 self.ui.status(_("no changes found\n"))
1148 return None, 1
1150 return None, 1
1149 elif not force:
1151 elif not force:
1150 # FIXME we don't properly detect creation of new heads
1152 # FIXME we don't properly detect creation of new heads
1151 # in the push -r case, assume the user knows what he's doing
1153 # in the push -r case, assume the user knows what he's doing
1152 if not revs and len(remote_heads) < len(heads) \
1154 if not revs and len(remote_heads) < len(heads) \
1153 and remote_heads != [nullid]:
1155 and remote_heads != [nullid]:
1154 self.ui.warn(_("abort: push creates new remote branches!\n"))
1156 self.ui.warn(_("abort: push creates new remote branches!\n"))
1155 self.ui.status(_("(did you forget to merge?"
1157 self.ui.status(_("(did you forget to merge?"
1156 " use push -f to force)\n"))
1158 " use push -f to force)\n"))
1157 return None, 1
1159 return None, 1
1158
1160
1159 if revs is None:
1161 if revs is None:
1160 cg = self.changegroup(update, 'push')
1162 cg = self.changegroup(update, 'push')
1161 else:
1163 else:
1162 cg = self.changegroupsubset(update, revs, 'push')
1164 cg = self.changegroupsubset(update, revs, 'push')
1163 return cg, remote_heads
1165 return cg, remote_heads
1164
1166
1165 def push_addchangegroup(self, remote, force, revs):
1167 def push_addchangegroup(self, remote, force, revs):
1166 lock = remote.lock()
1168 lock = remote.lock()
1167
1169
1168 ret = self.prepush(remote, force, revs)
1170 ret = self.prepush(remote, force, revs)
1169 if ret[0] is not None:
1171 if ret[0] is not None:
1170 cg, remote_heads = ret
1172 cg, remote_heads = ret
1171 return remote.addchangegroup(cg, 'push')
1173 return remote.addchangegroup(cg, 'push')
1172 return ret[1]
1174 return ret[1]
1173
1175
1174 def push_unbundle(self, remote, force, revs):
1176 def push_unbundle(self, remote, force, revs):
1175 # local repo finds heads on server, finds out what revs it
1177 # local repo finds heads on server, finds out what revs it
1176 # must push. once revs transferred, if server finds it has
1178 # must push. once revs transferred, if server finds it has
1177 # different heads (someone else won commit/push race), server
1179 # different heads (someone else won commit/push race), server
1178 # aborts.
1180 # aborts.
1179
1181
1180 ret = self.prepush(remote, force, revs)
1182 ret = self.prepush(remote, force, revs)
1181 if ret[0] is not None:
1183 if ret[0] is not None:
1182 cg, remote_heads = ret
1184 cg, remote_heads = ret
1183 if force: remote_heads = ['force']
1185 if force: remote_heads = ['force']
1184 return remote.unbundle(cg, remote_heads, 'push')
1186 return remote.unbundle(cg, remote_heads, 'push')
1185 return ret[1]
1187 return ret[1]
1186
1188
1187 def changegroupsubset(self, bases, heads, source):
1189 def changegroupsubset(self, bases, heads, source):
1188 """This function generates a changegroup consisting of all the nodes
1190 """This function generates a changegroup consisting of all the nodes
1189 that are descendents of any of the bases, and ancestors of any of
1191 that are descendents of any of the bases, and ancestors of any of
1190 the heads.
1192 the heads.
1191
1193
1192 It is fairly complex as determining which filenodes and which
1194 It is fairly complex as determining which filenodes and which
1193 manifest nodes need to be included for the changeset to be complete
1195 manifest nodes need to be included for the changeset to be complete
1194 is non-trivial.
1196 is non-trivial.
1195
1197
1196 Another wrinkle is doing the reverse, figuring out which changeset in
1198 Another wrinkle is doing the reverse, figuring out which changeset in
1197 the changegroup a particular filenode or manifestnode belongs to."""
1199 the changegroup a particular filenode or manifestnode belongs to."""
1198
1200
1199 self.hook('preoutgoing', throw=True, source=source)
1201 self.hook('preoutgoing', throw=True, source=source)
1200
1202
1201 # Set up some initial variables
1203 # Set up some initial variables
1202 # Make it easy to refer to self.changelog
1204 # Make it easy to refer to self.changelog
1203 cl = self.changelog
1205 cl = self.changelog
1204 # msng is short for missing - compute the list of changesets in this
1206 # msng is short for missing - compute the list of changesets in this
1205 # changegroup.
1207 # changegroup.
1206 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1208 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1207 # Some bases may turn out to be superfluous, and some heads may be
1209 # Some bases may turn out to be superfluous, and some heads may be
1208 # too. nodesbetween will return the minimal set of bases and heads
1210 # too. nodesbetween will return the minimal set of bases and heads
1209 # necessary to re-create the changegroup.
1211 # necessary to re-create the changegroup.
1210
1212
1211 # Known heads are the list of heads that it is assumed the recipient
1213 # Known heads are the list of heads that it is assumed the recipient
1212 # of this changegroup will know about.
1214 # of this changegroup will know about.
1213 knownheads = {}
1215 knownheads = {}
1214 # We assume that all parents of bases are known heads.
1216 # We assume that all parents of bases are known heads.
1215 for n in bases:
1217 for n in bases:
1216 for p in cl.parents(n):
1218 for p in cl.parents(n):
1217 if p != nullid:
1219 if p != nullid:
1218 knownheads[p] = 1
1220 knownheads[p] = 1
1219 knownheads = knownheads.keys()
1221 knownheads = knownheads.keys()
1220 if knownheads:
1222 if knownheads:
1221 # Now that we know what heads are known, we can compute which
1223 # Now that we know what heads are known, we can compute which
1222 # changesets are known. The recipient must know about all
1224 # changesets are known. The recipient must know about all
1223 # changesets required to reach the known heads from the null
1225 # changesets required to reach the known heads from the null
1224 # changeset.
1226 # changeset.
1225 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1227 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1226 junk = None
1228 junk = None
1227 # Transform the list into an ersatz set.
1229 # Transform the list into an ersatz set.
1228 has_cl_set = dict.fromkeys(has_cl_set)
1230 has_cl_set = dict.fromkeys(has_cl_set)
1229 else:
1231 else:
1230 # If there were no known heads, the recipient cannot be assumed to
1232 # If there were no known heads, the recipient cannot be assumed to
1231 # know about any changesets.
1233 # know about any changesets.
1232 has_cl_set = {}
1234 has_cl_set = {}
1233
1235
1234 # Make it easy to refer to self.manifest
1236 # Make it easy to refer to self.manifest
1235 mnfst = self.manifest
1237 mnfst = self.manifest
1236 # We don't know which manifests are missing yet
1238 # We don't know which manifests are missing yet
1237 msng_mnfst_set = {}
1239 msng_mnfst_set = {}
1238 # Nor do we know which filenodes are missing.
1240 # Nor do we know which filenodes are missing.
1239 msng_filenode_set = {}
1241 msng_filenode_set = {}
1240
1242
1241 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1243 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1242 junk = None
1244 junk = None
1243
1245
1244 # A changeset always belongs to itself, so the changenode lookup
1246 # A changeset always belongs to itself, so the changenode lookup
1245 # function for a changenode is identity.
1247 # function for a changenode is identity.
1246 def identity(x):
1248 def identity(x):
1247 return x
1249 return x
1248
1250
1249 # A function generating function. Sets up an environment for the
1251 # A function generating function. Sets up an environment for the
1250 # inner function.
1252 # inner function.
1251 def cmp_by_rev_func(revlog):
1253 def cmp_by_rev_func(revlog):
1252 # Compare two nodes by their revision number in the environment's
1254 # Compare two nodes by their revision number in the environment's
1253 # revision history. Since the revision number both represents the
1255 # revision history. Since the revision number both represents the
1254 # most efficient order to read the nodes in, and represents a
1256 # most efficient order to read the nodes in, and represents a
1255 # topological sorting of the nodes, this function is often useful.
1257 # topological sorting of the nodes, this function is often useful.
1256 def cmp_by_rev(a, b):
1258 def cmp_by_rev(a, b):
1257 return cmp(revlog.rev(a), revlog.rev(b))
1259 return cmp(revlog.rev(a), revlog.rev(b))
1258 return cmp_by_rev
1260 return cmp_by_rev
1259
1261
1260 # If we determine that a particular file or manifest node must be a
1262 # If we determine that a particular file or manifest node must be a
1261 # node that the recipient of the changegroup will already have, we can
1263 # node that the recipient of the changegroup will already have, we can
1262 # also assume the recipient will have all the parents. This function
1264 # also assume the recipient will have all the parents. This function
1263 # prunes them from the set of missing nodes.
1265 # prunes them from the set of missing nodes.
1264 def prune_parents(revlog, hasset, msngset):
1266 def prune_parents(revlog, hasset, msngset):
1265 haslst = hasset.keys()
1267 haslst = hasset.keys()
1266 haslst.sort(cmp_by_rev_func(revlog))
1268 haslst.sort(cmp_by_rev_func(revlog))
1267 for node in haslst:
1269 for node in haslst:
1268 parentlst = [p for p in revlog.parents(node) if p != nullid]
1270 parentlst = [p for p in revlog.parents(node) if p != nullid]
1269 while parentlst:
1271 while parentlst:
1270 n = parentlst.pop()
1272 n = parentlst.pop()
1271 if n not in hasset:
1273 if n not in hasset:
1272 hasset[n] = 1
1274 hasset[n] = 1
1273 p = [p for p in revlog.parents(n) if p != nullid]
1275 p = [p for p in revlog.parents(n) if p != nullid]
1274 parentlst.extend(p)
1276 parentlst.extend(p)
1275 for n in hasset:
1277 for n in hasset:
1276 msngset.pop(n, None)
1278 msngset.pop(n, None)
1277
1279
1278 # This is a function generating function used to set up an environment
1280 # This is a function generating function used to set up an environment
1279 # for the inner function to execute in.
1281 # for the inner function to execute in.
1280 def manifest_and_file_collector(changedfileset):
1282 def manifest_and_file_collector(changedfileset):
1281 # This is an information gathering function that gathers
1283 # This is an information gathering function that gathers
1282 # information from each changeset node that goes out as part of
1284 # information from each changeset node that goes out as part of
1283 # the changegroup. The information gathered is a list of which
1285 # the changegroup. The information gathered is a list of which
1284 # manifest nodes are potentially required (the recipient may
1286 # manifest nodes are potentially required (the recipient may
1285 # already have them) and total list of all files which were
1287 # already have them) and total list of all files which were
1286 # changed in any changeset in the changegroup.
1288 # changed in any changeset in the changegroup.
1287 #
1289 #
1288 # We also remember the first changenode we saw any manifest
1290 # We also remember the first changenode we saw any manifest
1289 # referenced by so we can later determine which changenode 'owns'
1291 # referenced by so we can later determine which changenode 'owns'
1290 # the manifest.
1292 # the manifest.
1291 def collect_manifests_and_files(clnode):
1293 def collect_manifests_and_files(clnode):
1292 c = cl.read(clnode)
1294 c = cl.read(clnode)
1293 for f in c[3]:
1295 for f in c[3]:
1294 # This is to make sure we only have one instance of each
1296 # This is to make sure we only have one instance of each
1295 # filename string for each filename.
1297 # filename string for each filename.
1296 changedfileset.setdefault(f, f)
1298 changedfileset.setdefault(f, f)
1297 msng_mnfst_set.setdefault(c[0], clnode)
1299 msng_mnfst_set.setdefault(c[0], clnode)
1298 return collect_manifests_and_files
1300 return collect_manifests_and_files
1299
1301
1300 # Figure out which manifest nodes (of the ones we think might be part
1302 # Figure out which manifest nodes (of the ones we think might be part
1301 # of the changegroup) the recipient must know about and remove them
1303 # of the changegroup) the recipient must know about and remove them
1302 # from the changegroup.
1304 # from the changegroup.
1303 def prune_manifests():
1305 def prune_manifests():
1304 has_mnfst_set = {}
1306 has_mnfst_set = {}
1305 for n in msng_mnfst_set:
1307 for n in msng_mnfst_set:
1306 # If a 'missing' manifest thinks it belongs to a changenode
1308 # If a 'missing' manifest thinks it belongs to a changenode
1307 # the recipient is assumed to have, obviously the recipient
1309 # the recipient is assumed to have, obviously the recipient
1308 # must have that manifest.
1310 # must have that manifest.
1309 linknode = cl.node(mnfst.linkrev(n))
1311 linknode = cl.node(mnfst.linkrev(n))
1310 if linknode in has_cl_set:
1312 if linknode in has_cl_set:
1311 has_mnfst_set[n] = 1
1313 has_mnfst_set[n] = 1
1312 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1314 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1313
1315
1314 # Use the information collected in collect_manifests_and_files to say
1316 # Use the information collected in collect_manifests_and_files to say
1315 # which changenode any manifestnode belongs to.
1317 # which changenode any manifestnode belongs to.
1316 def lookup_manifest_link(mnfstnode):
1318 def lookup_manifest_link(mnfstnode):
1317 return msng_mnfst_set[mnfstnode]
1319 return msng_mnfst_set[mnfstnode]
1318
1320
1319 # A function generating function that sets up the initial environment
1321 # A function generating function that sets up the initial environment
1320 # the inner function.
1322 # the inner function.
1321 def filenode_collector(changedfiles):
1323 def filenode_collector(changedfiles):
1322 next_rev = [0]
1324 next_rev = [0]
1323 # This gathers information from each manifestnode included in the
1325 # This gathers information from each manifestnode included in the
1324 # changegroup about which filenodes the manifest node references
1326 # changegroup about which filenodes the manifest node references
1325 # so we can include those in the changegroup too.
1327 # so we can include those in the changegroup too.
1326 #
1328 #
1327 # It also remembers which changenode each filenode belongs to. It
1329 # It also remembers which changenode each filenode belongs to. It
1328 # does this by assuming the a filenode belongs to the changenode
1330 # does this by assuming the a filenode belongs to the changenode
1329 # the first manifest that references it belongs to.
1331 # the first manifest that references it belongs to.
1330 def collect_msng_filenodes(mnfstnode):
1332 def collect_msng_filenodes(mnfstnode):
1331 r = mnfst.rev(mnfstnode)
1333 r = mnfst.rev(mnfstnode)
1332 if r == next_rev[0]:
1334 if r == next_rev[0]:
1333 # If the last rev we looked at was the one just previous,
1335 # If the last rev we looked at was the one just previous,
1334 # we only need to see a diff.
1336 # we only need to see a diff.
1335 delta = mdiff.patchtext(mnfst.delta(mnfstnode))
1337 delta = mdiff.patchtext(mnfst.delta(mnfstnode))
1336 # For each line in the delta
1338 # For each line in the delta
1337 for dline in delta.splitlines():
1339 for dline in delta.splitlines():
1338 # get the filename and filenode for that line
1340 # get the filename and filenode for that line
1339 f, fnode = dline.split('\0')
1341 f, fnode = dline.split('\0')
1340 fnode = bin(fnode[:40])
1342 fnode = bin(fnode[:40])
1341 f = changedfiles.get(f, None)
1343 f = changedfiles.get(f, None)
1342 # And if the file is in the list of files we care
1344 # And if the file is in the list of files we care
1343 # about.
1345 # about.
1344 if f is not None:
1346 if f is not None:
1345 # Get the changenode this manifest belongs to
1347 # Get the changenode this manifest belongs to
1346 clnode = msng_mnfst_set[mnfstnode]
1348 clnode = msng_mnfst_set[mnfstnode]
1347 # Create the set of filenodes for the file if
1349 # Create the set of filenodes for the file if
1348 # there isn't one already.
1350 # there isn't one already.
1349 ndset = msng_filenode_set.setdefault(f, {})
1351 ndset = msng_filenode_set.setdefault(f, {})
1350 # And set the filenode's changelog node to the
1352 # And set the filenode's changelog node to the
1351 # manifest's if it hasn't been set already.
1353 # manifest's if it hasn't been set already.
1352 ndset.setdefault(fnode, clnode)
1354 ndset.setdefault(fnode, clnode)
1353 else:
1355 else:
1354 # Otherwise we need a full manifest.
1356 # Otherwise we need a full manifest.
1355 m = mnfst.read(mnfstnode)
1357 m = mnfst.read(mnfstnode)
1356 # For every file in we care about.
1358 # For every file in we care about.
1357 for f in changedfiles:
1359 for f in changedfiles:
1358 fnode = m.get(f, None)
1360 fnode = m.get(f, None)
1359 # If it's in the manifest
1361 # If it's in the manifest
1360 if fnode is not None:
1362 if fnode is not None:
1361 # See comments above.
1363 # See comments above.
1362 clnode = msng_mnfst_set[mnfstnode]
1364 clnode = msng_mnfst_set[mnfstnode]
1363 ndset = msng_filenode_set.setdefault(f, {})
1365 ndset = msng_filenode_set.setdefault(f, {})
1364 ndset.setdefault(fnode, clnode)
1366 ndset.setdefault(fnode, clnode)
1365 # Remember the revision we hope to see next.
1367 # Remember the revision we hope to see next.
1366 next_rev[0] = r + 1
1368 next_rev[0] = r + 1
1367 return collect_msng_filenodes
1369 return collect_msng_filenodes
1368
1370
1369 # We have a list of filenodes we think we need for a file, lets remove
1371 # We have a list of filenodes we think we need for a file, lets remove
1370 # all those we now the recipient must have.
1372 # all those we now the recipient must have.
1371 def prune_filenodes(f, filerevlog):
1373 def prune_filenodes(f, filerevlog):
1372 msngset = msng_filenode_set[f]
1374 msngset = msng_filenode_set[f]
1373 hasset = {}
1375 hasset = {}
1374 # If a 'missing' filenode thinks it belongs to a changenode we
1376 # If a 'missing' filenode thinks it belongs to a changenode we
1375 # assume the recipient must have, then the recipient must have
1377 # assume the recipient must have, then the recipient must have
1376 # that filenode.
1378 # that filenode.
1377 for n in msngset:
1379 for n in msngset:
1378 clnode = cl.node(filerevlog.linkrev(n))
1380 clnode = cl.node(filerevlog.linkrev(n))
1379 if clnode in has_cl_set:
1381 if clnode in has_cl_set:
1380 hasset[n] = 1
1382 hasset[n] = 1
1381 prune_parents(filerevlog, hasset, msngset)
1383 prune_parents(filerevlog, hasset, msngset)
1382
1384
1383 # A function generator function that sets up the a context for the
1385 # A function generator function that sets up the a context for the
1384 # inner function.
1386 # inner function.
1385 def lookup_filenode_link_func(fname):
1387 def lookup_filenode_link_func(fname):
1386 msngset = msng_filenode_set[fname]
1388 msngset = msng_filenode_set[fname]
1387 # Lookup the changenode the filenode belongs to.
1389 # Lookup the changenode the filenode belongs to.
1388 def lookup_filenode_link(fnode):
1390 def lookup_filenode_link(fnode):
1389 return msngset[fnode]
1391 return msngset[fnode]
1390 return lookup_filenode_link
1392 return lookup_filenode_link
1391
1393
1392 # Now that we have all theses utility functions to help out and
1394 # Now that we have all theses utility functions to help out and
1393 # logically divide up the task, generate the group.
1395 # logically divide up the task, generate the group.
1394 def gengroup():
1396 def gengroup():
1395 # The set of changed files starts empty.
1397 # The set of changed files starts empty.
1396 changedfiles = {}
1398 changedfiles = {}
1397 # Create a changenode group generator that will call our functions
1399 # Create a changenode group generator that will call our functions
1398 # back to lookup the owning changenode and collect information.
1400 # back to lookup the owning changenode and collect information.
1399 group = cl.group(msng_cl_lst, identity,
1401 group = cl.group(msng_cl_lst, identity,
1400 manifest_and_file_collector(changedfiles))
1402 manifest_and_file_collector(changedfiles))
1401 for chnk in group:
1403 for chnk in group:
1402 yield chnk
1404 yield chnk
1403
1405
1404 # The list of manifests has been collected by the generator
1406 # The list of manifests has been collected by the generator
1405 # calling our functions back.
1407 # calling our functions back.
1406 prune_manifests()
1408 prune_manifests()
1407 msng_mnfst_lst = msng_mnfst_set.keys()
1409 msng_mnfst_lst = msng_mnfst_set.keys()
1408 # Sort the manifestnodes by revision number.
1410 # Sort the manifestnodes by revision number.
1409 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1411 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1410 # Create a generator for the manifestnodes that calls our lookup
1412 # Create a generator for the manifestnodes that calls our lookup
1411 # and data collection functions back.
1413 # and data collection functions back.
1412 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1414 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1413 filenode_collector(changedfiles))
1415 filenode_collector(changedfiles))
1414 for chnk in group:
1416 for chnk in group:
1415 yield chnk
1417 yield chnk
1416
1418
1417 # These are no longer needed, dereference and toss the memory for
1419 # These are no longer needed, dereference and toss the memory for
1418 # them.
1420 # them.
1419 msng_mnfst_lst = None
1421 msng_mnfst_lst = None
1420 msng_mnfst_set.clear()
1422 msng_mnfst_set.clear()
1421
1423
1422 changedfiles = changedfiles.keys()
1424 changedfiles = changedfiles.keys()
1423 changedfiles.sort()
1425 changedfiles.sort()
1424 # Go through all our files in order sorted by name.
1426 # Go through all our files in order sorted by name.
1425 for fname in changedfiles:
1427 for fname in changedfiles:
1426 filerevlog = self.file(fname)
1428 filerevlog = self.file(fname)
1427 # Toss out the filenodes that the recipient isn't really
1429 # Toss out the filenodes that the recipient isn't really
1428 # missing.
1430 # missing.
1429 if msng_filenode_set.has_key(fname):
1431 if msng_filenode_set.has_key(fname):
1430 prune_filenodes(fname, filerevlog)
1432 prune_filenodes(fname, filerevlog)
1431 msng_filenode_lst = msng_filenode_set[fname].keys()
1433 msng_filenode_lst = msng_filenode_set[fname].keys()
1432 else:
1434 else:
1433 msng_filenode_lst = []
1435 msng_filenode_lst = []
1434 # If any filenodes are left, generate the group for them,
1436 # If any filenodes are left, generate the group for them,
1435 # otherwise don't bother.
1437 # otherwise don't bother.
1436 if len(msng_filenode_lst) > 0:
1438 if len(msng_filenode_lst) > 0:
1437 yield changegroup.genchunk(fname)
1439 yield changegroup.genchunk(fname)
1438 # Sort the filenodes by their revision #
1440 # Sort the filenodes by their revision #
1439 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1441 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1440 # Create a group generator and only pass in a changenode
1442 # Create a group generator and only pass in a changenode
1441 # lookup function as we need to collect no information
1443 # lookup function as we need to collect no information
1442 # from filenodes.
1444 # from filenodes.
1443 group = filerevlog.group(msng_filenode_lst,
1445 group = filerevlog.group(msng_filenode_lst,
1444 lookup_filenode_link_func(fname))
1446 lookup_filenode_link_func(fname))
1445 for chnk in group:
1447 for chnk in group:
1446 yield chnk
1448 yield chnk
1447 if msng_filenode_set.has_key(fname):
1449 if msng_filenode_set.has_key(fname):
1448 # Don't need this anymore, toss it to free memory.
1450 # Don't need this anymore, toss it to free memory.
1449 del msng_filenode_set[fname]
1451 del msng_filenode_set[fname]
1450 # Signal that no more groups are left.
1452 # Signal that no more groups are left.
1451 yield changegroup.closechunk()
1453 yield changegroup.closechunk()
1452
1454
1453 if msng_cl_lst:
1455 if msng_cl_lst:
1454 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1456 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1455
1457
1456 return util.chunkbuffer(gengroup())
1458 return util.chunkbuffer(gengroup())
1457
1459
1458 def changegroup(self, basenodes, source):
1460 def changegroup(self, basenodes, source):
1459 """Generate a changegroup of all nodes that we have that a recipient
1461 """Generate a changegroup of all nodes that we have that a recipient
1460 doesn't.
1462 doesn't.
1461
1463
1462 This is much easier than the previous function as we can assume that
1464 This is much easier than the previous function as we can assume that
1463 the recipient has any changenode we aren't sending them."""
1465 the recipient has any changenode we aren't sending them."""
1464
1466
1465 self.hook('preoutgoing', throw=True, source=source)
1467 self.hook('preoutgoing', throw=True, source=source)
1466
1468
1467 cl = self.changelog
1469 cl = self.changelog
1468 nodes = cl.nodesbetween(basenodes, None)[0]
1470 nodes = cl.nodesbetween(basenodes, None)[0]
1469 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1471 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1470
1472
1471 def identity(x):
1473 def identity(x):
1472 return x
1474 return x
1473
1475
1474 def gennodelst(revlog):
1476 def gennodelst(revlog):
1475 for r in xrange(0, revlog.count()):
1477 for r in xrange(0, revlog.count()):
1476 n = revlog.node(r)
1478 n = revlog.node(r)
1477 if revlog.linkrev(n) in revset:
1479 if revlog.linkrev(n) in revset:
1478 yield n
1480 yield n
1479
1481
1480 def changed_file_collector(changedfileset):
1482 def changed_file_collector(changedfileset):
1481 def collect_changed_files(clnode):
1483 def collect_changed_files(clnode):
1482 c = cl.read(clnode)
1484 c = cl.read(clnode)
1483 for fname in c[3]:
1485 for fname in c[3]:
1484 changedfileset[fname] = 1
1486 changedfileset[fname] = 1
1485 return collect_changed_files
1487 return collect_changed_files
1486
1488
1487 def lookuprevlink_func(revlog):
1489 def lookuprevlink_func(revlog):
1488 def lookuprevlink(n):
1490 def lookuprevlink(n):
1489 return cl.node(revlog.linkrev(n))
1491 return cl.node(revlog.linkrev(n))
1490 return lookuprevlink
1492 return lookuprevlink
1491
1493
1492 def gengroup():
1494 def gengroup():
1493 # construct a list of all changed files
1495 # construct a list of all changed files
1494 changedfiles = {}
1496 changedfiles = {}
1495
1497
1496 for chnk in cl.group(nodes, identity,
1498 for chnk in cl.group(nodes, identity,
1497 changed_file_collector(changedfiles)):
1499 changed_file_collector(changedfiles)):
1498 yield chnk
1500 yield chnk
1499 changedfiles = changedfiles.keys()
1501 changedfiles = changedfiles.keys()
1500 changedfiles.sort()
1502 changedfiles.sort()
1501
1503
1502 mnfst = self.manifest
1504 mnfst = self.manifest
1503 nodeiter = gennodelst(mnfst)
1505 nodeiter = gennodelst(mnfst)
1504 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1506 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1505 yield chnk
1507 yield chnk
1506
1508
1507 for fname in changedfiles:
1509 for fname in changedfiles:
1508 filerevlog = self.file(fname)
1510 filerevlog = self.file(fname)
1509 nodeiter = gennodelst(filerevlog)
1511 nodeiter = gennodelst(filerevlog)
1510 nodeiter = list(nodeiter)
1512 nodeiter = list(nodeiter)
1511 if nodeiter:
1513 if nodeiter:
1512 yield changegroup.genchunk(fname)
1514 yield changegroup.genchunk(fname)
1513 lookup = lookuprevlink_func(filerevlog)
1515 lookup = lookuprevlink_func(filerevlog)
1514 for chnk in filerevlog.group(nodeiter, lookup):
1516 for chnk in filerevlog.group(nodeiter, lookup):
1515 yield chnk
1517 yield chnk
1516
1518
1517 yield changegroup.closechunk()
1519 yield changegroup.closechunk()
1518
1520
1519 if nodes:
1521 if nodes:
1520 self.hook('outgoing', node=hex(nodes[0]), source=source)
1522 self.hook('outgoing', node=hex(nodes[0]), source=source)
1521
1523
1522 return util.chunkbuffer(gengroup())
1524 return util.chunkbuffer(gengroup())
1523
1525
1524 def addchangegroup(self, source, srctype):
1526 def addchangegroup(self, source, srctype):
1525 """add changegroup to repo.
1527 """add changegroup to repo.
1526 returns number of heads modified or added + 1."""
1528 returns number of heads modified or added + 1."""
1527
1529
1528 def csmap(x):
1530 def csmap(x):
1529 self.ui.debug(_("add changeset %s\n") % short(x))
1531 self.ui.debug(_("add changeset %s\n") % short(x))
1530 return cl.count()
1532 return cl.count()
1531
1533
1532 def revmap(x):
1534 def revmap(x):
1533 return cl.rev(x)
1535 return cl.rev(x)
1534
1536
1535 if not source:
1537 if not source:
1536 return 0
1538 return 0
1537
1539
1538 self.hook('prechangegroup', throw=True, source=srctype)
1540 self.hook('prechangegroup', throw=True, source=srctype)
1539
1541
1540 changesets = files = revisions = 0
1542 changesets = files = revisions = 0
1541
1543
1542 tr = self.transaction()
1544 tr = self.transaction()
1543
1545
1544 # write changelog data to temp files so concurrent readers will not see
1546 # write changelog data to temp files so concurrent readers will not see
1545 # inconsistent view
1547 # inconsistent view
1546 cl = None
1548 cl = None
1547 try:
1549 try:
1548 cl = appendfile.appendchangelog(self.opener, self.changelog.version)
1550 cl = appendfile.appendchangelog(self.opener, self.changelog.version)
1549
1551
1550 oldheads = len(cl.heads())
1552 oldheads = len(cl.heads())
1551
1553
1552 # pull off the changeset group
1554 # pull off the changeset group
1553 self.ui.status(_("adding changesets\n"))
1555 self.ui.status(_("adding changesets\n"))
1554 cor = cl.count() - 1
1556 cor = cl.count() - 1
1555 chunkiter = changegroup.chunkiter(source)
1557 chunkiter = changegroup.chunkiter(source)
1556 if cl.addgroup(chunkiter, csmap, tr, 1) is None:
1558 if cl.addgroup(chunkiter, csmap, tr, 1) is None:
1557 raise util.Abort(_("received changelog group is empty"))
1559 raise util.Abort(_("received changelog group is empty"))
1558 cnr = cl.count() - 1
1560 cnr = cl.count() - 1
1559 changesets = cnr - cor
1561 changesets = cnr - cor
1560
1562
1561 # pull off the manifest group
1563 # pull off the manifest group
1562 self.ui.status(_("adding manifests\n"))
1564 self.ui.status(_("adding manifests\n"))
1563 chunkiter = changegroup.chunkiter(source)
1565 chunkiter = changegroup.chunkiter(source)
1564 # no need to check for empty manifest group here:
1566 # no need to check for empty manifest group here:
1565 # if the result of the merge of 1 and 2 is the same in 3 and 4,
1567 # if the result of the merge of 1 and 2 is the same in 3 and 4,
1566 # no new manifest will be created and the manifest group will
1568 # no new manifest will be created and the manifest group will
1567 # be empty during the pull
1569 # be empty during the pull
1568 self.manifest.addgroup(chunkiter, revmap, tr)
1570 self.manifest.addgroup(chunkiter, revmap, tr)
1569
1571
1570 # process the files
1572 # process the files
1571 self.ui.status(_("adding file changes\n"))
1573 self.ui.status(_("adding file changes\n"))
1572 while 1:
1574 while 1:
1573 f = changegroup.getchunk(source)
1575 f = changegroup.getchunk(source)
1574 if not f:
1576 if not f:
1575 break
1577 break
1576 self.ui.debug(_("adding %s revisions\n") % f)
1578 self.ui.debug(_("adding %s revisions\n") % f)
1577 fl = self.file(f)
1579 fl = self.file(f)
1578 o = fl.count()
1580 o = fl.count()
1579 chunkiter = changegroup.chunkiter(source)
1581 chunkiter = changegroup.chunkiter(source)
1580 if fl.addgroup(chunkiter, revmap, tr) is None:
1582 if fl.addgroup(chunkiter, revmap, tr) is None:
1581 raise util.Abort(_("received file revlog group is empty"))
1583 raise util.Abort(_("received file revlog group is empty"))
1582 revisions += fl.count() - o
1584 revisions += fl.count() - o
1583 files += 1
1585 files += 1
1584
1586
1585 cl.writedata()
1587 cl.writedata()
1586 finally:
1588 finally:
1587 if cl:
1589 if cl:
1588 cl.cleanup()
1590 cl.cleanup()
1589
1591
1590 # make changelog see real files again
1592 # make changelog see real files again
1591 self.changelog = changelog.changelog(self.opener, self.changelog.version)
1593 self.changelog = changelog.changelog(self.opener, self.changelog.version)
1592 self.changelog.checkinlinesize(tr)
1594 self.changelog.checkinlinesize(tr)
1593
1595
1594 newheads = len(self.changelog.heads())
1596 newheads = len(self.changelog.heads())
1595 heads = ""
1597 heads = ""
1596 if oldheads and newheads != oldheads:
1598 if oldheads and newheads != oldheads:
1597 heads = _(" (%+d heads)") % (newheads - oldheads)
1599 heads = _(" (%+d heads)") % (newheads - oldheads)
1598
1600
1599 self.ui.status(_("added %d changesets"
1601 self.ui.status(_("added %d changesets"
1600 " with %d changes to %d files%s\n")
1602 " with %d changes to %d files%s\n")
1601 % (changesets, revisions, files, heads))
1603 % (changesets, revisions, files, heads))
1602
1604
1603 if changesets > 0:
1605 if changesets > 0:
1604 self.hook('pretxnchangegroup', throw=True,
1606 self.hook('pretxnchangegroup', throw=True,
1605 node=hex(self.changelog.node(cor+1)), source=srctype)
1607 node=hex(self.changelog.node(cor+1)), source=srctype)
1606
1608
1607 tr.close()
1609 tr.close()
1608
1610
1609 if changesets > 0:
1611 if changesets > 0:
1610 self.hook("changegroup", node=hex(self.changelog.node(cor+1)),
1612 self.hook("changegroup", node=hex(self.changelog.node(cor+1)),
1611 source=srctype)
1613 source=srctype)
1612
1614
1613 for i in range(cor + 1, cnr + 1):
1615 for i in range(cor + 1, cnr + 1):
1614 self.hook("incoming", node=hex(self.changelog.node(i)),
1616 self.hook("incoming", node=hex(self.changelog.node(i)),
1615 source=srctype)
1617 source=srctype)
1616
1618
1617 return newheads - oldheads + 1
1619 return newheads - oldheads + 1
1618
1620
1619 def update(self, node, allow=False, force=False, choose=None,
1621 def update(self, node, allow=False, force=False, choose=None,
1620 moddirstate=True, forcemerge=False, wlock=None, show_stats=True):
1622 moddirstate=True, forcemerge=False, wlock=None, show_stats=True):
1621 pl = self.dirstate.parents()
1623 pl = self.dirstate.parents()
1622 if not force and pl[1] != nullid:
1624 if not force and pl[1] != nullid:
1623 raise util.Abort(_("outstanding uncommitted merges"))
1625 raise util.Abort(_("outstanding uncommitted merges"))
1624
1626
1625 err = False
1627 err = False
1626
1628
1627 p1, p2 = pl[0], node
1629 p1, p2 = pl[0], node
1628 pa = self.changelog.ancestor(p1, p2)
1630 pa = self.changelog.ancestor(p1, p2)
1629 m1n = self.changelog.read(p1)[0]
1631 m1n = self.changelog.read(p1)[0]
1630 m2n = self.changelog.read(p2)[0]
1632 m2n = self.changelog.read(p2)[0]
1631 man = self.manifest.ancestor(m1n, m2n)
1633 man = self.manifest.ancestor(m1n, m2n)
1632 m1 = self.manifest.read(m1n)
1634 m1 = self.manifest.read(m1n)
1633 mf1 = self.manifest.readflags(m1n)
1635 mf1 = self.manifest.readflags(m1n)
1634 m2 = self.manifest.read(m2n).copy()
1636 m2 = self.manifest.read(m2n).copy()
1635 mf2 = self.manifest.readflags(m2n)
1637 mf2 = self.manifest.readflags(m2n)
1636 ma = self.manifest.read(man)
1638 ma = self.manifest.read(man)
1637 mfa = self.manifest.readflags(man)
1639 mfa = self.manifest.readflags(man)
1638
1640
1639 modified, added, removed, deleted, unknown = self.changes()
1641 modified, added, removed, deleted, unknown = self.changes()
1640
1642
1641 # is this a jump, or a merge? i.e. is there a linear path
1643 # is this a jump, or a merge? i.e. is there a linear path
1642 # from p1 to p2?
1644 # from p1 to p2?
1643 linear_path = (pa == p1 or pa == p2)
1645 linear_path = (pa == p1 or pa == p2)
1644
1646
1645 if allow and linear_path:
1647 if allow and linear_path:
1646 raise util.Abort(_("there is nothing to merge, "
1648 raise util.Abort(_("there is nothing to merge, "
1647 "just use 'hg update'"))
1649 "just use 'hg update'"))
1648 if allow and not forcemerge:
1650 if allow and not forcemerge:
1649 if modified or added or removed:
1651 if modified or added or removed:
1650 raise util.Abort(_("outstanding uncommitted changes"))
1652 raise util.Abort(_("outstanding uncommitted changes"))
1651
1653
1652 if not forcemerge and not force:
1654 if not forcemerge and not force:
1653 for f in unknown:
1655 for f in unknown:
1654 if f in m2:
1656 if f in m2:
1655 t1 = self.wread(f)
1657 t1 = self.wread(f)
1656 t2 = self.file(f).read(m2[f])
1658 t2 = self.file(f).read(m2[f])
1657 if cmp(t1, t2) != 0:
1659 if cmp(t1, t2) != 0:
1658 raise util.Abort(_("'%s' already exists in the working"
1660 raise util.Abort(_("'%s' already exists in the working"
1659 " dir and differs from remote") % f)
1661 " dir and differs from remote") % f)
1660
1662
1661 # resolve the manifest to determine which files
1663 # resolve the manifest to determine which files
1662 # we care about merging
1664 # we care about merging
1663 self.ui.note(_("resolving manifests\n"))
1665 self.ui.note(_("resolving manifests\n"))
1664 self.ui.debug(_(" force %s allow %s moddirstate %s linear %s\n") %
1666 self.ui.debug(_(" force %s allow %s moddirstate %s linear %s\n") %
1665 (force, allow, moddirstate, linear_path))
1667 (force, allow, moddirstate, linear_path))
1666 self.ui.debug(_(" ancestor %s local %s remote %s\n") %
1668 self.ui.debug(_(" ancestor %s local %s remote %s\n") %
1667 (short(man), short(m1n), short(m2n)))
1669 (short(man), short(m1n), short(m2n)))
1668
1670
1669 merge = {}
1671 merge = {}
1670 get = {}
1672 get = {}
1671 remove = []
1673 remove = []
1672
1674
1673 # construct a working dir manifest
1675 # construct a working dir manifest
1674 mw = m1.copy()
1676 mw = m1.copy()
1675 mfw = mf1.copy()
1677 mfw = mf1.copy()
1676 umap = dict.fromkeys(unknown)
1678 umap = dict.fromkeys(unknown)
1677
1679
1678 for f in added + modified + unknown:
1680 for f in added + modified + unknown:
1679 mw[f] = ""
1681 mw[f] = ""
1680 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1682 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1681
1683
1682 if moddirstate and not wlock:
1684 if moddirstate and not wlock:
1683 wlock = self.wlock()
1685 wlock = self.wlock()
1684
1686
1685 for f in deleted + removed:
1687 for f in deleted + removed:
1686 if f in mw:
1688 if f in mw:
1687 del mw[f]
1689 del mw[f]
1688
1690
1689 # If we're jumping between revisions (as opposed to merging),
1691 # If we're jumping between revisions (as opposed to merging),
1690 # and if neither the working directory nor the target rev has
1692 # and if neither the working directory nor the target rev has
1691 # the file, then we need to remove it from the dirstate, to
1693 # the file, then we need to remove it from the dirstate, to
1692 # prevent the dirstate from listing the file when it is no
1694 # prevent the dirstate from listing the file when it is no
1693 # longer in the manifest.
1695 # longer in the manifest.
1694 if moddirstate and linear_path and f not in m2:
1696 if moddirstate and linear_path and f not in m2:
1695 self.dirstate.forget((f,))
1697 self.dirstate.forget((f,))
1696
1698
1697 # Compare manifests
1699 # Compare manifests
1698 for f, n in mw.iteritems():
1700 for f, n in mw.iteritems():
1699 if choose and not choose(f):
1701 if choose and not choose(f):
1700 continue
1702 continue
1701 if f in m2:
1703 if f in m2:
1702 s = 0
1704 s = 0
1703
1705
1704 # is the wfile new since m1, and match m2?
1706 # is the wfile new since m1, and match m2?
1705 if f not in m1:
1707 if f not in m1:
1706 t1 = self.wread(f)
1708 t1 = self.wread(f)
1707 t2 = self.file(f).read(m2[f])
1709 t2 = self.file(f).read(m2[f])
1708 if cmp(t1, t2) == 0:
1710 if cmp(t1, t2) == 0:
1709 n = m2[f]
1711 n = m2[f]
1710 del t1, t2
1712 del t1, t2
1711
1713
1712 # are files different?
1714 # are files different?
1713 if n != m2[f]:
1715 if n != m2[f]:
1714 a = ma.get(f, nullid)
1716 a = ma.get(f, nullid)
1715 # are both different from the ancestor?
1717 # are both different from the ancestor?
1716 if n != a and m2[f] != a:
1718 if n != a and m2[f] != a:
1717 self.ui.debug(_(" %s versions differ, resolve\n") % f)
1719 self.ui.debug(_(" %s versions differ, resolve\n") % f)
1718 # merge executable bits
1720 # merge executable bits
1719 # "if we changed or they changed, change in merge"
1721 # "if we changed or they changed, change in merge"
1720 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1722 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1721 mode = ((a^b) | (a^c)) ^ a
1723 mode = ((a^b) | (a^c)) ^ a
1722 merge[f] = (m1.get(f, nullid), m2[f], mode)
1724 merge[f] = (m1.get(f, nullid), m2[f], mode)
1723 s = 1
1725 s = 1
1724 # are we clobbering?
1726 # are we clobbering?
1725 # is remote's version newer?
1727 # is remote's version newer?
1726 # or are we going back in time?
1728 # or are we going back in time?
1727 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1729 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1728 self.ui.debug(_(" remote %s is newer, get\n") % f)
1730 self.ui.debug(_(" remote %s is newer, get\n") % f)
1729 get[f] = m2[f]
1731 get[f] = m2[f]
1730 s = 1
1732 s = 1
1731 elif f in umap or f in added:
1733 elif f in umap or f in added:
1732 # this unknown file is the same as the checkout
1734 # this unknown file is the same as the checkout
1733 # we need to reset the dirstate if the file was added
1735 # we need to reset the dirstate if the file was added
1734 get[f] = m2[f]
1736 get[f] = m2[f]
1735
1737
1736 if not s and mfw[f] != mf2[f]:
1738 if not s and mfw[f] != mf2[f]:
1737 if force:
1739 if force:
1738 self.ui.debug(_(" updating permissions for %s\n") % f)
1740 self.ui.debug(_(" updating permissions for %s\n") % f)
1739 util.set_exec(self.wjoin(f), mf2[f])
1741 util.set_exec(self.wjoin(f), mf2[f])
1740 else:
1742 else:
1741 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1743 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1742 mode = ((a^b) | (a^c)) ^ a
1744 mode = ((a^b) | (a^c)) ^ a
1743 if mode != b:
1745 if mode != b:
1744 self.ui.debug(_(" updating permissions for %s\n")
1746 self.ui.debug(_(" updating permissions for %s\n")
1745 % f)
1747 % f)
1746 util.set_exec(self.wjoin(f), mode)
1748 util.set_exec(self.wjoin(f), mode)
1747 del m2[f]
1749 del m2[f]
1748 elif f in ma:
1750 elif f in ma:
1749 if n != ma[f]:
1751 if n != ma[f]:
1750 r = _("d")
1752 r = _("d")
1751 if not force and (linear_path or allow):
1753 if not force and (linear_path or allow):
1752 r = self.ui.prompt(
1754 r = self.ui.prompt(
1753 (_(" local changed %s which remote deleted\n") % f) +
1755 (_(" local changed %s which remote deleted\n") % f) +
1754 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1756 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1755 if r == _("d"):
1757 if r == _("d"):
1756 remove.append(f)
1758 remove.append(f)
1757 else:
1759 else:
1758 self.ui.debug(_("other deleted %s\n") % f)
1760 self.ui.debug(_("other deleted %s\n") % f)
1759 remove.append(f) # other deleted it
1761 remove.append(f) # other deleted it
1760 else:
1762 else:
1761 # file is created on branch or in working directory
1763 # file is created on branch or in working directory
1762 if force and f not in umap:
1764 if force and f not in umap:
1763 self.ui.debug(_("remote deleted %s, clobbering\n") % f)
1765 self.ui.debug(_("remote deleted %s, clobbering\n") % f)
1764 remove.append(f)
1766 remove.append(f)
1765 elif n == m1.get(f, nullid): # same as parent
1767 elif n == m1.get(f, nullid): # same as parent
1766 if p2 == pa: # going backwards?
1768 if p2 == pa: # going backwards?
1767 self.ui.debug(_("remote deleted %s\n") % f)
1769 self.ui.debug(_("remote deleted %s\n") % f)
1768 remove.append(f)
1770 remove.append(f)
1769 else:
1771 else:
1770 self.ui.debug(_("local modified %s, keeping\n") % f)
1772 self.ui.debug(_("local modified %s, keeping\n") % f)
1771 else:
1773 else:
1772 self.ui.debug(_("working dir created %s, keeping\n") % f)
1774 self.ui.debug(_("working dir created %s, keeping\n") % f)
1773
1775
1774 for f, n in m2.iteritems():
1776 for f, n in m2.iteritems():
1775 if choose and not choose(f):
1777 if choose and not choose(f):
1776 continue
1778 continue
1777 if f[0] == "/":
1779 if f[0] == "/":
1778 continue
1780 continue
1779 if f in ma and n != ma[f]:
1781 if f in ma and n != ma[f]:
1780 r = _("k")
1782 r = _("k")
1781 if not force and (linear_path or allow):
1783 if not force and (linear_path or allow):
1782 r = self.ui.prompt(
1784 r = self.ui.prompt(
1783 (_("remote changed %s which local deleted\n") % f) +
1785 (_("remote changed %s which local deleted\n") % f) +
1784 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1786 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1785 if r == _("k"):
1787 if r == _("k"):
1786 get[f] = n
1788 get[f] = n
1787 elif f not in ma:
1789 elif f not in ma:
1788 self.ui.debug(_("remote created %s\n") % f)
1790 self.ui.debug(_("remote created %s\n") % f)
1789 get[f] = n
1791 get[f] = n
1790 else:
1792 else:
1791 if force or p2 == pa: # going backwards?
1793 if force or p2 == pa: # going backwards?
1792 self.ui.debug(_("local deleted %s, recreating\n") % f)
1794 self.ui.debug(_("local deleted %s, recreating\n") % f)
1793 get[f] = n
1795 get[f] = n
1794 else:
1796 else:
1795 self.ui.debug(_("local deleted %s\n") % f)
1797 self.ui.debug(_("local deleted %s\n") % f)
1796
1798
1797 del mw, m1, m2, ma
1799 del mw, m1, m2, ma
1798
1800
1799 if force:
1801 if force:
1800 for f in merge:
1802 for f in merge:
1801 get[f] = merge[f][1]
1803 get[f] = merge[f][1]
1802 merge = {}
1804 merge = {}
1803
1805
1804 if linear_path or force:
1806 if linear_path or force:
1805 # we don't need to do any magic, just jump to the new rev
1807 # we don't need to do any magic, just jump to the new rev
1806 branch_merge = False
1808 branch_merge = False
1807 p1, p2 = p2, nullid
1809 p1, p2 = p2, nullid
1808 else:
1810 else:
1809 if not allow:
1811 if not allow:
1810 self.ui.status(_("this update spans a branch"
1812 self.ui.status(_("this update spans a branch"
1811 " affecting the following files:\n"))
1813 " affecting the following files:\n"))
1812 fl = merge.keys() + get.keys()
1814 fl = merge.keys() + get.keys()
1813 fl.sort()
1815 fl.sort()
1814 for f in fl:
1816 for f in fl:
1815 cf = ""
1817 cf = ""
1816 if f in merge:
1818 if f in merge:
1817 cf = _(" (resolve)")
1819 cf = _(" (resolve)")
1818 self.ui.status(" %s%s\n" % (f, cf))
1820 self.ui.status(" %s%s\n" % (f, cf))
1819 self.ui.warn(_("aborting update spanning branches!\n"))
1821 self.ui.warn(_("aborting update spanning branches!\n"))
1820 self.ui.status(_("(use 'hg merge' to merge across branches"
1822 self.ui.status(_("(use 'hg merge' to merge across branches"
1821 " or 'hg update -C' to lose changes)\n"))
1823 " or 'hg update -C' to lose changes)\n"))
1822 return 1
1824 return 1
1823 branch_merge = True
1825 branch_merge = True
1824
1826
1825 xp1 = hex(p1)
1827 xp1 = hex(p1)
1826 xp2 = hex(p2)
1828 xp2 = hex(p2)
1827 if p2 == nullid: xxp2 = ''
1829 if p2 == nullid: xxp2 = ''
1828 else: xxp2 = xp2
1830 else: xxp2 = xp2
1829
1831
1830 self.hook('preupdate', throw=True, parent1=xp1, parent2=xxp2)
1832 self.hook('preupdate', throw=True, parent1=xp1, parent2=xxp2)
1831
1833
1832 # get the files we don't need to change
1834 # get the files we don't need to change
1833 files = get.keys()
1835 files = get.keys()
1834 files.sort()
1836 files.sort()
1835 for f in files:
1837 for f in files:
1836 if f[0] == "/":
1838 if f[0] == "/":
1837 continue
1839 continue
1838 self.ui.note(_("getting %s\n") % f)
1840 self.ui.note(_("getting %s\n") % f)
1839 t = self.file(f).read(get[f])
1841 t = self.file(f).read(get[f])
1840 self.wwrite(f, t)
1842 self.wwrite(f, t)
1841 util.set_exec(self.wjoin(f), mf2[f])
1843 util.set_exec(self.wjoin(f), mf2[f])
1842 if moddirstate:
1844 if moddirstate:
1843 if branch_merge:
1845 if branch_merge:
1844 self.dirstate.update([f], 'n', st_mtime=-1)
1846 self.dirstate.update([f], 'n', st_mtime=-1)
1845 else:
1847 else:
1846 self.dirstate.update([f], 'n')
1848 self.dirstate.update([f], 'n')
1847
1849
1848 # merge the tricky bits
1850 # merge the tricky bits
1849 failedmerge = []
1851 failedmerge = []
1850 files = merge.keys()
1852 files = merge.keys()
1851 files.sort()
1853 files.sort()
1852 for f in files:
1854 for f in files:
1853 self.ui.status(_("merging %s\n") % f)
1855 self.ui.status(_("merging %s\n") % f)
1854 my, other, flag = merge[f]
1856 my, other, flag = merge[f]
1855 ret = self.merge3(f, my, other, xp1, xp2)
1857 ret = self.merge3(f, my, other, xp1, xp2)
1856 if ret:
1858 if ret:
1857 err = True
1859 err = True
1858 failedmerge.append(f)
1860 failedmerge.append(f)
1859 util.set_exec(self.wjoin(f), flag)
1861 util.set_exec(self.wjoin(f), flag)
1860 if moddirstate:
1862 if moddirstate:
1861 if branch_merge:
1863 if branch_merge:
1862 # We've done a branch merge, mark this file as merged
1864 # We've done a branch merge, mark this file as merged
1863 # so that we properly record the merger later
1865 # so that we properly record the merger later
1864 self.dirstate.update([f], 'm')
1866 self.dirstate.update([f], 'm')
1865 else:
1867 else:
1866 # We've update-merged a locally modified file, so
1868 # We've update-merged a locally modified file, so
1867 # we set the dirstate to emulate a normal checkout
1869 # we set the dirstate to emulate a normal checkout
1868 # of that file some time in the past. Thus our
1870 # of that file some time in the past. Thus our
1869 # merge will appear as a normal local file
1871 # merge will appear as a normal local file
1870 # modification.
1872 # modification.
1871 f_len = len(self.file(f).read(other))
1873 f_len = len(self.file(f).read(other))
1872 self.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
1874 self.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
1873
1875
1874 remove.sort()
1876 remove.sort()
1875 for f in remove:
1877 for f in remove:
1876 self.ui.note(_("removing %s\n") % f)
1878 self.ui.note(_("removing %s\n") % f)
1877 util.audit_path(f)
1879 util.audit_path(f)
1878 try:
1880 try:
1879 util.unlink(self.wjoin(f))
1881 util.unlink(self.wjoin(f))
1880 except OSError, inst:
1882 except OSError, inst:
1881 if inst.errno != errno.ENOENT:
1883 if inst.errno != errno.ENOENT:
1882 self.ui.warn(_("update failed to remove %s: %s!\n") %
1884 self.ui.warn(_("update failed to remove %s: %s!\n") %
1883 (f, inst.strerror))
1885 (f, inst.strerror))
1884 if moddirstate:
1886 if moddirstate:
1885 if branch_merge:
1887 if branch_merge:
1886 self.dirstate.update(remove, 'r')
1888 self.dirstate.update(remove, 'r')
1887 else:
1889 else:
1888 self.dirstate.forget(remove)
1890 self.dirstate.forget(remove)
1889
1891
1890 if moddirstate:
1892 if moddirstate:
1891 self.dirstate.setparents(p1, p2)
1893 self.dirstate.setparents(p1, p2)
1892
1894
1893 if show_stats:
1895 if show_stats:
1894 stats = ((len(get), _("updated")),
1896 stats = ((len(get), _("updated")),
1895 (len(merge) - len(failedmerge), _("merged")),
1897 (len(merge) - len(failedmerge), _("merged")),
1896 (len(remove), _("removed")),
1898 (len(remove), _("removed")),
1897 (len(failedmerge), _("unresolved")))
1899 (len(failedmerge), _("unresolved")))
1898 note = ", ".join([_("%d files %s") % s for s in stats])
1900 note = ", ".join([_("%d files %s") % s for s in stats])
1899 self.ui.status("%s\n" % note)
1901 self.ui.status("%s\n" % note)
1900 if moddirstate:
1902 if moddirstate:
1901 if branch_merge:
1903 if branch_merge:
1902 if failedmerge:
1904 if failedmerge:
1903 self.ui.status(_("There are unresolved merges,"
1905 self.ui.status(_("There are unresolved merges,"
1904 " you can redo the full merge using:\n"
1906 " you can redo the full merge using:\n"
1905 " hg update -C %s\n"
1907 " hg update -C %s\n"
1906 " hg merge %s\n"
1908 " hg merge %s\n"
1907 % (self.changelog.rev(p1),
1909 % (self.changelog.rev(p1),
1908 self.changelog.rev(p2))))
1910 self.changelog.rev(p2))))
1909 else:
1911 else:
1910 self.ui.status(_("(branch merge, don't forget to commit)\n"))
1912 self.ui.status(_("(branch merge, don't forget to commit)\n"))
1911 elif failedmerge:
1913 elif failedmerge:
1912 self.ui.status(_("There are unresolved merges with"
1914 self.ui.status(_("There are unresolved merges with"
1913 " locally modified files.\n"))
1915 " locally modified files.\n"))
1914
1916
1915 self.hook('update', parent1=xp1, parent2=xxp2, error=int(err))
1917 self.hook('update', parent1=xp1, parent2=xxp2, error=int(err))
1916 return err
1918 return err
1917
1919
1918 def merge3(self, fn, my, other, p1, p2):
1920 def merge3(self, fn, my, other, p1, p2):
1919 """perform a 3-way merge in the working directory"""
1921 """perform a 3-way merge in the working directory"""
1920
1922
1921 def temp(prefix, node):
1923 def temp(prefix, node):
1922 pre = "%s~%s." % (os.path.basename(fn), prefix)
1924 pre = "%s~%s." % (os.path.basename(fn), prefix)
1923 (fd, name) = tempfile.mkstemp(prefix=pre)
1925 (fd, name) = tempfile.mkstemp(prefix=pre)
1924 f = os.fdopen(fd, "wb")
1926 f = os.fdopen(fd, "wb")
1925 self.wwrite(fn, fl.read(node), f)
1927 self.wwrite(fn, fl.read(node), f)
1926 f.close()
1928 f.close()
1927 return name
1929 return name
1928
1930
1929 fl = self.file(fn)
1931 fl = self.file(fn)
1930 base = fl.ancestor(my, other)
1932 base = fl.ancestor(my, other)
1931 a = self.wjoin(fn)
1933 a = self.wjoin(fn)
1932 b = temp("base", base)
1934 b = temp("base", base)
1933 c = temp("other", other)
1935 c = temp("other", other)
1934
1936
1935 self.ui.note(_("resolving %s\n") % fn)
1937 self.ui.note(_("resolving %s\n") % fn)
1936 self.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
1938 self.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
1937 (fn, short(my), short(other), short(base)))
1939 (fn, short(my), short(other), short(base)))
1938
1940
1939 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1941 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1940 or "hgmerge")
1942 or "hgmerge")
1941 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=self.root,
1943 r = util.system('%s "%s" "%s" "%s"' % (cmd, a, b, c), cwd=self.root,
1942 environ={'HG_FILE': fn,
1944 environ={'HG_FILE': fn,
1943 'HG_MY_NODE': p1,
1945 'HG_MY_NODE': p1,
1944 'HG_OTHER_NODE': p2,
1946 'HG_OTHER_NODE': p2,
1945 'HG_FILE_MY_NODE': hex(my),
1947 'HG_FILE_MY_NODE': hex(my),
1946 'HG_FILE_OTHER_NODE': hex(other),
1948 'HG_FILE_OTHER_NODE': hex(other),
1947 'HG_FILE_BASE_NODE': hex(base)})
1949 'HG_FILE_BASE_NODE': hex(base)})
1948 if r:
1950 if r:
1949 self.ui.warn(_("merging %s failed!\n") % fn)
1951 self.ui.warn(_("merging %s failed!\n") % fn)
1950
1952
1951 os.unlink(b)
1953 os.unlink(b)
1952 os.unlink(c)
1954 os.unlink(c)
1953 return r
1955 return r
1954
1956
1955 def verify(self):
1957 def verify(self):
1956 filelinkrevs = {}
1958 filelinkrevs = {}
1957 filenodes = {}
1959 filenodes = {}
1958 changesets = revisions = files = 0
1960 changesets = revisions = files = 0
1959 errors = [0]
1961 errors = [0]
1960 warnings = [0]
1962 warnings = [0]
1961 neededmanifests = {}
1963 neededmanifests = {}
1962
1964
1963 def err(msg):
1965 def err(msg):
1964 self.ui.warn(msg + "\n")
1966 self.ui.warn(msg + "\n")
1965 errors[0] += 1
1967 errors[0] += 1
1966
1968
1967 def warn(msg):
1969 def warn(msg):
1968 self.ui.warn(msg + "\n")
1970 self.ui.warn(msg + "\n")
1969 warnings[0] += 1
1971 warnings[0] += 1
1970
1972
1971 def checksize(obj, name):
1973 def checksize(obj, name):
1972 d = obj.checksize()
1974 d = obj.checksize()
1973 if d[0]:
1975 if d[0]:
1974 err(_("%s data length off by %d bytes") % (name, d[0]))
1976 err(_("%s data length off by %d bytes") % (name, d[0]))
1975 if d[1]:
1977 if d[1]:
1976 err(_("%s index contains %d extra bytes") % (name, d[1]))
1978 err(_("%s index contains %d extra bytes") % (name, d[1]))
1977
1979
1978 def checkversion(obj, name):
1980 def checkversion(obj, name):
1979 if obj.version != revlog.REVLOGV0:
1981 if obj.version != revlog.REVLOGV0:
1980 if not revlogv1:
1982 if not revlogv1:
1981 warn(_("warning: `%s' uses revlog format 1") % name)
1983 warn(_("warning: `%s' uses revlog format 1") % name)
1982 elif revlogv1:
1984 elif revlogv1:
1983 warn(_("warning: `%s' uses revlog format 0") % name)
1985 warn(_("warning: `%s' uses revlog format 0") % name)
1984
1986
1985 revlogv1 = self.revlogversion != revlog.REVLOGV0
1987 revlogv1 = self.revlogversion != revlog.REVLOGV0
1986 if self.ui.verbose or revlogv1 != self.revlogv1:
1988 if self.ui.verbose or revlogv1 != self.revlogv1:
1987 self.ui.status(_("repository uses revlog format %d\n") %
1989 self.ui.status(_("repository uses revlog format %d\n") %
1988 (revlogv1 and 1 or 0))
1990 (revlogv1 and 1 or 0))
1989
1991
1990 seen = {}
1992 seen = {}
1991 self.ui.status(_("checking changesets\n"))
1993 self.ui.status(_("checking changesets\n"))
1992 checksize(self.changelog, "changelog")
1994 checksize(self.changelog, "changelog")
1993
1995
1994 for i in range(self.changelog.count()):
1996 for i in range(self.changelog.count()):
1995 changesets += 1
1997 changesets += 1
1996 n = self.changelog.node(i)
1998 n = self.changelog.node(i)
1997 l = self.changelog.linkrev(n)
1999 l = self.changelog.linkrev(n)
1998 if l != i:
2000 if l != i:
1999 err(_("incorrect link (%d) for changeset revision %d") %(l, i))
2001 err(_("incorrect link (%d) for changeset revision %d") %(l, i))
2000 if n in seen:
2002 if n in seen:
2001 err(_("duplicate changeset at revision %d") % i)
2003 err(_("duplicate changeset at revision %d") % i)
2002 seen[n] = 1
2004 seen[n] = 1
2003
2005
2004 for p in self.changelog.parents(n):
2006 for p in self.changelog.parents(n):
2005 if p not in self.changelog.nodemap:
2007 if p not in self.changelog.nodemap:
2006 err(_("changeset %s has unknown parent %s") %
2008 err(_("changeset %s has unknown parent %s") %
2007 (short(n), short(p)))
2009 (short(n), short(p)))
2008 try:
2010 try:
2009 changes = self.changelog.read(n)
2011 changes = self.changelog.read(n)
2010 except KeyboardInterrupt:
2012 except KeyboardInterrupt:
2011 self.ui.warn(_("interrupted"))
2013 self.ui.warn(_("interrupted"))
2012 raise
2014 raise
2013 except Exception, inst:
2015 except Exception, inst:
2014 err(_("unpacking changeset %s: %s") % (short(n), inst))
2016 err(_("unpacking changeset %s: %s") % (short(n), inst))
2015 continue
2017 continue
2016
2018
2017 neededmanifests[changes[0]] = n
2019 neededmanifests[changes[0]] = n
2018
2020
2019 for f in changes[3]:
2021 for f in changes[3]:
2020 filelinkrevs.setdefault(f, []).append(i)
2022 filelinkrevs.setdefault(f, []).append(i)
2021
2023
2022 seen = {}
2024 seen = {}
2023 self.ui.status(_("checking manifests\n"))
2025 self.ui.status(_("checking manifests\n"))
2024 checkversion(self.manifest, "manifest")
2026 checkversion(self.manifest, "manifest")
2025 checksize(self.manifest, "manifest")
2027 checksize(self.manifest, "manifest")
2026
2028
2027 for i in range(self.manifest.count()):
2029 for i in range(self.manifest.count()):
2028 n = self.manifest.node(i)
2030 n = self.manifest.node(i)
2029 l = self.manifest.linkrev(n)
2031 l = self.manifest.linkrev(n)
2030
2032
2031 if l < 0 or l >= self.changelog.count():
2033 if l < 0 or l >= self.changelog.count():
2032 err(_("bad manifest link (%d) at revision %d") % (l, i))
2034 err(_("bad manifest link (%d) at revision %d") % (l, i))
2033
2035
2034 if n in neededmanifests:
2036 if n in neededmanifests:
2035 del neededmanifests[n]
2037 del neededmanifests[n]
2036
2038
2037 if n in seen:
2039 if n in seen:
2038 err(_("duplicate manifest at revision %d") % i)
2040 err(_("duplicate manifest at revision %d") % i)
2039
2041
2040 seen[n] = 1
2042 seen[n] = 1
2041
2043
2042 for p in self.manifest.parents(n):
2044 for p in self.manifest.parents(n):
2043 if p not in self.manifest.nodemap:
2045 if p not in self.manifest.nodemap:
2044 err(_("manifest %s has unknown parent %s") %
2046 err(_("manifest %s has unknown parent %s") %
2045 (short(n), short(p)))
2047 (short(n), short(p)))
2046
2048
2047 try:
2049 try:
2048 delta = mdiff.patchtext(self.manifest.delta(n))
2050 delta = mdiff.patchtext(self.manifest.delta(n))
2049 except KeyboardInterrupt:
2051 except KeyboardInterrupt:
2050 self.ui.warn(_("interrupted"))
2052 self.ui.warn(_("interrupted"))
2051 raise
2053 raise
2052 except Exception, inst:
2054 except Exception, inst:
2053 err(_("unpacking manifest %s: %s") % (short(n), inst))
2055 err(_("unpacking manifest %s: %s") % (short(n), inst))
2054 continue
2056 continue
2055
2057
2056 try:
2058 try:
2057 ff = [ l.split('\0') for l in delta.splitlines() ]
2059 ff = [ l.split('\0') for l in delta.splitlines() ]
2058 for f, fn in ff:
2060 for f, fn in ff:
2059 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
2061 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
2060 except (ValueError, TypeError), inst:
2062 except (ValueError, TypeError), inst:
2061 err(_("broken delta in manifest %s: %s") % (short(n), inst))
2063 err(_("broken delta in manifest %s: %s") % (short(n), inst))
2062
2064
2063 self.ui.status(_("crosschecking files in changesets and manifests\n"))
2065 self.ui.status(_("crosschecking files in changesets and manifests\n"))
2064
2066
2065 for m, c in neededmanifests.items():
2067 for m, c in neededmanifests.items():
2066 err(_("Changeset %s refers to unknown manifest %s") %
2068 err(_("Changeset %s refers to unknown manifest %s") %
2067 (short(m), short(c)))
2069 (short(m), short(c)))
2068 del neededmanifests
2070 del neededmanifests
2069
2071
2070 for f in filenodes:
2072 for f in filenodes:
2071 if f not in filelinkrevs:
2073 if f not in filelinkrevs:
2072 err(_("file %s in manifest but not in changesets") % f)
2074 err(_("file %s in manifest but not in changesets") % f)
2073
2075
2074 for f in filelinkrevs:
2076 for f in filelinkrevs:
2075 if f not in filenodes:
2077 if f not in filenodes:
2076 err(_("file %s in changeset but not in manifest") % f)
2078 err(_("file %s in changeset but not in manifest") % f)
2077
2079
2078 self.ui.status(_("checking files\n"))
2080 self.ui.status(_("checking files\n"))
2079 ff = filenodes.keys()
2081 ff = filenodes.keys()
2080 ff.sort()
2082 ff.sort()
2081 for f in ff:
2083 for f in ff:
2082 if f == "/dev/null":
2084 if f == "/dev/null":
2083 continue
2085 continue
2084 files += 1
2086 files += 1
2085 if not f:
2087 if not f:
2086 err(_("file without name in manifest %s") % short(n))
2088 err(_("file without name in manifest %s") % short(n))
2087 continue
2089 continue
2088 fl = self.file(f)
2090 fl = self.file(f)
2089 checkversion(fl, f)
2091 checkversion(fl, f)
2090 checksize(fl, f)
2092 checksize(fl, f)
2091
2093
2092 nodes = {nullid: 1}
2094 nodes = {nullid: 1}
2093 seen = {}
2095 seen = {}
2094 for i in range(fl.count()):
2096 for i in range(fl.count()):
2095 revisions += 1
2097 revisions += 1
2096 n = fl.node(i)
2098 n = fl.node(i)
2097
2099
2098 if n in seen:
2100 if n in seen:
2099 err(_("%s: duplicate revision %d") % (f, i))
2101 err(_("%s: duplicate revision %d") % (f, i))
2100 if n not in filenodes[f]:
2102 if n not in filenodes[f]:
2101 err(_("%s: %d:%s not in manifests") % (f, i, short(n)))
2103 err(_("%s: %d:%s not in manifests") % (f, i, short(n)))
2102 else:
2104 else:
2103 del filenodes[f][n]
2105 del filenodes[f][n]
2104
2106
2105 flr = fl.linkrev(n)
2107 flr = fl.linkrev(n)
2106 if flr not in filelinkrevs.get(f, []):
2108 if flr not in filelinkrevs.get(f, []):
2107 err(_("%s:%s points to unexpected changeset %d")
2109 err(_("%s:%s points to unexpected changeset %d")
2108 % (f, short(n), flr))
2110 % (f, short(n), flr))
2109 else:
2111 else:
2110 filelinkrevs[f].remove(flr)
2112 filelinkrevs[f].remove(flr)
2111
2113
2112 # verify contents
2114 # verify contents
2113 try:
2115 try:
2114 t = fl.read(n)
2116 t = fl.read(n)
2115 except KeyboardInterrupt:
2117 except KeyboardInterrupt:
2116 self.ui.warn(_("interrupted"))
2118 self.ui.warn(_("interrupted"))
2117 raise
2119 raise
2118 except Exception, inst:
2120 except Exception, inst:
2119 err(_("unpacking file %s %s: %s") % (f, short(n), inst))
2121 err(_("unpacking file %s %s: %s") % (f, short(n), inst))
2120
2122
2121 # verify parents
2123 # verify parents
2122 (p1, p2) = fl.parents(n)
2124 (p1, p2) = fl.parents(n)
2123 if p1 not in nodes:
2125 if p1 not in nodes:
2124 err(_("file %s:%s unknown parent 1 %s") %
2126 err(_("file %s:%s unknown parent 1 %s") %
2125 (f, short(n), short(p1)))
2127 (f, short(n), short(p1)))
2126 if p2 not in nodes:
2128 if p2 not in nodes:
2127 err(_("file %s:%s unknown parent 2 %s") %
2129 err(_("file %s:%s unknown parent 2 %s") %
2128 (f, short(n), short(p1)))
2130 (f, short(n), short(p1)))
2129 nodes[n] = 1
2131 nodes[n] = 1
2130
2132
2131 # cross-check
2133 # cross-check
2132 for node in filenodes[f]:
2134 for node in filenodes[f]:
2133 err(_("node %s in manifests not in %s") % (hex(node), f))
2135 err(_("node %s in manifests not in %s") % (hex(node), f))
2134
2136
2135 self.ui.status(_("%d files, %d changesets, %d total revisions\n") %
2137 self.ui.status(_("%d files, %d changesets, %d total revisions\n") %
2136 (files, changesets, revisions))
2138 (files, changesets, revisions))
2137
2139
2138 if warnings[0]:
2140 if warnings[0]:
2139 self.ui.warn(_("%d warnings encountered!\n") % warnings[0])
2141 self.ui.warn(_("%d warnings encountered!\n") % warnings[0])
2140 if errors[0]:
2142 if errors[0]:
2141 self.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
2143 self.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
2142 return 1
2144 return 1
2143
2145
2144 # used to avoid circular references so destructors work
2146 # used to avoid circular references so destructors work
2145 def aftertrans(base):
2147 def aftertrans(base):
2146 p = base
2148 p = base
2147 def a():
2149 def a():
2148 util.rename(os.path.join(p, "journal"), os.path.join(p, "undo"))
2150 util.rename(os.path.join(p, "journal"), os.path.join(p, "undo"))
2149 util.rename(os.path.join(p, "journal.dirstate"),
2151 util.rename(os.path.join(p, "journal.dirstate"),
2150 os.path.join(p, "undo.dirstate"))
2152 os.path.join(p, "undo.dirstate"))
2151 return a
2153 return a
2152
2154
@@ -1,181 +1,200 b''
1 # sshrepo.py - ssh repository proxy class for mercurial
1 # sshrepo.py - ssh repository proxy 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 from node import *
8 from node import *
9 from remoterepo import *
9 from remoterepo import *
10 from i18n import gettext as _
10 from i18n import gettext as _
11 from demandload import *
11 from demandload import *
12 demandload(globals(), "hg os re stat util")
12 demandload(globals(), "hg os re stat util")
13
13
14 class sshrepository(remoterepository):
14 class sshrepository(remoterepository):
15 def __init__(self, ui, path):
15 def __init__(self, ui, path, create=0):
16 self.url = path
16 self.url = path
17 self.ui = ui
17 self.ui = ui
18
18
19 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', path)
19 m = re.match(r'ssh://(([^@]+)@)?([^:/]+)(:(\d+))?(/(.*))?', path)
20 if not m:
20 if not m:
21 raise hg.RepoError(_("couldn't parse location %s") % path)
21 raise hg.RepoError(_("couldn't parse location %s") % path)
22
22
23 self.user = m.group(2)
23 self.user = m.group(2)
24 self.host = m.group(3)
24 self.host = m.group(3)
25 self.port = m.group(5)
25 self.port = m.group(5)
26 self.path = m.group(7) or "."
26 self.path = m.group(7) or "."
27
27
28 args = self.user and ("%s@%s" % (self.user, self.host)) or self.host
28 args = self.user and ("%s@%s" % (self.user, self.host)) or self.host
29 args = self.port and ("%s -p %s") % (args, self.port) or args
29 args = self.port and ("%s -p %s") % (args, self.port) or args
30
30
31 sshcmd = self.ui.config("ui", "ssh", "ssh")
31 sshcmd = self.ui.config("ui", "ssh", "ssh")
32 remotecmd = self.ui.config("ui", "remotecmd", "hg")
32 remotecmd = self.ui.config("ui", "remotecmd", "hg")
33
34 if create:
35 try:
36 self.validate_repo(ui, sshcmd, args, remotecmd)
37 return # the repo is good, nothing more to do
38 except hg.RepoError:
39 pass
40
41 cmd = '%s %s "%s init %s"'
42 cmd = cmd % (sshcmd, args, remotecmd, self.path)
43
44 ui.note('running %s\n' % cmd)
45 res = os.system(cmd)
46 if res != 0:
47 raise hg.RepoError(_("could not create remote repo"))
48
49 self.validate_repo(ui, sshcmd, args, remotecmd)
50
51 def validate_repo(self, ui, sshcmd, args, remotecmd):
33 cmd = '%s %s "%s -R %s serve --stdio"'
52 cmd = '%s %s "%s -R %s serve --stdio"'
34 cmd = cmd % (sshcmd, args, remotecmd, self.path)
53 cmd = cmd % (sshcmd, args, remotecmd, self.path)
35
54
36 ui.note('running %s\n' % cmd)
55 ui.note('running %s\n' % cmd)
37 self.pipeo, self.pipei, self.pipee = os.popen3(cmd, 'b')
56 self.pipeo, self.pipei, self.pipee = os.popen3(cmd, 'b')
38
57
39 # skip any noise generated by remote shell
58 # skip any noise generated by remote shell
40 self.do_cmd("hello")
59 self.do_cmd("hello")
41 r = self.do_cmd("between", pairs=("%s-%s" % ("0"*40, "0"*40)))
60 r = self.do_cmd("between", pairs=("%s-%s" % ("0"*40, "0"*40)))
42 lines = ["", "dummy"]
61 lines = ["", "dummy"]
43 max_noise = 500
62 max_noise = 500
44 while lines[-1] and max_noise:
63 while lines[-1] and max_noise:
45 l = r.readline()
64 l = r.readline()
46 self.readerr()
65 self.readerr()
47 if lines[-1] == "1\n" and l == "\n":
66 if lines[-1] == "1\n" and l == "\n":
48 break
67 break
49 if l:
68 if l:
50 ui.debug(_("remote: "), l)
69 ui.debug(_("remote: "), l)
51 lines.append(l)
70 lines.append(l)
52 max_noise -= 1
71 max_noise -= 1
53 else:
72 else:
54 raise hg.RepoError(_("no response from remote hg"))
73 raise hg.RepoError(_("no response from remote hg"))
55
74
56 self.capabilities = ()
75 self.capabilities = ()
57 lines.reverse()
76 lines.reverse()
58 for l in lines:
77 for l in lines:
59 if l.startswith("capabilities:"):
78 if l.startswith("capabilities:"):
60 self.capabilities = l[:-1].split(":")[1].split()
79 self.capabilities = l[:-1].split(":")[1].split()
61 break
80 break
62
81
63 def readerr(self):
82 def readerr(self):
64 while 1:
83 while 1:
65 size = util.fstat(self.pipee).st_size
84 size = util.fstat(self.pipee).st_size
66 if size == 0: break
85 if size == 0: break
67 l = self.pipee.readline()
86 l = self.pipee.readline()
68 if not l: break
87 if not l: break
69 self.ui.status(_("remote: "), l)
88 self.ui.status(_("remote: "), l)
70
89
71 def __del__(self):
90 def __del__(self):
72 try:
91 try:
73 self.pipeo.close()
92 self.pipeo.close()
74 self.pipei.close()
93 self.pipei.close()
75 # read the error descriptor until EOF
94 # read the error descriptor until EOF
76 for l in self.pipee:
95 for l in self.pipee:
77 self.ui.status(_("remote: "), l)
96 self.ui.status(_("remote: "), l)
78 self.pipee.close()
97 self.pipee.close()
79 except:
98 except:
80 pass
99 pass
81
100
82 def do_cmd(self, cmd, **args):
101 def do_cmd(self, cmd, **args):
83 self.ui.debug(_("sending %s command\n") % cmd)
102 self.ui.debug(_("sending %s command\n") % cmd)
84 self.pipeo.write("%s\n" % cmd)
103 self.pipeo.write("%s\n" % cmd)
85 for k, v in args.items():
104 for k, v in args.items():
86 self.pipeo.write("%s %d\n" % (k, len(v)))
105 self.pipeo.write("%s %d\n" % (k, len(v)))
87 self.pipeo.write(v)
106 self.pipeo.write(v)
88 self.pipeo.flush()
107 self.pipeo.flush()
89
108
90 return self.pipei
109 return self.pipei
91
110
92 def call(self, cmd, **args):
111 def call(self, cmd, **args):
93 r = self.do_cmd(cmd, **args)
112 r = self.do_cmd(cmd, **args)
94 l = r.readline()
113 l = r.readline()
95 self.readerr()
114 self.readerr()
96 try:
115 try:
97 l = int(l)
116 l = int(l)
98 except:
117 except:
99 raise hg.RepoError(_("unexpected response '%s'") % l)
118 raise hg.RepoError(_("unexpected response '%s'") % l)
100 return r.read(l)
119 return r.read(l)
101
120
102 def lock(self):
121 def lock(self):
103 self.call("lock")
122 self.call("lock")
104 return remotelock(self)
123 return remotelock(self)
105
124
106 def unlock(self):
125 def unlock(self):
107 self.call("unlock")
126 self.call("unlock")
108
127
109 def heads(self):
128 def heads(self):
110 d = self.call("heads")
129 d = self.call("heads")
111 try:
130 try:
112 return map(bin, d[:-1].split(" "))
131 return map(bin, d[:-1].split(" "))
113 except:
132 except:
114 raise hg.RepoError(_("unexpected response '%s'") % (d[:400] + "..."))
133 raise hg.RepoError(_("unexpected response '%s'") % (d[:400] + "..."))
115
134
116 def branches(self, nodes):
135 def branches(self, nodes):
117 n = " ".join(map(hex, nodes))
136 n = " ".join(map(hex, nodes))
118 d = self.call("branches", nodes=n)
137 d = self.call("branches", nodes=n)
119 try:
138 try:
120 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
139 br = [ tuple(map(bin, b.split(" "))) for b in d.splitlines() ]
121 return br
140 return br
122 except:
141 except:
123 raise hg.RepoError(_("unexpected response '%s'") % (d[:400] + "..."))
142 raise hg.RepoError(_("unexpected response '%s'") % (d[:400] + "..."))
124
143
125 def between(self, pairs):
144 def between(self, pairs):
126 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
145 n = "\n".join(["-".join(map(hex, p)) for p in pairs])
127 d = self.call("between", pairs=n)
146 d = self.call("between", pairs=n)
128 try:
147 try:
129 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
148 p = [ l and map(bin, l.split(" ")) or [] for l in d.splitlines() ]
130 return p
149 return p
131 except:
150 except:
132 raise hg.RepoError(_("unexpected response '%s'") % (d[:400] + "..."))
151 raise hg.RepoError(_("unexpected response '%s'") % (d[:400] + "..."))
133
152
134 def changegroup(self, nodes, kind):
153 def changegroup(self, nodes, kind):
135 n = " ".join(map(hex, nodes))
154 n = " ".join(map(hex, nodes))
136 return self.do_cmd("changegroup", roots=n)
155 return self.do_cmd("changegroup", roots=n)
137
156
138 def unbundle(self, cg, heads, source):
157 def unbundle(self, cg, heads, source):
139 d = self.call("unbundle", heads=' '.join(map(hex, heads)))
158 d = self.call("unbundle", heads=' '.join(map(hex, heads)))
140 if d:
159 if d:
141 raise hg.RepoError(_("push refused: %s") % d)
160 raise hg.RepoError(_("push refused: %s") % d)
142
161
143 while 1:
162 while 1:
144 d = cg.read(4096)
163 d = cg.read(4096)
145 if not d: break
164 if not d: break
146 self.pipeo.write(str(len(d)) + '\n')
165 self.pipeo.write(str(len(d)) + '\n')
147 self.pipeo.write(d)
166 self.pipeo.write(d)
148 self.readerr()
167 self.readerr()
149
168
150 self.pipeo.write('0\n')
169 self.pipeo.write('0\n')
151 self.pipeo.flush()
170 self.pipeo.flush()
152
171
153 self.readerr()
172 self.readerr()
154 d = self.pipei.readline()
173 d = self.pipei.readline()
155 if d != '\n':
174 if d != '\n':
156 return 1
175 return 1
157
176
158 l = int(self.pipei.readline())
177 l = int(self.pipei.readline())
159 r = self.pipei.read(l)
178 r = self.pipei.read(l)
160 if not r:
179 if not r:
161 return 1
180 return 1
162 return int(r)
181 return int(r)
163
182
164 def addchangegroup(self, cg, source):
183 def addchangegroup(self, cg, source):
165 d = self.call("addchangegroup")
184 d = self.call("addchangegroup")
166 if d:
185 if d:
167 raise hg.RepoError(_("push refused: %s") % d)
186 raise hg.RepoError(_("push refused: %s") % d)
168 while 1:
187 while 1:
169 d = cg.read(4096)
188 d = cg.read(4096)
170 if not d: break
189 if not d: break
171 self.pipeo.write(d)
190 self.pipeo.write(d)
172 self.readerr()
191 self.readerr()
173
192
174 self.pipeo.flush()
193 self.pipeo.flush()
175
194
176 self.readerr()
195 self.readerr()
177 l = int(self.pipei.readline())
196 l = int(self.pipei.readline())
178 r = self.pipei.read(l)
197 r = self.pipei.read(l)
179 if not r:
198 if not r:
180 return 1
199 return 1
181 return int(r)
200 return int(r)
General Comments 0
You need to be logged in to leave comments. Login now