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