##// END OF EJS Templates
abort when merging two heads and repository has local changes
Benoit Boissinot -
r1581:db10b711 default
parent child Browse files
Show More
@@ -1,2708 +1,2710 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")
12 demandload(globals(), "fancyopts ui hg util lock revlog")
13 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
13 demandload(globals(), "fnmatch hgweb mdiff random signal time traceback")
14 demandload(globals(), "errno socket version struct atexit sets bz2")
14 demandload(globals(), "errno socket version struct atexit sets bz2")
15
15
16 class UnknownCommand(Exception):
16 class UnknownCommand(Exception):
17 """Exception raised if command is not in the command table."""
17 """Exception raised if command is not in the command table."""
18 class AmbiguousCommand(Exception):
18 class AmbiguousCommand(Exception):
19 """Exception raised if command shortcut matches more than one command."""
19 """Exception raised if command shortcut matches more than one command."""
20
20
21 def filterfiles(filters, files):
21 def filterfiles(filters, files):
22 l = [x for x in files if x in filters]
22 l = [x for x in files if x in filters]
23
23
24 for t in filters:
24 for t in filters:
25 if t and t[-1] != "/":
25 if t and t[-1] != "/":
26 t += "/"
26 t += "/"
27 l += [x for x in files if x.startswith(t)]
27 l += [x for x in files if x.startswith(t)]
28 return l
28 return l
29
29
30 def relpath(repo, args):
30 def relpath(repo, args):
31 cwd = repo.getcwd()
31 cwd = repo.getcwd()
32 if cwd:
32 if cwd:
33 return [util.normpath(os.path.join(cwd, x)) for x in args]
33 return [util.normpath(os.path.join(cwd, x)) for x in args]
34 return args
34 return args
35
35
36 def matchpats(repo, pats=[], opts={}, head=''):
36 def matchpats(repo, pats=[], opts={}, head=''):
37 cwd = repo.getcwd()
37 cwd = repo.getcwd()
38 if not pats and cwd:
38 if not pats and cwd:
39 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
39 opts['include'] = [os.path.join(cwd, i) for i in opts['include']]
40 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
40 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
41 cwd = ''
41 cwd = ''
42 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
42 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
43 opts.get('exclude'), head) + (cwd,)
43 opts.get('exclude'), head) + (cwd,)
44
44
45 def makewalk(repo, pats, opts, head=''):
45 def makewalk(repo, pats, opts, head=''):
46 files, matchfn, anypats, cwd = matchpats(repo, pats, opts, head)
46 files, matchfn, anypats, cwd = matchpats(repo, pats, opts, head)
47 exact = dict(zip(files, files))
47 exact = dict(zip(files, files))
48 def walk():
48 def walk():
49 for src, fn in repo.walk(files=files, match=matchfn):
49 for src, fn in repo.walk(files=files, match=matchfn):
50 yield src, fn, util.pathto(cwd, fn), fn in exact
50 yield src, fn, util.pathto(cwd, fn), fn in exact
51 return files, matchfn, walk()
51 return files, matchfn, walk()
52
52
53 def walk(repo, pats, opts, head=''):
53 def walk(repo, pats, opts, head=''):
54 files, matchfn, results = makewalk(repo, pats, opts, head)
54 files, matchfn, results = makewalk(repo, pats, opts, head)
55 for r in results:
55 for r in results:
56 yield r
56 yield r
57
57
58 def walkchangerevs(ui, repo, pats, opts):
58 def walkchangerevs(ui, repo, pats, opts):
59 '''Iterate over files and the revs they changed in.
59 '''Iterate over files and the revs they changed in.
60
60
61 Callers most commonly need to iterate backwards over the history
61 Callers most commonly need to iterate backwards over the history
62 it is interested in. Doing so has awful (quadratic-looking)
62 it is interested in. Doing so has awful (quadratic-looking)
63 performance, so we use iterators in a "windowed" way.
63 performance, so we use iterators in a "windowed" way.
64
64
65 We walk a window of revisions in the desired order. Within the
65 We walk a window of revisions in the desired order. Within the
66 window, we first walk forwards to gather data, then in the desired
66 window, we first walk forwards to gather data, then in the desired
67 order (usually backwards) to display it.
67 order (usually backwards) to display it.
68
68
69 This function returns an (iterator, getchange, matchfn) tuple. The
69 This function returns an (iterator, getchange, matchfn) tuple. The
70 getchange function returns the changelog entry for a numeric
70 getchange function returns the changelog entry for a numeric
71 revision. The iterator yields 3-tuples. They will be of one of
71 revision. The iterator yields 3-tuples. They will be of one of
72 the following forms:
72 the following forms:
73
73
74 "window", incrementing, lastrev: stepping through a window,
74 "window", incrementing, lastrev: stepping through a window,
75 positive if walking forwards through revs, last rev in the
75 positive if walking forwards through revs, last rev in the
76 sequence iterated over - use to reset state for the current window
76 sequence iterated over - use to reset state for the current window
77
77
78 "add", rev, fns: out-of-order traversal of the given file names
78 "add", rev, fns: out-of-order traversal of the given file names
79 fns, which changed during revision rev - use to gather data for
79 fns, which changed during revision rev - use to gather data for
80 possible display
80 possible display
81
81
82 "iter", rev, None: in-order traversal of the revs earlier iterated
82 "iter", rev, None: in-order traversal of the revs earlier iterated
83 over with "add" - use to display data'''
83 over with "add" - use to display data'''
84
84
85 files, matchfn, anypats, cwd = matchpats(repo, pats, opts)
85 files, matchfn, anypats, cwd = matchpats(repo, pats, opts)
86
86
87 if repo.changelog.count() == 0:
87 if repo.changelog.count() == 0:
88 return [], False, matchfn
88 return [], False, matchfn
89
89
90 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
90 revs = map(int, revrange(ui, repo, opts['rev'] or ['tip:0']))
91 wanted = {}
91 wanted = {}
92 slowpath = anypats
92 slowpath = anypats
93 window = 300
93 window = 300
94 fncache = {}
94 fncache = {}
95
95
96 chcache = {}
96 chcache = {}
97 def getchange(rev):
97 def getchange(rev):
98 ch = chcache.get(rev)
98 ch = chcache.get(rev)
99 if ch is None:
99 if ch is None:
100 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
100 chcache[rev] = ch = repo.changelog.read(repo.lookup(str(rev)))
101 return ch
101 return ch
102
102
103 if not slowpath and not files:
103 if not slowpath and not files:
104 # No files, no patterns. Display all revs.
104 # No files, no patterns. Display all revs.
105 wanted = dict(zip(revs, revs))
105 wanted = dict(zip(revs, revs))
106 if not slowpath:
106 if not slowpath:
107 # Only files, no patterns. Check the history of each file.
107 # Only files, no patterns. Check the history of each file.
108 def filerevgen(filelog):
108 def filerevgen(filelog):
109 for i in xrange(filelog.count() - 1, -1, -window):
109 for i in xrange(filelog.count() - 1, -1, -window):
110 revs = []
110 revs = []
111 for j in xrange(max(0, i - window), i + 1):
111 for j in xrange(max(0, i - window), i + 1):
112 revs.append(filelog.linkrev(filelog.node(j)))
112 revs.append(filelog.linkrev(filelog.node(j)))
113 revs.reverse()
113 revs.reverse()
114 for rev in revs:
114 for rev in revs:
115 yield rev
115 yield rev
116
116
117 minrev, maxrev = min(revs), max(revs)
117 minrev, maxrev = min(revs), max(revs)
118 for file in files:
118 for file in files:
119 filelog = repo.file(file)
119 filelog = repo.file(file)
120 # A zero count may be a directory or deleted file, so
120 # A zero count may be a directory or deleted file, so
121 # try to find matching entries on the slow path.
121 # try to find matching entries on the slow path.
122 if filelog.count() == 0:
122 if filelog.count() == 0:
123 slowpath = True
123 slowpath = True
124 break
124 break
125 for rev in filerevgen(filelog):
125 for rev in filerevgen(filelog):
126 if rev <= maxrev:
126 if rev <= maxrev:
127 if rev < minrev:
127 if rev < minrev:
128 break
128 break
129 fncache.setdefault(rev, [])
129 fncache.setdefault(rev, [])
130 fncache[rev].append(file)
130 fncache[rev].append(file)
131 wanted[rev] = 1
131 wanted[rev] = 1
132 if slowpath:
132 if slowpath:
133 # The slow path checks files modified in every changeset.
133 # The slow path checks files modified in every changeset.
134 def changerevgen():
134 def changerevgen():
135 for i in xrange(repo.changelog.count() - 1, -1, -window):
135 for i in xrange(repo.changelog.count() - 1, -1, -window):
136 for j in xrange(max(0, i - window), i + 1):
136 for j in xrange(max(0, i - window), i + 1):
137 yield j, getchange(j)[3]
137 yield j, getchange(j)[3]
138
138
139 for rev, changefiles in changerevgen():
139 for rev, changefiles in changerevgen():
140 matches = filter(matchfn, changefiles)
140 matches = filter(matchfn, changefiles)
141 if matches:
141 if matches:
142 fncache[rev] = matches
142 fncache[rev] = matches
143 wanted[rev] = 1
143 wanted[rev] = 1
144
144
145 def iterate():
145 def iterate():
146 for i in xrange(0, len(revs), window):
146 for i in xrange(0, len(revs), window):
147 yield 'window', revs[0] < revs[-1], revs[-1]
147 yield 'window', revs[0] < revs[-1], revs[-1]
148 nrevs = [rev for rev in revs[i:min(i+window, len(revs))]
148 nrevs = [rev for rev in revs[i:min(i+window, len(revs))]
149 if rev in wanted]
149 if rev in wanted]
150 srevs = list(nrevs)
150 srevs = list(nrevs)
151 srevs.sort()
151 srevs.sort()
152 for rev in srevs:
152 for rev in srevs:
153 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
153 fns = fncache.get(rev) or filter(matchfn, getchange(rev)[3])
154 yield 'add', rev, fns
154 yield 'add', rev, fns
155 for rev in nrevs:
155 for rev in nrevs:
156 yield 'iter', rev, None
156 yield 'iter', rev, None
157 return iterate(), getchange, matchfn
157 return iterate(), getchange, matchfn
158
158
159 revrangesep = ':'
159 revrangesep = ':'
160
160
161 def revrange(ui, repo, revs, revlog=None):
161 def revrange(ui, repo, revs, revlog=None):
162 """Yield revision as strings from a list of revision specifications."""
162 """Yield revision as strings from a list of revision specifications."""
163 if revlog is None:
163 if revlog is None:
164 revlog = repo.changelog
164 revlog = repo.changelog
165 revcount = revlog.count()
165 revcount = revlog.count()
166 def fix(val, defval):
166 def fix(val, defval):
167 if not val:
167 if not val:
168 return defval
168 return defval
169 try:
169 try:
170 num = int(val)
170 num = int(val)
171 if str(num) != val:
171 if str(num) != val:
172 raise ValueError
172 raise ValueError
173 if num < 0: num += revcount
173 if num < 0: num += revcount
174 if num < 0: num = 0
174 if num < 0: num = 0
175 elif num >= revcount:
175 elif num >= revcount:
176 raise ValueError
176 raise ValueError
177 except ValueError:
177 except ValueError:
178 try:
178 try:
179 num = repo.changelog.rev(repo.lookup(val))
179 num = repo.changelog.rev(repo.lookup(val))
180 except KeyError:
180 except KeyError:
181 try:
181 try:
182 num = revlog.rev(revlog.lookup(val))
182 num = revlog.rev(revlog.lookup(val))
183 except KeyError:
183 except KeyError:
184 raise util.Abort(_('invalid revision identifier %s'), val)
184 raise util.Abort(_('invalid revision identifier %s'), val)
185 return num
185 return num
186 seen = {}
186 seen = {}
187 for spec in revs:
187 for spec in revs:
188 if spec.find(revrangesep) >= 0:
188 if spec.find(revrangesep) >= 0:
189 start, end = spec.split(revrangesep, 1)
189 start, end = spec.split(revrangesep, 1)
190 start = fix(start, 0)
190 start = fix(start, 0)
191 end = fix(end, revcount - 1)
191 end = fix(end, revcount - 1)
192 step = start > end and -1 or 1
192 step = start > end and -1 or 1
193 for rev in xrange(start, end+step, step):
193 for rev in xrange(start, end+step, step):
194 if rev in seen: continue
194 if rev in seen: continue
195 seen[rev] = 1
195 seen[rev] = 1
196 yield str(rev)
196 yield str(rev)
197 else:
197 else:
198 rev = fix(spec, None)
198 rev = fix(spec, None)
199 if rev in seen: continue
199 if rev in seen: continue
200 seen[rev] = 1
200 seen[rev] = 1
201 yield str(rev)
201 yield str(rev)
202
202
203 def make_filename(repo, r, pat, node=None,
203 def make_filename(repo, r, pat, node=None,
204 total=None, seqno=None, revwidth=None, pathname=None):
204 total=None, seqno=None, revwidth=None, pathname=None):
205 node_expander = {
205 node_expander = {
206 'H': lambda: hex(node),
206 'H': lambda: hex(node),
207 'R': lambda: str(r.rev(node)),
207 'R': lambda: str(r.rev(node)),
208 'h': lambda: short(node),
208 'h': lambda: short(node),
209 }
209 }
210 expander = {
210 expander = {
211 '%': lambda: '%',
211 '%': lambda: '%',
212 'b': lambda: os.path.basename(repo.root),
212 'b': lambda: os.path.basename(repo.root),
213 }
213 }
214
214
215 try:
215 try:
216 if node:
216 if node:
217 expander.update(node_expander)
217 expander.update(node_expander)
218 if node and revwidth is not None:
218 if node and revwidth is not None:
219 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
219 expander['r'] = lambda: str(r.rev(node)).zfill(revwidth)
220 if total is not None:
220 if total is not None:
221 expander['N'] = lambda: str(total)
221 expander['N'] = lambda: str(total)
222 if seqno is not None:
222 if seqno is not None:
223 expander['n'] = lambda: str(seqno)
223 expander['n'] = lambda: str(seqno)
224 if total is not None and seqno is not None:
224 if total is not None and seqno is not None:
225 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
225 expander['n'] = lambda:str(seqno).zfill(len(str(total)))
226 if pathname is not None:
226 if pathname is not None:
227 expander['s'] = lambda: os.path.basename(pathname)
227 expander['s'] = lambda: os.path.basename(pathname)
228 expander['d'] = lambda: os.path.dirname(pathname) or '.'
228 expander['d'] = lambda: os.path.dirname(pathname) or '.'
229 expander['p'] = lambda: pathname
229 expander['p'] = lambda: pathname
230
230
231 newname = []
231 newname = []
232 patlen = len(pat)
232 patlen = len(pat)
233 i = 0
233 i = 0
234 while i < patlen:
234 while i < patlen:
235 c = pat[i]
235 c = pat[i]
236 if c == '%':
236 if c == '%':
237 i += 1
237 i += 1
238 c = pat[i]
238 c = pat[i]
239 c = expander[c]()
239 c = expander[c]()
240 newname.append(c)
240 newname.append(c)
241 i += 1
241 i += 1
242 return ''.join(newname)
242 return ''.join(newname)
243 except KeyError, inst:
243 except KeyError, inst:
244 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
244 raise util.Abort(_("invalid format spec '%%%s' in output file name"),
245 inst.args[0])
245 inst.args[0])
246
246
247 def make_file(repo, r, pat, node=None,
247 def make_file(repo, r, pat, node=None,
248 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
248 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
249 if not pat or pat == '-':
249 if not pat or pat == '-':
250 return 'w' in mode and sys.stdout or sys.stdin
250 return 'w' in mode and sys.stdout or sys.stdin
251 if hasattr(pat, 'write') and 'w' in mode:
251 if hasattr(pat, 'write') and 'w' in mode:
252 return pat
252 return pat
253 if hasattr(pat, 'read') and 'r' in mode:
253 if hasattr(pat, 'read') and 'r' in mode:
254 return pat
254 return pat
255 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
255 return open(make_filename(repo, r, pat, node, total, seqno, revwidth,
256 pathname),
256 pathname),
257 mode)
257 mode)
258
258
259 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
259 def dodiff(fp, ui, repo, node1, node2, files=None, match=util.always,
260 changes=None, text=False):
260 changes=None, text=False):
261 if not changes:
261 if not changes:
262 (c, a, d, u) = repo.changes(node1, node2, files, match=match)
262 (c, a, d, u) = repo.changes(node1, node2, files, match=match)
263 else:
263 else:
264 (c, a, d, u) = changes
264 (c, a, d, u) = changes
265 if files:
265 if files:
266 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
266 c, a, d = map(lambda x: filterfiles(files, x), (c, a, d))
267
267
268 if not c and not a and not d:
268 if not c and not a and not d:
269 return
269 return
270
270
271 if node2:
271 if node2:
272 change = repo.changelog.read(node2)
272 change = repo.changelog.read(node2)
273 mmap2 = repo.manifest.read(change[0])
273 mmap2 = repo.manifest.read(change[0])
274 date2 = util.datestr(change[2])
274 date2 = util.datestr(change[2])
275 def read(f):
275 def read(f):
276 return repo.file(f).read(mmap2[f])
276 return repo.file(f).read(mmap2[f])
277 else:
277 else:
278 date2 = util.datestr()
278 date2 = util.datestr()
279 if not node1:
279 if not node1:
280 node1 = repo.dirstate.parents()[0]
280 node1 = repo.dirstate.parents()[0]
281 def read(f):
281 def read(f):
282 return repo.wfile(f).read()
282 return repo.wfile(f).read()
283
283
284 if ui.quiet:
284 if ui.quiet:
285 r = None
285 r = None
286 else:
286 else:
287 hexfunc = ui.verbose and hex or short
287 hexfunc = ui.verbose and hex or short
288 r = [hexfunc(node) for node in [node1, node2] if node]
288 r = [hexfunc(node) for node in [node1, node2] if node]
289
289
290 change = repo.changelog.read(node1)
290 change = repo.changelog.read(node1)
291 mmap = repo.manifest.read(change[0])
291 mmap = repo.manifest.read(change[0])
292 date1 = util.datestr(change[2])
292 date1 = util.datestr(change[2])
293
293
294 for f in c:
294 for f in c:
295 to = None
295 to = None
296 if f in mmap:
296 if f in mmap:
297 to = repo.file(f).read(mmap[f])
297 to = repo.file(f).read(mmap[f])
298 tn = read(f)
298 tn = read(f)
299 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
299 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
300 for f in a:
300 for f in a:
301 to = None
301 to = None
302 tn = read(f)
302 tn = read(f)
303 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
303 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
304 for f in d:
304 for f in d:
305 to = repo.file(f).read(mmap[f])
305 to = repo.file(f).read(mmap[f])
306 tn = None
306 tn = None
307 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
307 fp.write(mdiff.unidiff(to, date1, tn, date2, f, r, text=text))
308
308
309 def trimuser(ui, name, rev, revcache):
309 def trimuser(ui, name, rev, revcache):
310 """trim the name of the user who committed a change"""
310 """trim the name of the user who committed a change"""
311 user = revcache.get(rev)
311 user = revcache.get(rev)
312 if user is None:
312 if user is None:
313 user = revcache[rev] = ui.shortuser(name)
313 user = revcache[rev] = ui.shortuser(name)
314 return user
314 return user
315
315
316 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
316 def show_changeset(ui, repo, rev=0, changenode=None, brinfo=None):
317 """show a single changeset or file revision"""
317 """show a single changeset or file revision"""
318 log = repo.changelog
318 log = repo.changelog
319 if changenode is None:
319 if changenode is None:
320 changenode = log.node(rev)
320 changenode = log.node(rev)
321 elif not rev:
321 elif not rev:
322 rev = log.rev(changenode)
322 rev = log.rev(changenode)
323
323
324 if ui.quiet:
324 if ui.quiet:
325 ui.write("%d:%s\n" % (rev, short(changenode)))
325 ui.write("%d:%s\n" % (rev, short(changenode)))
326 return
326 return
327
327
328 changes = log.read(changenode)
328 changes = log.read(changenode)
329 date = util.datestr(changes[2])
329 date = util.datestr(changes[2])
330
330
331 parents = [(log.rev(p), ui.verbose and hex(p) or short(p))
331 parents = [(log.rev(p), ui.verbose and hex(p) or short(p))
332 for p in log.parents(changenode)
332 for p in log.parents(changenode)
333 if ui.debugflag or p != nullid]
333 if ui.debugflag or p != nullid]
334 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
334 if not ui.debugflag and len(parents) == 1 and parents[0][0] == rev-1:
335 parents = []
335 parents = []
336
336
337 if ui.verbose:
337 if ui.verbose:
338 ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
338 ui.write(_("changeset: %d:%s\n") % (rev, hex(changenode)))
339 else:
339 else:
340 ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
340 ui.write(_("changeset: %d:%s\n") % (rev, short(changenode)))
341
341
342 for tag in repo.nodetags(changenode):
342 for tag in repo.nodetags(changenode):
343 ui.status(_("tag: %s\n") % tag)
343 ui.status(_("tag: %s\n") % tag)
344 for parent in parents:
344 for parent in parents:
345 ui.write(_("parent: %d:%s\n") % parent)
345 ui.write(_("parent: %d:%s\n") % parent)
346
346
347 if brinfo and changenode in brinfo:
347 if brinfo and changenode in brinfo:
348 br = brinfo[changenode]
348 br = brinfo[changenode]
349 ui.write(_("branch: %s\n") % " ".join(br))
349 ui.write(_("branch: %s\n") % " ".join(br))
350
350
351 ui.debug(_("manifest: %d:%s\n") % (repo.manifest.rev(changes[0]),
351 ui.debug(_("manifest: %d:%s\n") % (repo.manifest.rev(changes[0]),
352 hex(changes[0])))
352 hex(changes[0])))
353 ui.status(_("user: %s\n") % changes[1])
353 ui.status(_("user: %s\n") % changes[1])
354 ui.status(_("date: %s\n") % date)
354 ui.status(_("date: %s\n") % date)
355
355
356 if ui.debugflag:
356 if ui.debugflag:
357 files = repo.changes(log.parents(changenode)[0], changenode)
357 files = repo.changes(log.parents(changenode)[0], changenode)
358 for key, value in zip([_("files:"), _("files+:"), _("files-:")], files):
358 for key, value in zip([_("files:"), _("files+:"), _("files-:")], files):
359 if value:
359 if value:
360 ui.note("%-12s %s\n" % (key, " ".join(value)))
360 ui.note("%-12s %s\n" % (key, " ".join(value)))
361 else:
361 else:
362 ui.note(_("files: %s\n") % " ".join(changes[3]))
362 ui.note(_("files: %s\n") % " ".join(changes[3]))
363
363
364 description = changes[4].strip()
364 description = changes[4].strip()
365 if description:
365 if description:
366 if ui.verbose:
366 if ui.verbose:
367 ui.status(_("description:\n"))
367 ui.status(_("description:\n"))
368 ui.status(description)
368 ui.status(description)
369 ui.status("\n\n")
369 ui.status("\n\n")
370 else:
370 else:
371 ui.status(_("summary: %s\n") % description.splitlines()[0])
371 ui.status(_("summary: %s\n") % description.splitlines()[0])
372 ui.status("\n")
372 ui.status("\n")
373
373
374 def show_version(ui):
374 def show_version(ui):
375 """output version and copyright information"""
375 """output version and copyright information"""
376 ui.write(_("Mercurial Distributed SCM (version %s)\n")
376 ui.write(_("Mercurial Distributed SCM (version %s)\n")
377 % version.get_version())
377 % version.get_version())
378 ui.status(_(
378 ui.status(_(
379 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
379 "\nCopyright (C) 2005 Matt Mackall <mpm@selenic.com>\n"
380 "This is free software; see the source for copying conditions. "
380 "This is free software; see the source for copying conditions. "
381 "There is NO\nwarranty; "
381 "There is NO\nwarranty; "
382 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
382 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
383 ))
383 ))
384
384
385 def help_(ui, cmd=None, with_version=False):
385 def help_(ui, cmd=None, with_version=False):
386 """show help for a given command or all commands"""
386 """show help for a given command or all commands"""
387 option_lists = []
387 option_lists = []
388 if cmd and cmd != 'shortlist':
388 if cmd and cmd != 'shortlist':
389 if with_version:
389 if with_version:
390 show_version(ui)
390 show_version(ui)
391 ui.write('\n')
391 ui.write('\n')
392 aliases, i = find(cmd)
392 aliases, i = find(cmd)
393 # synopsis
393 # synopsis
394 ui.write("%s\n\n" % i[2])
394 ui.write("%s\n\n" % i[2])
395
395
396 # description
396 # description
397 doc = i[0].__doc__
397 doc = i[0].__doc__
398 if ui.quiet:
398 if ui.quiet:
399 doc = doc.splitlines(0)[0]
399 doc = doc.splitlines(0)[0]
400 ui.write("%s\n" % doc.rstrip())
400 ui.write("%s\n" % doc.rstrip())
401
401
402 if not ui.quiet:
402 if not ui.quiet:
403 # aliases
403 # aliases
404 if len(aliases) > 1:
404 if len(aliases) > 1:
405 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
405 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
406
406
407 # options
407 # options
408 if i[1]:
408 if i[1]:
409 option_lists.append(("options", i[1]))
409 option_lists.append(("options", i[1]))
410
410
411 else:
411 else:
412 # program name
412 # program name
413 if ui.verbose or with_version:
413 if ui.verbose or with_version:
414 show_version(ui)
414 show_version(ui)
415 else:
415 else:
416 ui.status(_("Mercurial Distributed SCM\n"))
416 ui.status(_("Mercurial Distributed SCM\n"))
417 ui.status('\n')
417 ui.status('\n')
418
418
419 # list of commands
419 # list of commands
420 if cmd == "shortlist":
420 if cmd == "shortlist":
421 ui.status(_('basic commands (use "hg help" '
421 ui.status(_('basic commands (use "hg help" '
422 'for the full list or option "-v" for details):\n\n'))
422 'for the full list or option "-v" for details):\n\n'))
423 elif ui.verbose:
423 elif ui.verbose:
424 ui.status(_('list of commands:\n\n'))
424 ui.status(_('list of commands:\n\n'))
425 else:
425 else:
426 ui.status(_('list of commands (use "hg help -v" '
426 ui.status(_('list of commands (use "hg help -v" '
427 'to show aliases and global options):\n\n'))
427 'to show aliases and global options):\n\n'))
428
428
429 h = {}
429 h = {}
430 cmds = {}
430 cmds = {}
431 for c, e in table.items():
431 for c, e in table.items():
432 f = c.split("|")[0]
432 f = c.split("|")[0]
433 if cmd == "shortlist" and not f.startswith("^"):
433 if cmd == "shortlist" and not f.startswith("^"):
434 continue
434 continue
435 f = f.lstrip("^")
435 f = f.lstrip("^")
436 if not ui.debugflag and f.startswith("debug"):
436 if not ui.debugflag and f.startswith("debug"):
437 continue
437 continue
438 d = ""
438 d = ""
439 if e[0].__doc__:
439 if e[0].__doc__:
440 d = e[0].__doc__.splitlines(0)[0].rstrip()
440 d = e[0].__doc__.splitlines(0)[0].rstrip()
441 h[f] = d
441 h[f] = d
442 cmds[f]=c.lstrip("^")
442 cmds[f]=c.lstrip("^")
443
443
444 fns = h.keys()
444 fns = h.keys()
445 fns.sort()
445 fns.sort()
446 m = max(map(len, fns))
446 m = max(map(len, fns))
447 for f in fns:
447 for f in fns:
448 if ui.verbose:
448 if ui.verbose:
449 commands = cmds[f].replace("|",", ")
449 commands = cmds[f].replace("|",", ")
450 ui.write(" %s:\n %s\n"%(commands,h[f]))
450 ui.write(" %s:\n %s\n"%(commands,h[f]))
451 else:
451 else:
452 ui.write(' %-*s %s\n' % (m, f, h[f]))
452 ui.write(' %-*s %s\n' % (m, f, h[f]))
453
453
454 # global options
454 # global options
455 if ui.verbose:
455 if ui.verbose:
456 option_lists.append(("global options", globalopts))
456 option_lists.append(("global options", globalopts))
457
457
458 # list all option lists
458 # list all option lists
459 opt_output = []
459 opt_output = []
460 for title, options in option_lists:
460 for title, options in option_lists:
461 opt_output.append(("\n%s:\n" % title, None))
461 opt_output.append(("\n%s:\n" % title, None))
462 for shortopt, longopt, default, desc in options:
462 for shortopt, longopt, default, desc in options:
463 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
463 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
464 longopt and " --%s" % longopt),
464 longopt and " --%s" % longopt),
465 "%s%s" % (desc,
465 "%s%s" % (desc,
466 default and _(" (default: %s)") % default
466 default and _(" (default: %s)") % default
467 or "")))
467 or "")))
468
468
469 if opt_output:
469 if opt_output:
470 opts_len = max([len(line[0]) for line in opt_output if line[1]])
470 opts_len = max([len(line[0]) for line in opt_output if line[1]])
471 for first, second in opt_output:
471 for first, second in opt_output:
472 if second:
472 if second:
473 ui.write(" %-*s %s\n" % (opts_len, first, second))
473 ui.write(" %-*s %s\n" % (opts_len, first, second))
474 else:
474 else:
475 ui.write("%s\n" % first)
475 ui.write("%s\n" % first)
476
476
477 # Commands start here, listed alphabetically
477 # Commands start here, listed alphabetically
478
478
479 def add(ui, repo, *pats, **opts):
479 def add(ui, repo, *pats, **opts):
480 """add the specified files on the next commit
480 """add the specified files on the next commit
481
481
482 Schedule files to be version controlled and added to the repository.
482 Schedule files to be version controlled and added to the repository.
483
483
484 The files will be added to the repository at the next commit.
484 The files will be added to the repository at the next commit.
485
485
486 If no names are given, add all files in the repository.
486 If no names are given, add all files in the repository.
487 """
487 """
488
488
489 names = []
489 names = []
490 for src, abs, rel, exact in walk(repo, pats, opts):
490 for src, abs, rel, exact in walk(repo, pats, opts):
491 if exact:
491 if exact:
492 if ui.verbose: ui.status(_('adding %s\n') % rel)
492 if ui.verbose: ui.status(_('adding %s\n') % rel)
493 names.append(abs)
493 names.append(abs)
494 elif repo.dirstate.state(abs) == '?':
494 elif repo.dirstate.state(abs) == '?':
495 ui.status(_('adding %s\n') % rel)
495 ui.status(_('adding %s\n') % rel)
496 names.append(abs)
496 names.append(abs)
497 repo.add(names)
497 repo.add(names)
498
498
499 def addremove(ui, repo, *pats, **opts):
499 def addremove(ui, repo, *pats, **opts):
500 """add all new files, delete all missing files
500 """add all new files, delete all missing files
501
501
502 Add all new files and remove all missing files from the repository.
502 Add all new files and remove all missing files from the repository.
503
503
504 New files are ignored if they match any of the patterns in .hgignore. As
504 New files are ignored if they match any of the patterns in .hgignore. As
505 with add, these changes take effect at the next commit.
505 with add, these changes take effect at the next commit.
506 """
506 """
507 add, remove = [], []
507 add, remove = [], []
508 for src, abs, rel, exact in walk(repo, pats, opts):
508 for src, abs, rel, exact in walk(repo, pats, opts):
509 if src == 'f' and repo.dirstate.state(abs) == '?':
509 if src == 'f' and repo.dirstate.state(abs) == '?':
510 add.append(abs)
510 add.append(abs)
511 if ui.verbose or not exact:
511 if ui.verbose or not exact:
512 ui.status(_('adding %s\n') % rel)
512 ui.status(_('adding %s\n') % rel)
513 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
513 if repo.dirstate.state(abs) != 'r' and not os.path.exists(rel):
514 remove.append(abs)
514 remove.append(abs)
515 if ui.verbose or not exact:
515 if ui.verbose or not exact:
516 ui.status(_('removing %s\n') % rel)
516 ui.status(_('removing %s\n') % rel)
517 repo.add(add)
517 repo.add(add)
518 repo.remove(remove)
518 repo.remove(remove)
519
519
520 def annotate(ui, repo, *pats, **opts):
520 def annotate(ui, repo, *pats, **opts):
521 """show changeset information per file line
521 """show changeset information per file line
522
522
523 List changes in files, showing the revision id responsible for each line
523 List changes in files, showing the revision id responsible for each line
524
524
525 This command is useful to discover who did a change or when a change took
525 This command is useful to discover who did a change or when a change took
526 place.
526 place.
527
527
528 Without the -a option, annotate will avoid processing files it
528 Without the -a option, annotate will avoid processing files it
529 detects as binary. With -a, annotate will generate an annotation
529 detects as binary. With -a, annotate will generate an annotation
530 anyway, probably with undesirable results.
530 anyway, probably with undesirable results.
531 """
531 """
532 def getnode(rev):
532 def getnode(rev):
533 return short(repo.changelog.node(rev))
533 return short(repo.changelog.node(rev))
534
534
535 ucache = {}
535 ucache = {}
536 def getname(rev):
536 def getname(rev):
537 cl = repo.changelog.read(repo.changelog.node(rev))
537 cl = repo.changelog.read(repo.changelog.node(rev))
538 return trimuser(ui, cl[1], rev, ucache)
538 return trimuser(ui, cl[1], rev, ucache)
539
539
540 dcache = {}
540 dcache = {}
541 def getdate(rev):
541 def getdate(rev):
542 datestr = dcache.get(rev)
542 datestr = dcache.get(rev)
543 if datestr is None:
543 if datestr is None:
544 cl = repo.changelog.read(repo.changelog.node(rev))
544 cl = repo.changelog.read(repo.changelog.node(rev))
545 datestr = dcache[rev] = util.datestr(cl[2])
545 datestr = dcache[rev] = util.datestr(cl[2])
546 return datestr
546 return datestr
547
547
548 if not pats:
548 if not pats:
549 raise util.Abort(_('at least one file name or pattern required'))
549 raise util.Abort(_('at least one file name or pattern required'))
550
550
551 opmap = [['user', getname], ['number', str], ['changeset', getnode],
551 opmap = [['user', getname], ['number', str], ['changeset', getnode],
552 ['date', getdate]]
552 ['date', getdate]]
553 if not opts['user'] and not opts['changeset'] and not opts['date']:
553 if not opts['user'] and not opts['changeset'] and not opts['date']:
554 opts['number'] = 1
554 opts['number'] = 1
555
555
556 if opts['rev']:
556 if opts['rev']:
557 node = repo.changelog.lookup(opts['rev'])
557 node = repo.changelog.lookup(opts['rev'])
558 else:
558 else:
559 node = repo.dirstate.parents()[0]
559 node = repo.dirstate.parents()[0]
560 change = repo.changelog.read(node)
560 change = repo.changelog.read(node)
561 mmap = repo.manifest.read(change[0])
561 mmap = repo.manifest.read(change[0])
562
562
563 for src, abs, rel, exact in walk(repo, pats, opts):
563 for src, abs, rel, exact in walk(repo, pats, opts):
564 if abs not in mmap:
564 if abs not in mmap:
565 ui.warn(_("warning: %s is not in the repository!\n") % rel)
565 ui.warn(_("warning: %s is not in the repository!\n") % rel)
566 continue
566 continue
567
567
568 f = repo.file(abs)
568 f = repo.file(abs)
569 if not opts['text'] and util.binary(f.read(mmap[abs])):
569 if not opts['text'] and util.binary(f.read(mmap[abs])):
570 ui.write(_("%s: binary file\n") % rel)
570 ui.write(_("%s: binary file\n") % rel)
571 continue
571 continue
572
572
573 lines = f.annotate(mmap[abs])
573 lines = f.annotate(mmap[abs])
574 pieces = []
574 pieces = []
575
575
576 for o, f in opmap:
576 for o, f in opmap:
577 if opts[o]:
577 if opts[o]:
578 l = [f(n) for n, dummy in lines]
578 l = [f(n) for n, dummy in lines]
579 if l:
579 if l:
580 m = max(map(len, l))
580 m = max(map(len, l))
581 pieces.append(["%*s" % (m, x) for x in l])
581 pieces.append(["%*s" % (m, x) for x in l])
582
582
583 if pieces:
583 if pieces:
584 for p, l in zip(zip(*pieces), lines):
584 for p, l in zip(zip(*pieces), lines):
585 ui.write("%s: %s" % (" ".join(p), l[1]))
585 ui.write("%s: %s" % (" ".join(p), l[1]))
586
586
587 def bundle(ui, repo, fname, dest="default-push", **opts):
587 def bundle(ui, repo, fname, dest="default-push", **opts):
588 """create a changegroup file
588 """create a changegroup file
589
589
590 Generate a compressed changegroup file collecting all changesets
590 Generate a compressed changegroup file collecting all changesets
591 not found in the other repository.
591 not found in the other repository.
592
592
593 This file can then be transferred using conventional means and
593 This file can then be transferred using conventional means and
594 applied to another repository with the unbundle command. This is
594 applied to another repository with the unbundle command. This is
595 useful when native push and pull are not available or when
595 useful when native push and pull are not available or when
596 exporting an entire repository is undesirable. The standard file
596 exporting an entire repository is undesirable. The standard file
597 extension is ".hg".
597 extension is ".hg".
598
598
599 Unlike import/export, this exactly preserves all changeset
599 Unlike import/export, this exactly preserves all changeset
600 contents including permissions, rename data, and revision history.
600 contents including permissions, rename data, and revision history.
601 """
601 """
602 f = open(fname, "wb")
602 f = open(fname, "wb")
603 dest = ui.expandpath(dest, repo.root)
603 dest = ui.expandpath(dest, repo.root)
604 other = hg.repository(ui, dest)
604 other = hg.repository(ui, dest)
605 o = repo.findoutgoing(other)
605 o = repo.findoutgoing(other)
606 cg = repo.changegroup(o)
606 cg = repo.changegroup(o)
607
607
608 try:
608 try:
609 f.write("HG10")
609 f.write("HG10")
610 z = bz2.BZ2Compressor(9)
610 z = bz2.BZ2Compressor(9)
611 while 1:
611 while 1:
612 chunk = cg.read(4096)
612 chunk = cg.read(4096)
613 if not chunk:
613 if not chunk:
614 break
614 break
615 f.write(z.compress(chunk))
615 f.write(z.compress(chunk))
616 f.write(z.flush())
616 f.write(z.flush())
617 except:
617 except:
618 os.unlink(fname)
618 os.unlink(fname)
619 raise
619 raise
620
620
621 def cat(ui, repo, file1, *pats, **opts):
621 def cat(ui, repo, file1, *pats, **opts):
622 """output the latest or given revisions of files
622 """output the latest or given revisions of files
623
623
624 Print the specified files as they were at the given revision.
624 Print the specified files as they were at the given revision.
625 If no revision is given then the tip is used.
625 If no revision is given then the tip is used.
626
626
627 Output may be to a file, in which case the name of the file is
627 Output may be to a file, in which case the name of the file is
628 given using a format string. The formatting rules are the same as
628 given using a format string. The formatting rules are the same as
629 for the export command, with the following additions:
629 for the export command, with the following additions:
630
630
631 %s basename of file being printed
631 %s basename of file being printed
632 %d dirname of file being printed, or '.' if in repo root
632 %d dirname of file being printed, or '.' if in repo root
633 %p root-relative path name of file being printed
633 %p root-relative path name of file being printed
634 """
634 """
635 mf = {}
635 mf = {}
636 rev = opts['rev']
636 rev = opts['rev']
637 if rev:
637 if rev:
638 change = repo.changelog.read(repo.lookup(rev))
638 change = repo.changelog.read(repo.lookup(rev))
639 mf = repo.manifest.read(change[0])
639 mf = repo.manifest.read(change[0])
640 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts):
640 for src, abs, rel, exact in walk(repo, (file1,) + pats, opts):
641 r = repo.file(abs)
641 r = repo.file(abs)
642 if rev:
642 if rev:
643 try:
643 try:
644 n = mf[abs]
644 n = mf[abs]
645 except (hg.RepoError, KeyError):
645 except (hg.RepoError, KeyError):
646 try:
646 try:
647 n = r.lookup(rev)
647 n = r.lookup(rev)
648 except KeyError, inst:
648 except KeyError, inst:
649 raise util.Abort(_('cannot find file %s in rev %s'), rel, rev)
649 raise util.Abort(_('cannot find file %s in rev %s'), rel, rev)
650 else:
650 else:
651 n = r.tip()
651 n = r.tip()
652 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
652 fp = make_file(repo, r, opts['output'], node=n, pathname=abs)
653 fp.write(r.read(n))
653 fp.write(r.read(n))
654
654
655 def clone(ui, source, dest=None, **opts):
655 def clone(ui, source, dest=None, **opts):
656 """make a copy of an existing repository
656 """make a copy of an existing repository
657
657
658 Create a copy of an existing repository in a new directory.
658 Create a copy of an existing repository in a new directory.
659
659
660 If no destination directory name is specified, it defaults to the
660 If no destination directory name is specified, it defaults to the
661 basename of the source.
661 basename of the source.
662
662
663 The location of the source is added to the new repository's
663 The location of the source is added to the new repository's
664 .hg/hgrc file, as the default to be used for future pulls.
664 .hg/hgrc file, as the default to be used for future pulls.
665
665
666 For efficiency, hardlinks are used for cloning whenever the source
666 For efficiency, hardlinks are used for cloning whenever the source
667 and destination are on the same filesystem. Some filesystems,
667 and destination are on the same filesystem. Some filesystems,
668 such as AFS, implement hardlinking incorrectly, but do not report
668 such as AFS, implement hardlinking incorrectly, but do not report
669 errors. In these cases, use the --pull option to avoid
669 errors. In these cases, use the --pull option to avoid
670 hardlinking.
670 hardlinking.
671 """
671 """
672 if dest is None:
672 if dest is None:
673 dest = os.path.basename(os.path.normpath(source))
673 dest = os.path.basename(os.path.normpath(source))
674
674
675 if os.path.exists(dest):
675 if os.path.exists(dest):
676 raise util.Abort(_("destination '%s' already exists"), dest)
676 raise util.Abort(_("destination '%s' already exists"), dest)
677
677
678 dest = os.path.realpath(dest)
678 dest = os.path.realpath(dest)
679
679
680 class Dircleanup(object):
680 class Dircleanup(object):
681 def __init__(self, dir_):
681 def __init__(self, dir_):
682 self.rmtree = shutil.rmtree
682 self.rmtree = shutil.rmtree
683 self.dir_ = dir_
683 self.dir_ = dir_
684 os.mkdir(dir_)
684 os.mkdir(dir_)
685 def close(self):
685 def close(self):
686 self.dir_ = None
686 self.dir_ = None
687 def __del__(self):
687 def __del__(self):
688 if self.dir_:
688 if self.dir_:
689 self.rmtree(self.dir_, True)
689 self.rmtree(self.dir_, True)
690
690
691 if opts['ssh']:
691 if opts['ssh']:
692 ui.setconfig("ui", "ssh", opts['ssh'])
692 ui.setconfig("ui", "ssh", opts['ssh'])
693 if opts['remotecmd']:
693 if opts['remotecmd']:
694 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
694 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
695
695
696 if not os.path.exists(source):
696 if not os.path.exists(source):
697 source = ui.expandpath(source)
697 source = ui.expandpath(source)
698
698
699 d = Dircleanup(dest)
699 d = Dircleanup(dest)
700 abspath = source
700 abspath = source
701 other = hg.repository(ui, source)
701 other = hg.repository(ui, source)
702
702
703 copy = False
703 copy = False
704 if other.dev() != -1:
704 if other.dev() != -1:
705 abspath = os.path.abspath(source)
705 abspath = os.path.abspath(source)
706 if not opts['pull'] and not opts['rev']:
706 if not opts['pull'] and not opts['rev']:
707 copy = True
707 copy = True
708
708
709 if copy:
709 if copy:
710 try:
710 try:
711 # we use a lock here because if we race with commit, we
711 # we use a lock here because if we race with commit, we
712 # can end up with extra data in the cloned revlogs that's
712 # can end up with extra data in the cloned revlogs that's
713 # not pointed to by changesets, thus causing verify to
713 # not pointed to by changesets, thus causing verify to
714 # fail
714 # fail
715 l1 = lock.lock(os.path.join(source, ".hg", "lock"))
715 l1 = lock.lock(os.path.join(source, ".hg", "lock"))
716 except OSError:
716 except OSError:
717 copy = False
717 copy = False
718
718
719 if copy:
719 if copy:
720 # we lock here to avoid premature writing to the target
720 # we lock here to avoid premature writing to the target
721 os.mkdir(os.path.join(dest, ".hg"))
721 os.mkdir(os.path.join(dest, ".hg"))
722 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
722 l2 = lock.lock(os.path.join(dest, ".hg", "lock"))
723
723
724 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
724 files = "data 00manifest.d 00manifest.i 00changelog.d 00changelog.i"
725 for f in files.split():
725 for f in files.split():
726 src = os.path.join(source, ".hg", f)
726 src = os.path.join(source, ".hg", f)
727 dst = os.path.join(dest, ".hg", f)
727 dst = os.path.join(dest, ".hg", f)
728 try:
728 try:
729 util.copyfiles(src, dst)
729 util.copyfiles(src, dst)
730 except OSError, inst:
730 except OSError, inst:
731 if inst.errno != errno.ENOENT: raise
731 if inst.errno != errno.ENOENT: raise
732
732
733 repo = hg.repository(ui, dest)
733 repo = hg.repository(ui, dest)
734
734
735 else:
735 else:
736 revs = None
736 revs = None
737 if opts['rev']:
737 if opts['rev']:
738 if not other.local():
738 if not other.local():
739 raise util.Abort("clone -r not supported yet for remote repositories.")
739 raise util.Abort("clone -r not supported yet for remote repositories.")
740 else:
740 else:
741 revs = [other.lookup(rev) for rev in opts['rev']]
741 revs = [other.lookup(rev) for rev in opts['rev']]
742 repo = hg.repository(ui, dest, create=1)
742 repo = hg.repository(ui, dest, create=1)
743 repo.pull(other, heads = revs)
743 repo.pull(other, heads = revs)
744
744
745 f = repo.opener("hgrc", "w", text=True)
745 f = repo.opener("hgrc", "w", text=True)
746 f.write("[paths]\n")
746 f.write("[paths]\n")
747 f.write("default = %s\n" % abspath)
747 f.write("default = %s\n" % abspath)
748 f.close()
748 f.close()
749
749
750 if not opts['noupdate']:
750 if not opts['noupdate']:
751 update(ui, repo)
751 update(ui, repo)
752
752
753 d.close()
753 d.close()
754
754
755 def commit(ui, repo, *pats, **opts):
755 def commit(ui, repo, *pats, **opts):
756 """commit the specified files or all outstanding changes
756 """commit the specified files or all outstanding changes
757
757
758 Commit changes to the given files into the repository.
758 Commit changes to the given files into the repository.
759
759
760 If a list of files is omitted, all changes reported by "hg status"
760 If a list of files is omitted, all changes reported by "hg status"
761 will be commited.
761 will be commited.
762
762
763 The HGEDITOR or EDITOR environment variables are used to start an
763 The HGEDITOR or EDITOR environment variables are used to start an
764 editor to add a commit comment.
764 editor to add a commit comment.
765 """
765 """
766 message = opts['message']
766 message = opts['message']
767 logfile = opts['logfile']
767 logfile = opts['logfile']
768
768
769 if message and logfile:
769 if message and logfile:
770 raise util.Abort(_('options --message and --logfile are mutually '
770 raise util.Abort(_('options --message and --logfile are mutually '
771 'exclusive'))
771 'exclusive'))
772 if not message and logfile:
772 if not message and logfile:
773 try:
773 try:
774 if logfile == '-':
774 if logfile == '-':
775 message = sys.stdin.read()
775 message = sys.stdin.read()
776 else:
776 else:
777 message = open(logfile).read()
777 message = open(logfile).read()
778 except IOError, inst:
778 except IOError, inst:
779 raise util.Abort(_("can't read commit message '%s': %s") %
779 raise util.Abort(_("can't read commit message '%s': %s") %
780 (logfile, inst.strerror))
780 (logfile, inst.strerror))
781
781
782 if opts['addremove']:
782 if opts['addremove']:
783 addremove(ui, repo, *pats, **opts)
783 addremove(ui, repo, *pats, **opts)
784 fns, match, anypats, cwd = matchpats(repo, pats, opts)
784 fns, match, anypats, cwd = matchpats(repo, pats, opts)
785 if pats:
785 if pats:
786 c, a, d, u = repo.changes(files=fns, match=match)
786 c, a, d, u = repo.changes(files=fns, match=match)
787 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
787 files = c + a + [fn for fn in d if repo.dirstate.state(fn) == 'r']
788 else:
788 else:
789 files = []
789 files = []
790 try:
790 try:
791 repo.commit(files, message, opts['user'], opts['date'], match)
791 repo.commit(files, message, opts['user'], opts['date'], match)
792 except ValueError, inst:
792 except ValueError, inst:
793 raise util.Abort(str(inst))
793 raise util.Abort(str(inst))
794
794
795 def docopy(ui, repo, pats, opts):
795 def docopy(ui, repo, pats, opts):
796 cwd = repo.getcwd()
796 cwd = repo.getcwd()
797 errors = 0
797 errors = 0
798 copied = []
798 copied = []
799 targets = {}
799 targets = {}
800
800
801 def okaytocopy(abs, rel, exact):
801 def okaytocopy(abs, rel, exact):
802 reasons = {'?': _('is not managed'),
802 reasons = {'?': _('is not managed'),
803 'a': _('has been marked for add')}
803 'a': _('has been marked for add')}
804 reason = reasons.get(repo.dirstate.state(abs))
804 reason = reasons.get(repo.dirstate.state(abs))
805 if reason:
805 if reason:
806 if exact: ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
806 if exact: ui.warn(_('%s: not copying - file %s\n') % (rel, reason))
807 else:
807 else:
808 return True
808 return True
809
809
810 def copy(abssrc, relsrc, target, exact):
810 def copy(abssrc, relsrc, target, exact):
811 abstarget = util.canonpath(repo.root, cwd, target)
811 abstarget = util.canonpath(repo.root, cwd, target)
812 reltarget = util.pathto(cwd, abstarget)
812 reltarget = util.pathto(cwd, abstarget)
813 prevsrc = targets.get(abstarget)
813 prevsrc = targets.get(abstarget)
814 if prevsrc is not None:
814 if prevsrc is not None:
815 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
815 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
816 (reltarget, abssrc, prevsrc))
816 (reltarget, abssrc, prevsrc))
817 return
817 return
818 if (not opts['after'] and os.path.exists(reltarget) or
818 if (not opts['after'] and os.path.exists(reltarget) or
819 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
819 opts['after'] and repo.dirstate.state(abstarget) not in '?r'):
820 if not opts['force']:
820 if not opts['force']:
821 ui.warn(_('%s: not overwriting - file exists\n') %
821 ui.warn(_('%s: not overwriting - file exists\n') %
822 reltarget)
822 reltarget)
823 return
823 return
824 if not opts['after']:
824 if not opts['after']:
825 os.unlink(reltarget)
825 os.unlink(reltarget)
826 if opts['after']:
826 if opts['after']:
827 if not os.path.exists(reltarget):
827 if not os.path.exists(reltarget):
828 return
828 return
829 else:
829 else:
830 targetdir = os.path.dirname(reltarget) or '.'
830 targetdir = os.path.dirname(reltarget) or '.'
831 if not os.path.isdir(targetdir):
831 if not os.path.isdir(targetdir):
832 os.makedirs(targetdir)
832 os.makedirs(targetdir)
833 try:
833 try:
834 shutil.copyfile(relsrc, reltarget)
834 shutil.copyfile(relsrc, reltarget)
835 shutil.copymode(relsrc, reltarget)
835 shutil.copymode(relsrc, reltarget)
836 except shutil.Error, inst:
836 except shutil.Error, inst:
837 raise util.Abort(str(inst))
837 raise util.Abort(str(inst))
838 except IOError, inst:
838 except IOError, inst:
839 if inst.errno == errno.ENOENT:
839 if inst.errno == errno.ENOENT:
840 ui.warn(_('%s: deleted in working copy\n') % relsrc)
840 ui.warn(_('%s: deleted in working copy\n') % relsrc)
841 else:
841 else:
842 ui.warn(_('%s: cannot copy - %s\n') %
842 ui.warn(_('%s: cannot copy - %s\n') %
843 (relsrc, inst.strerror))
843 (relsrc, inst.strerror))
844 errors += 1
844 errors += 1
845 return
845 return
846 if ui.verbose or not exact:
846 if ui.verbose or not exact:
847 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
847 ui.status(_('copying %s to %s\n') % (relsrc, reltarget))
848 targets[abstarget] = abssrc
848 targets[abstarget] = abssrc
849 repo.copy(abssrc, abstarget)
849 repo.copy(abssrc, abstarget)
850 copied.append((abssrc, relsrc, exact))
850 copied.append((abssrc, relsrc, exact))
851
851
852 def targetpathfn(pat, dest, srcs):
852 def targetpathfn(pat, dest, srcs):
853 if os.path.isdir(pat):
853 if os.path.isdir(pat):
854 if pat.endswith(os.sep):
854 if pat.endswith(os.sep):
855 pat = pat[:-len(os.sep)]
855 pat = pat[:-len(os.sep)]
856 if destdirexists:
856 if destdirexists:
857 striplen = len(os.path.split(pat)[0])
857 striplen = len(os.path.split(pat)[0])
858 else:
858 else:
859 striplen = len(pat)
859 striplen = len(pat)
860 if striplen:
860 if striplen:
861 striplen += len(os.sep)
861 striplen += len(os.sep)
862 res = lambda p: os.path.join(dest, p[striplen:])
862 res = lambda p: os.path.join(dest, p[striplen:])
863 elif destdirexists:
863 elif destdirexists:
864 res = lambda p: os.path.join(dest, os.path.basename(p))
864 res = lambda p: os.path.join(dest, os.path.basename(p))
865 else:
865 else:
866 res = lambda p: dest
866 res = lambda p: dest
867 return res
867 return res
868
868
869 def targetpathafterfn(pat, dest, srcs):
869 def targetpathafterfn(pat, dest, srcs):
870 if util.patkind(pat, None)[0]:
870 if util.patkind(pat, None)[0]:
871 # a mercurial pattern
871 # a mercurial pattern
872 res = lambda p: os.path.join(dest, os.path.basename(p))
872 res = lambda p: os.path.join(dest, os.path.basename(p))
873 elif len(util.canonpath(repo.root, cwd, pat)) < len(srcs[0][0]):
873 elif len(util.canonpath(repo.root, cwd, pat)) < len(srcs[0][0]):
874 # A directory. Either the target path contains the last
874 # A directory. Either the target path contains the last
875 # component of the source path or it does not.
875 # component of the source path or it does not.
876 def evalpath(striplen):
876 def evalpath(striplen):
877 score = 0
877 score = 0
878 for s in srcs:
878 for s in srcs:
879 t = os.path.join(dest, s[1][striplen:])
879 t = os.path.join(dest, s[1][striplen:])
880 if os.path.exists(t):
880 if os.path.exists(t):
881 score += 1
881 score += 1
882 return score
882 return score
883
883
884 if pat.endswith(os.sep):
884 if pat.endswith(os.sep):
885 pat = pat[:-len(os.sep)]
885 pat = pat[:-len(os.sep)]
886 striplen = len(pat) + len(os.sep)
886 striplen = len(pat) + len(os.sep)
887 if os.path.isdir(os.path.join(dest, os.path.split(pat)[1])):
887 if os.path.isdir(os.path.join(dest, os.path.split(pat)[1])):
888 score = evalpath(striplen)
888 score = evalpath(striplen)
889 striplen1 = len(os.path.split(pat)[0])
889 striplen1 = len(os.path.split(pat)[0])
890 if striplen1:
890 if striplen1:
891 striplen1 += len(os.sep)
891 striplen1 += len(os.sep)
892 if evalpath(striplen1) > score:
892 if evalpath(striplen1) > score:
893 striplen = striplen1
893 striplen = striplen1
894 res = lambda p: os.path.join(dest, p[striplen:])
894 res = lambda p: os.path.join(dest, p[striplen:])
895 else:
895 else:
896 # a file
896 # a file
897 if destdirexists:
897 if destdirexists:
898 res = lambda p: os.path.join(dest, os.path.basename(p))
898 res = lambda p: os.path.join(dest, os.path.basename(p))
899 else:
899 else:
900 res = lambda p: dest
900 res = lambda p: dest
901 return res
901 return res
902
902
903
903
904 pats = list(pats)
904 pats = list(pats)
905 if not pats:
905 if not pats:
906 raise util.Abort(_('no source or destination specified'))
906 raise util.Abort(_('no source or destination specified'))
907 if len(pats) == 1:
907 if len(pats) == 1:
908 raise util.Abort(_('no destination specified'))
908 raise util.Abort(_('no destination specified'))
909 dest = pats.pop()
909 dest = pats.pop()
910 destdirexists = os.path.isdir(dest)
910 destdirexists = os.path.isdir(dest)
911 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
911 if (len(pats) > 1 or util.patkind(pats[0], None)[0]) and not destdirexists:
912 raise util.Abort(_('with multiple sources, destination must be an '
912 raise util.Abort(_('with multiple sources, destination must be an '
913 'existing directory'))
913 'existing directory'))
914 if opts['after']:
914 if opts['after']:
915 tfn = targetpathafterfn
915 tfn = targetpathafterfn
916 else:
916 else:
917 tfn = targetpathfn
917 tfn = targetpathfn
918 copylist = []
918 copylist = []
919 for pat in pats:
919 for pat in pats:
920 srcs = []
920 srcs = []
921 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
921 for tag, abssrc, relsrc, exact in walk(repo, [pat], opts):
922 if okaytocopy(abssrc, relsrc, exact):
922 if okaytocopy(abssrc, relsrc, exact):
923 srcs.append((abssrc, relsrc, exact))
923 srcs.append((abssrc, relsrc, exact))
924 if not srcs:
924 if not srcs:
925 continue
925 continue
926 copylist.append((tfn(pat, dest, srcs), srcs))
926 copylist.append((tfn(pat, dest, srcs), srcs))
927 if not copylist:
927 if not copylist:
928 raise util.Abort(_('no files to copy'))
928 raise util.Abort(_('no files to copy'))
929
929
930 for targetpath, srcs in copylist:
930 for targetpath, srcs in copylist:
931 for abssrc, relsrc, exact in srcs:
931 for abssrc, relsrc, exact in srcs:
932 copy(abssrc, relsrc, targetpath(relsrc), exact)
932 copy(abssrc, relsrc, targetpath(relsrc), exact)
933
933
934 if errors:
934 if errors:
935 ui.warn(_('(consider using --after)\n'))
935 ui.warn(_('(consider using --after)\n'))
936 return errors, copied
936 return errors, copied
937
937
938 def copy(ui, repo, *pats, **opts):
938 def copy(ui, repo, *pats, **opts):
939 """mark files as copied for the next commit
939 """mark files as copied for the next commit
940
940
941 Mark dest as having copies of source files. If dest is a
941 Mark dest as having copies of source files. If dest is a
942 directory, copies are put in that directory. If dest is a file,
942 directory, copies are put in that directory. If dest is a file,
943 there can only be one source.
943 there can only be one source.
944
944
945 By default, this command copies the contents of files as they
945 By default, this command copies the contents of files as they
946 stand in the working directory. If invoked with --after, the
946 stand in the working directory. If invoked with --after, the
947 operation is recorded, but no copying is performed.
947 operation is recorded, but no copying is performed.
948
948
949 This command takes effect in the next commit.
949 This command takes effect in the next commit.
950
950
951 NOTE: This command should be treated as experimental. While it
951 NOTE: This command should be treated as experimental. While it
952 should properly record copied files, this information is not yet
952 should properly record copied files, this information is not yet
953 fully used by merge, nor fully reported by log.
953 fully used by merge, nor fully reported by log.
954 """
954 """
955 errs, copied = docopy(ui, repo, pats, opts)
955 errs, copied = docopy(ui, repo, pats, opts)
956 return errs
956 return errs
957
957
958 def debugancestor(ui, index, rev1, rev2):
958 def debugancestor(ui, index, rev1, rev2):
959 """find the ancestor revision of two revisions in a given index"""
959 """find the ancestor revision of two revisions in a given index"""
960 r = revlog.revlog(util.opener(os.getcwd()), index, "")
960 r = revlog.revlog(util.opener(os.getcwd()), index, "")
961 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
961 a = r.ancestor(r.lookup(rev1), r.lookup(rev2))
962 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
962 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
963
963
964 def debugcheckstate(ui, repo):
964 def debugcheckstate(ui, repo):
965 """validate the correctness of the current dirstate"""
965 """validate the correctness of the current dirstate"""
966 parent1, parent2 = repo.dirstate.parents()
966 parent1, parent2 = repo.dirstate.parents()
967 repo.dirstate.read()
967 repo.dirstate.read()
968 dc = repo.dirstate.map
968 dc = repo.dirstate.map
969 keys = dc.keys()
969 keys = dc.keys()
970 keys.sort()
970 keys.sort()
971 m1n = repo.changelog.read(parent1)[0]
971 m1n = repo.changelog.read(parent1)[0]
972 m2n = repo.changelog.read(parent2)[0]
972 m2n = repo.changelog.read(parent2)[0]
973 m1 = repo.manifest.read(m1n)
973 m1 = repo.manifest.read(m1n)
974 m2 = repo.manifest.read(m2n)
974 m2 = repo.manifest.read(m2n)
975 errors = 0
975 errors = 0
976 for f in dc:
976 for f in dc:
977 state = repo.dirstate.state(f)
977 state = repo.dirstate.state(f)
978 if state in "nr" and f not in m1:
978 if state in "nr" and f not in m1:
979 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
979 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
980 errors += 1
980 errors += 1
981 if state in "a" and f in m1:
981 if state in "a" and f in m1:
982 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
982 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
983 errors += 1
983 errors += 1
984 if state in "m" and f not in m1 and f not in m2:
984 if state in "m" and f not in m1 and f not in m2:
985 ui.warn(_("%s in state %s, but not in either manifest\n") %
985 ui.warn(_("%s in state %s, but not in either manifest\n") %
986 (f, state))
986 (f, state))
987 errors += 1
987 errors += 1
988 for f in m1:
988 for f in m1:
989 state = repo.dirstate.state(f)
989 state = repo.dirstate.state(f)
990 if state not in "nrm":
990 if state not in "nrm":
991 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
991 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
992 errors += 1
992 errors += 1
993 if errors:
993 if errors:
994 raise util.Abort(_(".hg/dirstate inconsistent with current parent's manifest"))
994 raise util.Abort(_(".hg/dirstate inconsistent with current parent's manifest"))
995
995
996 def debugconfig(ui):
996 def debugconfig(ui):
997 """show combined config settings from all hgrc files"""
997 """show combined config settings from all hgrc files"""
998 try:
998 try:
999 repo = hg.repository(ui)
999 repo = hg.repository(ui)
1000 except hg.RepoError:
1000 except hg.RepoError:
1001 pass
1001 pass
1002 for section, name, value in ui.walkconfig():
1002 for section, name, value in ui.walkconfig():
1003 ui.write('%s.%s=%s\n' % (section, name, value))
1003 ui.write('%s.%s=%s\n' % (section, name, value))
1004
1004
1005 def debugsetparents(ui, repo, rev1, rev2=None):
1005 def debugsetparents(ui, repo, rev1, rev2=None):
1006 """manually set the parents of the current working directory
1006 """manually set the parents of the current working directory
1007
1007
1008 This is useful for writing repository conversion tools, but should
1008 This is useful for writing repository conversion tools, but should
1009 be used with care.
1009 be used with care.
1010 """
1010 """
1011
1011
1012 if not rev2:
1012 if not rev2:
1013 rev2 = hex(nullid)
1013 rev2 = hex(nullid)
1014
1014
1015 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1015 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
1016
1016
1017 def debugstate(ui, repo):
1017 def debugstate(ui, repo):
1018 """show the contents of the current dirstate"""
1018 """show the contents of the current dirstate"""
1019 repo.dirstate.read()
1019 repo.dirstate.read()
1020 dc = repo.dirstate.map
1020 dc = repo.dirstate.map
1021 keys = dc.keys()
1021 keys = dc.keys()
1022 keys.sort()
1022 keys.sort()
1023 for file_ in keys:
1023 for file_ in keys:
1024 ui.write("%c %3o %10d %s %s\n"
1024 ui.write("%c %3o %10d %s %s\n"
1025 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1025 % (dc[file_][0], dc[file_][1] & 0777, dc[file_][2],
1026 time.strftime("%x %X",
1026 time.strftime("%x %X",
1027 time.localtime(dc[file_][3])), file_))
1027 time.localtime(dc[file_][3])), file_))
1028 for f in repo.dirstate.copies:
1028 for f in repo.dirstate.copies:
1029 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1029 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copies[f], f))
1030
1030
1031 def debugdata(ui, file_, rev):
1031 def debugdata(ui, file_, rev):
1032 """dump the contents of an data file revision"""
1032 """dump the contents of an data file revision"""
1033 r = revlog.revlog(util.opener(os.getcwd()), file_[:-2] + ".i", file_)
1033 r = revlog.revlog(util.opener(os.getcwd()), file_[:-2] + ".i", file_)
1034 try:
1034 try:
1035 ui.write(r.revision(r.lookup(rev)))
1035 ui.write(r.revision(r.lookup(rev)))
1036 except KeyError:
1036 except KeyError:
1037 raise util.Abort(_('invalid revision identifier %s'), rev)
1037 raise util.Abort(_('invalid revision identifier %s'), rev)
1038
1038
1039 def debugindex(ui, file_):
1039 def debugindex(ui, file_):
1040 """dump the contents of an index file"""
1040 """dump the contents of an index file"""
1041 r = revlog.revlog(util.opener(os.getcwd()), file_, "")
1041 r = revlog.revlog(util.opener(os.getcwd()), file_, "")
1042 ui.write(" rev offset length base linkrev" +
1042 ui.write(" rev offset length base linkrev" +
1043 " nodeid p1 p2\n")
1043 " nodeid p1 p2\n")
1044 for i in range(r.count()):
1044 for i in range(r.count()):
1045 e = r.index[i]
1045 e = r.index[i]
1046 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1046 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
1047 i, e[0], e[1], e[2], e[3],
1047 i, e[0], e[1], e[2], e[3],
1048 short(e[6]), short(e[4]), short(e[5])))
1048 short(e[6]), short(e[4]), short(e[5])))
1049
1049
1050 def debugindexdot(ui, file_):
1050 def debugindexdot(ui, file_):
1051 """dump an index DAG as a .dot file"""
1051 """dump an index DAG as a .dot file"""
1052 r = revlog.revlog(util.opener(os.getcwd()), file_, "")
1052 r = revlog.revlog(util.opener(os.getcwd()), file_, "")
1053 ui.write("digraph G {\n")
1053 ui.write("digraph G {\n")
1054 for i in range(r.count()):
1054 for i in range(r.count()):
1055 e = r.index[i]
1055 e = r.index[i]
1056 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
1056 ui.write("\t%d -> %d\n" % (r.rev(e[4]), i))
1057 if e[5] != nullid:
1057 if e[5] != nullid:
1058 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
1058 ui.write("\t%d -> %d\n" % (r.rev(e[5]), i))
1059 ui.write("}\n")
1059 ui.write("}\n")
1060
1060
1061 def debugrename(ui, repo, file, rev=None):
1061 def debugrename(ui, repo, file, rev=None):
1062 """dump rename information"""
1062 """dump rename information"""
1063 r = repo.file(relpath(repo, [file])[0])
1063 r = repo.file(relpath(repo, [file])[0])
1064 if rev:
1064 if rev:
1065 try:
1065 try:
1066 # assume all revision numbers are for changesets
1066 # assume all revision numbers are for changesets
1067 n = repo.lookup(rev)
1067 n = repo.lookup(rev)
1068 change = repo.changelog.read(n)
1068 change = repo.changelog.read(n)
1069 m = repo.manifest.read(change[0])
1069 m = repo.manifest.read(change[0])
1070 n = m[relpath(repo, [file])[0]]
1070 n = m[relpath(repo, [file])[0]]
1071 except (hg.RepoError, KeyError):
1071 except (hg.RepoError, KeyError):
1072 n = r.lookup(rev)
1072 n = r.lookup(rev)
1073 else:
1073 else:
1074 n = r.tip()
1074 n = r.tip()
1075 m = r.renamed(n)
1075 m = r.renamed(n)
1076 if m:
1076 if m:
1077 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1077 ui.write(_("renamed from %s:%s\n") % (m[0], hex(m[1])))
1078 else:
1078 else:
1079 ui.write(_("not renamed\n"))
1079 ui.write(_("not renamed\n"))
1080
1080
1081 def debugwalk(ui, repo, *pats, **opts):
1081 def debugwalk(ui, repo, *pats, **opts):
1082 """show how files match on given patterns"""
1082 """show how files match on given patterns"""
1083 items = list(walk(repo, pats, opts))
1083 items = list(walk(repo, pats, opts))
1084 if not items:
1084 if not items:
1085 return
1085 return
1086 fmt = '%%s %%-%ds %%-%ds %%s' % (
1086 fmt = '%%s %%-%ds %%-%ds %%s' % (
1087 max([len(abs) for (src, abs, rel, exact) in items]),
1087 max([len(abs) for (src, abs, rel, exact) in items]),
1088 max([len(rel) for (src, abs, rel, exact) in items]))
1088 max([len(rel) for (src, abs, rel, exact) in items]))
1089 for src, abs, rel, exact in items:
1089 for src, abs, rel, exact in items:
1090 line = fmt % (src, abs, rel, exact and 'exact' or '')
1090 line = fmt % (src, abs, rel, exact and 'exact' or '')
1091 ui.write("%s\n" % line.rstrip())
1091 ui.write("%s\n" % line.rstrip())
1092
1092
1093 def diff(ui, repo, *pats, **opts):
1093 def diff(ui, repo, *pats, **opts):
1094 """diff repository (or selected files)
1094 """diff repository (or selected files)
1095
1095
1096 Show differences between revisions for the specified files.
1096 Show differences between revisions for the specified files.
1097
1097
1098 Differences between files are shown using the unified diff format.
1098 Differences between files are shown using the unified diff format.
1099
1099
1100 When two revision arguments are given, then changes are shown
1100 When two revision arguments are given, then changes are shown
1101 between those revisions. If only one revision is specified then
1101 between those revisions. If only one revision is specified then
1102 that revision is compared to the working directory, and, when no
1102 that revision is compared to the working directory, and, when no
1103 revisions are specified, the working directory files are compared
1103 revisions are specified, the working directory files are compared
1104 to its parent.
1104 to its parent.
1105
1105
1106 Without the -a option, diff will avoid generating diffs of files
1106 Without the -a option, diff will avoid generating diffs of files
1107 it detects as binary. With -a, diff will generate a diff anyway,
1107 it detects as binary. With -a, diff will generate a diff anyway,
1108 probably with undesirable results.
1108 probably with undesirable results.
1109 """
1109 """
1110 node1, node2 = None, None
1110 node1, node2 = None, None
1111 revs = [repo.lookup(x) for x in opts['rev']]
1111 revs = [repo.lookup(x) for x in opts['rev']]
1112
1112
1113 if len(revs) > 0:
1113 if len(revs) > 0:
1114 node1 = revs[0]
1114 node1 = revs[0]
1115 if len(revs) > 1:
1115 if len(revs) > 1:
1116 node2 = revs[1]
1116 node2 = revs[1]
1117 if len(revs) > 2:
1117 if len(revs) > 2:
1118 raise util.Abort(_("too many revisions to diff"))
1118 raise util.Abort(_("too many revisions to diff"))
1119
1119
1120 fns, matchfn, anypats, cwd = matchpats(repo, pats, opts)
1120 fns, matchfn, anypats, cwd = matchpats(repo, pats, opts)
1121
1121
1122 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1122 dodiff(sys.stdout, ui, repo, node1, node2, fns, match=matchfn,
1123 text=opts['text'])
1123 text=opts['text'])
1124
1124
1125 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1125 def doexport(ui, repo, changeset, seqno, total, revwidth, opts):
1126 node = repo.lookup(changeset)
1126 node = repo.lookup(changeset)
1127 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1127 parents = [p for p in repo.changelog.parents(node) if p != nullid]
1128 prev = (parents and parents[0]) or nullid
1128 prev = (parents and parents[0]) or nullid
1129 change = repo.changelog.read(node)
1129 change = repo.changelog.read(node)
1130
1130
1131 if opts['switch_parent']:
1131 if opts['switch_parent']:
1132 parents.reverse()
1132 parents.reverse()
1133 fp = make_file(repo, repo.changelog, opts['output'],
1133 fp = make_file(repo, repo.changelog, opts['output'],
1134 node=node, total=total, seqno=seqno,
1134 node=node, total=total, seqno=seqno,
1135 revwidth=revwidth)
1135 revwidth=revwidth)
1136 if fp != sys.stdout:
1136 if fp != sys.stdout:
1137 ui.note("%s\n" % fp.name)
1137 ui.note("%s\n" % fp.name)
1138
1138
1139 fp.write("# HG changeset patch\n")
1139 fp.write("# HG changeset patch\n")
1140 fp.write("# User %s\n" % change[1])
1140 fp.write("# User %s\n" % change[1])
1141 fp.write("# Node ID %s\n" % hex(node))
1141 fp.write("# Node ID %s\n" % hex(node))
1142 fp.write("# Parent %s\n" % hex(prev))
1142 fp.write("# Parent %s\n" % hex(prev))
1143 if len(parents) > 1:
1143 if len(parents) > 1:
1144 fp.write("# Parent %s\n" % hex(parents[1]))
1144 fp.write("# Parent %s\n" % hex(parents[1]))
1145 fp.write(change[4].rstrip())
1145 fp.write(change[4].rstrip())
1146 fp.write("\n\n")
1146 fp.write("\n\n")
1147
1147
1148 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1148 dodiff(fp, ui, repo, prev, node, text=opts['text'])
1149 if fp != sys.stdout:
1149 if fp != sys.stdout:
1150 fp.close()
1150 fp.close()
1151
1151
1152 def export(ui, repo, *changesets, **opts):
1152 def export(ui, repo, *changesets, **opts):
1153 """dump the header and diffs for one or more changesets
1153 """dump the header and diffs for one or more changesets
1154
1154
1155 Print the changeset header and diffs for one or more revisions.
1155 Print the changeset header and diffs for one or more revisions.
1156
1156
1157 The information shown in the changeset header is: author,
1157 The information shown in the changeset header is: author,
1158 changeset hash, parent and commit comment.
1158 changeset hash, parent and commit comment.
1159
1159
1160 Output may be to a file, in which case the name of the file is
1160 Output may be to a file, in which case the name of the file is
1161 given using a format string. The formatting rules are as follows:
1161 given using a format string. The formatting rules are as follows:
1162
1162
1163 %% literal "%" character
1163 %% literal "%" character
1164 %H changeset hash (40 bytes of hexadecimal)
1164 %H changeset hash (40 bytes of hexadecimal)
1165 %N number of patches being generated
1165 %N number of patches being generated
1166 %R changeset revision number
1166 %R changeset revision number
1167 %b basename of the exporting repository
1167 %b basename of the exporting repository
1168 %h short-form changeset hash (12 bytes of hexadecimal)
1168 %h short-form changeset hash (12 bytes of hexadecimal)
1169 %n zero-padded sequence number, starting at 1
1169 %n zero-padded sequence number, starting at 1
1170 %r zero-padded changeset revision number
1170 %r zero-padded changeset revision number
1171
1171
1172 Without the -a option, export will avoid generating diffs of files
1172 Without the -a option, export will avoid generating diffs of files
1173 it detects as binary. With -a, export will generate a diff anyway,
1173 it detects as binary. With -a, export will generate a diff anyway,
1174 probably with undesirable results.
1174 probably with undesirable results.
1175
1175
1176 With the --switch-parent option, the diff will be against the second
1176 With the --switch-parent option, the diff will be against the second
1177 parent. It can be useful to review a merge.
1177 parent. It can be useful to review a merge.
1178 """
1178 """
1179 if not changesets:
1179 if not changesets:
1180 raise util.Abort(_("export requires at least one changeset"))
1180 raise util.Abort(_("export requires at least one changeset"))
1181 seqno = 0
1181 seqno = 0
1182 revs = list(revrange(ui, repo, changesets))
1182 revs = list(revrange(ui, repo, changesets))
1183 total = len(revs)
1183 total = len(revs)
1184 revwidth = max(map(len, revs))
1184 revwidth = max(map(len, revs))
1185 ui.note(len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n"))
1185 ui.note(len(revs) > 1 and _("Exporting patches:\n") or _("Exporting patch:\n"))
1186 for cset in revs:
1186 for cset in revs:
1187 seqno += 1
1187 seqno += 1
1188 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1188 doexport(ui, repo, cset, seqno, total, revwidth, opts)
1189
1189
1190 def forget(ui, repo, *pats, **opts):
1190 def forget(ui, repo, *pats, **opts):
1191 """don't add the specified files on the next commit
1191 """don't add the specified files on the next commit
1192
1192
1193 Undo an 'hg add' scheduled for the next commit.
1193 Undo an 'hg add' scheduled for the next commit.
1194 """
1194 """
1195 forget = []
1195 forget = []
1196 for src, abs, rel, exact in walk(repo, pats, opts):
1196 for src, abs, rel, exact in walk(repo, pats, opts):
1197 if repo.dirstate.state(abs) == 'a':
1197 if repo.dirstate.state(abs) == 'a':
1198 forget.append(abs)
1198 forget.append(abs)
1199 if ui.verbose or not exact:
1199 if ui.verbose or not exact:
1200 ui.status(_('forgetting %s\n') % rel)
1200 ui.status(_('forgetting %s\n') % rel)
1201 repo.forget(forget)
1201 repo.forget(forget)
1202
1202
1203 def grep(ui, repo, pattern, *pats, **opts):
1203 def grep(ui, repo, pattern, *pats, **opts):
1204 """search for a pattern in specified files and revisions
1204 """search for a pattern in specified files and revisions
1205
1205
1206 Search revisions of files for a regular expression.
1206 Search revisions of files for a regular expression.
1207
1207
1208 This command behaves differently than Unix grep. It only accepts
1208 This command behaves differently than Unix grep. It only accepts
1209 Python/Perl regexps. It searches repository history, not the
1209 Python/Perl regexps. It searches repository history, not the
1210 working directory. It always prints the revision number in which
1210 working directory. It always prints the revision number in which
1211 a match appears.
1211 a match appears.
1212
1212
1213 By default, grep only prints output for the first revision of a
1213 By default, grep only prints output for the first revision of a
1214 file in which it finds a match. To get it to print every revision
1214 file in which it finds a match. To get it to print every revision
1215 that contains a change in match status ("-" for a match that
1215 that contains a change in match status ("-" for a match that
1216 becomes a non-match, or "+" for a non-match that becomes a match),
1216 becomes a non-match, or "+" for a non-match that becomes a match),
1217 use the --all flag.
1217 use the --all flag.
1218 """
1218 """
1219 reflags = 0
1219 reflags = 0
1220 if opts['ignore_case']:
1220 if opts['ignore_case']:
1221 reflags |= re.I
1221 reflags |= re.I
1222 regexp = re.compile(pattern, reflags)
1222 regexp = re.compile(pattern, reflags)
1223 sep, eol = ':', '\n'
1223 sep, eol = ':', '\n'
1224 if opts['print0']:
1224 if opts['print0']:
1225 sep = eol = '\0'
1225 sep = eol = '\0'
1226
1226
1227 fcache = {}
1227 fcache = {}
1228 def getfile(fn):
1228 def getfile(fn):
1229 if fn not in fcache:
1229 if fn not in fcache:
1230 fcache[fn] = repo.file(fn)
1230 fcache[fn] = repo.file(fn)
1231 return fcache[fn]
1231 return fcache[fn]
1232
1232
1233 def matchlines(body):
1233 def matchlines(body):
1234 begin = 0
1234 begin = 0
1235 linenum = 0
1235 linenum = 0
1236 while True:
1236 while True:
1237 match = regexp.search(body, begin)
1237 match = regexp.search(body, begin)
1238 if not match:
1238 if not match:
1239 break
1239 break
1240 mstart, mend = match.span()
1240 mstart, mend = match.span()
1241 linenum += body.count('\n', begin, mstart) + 1
1241 linenum += body.count('\n', begin, mstart) + 1
1242 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1242 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1243 lend = body.find('\n', mend)
1243 lend = body.find('\n', mend)
1244 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1244 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1245 begin = lend + 1
1245 begin = lend + 1
1246
1246
1247 class linestate(object):
1247 class linestate(object):
1248 def __init__(self, line, linenum, colstart, colend):
1248 def __init__(self, line, linenum, colstart, colend):
1249 self.line = line
1249 self.line = line
1250 self.linenum = linenum
1250 self.linenum = linenum
1251 self.colstart = colstart
1251 self.colstart = colstart
1252 self.colend = colend
1252 self.colend = colend
1253 def __eq__(self, other):
1253 def __eq__(self, other):
1254 return self.line == other.line
1254 return self.line == other.line
1255 def __hash__(self):
1255 def __hash__(self):
1256 return hash(self.line)
1256 return hash(self.line)
1257
1257
1258 matches = {}
1258 matches = {}
1259 def grepbody(fn, rev, body):
1259 def grepbody(fn, rev, body):
1260 matches[rev].setdefault(fn, {})
1260 matches[rev].setdefault(fn, {})
1261 m = matches[rev][fn]
1261 m = matches[rev][fn]
1262 for lnum, cstart, cend, line in matchlines(body):
1262 for lnum, cstart, cend, line in matchlines(body):
1263 s = linestate(line, lnum, cstart, cend)
1263 s = linestate(line, lnum, cstart, cend)
1264 m[s] = s
1264 m[s] = s
1265
1265
1266 prev = {}
1266 prev = {}
1267 ucache = {}
1267 ucache = {}
1268 def display(fn, rev, states, prevstates):
1268 def display(fn, rev, states, prevstates):
1269 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1269 diff = list(sets.Set(states).symmetric_difference(sets.Set(prevstates)))
1270 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1270 diff.sort(lambda x, y: cmp(x.linenum, y.linenum))
1271 counts = {'-': 0, '+': 0}
1271 counts = {'-': 0, '+': 0}
1272 filerevmatches = {}
1272 filerevmatches = {}
1273 for l in diff:
1273 for l in diff:
1274 if incrementing or not opts['all']:
1274 if incrementing or not opts['all']:
1275 change = ((l in prevstates) and '-') or '+'
1275 change = ((l in prevstates) and '-') or '+'
1276 r = rev
1276 r = rev
1277 else:
1277 else:
1278 change = ((l in states) and '-') or '+'
1278 change = ((l in states) and '-') or '+'
1279 r = prev[fn]
1279 r = prev[fn]
1280 cols = [fn, str(rev)]
1280 cols = [fn, str(rev)]
1281 if opts['line_number']: cols.append(str(l.linenum))
1281 if opts['line_number']: cols.append(str(l.linenum))
1282 if opts['all']: cols.append(change)
1282 if opts['all']: cols.append(change)
1283 if opts['user']: cols.append(trimuser(ui, getchange(rev)[1], rev,
1283 if opts['user']: cols.append(trimuser(ui, getchange(rev)[1], rev,
1284 ucache))
1284 ucache))
1285 if opts['files_with_matches']:
1285 if opts['files_with_matches']:
1286 c = (fn, rev)
1286 c = (fn, rev)
1287 if c in filerevmatches: continue
1287 if c in filerevmatches: continue
1288 filerevmatches[c] = 1
1288 filerevmatches[c] = 1
1289 else:
1289 else:
1290 cols.append(l.line)
1290 cols.append(l.line)
1291 ui.write(sep.join(cols), eol)
1291 ui.write(sep.join(cols), eol)
1292 counts[change] += 1
1292 counts[change] += 1
1293 return counts['+'], counts['-']
1293 return counts['+'], counts['-']
1294
1294
1295 fstate = {}
1295 fstate = {}
1296 skip = {}
1296 skip = {}
1297 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1297 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1298 count = 0
1298 count = 0
1299 incrementing = False
1299 incrementing = False
1300 for st, rev, fns in changeiter:
1300 for st, rev, fns in changeiter:
1301 if st == 'window':
1301 if st == 'window':
1302 incrementing = rev
1302 incrementing = rev
1303 matches.clear()
1303 matches.clear()
1304 elif st == 'add':
1304 elif st == 'add':
1305 change = repo.changelog.read(repo.lookup(str(rev)))
1305 change = repo.changelog.read(repo.lookup(str(rev)))
1306 mf = repo.manifest.read(change[0])
1306 mf = repo.manifest.read(change[0])
1307 matches[rev] = {}
1307 matches[rev] = {}
1308 for fn in fns:
1308 for fn in fns:
1309 if fn in skip: continue
1309 if fn in skip: continue
1310 fstate.setdefault(fn, {})
1310 fstate.setdefault(fn, {})
1311 try:
1311 try:
1312 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1312 grepbody(fn, rev, getfile(fn).read(mf[fn]))
1313 except KeyError:
1313 except KeyError:
1314 pass
1314 pass
1315 elif st == 'iter':
1315 elif st == 'iter':
1316 states = matches[rev].items()
1316 states = matches[rev].items()
1317 states.sort()
1317 states.sort()
1318 for fn, m in states:
1318 for fn, m in states:
1319 if fn in skip: continue
1319 if fn in skip: continue
1320 if incrementing or not opts['all'] or fstate[fn]:
1320 if incrementing or not opts['all'] or fstate[fn]:
1321 pos, neg = display(fn, rev, m, fstate[fn])
1321 pos, neg = display(fn, rev, m, fstate[fn])
1322 count += pos + neg
1322 count += pos + neg
1323 if pos and not opts['all']:
1323 if pos and not opts['all']:
1324 skip[fn] = True
1324 skip[fn] = True
1325 fstate[fn] = m
1325 fstate[fn] = m
1326 prev[fn] = rev
1326 prev[fn] = rev
1327
1327
1328 if not incrementing:
1328 if not incrementing:
1329 fstate = fstate.items()
1329 fstate = fstate.items()
1330 fstate.sort()
1330 fstate.sort()
1331 for fn, state in fstate:
1331 for fn, state in fstate:
1332 if fn in skip: continue
1332 if fn in skip: continue
1333 display(fn, rev, {}, state)
1333 display(fn, rev, {}, state)
1334 return (count == 0 and 1) or 0
1334 return (count == 0 and 1) or 0
1335
1335
1336 def heads(ui, repo, **opts):
1336 def heads(ui, repo, **opts):
1337 """show current repository heads
1337 """show current repository heads
1338
1338
1339 Show all repository head changesets.
1339 Show all repository head changesets.
1340
1340
1341 Repository "heads" are changesets that don't have children
1341 Repository "heads" are changesets that don't have children
1342 changesets. They are where development generally takes place and
1342 changesets. They are where development generally takes place and
1343 are the usual targets for update and merge operations.
1343 are the usual targets for update and merge operations.
1344 """
1344 """
1345 if opts['rev']:
1345 if opts['rev']:
1346 heads = repo.heads(repo.lookup(opts['rev']))
1346 heads = repo.heads(repo.lookup(opts['rev']))
1347 else:
1347 else:
1348 heads = repo.heads()
1348 heads = repo.heads()
1349 br = None
1349 br = None
1350 if opts['branches']:
1350 if opts['branches']:
1351 br = repo.branchlookup(heads)
1351 br = repo.branchlookup(heads)
1352 for n in heads:
1352 for n in heads:
1353 show_changeset(ui, repo, changenode=n, brinfo=br)
1353 show_changeset(ui, repo, changenode=n, brinfo=br)
1354
1354
1355 def identify(ui, repo):
1355 def identify(ui, repo):
1356 """print information about the working copy
1356 """print information about the working copy
1357
1357
1358 Print a short summary of the current state of the repo.
1358 Print a short summary of the current state of the repo.
1359
1359
1360 This summary identifies the repository state using one or two parent
1360 This summary identifies the repository state using one or two parent
1361 hash identifiers, followed by a "+" if there are uncommitted changes
1361 hash identifiers, followed by a "+" if there are uncommitted changes
1362 in the working directory, followed by a list of tags for this revision.
1362 in the working directory, followed by a list of tags for this revision.
1363 """
1363 """
1364 parents = [p for p in repo.dirstate.parents() if p != nullid]
1364 parents = [p for p in repo.dirstate.parents() if p != nullid]
1365 if not parents:
1365 if not parents:
1366 ui.write(_("unknown\n"))
1366 ui.write(_("unknown\n"))
1367 return
1367 return
1368
1368
1369 hexfunc = ui.verbose and hex or short
1369 hexfunc = ui.verbose and hex or short
1370 (c, a, d, u) = repo.changes()
1370 (c, a, d, u) = repo.changes()
1371 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
1371 output = ["%s%s" % ('+'.join([hexfunc(parent) for parent in parents]),
1372 (c or a or d) and "+" or "")]
1372 (c or a or d) and "+" or "")]
1373
1373
1374 if not ui.quiet:
1374 if not ui.quiet:
1375 # multiple tags for a single parent separated by '/'
1375 # multiple tags for a single parent separated by '/'
1376 parenttags = ['/'.join(tags)
1376 parenttags = ['/'.join(tags)
1377 for tags in map(repo.nodetags, parents) if tags]
1377 for tags in map(repo.nodetags, parents) if tags]
1378 # tags for multiple parents separated by ' + '
1378 # tags for multiple parents separated by ' + '
1379 if parenttags:
1379 if parenttags:
1380 output.append(' + '.join(parenttags))
1380 output.append(' + '.join(parenttags))
1381
1381
1382 ui.write("%s\n" % ' '.join(output))
1382 ui.write("%s\n" % ' '.join(output))
1383
1383
1384 def import_(ui, repo, patch1, *patches, **opts):
1384 def import_(ui, repo, patch1, *patches, **opts):
1385 """import an ordered set of patches
1385 """import an ordered set of patches
1386
1386
1387 Import a list of patches and commit them individually.
1387 Import a list of patches and commit them individually.
1388
1388
1389 If there are outstanding changes in the working directory, import
1389 If there are outstanding changes in the working directory, import
1390 will abort unless given the -f flag.
1390 will abort unless given the -f flag.
1391
1391
1392 If a patch looks like a mail message (its first line starts with
1392 If a patch looks like a mail message (its first line starts with
1393 "From " or looks like an RFC822 header), it will not be applied
1393 "From " or looks like an RFC822 header), it will not be applied
1394 unless the -f option is used. The importer neither parses nor
1394 unless the -f option is used. The importer neither parses nor
1395 discards mail headers, so use -f only to override the "mailness"
1395 discards mail headers, so use -f only to override the "mailness"
1396 safety check, not to import a real mail message.
1396 safety check, not to import a real mail message.
1397 """
1397 """
1398 patches = (patch1,) + patches
1398 patches = (patch1,) + patches
1399
1399
1400 if not opts['force']:
1400 if not opts['force']:
1401 (c, a, d, u) = repo.changes()
1401 (c, a, d, u) = repo.changes()
1402 if c or a or d:
1402 if c or a or d:
1403 raise util.Abort(_("outstanding uncommitted changes"))
1403 raise util.Abort(_("outstanding uncommitted changes"))
1404
1404
1405 d = opts["base"]
1405 d = opts["base"]
1406 strip = opts["strip"]
1406 strip = opts["strip"]
1407
1407
1408 mailre = re.compile(r'(?:From |[\w-]+:)')
1408 mailre = re.compile(r'(?:From |[\w-]+:)')
1409
1409
1410 # attempt to detect the start of a patch
1410 # attempt to detect the start of a patch
1411 # (this heuristic is borrowed from quilt)
1411 # (this heuristic is borrowed from quilt)
1412 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1412 diffre = re.compile(r'(?:Index:[ \t]|diff[ \t]|RCS file: |' +
1413 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1413 'retrieving revision [0-9]+(\.[0-9]+)*$|' +
1414 '(---|\*\*\*)[ \t])')
1414 '(---|\*\*\*)[ \t])')
1415
1415
1416 for patch in patches:
1416 for patch in patches:
1417 ui.status(_("applying %s\n") % patch)
1417 ui.status(_("applying %s\n") % patch)
1418 pf = os.path.join(d, patch)
1418 pf = os.path.join(d, patch)
1419
1419
1420 message = []
1420 message = []
1421 user = None
1421 user = None
1422 hgpatch = False
1422 hgpatch = False
1423 for line in file(pf):
1423 for line in file(pf):
1424 line = line.rstrip()
1424 line = line.rstrip()
1425 if (not message and not hgpatch and
1425 if (not message and not hgpatch and
1426 mailre.match(line) and not opts['force']):
1426 mailre.match(line) and not opts['force']):
1427 if len(line) > 35: line = line[:32] + '...'
1427 if len(line) > 35: line = line[:32] + '...'
1428 raise util.Abort(_('first line looks like a '
1428 raise util.Abort(_('first line looks like a '
1429 'mail header: ') + line)
1429 'mail header: ') + line)
1430 if diffre.match(line):
1430 if diffre.match(line):
1431 break
1431 break
1432 elif hgpatch:
1432 elif hgpatch:
1433 # parse values when importing the result of an hg export
1433 # parse values when importing the result of an hg export
1434 if line.startswith("# User "):
1434 if line.startswith("# User "):
1435 user = line[7:]
1435 user = line[7:]
1436 ui.debug(_('User: %s\n') % user)
1436 ui.debug(_('User: %s\n') % user)
1437 elif not line.startswith("# ") and line:
1437 elif not line.startswith("# ") and line:
1438 message.append(line)
1438 message.append(line)
1439 hgpatch = False
1439 hgpatch = False
1440 elif line == '# HG changeset patch':
1440 elif line == '# HG changeset patch':
1441 hgpatch = True
1441 hgpatch = True
1442 message = [] # We may have collected garbage
1442 message = [] # We may have collected garbage
1443 else:
1443 else:
1444 message.append(line)
1444 message.append(line)
1445
1445
1446 # make sure message isn't empty
1446 # make sure message isn't empty
1447 if not message:
1447 if not message:
1448 message = _("imported patch %s\n") % patch
1448 message = _("imported patch %s\n") % patch
1449 else:
1449 else:
1450 message = "%s\n" % '\n'.join(message)
1450 message = "%s\n" % '\n'.join(message)
1451 ui.debug(_('message:\n%s\n') % message)
1451 ui.debug(_('message:\n%s\n') % message)
1452
1452
1453 files = util.patch(strip, pf, ui)
1453 files = util.patch(strip, pf, ui)
1454
1454
1455 if len(files) > 0:
1455 if len(files) > 0:
1456 addremove(ui, repo, *files)
1456 addremove(ui, repo, *files)
1457 repo.commit(files, message, user)
1457 repo.commit(files, message, user)
1458
1458
1459 def incoming(ui, repo, source="default", **opts):
1459 def incoming(ui, repo, source="default", **opts):
1460 """show new changesets found in source
1460 """show new changesets found in source
1461
1461
1462 Show new changesets found in the specified repo or the default
1462 Show new changesets found in the specified repo or the default
1463 pull repo. These are the changesets that would be pulled if a pull
1463 pull repo. These are the changesets that would be pulled if a pull
1464 was requested.
1464 was requested.
1465
1465
1466 Currently only local repositories are supported.
1466 Currently only local repositories are supported.
1467 """
1467 """
1468 source = ui.expandpath(source, repo.root)
1468 source = ui.expandpath(source, repo.root)
1469 other = hg.repository(ui, source)
1469 other = hg.repository(ui, source)
1470 if not other.local():
1470 if not other.local():
1471 raise util.Abort(_("incoming doesn't work for remote repositories yet"))
1471 raise util.Abort(_("incoming doesn't work for remote repositories yet"))
1472 o = repo.findincoming(other)
1472 o = repo.findincoming(other)
1473 if not o:
1473 if not o:
1474 return
1474 return
1475 o = other.changelog.nodesbetween(o)[0]
1475 o = other.changelog.nodesbetween(o)[0]
1476 if opts['newest_first']:
1476 if opts['newest_first']:
1477 o.reverse()
1477 o.reverse()
1478 for n in o:
1478 for n in o:
1479 parents = [p for p in other.changelog.parents(n) if p != nullid]
1479 parents = [p for p in other.changelog.parents(n) if p != nullid]
1480 if opts['no_merges'] and len(parents) == 2:
1480 if opts['no_merges'] and len(parents) == 2:
1481 continue
1481 continue
1482 show_changeset(ui, other, changenode=n)
1482 show_changeset(ui, other, changenode=n)
1483 if opts['patch']:
1483 if opts['patch']:
1484 prev = (parents and parents[0]) or nullid
1484 prev = (parents and parents[0]) or nullid
1485 dodiff(ui, ui, other, prev, n)
1485 dodiff(ui, ui, other, prev, n)
1486 ui.write("\n")
1486 ui.write("\n")
1487
1487
1488 def init(ui, dest="."):
1488 def init(ui, dest="."):
1489 """create a new repository in the given directory
1489 """create a new repository in the given directory
1490
1490
1491 Initialize a new repository in the given directory. If the given
1491 Initialize a new repository in the given directory. If the given
1492 directory does not exist, it is created.
1492 directory does not exist, it is created.
1493
1493
1494 If no directory is given, the current directory is used.
1494 If no directory is given, the current directory is used.
1495 """
1495 """
1496 if not os.path.exists(dest):
1496 if not os.path.exists(dest):
1497 os.mkdir(dest)
1497 os.mkdir(dest)
1498 hg.repository(ui, dest, create=1)
1498 hg.repository(ui, dest, create=1)
1499
1499
1500 def locate(ui, repo, *pats, **opts):
1500 def locate(ui, repo, *pats, **opts):
1501 """locate files matching specific patterns
1501 """locate files matching specific patterns
1502
1502
1503 Print all files under Mercurial control whose names match the
1503 Print all files under Mercurial control whose names match the
1504 given patterns.
1504 given patterns.
1505
1505
1506 This command searches the current directory and its
1506 This command searches the current directory and its
1507 subdirectories. To search an entire repository, move to the root
1507 subdirectories. To search an entire repository, move to the root
1508 of the repository.
1508 of the repository.
1509
1509
1510 If no patterns are given to match, this command prints all file
1510 If no patterns are given to match, this command prints all file
1511 names.
1511 names.
1512
1512
1513 If you want to feed the output of this command into the "xargs"
1513 If you want to feed the output of this command into the "xargs"
1514 command, use the "-0" option to both this command and "xargs".
1514 command, use the "-0" option to both this command and "xargs".
1515 This will avoid the problem of "xargs" treating single filenames
1515 This will avoid the problem of "xargs" treating single filenames
1516 that contain white space as multiple filenames.
1516 that contain white space as multiple filenames.
1517 """
1517 """
1518 end = opts['print0'] and '\0' or '\n'
1518 end = opts['print0'] and '\0' or '\n'
1519
1519
1520 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1520 for src, abs, rel, exact in walk(repo, pats, opts, '(?:.*/|)'):
1521 if repo.dirstate.state(abs) == '?':
1521 if repo.dirstate.state(abs) == '?':
1522 continue
1522 continue
1523 if opts['fullpath']:
1523 if opts['fullpath']:
1524 ui.write(os.path.join(repo.root, abs), end)
1524 ui.write(os.path.join(repo.root, abs), end)
1525 else:
1525 else:
1526 ui.write(rel, end)
1526 ui.write(rel, end)
1527
1527
1528 def log(ui, repo, *pats, **opts):
1528 def log(ui, repo, *pats, **opts):
1529 """show revision history of entire repository or files
1529 """show revision history of entire repository or files
1530
1530
1531 Print the revision history of the specified files or the entire project.
1531 Print the revision history of the specified files or the entire project.
1532
1532
1533 By default this command outputs: changeset id and hash, tags,
1533 By default this command outputs: changeset id and hash, tags,
1534 non-trivial parents, user, date and time, and a summary for each
1534 non-trivial parents, user, date and time, and a summary for each
1535 commit. When the -v/--verbose switch is used, the list of changed
1535 commit. When the -v/--verbose switch is used, the list of changed
1536 files and full commit message is shown.
1536 files and full commit message is shown.
1537 """
1537 """
1538 class dui(object):
1538 class dui(object):
1539 # Implement and delegate some ui protocol. Save hunks of
1539 # Implement and delegate some ui protocol. Save hunks of
1540 # output for later display in the desired order.
1540 # output for later display in the desired order.
1541 def __init__(self, ui):
1541 def __init__(self, ui):
1542 self.ui = ui
1542 self.ui = ui
1543 self.hunk = {}
1543 self.hunk = {}
1544 def bump(self, rev):
1544 def bump(self, rev):
1545 self.rev = rev
1545 self.rev = rev
1546 self.hunk[rev] = []
1546 self.hunk[rev] = []
1547 def note(self, *args):
1547 def note(self, *args):
1548 if self.verbose:
1548 if self.verbose:
1549 self.write(*args)
1549 self.write(*args)
1550 def status(self, *args):
1550 def status(self, *args):
1551 if not self.quiet:
1551 if not self.quiet:
1552 self.write(*args)
1552 self.write(*args)
1553 def write(self, *args):
1553 def write(self, *args):
1554 self.hunk[self.rev].append(args)
1554 self.hunk[self.rev].append(args)
1555 def debug(self, *args):
1555 def debug(self, *args):
1556 if self.debugflag:
1556 if self.debugflag:
1557 self.write(*args)
1557 self.write(*args)
1558 def __getattr__(self, key):
1558 def __getattr__(self, key):
1559 return getattr(self.ui, key)
1559 return getattr(self.ui, key)
1560 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1560 changeiter, getchange, matchfn = walkchangerevs(ui, repo, pats, opts)
1561 for st, rev, fns in changeiter:
1561 for st, rev, fns in changeiter:
1562 if st == 'window':
1562 if st == 'window':
1563 du = dui(ui)
1563 du = dui(ui)
1564 elif st == 'add':
1564 elif st == 'add':
1565 du.bump(rev)
1565 du.bump(rev)
1566 changenode = repo.changelog.node(rev)
1566 changenode = repo.changelog.node(rev)
1567 parents = [p for p in repo.changelog.parents(changenode)
1567 parents = [p for p in repo.changelog.parents(changenode)
1568 if p != nullid]
1568 if p != nullid]
1569 if opts['no_merges'] and len(parents) == 2:
1569 if opts['no_merges'] and len(parents) == 2:
1570 continue
1570 continue
1571 if opts['only_merges'] and len(parents) != 2:
1571 if opts['only_merges'] and len(parents) != 2:
1572 continue
1572 continue
1573
1573
1574 br = None
1574 br = None
1575 if opts['keyword']:
1575 if opts['keyword']:
1576 changes = getchange(rev)
1576 changes = getchange(rev)
1577 miss = 0
1577 miss = 0
1578 for k in [kw.lower() for kw in opts['keyword']]:
1578 for k in [kw.lower() for kw in opts['keyword']]:
1579 if not (k in changes[1].lower() or
1579 if not (k in changes[1].lower() or
1580 k in changes[4].lower() or
1580 k in changes[4].lower() or
1581 k in " ".join(changes[3][:20]).lower()):
1581 k in " ".join(changes[3][:20]).lower()):
1582 miss = 1
1582 miss = 1
1583 break
1583 break
1584 if miss:
1584 if miss:
1585 continue
1585 continue
1586
1586
1587 if opts['branch']:
1587 if opts['branch']:
1588 br = repo.branchlookup([repo.changelog.node(rev)])
1588 br = repo.branchlookup([repo.changelog.node(rev)])
1589
1589
1590 show_changeset(du, repo, rev, brinfo=br)
1590 show_changeset(du, repo, rev, brinfo=br)
1591 if opts['patch']:
1591 if opts['patch']:
1592 prev = (parents and parents[0]) or nullid
1592 prev = (parents and parents[0]) or nullid
1593 dodiff(du, du, repo, prev, changenode, match=matchfn)
1593 dodiff(du, du, repo, prev, changenode, match=matchfn)
1594 du.write("\n\n")
1594 du.write("\n\n")
1595 elif st == 'iter':
1595 elif st == 'iter':
1596 for args in du.hunk[rev]:
1596 for args in du.hunk[rev]:
1597 ui.write(*args)
1597 ui.write(*args)
1598
1598
1599 def manifest(ui, repo, rev=None):
1599 def manifest(ui, repo, rev=None):
1600 """output the latest or given revision of the project manifest
1600 """output the latest or given revision of the project manifest
1601
1601
1602 Print a list of version controlled files for the given revision.
1602 Print a list of version controlled files for the given revision.
1603
1603
1604 The manifest is the list of files being version controlled. If no revision
1604 The manifest is the list of files being version controlled. If no revision
1605 is given then the tip is used.
1605 is given then the tip is used.
1606 """
1606 """
1607 if rev:
1607 if rev:
1608 try:
1608 try:
1609 # assume all revision numbers are for changesets
1609 # assume all revision numbers are for changesets
1610 n = repo.lookup(rev)
1610 n = repo.lookup(rev)
1611 change = repo.changelog.read(n)
1611 change = repo.changelog.read(n)
1612 n = change[0]
1612 n = change[0]
1613 except hg.RepoError:
1613 except hg.RepoError:
1614 n = repo.manifest.lookup(rev)
1614 n = repo.manifest.lookup(rev)
1615 else:
1615 else:
1616 n = repo.manifest.tip()
1616 n = repo.manifest.tip()
1617 m = repo.manifest.read(n)
1617 m = repo.manifest.read(n)
1618 mf = repo.manifest.readflags(n)
1618 mf = repo.manifest.readflags(n)
1619 files = m.keys()
1619 files = m.keys()
1620 files.sort()
1620 files.sort()
1621
1621
1622 for f in files:
1622 for f in files:
1623 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1623 ui.write("%40s %3s %s\n" % (hex(m[f]), mf[f] and "755" or "644", f))
1624
1624
1625 def outgoing(ui, repo, dest="default-push", **opts):
1625 def outgoing(ui, repo, dest="default-push", **opts):
1626 """show changesets not found in destination
1626 """show changesets not found in destination
1627
1627
1628 Show changesets not found in the specified destination repo or the
1628 Show changesets not found in the specified destination repo or the
1629 default push repo. These are the changesets that would be pushed
1629 default push repo. These are the changesets that would be pushed
1630 if a push was requested.
1630 if a push was requested.
1631 """
1631 """
1632 dest = ui.expandpath(dest, repo.root)
1632 dest = ui.expandpath(dest, repo.root)
1633 other = hg.repository(ui, dest)
1633 other = hg.repository(ui, dest)
1634 o = repo.findoutgoing(other)
1634 o = repo.findoutgoing(other)
1635 o = repo.changelog.nodesbetween(o)[0]
1635 o = repo.changelog.nodesbetween(o)[0]
1636 if opts['newest_first']:
1636 if opts['newest_first']:
1637 o.reverse()
1637 o.reverse()
1638 for n in o:
1638 for n in o:
1639 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1639 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1640 if opts['no_merges'] and len(parents) == 2:
1640 if opts['no_merges'] and len(parents) == 2:
1641 continue
1641 continue
1642 show_changeset(ui, repo, changenode=n)
1642 show_changeset(ui, repo, changenode=n)
1643 if opts['patch']:
1643 if opts['patch']:
1644 prev = (parents and parents[0]) or nullid
1644 prev = (parents and parents[0]) or nullid
1645 dodiff(ui, ui, repo, prev, n)
1645 dodiff(ui, ui, repo, prev, n)
1646 ui.write("\n")
1646 ui.write("\n")
1647
1647
1648 def parents(ui, repo, rev=None):
1648 def parents(ui, repo, rev=None):
1649 """show the parents of the working dir or revision
1649 """show the parents of the working dir or revision
1650
1650
1651 Print the working directory's parent revisions.
1651 Print the working directory's parent revisions.
1652 """
1652 """
1653 if rev:
1653 if rev:
1654 p = repo.changelog.parents(repo.lookup(rev))
1654 p = repo.changelog.parents(repo.lookup(rev))
1655 else:
1655 else:
1656 p = repo.dirstate.parents()
1656 p = repo.dirstate.parents()
1657
1657
1658 for n in p:
1658 for n in p:
1659 if n != nullid:
1659 if n != nullid:
1660 show_changeset(ui, repo, changenode=n)
1660 show_changeset(ui, repo, changenode=n)
1661
1661
1662 def paths(ui, search=None):
1662 def paths(ui, search=None):
1663 """show definition of symbolic path names
1663 """show definition of symbolic path names
1664
1664
1665 Show definition of symbolic path name NAME. If no name is given, show
1665 Show definition of symbolic path name NAME. If no name is given, show
1666 definition of available names.
1666 definition of available names.
1667
1667
1668 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1668 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1669 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1669 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1670 """
1670 """
1671 try:
1671 try:
1672 repo = hg.repository(ui=ui)
1672 repo = hg.repository(ui=ui)
1673 except hg.RepoError:
1673 except hg.RepoError:
1674 pass
1674 pass
1675
1675
1676 if search:
1676 if search:
1677 for name, path in ui.configitems("paths"):
1677 for name, path in ui.configitems("paths"):
1678 if name == search:
1678 if name == search:
1679 ui.write("%s\n" % path)
1679 ui.write("%s\n" % path)
1680 return
1680 return
1681 ui.warn(_("not found!\n"))
1681 ui.warn(_("not found!\n"))
1682 return 1
1682 return 1
1683 else:
1683 else:
1684 for name, path in ui.configitems("paths"):
1684 for name, path in ui.configitems("paths"):
1685 ui.write("%s = %s\n" % (name, path))
1685 ui.write("%s = %s\n" % (name, path))
1686
1686
1687 def pull(ui, repo, source="default", **opts):
1687 def pull(ui, repo, source="default", **opts):
1688 """pull changes from the specified source
1688 """pull changes from the specified source
1689
1689
1690 Pull changes from a remote repository to a local one.
1690 Pull changes from a remote repository to a local one.
1691
1691
1692 This finds all changes from the repository at the specified path
1692 This finds all changes from the repository at the specified path
1693 or URL and adds them to the local repository. By default, this
1693 or URL and adds them to the local repository. By default, this
1694 does not update the copy of the project in the working directory.
1694 does not update the copy of the project in the working directory.
1695
1695
1696 Valid URLs are of the form:
1696 Valid URLs are of the form:
1697
1697
1698 local/filesystem/path
1698 local/filesystem/path
1699 http://[user@]host[:port][/path]
1699 http://[user@]host[:port][/path]
1700 https://[user@]host[:port][/path]
1700 https://[user@]host[:port][/path]
1701 ssh://[user@]host[:port][/path]
1701 ssh://[user@]host[:port][/path]
1702
1702
1703 SSH requires an accessible shell account on the destination machine
1703 SSH requires an accessible shell account on the destination machine
1704 and a copy of hg in the remote path. With SSH, paths are relative
1704 and a copy of hg in the remote path. With SSH, paths are relative
1705 to the remote user's home directory by default; use two slashes at
1705 to the remote user's home directory by default; use two slashes at
1706 the start of a path to specify it as relative to the filesystem root.
1706 the start of a path to specify it as relative to the filesystem root.
1707 """
1707 """
1708 source = ui.expandpath(source, repo.root)
1708 source = ui.expandpath(source, repo.root)
1709 ui.status(_('pulling from %s\n') % (source))
1709 ui.status(_('pulling from %s\n') % (source))
1710
1710
1711 if opts['ssh']:
1711 if opts['ssh']:
1712 ui.setconfig("ui", "ssh", opts['ssh'])
1712 ui.setconfig("ui", "ssh", opts['ssh'])
1713 if opts['remotecmd']:
1713 if opts['remotecmd']:
1714 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1714 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
1715
1715
1716 other = hg.repository(ui, source)
1716 other = hg.repository(ui, source)
1717 revs = None
1717 revs = None
1718 if opts['rev'] and not other.local():
1718 if opts['rev'] and not other.local():
1719 raise util.Abort("pull -r doesn't work for remote repositories yet")
1719 raise util.Abort("pull -r doesn't work for remote repositories yet")
1720 elif opts['rev']:
1720 elif opts['rev']:
1721 revs = [other.lookup(rev) for rev in opts['rev']]
1721 revs = [other.lookup(rev) for rev in opts['rev']]
1722 r = repo.pull(other, heads=revs)
1722 r = repo.pull(other, heads=revs)
1723 if not r:
1723 if not r:
1724 if opts['update']:
1724 if opts['update']:
1725 return update(ui, repo)
1725 return update(ui, repo)
1726 else:
1726 else:
1727 ui.status(_("(run 'hg update' to get a working copy)\n"))
1727 ui.status(_("(run 'hg update' to get a working copy)\n"))
1728
1728
1729 return r
1729 return r
1730
1730
1731 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1731 def push(ui, repo, dest="default-push", force=False, ssh=None, remotecmd=None):
1732 """push changes to the specified destination
1732 """push changes to the specified destination
1733
1733
1734 Push changes from the local repository to the given destination.
1734 Push changes from the local repository to the given destination.
1735
1735
1736 This is the symmetrical operation for pull. It helps to move
1736 This is the symmetrical operation for pull. It helps to move
1737 changes from the current repository to a different one. If the
1737 changes from the current repository to a different one. If the
1738 destination is local this is identical to a pull in that directory
1738 destination is local this is identical to a pull in that directory
1739 from the current one.
1739 from the current one.
1740
1740
1741 By default, push will refuse to run if it detects the result would
1741 By default, push will refuse to run if it detects the result would
1742 increase the number of remote heads. This generally indicates the
1742 increase the number of remote heads. This generally indicates the
1743 the client has forgotten to sync and merge before pushing.
1743 the client has forgotten to sync and merge before pushing.
1744
1744
1745 Valid URLs are of the form:
1745 Valid URLs are of the form:
1746
1746
1747 local/filesystem/path
1747 local/filesystem/path
1748 ssh://[user@]host[:port][/path]
1748 ssh://[user@]host[:port][/path]
1749
1749
1750 SSH requires an accessible shell account on the destination
1750 SSH requires an accessible shell account on the destination
1751 machine and a copy of hg in the remote path.
1751 machine and a copy of hg in the remote path.
1752 """
1752 """
1753 dest = ui.expandpath(dest, repo.root)
1753 dest = ui.expandpath(dest, repo.root)
1754 ui.status('pushing to %s\n' % (dest))
1754 ui.status('pushing to %s\n' % (dest))
1755
1755
1756 if ssh:
1756 if ssh:
1757 ui.setconfig("ui", "ssh", ssh)
1757 ui.setconfig("ui", "ssh", ssh)
1758 if remotecmd:
1758 if remotecmd:
1759 ui.setconfig("ui", "remotecmd", remotecmd)
1759 ui.setconfig("ui", "remotecmd", remotecmd)
1760
1760
1761 other = hg.repository(ui, dest)
1761 other = hg.repository(ui, dest)
1762 r = repo.push(other, force)
1762 r = repo.push(other, force)
1763 return r
1763 return r
1764
1764
1765 def rawcommit(ui, repo, *flist, **rc):
1765 def rawcommit(ui, repo, *flist, **rc):
1766 """raw commit interface
1766 """raw commit interface
1767
1767
1768 Lowlevel commit, for use in helper scripts.
1768 Lowlevel commit, for use in helper scripts.
1769
1769
1770 This command is not intended to be used by normal users, as it is
1770 This command is not intended to be used by normal users, as it is
1771 primarily useful for importing from other SCMs.
1771 primarily useful for importing from other SCMs.
1772 """
1772 """
1773 message = rc['message']
1773 message = rc['message']
1774 if not message and rc['logfile']:
1774 if not message and rc['logfile']:
1775 try:
1775 try:
1776 message = open(rc['logfile']).read()
1776 message = open(rc['logfile']).read()
1777 except IOError:
1777 except IOError:
1778 pass
1778 pass
1779 if not message and not rc['logfile']:
1779 if not message and not rc['logfile']:
1780 raise util.Abort(_("missing commit message"))
1780 raise util.Abort(_("missing commit message"))
1781
1781
1782 files = relpath(repo, list(flist))
1782 files = relpath(repo, list(flist))
1783 if rc['files']:
1783 if rc['files']:
1784 files += open(rc['files']).read().splitlines()
1784 files += open(rc['files']).read().splitlines()
1785
1785
1786 rc['parent'] = map(repo.lookup, rc['parent'])
1786 rc['parent'] = map(repo.lookup, rc['parent'])
1787
1787
1788 try:
1788 try:
1789 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1789 repo.rawcommit(files, message, rc['user'], rc['date'], *rc['parent'])
1790 except ValueError, inst:
1790 except ValueError, inst:
1791 raise util.Abort(str(inst))
1791 raise util.Abort(str(inst))
1792
1792
1793 def recover(ui, repo):
1793 def recover(ui, repo):
1794 """roll back an interrupted transaction
1794 """roll back an interrupted transaction
1795
1795
1796 Recover from an interrupted commit or pull.
1796 Recover from an interrupted commit or pull.
1797
1797
1798 This command tries to fix the repository status after an interrupted
1798 This command tries to fix the repository status after an interrupted
1799 operation. It should only be necessary when Mercurial suggests it.
1799 operation. It should only be necessary when Mercurial suggests it.
1800 """
1800 """
1801 if repo.recover():
1801 if repo.recover():
1802 return repo.verify()
1802 return repo.verify()
1803 return False
1803 return False
1804
1804
1805 def remove(ui, repo, pat, *pats, **opts):
1805 def remove(ui, repo, pat, *pats, **opts):
1806 """remove the specified files on the next commit
1806 """remove the specified files on the next commit
1807
1807
1808 Schedule the indicated files for removal from the repository.
1808 Schedule the indicated files for removal from the repository.
1809
1809
1810 This command schedules the files to be removed at the next commit.
1810 This command schedules the files to be removed at the next commit.
1811 This only removes files from the current branch, not from the
1811 This only removes files from the current branch, not from the
1812 entire project history. If the files still exist in the working
1812 entire project history. If the files still exist in the working
1813 directory, they will be deleted from it.
1813 directory, they will be deleted from it.
1814 """
1814 """
1815 names = []
1815 names = []
1816 def okaytoremove(abs, rel, exact):
1816 def okaytoremove(abs, rel, exact):
1817 c, a, d, u = repo.changes(files = [abs])
1817 c, a, d, u = repo.changes(files = [abs])
1818 reason = None
1818 reason = None
1819 if c: reason = _('is modified')
1819 if c: reason = _('is modified')
1820 elif a: reason = _('has been marked for add')
1820 elif a: reason = _('has been marked for add')
1821 elif u: reason = _('is not managed')
1821 elif u: reason = _('is not managed')
1822 if reason:
1822 if reason:
1823 if exact: ui.warn(_('not removing %s: file %s\n') % (rel, reason))
1823 if exact: ui.warn(_('not removing %s: file %s\n') % (rel, reason))
1824 else:
1824 else:
1825 return True
1825 return True
1826 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1826 for src, abs, rel, exact in walk(repo, (pat,) + pats, opts):
1827 if okaytoremove(abs, rel, exact):
1827 if okaytoremove(abs, rel, exact):
1828 if ui.verbose or not exact: ui.status(_('removing %s\n') % rel)
1828 if ui.verbose or not exact: ui.status(_('removing %s\n') % rel)
1829 names.append(abs)
1829 names.append(abs)
1830 repo.remove(names, unlink=True)
1830 repo.remove(names, unlink=True)
1831
1831
1832 def rename(ui, repo, *pats, **opts):
1832 def rename(ui, repo, *pats, **opts):
1833 """rename files; equivalent of copy + remove
1833 """rename files; equivalent of copy + remove
1834
1834
1835 Mark dest as copies of sources; mark sources for deletion. If
1835 Mark dest as copies of sources; mark sources for deletion. If
1836 dest is a directory, copies are put in that directory. If dest is
1836 dest is a directory, copies are put in that directory. If dest is
1837 a file, there can only be one source.
1837 a file, there can only be one source.
1838
1838
1839 By default, this command copies the contents of files as they
1839 By default, this command copies the contents of files as they
1840 stand in the working directory. If invoked with --after, the
1840 stand in the working directory. If invoked with --after, the
1841 operation is recorded, but no copying is performed.
1841 operation is recorded, but no copying is performed.
1842
1842
1843 This command takes effect in the next commit.
1843 This command takes effect in the next commit.
1844
1844
1845 NOTE: This command should be treated as experimental. While it
1845 NOTE: This command should be treated as experimental. While it
1846 should properly record rename files, this information is not yet
1846 should properly record rename files, this information is not yet
1847 fully used by merge, nor fully reported by log.
1847 fully used by merge, nor fully reported by log.
1848 """
1848 """
1849 errs, copied = docopy(ui, repo, pats, opts)
1849 errs, copied = docopy(ui, repo, pats, opts)
1850 names = []
1850 names = []
1851 for abs, rel, exact in copied:
1851 for abs, rel, exact in copied:
1852 if ui.verbose or not exact: ui.status(_('removing %s\n') % rel)
1852 if ui.verbose or not exact: ui.status(_('removing %s\n') % rel)
1853 names.append(abs)
1853 names.append(abs)
1854 repo.remove(names, unlink=True)
1854 repo.remove(names, unlink=True)
1855 return errs
1855 return errs
1856
1856
1857 def revert(ui, repo, *pats, **opts):
1857 def revert(ui, repo, *pats, **opts):
1858 """revert modified files or dirs back to their unmodified states
1858 """revert modified files or dirs back to their unmodified states
1859
1859
1860 Revert any uncommitted modifications made to the named files or
1860 Revert any uncommitted modifications made to the named files or
1861 directories. This restores the contents of the affected files to
1861 directories. This restores the contents of the affected files to
1862 an unmodified state.
1862 an unmodified state.
1863
1863
1864 If a file has been deleted, it is recreated. If the executable
1864 If a file has been deleted, it is recreated. If the executable
1865 mode of a file was changed, it is reset.
1865 mode of a file was changed, it is reset.
1866
1866
1867 If names are given, all files matching the names are reverted.
1867 If names are given, all files matching the names are reverted.
1868
1868
1869 If no arguments are given, all files in the repository are reverted.
1869 If no arguments are given, all files in the repository are reverted.
1870 """
1870 """
1871 node = opts['rev'] and repo.lookup(opts['rev']) or \
1871 node = opts['rev'] and repo.lookup(opts['rev']) or \
1872 repo.dirstate.parents()[0]
1872 repo.dirstate.parents()[0]
1873
1873
1874 files, choose, anypats, cwd = matchpats(repo, pats, opts)
1874 files, choose, anypats, cwd = matchpats(repo, pats, opts)
1875 (c, a, d, u) = repo.changes(match=choose)
1875 (c, a, d, u) = repo.changes(match=choose)
1876 repo.forget(a)
1876 repo.forget(a)
1877 repo.undelete(d)
1877 repo.undelete(d)
1878
1878
1879 return repo.update(node, False, True, choose, False)
1879 return repo.update(node, False, True, choose, False)
1880
1880
1881 def root(ui, repo):
1881 def root(ui, repo):
1882 """print the root (top) of the current working dir
1882 """print the root (top) of the current working dir
1883
1883
1884 Print the root directory of the current repository.
1884 Print the root directory of the current repository.
1885 """
1885 """
1886 ui.write(repo.root + "\n")
1886 ui.write(repo.root + "\n")
1887
1887
1888 def serve(ui, repo, **opts):
1888 def serve(ui, repo, **opts):
1889 """export the repository via HTTP
1889 """export the repository via HTTP
1890
1890
1891 Start a local HTTP repository browser and pull server.
1891 Start a local HTTP repository browser and pull server.
1892
1892
1893 By default, the server logs accesses to stdout and errors to
1893 By default, the server logs accesses to stdout and errors to
1894 stderr. Use the "-A" and "-E" options to log to files.
1894 stderr. Use the "-A" and "-E" options to log to files.
1895 """
1895 """
1896
1896
1897 if opts["stdio"]:
1897 if opts["stdio"]:
1898 fin, fout = sys.stdin, sys.stdout
1898 fin, fout = sys.stdin, sys.stdout
1899 sys.stdout = sys.stderr
1899 sys.stdout = sys.stderr
1900
1900
1901 # Prevent insertion/deletion of CRs
1901 # Prevent insertion/deletion of CRs
1902 util.set_binary(fin)
1902 util.set_binary(fin)
1903 util.set_binary(fout)
1903 util.set_binary(fout)
1904
1904
1905 def getarg():
1905 def getarg():
1906 argline = fin.readline()[:-1]
1906 argline = fin.readline()[:-1]
1907 arg, l = argline.split()
1907 arg, l = argline.split()
1908 val = fin.read(int(l))
1908 val = fin.read(int(l))
1909 return arg, val
1909 return arg, val
1910 def respond(v):
1910 def respond(v):
1911 fout.write("%d\n" % len(v))
1911 fout.write("%d\n" % len(v))
1912 fout.write(v)
1912 fout.write(v)
1913 fout.flush()
1913 fout.flush()
1914
1914
1915 lock = None
1915 lock = None
1916
1916
1917 while 1:
1917 while 1:
1918 cmd = fin.readline()[:-1]
1918 cmd = fin.readline()[:-1]
1919 if cmd == '':
1919 if cmd == '':
1920 return
1920 return
1921 if cmd == "heads":
1921 if cmd == "heads":
1922 h = repo.heads()
1922 h = repo.heads()
1923 respond(" ".join(map(hex, h)) + "\n")
1923 respond(" ".join(map(hex, h)) + "\n")
1924 if cmd == "lock":
1924 if cmd == "lock":
1925 lock = repo.lock()
1925 lock = repo.lock()
1926 respond("")
1926 respond("")
1927 if cmd == "unlock":
1927 if cmd == "unlock":
1928 if lock:
1928 if lock:
1929 lock.release()
1929 lock.release()
1930 lock = None
1930 lock = None
1931 respond("")
1931 respond("")
1932 elif cmd == "branches":
1932 elif cmd == "branches":
1933 arg, nodes = getarg()
1933 arg, nodes = getarg()
1934 nodes = map(bin, nodes.split(" "))
1934 nodes = map(bin, nodes.split(" "))
1935 r = []
1935 r = []
1936 for b in repo.branches(nodes):
1936 for b in repo.branches(nodes):
1937 r.append(" ".join(map(hex, b)) + "\n")
1937 r.append(" ".join(map(hex, b)) + "\n")
1938 respond("".join(r))
1938 respond("".join(r))
1939 elif cmd == "between":
1939 elif cmd == "between":
1940 arg, pairs = getarg()
1940 arg, pairs = getarg()
1941 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
1941 pairs = [map(bin, p.split("-")) for p in pairs.split(" ")]
1942 r = []
1942 r = []
1943 for b in repo.between(pairs):
1943 for b in repo.between(pairs):
1944 r.append(" ".join(map(hex, b)) + "\n")
1944 r.append(" ".join(map(hex, b)) + "\n")
1945 respond("".join(r))
1945 respond("".join(r))
1946 elif cmd == "changegroup":
1946 elif cmd == "changegroup":
1947 nodes = []
1947 nodes = []
1948 arg, roots = getarg()
1948 arg, roots = getarg()
1949 nodes = map(bin, roots.split(" "))
1949 nodes = map(bin, roots.split(" "))
1950
1950
1951 cg = repo.changegroup(nodes)
1951 cg = repo.changegroup(nodes)
1952 while 1:
1952 while 1:
1953 d = cg.read(4096)
1953 d = cg.read(4096)
1954 if not d:
1954 if not d:
1955 break
1955 break
1956 fout.write(d)
1956 fout.write(d)
1957
1957
1958 fout.flush()
1958 fout.flush()
1959
1959
1960 elif cmd == "addchangegroup":
1960 elif cmd == "addchangegroup":
1961 if not lock:
1961 if not lock:
1962 respond("not locked")
1962 respond("not locked")
1963 continue
1963 continue
1964 respond("")
1964 respond("")
1965
1965
1966 r = repo.addchangegroup(fin)
1966 r = repo.addchangegroup(fin)
1967 respond("")
1967 respond("")
1968
1968
1969 optlist = "name templates style address port ipv6 accesslog errorlog"
1969 optlist = "name templates style address port ipv6 accesslog errorlog"
1970 for o in optlist.split():
1970 for o in optlist.split():
1971 if opts[o]:
1971 if opts[o]:
1972 ui.setconfig("web", o, opts[o])
1972 ui.setconfig("web", o, opts[o])
1973
1973
1974 try:
1974 try:
1975 httpd = hgweb.create_server(repo)
1975 httpd = hgweb.create_server(repo)
1976 except socket.error, inst:
1976 except socket.error, inst:
1977 raise util.Abort('cannot start server: ' + inst.args[1])
1977 raise util.Abort('cannot start server: ' + inst.args[1])
1978
1978
1979 if ui.verbose:
1979 if ui.verbose:
1980 addr, port = httpd.socket.getsockname()
1980 addr, port = httpd.socket.getsockname()
1981 if addr == '0.0.0.0':
1981 if addr == '0.0.0.0':
1982 addr = socket.gethostname()
1982 addr = socket.gethostname()
1983 else:
1983 else:
1984 try:
1984 try:
1985 addr = socket.gethostbyaddr(addr)[0]
1985 addr = socket.gethostbyaddr(addr)[0]
1986 except socket.error:
1986 except socket.error:
1987 pass
1987 pass
1988 if port != 80:
1988 if port != 80:
1989 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
1989 ui.status(_('listening at http://%s:%d/\n') % (addr, port))
1990 else:
1990 else:
1991 ui.status(_('listening at http://%s/\n') % addr)
1991 ui.status(_('listening at http://%s/\n') % addr)
1992 httpd.serve_forever()
1992 httpd.serve_forever()
1993
1993
1994 def status(ui, repo, *pats, **opts):
1994 def status(ui, repo, *pats, **opts):
1995 """show changed files in the working directory
1995 """show changed files in the working directory
1996
1996
1997 Show changed files in the repository. If names are
1997 Show changed files in the repository. If names are
1998 given, only files that match are shown.
1998 given, only files that match are shown.
1999
1999
2000 The codes used to show the status of files are:
2000 The codes used to show the status of files are:
2001 M = modified
2001 M = modified
2002 A = added
2002 A = added
2003 R = removed
2003 R = removed
2004 ? = not tracked
2004 ? = not tracked
2005 """
2005 """
2006
2006
2007 files, matchfn, anypats, cwd = matchpats(repo, pats, opts)
2007 files, matchfn, anypats, cwd = matchpats(repo, pats, opts)
2008 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
2008 (c, a, d, u) = [[util.pathto(cwd, x) for x in n]
2009 for n in repo.changes(files=files, match=matchfn)]
2009 for n in repo.changes(files=files, match=matchfn)]
2010
2010
2011 changetypes = [(_('modified'), 'M', c),
2011 changetypes = [(_('modified'), 'M', c),
2012 (_('added'), 'A', a),
2012 (_('added'), 'A', a),
2013 (_('removed'), 'R', d),
2013 (_('removed'), 'R', d),
2014 (_('unknown'), '?', u)]
2014 (_('unknown'), '?', u)]
2015
2015
2016 end = opts['print0'] and '\0' or '\n'
2016 end = opts['print0'] and '\0' or '\n'
2017
2017
2018 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2018 for opt, char, changes in ([ct for ct in changetypes if opts[ct[0]]]
2019 or changetypes):
2019 or changetypes):
2020 if opts['no_status']:
2020 if opts['no_status']:
2021 format = "%%s%s" % end
2021 format = "%%s%s" % end
2022 else:
2022 else:
2023 format = "%s %%s%s" % (char, end);
2023 format = "%s %%s%s" % (char, end);
2024
2024
2025 for f in changes:
2025 for f in changes:
2026 ui.write(format % f)
2026 ui.write(format % f)
2027
2027
2028 def tag(ui, repo, name, rev=None, **opts):
2028 def tag(ui, repo, name, rev=None, **opts):
2029 """add a tag for the current tip or a given revision
2029 """add a tag for the current tip or a given revision
2030
2030
2031 Name a particular revision using <name>.
2031 Name a particular revision using <name>.
2032
2032
2033 Tags are used to name particular revisions of the repository and are
2033 Tags are used to name particular revisions of the repository and are
2034 very useful to compare different revision, to go back to significant
2034 very useful to compare different revision, to go back to significant
2035 earlier versions or to mark branch points as releases, etc.
2035 earlier versions or to mark branch points as releases, etc.
2036
2036
2037 If no revision is given, the tip is used.
2037 If no revision is given, the tip is used.
2038
2038
2039 To facilitate version control, distribution, and merging of tags,
2039 To facilitate version control, distribution, and merging of tags,
2040 they are stored as a file named ".hgtags" which is managed
2040 they are stored as a file named ".hgtags" which is managed
2041 similarly to other project files and can be hand-edited if
2041 similarly to other project files and can be hand-edited if
2042 necessary.
2042 necessary.
2043 """
2043 """
2044 if name == "tip":
2044 if name == "tip":
2045 raise util.Abort(_("the name 'tip' is reserved"))
2045 raise util.Abort(_("the name 'tip' is reserved"))
2046 if 'rev' in opts:
2046 if 'rev' in opts:
2047 rev = opts['rev']
2047 rev = opts['rev']
2048 if rev:
2048 if rev:
2049 r = hex(repo.lookup(rev))
2049 r = hex(repo.lookup(rev))
2050 else:
2050 else:
2051 r = hex(repo.changelog.tip())
2051 r = hex(repo.changelog.tip())
2052
2052
2053 disallowed = (revrangesep, '\r', '\n')
2053 disallowed = (revrangesep, '\r', '\n')
2054 for c in disallowed:
2054 for c in disallowed:
2055 if name.find(c) >= 0:
2055 if name.find(c) >= 0:
2056 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2056 raise util.Abort(_("%s cannot be used in a tag name") % repr(c))
2057
2057
2058 if opts['local']:
2058 if opts['local']:
2059 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2059 repo.opener("localtags", "a").write("%s %s\n" % (r, name))
2060 return
2060 return
2061
2061
2062 (c, a, d, u) = repo.changes()
2062 (c, a, d, u) = repo.changes()
2063 for x in (c, a, d, u):
2063 for x in (c, a, d, u):
2064 if ".hgtags" in x:
2064 if ".hgtags" in x:
2065 raise util.Abort(_("working copy of .hgtags is changed "
2065 raise util.Abort(_("working copy of .hgtags is changed "
2066 "(please commit .hgtags manually)"))
2066 "(please commit .hgtags manually)"))
2067
2067
2068 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2068 repo.wfile(".hgtags", "ab").write("%s %s\n" % (r, name))
2069 if repo.dirstate.state(".hgtags") == '?':
2069 if repo.dirstate.state(".hgtags") == '?':
2070 repo.add([".hgtags"])
2070 repo.add([".hgtags"])
2071
2071
2072 message = (opts['message'] or
2072 message = (opts['message'] or
2073 _("Added tag %s for changeset %s") % (name, r))
2073 _("Added tag %s for changeset %s") % (name, r))
2074 try:
2074 try:
2075 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2075 repo.commit([".hgtags"], message, opts['user'], opts['date'])
2076 except ValueError, inst:
2076 except ValueError, inst:
2077 raise util.Abort(str(inst))
2077 raise util.Abort(str(inst))
2078
2078
2079 def tags(ui, repo):
2079 def tags(ui, repo):
2080 """list repository tags
2080 """list repository tags
2081
2081
2082 List the repository tags.
2082 List the repository tags.
2083
2083
2084 This lists both regular and local tags.
2084 This lists both regular and local tags.
2085 """
2085 """
2086
2086
2087 l = repo.tagslist()
2087 l = repo.tagslist()
2088 l.reverse()
2088 l.reverse()
2089 for t, n in l:
2089 for t, n in l:
2090 try:
2090 try:
2091 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2091 r = "%5d:%s" % (repo.changelog.rev(n), hex(n))
2092 except KeyError:
2092 except KeyError:
2093 r = " ?:?"
2093 r = " ?:?"
2094 ui.write("%-30s %s\n" % (t, r))
2094 ui.write("%-30s %s\n" % (t, r))
2095
2095
2096 def tip(ui, repo):
2096 def tip(ui, repo):
2097 """show the tip revision
2097 """show the tip revision
2098
2098
2099 Show the tip revision.
2099 Show the tip revision.
2100 """
2100 """
2101 n = repo.changelog.tip()
2101 n = repo.changelog.tip()
2102 show_changeset(ui, repo, changenode=n)
2102 show_changeset(ui, repo, changenode=n)
2103
2103
2104 def unbundle(ui, repo, fname):
2104 def unbundle(ui, repo, fname):
2105 """apply a changegroup file
2105 """apply a changegroup file
2106
2106
2107 Apply a compressed changegroup file generated by the bundle
2107 Apply a compressed changegroup file generated by the bundle
2108 command.
2108 command.
2109 """
2109 """
2110 f = urllib.urlopen(fname)
2110 f = urllib.urlopen(fname)
2111
2111
2112 if f.read(4) != "HG10":
2112 if f.read(4) != "HG10":
2113 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2113 raise util.Abort(_("%s: not a Mercurial bundle file") % fname)
2114
2114
2115 def bzgenerator(f):
2115 def bzgenerator(f):
2116 zd = bz2.BZ2Decompressor()
2116 zd = bz2.BZ2Decompressor()
2117 for chunk in f:
2117 for chunk in f:
2118 yield zd.decompress(chunk)
2118 yield zd.decompress(chunk)
2119
2119
2120 bzgen = bzgenerator(util.filechunkiter(f, 4096))
2120 bzgen = bzgenerator(util.filechunkiter(f, 4096))
2121 repo.addchangegroup(util.chunkbuffer(bzgen))
2121 repo.addchangegroup(util.chunkbuffer(bzgen))
2122
2122
2123 def undo(ui, repo):
2123 def undo(ui, repo):
2124 """undo the last commit or pull
2124 """undo the last commit or pull
2125
2125
2126 Roll back the last pull or commit transaction on the
2126 Roll back the last pull or commit transaction on the
2127 repository, restoring the project to its earlier state.
2127 repository, restoring the project to its earlier state.
2128
2128
2129 This command should be used with care. There is only one level of
2129 This command should be used with care. There is only one level of
2130 undo and there is no redo.
2130 undo and there is no redo.
2131
2131
2132 This command is not intended for use on public repositories. Once
2132 This command is not intended for use on public repositories. Once
2133 a change is visible for pull by other users, undoing it locally is
2133 a change is visible for pull by other users, undoing it locally is
2134 ineffective.
2134 ineffective.
2135 """
2135 """
2136 repo.undo()
2136 repo.undo()
2137
2137
2138 def update(ui, repo, node=None, merge=False, clean=False, branch=None):
2138 def update(ui, repo, node=None, merge=False, clean=False, force=None,
2139 branch=None):
2139 """update or merge working directory
2140 """update or merge working directory
2140
2141
2141 Update the working directory to the specified revision.
2142 Update the working directory to the specified revision.
2142
2143
2143 If there are no outstanding changes in the working directory and
2144 If there are no outstanding changes in the working directory and
2144 there is a linear relationship between the current version and the
2145 there is a linear relationship between the current version and the
2145 requested version, the result is the requested version.
2146 requested version, the result is the requested version.
2146
2147
2147 Otherwise the result is a merge between the contents of the
2148 Otherwise the result is a merge between the contents of the
2148 current working directory and the requested version. Files that
2149 current working directory and the requested version. Files that
2149 changed between either parent are marked as changed for the next
2150 changed between either parent are marked as changed for the next
2150 commit and a commit must be performed before any further updates
2151 commit and a commit must be performed before any further updates
2151 are allowed.
2152 are allowed.
2152
2153
2153 By default, update will refuse to run if doing so would require
2154 By default, update will refuse to run if doing so would require
2154 merging or discarding local changes.
2155 merging or discarding local changes.
2155 """
2156 """
2156 if branch:
2157 if branch:
2157 br = repo.branchlookup(branch=branch)
2158 br = repo.branchlookup(branch=branch)
2158 found = []
2159 found = []
2159 for x in br:
2160 for x in br:
2160 if branch in br[x]:
2161 if branch in br[x]:
2161 found.append(x)
2162 found.append(x)
2162 if len(found) > 1:
2163 if len(found) > 1:
2163 ui.warn(_("Found multiple heads for %s\n") % branch)
2164 ui.warn(_("Found multiple heads for %s\n") % branch)
2164 for x in found:
2165 for x in found:
2165 show_changeset(ui, repo, changenode=x, brinfo=br)
2166 show_changeset(ui, repo, changenode=x, brinfo=br)
2166 return 1
2167 return 1
2167 if len(found) == 1:
2168 if len(found) == 1:
2168 node = found[0]
2169 node = found[0]
2169 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2170 ui.warn(_("Using head %s for branch %s\n") % (short(node), branch))
2170 else:
2171 else:
2171 ui.warn(_("branch %s not found\n") % (branch))
2172 ui.warn(_("branch %s not found\n") % (branch))
2172 return 1
2173 return 1
2173 else:
2174 else:
2174 node = node and repo.lookup(node) or repo.changelog.tip()
2175 node = node and repo.lookup(node) or repo.changelog.tip()
2175 return repo.update(node, allow=merge, force=clean)
2176 return repo.update(node, allow=merge, force=clean, forcemerge=force)
2176
2177
2177 def verify(ui, repo):
2178 def verify(ui, repo):
2178 """verify the integrity of the repository
2179 """verify the integrity of the repository
2179
2180
2180 Verify the integrity of the current repository.
2181 Verify the integrity of the current repository.
2181
2182
2182 This will perform an extensive check of the repository's
2183 This will perform an extensive check of the repository's
2183 integrity, validating the hashes and checksums of each entry in
2184 integrity, validating the hashes and checksums of each entry in
2184 the changelog, manifest, and tracked files, as well as the
2185 the changelog, manifest, and tracked files, as well as the
2185 integrity of their crosslinks and indices.
2186 integrity of their crosslinks and indices.
2186 """
2187 """
2187 return repo.verify()
2188 return repo.verify()
2188
2189
2189 # Command options and aliases are listed here, alphabetically
2190 # Command options and aliases are listed here, alphabetically
2190
2191
2191 table = {
2192 table = {
2192 "^add":
2193 "^add":
2193 (add,
2194 (add,
2194 [('I', 'include', [], _('include names matching the given patterns')),
2195 [('I', 'include', [], _('include names matching the given patterns')),
2195 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2196 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2196 "hg add [OPTION]... [FILE]..."),
2197 "hg add [OPTION]... [FILE]..."),
2197 "addremove":
2198 "addremove":
2198 (addremove,
2199 (addremove,
2199 [('I', 'include', [], _('include names matching the given patterns')),
2200 [('I', 'include', [], _('include names matching the given patterns')),
2200 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2201 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2201 "hg addremove [OPTION]... [FILE]..."),
2202 "hg addremove [OPTION]... [FILE]..."),
2202 "^annotate":
2203 "^annotate":
2203 (annotate,
2204 (annotate,
2204 [('r', 'rev', '', _('annotate the specified revision')),
2205 [('r', 'rev', '', _('annotate the specified revision')),
2205 ('a', 'text', None, _('treat all files as text')),
2206 ('a', 'text', None, _('treat all files as text')),
2206 ('u', 'user', None, _('list the author')),
2207 ('u', 'user', None, _('list the author')),
2207 ('d', 'date', None, _('list the date')),
2208 ('d', 'date', None, _('list the date')),
2208 ('n', 'number', None, _('list the revision number (default)')),
2209 ('n', 'number', None, _('list the revision number (default)')),
2209 ('c', 'changeset', None, _('list the changeset')),
2210 ('c', 'changeset', None, _('list the changeset')),
2210 ('I', 'include', [], _('include names matching the given patterns')),
2211 ('I', 'include', [], _('include names matching the given patterns')),
2211 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2212 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2212 _('hg annotate [OPTION]... FILE...')),
2213 _('hg annotate [OPTION]... FILE...')),
2213 "bundle":
2214 "bundle":
2214 (bundle,
2215 (bundle,
2215 [],
2216 [],
2216 _('hg bundle FILE DEST')),
2217 _('hg bundle FILE DEST')),
2217 "cat":
2218 "cat":
2218 (cat,
2219 (cat,
2219 [('I', 'include', [], _('include names matching the given patterns')),
2220 [('I', 'include', [], _('include names matching the given patterns')),
2220 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2221 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2221 ('o', 'output', "", _('print output to file with formatted name')),
2222 ('o', 'output', "", _('print output to file with formatted name')),
2222 ('r', 'rev', '', _('print the given revision'))],
2223 ('r', 'rev', '', _('print the given revision'))],
2223 _('hg cat [OPTION]... FILE...')),
2224 _('hg cat [OPTION]... FILE...')),
2224 "^clone":
2225 "^clone":
2225 (clone,
2226 (clone,
2226 [('U', 'noupdate', None, _('do not update the new working directory')),
2227 [('U', 'noupdate', None, _('do not update the new working directory')),
2227 ('e', 'ssh', "", _('specify ssh command to use')),
2228 ('e', 'ssh', "", _('specify ssh command to use')),
2228 ('', 'pull', None, _('use pull protocol to copy metadata')),
2229 ('', 'pull', None, _('use pull protocol to copy metadata')),
2229 ('r', 'rev', [], _('a changeset you would like to have after cloning')),
2230 ('r', 'rev', [], _('a changeset you would like to have after cloning')),
2230 ('', 'remotecmd', "", _('specify hg command to run on the remote side'))],
2231 ('', 'remotecmd', "", _('specify hg command to run on the remote side'))],
2231 _('hg clone [OPTION]... SOURCE [DEST]')),
2232 _('hg clone [OPTION]... SOURCE [DEST]')),
2232 "^commit|ci":
2233 "^commit|ci":
2233 (commit,
2234 (commit,
2234 [('A', 'addremove', None, _('run addremove during commit')),
2235 [('A', 'addremove', None, _('run addremove during commit')),
2235 ('I', 'include', [], _('include names matching the given patterns')),
2236 ('I', 'include', [], _('include names matching the given patterns')),
2236 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2237 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2237 ('m', 'message', "", _('use <text> as commit message')),
2238 ('m', 'message', "", _('use <text> as commit message')),
2238 ('l', 'logfile', "", _('read the commit message from <file>')),
2239 ('l', 'logfile', "", _('read the commit message from <file>')),
2239 ('d', 'date', "", _('record datecode as commit date')),
2240 ('d', 'date', "", _('record datecode as commit date')),
2240 ('u', 'user', "", _('record user as commiter'))],
2241 ('u', 'user', "", _('record user as commiter'))],
2241 _('hg commit [OPTION]... [FILE]...')),
2242 _('hg commit [OPTION]... [FILE]...')),
2242 "copy|cp": (copy,
2243 "copy|cp": (copy,
2243 [('I', 'include', [], _('include names matching the given patterns')),
2244 [('I', 'include', [], _('include names matching the given patterns')),
2244 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2245 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2245 ('A', 'after', None, _('record a copy that has already occurred')),
2246 ('A', 'after', None, _('record a copy that has already occurred')),
2246 ('f', 'force', None, _('forcibly copy over an existing managed file'))],
2247 ('f', 'force', None, _('forcibly copy over an existing managed file'))],
2247 _('hg copy [OPTION]... [SOURCE]... DEST')),
2248 _('hg copy [OPTION]... [SOURCE]... DEST')),
2248 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2249 "debugancestor": (debugancestor, [], _('debugancestor INDEX REV1 REV2')),
2249 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2250 "debugcheckstate": (debugcheckstate, [], _('debugcheckstate')),
2250 "debugconfig": (debugconfig, [], _('debugconfig')),
2251 "debugconfig": (debugconfig, [], _('debugconfig')),
2251 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2252 "debugsetparents": (debugsetparents, [], _('debugsetparents REV1 [REV2]')),
2252 "debugstate": (debugstate, [], _('debugstate')),
2253 "debugstate": (debugstate, [], _('debugstate')),
2253 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2254 "debugdata": (debugdata, [], _('debugdata FILE REV')),
2254 "debugindex": (debugindex, [], _('debugindex FILE')),
2255 "debugindex": (debugindex, [], _('debugindex FILE')),
2255 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2256 "debugindexdot": (debugindexdot, [], _('debugindexdot FILE')),
2256 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2257 "debugrename": (debugrename, [], _('debugrename FILE [REV]')),
2257 "debugwalk":
2258 "debugwalk":
2258 (debugwalk,
2259 (debugwalk,
2259 [('I', 'include', [], _('include names matching the given patterns')),
2260 [('I', 'include', [], _('include names matching the given patterns')),
2260 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2261 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2261 _('debugwalk [OPTION]... [FILE]...')),
2262 _('debugwalk [OPTION]... [FILE]...')),
2262 "^diff":
2263 "^diff":
2263 (diff,
2264 (diff,
2264 [('r', 'rev', [], _('revision')),
2265 [('r', 'rev', [], _('revision')),
2265 ('a', 'text', None, _('treat all files as text')),
2266 ('a', 'text', None, _('treat all files as text')),
2266 ('I', 'include', [], _('include names matching the given patterns')),
2267 ('I', 'include', [], _('include names matching the given patterns')),
2267 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2268 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2268 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2269 _('hg diff [-a] [-I] [-X] [-r REV1 [-r REV2]] [FILE]...')),
2269 "^export":
2270 "^export":
2270 (export,
2271 (export,
2271 [('o', 'output', "", _('print output to file with formatted name')),
2272 [('o', 'output', "", _('print output to file with formatted name')),
2272 ('a', 'text', None, _('treat all files as text')),
2273 ('a', 'text', None, _('treat all files as text')),
2273 ('', 'switch-parent', None, _('diff against the second parent'))],
2274 ('', 'switch-parent', None, _('diff against the second parent'))],
2274 "hg export [-a] [-o OUTFILE] REV..."),
2275 "hg export [-a] [-o OUTFILE] REV..."),
2275 "forget":
2276 "forget":
2276 (forget,
2277 (forget,
2277 [('I', 'include', [], _('include names matching the given patterns')),
2278 [('I', 'include', [], _('include names matching the given patterns')),
2278 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2279 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2279 "hg forget [OPTION]... FILE..."),
2280 "hg forget [OPTION]... FILE..."),
2280 "grep":
2281 "grep":
2281 (grep,
2282 (grep,
2282 [('0', 'print0', None, _('end fields with NUL')),
2283 [('0', 'print0', None, _('end fields with NUL')),
2283 ('I', 'include', [], _('include names matching the given patterns')),
2284 ('I', 'include', [], _('include names matching the given patterns')),
2284 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2285 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2285 ('', 'all', None, _('print all revisions that match')),
2286 ('', 'all', None, _('print all revisions that match')),
2286 ('i', 'ignore-case', None, _('ignore case when matching')),
2287 ('i', 'ignore-case', None, _('ignore case when matching')),
2287 ('l', 'files-with-matches', None, _('print only filenames and revs that match')),
2288 ('l', 'files-with-matches', None, _('print only filenames and revs that match')),
2288 ('n', 'line-number', None, _('print matching line numbers')),
2289 ('n', 'line-number', None, _('print matching line numbers')),
2289 ('r', 'rev', [], _('search in given revision range')),
2290 ('r', 'rev', [], _('search in given revision range')),
2290 ('u', 'user', None, _('print user who committed change'))],
2291 ('u', 'user', None, _('print user who committed change'))],
2291 "hg grep [OPTION]... PATTERN [FILE]..."),
2292 "hg grep [OPTION]... PATTERN [FILE]..."),
2292 "heads":
2293 "heads":
2293 (heads,
2294 (heads,
2294 [('b', 'branches', None, _('find branch info')),
2295 [('b', 'branches', None, _('find branch info')),
2295 ('r', 'rev', "", _('show only heads which are descendants of rev'))],
2296 ('r', 'rev', "", _('show only heads which are descendants of rev'))],
2296 _('hg heads [-b] [-r <rev>]')),
2297 _('hg heads [-b] [-r <rev>]')),
2297 "help": (help_, [], _('hg help [COMMAND]')),
2298 "help": (help_, [], _('hg help [COMMAND]')),
2298 "identify|id": (identify, [], _('hg identify')),
2299 "identify|id": (identify, [], _('hg identify')),
2299 "import|patch":
2300 "import|patch":
2300 (import_,
2301 (import_,
2301 [('p', 'strip', 1, _('directory strip option for patch. This has the same\n') +
2302 [('p', 'strip', 1, _('directory strip option for patch. This has the same\n') +
2302 _('meaning as the corresponding patch option')),
2303 _('meaning as the corresponding patch option')),
2303 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
2304 ('f', 'force', None, _('skip check for outstanding uncommitted changes')),
2304 ('b', 'base', "", _('base path'))],
2305 ('b', 'base', "", _('base path'))],
2305 "hg import [-f] [-p NUM] [-b BASE] PATCH..."),
2306 "hg import [-f] [-p NUM] [-b BASE] PATCH..."),
2306 "incoming|in": (incoming,
2307 "incoming|in": (incoming,
2307 [('M', 'no-merges', None, _("do not show merges")),
2308 [('M', 'no-merges', None, _("do not show merges")),
2308 ('p', 'patch', None, _('show patch')),
2309 ('p', 'patch', None, _('show patch')),
2309 ('n', 'newest-first', None, _('show newest record first'))],
2310 ('n', 'newest-first', None, _('show newest record first'))],
2310 _('hg incoming [-p] [-n] [-M] [SOURCE]')),
2311 _('hg incoming [-p] [-n] [-M] [SOURCE]')),
2311 "^init": (init, [], _('hg init [DEST]')),
2312 "^init": (init, [], _('hg init [DEST]')),
2312 "locate":
2313 "locate":
2313 (locate,
2314 (locate,
2314 [('r', 'rev', '', _('search the repository as it stood at rev')),
2315 [('r', 'rev', '', _('search the repository as it stood at rev')),
2315 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2316 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2316 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
2317 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
2317 ('I', 'include', [], _('include names matching the given patterns')),
2318 ('I', 'include', [], _('include names matching the given patterns')),
2318 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2319 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2319 _('hg locate [OPTION]... [PATTERN]...')),
2320 _('hg locate [OPTION]... [PATTERN]...')),
2320 "^log|history":
2321 "^log|history":
2321 (log,
2322 (log,
2322 [('I', 'include', [], _('include names matching the given patterns')),
2323 [('I', 'include', [], _('include names matching the given patterns')),
2323 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2324 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2324 ('b', 'branch', None, _('show branches')),
2325 ('b', 'branch', None, _('show branches')),
2325 ('k', 'keyword', [], _('search for a keyword')),
2326 ('k', 'keyword', [], _('search for a keyword')),
2326 ('r', 'rev', [], _('show the specified revision or range')),
2327 ('r', 'rev', [], _('show the specified revision or range')),
2327 ('M', 'no-merges', None, _("do not show merges")),
2328 ('M', 'no-merges', None, _("do not show merges")),
2328 ('m', 'only-merges', None, _("show only merges")),
2329 ('m', 'only-merges', None, _("show only merges")),
2329 ('p', 'patch', None, _('show patch'))],
2330 ('p', 'patch', None, _('show patch'))],
2330 _('hg log [-I] [-X] [-r REV]... [-p] [FILE]')),
2331 _('hg log [-I] [-X] [-r REV]... [-p] [FILE]')),
2331 "manifest": (manifest, [], _('hg manifest [REV]')),
2332 "manifest": (manifest, [], _('hg manifest [REV]')),
2332 "outgoing|out": (outgoing,
2333 "outgoing|out": (outgoing,
2333 [('M', 'no-merges', None, _("do not show merges")),
2334 [('M', 'no-merges', None, _("do not show merges")),
2334 ('p', 'patch', None, _('show patch')),
2335 ('p', 'patch', None, _('show patch')),
2335 ('n', 'newest-first', None, _('show newest record first'))],
2336 ('n', 'newest-first', None, _('show newest record first'))],
2336 _('hg outgoing [-p] [-n] [-M] [DEST]')),
2337 _('hg outgoing [-p] [-n] [-M] [DEST]')),
2337 "^parents": (parents, [], _('hg parents [REV]')),
2338 "^parents": (parents, [], _('hg parents [REV]')),
2338 "paths": (paths, [], _('hg paths [NAME]')),
2339 "paths": (paths, [], _('hg paths [NAME]')),
2339 "^pull":
2340 "^pull":
2340 (pull,
2341 (pull,
2341 [('u', 'update', None, _('update the working directory to tip after pull')),
2342 [('u', 'update', None, _('update the working directory to tip after pull')),
2342 ('e', 'ssh', "", _('specify ssh command to use')),
2343 ('e', 'ssh', "", _('specify ssh command to use')),
2343 ('r', 'rev', [], _('a specific revision you would like to pull')),
2344 ('r', 'rev', [], _('a specific revision you would like to pull')),
2344 ('', 'remotecmd', "", _('specify hg command to run on the remote side'))],
2345 ('', 'remotecmd', "", _('specify hg command to run on the remote side'))],
2345 _('hg pull [-u] [-e FILE] [-r rev] [--remotecmd FILE] [SOURCE]')),
2346 _('hg pull [-u] [-e FILE] [-r rev] [--remotecmd FILE] [SOURCE]')),
2346 "^push":
2347 "^push":
2347 (push,
2348 (push,
2348 [('f', 'force', None, _('force push')),
2349 [('f', 'force', None, _('force push')),
2349 ('e', 'ssh', "", _('specify ssh command to use')),
2350 ('e', 'ssh', "", _('specify ssh command to use')),
2350 ('', 'remotecmd', "", _('specify hg command to run on the remote side'))],
2351 ('', 'remotecmd', "", _('specify hg command to run on the remote side'))],
2351 _('hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]')),
2352 _('hg push [-f] [-e FILE] [--remotecmd FILE] [DEST]')),
2352 "rawcommit":
2353 "rawcommit":
2353 (rawcommit,
2354 (rawcommit,
2354 [('p', 'parent', [], _('parent')),
2355 [('p', 'parent', [], _('parent')),
2355 ('d', 'date', "", _('date code')),
2356 ('d', 'date', "", _('date code')),
2356 ('u', 'user', "", _('user')),
2357 ('u', 'user', "", _('user')),
2357 ('F', 'files', "", _('file list')),
2358 ('F', 'files', "", _('file list')),
2358 ('m', 'message', "", _('commit message')),
2359 ('m', 'message', "", _('commit message')),
2359 ('l', 'logfile', "", _('commit message file'))],
2360 ('l', 'logfile', "", _('commit message file'))],
2360 _('hg rawcommit [OPTION]... [FILE]...')),
2361 _('hg rawcommit [OPTION]... [FILE]...')),
2361 "recover": (recover, [], _("hg recover")),
2362 "recover": (recover, [], _("hg recover")),
2362 "^remove|rm": (remove,
2363 "^remove|rm": (remove,
2363 [('I', 'include', [], _('include names matching the given patterns')),
2364 [('I', 'include', [], _('include names matching the given patterns')),
2364 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2365 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2365 _("hg remove [OPTION]... FILE...")),
2366 _("hg remove [OPTION]... FILE...")),
2366 "rename|mv": (rename,
2367 "rename|mv": (rename,
2367 [('I', 'include', [], _('include names matching the given patterns')),
2368 [('I', 'include', [], _('include names matching the given patterns')),
2368 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2369 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2369 ('A', 'after', None, _('record a rename that has already occurred')),
2370 ('A', 'after', None, _('record a rename that has already occurred')),
2370 ('f', 'force', None, _('forcibly copy over an existing managed file'))],
2371 ('f', 'force', None, _('forcibly copy over an existing managed file'))],
2371 _('hg rename [OPTION]... [SOURCE]... DEST')),
2372 _('hg rename [OPTION]... [SOURCE]... DEST')),
2372 "^revert":
2373 "^revert":
2373 (revert,
2374 (revert,
2374 [('I', 'include', [], _('include names matching the given patterns')),
2375 [('I', 'include', [], _('include names matching the given patterns')),
2375 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2376 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2376 ("r", "rev", "", _("revision to revert to"))],
2377 ("r", "rev", "", _("revision to revert to"))],
2377 _("hg revert [-n] [-r REV] [NAME]...")),
2378 _("hg revert [-n] [-r REV] [NAME]...")),
2378 "root": (root, [], _("hg root")),
2379 "root": (root, [], _("hg root")),
2379 "^serve":
2380 "^serve":
2380 (serve,
2381 (serve,
2381 [('A', 'accesslog', '', _('name of access log file to write to')),
2382 [('A', 'accesslog', '', _('name of access log file to write to')),
2382 ('E', 'errorlog', '', _('name of error log file to write to')),
2383 ('E', 'errorlog', '', _('name of error log file to write to')),
2383 ('p', 'port', 0, _('port to use (default: 8000)')),
2384 ('p', 'port', 0, _('port to use (default: 8000)')),
2384 ('a', 'address', '', _('address to use')),
2385 ('a', 'address', '', _('address to use')),
2385 ('n', 'name', "", _('name to show in web pages (default: working dir)')),
2386 ('n', 'name', "", _('name to show in web pages (default: working dir)')),
2386 ('', 'stdio', None, _('for remote clients')),
2387 ('', 'stdio', None, _('for remote clients')),
2387 ('t', 'templates', "", _('web templates to use')),
2388 ('t', 'templates', "", _('web templates to use')),
2388 ('', 'style', "", _('template style to use')),
2389 ('', 'style', "", _('template style to use')),
2389 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2390 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4'))],
2390 _("hg serve [OPTION]...")),
2391 _("hg serve [OPTION]...")),
2391 "^status|st":
2392 "^status|st":
2392 (status,
2393 (status,
2393 [('m', 'modified', None, _('show only modified files')),
2394 [('m', 'modified', None, _('show only modified files')),
2394 ('a', 'added', None, _('show only added files')),
2395 ('a', 'added', None, _('show only added files')),
2395 ('r', 'removed', None, _('show only removed files')),
2396 ('r', 'removed', None, _('show only removed files')),
2396 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2397 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
2397 ('n', 'no-status', None, _('hide status prefix')),
2398 ('n', 'no-status', None, _('hide status prefix')),
2398 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2399 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
2399 ('I', 'include', [], _('include names matching the given patterns')),
2400 ('I', 'include', [], _('include names matching the given patterns')),
2400 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2401 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2401 _("hg status [OPTION]... [FILE]...")),
2402 _("hg status [OPTION]... [FILE]...")),
2402 "tag":
2403 "tag":
2403 (tag,
2404 (tag,
2404 [('l', 'local', None, _('make the tag local')),
2405 [('l', 'local', None, _('make the tag local')),
2405 ('m', 'message', "", _('message for tag commit log entry')),
2406 ('m', 'message', "", _('message for tag commit log entry')),
2406 ('d', 'date', "", _('record datecode as commit date')),
2407 ('d', 'date', "", _('record datecode as commit date')),
2407 ('u', 'user', "", _('record user as commiter')),
2408 ('u', 'user', "", _('record user as commiter')),
2408 ('r', 'rev', "", _('revision to tag'))],
2409 ('r', 'rev', "", _('revision to tag'))],
2409 _('hg tag [OPTION]... NAME [REV]')),
2410 _('hg tag [OPTION]... NAME [REV]')),
2410 "tags": (tags, [], _('hg tags')),
2411 "tags": (tags, [], _('hg tags')),
2411 "tip": (tip, [], _('hg tip')),
2412 "tip": (tip, [], _('hg tip')),
2412 "unbundle":
2413 "unbundle":
2413 (unbundle,
2414 (unbundle,
2414 [],
2415 [],
2415 _('hg unbundle FILE')),
2416 _('hg unbundle FILE')),
2416 "undo": (undo, [], _('hg undo')),
2417 "undo": (undo, [], _('hg undo')),
2417 "^update|up|checkout|co":
2418 "^update|up|checkout|co":
2418 (update,
2419 (update,
2419 [('b', 'branch', "", _('checkout the head of a specific branch')),
2420 [('b', 'branch', "", _('checkout the head of a specific branch')),
2420 ('m', 'merge', None, _('allow merging of branches')),
2421 ('m', 'merge', None, _('allow merging of branches')),
2421 ('C', 'clean', None, _('overwrite locally modified files'))],
2422 ('C', 'clean', None, _('overwrite locally modified files')),
2422 _('hg update [-b TAG] [-m] [-C] [REV]')),
2423 ('f', 'force', None, _('force a merge with outstanding changes'))],
2424 _('hg update [-b TAG] [-m] [-C] [-f] [REV]')),
2423 "verify": (verify, [], _('hg verify')),
2425 "verify": (verify, [], _('hg verify')),
2424 "version": (show_version, [], _('hg version')),
2426 "version": (show_version, [], _('hg version')),
2425 }
2427 }
2426
2428
2427 globalopts = [
2429 globalopts = [
2428 ('R', 'repository', "", _("repository root directory")),
2430 ('R', 'repository', "", _("repository root directory")),
2429 ('', 'cwd', '', _("change working directory")),
2431 ('', 'cwd', '', _("change working directory")),
2430 ('y', 'noninteractive', None, _("do not prompt, assume 'yes' for any required answers")),
2432 ('y', 'noninteractive', None, _("do not prompt, assume 'yes' for any required answers")),
2431 ('q', 'quiet', None, _("suppress output")),
2433 ('q', 'quiet', None, _("suppress output")),
2432 ('v', 'verbose', None, _("enable additional output")),
2434 ('v', 'verbose', None, _("enable additional output")),
2433 ('', 'debug', None, _("enable debugging output")),
2435 ('', 'debug', None, _("enable debugging output")),
2434 ('', 'debugger', None, _("start debugger")),
2436 ('', 'debugger', None, _("start debugger")),
2435 ('', 'traceback', None, _("print traceback on exception")),
2437 ('', 'traceback', None, _("print traceback on exception")),
2436 ('', 'time', None, _("time how long the command takes")),
2438 ('', 'time', None, _("time how long the command takes")),
2437 ('', 'profile', None, _("print command execution profile")),
2439 ('', 'profile', None, _("print command execution profile")),
2438 ('', 'version', None, _("output version information and exit")),
2440 ('', 'version', None, _("output version information and exit")),
2439 ('h', 'help', None, _("display help and exit")),
2441 ('h', 'help', None, _("display help and exit")),
2440 ]
2442 ]
2441
2443
2442 norepo = ("clone init version help debugancestor debugconfig debugdata"
2444 norepo = ("clone init version help debugancestor debugconfig debugdata"
2443 " debugindex debugindexdot paths")
2445 " debugindex debugindexdot paths")
2444
2446
2445 def find(cmd):
2447 def find(cmd):
2446 """Return (aliases, command table entry) for command string."""
2448 """Return (aliases, command table entry) for command string."""
2447 choice = None
2449 choice = None
2448 for e in table.keys():
2450 for e in table.keys():
2449 aliases = e.lstrip("^").split("|")
2451 aliases = e.lstrip("^").split("|")
2450 if cmd in aliases:
2452 if cmd in aliases:
2451 return aliases, table[e]
2453 return aliases, table[e]
2452 for a in aliases:
2454 for a in aliases:
2453 if a.startswith(cmd):
2455 if a.startswith(cmd):
2454 if choice:
2456 if choice:
2455 raise AmbiguousCommand(cmd)
2457 raise AmbiguousCommand(cmd)
2456 else:
2458 else:
2457 choice = aliases, table[e]
2459 choice = aliases, table[e]
2458 break
2460 break
2459 if choice:
2461 if choice:
2460 return choice
2462 return choice
2461
2463
2462 raise UnknownCommand(cmd)
2464 raise UnknownCommand(cmd)
2463
2465
2464 class SignalInterrupt(Exception):
2466 class SignalInterrupt(Exception):
2465 """Exception raised on SIGTERM and SIGHUP."""
2467 """Exception raised on SIGTERM and SIGHUP."""
2466
2468
2467 def catchterm(*args):
2469 def catchterm(*args):
2468 raise SignalInterrupt
2470 raise SignalInterrupt
2469
2471
2470 def run():
2472 def run():
2471 sys.exit(dispatch(sys.argv[1:]))
2473 sys.exit(dispatch(sys.argv[1:]))
2472
2474
2473 class ParseError(Exception):
2475 class ParseError(Exception):
2474 """Exception raised on errors in parsing the command line."""
2476 """Exception raised on errors in parsing the command line."""
2475
2477
2476 def parse(ui, args):
2478 def parse(ui, args):
2477 options = {}
2479 options = {}
2478 cmdoptions = {}
2480 cmdoptions = {}
2479
2481
2480 try:
2482 try:
2481 args = fancyopts.fancyopts(args, globalopts, options)
2483 args = fancyopts.fancyopts(args, globalopts, options)
2482 except fancyopts.getopt.GetoptError, inst:
2484 except fancyopts.getopt.GetoptError, inst:
2483 raise ParseError(None, inst)
2485 raise ParseError(None, inst)
2484
2486
2485 if args:
2487 if args:
2486 cmd, args = args[0], args[1:]
2488 cmd, args = args[0], args[1:]
2487 aliases, i = find(cmd)
2489 aliases, i = find(cmd)
2488 cmd = aliases[0]
2490 cmd = aliases[0]
2489 defaults = ui.config("defaults", cmd)
2491 defaults = ui.config("defaults", cmd)
2490 if defaults:
2492 if defaults:
2491 args = defaults.split() + args
2493 args = defaults.split() + args
2492 c = list(i[1])
2494 c = list(i[1])
2493 else:
2495 else:
2494 cmd = None
2496 cmd = None
2495 c = []
2497 c = []
2496
2498
2497 # combine global options into local
2499 # combine global options into local
2498 for o in globalopts:
2500 for o in globalopts:
2499 c.append((o[0], o[1], options[o[1]], o[3]))
2501 c.append((o[0], o[1], options[o[1]], o[3]))
2500
2502
2501 try:
2503 try:
2502 args = fancyopts.fancyopts(args, c, cmdoptions)
2504 args = fancyopts.fancyopts(args, c, cmdoptions)
2503 except fancyopts.getopt.GetoptError, inst:
2505 except fancyopts.getopt.GetoptError, inst:
2504 raise ParseError(cmd, inst)
2506 raise ParseError(cmd, inst)
2505
2507
2506 # separate global options back out
2508 # separate global options back out
2507 for o in globalopts:
2509 for o in globalopts:
2508 n = o[1]
2510 n = o[1]
2509 options[n] = cmdoptions[n]
2511 options[n] = cmdoptions[n]
2510 del cmdoptions[n]
2512 del cmdoptions[n]
2511
2513
2512 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
2514 return (cmd, cmd and i[0] or None, args, options, cmdoptions)
2513
2515
2514 def dispatch(args):
2516 def dispatch(args):
2515 signal.signal(signal.SIGTERM, catchterm)
2517 signal.signal(signal.SIGTERM, catchterm)
2516 try:
2518 try:
2517 signal.signal(signal.SIGHUP, catchterm)
2519 signal.signal(signal.SIGHUP, catchterm)
2518 except AttributeError:
2520 except AttributeError:
2519 pass
2521 pass
2520
2522
2521 try:
2523 try:
2522 u = ui.ui()
2524 u = ui.ui()
2523 except util.Abort, inst:
2525 except util.Abort, inst:
2524 sys.stderr.write(_("abort: %s\n") % inst)
2526 sys.stderr.write(_("abort: %s\n") % inst)
2525 sys.exit(1)
2527 sys.exit(1)
2526
2528
2527 external = []
2529 external = []
2528 for x in u.extensions():
2530 for x in u.extensions():
2529 def on_exception(exc, inst):
2531 def on_exception(exc, inst):
2530 u.warn(_("*** failed to import extension %s\n") % x[1])
2532 u.warn(_("*** failed to import extension %s\n") % x[1])
2531 u.warn("%s\n" % inst)
2533 u.warn("%s\n" % inst)
2532 if "--traceback" in sys.argv[1:]:
2534 if "--traceback" in sys.argv[1:]:
2533 traceback.print_exc()
2535 traceback.print_exc()
2534 if x[1]:
2536 if x[1]:
2535 try:
2537 try:
2536 mod = imp.load_source(x[0], x[1])
2538 mod = imp.load_source(x[0], x[1])
2537 except Exception, inst:
2539 except Exception, inst:
2538 on_exception(Exception, inst)
2540 on_exception(Exception, inst)
2539 continue
2541 continue
2540 else:
2542 else:
2541 def importh(name):
2543 def importh(name):
2542 mod = __import__(name)
2544 mod = __import__(name)
2543 components = name.split('.')
2545 components = name.split('.')
2544 for comp in components[1:]:
2546 for comp in components[1:]:
2545 mod = getattr(mod, comp)
2547 mod = getattr(mod, comp)
2546 return mod
2548 return mod
2547 try:
2549 try:
2548 mod = importh(x[0])
2550 mod = importh(x[0])
2549 except Exception, inst:
2551 except Exception, inst:
2550 on_exception(Exception, inst)
2552 on_exception(Exception, inst)
2551 continue
2553 continue
2552
2554
2553 external.append(mod)
2555 external.append(mod)
2554 for x in external:
2556 for x in external:
2555 cmdtable = getattr(x, 'cmdtable', {})
2557 cmdtable = getattr(x, 'cmdtable', {})
2556 for t in cmdtable:
2558 for t in cmdtable:
2557 if t in table:
2559 if t in table:
2558 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
2560 u.warn(_("module %s overrides %s\n") % (x.__name__, t))
2559 table.update(cmdtable)
2561 table.update(cmdtable)
2560
2562
2561 try:
2563 try:
2562 cmd, func, args, options, cmdoptions = parse(u, args)
2564 cmd, func, args, options, cmdoptions = parse(u, args)
2563 except ParseError, inst:
2565 except ParseError, inst:
2564 if inst.args[0]:
2566 if inst.args[0]:
2565 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
2567 u.warn(_("hg %s: %s\n") % (inst.args[0], inst.args[1]))
2566 help_(u, inst.args[0])
2568 help_(u, inst.args[0])
2567 else:
2569 else:
2568 u.warn(_("hg: %s\n") % inst.args[1])
2570 u.warn(_("hg: %s\n") % inst.args[1])
2569 help_(u, 'shortlist')
2571 help_(u, 'shortlist')
2570 sys.exit(-1)
2572 sys.exit(-1)
2571 except AmbiguousCommand, inst:
2573 except AmbiguousCommand, inst:
2572 u.warn(_("hg: command '%s' is ambiguous.\n") % inst.args[0])
2574 u.warn(_("hg: command '%s' is ambiguous.\n") % inst.args[0])
2573 sys.exit(1)
2575 sys.exit(1)
2574 except UnknownCommand, inst:
2576 except UnknownCommand, inst:
2575 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2577 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2576 help_(u, 'shortlist')
2578 help_(u, 'shortlist')
2577 sys.exit(1)
2579 sys.exit(1)
2578
2580
2579 if options["time"]:
2581 if options["time"]:
2580 def get_times():
2582 def get_times():
2581 t = os.times()
2583 t = os.times()
2582 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
2584 if t[4] == 0.0: # Windows leaves this as zero, so use time.clock()
2583 t = (t[0], t[1], t[2], t[3], time.clock())
2585 t = (t[0], t[1], t[2], t[3], time.clock())
2584 return t
2586 return t
2585 s = get_times()
2587 s = get_times()
2586 def print_time():
2588 def print_time():
2587 t = get_times()
2589 t = get_times()
2588 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
2590 u.warn(_("Time: real %.3f secs (user %.3f+%.3f sys %.3f+%.3f)\n") %
2589 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
2591 (t[4]-s[4], t[0]-s[0], t[2]-s[2], t[1]-s[1], t[3]-s[3]))
2590 atexit.register(print_time)
2592 atexit.register(print_time)
2591
2593
2592 u.updateopts(options["verbose"], options["debug"], options["quiet"],
2594 u.updateopts(options["verbose"], options["debug"], options["quiet"],
2593 not options["noninteractive"])
2595 not options["noninteractive"])
2594
2596
2595 # enter the debugger before command execution
2597 # enter the debugger before command execution
2596 if options['debugger']:
2598 if options['debugger']:
2597 pdb.set_trace()
2599 pdb.set_trace()
2598
2600
2599 try:
2601 try:
2600 try:
2602 try:
2601 if options['help']:
2603 if options['help']:
2602 help_(u, cmd, options['version'])
2604 help_(u, cmd, options['version'])
2603 sys.exit(0)
2605 sys.exit(0)
2604 elif options['version']:
2606 elif options['version']:
2605 show_version(u)
2607 show_version(u)
2606 sys.exit(0)
2608 sys.exit(0)
2607 elif not cmd:
2609 elif not cmd:
2608 help_(u, 'shortlist')
2610 help_(u, 'shortlist')
2609 sys.exit(0)
2611 sys.exit(0)
2610
2612
2611 if options['cwd']:
2613 if options['cwd']:
2612 try:
2614 try:
2613 os.chdir(options['cwd'])
2615 os.chdir(options['cwd'])
2614 except OSError, inst:
2616 except OSError, inst:
2615 raise util.Abort('%s: %s' %
2617 raise util.Abort('%s: %s' %
2616 (options['cwd'], inst.strerror))
2618 (options['cwd'], inst.strerror))
2617
2619
2618 if cmd not in norepo.split():
2620 if cmd not in norepo.split():
2619 path = options["repository"] or ""
2621 path = options["repository"] or ""
2620 repo = hg.repository(ui=u, path=path)
2622 repo = hg.repository(ui=u, path=path)
2621 for x in external:
2623 for x in external:
2622 if hasattr(x, 'reposetup'): x.reposetup(u, repo)
2624 if hasattr(x, 'reposetup'): x.reposetup(u, repo)
2623 d = lambda: func(u, repo, *args, **cmdoptions)
2625 d = lambda: func(u, repo, *args, **cmdoptions)
2624 else:
2626 else:
2625 d = lambda: func(u, *args, **cmdoptions)
2627 d = lambda: func(u, *args, **cmdoptions)
2626
2628
2627 if options['profile']:
2629 if options['profile']:
2628 import hotshot, hotshot.stats
2630 import hotshot, hotshot.stats
2629 prof = hotshot.Profile("hg.prof")
2631 prof = hotshot.Profile("hg.prof")
2630 r = prof.runcall(d)
2632 r = prof.runcall(d)
2631 prof.close()
2633 prof.close()
2632 stats = hotshot.stats.load("hg.prof")
2634 stats = hotshot.stats.load("hg.prof")
2633 stats.strip_dirs()
2635 stats.strip_dirs()
2634 stats.sort_stats('time', 'calls')
2636 stats.sort_stats('time', 'calls')
2635 stats.print_stats(40)
2637 stats.print_stats(40)
2636 return r
2638 return r
2637 else:
2639 else:
2638 return d()
2640 return d()
2639 except:
2641 except:
2640 # enter the debugger when we hit an exception
2642 # enter the debugger when we hit an exception
2641 if options['debugger']:
2643 if options['debugger']:
2642 pdb.post_mortem(sys.exc_info()[2])
2644 pdb.post_mortem(sys.exc_info()[2])
2643 if options['traceback']:
2645 if options['traceback']:
2644 traceback.print_exc()
2646 traceback.print_exc()
2645 raise
2647 raise
2646 except hg.RepoError, inst:
2648 except hg.RepoError, inst:
2647 u.warn(_("abort: "), inst, "!\n")
2649 u.warn(_("abort: "), inst, "!\n")
2648 except revlog.RevlogError, inst:
2650 except revlog.RevlogError, inst:
2649 u.warn(_("abort: "), inst, "!\n")
2651 u.warn(_("abort: "), inst, "!\n")
2650 except SignalInterrupt:
2652 except SignalInterrupt:
2651 u.warn(_("killed!\n"))
2653 u.warn(_("killed!\n"))
2652 except KeyboardInterrupt:
2654 except KeyboardInterrupt:
2653 try:
2655 try:
2654 u.warn(_("interrupted!\n"))
2656 u.warn(_("interrupted!\n"))
2655 except IOError, inst:
2657 except IOError, inst:
2656 if inst.errno == errno.EPIPE:
2658 if inst.errno == errno.EPIPE:
2657 if u.debugflag:
2659 if u.debugflag:
2658 u.warn(_("\nbroken pipe\n"))
2660 u.warn(_("\nbroken pipe\n"))
2659 else:
2661 else:
2660 raise
2662 raise
2661 except IOError, inst:
2663 except IOError, inst:
2662 if hasattr(inst, "code"):
2664 if hasattr(inst, "code"):
2663 u.warn(_("abort: %s\n") % inst)
2665 u.warn(_("abort: %s\n") % inst)
2664 elif hasattr(inst, "reason"):
2666 elif hasattr(inst, "reason"):
2665 u.warn(_("abort: error: %s\n") % inst.reason[1])
2667 u.warn(_("abort: error: %s\n") % inst.reason[1])
2666 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
2668 elif hasattr(inst, "args") and inst[0] == errno.EPIPE:
2667 if u.debugflag:
2669 if u.debugflag:
2668 u.warn(_("broken pipe\n"))
2670 u.warn(_("broken pipe\n"))
2669 elif getattr(inst, "strerror", None):
2671 elif getattr(inst, "strerror", None):
2670 if getattr(inst, "filename", None):
2672 if getattr(inst, "filename", None):
2671 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
2673 u.warn(_("abort: %s - %s\n") % (inst.strerror, inst.filename))
2672 else:
2674 else:
2673 u.warn(_("abort: %s\n") % inst.strerror)
2675 u.warn(_("abort: %s\n") % inst.strerror)
2674 else:
2676 else:
2675 raise
2677 raise
2676 except OSError, inst:
2678 except OSError, inst:
2677 if hasattr(inst, "filename"):
2679 if hasattr(inst, "filename"):
2678 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
2680 u.warn(_("abort: %s: %s\n") % (inst.strerror, inst.filename))
2679 else:
2681 else:
2680 u.warn(_("abort: %s\n") % inst.strerror)
2682 u.warn(_("abort: %s\n") % inst.strerror)
2681 except util.Abort, inst:
2683 except util.Abort, inst:
2682 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
2684 u.warn(_('abort: '), inst.args[0] % inst.args[1:], '\n')
2683 sys.exit(1)
2685 sys.exit(1)
2684 except TypeError, inst:
2686 except TypeError, inst:
2685 # was this an argument error?
2687 # was this an argument error?
2686 tb = traceback.extract_tb(sys.exc_info()[2])
2688 tb = traceback.extract_tb(sys.exc_info()[2])
2687 if len(tb) > 2: # no
2689 if len(tb) > 2: # no
2688 raise
2690 raise
2689 u.debug(inst, "\n")
2691 u.debug(inst, "\n")
2690 u.warn(_("%s: invalid arguments\n") % cmd)
2692 u.warn(_("%s: invalid arguments\n") % cmd)
2691 help_(u, cmd)
2693 help_(u, cmd)
2692 except AmbiguousCommand, inst:
2694 except AmbiguousCommand, inst:
2693 u.warn(_("hg: command '%s' is ambiguous.\n") % inst.args[0])
2695 u.warn(_("hg: command '%s' is ambiguous.\n") % inst.args[0])
2694 help_(u, 'shortlist')
2696 help_(u, 'shortlist')
2695 except UnknownCommand, inst:
2697 except UnknownCommand, inst:
2696 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2698 u.warn(_("hg: unknown command '%s'\n") % inst.args[0])
2697 help_(u, 'shortlist')
2699 help_(u, 'shortlist')
2698 except SystemExit:
2700 except SystemExit:
2699 # don't catch this in the catch-all below
2701 # don't catch this in the catch-all below
2700 raise
2702 raise
2701 except:
2703 except:
2702 u.warn(_("** unknown exception encountered, details follow\n"))
2704 u.warn(_("** unknown exception encountered, details follow\n"))
2703 u.warn(_("** report bug details to mercurial@selenic.com\n"))
2705 u.warn(_("** report bug details to mercurial@selenic.com\n"))
2704 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
2706 u.warn(_("** Mercurial Distributed SCM (version %s)\n")
2705 % version.get_version())
2707 % version.get_version())
2706 raise
2708 raise
2707
2709
2708 sys.exit(-1)
2710 sys.exit(-1)
@@ -1,1780 +1,1792 b''
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class for mercurial
2 #
2 #
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 import struct, os, util
8 import struct, os, util
9 import filelog, manifest, changelog, dirstate, repo
9 import filelog, manifest, changelog, dirstate, repo
10 from node import *
10 from node import *
11 from i18n import gettext as _
11 from i18n import gettext as _
12 from demandload import *
12 from demandload import *
13 demandload(globals(), "re lock transaction tempfile stat mdiff errno")
13 demandload(globals(), "re lock transaction tempfile stat mdiff errno")
14
14
15 class localrepository(object):
15 class localrepository(object):
16 def __init__(self, ui, path=None, create=0):
16 def __init__(self, ui, path=None, create=0):
17 if not path:
17 if not path:
18 p = os.getcwd()
18 p = os.getcwd()
19 while not os.path.isdir(os.path.join(p, ".hg")):
19 while not os.path.isdir(os.path.join(p, ".hg")):
20 oldp = p
20 oldp = p
21 p = os.path.dirname(p)
21 p = os.path.dirname(p)
22 if p == oldp: raise repo.RepoError(_("no repo found"))
22 if p == oldp: raise repo.RepoError(_("no repo found"))
23 path = p
23 path = p
24 self.path = os.path.join(path, ".hg")
24 self.path = os.path.join(path, ".hg")
25
25
26 if not create and not os.path.isdir(self.path):
26 if not create and not os.path.isdir(self.path):
27 raise repo.RepoError(_("repository %s not found") % self.path)
27 raise repo.RepoError(_("repository %s not found") % self.path)
28
28
29 self.root = os.path.abspath(path)
29 self.root = os.path.abspath(path)
30 self.ui = ui
30 self.ui = ui
31 self.opener = util.opener(self.path)
31 self.opener = util.opener(self.path)
32 self.wopener = util.opener(self.root)
32 self.wopener = util.opener(self.root)
33 self.manifest = manifest.manifest(self.opener)
33 self.manifest = manifest.manifest(self.opener)
34 self.changelog = changelog.changelog(self.opener)
34 self.changelog = changelog.changelog(self.opener)
35 self.tagscache = None
35 self.tagscache = None
36 self.nodetagscache = None
36 self.nodetagscache = None
37 self.encodepats = None
37 self.encodepats = None
38 self.decodepats = None
38 self.decodepats = None
39
39
40 if create:
40 if create:
41 os.mkdir(self.path)
41 os.mkdir(self.path)
42 os.mkdir(self.join("data"))
42 os.mkdir(self.join("data"))
43
43
44 self.dirstate = dirstate.dirstate(self.opener, ui, self.root)
44 self.dirstate = dirstate.dirstate(self.opener, ui, self.root)
45 try:
45 try:
46 self.ui.readconfig(self.join("hgrc"))
46 self.ui.readconfig(self.join("hgrc"))
47 except IOError: pass
47 except IOError: pass
48
48
49 def hook(self, name, **args):
49 def hook(self, name, **args):
50 def runhook(name, cmd):
50 def runhook(name, cmd):
51 self.ui.note(_("running hook %s: %s\n") % (name, cmd))
51 self.ui.note(_("running hook %s: %s\n") % (name, cmd))
52 old = {}
52 old = {}
53 for k, v in args.items():
53 for k, v in args.items():
54 k = k.upper()
54 k = k.upper()
55 old[k] = os.environ.get(k, None)
55 old[k] = os.environ.get(k, None)
56 os.environ[k] = v
56 os.environ[k] = v
57
57
58 # Hooks run in the repository root
58 # Hooks run in the repository root
59 olddir = os.getcwd()
59 olddir = os.getcwd()
60 os.chdir(self.root)
60 os.chdir(self.root)
61 r = os.system(cmd)
61 r = os.system(cmd)
62 os.chdir(olddir)
62 os.chdir(olddir)
63
63
64 for k, v in old.items():
64 for k, v in old.items():
65 if v != None:
65 if v != None:
66 os.environ[k] = v
66 os.environ[k] = v
67 else:
67 else:
68 del os.environ[k]
68 del os.environ[k]
69
69
70 if r:
70 if r:
71 self.ui.warn(_("abort: %s hook failed with status %d!\n") %
71 self.ui.warn(_("abort: %s hook failed with status %d!\n") %
72 (name, r))
72 (name, r))
73 return False
73 return False
74 return True
74 return True
75
75
76 r = True
76 r = True
77 for hname, cmd in self.ui.configitems("hooks"):
77 for hname, cmd in self.ui.configitems("hooks"):
78 s = hname.split(".")
78 s = hname.split(".")
79 if s[0] == name and cmd:
79 if s[0] == name and cmd:
80 r = runhook(hname, cmd) and r
80 r = runhook(hname, cmd) and r
81 return r
81 return r
82
82
83 def tags(self):
83 def tags(self):
84 '''return a mapping of tag to node'''
84 '''return a mapping of tag to node'''
85 if not self.tagscache:
85 if not self.tagscache:
86 self.tagscache = {}
86 self.tagscache = {}
87 def addtag(self, k, n):
87 def addtag(self, k, n):
88 try:
88 try:
89 bin_n = bin(n)
89 bin_n = bin(n)
90 except TypeError:
90 except TypeError:
91 bin_n = ''
91 bin_n = ''
92 self.tagscache[k.strip()] = bin_n
92 self.tagscache[k.strip()] = bin_n
93
93
94 try:
94 try:
95 # read each head of the tags file, ending with the tip
95 # read each head of the tags file, ending with the tip
96 # and add each tag found to the map, with "newer" ones
96 # and add each tag found to the map, with "newer" ones
97 # taking precedence
97 # taking precedence
98 fl = self.file(".hgtags")
98 fl = self.file(".hgtags")
99 h = fl.heads()
99 h = fl.heads()
100 h.reverse()
100 h.reverse()
101 for r in h:
101 for r in h:
102 for l in fl.read(r).splitlines():
102 for l in fl.read(r).splitlines():
103 if l:
103 if l:
104 n, k = l.split(" ", 1)
104 n, k = l.split(" ", 1)
105 addtag(self, k, n)
105 addtag(self, k, n)
106 except KeyError:
106 except KeyError:
107 pass
107 pass
108
108
109 try:
109 try:
110 f = self.opener("localtags")
110 f = self.opener("localtags")
111 for l in f:
111 for l in f:
112 n, k = l.split(" ", 1)
112 n, k = l.split(" ", 1)
113 addtag(self, k, n)
113 addtag(self, k, n)
114 except IOError:
114 except IOError:
115 pass
115 pass
116
116
117 self.tagscache['tip'] = self.changelog.tip()
117 self.tagscache['tip'] = self.changelog.tip()
118
118
119 return self.tagscache
119 return self.tagscache
120
120
121 def tagslist(self):
121 def tagslist(self):
122 '''return a list of tags ordered by revision'''
122 '''return a list of tags ordered by revision'''
123 l = []
123 l = []
124 for t, n in self.tags().items():
124 for t, n in self.tags().items():
125 try:
125 try:
126 r = self.changelog.rev(n)
126 r = self.changelog.rev(n)
127 except:
127 except:
128 r = -2 # sort to the beginning of the list if unknown
128 r = -2 # sort to the beginning of the list if unknown
129 l.append((r,t,n))
129 l.append((r,t,n))
130 l.sort()
130 l.sort()
131 return [(t,n) for r,t,n in l]
131 return [(t,n) for r,t,n in l]
132
132
133 def nodetags(self, node):
133 def nodetags(self, node):
134 '''return the tags associated with a node'''
134 '''return the tags associated with a node'''
135 if not self.nodetagscache:
135 if not self.nodetagscache:
136 self.nodetagscache = {}
136 self.nodetagscache = {}
137 for t,n in self.tags().items():
137 for t,n in self.tags().items():
138 self.nodetagscache.setdefault(n,[]).append(t)
138 self.nodetagscache.setdefault(n,[]).append(t)
139 return self.nodetagscache.get(node, [])
139 return self.nodetagscache.get(node, [])
140
140
141 def lookup(self, key):
141 def lookup(self, key):
142 try:
142 try:
143 return self.tags()[key]
143 return self.tags()[key]
144 except KeyError:
144 except KeyError:
145 try:
145 try:
146 return self.changelog.lookup(key)
146 return self.changelog.lookup(key)
147 except:
147 except:
148 raise repo.RepoError(_("unknown revision '%s'") % key)
148 raise repo.RepoError(_("unknown revision '%s'") % key)
149
149
150 def dev(self):
150 def dev(self):
151 return os.stat(self.path).st_dev
151 return os.stat(self.path).st_dev
152
152
153 def local(self):
153 def local(self):
154 return True
154 return True
155
155
156 def join(self, f):
156 def join(self, f):
157 return os.path.join(self.path, f)
157 return os.path.join(self.path, f)
158
158
159 def wjoin(self, f):
159 def wjoin(self, f):
160 return os.path.join(self.root, f)
160 return os.path.join(self.root, f)
161
161
162 def file(self, f):
162 def file(self, f):
163 if f[0] == '/': f = f[1:]
163 if f[0] == '/': f = f[1:]
164 return filelog.filelog(self.opener, f)
164 return filelog.filelog(self.opener, f)
165
165
166 def getcwd(self):
166 def getcwd(self):
167 return self.dirstate.getcwd()
167 return self.dirstate.getcwd()
168
168
169 def wfile(self, f, mode='r'):
169 def wfile(self, f, mode='r'):
170 return self.wopener(f, mode)
170 return self.wopener(f, mode)
171
171
172 def wread(self, filename):
172 def wread(self, filename):
173 if self.encodepats == None:
173 if self.encodepats == None:
174 l = []
174 l = []
175 for pat, cmd in self.ui.configitems("encode"):
175 for pat, cmd in self.ui.configitems("encode"):
176 mf = util.matcher("", "/", [pat], [], [])[1]
176 mf = util.matcher("", "/", [pat], [], [])[1]
177 l.append((mf, cmd))
177 l.append((mf, cmd))
178 self.encodepats = l
178 self.encodepats = l
179
179
180 data = self.wopener(filename, 'r').read()
180 data = self.wopener(filename, 'r').read()
181
181
182 for mf, cmd in self.encodepats:
182 for mf, cmd in self.encodepats:
183 if mf(filename):
183 if mf(filename):
184 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
184 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
185 data = util.filter(data, cmd)
185 data = util.filter(data, cmd)
186 break
186 break
187
187
188 return data
188 return data
189
189
190 def wwrite(self, filename, data, fd=None):
190 def wwrite(self, filename, data, fd=None):
191 if self.decodepats == None:
191 if self.decodepats == None:
192 l = []
192 l = []
193 for pat, cmd in self.ui.configitems("decode"):
193 for pat, cmd in self.ui.configitems("decode"):
194 mf = util.matcher("", "/", [pat], [], [])[1]
194 mf = util.matcher("", "/", [pat], [], [])[1]
195 l.append((mf, cmd))
195 l.append((mf, cmd))
196 self.decodepats = l
196 self.decodepats = l
197
197
198 for mf, cmd in self.decodepats:
198 for mf, cmd in self.decodepats:
199 if mf(filename):
199 if mf(filename):
200 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
200 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
201 data = util.filter(data, cmd)
201 data = util.filter(data, cmd)
202 break
202 break
203
203
204 if fd:
204 if fd:
205 return fd.write(data)
205 return fd.write(data)
206 return self.wopener(filename, 'w').write(data)
206 return self.wopener(filename, 'w').write(data)
207
207
208 def transaction(self):
208 def transaction(self):
209 # save dirstate for undo
209 # save dirstate for undo
210 try:
210 try:
211 ds = self.opener("dirstate").read()
211 ds = self.opener("dirstate").read()
212 except IOError:
212 except IOError:
213 ds = ""
213 ds = ""
214 self.opener("journal.dirstate", "w").write(ds)
214 self.opener("journal.dirstate", "w").write(ds)
215
215
216 def after():
216 def after():
217 util.rename(self.join("journal"), self.join("undo"))
217 util.rename(self.join("journal"), self.join("undo"))
218 util.rename(self.join("journal.dirstate"),
218 util.rename(self.join("journal.dirstate"),
219 self.join("undo.dirstate"))
219 self.join("undo.dirstate"))
220
220
221 return transaction.transaction(self.ui.warn, self.opener,
221 return transaction.transaction(self.ui.warn, self.opener,
222 self.join("journal"), after)
222 self.join("journal"), after)
223
223
224 def recover(self):
224 def recover(self):
225 lock = self.lock()
225 lock = self.lock()
226 if os.path.exists(self.join("journal")):
226 if os.path.exists(self.join("journal")):
227 self.ui.status(_("rolling back interrupted transaction\n"))
227 self.ui.status(_("rolling back interrupted transaction\n"))
228 transaction.rollback(self.opener, self.join("journal"))
228 transaction.rollback(self.opener, self.join("journal"))
229 return True
229 return True
230 else:
230 else:
231 self.ui.warn(_("no interrupted transaction available\n"))
231 self.ui.warn(_("no interrupted transaction available\n"))
232 return False
232 return False
233
233
234 def undo(self):
234 def undo(self):
235 wlock = self.wlock()
235 wlock = self.wlock()
236 lock = self.lock()
236 lock = self.lock()
237 if os.path.exists(self.join("undo")):
237 if os.path.exists(self.join("undo")):
238 self.ui.status(_("rolling back last transaction\n"))
238 self.ui.status(_("rolling back last transaction\n"))
239 transaction.rollback(self.opener, self.join("undo"))
239 transaction.rollback(self.opener, self.join("undo"))
240 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
240 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
241 self.dirstate.read()
241 self.dirstate.read()
242 else:
242 else:
243 self.ui.warn(_("no undo information available\n"))
243 self.ui.warn(_("no undo information available\n"))
244
244
245 def lock(self, wait=1):
245 def lock(self, wait=1):
246 try:
246 try:
247 return lock.lock(self.join("lock"), 0)
247 return lock.lock(self.join("lock"), 0)
248 except lock.LockHeld, inst:
248 except lock.LockHeld, inst:
249 if wait:
249 if wait:
250 self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0])
250 self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0])
251 return lock.lock(self.join("lock"), wait)
251 return lock.lock(self.join("lock"), wait)
252 raise inst
252 raise inst
253
253
254 def wlock(self, wait=1):
254 def wlock(self, wait=1):
255 try:
255 try:
256 wlock = lock.lock(self.join("wlock"), 0, self.dirstate.write)
256 wlock = lock.lock(self.join("wlock"), 0, self.dirstate.write)
257 except lock.LockHeld, inst:
257 except lock.LockHeld, inst:
258 if not wait:
258 if not wait:
259 raise inst
259 raise inst
260 self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0])
260 self.ui.warn(_("waiting for lock held by %s\n") % inst.args[0])
261 wlock = lock.lock(self.join("wlock"), wait, self.dirstate.write)
261 wlock = lock.lock(self.join("wlock"), wait, self.dirstate.write)
262 self.dirstate.read()
262 self.dirstate.read()
263 return wlock
263 return wlock
264
264
265 def rawcommit(self, files, text, user, date, p1=None, p2=None):
265 def rawcommit(self, files, text, user, date, p1=None, p2=None):
266 orig_parent = self.dirstate.parents()[0] or nullid
266 orig_parent = self.dirstate.parents()[0] or nullid
267 p1 = p1 or self.dirstate.parents()[0] or nullid
267 p1 = p1 or self.dirstate.parents()[0] or nullid
268 p2 = p2 or self.dirstate.parents()[1] or nullid
268 p2 = p2 or self.dirstate.parents()[1] or nullid
269 c1 = self.changelog.read(p1)
269 c1 = self.changelog.read(p1)
270 c2 = self.changelog.read(p2)
270 c2 = self.changelog.read(p2)
271 m1 = self.manifest.read(c1[0])
271 m1 = self.manifest.read(c1[0])
272 mf1 = self.manifest.readflags(c1[0])
272 mf1 = self.manifest.readflags(c1[0])
273 m2 = self.manifest.read(c2[0])
273 m2 = self.manifest.read(c2[0])
274 changed = []
274 changed = []
275
275
276 if orig_parent == p1:
276 if orig_parent == p1:
277 update_dirstate = 1
277 update_dirstate = 1
278 else:
278 else:
279 update_dirstate = 0
279 update_dirstate = 0
280
280
281 wlock = self.wlock()
281 wlock = self.wlock()
282 lock = self.lock()
282 lock = self.lock()
283 tr = self.transaction()
283 tr = self.transaction()
284 mm = m1.copy()
284 mm = m1.copy()
285 mfm = mf1.copy()
285 mfm = mf1.copy()
286 linkrev = self.changelog.count()
286 linkrev = self.changelog.count()
287 for f in files:
287 for f in files:
288 try:
288 try:
289 t = self.wread(f)
289 t = self.wread(f)
290 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
290 tm = util.is_exec(self.wjoin(f), mfm.get(f, False))
291 r = self.file(f)
291 r = self.file(f)
292 mfm[f] = tm
292 mfm[f] = tm
293
293
294 fp1 = m1.get(f, nullid)
294 fp1 = m1.get(f, nullid)
295 fp2 = m2.get(f, nullid)
295 fp2 = m2.get(f, nullid)
296
296
297 # is the same revision on two branches of a merge?
297 # is the same revision on two branches of a merge?
298 if fp2 == fp1:
298 if fp2 == fp1:
299 fp2 = nullid
299 fp2 = nullid
300
300
301 if fp2 != nullid:
301 if fp2 != nullid:
302 # is one parent an ancestor of the other?
302 # is one parent an ancestor of the other?
303 fpa = r.ancestor(fp1, fp2)
303 fpa = r.ancestor(fp1, fp2)
304 if fpa == fp1:
304 if fpa == fp1:
305 fp1, fp2 = fp2, nullid
305 fp1, fp2 = fp2, nullid
306 elif fpa == fp2:
306 elif fpa == fp2:
307 fp2 = nullid
307 fp2 = nullid
308
308
309 # is the file unmodified from the parent?
309 # is the file unmodified from the parent?
310 if t == r.read(fp1):
310 if t == r.read(fp1):
311 # record the proper existing parent in manifest
311 # record the proper existing parent in manifest
312 # no need to add a revision
312 # no need to add a revision
313 mm[f] = fp1
313 mm[f] = fp1
314 continue
314 continue
315
315
316 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
316 mm[f] = r.add(t, {}, tr, linkrev, fp1, fp2)
317 changed.append(f)
317 changed.append(f)
318 if update_dirstate:
318 if update_dirstate:
319 self.dirstate.update([f], "n")
319 self.dirstate.update([f], "n")
320 except IOError:
320 except IOError:
321 try:
321 try:
322 del mm[f]
322 del mm[f]
323 del mfm[f]
323 del mfm[f]
324 if update_dirstate:
324 if update_dirstate:
325 self.dirstate.forget([f])
325 self.dirstate.forget([f])
326 except:
326 except:
327 # deleted from p2?
327 # deleted from p2?
328 pass
328 pass
329
329
330 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
330 mnode = self.manifest.add(mm, mfm, tr, linkrev, c1[0], c2[0])
331 user = user or self.ui.username()
331 user = user or self.ui.username()
332 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
332 n = self.changelog.add(mnode, changed, text, tr, p1, p2, user, date)
333 tr.close()
333 tr.close()
334 if update_dirstate:
334 if update_dirstate:
335 self.dirstate.setparents(n, nullid)
335 self.dirstate.setparents(n, nullid)
336
336
337 def commit(self, files = None, text = "", user = None, date = None,
337 def commit(self, files = None, text = "", user = None, date = None,
338 match = util.always, force=False):
338 match = util.always, force=False):
339 commit = []
339 commit = []
340 remove = []
340 remove = []
341 changed = []
341 changed = []
342
342
343 if files:
343 if files:
344 for f in files:
344 for f in files:
345 s = self.dirstate.state(f)
345 s = self.dirstate.state(f)
346 if s in 'nmai':
346 if s in 'nmai':
347 commit.append(f)
347 commit.append(f)
348 elif s == 'r':
348 elif s == 'r':
349 remove.append(f)
349 remove.append(f)
350 else:
350 else:
351 self.ui.warn(_("%s not tracked!\n") % f)
351 self.ui.warn(_("%s not tracked!\n") % f)
352 else:
352 else:
353 (c, a, d, u) = self.changes(match=match)
353 (c, a, d, u) = self.changes(match=match)
354 commit = c + a
354 commit = c + a
355 remove = d
355 remove = d
356
356
357 p1, p2 = self.dirstate.parents()
357 p1, p2 = self.dirstate.parents()
358 c1 = self.changelog.read(p1)
358 c1 = self.changelog.read(p1)
359 c2 = self.changelog.read(p2)
359 c2 = self.changelog.read(p2)
360 m1 = self.manifest.read(c1[0])
360 m1 = self.manifest.read(c1[0])
361 mf1 = self.manifest.readflags(c1[0])
361 mf1 = self.manifest.readflags(c1[0])
362 m2 = self.manifest.read(c2[0])
362 m2 = self.manifest.read(c2[0])
363
363
364 if not commit and not remove and not force and p2 == nullid:
364 if not commit and not remove and not force and p2 == nullid:
365 self.ui.status(_("nothing changed\n"))
365 self.ui.status(_("nothing changed\n"))
366 return None
366 return None
367
367
368 if not self.hook("precommit"):
368 if not self.hook("precommit"):
369 return None
369 return None
370
370
371 wlock = self.wlock()
371 wlock = self.wlock()
372 lock = self.lock()
372 lock = self.lock()
373 tr = self.transaction()
373 tr = self.transaction()
374
374
375 # check in files
375 # check in files
376 new = {}
376 new = {}
377 linkrev = self.changelog.count()
377 linkrev = self.changelog.count()
378 commit.sort()
378 commit.sort()
379 for f in commit:
379 for f in commit:
380 self.ui.note(f + "\n")
380 self.ui.note(f + "\n")
381 try:
381 try:
382 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
382 mf1[f] = util.is_exec(self.wjoin(f), mf1.get(f, False))
383 t = self.wread(f)
383 t = self.wread(f)
384 except IOError:
384 except IOError:
385 self.ui.warn(_("trouble committing %s!\n") % f)
385 self.ui.warn(_("trouble committing %s!\n") % f)
386 raise
386 raise
387
387
388 r = self.file(f)
388 r = self.file(f)
389
389
390 meta = {}
390 meta = {}
391 cp = self.dirstate.copied(f)
391 cp = self.dirstate.copied(f)
392 if cp:
392 if cp:
393 meta["copy"] = cp
393 meta["copy"] = cp
394 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
394 meta["copyrev"] = hex(m1.get(cp, m2.get(cp, nullid)))
395 self.ui.debug(_(" %s: copy %s:%s\n") % (f, cp, meta["copyrev"]))
395 self.ui.debug(_(" %s: copy %s:%s\n") % (f, cp, meta["copyrev"]))
396 fp1, fp2 = nullid, nullid
396 fp1, fp2 = nullid, nullid
397 else:
397 else:
398 fp1 = m1.get(f, nullid)
398 fp1 = m1.get(f, nullid)
399 fp2 = m2.get(f, nullid)
399 fp2 = m2.get(f, nullid)
400
400
401 # is the same revision on two branches of a merge?
401 # is the same revision on two branches of a merge?
402 if fp2 == fp1:
402 if fp2 == fp1:
403 fp2 = nullid
403 fp2 = nullid
404
404
405 if fp2 != nullid:
405 if fp2 != nullid:
406 # is one parent an ancestor of the other?
406 # is one parent an ancestor of the other?
407 fpa = r.ancestor(fp1, fp2)
407 fpa = r.ancestor(fp1, fp2)
408 if fpa == fp1:
408 if fpa == fp1:
409 fp1, fp2 = fp2, nullid
409 fp1, fp2 = fp2, nullid
410 elif fpa == fp2:
410 elif fpa == fp2:
411 fp2 = nullid
411 fp2 = nullid
412
412
413 # is the file unmodified from the parent?
413 # is the file unmodified from the parent?
414 if not meta and t == r.read(fp1):
414 if not meta and t == r.read(fp1):
415 # record the proper existing parent in manifest
415 # record the proper existing parent in manifest
416 # no need to add a revision
416 # no need to add a revision
417 new[f] = fp1
417 new[f] = fp1
418 continue
418 continue
419
419
420 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
420 new[f] = r.add(t, meta, tr, linkrev, fp1, fp2)
421 # remember what we've added so that we can later calculate
421 # remember what we've added so that we can later calculate
422 # the files to pull from a set of changesets
422 # the files to pull from a set of changesets
423 changed.append(f)
423 changed.append(f)
424
424
425 # update manifest
425 # update manifest
426 m1.update(new)
426 m1.update(new)
427 for f in remove:
427 for f in remove:
428 if f in m1:
428 if f in m1:
429 del m1[f]
429 del m1[f]
430 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
430 mn = self.manifest.add(m1, mf1, tr, linkrev, c1[0], c2[0],
431 (new, remove))
431 (new, remove))
432
432
433 # add changeset
433 # add changeset
434 new = new.keys()
434 new = new.keys()
435 new.sort()
435 new.sort()
436
436
437 if not text:
437 if not text:
438 edittext = ""
438 edittext = ""
439 if p2 != nullid:
439 if p2 != nullid:
440 edittext += "HG: branch merge\n"
440 edittext += "HG: branch merge\n"
441 edittext += "\n" + "HG: manifest hash %s\n" % hex(mn)
441 edittext += "\n" + "HG: manifest hash %s\n" % hex(mn)
442 edittext += "".join(["HG: changed %s\n" % f for f in changed])
442 edittext += "".join(["HG: changed %s\n" % f for f in changed])
443 edittext += "".join(["HG: removed %s\n" % f for f in remove])
443 edittext += "".join(["HG: removed %s\n" % f for f in remove])
444 if not changed and not remove:
444 if not changed and not remove:
445 edittext += "HG: no files changed\n"
445 edittext += "HG: no files changed\n"
446 edittext = self.ui.edit(edittext)
446 edittext = self.ui.edit(edittext)
447 if not edittext.rstrip():
447 if not edittext.rstrip():
448 return None
448 return None
449 text = edittext
449 text = edittext
450
450
451 user = user or self.ui.username()
451 user = user or self.ui.username()
452 n = self.changelog.add(mn, changed, text, tr, p1, p2, user, date)
452 n = self.changelog.add(mn, changed, text, tr, p1, p2, user, date)
453 tr.close()
453 tr.close()
454
454
455 self.dirstate.setparents(n)
455 self.dirstate.setparents(n)
456 self.dirstate.update(new, "n")
456 self.dirstate.update(new, "n")
457 self.dirstate.forget(remove)
457 self.dirstate.forget(remove)
458
458
459 if not self.hook("commit", node=hex(n)):
459 if not self.hook("commit", node=hex(n)):
460 return None
460 return None
461 return n
461 return n
462
462
463 def walk(self, node=None, files=[], match=util.always):
463 def walk(self, node=None, files=[], match=util.always):
464 if node:
464 if node:
465 for fn in self.manifest.read(self.changelog.read(node)[0]):
465 for fn in self.manifest.read(self.changelog.read(node)[0]):
466 if match(fn): yield 'm', fn
466 if match(fn): yield 'm', fn
467 else:
467 else:
468 for src, fn in self.dirstate.walk(files, match):
468 for src, fn in self.dirstate.walk(files, match):
469 yield src, fn
469 yield src, fn
470
470
471 def changes(self, node1 = None, node2 = None, files = [],
471 def changes(self, node1 = None, node2 = None, files = [],
472 match = util.always):
472 match = util.always):
473 mf2, u = None, []
473 mf2, u = None, []
474
474
475 def fcmp(fn, mf):
475 def fcmp(fn, mf):
476 t1 = self.wread(fn)
476 t1 = self.wread(fn)
477 t2 = self.file(fn).read(mf.get(fn, nullid))
477 t2 = self.file(fn).read(mf.get(fn, nullid))
478 return cmp(t1, t2)
478 return cmp(t1, t2)
479
479
480 def mfmatches(node):
480 def mfmatches(node):
481 mf = dict(self.manifest.read(node))
481 mf = dict(self.manifest.read(node))
482 for fn in mf.keys():
482 for fn in mf.keys():
483 if not match(fn):
483 if not match(fn):
484 del mf[fn]
484 del mf[fn]
485 return mf
485 return mf
486
486
487 # are we comparing the working directory?
487 # are we comparing the working directory?
488 if not node2:
488 if not node2:
489 try:
489 try:
490 wlock = self.wlock(wait=0)
490 wlock = self.wlock(wait=0)
491 except lock.LockHeld:
491 except lock.LockHeld:
492 wlock = None
492 wlock = None
493 l, c, a, d, u = self.dirstate.changes(files, match)
493 l, c, a, d, u = self.dirstate.changes(files, match)
494
494
495 # are we comparing working dir against its parent?
495 # are we comparing working dir against its parent?
496 if not node1:
496 if not node1:
497 if l:
497 if l:
498 # do a full compare of any files that might have changed
498 # do a full compare of any files that might have changed
499 change = self.changelog.read(self.dirstate.parents()[0])
499 change = self.changelog.read(self.dirstate.parents()[0])
500 mf2 = mfmatches(change[0])
500 mf2 = mfmatches(change[0])
501 for f in l:
501 for f in l:
502 if fcmp(f, mf2):
502 if fcmp(f, mf2):
503 c.append(f)
503 c.append(f)
504 elif wlock is not None:
504 elif wlock is not None:
505 self.dirstate.update([f], "n")
505 self.dirstate.update([f], "n")
506
506
507 for l in c, a, d, u:
507 for l in c, a, d, u:
508 l.sort()
508 l.sort()
509
509
510 return (c, a, d, u)
510 return (c, a, d, u)
511
511
512 # are we comparing working dir against non-tip?
512 # are we comparing working dir against non-tip?
513 # generate a pseudo-manifest for the working dir
513 # generate a pseudo-manifest for the working dir
514 if not node2:
514 if not node2:
515 if not mf2:
515 if not mf2:
516 change = self.changelog.read(self.dirstate.parents()[0])
516 change = self.changelog.read(self.dirstate.parents()[0])
517 mf2 = mfmatches(change[0])
517 mf2 = mfmatches(change[0])
518 for f in a + c + l:
518 for f in a + c + l:
519 mf2[f] = ""
519 mf2[f] = ""
520 for f in d:
520 for f in d:
521 if f in mf2: del mf2[f]
521 if f in mf2: del mf2[f]
522 else:
522 else:
523 change = self.changelog.read(node2)
523 change = self.changelog.read(node2)
524 mf2 = mfmatches(change[0])
524 mf2 = mfmatches(change[0])
525
525
526 # flush lists from dirstate before comparing manifests
526 # flush lists from dirstate before comparing manifests
527 c, a = [], []
527 c, a = [], []
528
528
529 change = self.changelog.read(node1)
529 change = self.changelog.read(node1)
530 mf1 = mfmatches(change[0])
530 mf1 = mfmatches(change[0])
531
531
532 for fn in mf2:
532 for fn in mf2:
533 if mf1.has_key(fn):
533 if mf1.has_key(fn):
534 if mf1[fn] != mf2[fn]:
534 if mf1[fn] != mf2[fn]:
535 if mf2[fn] != "" or fcmp(fn, mf1):
535 if mf2[fn] != "" or fcmp(fn, mf1):
536 c.append(fn)
536 c.append(fn)
537 del mf1[fn]
537 del mf1[fn]
538 else:
538 else:
539 a.append(fn)
539 a.append(fn)
540
540
541 d = mf1.keys()
541 d = mf1.keys()
542
542
543 for l in c, a, d, u:
543 for l in c, a, d, u:
544 l.sort()
544 l.sort()
545
545
546 return (c, a, d, u)
546 return (c, a, d, u)
547
547
548 def add(self, list):
548 def add(self, list):
549 wlock = self.wlock()
549 wlock = self.wlock()
550 for f in list:
550 for f in list:
551 p = self.wjoin(f)
551 p = self.wjoin(f)
552 if not os.path.exists(p):
552 if not os.path.exists(p):
553 self.ui.warn(_("%s does not exist!\n") % f)
553 self.ui.warn(_("%s does not exist!\n") % f)
554 elif not os.path.isfile(p):
554 elif not os.path.isfile(p):
555 self.ui.warn(_("%s not added: only files supported currently\n") % f)
555 self.ui.warn(_("%s not added: only files supported currently\n") % f)
556 elif self.dirstate.state(f) in 'an':
556 elif self.dirstate.state(f) in 'an':
557 self.ui.warn(_("%s already tracked!\n") % f)
557 self.ui.warn(_("%s already tracked!\n") % f)
558 else:
558 else:
559 self.dirstate.update([f], "a")
559 self.dirstate.update([f], "a")
560
560
561 def forget(self, list):
561 def forget(self, list):
562 wlock = self.wlock()
562 wlock = self.wlock()
563 for f in list:
563 for f in list:
564 if self.dirstate.state(f) not in 'ai':
564 if self.dirstate.state(f) not in 'ai':
565 self.ui.warn(_("%s not added!\n") % f)
565 self.ui.warn(_("%s not added!\n") % f)
566 else:
566 else:
567 self.dirstate.forget([f])
567 self.dirstate.forget([f])
568
568
569 def remove(self, list, unlink=False):
569 def remove(self, list, unlink=False):
570 if unlink:
570 if unlink:
571 for f in list:
571 for f in list:
572 try:
572 try:
573 util.unlink(self.wjoin(f))
573 util.unlink(self.wjoin(f))
574 except OSError, inst:
574 except OSError, inst:
575 if inst.errno != errno.ENOENT: raise
575 if inst.errno != errno.ENOENT: raise
576 wlock = self.wlock()
576 wlock = self.wlock()
577 for f in list:
577 for f in list:
578 p = self.wjoin(f)
578 p = self.wjoin(f)
579 if os.path.exists(p):
579 if os.path.exists(p):
580 self.ui.warn(_("%s still exists!\n") % f)
580 self.ui.warn(_("%s still exists!\n") % f)
581 elif self.dirstate.state(f) == 'a':
581 elif self.dirstate.state(f) == 'a':
582 self.ui.warn(_("%s never committed!\n") % f)
582 self.ui.warn(_("%s never committed!\n") % f)
583 self.dirstate.forget([f])
583 self.dirstate.forget([f])
584 elif f not in self.dirstate:
584 elif f not in self.dirstate:
585 self.ui.warn(_("%s not tracked!\n") % f)
585 self.ui.warn(_("%s not tracked!\n") % f)
586 else:
586 else:
587 self.dirstate.update([f], "r")
587 self.dirstate.update([f], "r")
588
588
589 def undelete(self, list):
589 def undelete(self, list):
590 p = self.dirstate.parents()[0]
590 p = self.dirstate.parents()[0]
591 mn = self.changelog.read(p)[0]
591 mn = self.changelog.read(p)[0]
592 mf = self.manifest.readflags(mn)
592 mf = self.manifest.readflags(mn)
593 m = self.manifest.read(mn)
593 m = self.manifest.read(mn)
594 wlock = self.wlock()
594 wlock = self.wlock()
595 for f in list:
595 for f in list:
596 if self.dirstate.state(f) not in "r":
596 if self.dirstate.state(f) not in "r":
597 self.ui.warn("%s not removed!\n" % f)
597 self.ui.warn("%s not removed!\n" % f)
598 else:
598 else:
599 t = self.file(f).read(m[f])
599 t = self.file(f).read(m[f])
600 self.wwrite(f, t)
600 self.wwrite(f, t)
601 util.set_exec(self.wjoin(f), mf[f])
601 util.set_exec(self.wjoin(f), mf[f])
602 self.dirstate.update([f], "n")
602 self.dirstate.update([f], "n")
603
603
604 def copy(self, source, dest):
604 def copy(self, source, dest):
605 p = self.wjoin(dest)
605 p = self.wjoin(dest)
606 if not os.path.exists(p):
606 if not os.path.exists(p):
607 self.ui.warn(_("%s does not exist!\n") % dest)
607 self.ui.warn(_("%s does not exist!\n") % dest)
608 elif not os.path.isfile(p):
608 elif not os.path.isfile(p):
609 self.ui.warn(_("copy failed: %s is not a file\n") % dest)
609 self.ui.warn(_("copy failed: %s is not a file\n") % dest)
610 else:
610 else:
611 wlock = self.wlock()
611 wlock = self.wlock()
612 if self.dirstate.state(dest) == '?':
612 if self.dirstate.state(dest) == '?':
613 self.dirstate.update([dest], "a")
613 self.dirstate.update([dest], "a")
614 self.dirstate.copy(source, dest)
614 self.dirstate.copy(source, dest)
615
615
616 def heads(self, start=None):
616 def heads(self, start=None):
617 heads = self.changelog.heads(start)
617 heads = self.changelog.heads(start)
618 # sort the output in rev descending order
618 # sort the output in rev descending order
619 heads = [(-self.changelog.rev(h), h) for h in heads]
619 heads = [(-self.changelog.rev(h), h) for h in heads]
620 heads.sort()
620 heads.sort()
621 return [n for (r, n) in heads]
621 return [n for (r, n) in heads]
622
622
623 # branchlookup returns a dict giving a list of branches for
623 # branchlookup returns a dict giving a list of branches for
624 # each head. A branch is defined as the tag of a node or
624 # each head. A branch is defined as the tag of a node or
625 # the branch of the node's parents. If a node has multiple
625 # the branch of the node's parents. If a node has multiple
626 # branch tags, tags are eliminated if they are visible from other
626 # branch tags, tags are eliminated if they are visible from other
627 # branch tags.
627 # branch tags.
628 #
628 #
629 # So, for this graph: a->b->c->d->e
629 # So, for this graph: a->b->c->d->e
630 # \ /
630 # \ /
631 # aa -----/
631 # aa -----/
632 # a has tag 2.6.12
632 # a has tag 2.6.12
633 # d has tag 2.6.13
633 # d has tag 2.6.13
634 # e would have branch tags for 2.6.12 and 2.6.13. Because the node
634 # e would have branch tags for 2.6.12 and 2.6.13. Because the node
635 # for 2.6.12 can be reached from the node 2.6.13, that is eliminated
635 # for 2.6.12 can be reached from the node 2.6.13, that is eliminated
636 # from the list.
636 # from the list.
637 #
637 #
638 # It is possible that more than one head will have the same branch tag.
638 # It is possible that more than one head will have the same branch tag.
639 # callers need to check the result for multiple heads under the same
639 # callers need to check the result for multiple heads under the same
640 # branch tag if that is a problem for them (ie checkout of a specific
640 # branch tag if that is a problem for them (ie checkout of a specific
641 # branch).
641 # branch).
642 #
642 #
643 # passing in a specific branch will limit the depth of the search
643 # passing in a specific branch will limit the depth of the search
644 # through the parents. It won't limit the branches returned in the
644 # through the parents. It won't limit the branches returned in the
645 # result though.
645 # result though.
646 def branchlookup(self, heads=None, branch=None):
646 def branchlookup(self, heads=None, branch=None):
647 if not heads:
647 if not heads:
648 heads = self.heads()
648 heads = self.heads()
649 headt = [ h for h in heads ]
649 headt = [ h for h in heads ]
650 chlog = self.changelog
650 chlog = self.changelog
651 branches = {}
651 branches = {}
652 merges = []
652 merges = []
653 seenmerge = {}
653 seenmerge = {}
654
654
655 # traverse the tree once for each head, recording in the branches
655 # traverse the tree once for each head, recording in the branches
656 # dict which tags are visible from this head. The branches
656 # dict which tags are visible from this head. The branches
657 # dict also records which tags are visible from each tag
657 # dict also records which tags are visible from each tag
658 # while we traverse.
658 # while we traverse.
659 while headt or merges:
659 while headt or merges:
660 if merges:
660 if merges:
661 n, found = merges.pop()
661 n, found = merges.pop()
662 visit = [n]
662 visit = [n]
663 else:
663 else:
664 h = headt.pop()
664 h = headt.pop()
665 visit = [h]
665 visit = [h]
666 found = [h]
666 found = [h]
667 seen = {}
667 seen = {}
668 while visit:
668 while visit:
669 n = visit.pop()
669 n = visit.pop()
670 if n in seen:
670 if n in seen:
671 continue
671 continue
672 pp = chlog.parents(n)
672 pp = chlog.parents(n)
673 tags = self.nodetags(n)
673 tags = self.nodetags(n)
674 if tags:
674 if tags:
675 for x in tags:
675 for x in tags:
676 if x == 'tip':
676 if x == 'tip':
677 continue
677 continue
678 for f in found:
678 for f in found:
679 branches.setdefault(f, {})[n] = 1
679 branches.setdefault(f, {})[n] = 1
680 branches.setdefault(n, {})[n] = 1
680 branches.setdefault(n, {})[n] = 1
681 break
681 break
682 if n not in found:
682 if n not in found:
683 found.append(n)
683 found.append(n)
684 if branch in tags:
684 if branch in tags:
685 continue
685 continue
686 seen[n] = 1
686 seen[n] = 1
687 if pp[1] != nullid and n not in seenmerge:
687 if pp[1] != nullid and n not in seenmerge:
688 merges.append((pp[1], [x for x in found]))
688 merges.append((pp[1], [x for x in found]))
689 seenmerge[n] = 1
689 seenmerge[n] = 1
690 if pp[0] != nullid:
690 if pp[0] != nullid:
691 visit.append(pp[0])
691 visit.append(pp[0])
692 # traverse the branches dict, eliminating branch tags from each
692 # traverse the branches dict, eliminating branch tags from each
693 # head that are visible from another branch tag for that head.
693 # head that are visible from another branch tag for that head.
694 out = {}
694 out = {}
695 viscache = {}
695 viscache = {}
696 for h in heads:
696 for h in heads:
697 def visible(node):
697 def visible(node):
698 if node in viscache:
698 if node in viscache:
699 return viscache[node]
699 return viscache[node]
700 ret = {}
700 ret = {}
701 visit = [node]
701 visit = [node]
702 while visit:
702 while visit:
703 x = visit.pop()
703 x = visit.pop()
704 if x in viscache:
704 if x in viscache:
705 ret.update(viscache[x])
705 ret.update(viscache[x])
706 elif x not in ret:
706 elif x not in ret:
707 ret[x] = 1
707 ret[x] = 1
708 if x in branches:
708 if x in branches:
709 visit[len(visit):] = branches[x].keys()
709 visit[len(visit):] = branches[x].keys()
710 viscache[node] = ret
710 viscache[node] = ret
711 return ret
711 return ret
712 if h not in branches:
712 if h not in branches:
713 continue
713 continue
714 # O(n^2), but somewhat limited. This only searches the
714 # O(n^2), but somewhat limited. This only searches the
715 # tags visible from a specific head, not all the tags in the
715 # tags visible from a specific head, not all the tags in the
716 # whole repo.
716 # whole repo.
717 for b in branches[h]:
717 for b in branches[h]:
718 vis = False
718 vis = False
719 for bb in branches[h].keys():
719 for bb in branches[h].keys():
720 if b != bb:
720 if b != bb:
721 if b in visible(bb):
721 if b in visible(bb):
722 vis = True
722 vis = True
723 break
723 break
724 if not vis:
724 if not vis:
725 l = out.setdefault(h, [])
725 l = out.setdefault(h, [])
726 l[len(l):] = self.nodetags(b)
726 l[len(l):] = self.nodetags(b)
727 return out
727 return out
728
728
729 def branches(self, nodes):
729 def branches(self, nodes):
730 if not nodes: nodes = [self.changelog.tip()]
730 if not nodes: nodes = [self.changelog.tip()]
731 b = []
731 b = []
732 for n in nodes:
732 for n in nodes:
733 t = n
733 t = n
734 while n:
734 while n:
735 p = self.changelog.parents(n)
735 p = self.changelog.parents(n)
736 if p[1] != nullid or p[0] == nullid:
736 if p[1] != nullid or p[0] == nullid:
737 b.append((t, n, p[0], p[1]))
737 b.append((t, n, p[0], p[1]))
738 break
738 break
739 n = p[0]
739 n = p[0]
740 return b
740 return b
741
741
742 def between(self, pairs):
742 def between(self, pairs):
743 r = []
743 r = []
744
744
745 for top, bottom in pairs:
745 for top, bottom in pairs:
746 n, l, i = top, [], 0
746 n, l, i = top, [], 0
747 f = 1
747 f = 1
748
748
749 while n != bottom:
749 while n != bottom:
750 p = self.changelog.parents(n)[0]
750 p = self.changelog.parents(n)[0]
751 if i == f:
751 if i == f:
752 l.append(n)
752 l.append(n)
753 f = f * 2
753 f = f * 2
754 n = p
754 n = p
755 i += 1
755 i += 1
756
756
757 r.append(l)
757 r.append(l)
758
758
759 return r
759 return r
760
760
761 def findincoming(self, remote, base=None, heads=None):
761 def findincoming(self, remote, base=None, heads=None):
762 m = self.changelog.nodemap
762 m = self.changelog.nodemap
763 search = []
763 search = []
764 fetch = {}
764 fetch = {}
765 seen = {}
765 seen = {}
766 seenbranch = {}
766 seenbranch = {}
767 if base == None:
767 if base == None:
768 base = {}
768 base = {}
769
769
770 # assume we're closer to the tip than the root
770 # assume we're closer to the tip than the root
771 # and start by examining the heads
771 # and start by examining the heads
772 self.ui.status(_("searching for changes\n"))
772 self.ui.status(_("searching for changes\n"))
773
773
774 if not heads:
774 if not heads:
775 heads = remote.heads()
775 heads = remote.heads()
776
776
777 unknown = []
777 unknown = []
778 for h in heads:
778 for h in heads:
779 if h not in m:
779 if h not in m:
780 unknown.append(h)
780 unknown.append(h)
781 else:
781 else:
782 base[h] = 1
782 base[h] = 1
783
783
784 if not unknown:
784 if not unknown:
785 return None
785 return None
786
786
787 rep = {}
787 rep = {}
788 reqcnt = 0
788 reqcnt = 0
789
789
790 # search through remote branches
790 # search through remote branches
791 # a 'branch' here is a linear segment of history, with four parts:
791 # a 'branch' here is a linear segment of history, with four parts:
792 # head, root, first parent, second parent
792 # head, root, first parent, second parent
793 # (a branch always has two parents (or none) by definition)
793 # (a branch always has two parents (or none) by definition)
794 unknown = remote.branches(unknown)
794 unknown = remote.branches(unknown)
795 while unknown:
795 while unknown:
796 r = []
796 r = []
797 while unknown:
797 while unknown:
798 n = unknown.pop(0)
798 n = unknown.pop(0)
799 if n[0] in seen:
799 if n[0] in seen:
800 continue
800 continue
801
801
802 self.ui.debug(_("examining %s:%s\n") % (short(n[0]), short(n[1])))
802 self.ui.debug(_("examining %s:%s\n") % (short(n[0]), short(n[1])))
803 if n[0] == nullid:
803 if n[0] == nullid:
804 break
804 break
805 if n in seenbranch:
805 if n in seenbranch:
806 self.ui.debug(_("branch already found\n"))
806 self.ui.debug(_("branch already found\n"))
807 continue
807 continue
808 if n[1] and n[1] in m: # do we know the base?
808 if n[1] and n[1] in m: # do we know the base?
809 self.ui.debug(_("found incomplete branch %s:%s\n")
809 self.ui.debug(_("found incomplete branch %s:%s\n")
810 % (short(n[0]), short(n[1])))
810 % (short(n[0]), short(n[1])))
811 search.append(n) # schedule branch range for scanning
811 search.append(n) # schedule branch range for scanning
812 seenbranch[n] = 1
812 seenbranch[n] = 1
813 else:
813 else:
814 if n[1] not in seen and n[1] not in fetch:
814 if n[1] not in seen and n[1] not in fetch:
815 if n[2] in m and n[3] in m:
815 if n[2] in m and n[3] in m:
816 self.ui.debug(_("found new changeset %s\n") %
816 self.ui.debug(_("found new changeset %s\n") %
817 short(n[1]))
817 short(n[1]))
818 fetch[n[1]] = 1 # earliest unknown
818 fetch[n[1]] = 1 # earliest unknown
819 base[n[2]] = 1 # latest known
819 base[n[2]] = 1 # latest known
820 continue
820 continue
821
821
822 for a in n[2:4]:
822 for a in n[2:4]:
823 if a not in rep:
823 if a not in rep:
824 r.append(a)
824 r.append(a)
825 rep[a] = 1
825 rep[a] = 1
826
826
827 seen[n[0]] = 1
827 seen[n[0]] = 1
828
828
829 if r:
829 if r:
830 reqcnt += 1
830 reqcnt += 1
831 self.ui.debug(_("request %d: %s\n") %
831 self.ui.debug(_("request %d: %s\n") %
832 (reqcnt, " ".join(map(short, r))))
832 (reqcnt, " ".join(map(short, r))))
833 for p in range(0, len(r), 10):
833 for p in range(0, len(r), 10):
834 for b in remote.branches(r[p:p+10]):
834 for b in remote.branches(r[p:p+10]):
835 self.ui.debug(_("received %s:%s\n") %
835 self.ui.debug(_("received %s:%s\n") %
836 (short(b[0]), short(b[1])))
836 (short(b[0]), short(b[1])))
837 if b[0] in m:
837 if b[0] in m:
838 self.ui.debug(_("found base node %s\n") % short(b[0]))
838 self.ui.debug(_("found base node %s\n") % short(b[0]))
839 base[b[0]] = 1
839 base[b[0]] = 1
840 elif b[0] not in seen:
840 elif b[0] not in seen:
841 unknown.append(b)
841 unknown.append(b)
842
842
843 # do binary search on the branches we found
843 # do binary search on the branches we found
844 while search:
844 while search:
845 n = search.pop(0)
845 n = search.pop(0)
846 reqcnt += 1
846 reqcnt += 1
847 l = remote.between([(n[0], n[1])])[0]
847 l = remote.between([(n[0], n[1])])[0]
848 l.append(n[1])
848 l.append(n[1])
849 p = n[0]
849 p = n[0]
850 f = 1
850 f = 1
851 for i in l:
851 for i in l:
852 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
852 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
853 if i in m:
853 if i in m:
854 if f <= 2:
854 if f <= 2:
855 self.ui.debug(_("found new branch changeset %s\n") %
855 self.ui.debug(_("found new branch changeset %s\n") %
856 short(p))
856 short(p))
857 fetch[p] = 1
857 fetch[p] = 1
858 base[i] = 1
858 base[i] = 1
859 else:
859 else:
860 self.ui.debug(_("narrowed branch search to %s:%s\n")
860 self.ui.debug(_("narrowed branch search to %s:%s\n")
861 % (short(p), short(i)))
861 % (short(p), short(i)))
862 search.append((p, i))
862 search.append((p, i))
863 break
863 break
864 p, f = i, f * 2
864 p, f = i, f * 2
865
865
866 # sanity check our fetch list
866 # sanity check our fetch list
867 for f in fetch.keys():
867 for f in fetch.keys():
868 if f in m:
868 if f in m:
869 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
869 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
870
870
871 if base.keys() == [nullid]:
871 if base.keys() == [nullid]:
872 self.ui.warn(_("warning: pulling from an unrelated repository!\n"))
872 self.ui.warn(_("warning: pulling from an unrelated repository!\n"))
873
873
874 self.ui.note(_("found new changesets starting at ") +
874 self.ui.note(_("found new changesets starting at ") +
875 " ".join([short(f) for f in fetch]) + "\n")
875 " ".join([short(f) for f in fetch]) + "\n")
876
876
877 self.ui.debug(_("%d total queries\n") % reqcnt)
877 self.ui.debug(_("%d total queries\n") % reqcnt)
878
878
879 return fetch.keys()
879 return fetch.keys()
880
880
881 def findoutgoing(self, remote, base=None, heads=None):
881 def findoutgoing(self, remote, base=None, heads=None):
882 if base == None:
882 if base == None:
883 base = {}
883 base = {}
884 self.findincoming(remote, base, heads)
884 self.findincoming(remote, base, heads)
885
885
886 self.ui.debug(_("common changesets up to ")
886 self.ui.debug(_("common changesets up to ")
887 + " ".join(map(short, base.keys())) + "\n")
887 + " ".join(map(short, base.keys())) + "\n")
888
888
889 remain = dict.fromkeys(self.changelog.nodemap)
889 remain = dict.fromkeys(self.changelog.nodemap)
890
890
891 # prune everything remote has from the tree
891 # prune everything remote has from the tree
892 del remain[nullid]
892 del remain[nullid]
893 remove = base.keys()
893 remove = base.keys()
894 while remove:
894 while remove:
895 n = remove.pop(0)
895 n = remove.pop(0)
896 if n in remain:
896 if n in remain:
897 del remain[n]
897 del remain[n]
898 for p in self.changelog.parents(n):
898 for p in self.changelog.parents(n):
899 remove.append(p)
899 remove.append(p)
900
900
901 # find every node whose parents have been pruned
901 # find every node whose parents have been pruned
902 subset = []
902 subset = []
903 for n in remain:
903 for n in remain:
904 p1, p2 = self.changelog.parents(n)
904 p1, p2 = self.changelog.parents(n)
905 if p1 not in remain and p2 not in remain:
905 if p1 not in remain and p2 not in remain:
906 subset.append(n)
906 subset.append(n)
907
907
908 # this is the set of all roots we have to push
908 # this is the set of all roots we have to push
909 return subset
909 return subset
910
910
911 def pull(self, remote, heads = None):
911 def pull(self, remote, heads = None):
912 lock = self.lock()
912 lock = self.lock()
913
913
914 # if we have an empty repo, fetch everything
914 # if we have an empty repo, fetch everything
915 if self.changelog.tip() == nullid:
915 if self.changelog.tip() == nullid:
916 self.ui.status(_("requesting all changes\n"))
916 self.ui.status(_("requesting all changes\n"))
917 fetch = [nullid]
917 fetch = [nullid]
918 else:
918 else:
919 fetch = self.findincoming(remote)
919 fetch = self.findincoming(remote)
920
920
921 if not fetch:
921 if not fetch:
922 self.ui.status(_("no changes found\n"))
922 self.ui.status(_("no changes found\n"))
923 return 1
923 return 1
924
924
925 if heads is None:
925 if heads is None:
926 cg = remote.changegroup(fetch)
926 cg = remote.changegroup(fetch)
927 else:
927 else:
928 cg = remote.changegroupsubset(fetch, heads)
928 cg = remote.changegroupsubset(fetch, heads)
929 return self.addchangegroup(cg)
929 return self.addchangegroup(cg)
930
930
931 def push(self, remote, force=False):
931 def push(self, remote, force=False):
932 lock = remote.lock()
932 lock = remote.lock()
933
933
934 base = {}
934 base = {}
935 heads = remote.heads()
935 heads = remote.heads()
936 inc = self.findincoming(remote, base, heads)
936 inc = self.findincoming(remote, base, heads)
937 if not force and inc:
937 if not force and inc:
938 self.ui.warn(_("abort: unsynced remote changes!\n"))
938 self.ui.warn(_("abort: unsynced remote changes!\n"))
939 self.ui.status(_("(did you forget to sync? use push -f to force)\n"))
939 self.ui.status(_("(did you forget to sync? use push -f to force)\n"))
940 return 1
940 return 1
941
941
942 update = self.findoutgoing(remote, base)
942 update = self.findoutgoing(remote, base)
943 if not update:
943 if not update:
944 self.ui.status(_("no changes found\n"))
944 self.ui.status(_("no changes found\n"))
945 return 1
945 return 1
946 elif not force:
946 elif not force:
947 if len(heads) < len(self.changelog.heads()):
947 if len(heads) < len(self.changelog.heads()):
948 self.ui.warn(_("abort: push creates new remote branches!\n"))
948 self.ui.warn(_("abort: push creates new remote branches!\n"))
949 self.ui.status(_("(did you forget to merge?"
949 self.ui.status(_("(did you forget to merge?"
950 " use push -f to force)\n"))
950 " use push -f to force)\n"))
951 return 1
951 return 1
952
952
953 cg = self.changegroup(update)
953 cg = self.changegroup(update)
954 return remote.addchangegroup(cg)
954 return remote.addchangegroup(cg)
955
955
956 def changegroupsubset(self, bases, heads):
956 def changegroupsubset(self, bases, heads):
957 """This function generates a changegroup consisting of all the nodes
957 """This function generates a changegroup consisting of all the nodes
958 that are descendents of any of the bases, and ancestors of any of
958 that are descendents of any of the bases, and ancestors of any of
959 the heads.
959 the heads.
960
960
961 It is fairly complex as determining which filenodes and which
961 It is fairly complex as determining which filenodes and which
962 manifest nodes need to be included for the changeset to be complete
962 manifest nodes need to be included for the changeset to be complete
963 is non-trivial.
963 is non-trivial.
964
964
965 Another wrinkle is doing the reverse, figuring out which changeset in
965 Another wrinkle is doing the reverse, figuring out which changeset in
966 the changegroup a particular filenode or manifestnode belongs to."""
966 the changegroup a particular filenode or manifestnode belongs to."""
967
967
968 # Set up some initial variables
968 # Set up some initial variables
969 # Make it easy to refer to self.changelog
969 # Make it easy to refer to self.changelog
970 cl = self.changelog
970 cl = self.changelog
971 # msng is short for missing - compute the list of changesets in this
971 # msng is short for missing - compute the list of changesets in this
972 # changegroup.
972 # changegroup.
973 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
973 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
974 # Some bases may turn out to be superfluous, and some heads may be
974 # Some bases may turn out to be superfluous, and some heads may be
975 # too. nodesbetween will return the minimal set of bases and heads
975 # too. nodesbetween will return the minimal set of bases and heads
976 # necessary to re-create the changegroup.
976 # necessary to re-create the changegroup.
977
977
978 # Known heads are the list of heads that it is assumed the recipient
978 # Known heads are the list of heads that it is assumed the recipient
979 # of this changegroup will know about.
979 # of this changegroup will know about.
980 knownheads = {}
980 knownheads = {}
981 # We assume that all parents of bases are known heads.
981 # We assume that all parents of bases are known heads.
982 for n in bases:
982 for n in bases:
983 for p in cl.parents(n):
983 for p in cl.parents(n):
984 if p != nullid:
984 if p != nullid:
985 knownheads[p] = 1
985 knownheads[p] = 1
986 knownheads = knownheads.keys()
986 knownheads = knownheads.keys()
987 if knownheads:
987 if knownheads:
988 # Now that we know what heads are known, we can compute which
988 # Now that we know what heads are known, we can compute which
989 # changesets are known. The recipient must know about all
989 # changesets are known. The recipient must know about all
990 # changesets required to reach the known heads from the null
990 # changesets required to reach the known heads from the null
991 # changeset.
991 # changeset.
992 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
992 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
993 junk = None
993 junk = None
994 # Transform the list into an ersatz set.
994 # Transform the list into an ersatz set.
995 has_cl_set = dict.fromkeys(has_cl_set)
995 has_cl_set = dict.fromkeys(has_cl_set)
996 else:
996 else:
997 # If there were no known heads, the recipient cannot be assumed to
997 # If there were no known heads, the recipient cannot be assumed to
998 # know about any changesets.
998 # know about any changesets.
999 has_cl_set = {}
999 has_cl_set = {}
1000
1000
1001 # Make it easy to refer to self.manifest
1001 # Make it easy to refer to self.manifest
1002 mnfst = self.manifest
1002 mnfst = self.manifest
1003 # We don't know which manifests are missing yet
1003 # We don't know which manifests are missing yet
1004 msng_mnfst_set = {}
1004 msng_mnfst_set = {}
1005 # Nor do we know which filenodes are missing.
1005 # Nor do we know which filenodes are missing.
1006 msng_filenode_set = {}
1006 msng_filenode_set = {}
1007
1007
1008 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1008 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1009 junk = None
1009 junk = None
1010
1010
1011 # A changeset always belongs to itself, so the changenode lookup
1011 # A changeset always belongs to itself, so the changenode lookup
1012 # function for a changenode is identity.
1012 # function for a changenode is identity.
1013 def identity(x):
1013 def identity(x):
1014 return x
1014 return x
1015
1015
1016 # A function generating function. Sets up an environment for the
1016 # A function generating function. Sets up an environment for the
1017 # inner function.
1017 # inner function.
1018 def cmp_by_rev_func(revlog):
1018 def cmp_by_rev_func(revlog):
1019 # Compare two nodes by their revision number in the environment's
1019 # Compare two nodes by their revision number in the environment's
1020 # revision history. Since the revision number both represents the
1020 # revision history. Since the revision number both represents the
1021 # most efficient order to read the nodes in, and represents a
1021 # most efficient order to read the nodes in, and represents a
1022 # topological sorting of the nodes, this function is often useful.
1022 # topological sorting of the nodes, this function is often useful.
1023 def cmp_by_rev(a, b):
1023 def cmp_by_rev(a, b):
1024 return cmp(revlog.rev(a), revlog.rev(b))
1024 return cmp(revlog.rev(a), revlog.rev(b))
1025 return cmp_by_rev
1025 return cmp_by_rev
1026
1026
1027 # If we determine that a particular file or manifest node must be a
1027 # If we determine that a particular file or manifest node must be a
1028 # node that the recipient of the changegroup will already have, we can
1028 # node that the recipient of the changegroup will already have, we can
1029 # also assume the recipient will have all the parents. This function
1029 # also assume the recipient will have all the parents. This function
1030 # prunes them from the set of missing nodes.
1030 # prunes them from the set of missing nodes.
1031 def prune_parents(revlog, hasset, msngset):
1031 def prune_parents(revlog, hasset, msngset):
1032 haslst = hasset.keys()
1032 haslst = hasset.keys()
1033 haslst.sort(cmp_by_rev_func(revlog))
1033 haslst.sort(cmp_by_rev_func(revlog))
1034 for node in haslst:
1034 for node in haslst:
1035 parentlst = [p for p in revlog.parents(node) if p != nullid]
1035 parentlst = [p for p in revlog.parents(node) if p != nullid]
1036 while parentlst:
1036 while parentlst:
1037 n = parentlst.pop()
1037 n = parentlst.pop()
1038 if n not in hasset:
1038 if n not in hasset:
1039 hasset[n] = 1
1039 hasset[n] = 1
1040 p = [p for p in revlog.parents(n) if p != nullid]
1040 p = [p for p in revlog.parents(n) if p != nullid]
1041 parentlst.extend(p)
1041 parentlst.extend(p)
1042 for n in hasset:
1042 for n in hasset:
1043 msngset.pop(n, None)
1043 msngset.pop(n, None)
1044
1044
1045 # This is a function generating function used to set up an environment
1045 # This is a function generating function used to set up an environment
1046 # for the inner function to execute in.
1046 # for the inner function to execute in.
1047 def manifest_and_file_collector(changedfileset):
1047 def manifest_and_file_collector(changedfileset):
1048 # This is an information gathering function that gathers
1048 # This is an information gathering function that gathers
1049 # information from each changeset node that goes out as part of
1049 # information from each changeset node that goes out as part of
1050 # the changegroup. The information gathered is a list of which
1050 # the changegroup. The information gathered is a list of which
1051 # manifest nodes are potentially required (the recipient may
1051 # manifest nodes are potentially required (the recipient may
1052 # already have them) and total list of all files which were
1052 # already have them) and total list of all files which were
1053 # changed in any changeset in the changegroup.
1053 # changed in any changeset in the changegroup.
1054 #
1054 #
1055 # We also remember the first changenode we saw any manifest
1055 # We also remember the first changenode we saw any manifest
1056 # referenced by so we can later determine which changenode 'owns'
1056 # referenced by so we can later determine which changenode 'owns'
1057 # the manifest.
1057 # the manifest.
1058 def collect_manifests_and_files(clnode):
1058 def collect_manifests_and_files(clnode):
1059 c = cl.read(clnode)
1059 c = cl.read(clnode)
1060 for f in c[3]:
1060 for f in c[3]:
1061 # This is to make sure we only have one instance of each
1061 # This is to make sure we only have one instance of each
1062 # filename string for each filename.
1062 # filename string for each filename.
1063 changedfileset.setdefault(f, f)
1063 changedfileset.setdefault(f, f)
1064 msng_mnfst_set.setdefault(c[0], clnode)
1064 msng_mnfst_set.setdefault(c[0], clnode)
1065 return collect_manifests_and_files
1065 return collect_manifests_and_files
1066
1066
1067 # Figure out which manifest nodes (of the ones we think might be part
1067 # Figure out which manifest nodes (of the ones we think might be part
1068 # of the changegroup) the recipient must know about and remove them
1068 # of the changegroup) the recipient must know about and remove them
1069 # from the changegroup.
1069 # from the changegroup.
1070 def prune_manifests():
1070 def prune_manifests():
1071 has_mnfst_set = {}
1071 has_mnfst_set = {}
1072 for n in msng_mnfst_set:
1072 for n in msng_mnfst_set:
1073 # If a 'missing' manifest thinks it belongs to a changenode
1073 # If a 'missing' manifest thinks it belongs to a changenode
1074 # the recipient is assumed to have, obviously the recipient
1074 # the recipient is assumed to have, obviously the recipient
1075 # must have that manifest.
1075 # must have that manifest.
1076 linknode = cl.node(mnfst.linkrev(n))
1076 linknode = cl.node(mnfst.linkrev(n))
1077 if linknode in has_cl_set:
1077 if linknode in has_cl_set:
1078 has_mnfst_set[n] = 1
1078 has_mnfst_set[n] = 1
1079 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1079 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1080
1080
1081 # Use the information collected in collect_manifests_and_files to say
1081 # Use the information collected in collect_manifests_and_files to say
1082 # which changenode any manifestnode belongs to.
1082 # which changenode any manifestnode belongs to.
1083 def lookup_manifest_link(mnfstnode):
1083 def lookup_manifest_link(mnfstnode):
1084 return msng_mnfst_set[mnfstnode]
1084 return msng_mnfst_set[mnfstnode]
1085
1085
1086 # A function generating function that sets up the initial environment
1086 # A function generating function that sets up the initial environment
1087 # the inner function.
1087 # the inner function.
1088 def filenode_collector(changedfiles):
1088 def filenode_collector(changedfiles):
1089 next_rev = [0]
1089 next_rev = [0]
1090 # This gathers information from each manifestnode included in the
1090 # This gathers information from each manifestnode included in the
1091 # changegroup about which filenodes the manifest node references
1091 # changegroup about which filenodes the manifest node references
1092 # so we can include those in the changegroup too.
1092 # so we can include those in the changegroup too.
1093 #
1093 #
1094 # It also remembers which changenode each filenode belongs to. It
1094 # It also remembers which changenode each filenode belongs to. It
1095 # does this by assuming the a filenode belongs to the changenode
1095 # does this by assuming the a filenode belongs to the changenode
1096 # the first manifest that references it belongs to.
1096 # the first manifest that references it belongs to.
1097 def collect_msng_filenodes(mnfstnode):
1097 def collect_msng_filenodes(mnfstnode):
1098 r = mnfst.rev(mnfstnode)
1098 r = mnfst.rev(mnfstnode)
1099 if r == next_rev[0]:
1099 if r == next_rev[0]:
1100 # If the last rev we looked at was the one just previous,
1100 # If the last rev we looked at was the one just previous,
1101 # we only need to see a diff.
1101 # we only need to see a diff.
1102 delta = mdiff.patchtext(mnfst.delta(mnfstnode))
1102 delta = mdiff.patchtext(mnfst.delta(mnfstnode))
1103 # For each line in the delta
1103 # For each line in the delta
1104 for dline in delta.splitlines():
1104 for dline in delta.splitlines():
1105 # get the filename and filenode for that line
1105 # get the filename and filenode for that line
1106 f, fnode = dline.split('\0')
1106 f, fnode = dline.split('\0')
1107 fnode = bin(fnode[:40])
1107 fnode = bin(fnode[:40])
1108 f = changedfiles.get(f, None)
1108 f = changedfiles.get(f, None)
1109 # And if the file is in the list of files we care
1109 # And if the file is in the list of files we care
1110 # about.
1110 # about.
1111 if f is not None:
1111 if f is not None:
1112 # Get the changenode this manifest belongs to
1112 # Get the changenode this manifest belongs to
1113 clnode = msng_mnfst_set[mnfstnode]
1113 clnode = msng_mnfst_set[mnfstnode]
1114 # Create the set of filenodes for the file if
1114 # Create the set of filenodes for the file if
1115 # there isn't one already.
1115 # there isn't one already.
1116 ndset = msng_filenode_set.setdefault(f, {})
1116 ndset = msng_filenode_set.setdefault(f, {})
1117 # And set the filenode's changelog node to the
1117 # And set the filenode's changelog node to the
1118 # manifest's if it hasn't been set already.
1118 # manifest's if it hasn't been set already.
1119 ndset.setdefault(fnode, clnode)
1119 ndset.setdefault(fnode, clnode)
1120 else:
1120 else:
1121 # Otherwise we need a full manifest.
1121 # Otherwise we need a full manifest.
1122 m = mnfst.read(mnfstnode)
1122 m = mnfst.read(mnfstnode)
1123 # For every file in we care about.
1123 # For every file in we care about.
1124 for f in changedfiles:
1124 for f in changedfiles:
1125 fnode = m.get(f, None)
1125 fnode = m.get(f, None)
1126 # If it's in the manifest
1126 # If it's in the manifest
1127 if fnode is not None:
1127 if fnode is not None:
1128 # See comments above.
1128 # See comments above.
1129 clnode = msng_mnfst_set[mnfstnode]
1129 clnode = msng_mnfst_set[mnfstnode]
1130 ndset = msng_filenode_set.setdefault(f, {})
1130 ndset = msng_filenode_set.setdefault(f, {})
1131 ndset.setdefault(fnode, clnode)
1131 ndset.setdefault(fnode, clnode)
1132 # Remember the revision we hope to see next.
1132 # Remember the revision we hope to see next.
1133 next_rev[0] = r + 1
1133 next_rev[0] = r + 1
1134 return collect_msng_filenodes
1134 return collect_msng_filenodes
1135
1135
1136 # We have a list of filenodes we think we need for a file, lets remove
1136 # We have a list of filenodes we think we need for a file, lets remove
1137 # all those we now the recipient must have.
1137 # all those we now the recipient must have.
1138 def prune_filenodes(f, filerevlog):
1138 def prune_filenodes(f, filerevlog):
1139 msngset = msng_filenode_set[f]
1139 msngset = msng_filenode_set[f]
1140 hasset = {}
1140 hasset = {}
1141 # If a 'missing' filenode thinks it belongs to a changenode we
1141 # If a 'missing' filenode thinks it belongs to a changenode we
1142 # assume the recipient must have, then the recipient must have
1142 # assume the recipient must have, then the recipient must have
1143 # that filenode.
1143 # that filenode.
1144 for n in msngset:
1144 for n in msngset:
1145 clnode = cl.node(filerevlog.linkrev(n))
1145 clnode = cl.node(filerevlog.linkrev(n))
1146 if clnode in has_cl_set:
1146 if clnode in has_cl_set:
1147 hasset[n] = 1
1147 hasset[n] = 1
1148 prune_parents(filerevlog, hasset, msngset)
1148 prune_parents(filerevlog, hasset, msngset)
1149
1149
1150 # A function generator function that sets up the a context for the
1150 # A function generator function that sets up the a context for the
1151 # inner function.
1151 # inner function.
1152 def lookup_filenode_link_func(fname):
1152 def lookup_filenode_link_func(fname):
1153 msngset = msng_filenode_set[fname]
1153 msngset = msng_filenode_set[fname]
1154 # Lookup the changenode the filenode belongs to.
1154 # Lookup the changenode the filenode belongs to.
1155 def lookup_filenode_link(fnode):
1155 def lookup_filenode_link(fnode):
1156 return msngset[fnode]
1156 return msngset[fnode]
1157 return lookup_filenode_link
1157 return lookup_filenode_link
1158
1158
1159 # Now that we have all theses utility functions to help out and
1159 # Now that we have all theses utility functions to help out and
1160 # logically divide up the task, generate the group.
1160 # logically divide up the task, generate the group.
1161 def gengroup():
1161 def gengroup():
1162 # The set of changed files starts empty.
1162 # The set of changed files starts empty.
1163 changedfiles = {}
1163 changedfiles = {}
1164 # Create a changenode group generator that will call our functions
1164 # Create a changenode group generator that will call our functions
1165 # back to lookup the owning changenode and collect information.
1165 # back to lookup the owning changenode and collect information.
1166 group = cl.group(msng_cl_lst, identity,
1166 group = cl.group(msng_cl_lst, identity,
1167 manifest_and_file_collector(changedfiles))
1167 manifest_and_file_collector(changedfiles))
1168 for chnk in group:
1168 for chnk in group:
1169 yield chnk
1169 yield chnk
1170
1170
1171 # The list of manifests has been collected by the generator
1171 # The list of manifests has been collected by the generator
1172 # calling our functions back.
1172 # calling our functions back.
1173 prune_manifests()
1173 prune_manifests()
1174 msng_mnfst_lst = msng_mnfst_set.keys()
1174 msng_mnfst_lst = msng_mnfst_set.keys()
1175 # Sort the manifestnodes by revision number.
1175 # Sort the manifestnodes by revision number.
1176 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1176 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1177 # Create a generator for the manifestnodes that calls our lookup
1177 # Create a generator for the manifestnodes that calls our lookup
1178 # and data collection functions back.
1178 # and data collection functions back.
1179 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1179 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1180 filenode_collector(changedfiles))
1180 filenode_collector(changedfiles))
1181 for chnk in group:
1181 for chnk in group:
1182 yield chnk
1182 yield chnk
1183
1183
1184 # These are no longer needed, dereference and toss the memory for
1184 # These are no longer needed, dereference and toss the memory for
1185 # them.
1185 # them.
1186 msng_mnfst_lst = None
1186 msng_mnfst_lst = None
1187 msng_mnfst_set.clear()
1187 msng_mnfst_set.clear()
1188
1188
1189 changedfiles = changedfiles.keys()
1189 changedfiles = changedfiles.keys()
1190 changedfiles.sort()
1190 changedfiles.sort()
1191 # Go through all our files in order sorted by name.
1191 # Go through all our files in order sorted by name.
1192 for fname in changedfiles:
1192 for fname in changedfiles:
1193 filerevlog = self.file(fname)
1193 filerevlog = self.file(fname)
1194 # Toss out the filenodes that the recipient isn't really
1194 # Toss out the filenodes that the recipient isn't really
1195 # missing.
1195 # missing.
1196 prune_filenodes(fname, filerevlog)
1196 prune_filenodes(fname, filerevlog)
1197 msng_filenode_lst = msng_filenode_set[fname].keys()
1197 msng_filenode_lst = msng_filenode_set[fname].keys()
1198 # If any filenodes are left, generate the group for them,
1198 # If any filenodes are left, generate the group for them,
1199 # otherwise don't bother.
1199 # otherwise don't bother.
1200 if len(msng_filenode_lst) > 0:
1200 if len(msng_filenode_lst) > 0:
1201 yield struct.pack(">l", len(fname) + 4) + fname
1201 yield struct.pack(">l", len(fname) + 4) + fname
1202 # Sort the filenodes by their revision #
1202 # Sort the filenodes by their revision #
1203 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1203 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1204 # Create a group generator and only pass in a changenode
1204 # Create a group generator and only pass in a changenode
1205 # lookup function as we need to collect no information
1205 # lookup function as we need to collect no information
1206 # from filenodes.
1206 # from filenodes.
1207 group = filerevlog.group(msng_filenode_lst,
1207 group = filerevlog.group(msng_filenode_lst,
1208 lookup_filenode_link_func(fname))
1208 lookup_filenode_link_func(fname))
1209 for chnk in group:
1209 for chnk in group:
1210 yield chnk
1210 yield chnk
1211 # Don't need this anymore, toss it to free memory.
1211 # Don't need this anymore, toss it to free memory.
1212 del msng_filenode_set[fname]
1212 del msng_filenode_set[fname]
1213 # Signal that no more groups are left.
1213 # Signal that no more groups are left.
1214 yield struct.pack(">l", 0)
1214 yield struct.pack(">l", 0)
1215
1215
1216 return util.chunkbuffer(gengroup())
1216 return util.chunkbuffer(gengroup())
1217
1217
1218 def changegroup(self, basenodes):
1218 def changegroup(self, basenodes):
1219 """Generate a changegroup of all nodes that we have that a recipient
1219 """Generate a changegroup of all nodes that we have that a recipient
1220 doesn't.
1220 doesn't.
1221
1221
1222 This is much easier than the previous function as we can assume that
1222 This is much easier than the previous function as we can assume that
1223 the recipient has any changenode we aren't sending them."""
1223 the recipient has any changenode we aren't sending them."""
1224 cl = self.changelog
1224 cl = self.changelog
1225 nodes = cl.nodesbetween(basenodes, None)[0]
1225 nodes = cl.nodesbetween(basenodes, None)[0]
1226 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1226 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1227
1227
1228 def identity(x):
1228 def identity(x):
1229 return x
1229 return x
1230
1230
1231 def gennodelst(revlog):
1231 def gennodelst(revlog):
1232 for r in xrange(0, revlog.count()):
1232 for r in xrange(0, revlog.count()):
1233 n = revlog.node(r)
1233 n = revlog.node(r)
1234 if revlog.linkrev(n) in revset:
1234 if revlog.linkrev(n) in revset:
1235 yield n
1235 yield n
1236
1236
1237 def changed_file_collector(changedfileset):
1237 def changed_file_collector(changedfileset):
1238 def collect_changed_files(clnode):
1238 def collect_changed_files(clnode):
1239 c = cl.read(clnode)
1239 c = cl.read(clnode)
1240 for fname in c[3]:
1240 for fname in c[3]:
1241 changedfileset[fname] = 1
1241 changedfileset[fname] = 1
1242 return collect_changed_files
1242 return collect_changed_files
1243
1243
1244 def lookuprevlink_func(revlog):
1244 def lookuprevlink_func(revlog):
1245 def lookuprevlink(n):
1245 def lookuprevlink(n):
1246 return cl.node(revlog.linkrev(n))
1246 return cl.node(revlog.linkrev(n))
1247 return lookuprevlink
1247 return lookuprevlink
1248
1248
1249 def gengroup():
1249 def gengroup():
1250 # construct a list of all changed files
1250 # construct a list of all changed files
1251 changedfiles = {}
1251 changedfiles = {}
1252
1252
1253 for chnk in cl.group(nodes, identity,
1253 for chnk in cl.group(nodes, identity,
1254 changed_file_collector(changedfiles)):
1254 changed_file_collector(changedfiles)):
1255 yield chnk
1255 yield chnk
1256 changedfiles = changedfiles.keys()
1256 changedfiles = changedfiles.keys()
1257 changedfiles.sort()
1257 changedfiles.sort()
1258
1258
1259 mnfst = self.manifest
1259 mnfst = self.manifest
1260 nodeiter = gennodelst(mnfst)
1260 nodeiter = gennodelst(mnfst)
1261 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1261 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1262 yield chnk
1262 yield chnk
1263
1263
1264 for fname in changedfiles:
1264 for fname in changedfiles:
1265 filerevlog = self.file(fname)
1265 filerevlog = self.file(fname)
1266 nodeiter = gennodelst(filerevlog)
1266 nodeiter = gennodelst(filerevlog)
1267 nodeiter = list(nodeiter)
1267 nodeiter = list(nodeiter)
1268 if nodeiter:
1268 if nodeiter:
1269 yield struct.pack(">l", len(fname) + 4) + fname
1269 yield struct.pack(">l", len(fname) + 4) + fname
1270 lookup = lookuprevlink_func(filerevlog)
1270 lookup = lookuprevlink_func(filerevlog)
1271 for chnk in filerevlog.group(nodeiter, lookup):
1271 for chnk in filerevlog.group(nodeiter, lookup):
1272 yield chnk
1272 yield chnk
1273
1273
1274 yield struct.pack(">l", 0)
1274 yield struct.pack(">l", 0)
1275
1275
1276 return util.chunkbuffer(gengroup())
1276 return util.chunkbuffer(gengroup())
1277
1277
1278 def addchangegroup(self, source):
1278 def addchangegroup(self, source):
1279
1279
1280 def getchunk():
1280 def getchunk():
1281 d = source.read(4)
1281 d = source.read(4)
1282 if not d: return ""
1282 if not d: return ""
1283 l = struct.unpack(">l", d)[0]
1283 l = struct.unpack(">l", d)[0]
1284 if l <= 4: return ""
1284 if l <= 4: return ""
1285 d = source.read(l - 4)
1285 d = source.read(l - 4)
1286 if len(d) < l - 4:
1286 if len(d) < l - 4:
1287 raise repo.RepoError(_("premature EOF reading chunk"
1287 raise repo.RepoError(_("premature EOF reading chunk"
1288 " (got %d bytes, expected %d)")
1288 " (got %d bytes, expected %d)")
1289 % (len(d), l - 4))
1289 % (len(d), l - 4))
1290 return d
1290 return d
1291
1291
1292 def getgroup():
1292 def getgroup():
1293 while 1:
1293 while 1:
1294 c = getchunk()
1294 c = getchunk()
1295 if not c: break
1295 if not c: break
1296 yield c
1296 yield c
1297
1297
1298 def csmap(x):
1298 def csmap(x):
1299 self.ui.debug(_("add changeset %s\n") % short(x))
1299 self.ui.debug(_("add changeset %s\n") % short(x))
1300 return self.changelog.count()
1300 return self.changelog.count()
1301
1301
1302 def revmap(x):
1302 def revmap(x):
1303 return self.changelog.rev(x)
1303 return self.changelog.rev(x)
1304
1304
1305 if not source: return
1305 if not source: return
1306 changesets = files = revisions = 0
1306 changesets = files = revisions = 0
1307
1307
1308 tr = self.transaction()
1308 tr = self.transaction()
1309
1309
1310 oldheads = len(self.changelog.heads())
1310 oldheads = len(self.changelog.heads())
1311
1311
1312 # pull off the changeset group
1312 # pull off the changeset group
1313 self.ui.status(_("adding changesets\n"))
1313 self.ui.status(_("adding changesets\n"))
1314 co = self.changelog.tip()
1314 co = self.changelog.tip()
1315 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
1315 cn = self.changelog.addgroup(getgroup(), csmap, tr, 1) # unique
1316 cnr, cor = map(self.changelog.rev, (cn, co))
1316 cnr, cor = map(self.changelog.rev, (cn, co))
1317 if cn == nullid:
1317 if cn == nullid:
1318 cnr = cor
1318 cnr = cor
1319 changesets = cnr - cor
1319 changesets = cnr - cor
1320
1320
1321 # pull off the manifest group
1321 # pull off the manifest group
1322 self.ui.status(_("adding manifests\n"))
1322 self.ui.status(_("adding manifests\n"))
1323 mm = self.manifest.tip()
1323 mm = self.manifest.tip()
1324 mo = self.manifest.addgroup(getgroup(), revmap, tr)
1324 mo = self.manifest.addgroup(getgroup(), revmap, tr)
1325
1325
1326 # process the files
1326 # process the files
1327 self.ui.status(_("adding file changes\n"))
1327 self.ui.status(_("adding file changes\n"))
1328 while 1:
1328 while 1:
1329 f = getchunk()
1329 f = getchunk()
1330 if not f: break
1330 if not f: break
1331 self.ui.debug(_("adding %s revisions\n") % f)
1331 self.ui.debug(_("adding %s revisions\n") % f)
1332 fl = self.file(f)
1332 fl = self.file(f)
1333 o = fl.count()
1333 o = fl.count()
1334 n = fl.addgroup(getgroup(), revmap, tr)
1334 n = fl.addgroup(getgroup(), revmap, tr)
1335 revisions += fl.count() - o
1335 revisions += fl.count() - o
1336 files += 1
1336 files += 1
1337
1337
1338 newheads = len(self.changelog.heads())
1338 newheads = len(self.changelog.heads())
1339 heads = ""
1339 heads = ""
1340 if oldheads and newheads > oldheads:
1340 if oldheads and newheads > oldheads:
1341 heads = _(" (+%d heads)") % (newheads - oldheads)
1341 heads = _(" (+%d heads)") % (newheads - oldheads)
1342
1342
1343 self.ui.status(_("added %d changesets"
1343 self.ui.status(_("added %d changesets"
1344 " with %d changes to %d files%s\n")
1344 " with %d changes to %d files%s\n")
1345 % (changesets, revisions, files, heads))
1345 % (changesets, revisions, files, heads))
1346
1346
1347 tr.close()
1347 tr.close()
1348
1348
1349 if changesets > 0:
1349 if changesets > 0:
1350 if not self.hook("changegroup",
1350 if not self.hook("changegroup",
1351 node=hex(self.changelog.node(cor+1))):
1351 node=hex(self.changelog.node(cor+1))):
1352 self.ui.warn(_("abort: changegroup hook returned failure!\n"))
1352 self.ui.warn(_("abort: changegroup hook returned failure!\n"))
1353 return 1
1353 return 1
1354
1354
1355 for i in range(cor + 1, cnr + 1):
1355 for i in range(cor + 1, cnr + 1):
1356 self.hook("commit", node=hex(self.changelog.node(i)))
1356 self.hook("commit", node=hex(self.changelog.node(i)))
1357
1357
1358 return
1358 return
1359
1359
1360 def update(self, node, allow=False, force=False, choose=None,
1360 def update(self, node, allow=False, force=False, choose=None,
1361 moddirstate=True):
1361 moddirstate=True, forcemerge=False):
1362 pl = self.dirstate.parents()
1362 pl = self.dirstate.parents()
1363 if not force and pl[1] != nullid:
1363 if not force and pl[1] != nullid:
1364 self.ui.warn(_("aborting: outstanding uncommitted merges\n"))
1364 self.ui.warn(_("aborting: outstanding uncommitted merges\n"))
1365 return 1
1365 return 1
1366
1366
1367 p1, p2 = pl[0], node
1367 p1, p2 = pl[0], node
1368 pa = self.changelog.ancestor(p1, p2)
1368 pa = self.changelog.ancestor(p1, p2)
1369 m1n = self.changelog.read(p1)[0]
1369 m1n = self.changelog.read(p1)[0]
1370 m2n = self.changelog.read(p2)[0]
1370 m2n = self.changelog.read(p2)[0]
1371 man = self.manifest.ancestor(m1n, m2n)
1371 man = self.manifest.ancestor(m1n, m2n)
1372 m1 = self.manifest.read(m1n)
1372 m1 = self.manifest.read(m1n)
1373 mf1 = self.manifest.readflags(m1n)
1373 mf1 = self.manifest.readflags(m1n)
1374 m2 = self.manifest.read(m2n)
1374 m2 = self.manifest.read(m2n)
1375 mf2 = self.manifest.readflags(m2n)
1375 mf2 = self.manifest.readflags(m2n)
1376 ma = self.manifest.read(man)
1376 ma = self.manifest.read(man)
1377 mfa = self.manifest.readflags(man)
1377 mfa = self.manifest.readflags(man)
1378
1378
1379 (c, a, d, u) = self.changes()
1379 (c, a, d, u) = self.changes()
1380
1380
1381 if allow and not forcemerge:
1382 if c or a or d:
1383 raise util.Abort(_("outstanding uncommited changes"))
1384 if not forcemerge and not force:
1385 for f in u:
1386 if f in m2:
1387 t1 = self.wread(f)
1388 t2 = self.file(f).read(m2[f])
1389 if cmp(t1, t2) != 0:
1390 raise util.Abort(_("'%s' already exists in the working"
1391 " dir and differs from remote") % f)
1392
1381 # is this a jump, or a merge? i.e. is there a linear path
1393 # is this a jump, or a merge? i.e. is there a linear path
1382 # from p1 to p2?
1394 # from p1 to p2?
1383 linear_path = (pa == p1 or pa == p2)
1395 linear_path = (pa == p1 or pa == p2)
1384
1396
1385 # resolve the manifest to determine which files
1397 # resolve the manifest to determine which files
1386 # we care about merging
1398 # we care about merging
1387 self.ui.note(_("resolving manifests\n"))
1399 self.ui.note(_("resolving manifests\n"))
1388 self.ui.debug(_(" force %s allow %s moddirstate %s linear %s\n") %
1400 self.ui.debug(_(" force %s allow %s moddirstate %s linear %s\n") %
1389 (force, allow, moddirstate, linear_path))
1401 (force, allow, moddirstate, linear_path))
1390 self.ui.debug(_(" ancestor %s local %s remote %s\n") %
1402 self.ui.debug(_(" ancestor %s local %s remote %s\n") %
1391 (short(man), short(m1n), short(m2n)))
1403 (short(man), short(m1n), short(m2n)))
1392
1404
1393 merge = {}
1405 merge = {}
1394 get = {}
1406 get = {}
1395 remove = []
1407 remove = []
1396
1408
1397 # construct a working dir manifest
1409 # construct a working dir manifest
1398 mw = m1.copy()
1410 mw = m1.copy()
1399 mfw = mf1.copy()
1411 mfw = mf1.copy()
1400 umap = dict.fromkeys(u)
1412 umap = dict.fromkeys(u)
1401
1413
1402 for f in a + c + u:
1414 for f in a + c + u:
1403 mw[f] = ""
1415 mw[f] = ""
1404 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1416 mfw[f] = util.is_exec(self.wjoin(f), mfw.get(f, False))
1405
1417
1406 if moddirstate:
1418 if moddirstate:
1407 wlock = self.wlock()
1419 wlock = self.wlock()
1408
1420
1409 for f in d:
1421 for f in d:
1410 if f in mw: del mw[f]
1422 if f in mw: del mw[f]
1411
1423
1412 # If we're jumping between revisions (as opposed to merging),
1424 # If we're jumping between revisions (as opposed to merging),
1413 # and if neither the working directory nor the target rev has
1425 # and if neither the working directory nor the target rev has
1414 # the file, then we need to remove it from the dirstate, to
1426 # the file, then we need to remove it from the dirstate, to
1415 # prevent the dirstate from listing the file when it is no
1427 # prevent the dirstate from listing the file when it is no
1416 # longer in the manifest.
1428 # longer in the manifest.
1417 if moddirstate and linear_path and f not in m2:
1429 if moddirstate and linear_path and f not in m2:
1418 self.dirstate.forget((f,))
1430 self.dirstate.forget((f,))
1419
1431
1420 # Compare manifests
1432 # Compare manifests
1421 for f, n in mw.iteritems():
1433 for f, n in mw.iteritems():
1422 if choose and not choose(f): continue
1434 if choose and not choose(f): continue
1423 if f in m2:
1435 if f in m2:
1424 s = 0
1436 s = 0
1425
1437
1426 # is the wfile new since m1, and match m2?
1438 # is the wfile new since m1, and match m2?
1427 if f not in m1:
1439 if f not in m1:
1428 t1 = self.wread(f)
1440 t1 = self.wread(f)
1429 t2 = self.file(f).read(m2[f])
1441 t2 = self.file(f).read(m2[f])
1430 if cmp(t1, t2) == 0:
1442 if cmp(t1, t2) == 0:
1431 n = m2[f]
1443 n = m2[f]
1432 del t1, t2
1444 del t1, t2
1433
1445
1434 # are files different?
1446 # are files different?
1435 if n != m2[f]:
1447 if n != m2[f]:
1436 a = ma.get(f, nullid)
1448 a = ma.get(f, nullid)
1437 # are both different from the ancestor?
1449 # are both different from the ancestor?
1438 if n != a and m2[f] != a:
1450 if n != a and m2[f] != a:
1439 self.ui.debug(_(" %s versions differ, resolve\n") % f)
1451 self.ui.debug(_(" %s versions differ, resolve\n") % f)
1440 # merge executable bits
1452 # merge executable bits
1441 # "if we changed or they changed, change in merge"
1453 # "if we changed or they changed, change in merge"
1442 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1454 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1443 mode = ((a^b) | (a^c)) ^ a
1455 mode = ((a^b) | (a^c)) ^ a
1444 merge[f] = (m1.get(f, nullid), m2[f], mode)
1456 merge[f] = (m1.get(f, nullid), m2[f], mode)
1445 s = 1
1457 s = 1
1446 # are we clobbering?
1458 # are we clobbering?
1447 # is remote's version newer?
1459 # is remote's version newer?
1448 # or are we going back in time?
1460 # or are we going back in time?
1449 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1461 elif force or m2[f] != a or (p2 == pa and mw[f] == m1[f]):
1450 self.ui.debug(_(" remote %s is newer, get\n") % f)
1462 self.ui.debug(_(" remote %s is newer, get\n") % f)
1451 get[f] = m2[f]
1463 get[f] = m2[f]
1452 s = 1
1464 s = 1
1453 elif f in umap:
1465 elif f in umap:
1454 # this unknown file is the same as the checkout
1466 # this unknown file is the same as the checkout
1455 get[f] = m2[f]
1467 get[f] = m2[f]
1456
1468
1457 if not s and mfw[f] != mf2[f]:
1469 if not s and mfw[f] != mf2[f]:
1458 if force:
1470 if force:
1459 self.ui.debug(_(" updating permissions for %s\n") % f)
1471 self.ui.debug(_(" updating permissions for %s\n") % f)
1460 util.set_exec(self.wjoin(f), mf2[f])
1472 util.set_exec(self.wjoin(f), mf2[f])
1461 else:
1473 else:
1462 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1474 a, b, c = mfa.get(f, 0), mfw[f], mf2[f]
1463 mode = ((a^b) | (a^c)) ^ a
1475 mode = ((a^b) | (a^c)) ^ a
1464 if mode != b:
1476 if mode != b:
1465 self.ui.debug(_(" updating permissions for %s\n") % f)
1477 self.ui.debug(_(" updating permissions for %s\n") % f)
1466 util.set_exec(self.wjoin(f), mode)
1478 util.set_exec(self.wjoin(f), mode)
1467 del m2[f]
1479 del m2[f]
1468 elif f in ma:
1480 elif f in ma:
1469 if n != ma[f]:
1481 if n != ma[f]:
1470 r = _("d")
1482 r = _("d")
1471 if not force and (linear_path or allow):
1483 if not force and (linear_path or allow):
1472 r = self.ui.prompt(
1484 r = self.ui.prompt(
1473 (_(" local changed %s which remote deleted\n") % f) +
1485 (_(" local changed %s which remote deleted\n") % f) +
1474 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1486 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1475 if r == _("d"):
1487 if r == _("d"):
1476 remove.append(f)
1488 remove.append(f)
1477 else:
1489 else:
1478 self.ui.debug(_("other deleted %s\n") % f)
1490 self.ui.debug(_("other deleted %s\n") % f)
1479 remove.append(f) # other deleted it
1491 remove.append(f) # other deleted it
1480 else:
1492 else:
1481 # file is created on branch or in working directory
1493 # file is created on branch or in working directory
1482 if force and f not in umap:
1494 if force and f not in umap:
1483 self.ui.debug(_("remote deleted %s, clobbering\n") % f)
1495 self.ui.debug(_("remote deleted %s, clobbering\n") % f)
1484 remove.append(f)
1496 remove.append(f)
1485 elif n == m1.get(f, nullid): # same as parent
1497 elif n == m1.get(f, nullid): # same as parent
1486 if p2 == pa: # going backwards?
1498 if p2 == pa: # going backwards?
1487 self.ui.debug(_("remote deleted %s\n") % f)
1499 self.ui.debug(_("remote deleted %s\n") % f)
1488 remove.append(f)
1500 remove.append(f)
1489 else:
1501 else:
1490 self.ui.debug(_("local modified %s, keeping\n") % f)
1502 self.ui.debug(_("local modified %s, keeping\n") % f)
1491 else:
1503 else:
1492 self.ui.debug(_("working dir created %s, keeping\n") % f)
1504 self.ui.debug(_("working dir created %s, keeping\n") % f)
1493
1505
1494 for f, n in m2.iteritems():
1506 for f, n in m2.iteritems():
1495 if choose and not choose(f): continue
1507 if choose and not choose(f): continue
1496 if f[0] == "/": continue
1508 if f[0] == "/": continue
1497 if f in ma and n != ma[f]:
1509 if f in ma and n != ma[f]:
1498 r = _("k")
1510 r = _("k")
1499 if not force and (linear_path or allow):
1511 if not force and (linear_path or allow):
1500 r = self.ui.prompt(
1512 r = self.ui.prompt(
1501 (_("remote changed %s which local deleted\n") % f) +
1513 (_("remote changed %s which local deleted\n") % f) +
1502 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1514 _("(k)eep or (d)elete?"), _("[kd]"), _("k"))
1503 if r == _("k"): get[f] = n
1515 if r == _("k"): get[f] = n
1504 elif f not in ma:
1516 elif f not in ma:
1505 self.ui.debug(_("remote created %s\n") % f)
1517 self.ui.debug(_("remote created %s\n") % f)
1506 get[f] = n
1518 get[f] = n
1507 else:
1519 else:
1508 if force or p2 == pa: # going backwards?
1520 if force or p2 == pa: # going backwards?
1509 self.ui.debug(_("local deleted %s, recreating\n") % f)
1521 self.ui.debug(_("local deleted %s, recreating\n") % f)
1510 get[f] = n
1522 get[f] = n
1511 else:
1523 else:
1512 self.ui.debug(_("local deleted %s\n") % f)
1524 self.ui.debug(_("local deleted %s\n") % f)
1513
1525
1514 del mw, m1, m2, ma
1526 del mw, m1, m2, ma
1515
1527
1516 if force:
1528 if force:
1517 for f in merge:
1529 for f in merge:
1518 get[f] = merge[f][1]
1530 get[f] = merge[f][1]
1519 merge = {}
1531 merge = {}
1520
1532
1521 if linear_path or force:
1533 if linear_path or force:
1522 # we don't need to do any magic, just jump to the new rev
1534 # we don't need to do any magic, just jump to the new rev
1523 branch_merge = False
1535 branch_merge = False
1524 p1, p2 = p2, nullid
1536 p1, p2 = p2, nullid
1525 else:
1537 else:
1526 if not allow:
1538 if not allow:
1527 self.ui.status(_("this update spans a branch"
1539 self.ui.status(_("this update spans a branch"
1528 " affecting the following files:\n"))
1540 " affecting the following files:\n"))
1529 fl = merge.keys() + get.keys()
1541 fl = merge.keys() + get.keys()
1530 fl.sort()
1542 fl.sort()
1531 for f in fl:
1543 for f in fl:
1532 cf = ""
1544 cf = ""
1533 if f in merge: cf = _(" (resolve)")
1545 if f in merge: cf = _(" (resolve)")
1534 self.ui.status(" %s%s\n" % (f, cf))
1546 self.ui.status(" %s%s\n" % (f, cf))
1535 self.ui.warn(_("aborting update spanning branches!\n"))
1547 self.ui.warn(_("aborting update spanning branches!\n"))
1536 self.ui.status(_("(use update -m to merge across branches"
1548 self.ui.status(_("(use update -m to merge across branches"
1537 " or -C to lose changes)\n"))
1549 " or -C to lose changes)\n"))
1538 return 1
1550 return 1
1539 branch_merge = True
1551 branch_merge = True
1540
1552
1541 # get the files we don't need to change
1553 # get the files we don't need to change
1542 files = get.keys()
1554 files = get.keys()
1543 files.sort()
1555 files.sort()
1544 for f in files:
1556 for f in files:
1545 if f[0] == "/": continue
1557 if f[0] == "/": continue
1546 self.ui.note(_("getting %s\n") % f)
1558 self.ui.note(_("getting %s\n") % f)
1547 t = self.file(f).read(get[f])
1559 t = self.file(f).read(get[f])
1548 self.wwrite(f, t)
1560 self.wwrite(f, t)
1549 util.set_exec(self.wjoin(f), mf2[f])
1561 util.set_exec(self.wjoin(f), mf2[f])
1550 if moddirstate:
1562 if moddirstate:
1551 if branch_merge:
1563 if branch_merge:
1552 self.dirstate.update([f], 'n', st_mtime=-1)
1564 self.dirstate.update([f], 'n', st_mtime=-1)
1553 else:
1565 else:
1554 self.dirstate.update([f], 'n')
1566 self.dirstate.update([f], 'n')
1555
1567
1556 # merge the tricky bits
1568 # merge the tricky bits
1557 files = merge.keys()
1569 files = merge.keys()
1558 files.sort()
1570 files.sort()
1559 for f in files:
1571 for f in files:
1560 self.ui.status(_("merging %s\n") % f)
1572 self.ui.status(_("merging %s\n") % f)
1561 my, other, flag = merge[f]
1573 my, other, flag = merge[f]
1562 self.merge3(f, my, other)
1574 self.merge3(f, my, other)
1563 util.set_exec(self.wjoin(f), flag)
1575 util.set_exec(self.wjoin(f), flag)
1564 if moddirstate:
1576 if moddirstate:
1565 if branch_merge:
1577 if branch_merge:
1566 # We've done a branch merge, mark this file as merged
1578 # We've done a branch merge, mark this file as merged
1567 # so that we properly record the merger later
1579 # so that we properly record the merger later
1568 self.dirstate.update([f], 'm')
1580 self.dirstate.update([f], 'm')
1569 else:
1581 else:
1570 # We've update-merged a locally modified file, so
1582 # We've update-merged a locally modified file, so
1571 # we set the dirstate to emulate a normal checkout
1583 # we set the dirstate to emulate a normal checkout
1572 # of that file some time in the past. Thus our
1584 # of that file some time in the past. Thus our
1573 # merge will appear as a normal local file
1585 # merge will appear as a normal local file
1574 # modification.
1586 # modification.
1575 f_len = len(self.file(f).read(other))
1587 f_len = len(self.file(f).read(other))
1576 self.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
1588 self.dirstate.update([f], 'n', st_size=f_len, st_mtime=-1)
1577
1589
1578 remove.sort()
1590 remove.sort()
1579 for f in remove:
1591 for f in remove:
1580 self.ui.note(_("removing %s\n") % f)
1592 self.ui.note(_("removing %s\n") % f)
1581 try:
1593 try:
1582 util.unlink(self.wjoin(f))
1594 util.unlink(self.wjoin(f))
1583 except OSError, inst:
1595 except OSError, inst:
1584 if inst.errno != errno.ENOENT:
1596 if inst.errno != errno.ENOENT:
1585 self.ui.warn(_("update failed to remove %s: %s!\n") %
1597 self.ui.warn(_("update failed to remove %s: %s!\n") %
1586 (f, inst.strerror))
1598 (f, inst.strerror))
1587 if moddirstate:
1599 if moddirstate:
1588 if branch_merge:
1600 if branch_merge:
1589 self.dirstate.update(remove, 'r')
1601 self.dirstate.update(remove, 'r')
1590 else:
1602 else:
1591 self.dirstate.forget(remove)
1603 self.dirstate.forget(remove)
1592
1604
1593 if moddirstate:
1605 if moddirstate:
1594 self.dirstate.setparents(p1, p2)
1606 self.dirstate.setparents(p1, p2)
1595
1607
1596 def merge3(self, fn, my, other):
1608 def merge3(self, fn, my, other):
1597 """perform a 3-way merge in the working directory"""
1609 """perform a 3-way merge in the working directory"""
1598
1610
1599 def temp(prefix, node):
1611 def temp(prefix, node):
1600 pre = "%s~%s." % (os.path.basename(fn), prefix)
1612 pre = "%s~%s." % (os.path.basename(fn), prefix)
1601 (fd, name) = tempfile.mkstemp("", pre)
1613 (fd, name) = tempfile.mkstemp("", pre)
1602 f = os.fdopen(fd, "wb")
1614 f = os.fdopen(fd, "wb")
1603 self.wwrite(fn, fl.read(node), f)
1615 self.wwrite(fn, fl.read(node), f)
1604 f.close()
1616 f.close()
1605 return name
1617 return name
1606
1618
1607 fl = self.file(fn)
1619 fl = self.file(fn)
1608 base = fl.ancestor(my, other)
1620 base = fl.ancestor(my, other)
1609 a = self.wjoin(fn)
1621 a = self.wjoin(fn)
1610 b = temp("base", base)
1622 b = temp("base", base)
1611 c = temp("other", other)
1623 c = temp("other", other)
1612
1624
1613 self.ui.note(_("resolving %s\n") % fn)
1625 self.ui.note(_("resolving %s\n") % fn)
1614 self.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
1626 self.ui.debug(_("file %s: my %s other %s ancestor %s\n") %
1615 (fn, short(my), short(other), short(base)))
1627 (fn, short(my), short(other), short(base)))
1616
1628
1617 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1629 cmd = (os.environ.get("HGMERGE") or self.ui.config("ui", "merge")
1618 or "hgmerge")
1630 or "hgmerge")
1619 r = os.system('%s "%s" "%s" "%s"' % (cmd, a, b, c))
1631 r = os.system('%s "%s" "%s" "%s"' % (cmd, a, b, c))
1620 if r:
1632 if r:
1621 self.ui.warn(_("merging %s failed!\n") % fn)
1633 self.ui.warn(_("merging %s failed!\n") % fn)
1622
1634
1623 os.unlink(b)
1635 os.unlink(b)
1624 os.unlink(c)
1636 os.unlink(c)
1625
1637
1626 def verify(self):
1638 def verify(self):
1627 filelinkrevs = {}
1639 filelinkrevs = {}
1628 filenodes = {}
1640 filenodes = {}
1629 changesets = revisions = files = 0
1641 changesets = revisions = files = 0
1630 errors = [0]
1642 errors = [0]
1631 neededmanifests = {}
1643 neededmanifests = {}
1632
1644
1633 def err(msg):
1645 def err(msg):
1634 self.ui.warn(msg + "\n")
1646 self.ui.warn(msg + "\n")
1635 errors[0] += 1
1647 errors[0] += 1
1636
1648
1637 seen = {}
1649 seen = {}
1638 self.ui.status(_("checking changesets\n"))
1650 self.ui.status(_("checking changesets\n"))
1639 d = self.changelog.checksize()
1651 d = self.changelog.checksize()
1640 if d:
1652 if d:
1641 err(_("changeset data short %d bytes") % d)
1653 err(_("changeset data short %d bytes") % d)
1642 for i in range(self.changelog.count()):
1654 for i in range(self.changelog.count()):
1643 changesets += 1
1655 changesets += 1
1644 n = self.changelog.node(i)
1656 n = self.changelog.node(i)
1645 l = self.changelog.linkrev(n)
1657 l = self.changelog.linkrev(n)
1646 if l != i:
1658 if l != i:
1647 err(_("incorrect link (%d) for changeset revision %d") %(l, i))
1659 err(_("incorrect link (%d) for changeset revision %d") %(l, i))
1648 if n in seen:
1660 if n in seen:
1649 err(_("duplicate changeset at revision %d") % i)
1661 err(_("duplicate changeset at revision %d") % i)
1650 seen[n] = 1
1662 seen[n] = 1
1651
1663
1652 for p in self.changelog.parents(n):
1664 for p in self.changelog.parents(n):
1653 if p not in self.changelog.nodemap:
1665 if p not in self.changelog.nodemap:
1654 err(_("changeset %s has unknown parent %s") %
1666 err(_("changeset %s has unknown parent %s") %
1655 (short(n), short(p)))
1667 (short(n), short(p)))
1656 try:
1668 try:
1657 changes = self.changelog.read(n)
1669 changes = self.changelog.read(n)
1658 except KeyboardInterrupt:
1670 except KeyboardInterrupt:
1659 self.ui.warn(_("interrupted"))
1671 self.ui.warn(_("interrupted"))
1660 raise
1672 raise
1661 except Exception, inst:
1673 except Exception, inst:
1662 err(_("unpacking changeset %s: %s") % (short(n), inst))
1674 err(_("unpacking changeset %s: %s") % (short(n), inst))
1663
1675
1664 neededmanifests[changes[0]] = n
1676 neededmanifests[changes[0]] = n
1665
1677
1666 for f in changes[3]:
1678 for f in changes[3]:
1667 filelinkrevs.setdefault(f, []).append(i)
1679 filelinkrevs.setdefault(f, []).append(i)
1668
1680
1669 seen = {}
1681 seen = {}
1670 self.ui.status(_("checking manifests\n"))
1682 self.ui.status(_("checking manifests\n"))
1671 d = self.manifest.checksize()
1683 d = self.manifest.checksize()
1672 if d:
1684 if d:
1673 err(_("manifest data short %d bytes") % d)
1685 err(_("manifest data short %d bytes") % d)
1674 for i in range(self.manifest.count()):
1686 for i in range(self.manifest.count()):
1675 n = self.manifest.node(i)
1687 n = self.manifest.node(i)
1676 l = self.manifest.linkrev(n)
1688 l = self.manifest.linkrev(n)
1677
1689
1678 if l < 0 or l >= self.changelog.count():
1690 if l < 0 or l >= self.changelog.count():
1679 err(_("bad manifest link (%d) at revision %d") % (l, i))
1691 err(_("bad manifest link (%d) at revision %d") % (l, i))
1680
1692
1681 if n in neededmanifests:
1693 if n in neededmanifests:
1682 del neededmanifests[n]
1694 del neededmanifests[n]
1683
1695
1684 if n in seen:
1696 if n in seen:
1685 err(_("duplicate manifest at revision %d") % i)
1697 err(_("duplicate manifest at revision %d") % i)
1686
1698
1687 seen[n] = 1
1699 seen[n] = 1
1688
1700
1689 for p in self.manifest.parents(n):
1701 for p in self.manifest.parents(n):
1690 if p not in self.manifest.nodemap:
1702 if p not in self.manifest.nodemap:
1691 err(_("manifest %s has unknown parent %s") %
1703 err(_("manifest %s has unknown parent %s") %
1692 (short(n), short(p)))
1704 (short(n), short(p)))
1693
1705
1694 try:
1706 try:
1695 delta = mdiff.patchtext(self.manifest.delta(n))
1707 delta = mdiff.patchtext(self.manifest.delta(n))
1696 except KeyboardInterrupt:
1708 except KeyboardInterrupt:
1697 self.ui.warn(_("interrupted"))
1709 self.ui.warn(_("interrupted"))
1698 raise
1710 raise
1699 except Exception, inst:
1711 except Exception, inst:
1700 err(_("unpacking manifest %s: %s") % (short(n), inst))
1712 err(_("unpacking manifest %s: %s") % (short(n), inst))
1701
1713
1702 ff = [ l.split('\0') for l in delta.splitlines() ]
1714 ff = [ l.split('\0') for l in delta.splitlines() ]
1703 for f, fn in ff:
1715 for f, fn in ff:
1704 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1716 filenodes.setdefault(f, {})[bin(fn[:40])] = 1
1705
1717
1706 self.ui.status(_("crosschecking files in changesets and manifests\n"))
1718 self.ui.status(_("crosschecking files in changesets and manifests\n"))
1707
1719
1708 for m,c in neededmanifests.items():
1720 for m,c in neededmanifests.items():
1709 err(_("Changeset %s refers to unknown manifest %s") %
1721 err(_("Changeset %s refers to unknown manifest %s") %
1710 (short(m), short(c)))
1722 (short(m), short(c)))
1711 del neededmanifests
1723 del neededmanifests
1712
1724
1713 for f in filenodes:
1725 for f in filenodes:
1714 if f not in filelinkrevs:
1726 if f not in filelinkrevs:
1715 err(_("file %s in manifest but not in changesets") % f)
1727 err(_("file %s in manifest but not in changesets") % f)
1716
1728
1717 for f in filelinkrevs:
1729 for f in filelinkrevs:
1718 if f not in filenodes:
1730 if f not in filenodes:
1719 err(_("file %s in changeset but not in manifest") % f)
1731 err(_("file %s in changeset but not in manifest") % f)
1720
1732
1721 self.ui.status(_("checking files\n"))
1733 self.ui.status(_("checking files\n"))
1722 ff = filenodes.keys()
1734 ff = filenodes.keys()
1723 ff.sort()
1735 ff.sort()
1724 for f in ff:
1736 for f in ff:
1725 if f == "/dev/null": continue
1737 if f == "/dev/null": continue
1726 files += 1
1738 files += 1
1727 fl = self.file(f)
1739 fl = self.file(f)
1728 d = fl.checksize()
1740 d = fl.checksize()
1729 if d:
1741 if d:
1730 err(_("%s file data short %d bytes") % (f, d))
1742 err(_("%s file data short %d bytes") % (f, d))
1731
1743
1732 nodes = { nullid: 1 }
1744 nodes = { nullid: 1 }
1733 seen = {}
1745 seen = {}
1734 for i in range(fl.count()):
1746 for i in range(fl.count()):
1735 revisions += 1
1747 revisions += 1
1736 n = fl.node(i)
1748 n = fl.node(i)
1737
1749
1738 if n in seen:
1750 if n in seen:
1739 err(_("%s: duplicate revision %d") % (f, i))
1751 err(_("%s: duplicate revision %d") % (f, i))
1740 if n not in filenodes[f]:
1752 if n not in filenodes[f]:
1741 err(_("%s: %d:%s not in manifests") % (f, i, short(n)))
1753 err(_("%s: %d:%s not in manifests") % (f, i, short(n)))
1742 else:
1754 else:
1743 del filenodes[f][n]
1755 del filenodes[f][n]
1744
1756
1745 flr = fl.linkrev(n)
1757 flr = fl.linkrev(n)
1746 if flr not in filelinkrevs[f]:
1758 if flr not in filelinkrevs[f]:
1747 err(_("%s:%s points to unexpected changeset %d")
1759 err(_("%s:%s points to unexpected changeset %d")
1748 % (f, short(n), flr))
1760 % (f, short(n), flr))
1749 else:
1761 else:
1750 filelinkrevs[f].remove(flr)
1762 filelinkrevs[f].remove(flr)
1751
1763
1752 # verify contents
1764 # verify contents
1753 try:
1765 try:
1754 t = fl.read(n)
1766 t = fl.read(n)
1755 except KeyboardInterrupt:
1767 except KeyboardInterrupt:
1756 self.ui.warn(_("interrupted"))
1768 self.ui.warn(_("interrupted"))
1757 raise
1769 raise
1758 except Exception, inst:
1770 except Exception, inst:
1759 err(_("unpacking file %s %s: %s") % (f, short(n), inst))
1771 err(_("unpacking file %s %s: %s") % (f, short(n), inst))
1760
1772
1761 # verify parents
1773 # verify parents
1762 (p1, p2) = fl.parents(n)
1774 (p1, p2) = fl.parents(n)
1763 if p1 not in nodes:
1775 if p1 not in nodes:
1764 err(_("file %s:%s unknown parent 1 %s") %
1776 err(_("file %s:%s unknown parent 1 %s") %
1765 (f, short(n), short(p1)))
1777 (f, short(n), short(p1)))
1766 if p2 not in nodes:
1778 if p2 not in nodes:
1767 err(_("file %s:%s unknown parent 2 %s") %
1779 err(_("file %s:%s unknown parent 2 %s") %
1768 (f, short(n), short(p1)))
1780 (f, short(n), short(p1)))
1769 nodes[n] = 1
1781 nodes[n] = 1
1770
1782
1771 # cross-check
1783 # cross-check
1772 for node in filenodes[f]:
1784 for node in filenodes[f]:
1773 err(_("node %s in manifests not in %s") % (hex(node), f))
1785 err(_("node %s in manifests not in %s") % (hex(node), f))
1774
1786
1775 self.ui.status(_("%d files, %d changesets, %d total revisions\n") %
1787 self.ui.status(_("%d files, %d changesets, %d total revisions\n") %
1776 (files, changesets, revisions))
1788 (files, changesets, revisions))
1777
1789
1778 if errors[0]:
1790 if errors[0]:
1779 self.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
1791 self.ui.warn(_("%d integrity errors encountered!\n") % errors[0])
1780 return 1
1792 return 1
@@ -1,90 +1,96 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 cat <<'EOF' > merge
3 cat <<'EOF' > merge
4 #!/bin/sh
4 #!/bin/sh
5 echo merging for `basename $1`
5 echo merging for `basename $1`
6 EOF
6 EOF
7 chmod +x merge
7 chmod +x merge
8
8
9 mkdir t
9 mkdir t
10 cd t
10 cd t
11 hg init
11 hg init
12 echo This is file a1 > a
12 echo This is file a1 > a
13 hg add a
13 hg add a
14 hg commit -m "commit #0" -d "0 0"
14 hg commit -m "commit #0" -d "0 0"
15 echo This is file b1 > b
15 echo This is file b1 > b
16 hg add b
16 hg add b
17 hg commit -m "commit #1" -d "0 0"
17 hg commit -m "commit #1" -d "0 0"
18
18
19 hg update 0
19 hg update 0
20 echo This is file c1 > c
20 echo This is file c1 > c
21 hg add c
21 hg add c
22 hg commit -m "commit #2" -d "0 0"
22 hg commit -m "commit #2" -d "0 0"
23 echo This is file b1 > b
23 echo This is file b1 > b
24 echo %% no merges expected
24 echo %% no merges expected
25 env HGMERGE=../merge hg update -m 1
25 env HGMERGE=../merge hg update -m 1
26 cd ..; /bin/rm -rf t
26 cd ..; /bin/rm -rf t
27
27
28 mkdir t
28 mkdir t
29 cd t
29 cd t
30 hg init
30 hg init
31 echo This is file a1 > a
31 echo This is file a1 > a
32 hg add a
32 hg add a
33 hg commit -m "commit #0" -d "0 0"
33 hg commit -m "commit #0" -d "0 0"
34 echo This is file b1 > b
34 echo This is file b1 > b
35 hg add b
35 hg add b
36 hg commit -m "commit #1" -d "0 0"
36 hg commit -m "commit #1" -d "0 0"
37
37
38 hg update 0
38 hg update 0
39 echo This is file c1 > c
39 echo This is file c1 > c
40 hg add c
40 hg add c
41 hg commit -m "commit #2" -d "0 0"
41 hg commit -m "commit #2" -d "0 0"
42 echo This is file b2 > b
42 echo This is file b2 > b
43 echo %% merge should fail
44 env HGMERGE=../merge hg update -m 1
43 echo %% merge of b expected
45 echo %% merge of b expected
44 env HGMERGE=../merge hg update -m 1
46 env HGMERGE=../merge hg update -f -m 1
45 cd ..; /bin/rm -rf t
47 cd ..; /bin/rm -rf t
46 echo %%
48 echo %%
47
49
48 mkdir t
50 mkdir t
49 cd t
51 cd t
50 hg init
52 hg init
51 echo This is file a1 > a
53 echo This is file a1 > a
52 hg add a
54 hg add a
53 hg commit -m "commit #0" -d "0 0"
55 hg commit -m "commit #0" -d "0 0"
54 echo This is file b1 > b
56 echo This is file b1 > b
55 hg add b
57 hg add b
56 hg commit -m "commit #1" -d "0 0"
58 hg commit -m "commit #1" -d "0 0"
57 echo This is file b22 > b
59 echo This is file b22 > b
58 hg commit -m "commit #2" -d "0 0"
60 hg commit -m "commit #2" -d "0 0"
59 hg update 1
61 hg update 1
60 echo This is file c1 > c
62 echo This is file c1 > c
61 hg add c
63 hg add c
62 hg commit -m "commit #3" -d "0 0"
64 hg commit -m "commit #3" -d "0 0"
63
65
64 echo 'Contents of b should be "this is file b1"'
66 echo 'Contents of b should be "this is file b1"'
65 cat b
67 cat b
66
68
67 echo This is file b22 > b
69 echo This is file b22 > b
70 echo %% merge fails
71 env HGMERGE=../merge hg update -m 2
68 echo %% merge expected!
72 echo %% merge expected!
69 env HGMERGE=../merge hg update -m 2
73 env HGMERGE=../merge hg update -f -m 2
70 cd ..; /bin/rm -rf t
74 cd ..; /bin/rm -rf t
71
75
72 mkdir t
76 mkdir t
73 cd t
77 cd t
74 hg init
78 hg init
75 echo This is file a1 > a
79 echo This is file a1 > a
76 hg add a
80 hg add a
77 hg commit -m "commit #0" -d "0 0"
81 hg commit -m "commit #0" -d "0 0"
78 echo This is file b1 > b
82 echo This is file b1 > b
79 hg add b
83 hg add b
80 hg commit -m "commit #1" -d "0 0"
84 hg commit -m "commit #1" -d "0 0"
81 echo This is file b22 > b
85 echo This is file b22 > b
82 hg commit -m "commit #2" -d "0 0"
86 hg commit -m "commit #2" -d "0 0"
83 hg update 1
87 hg update 1
84 echo This is file c1 > c
88 echo This is file c1 > c
85 hg add c
89 hg add c
86 hg commit -m "commit #3" -d "0 0"
90 hg commit -m "commit #3" -d "0 0"
87 echo This is file b33 > b
91 echo This is file b33 > b
88 echo %% merge of b expected
92 echo %% merge of b should fail
89 env HGMERGE=../merge hg update -m 2
93 env HGMERGE=../merge hg update -m 2
94 echo %% merge of b expected
95 env HGMERGE=../merge hg update -f -m 2
90 cd ..; /bin/rm -rf t
96 cd ..; /bin/rm -rf t
@@ -1,13 +1,19 b''
1 %% no merges expected
1 %% no merges expected
2 %% merge should fail
3 abort: 'b' already exists in the working dir and differs from remote
2 %% merge of b expected
4 %% merge of b expected
3 merging for b
5 merging for b
4 merging b
6 merging b
5 %%
7 %%
6 Contents of b should be "this is file b1"
8 Contents of b should be "this is file b1"
7 This is file b1
9 This is file b1
10 %% merge fails
11 abort: outstanding uncommited changes
8 %% merge expected!
12 %% merge expected!
9 merging for b
13 merging for b
10 merging b
14 merging b
15 %% merge of b should fail
16 abort: outstanding uncommited changes
11 %% merge of b expected
17 %% merge of b expected
12 merging for b
18 merging for b
13 merging b
19 merging b
@@ -1,33 +1,34 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 set -e
3 set -e
4 mkdir r1
4 mkdir r1
5 cd r1
5 cd r1
6 hg init
6 hg init
7 echo a > a
7 echo a > a
8 hg addremove
8 hg addremove
9 hg commit -m "1" -d "0 0"
9 hg commit -m "1" -d "0 0"
10
10
11 hg clone . ../r2
11 hg clone . ../r2
12 cd ../r2
12 cd ../r2
13 hg up
13 hg up
14 echo abc > a
14 echo abc > a
15 hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
15 hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
16 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
16 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
17
17
18 cd ../r1
18 cd ../r1
19 echo b > b
19 echo b > b
20 echo a2 > a
20 echo a2 > a
21 hg addremove
21 hg addremove
22 hg commit -m "2" -d "0 0"
22 hg commit -m "2" -d "0 0"
23
23
24 cd ../r2
24 cd ../r2
25 hg -q pull ../r1
25 hg -q pull ../r1
26 hg status
26 hg status
27 hg --debug up
27 hg --debug up
28 hg --debug up -m
28 hg --debug up -m || echo failed
29 hg --debug up -f -m
29 hg parents
30 hg parents
30 hg -v history
31 hg -v history
31 hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
32 hg diff | sed -e "s/\(+++ [a-zA-Z0-9_/.-]*\).*/\1/" \
32 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
33 -e "s/\(--- [a-zA-Z0-9_/.-]*\).*/\1/"
33
34
@@ -1,50 +1,52 b''
1 adding a
1 adding a
2 diff -r c19d34741b0a a
2 diff -r c19d34741b0a a
3 --- a/a
3 --- a/a
4 +++ b/a
4 +++ b/a
5 @@ -1,1 +1,1 @@
5 @@ -1,1 +1,1 @@
6 -a
6 -a
7 +abc
7 +abc
8 adding b
8 adding b
9 M a
9 M a
10 resolving manifests
10 resolving manifests
11 force None allow None moddirstate True linear True
11 force None allow None moddirstate True linear True
12 ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e
12 ancestor a0c8bcbbb45c local a0c8bcbbb45c remote 1165e8bd193e
13 a versions differ, resolve
13 a versions differ, resolve
14 remote created b
14 remote created b
15 getting b
15 getting b
16 merging a
16 merging a
17 resolving a
17 resolving a
18 file a: my b789fdd96dc2 other d730145abbf9 ancestor b789fdd96dc2
18 file a: my b789fdd96dc2 other d730145abbf9 ancestor b789fdd96dc2
19 abort: outstanding uncommited changes
20 failed
19 resolving manifests
21 resolving manifests
20 force None allow 1 moddirstate True linear True
22 force None allow 1 moddirstate True linear True
21 ancestor 1165e8bd193e local 1165e8bd193e remote 1165e8bd193e
23 ancestor 1165e8bd193e local 1165e8bd193e remote 1165e8bd193e
22 changeset: 1:1e71731e6fbb
24 changeset: 1:1e71731e6fbb
23 tag: tip
25 tag: tip
24 user: test
26 user: test
25 date: Thu Jan 1 00:00:00 1970 +0000
27 date: Thu Jan 1 00:00:00 1970 +0000
26 summary: 2
28 summary: 2
27
29
28 changeset: 1:1e71731e6fbb5b35fae293120dea6964371c13c6
30 changeset: 1:1e71731e6fbb5b35fae293120dea6964371c13c6
29 tag: tip
31 tag: tip
30 user: test
32 user: test
31 date: Thu Jan 1 00:00:00 1970 +0000
33 date: Thu Jan 1 00:00:00 1970 +0000
32 files: a b
34 files: a b
33 description:
35 description:
34 2
36 2
35
37
36
38
37 changeset: 0:c19d34741b0a4ced8e4ba74bb834597d5193851e
39 changeset: 0:c19d34741b0a4ced8e4ba74bb834597d5193851e
38 user: test
40 user: test
39 date: Thu Jan 1 00:00:00 1970 +0000
41 date: Thu Jan 1 00:00:00 1970 +0000
40 files: a
42 files: a
41 description:
43 description:
42 1
44 1
43
45
44
46
45 diff -r 1e71731e6fbb a
47 diff -r 1e71731e6fbb a
46 --- a/a
48 --- a/a
47 +++ b/a
49 +++ b/a
48 @@ -1,1 +1,1 @@
50 @@ -1,1 +1,1 @@
49 -a2
51 -a2
50 +abc
52 +abc
General Comments 0
You need to be logged in to leave comments. Login now