##// END OF EJS Templates
merge: drop underscore prefix from _checkunknown()...
Martin von Zweigbergk -
r23319:3177d710 default
parent child Browse files
Show More
@@ -1,1287 +1,1287 b''
1 # Copyright 2009-2010 Gregory P. Ward
1 # Copyright 2009-2010 Gregory P. Ward
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
2 # Copyright 2009-2010 Intelerad Medical Systems Incorporated
3 # Copyright 2010-2011 Fog Creek Software
3 # Copyright 2010-2011 Fog Creek Software
4 # Copyright 2010-2011 Unity Technologies
4 # Copyright 2010-2011 Unity Technologies
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2 or any later version.
7 # GNU General Public License version 2 or any later version.
8
8
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
9 '''Overridden Mercurial commands and functions for the largefiles extension'''
10
10
11 import os
11 import os
12 import copy
12 import copy
13
13
14 from mercurial import hg, util, cmdutil, scmutil, match as match_, \
14 from mercurial import hg, util, cmdutil, scmutil, match as match_, \
15 archival, pathutil, revset
15 archival, pathutil, revset
16 from mercurial.i18n import _
16 from mercurial.i18n import _
17 from mercurial.node import hex
17 from mercurial.node import hex
18
18
19 import lfutil
19 import lfutil
20 import lfcommands
20 import lfcommands
21 import basestore
21 import basestore
22
22
23 # -- Utility functions: commonly/repeatedly needed functionality ---------------
23 # -- Utility functions: commonly/repeatedly needed functionality ---------------
24
24
25 def installnormalfilesmatchfn(manifest):
25 def installnormalfilesmatchfn(manifest):
26 '''installmatchfn with a matchfn that ignores all largefiles'''
26 '''installmatchfn with a matchfn that ignores all largefiles'''
27 def overridematch(ctx, pats=[], opts={}, globbed=False,
27 def overridematch(ctx, pats=[], opts={}, globbed=False,
28 default='relpath'):
28 default='relpath'):
29 match = oldmatch(ctx, pats, opts, globbed, default)
29 match = oldmatch(ctx, pats, opts, globbed, default)
30 m = copy.copy(match)
30 m = copy.copy(match)
31 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
31 notlfile = lambda f: not (lfutil.isstandin(f) or lfutil.standin(f) in
32 manifest)
32 manifest)
33 m._files = filter(notlfile, m._files)
33 m._files = filter(notlfile, m._files)
34 m._fmap = set(m._files)
34 m._fmap = set(m._files)
35 m._always = False
35 m._always = False
36 origmatchfn = m.matchfn
36 origmatchfn = m.matchfn
37 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
37 m.matchfn = lambda f: notlfile(f) and origmatchfn(f)
38 return m
38 return m
39 oldmatch = installmatchfn(overridematch)
39 oldmatch = installmatchfn(overridematch)
40
40
41 def installmatchfn(f):
41 def installmatchfn(f):
42 '''monkey patch the scmutil module with a custom match function.
42 '''monkey patch the scmutil module with a custom match function.
43 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
43 Warning: it is monkey patching the _module_ on runtime! Not thread safe!'''
44 oldmatch = scmutil.match
44 oldmatch = scmutil.match
45 setattr(f, 'oldmatch', oldmatch)
45 setattr(f, 'oldmatch', oldmatch)
46 scmutil.match = f
46 scmutil.match = f
47 return oldmatch
47 return oldmatch
48
48
49 def restorematchfn():
49 def restorematchfn():
50 '''restores scmutil.match to what it was before installmatchfn
50 '''restores scmutil.match to what it was before installmatchfn
51 was called. no-op if scmutil.match is its original function.
51 was called. no-op if scmutil.match is its original function.
52
52
53 Note that n calls to installmatchfn will require n calls to
53 Note that n calls to installmatchfn will require n calls to
54 restore matchfn to reverse'''
54 restore matchfn to reverse'''
55 scmutil.match = getattr(scmutil.match, 'oldmatch')
55 scmutil.match = getattr(scmutil.match, 'oldmatch')
56
56
57 def installmatchandpatsfn(f):
57 def installmatchandpatsfn(f):
58 oldmatchandpats = scmutil.matchandpats
58 oldmatchandpats = scmutil.matchandpats
59 setattr(f, 'oldmatchandpats', oldmatchandpats)
59 setattr(f, 'oldmatchandpats', oldmatchandpats)
60 scmutil.matchandpats = f
60 scmutil.matchandpats = f
61 return oldmatchandpats
61 return oldmatchandpats
62
62
63 def restorematchandpatsfn():
63 def restorematchandpatsfn():
64 '''restores scmutil.matchandpats to what it was before
64 '''restores scmutil.matchandpats to what it was before
65 installmatchandpatsfn was called. No-op if scmutil.matchandpats
65 installmatchandpatsfn was called. No-op if scmutil.matchandpats
66 is its original function.
66 is its original function.
67
67
68 Note that n calls to installmatchandpatsfn will require n calls
68 Note that n calls to installmatchandpatsfn will require n calls
69 to restore matchfn to reverse'''
69 to restore matchfn to reverse'''
70 scmutil.matchandpats = getattr(scmutil.matchandpats, 'oldmatchandpats',
70 scmutil.matchandpats = getattr(scmutil.matchandpats, 'oldmatchandpats',
71 scmutil.matchandpats)
71 scmutil.matchandpats)
72
72
73 def addlargefiles(ui, repo, *pats, **opts):
73 def addlargefiles(ui, repo, *pats, **opts):
74 large = opts.pop('large', None)
74 large = opts.pop('large', None)
75 lfsize = lfutil.getminsize(
75 lfsize = lfutil.getminsize(
76 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
76 ui, lfutil.islfilesrepo(repo), opts.pop('lfsize', None))
77
77
78 lfmatcher = None
78 lfmatcher = None
79 if lfutil.islfilesrepo(repo):
79 if lfutil.islfilesrepo(repo):
80 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
80 lfpats = ui.configlist(lfutil.longname, 'patterns', default=[])
81 if lfpats:
81 if lfpats:
82 lfmatcher = match_.match(repo.root, '', list(lfpats))
82 lfmatcher = match_.match(repo.root, '', list(lfpats))
83
83
84 lfnames = []
84 lfnames = []
85 m = scmutil.match(repo[None], pats, opts)
85 m = scmutil.match(repo[None], pats, opts)
86 m.bad = lambda x, y: None
86 m.bad = lambda x, y: None
87 wctx = repo[None]
87 wctx = repo[None]
88 for f in repo.walk(m):
88 for f in repo.walk(m):
89 exact = m.exact(f)
89 exact = m.exact(f)
90 lfile = lfutil.standin(f) in wctx
90 lfile = lfutil.standin(f) in wctx
91 nfile = f in wctx
91 nfile = f in wctx
92 exists = lfile or nfile
92 exists = lfile or nfile
93
93
94 # Don't warn the user when they attempt to add a normal tracked file.
94 # Don't warn the user when they attempt to add a normal tracked file.
95 # The normal add code will do that for us.
95 # The normal add code will do that for us.
96 if exact and exists:
96 if exact and exists:
97 if lfile:
97 if lfile:
98 ui.warn(_('%s already a largefile\n') % f)
98 ui.warn(_('%s already a largefile\n') % f)
99 continue
99 continue
100
100
101 if (exact or not exists) and not lfutil.isstandin(f):
101 if (exact or not exists) and not lfutil.isstandin(f):
102 wfile = repo.wjoin(f)
102 wfile = repo.wjoin(f)
103
103
104 # In case the file was removed previously, but not committed
104 # In case the file was removed previously, but not committed
105 # (issue3507)
105 # (issue3507)
106 if not os.path.exists(wfile):
106 if not os.path.exists(wfile):
107 continue
107 continue
108
108
109 abovemin = (lfsize and
109 abovemin = (lfsize and
110 os.lstat(wfile).st_size >= lfsize * 1024 * 1024)
110 os.lstat(wfile).st_size >= lfsize * 1024 * 1024)
111 if large or abovemin or (lfmatcher and lfmatcher(f)):
111 if large or abovemin or (lfmatcher and lfmatcher(f)):
112 lfnames.append(f)
112 lfnames.append(f)
113 if ui.verbose or not exact:
113 if ui.verbose or not exact:
114 ui.status(_('adding %s as a largefile\n') % m.rel(f))
114 ui.status(_('adding %s as a largefile\n') % m.rel(f))
115
115
116 bad = []
116 bad = []
117
117
118 # Need to lock, otherwise there could be a race condition between
118 # Need to lock, otherwise there could be a race condition between
119 # when standins are created and added to the repo.
119 # when standins are created and added to the repo.
120 wlock = repo.wlock()
120 wlock = repo.wlock()
121 try:
121 try:
122 if not opts.get('dry_run'):
122 if not opts.get('dry_run'):
123 standins = []
123 standins = []
124 lfdirstate = lfutil.openlfdirstate(ui, repo)
124 lfdirstate = lfutil.openlfdirstate(ui, repo)
125 for f in lfnames:
125 for f in lfnames:
126 standinname = lfutil.standin(f)
126 standinname = lfutil.standin(f)
127 lfutil.writestandin(repo, standinname, hash='',
127 lfutil.writestandin(repo, standinname, hash='',
128 executable=lfutil.getexecutable(repo.wjoin(f)))
128 executable=lfutil.getexecutable(repo.wjoin(f)))
129 standins.append(standinname)
129 standins.append(standinname)
130 if lfdirstate[f] == 'r':
130 if lfdirstate[f] == 'r':
131 lfdirstate.normallookup(f)
131 lfdirstate.normallookup(f)
132 else:
132 else:
133 lfdirstate.add(f)
133 lfdirstate.add(f)
134 lfdirstate.write()
134 lfdirstate.write()
135 bad += [lfutil.splitstandin(f)
135 bad += [lfutil.splitstandin(f)
136 for f in repo[None].add(standins)
136 for f in repo[None].add(standins)
137 if f in m.files()]
137 if f in m.files()]
138 finally:
138 finally:
139 wlock.release()
139 wlock.release()
140 return bad
140 return bad
141
141
142 def removelargefiles(ui, repo, isaddremove, *pats, **opts):
142 def removelargefiles(ui, repo, isaddremove, *pats, **opts):
143 after = opts.get('after')
143 after = opts.get('after')
144 if not pats and not after:
144 if not pats and not after:
145 raise util.Abort(_('no files specified'))
145 raise util.Abort(_('no files specified'))
146 m = scmutil.match(repo[None], pats, opts)
146 m = scmutil.match(repo[None], pats, opts)
147 try:
147 try:
148 repo.lfstatus = True
148 repo.lfstatus = True
149 s = repo.status(match=m, clean=True)
149 s = repo.status(match=m, clean=True)
150 finally:
150 finally:
151 repo.lfstatus = False
151 repo.lfstatus = False
152 manifest = repo[None].manifest()
152 manifest = repo[None].manifest()
153 modified, added, deleted, clean = [[f for f in list
153 modified, added, deleted, clean = [[f for f in list
154 if lfutil.standin(f) in manifest]
154 if lfutil.standin(f) in manifest]
155 for list in (s.modified, s.added,
155 for list in (s.modified, s.added,
156 s.deleted, s.clean)]
156 s.deleted, s.clean)]
157
157
158 def warn(files, msg):
158 def warn(files, msg):
159 for f in files:
159 for f in files:
160 ui.warn(msg % m.rel(f))
160 ui.warn(msg % m.rel(f))
161 return int(len(files) > 0)
161 return int(len(files) > 0)
162
162
163 result = 0
163 result = 0
164
164
165 if after:
165 if after:
166 remove = deleted
166 remove = deleted
167 result = warn(modified + added + clean,
167 result = warn(modified + added + clean,
168 _('not removing %s: file still exists\n'))
168 _('not removing %s: file still exists\n'))
169 else:
169 else:
170 remove = deleted + clean
170 remove = deleted + clean
171 result = warn(modified, _('not removing %s: file is modified (use -f'
171 result = warn(modified, _('not removing %s: file is modified (use -f'
172 ' to force removal)\n'))
172 ' to force removal)\n'))
173 result = warn(added, _('not removing %s: file has been marked for add'
173 result = warn(added, _('not removing %s: file has been marked for add'
174 ' (use forget to undo)\n')) or result
174 ' (use forget to undo)\n')) or result
175
175
176 for f in sorted(remove):
176 for f in sorted(remove):
177 if ui.verbose or not m.exact(f):
177 if ui.verbose or not m.exact(f):
178 ui.status(_('removing %s\n') % m.rel(f))
178 ui.status(_('removing %s\n') % m.rel(f))
179
179
180 # Need to lock because standin files are deleted then removed from the
180 # Need to lock because standin files are deleted then removed from the
181 # repository and we could race in-between.
181 # repository and we could race in-between.
182 wlock = repo.wlock()
182 wlock = repo.wlock()
183 try:
183 try:
184 lfdirstate = lfutil.openlfdirstate(ui, repo)
184 lfdirstate = lfutil.openlfdirstate(ui, repo)
185 for f in remove:
185 for f in remove:
186 if not after:
186 if not after:
187 # If this is being called by addremove, notify the user that we
187 # If this is being called by addremove, notify the user that we
188 # are removing the file.
188 # are removing the file.
189 if isaddremove:
189 if isaddremove:
190 ui.status(_('removing %s\n') % f)
190 ui.status(_('removing %s\n') % f)
191 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
191 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
192 lfdirstate.remove(f)
192 lfdirstate.remove(f)
193 lfdirstate.write()
193 lfdirstate.write()
194 remove = [lfutil.standin(f) for f in remove]
194 remove = [lfutil.standin(f) for f in remove]
195 # If this is being called by addremove, let the original addremove
195 # If this is being called by addremove, let the original addremove
196 # function handle this.
196 # function handle this.
197 if not isaddremove:
197 if not isaddremove:
198 for f in remove:
198 for f in remove:
199 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
199 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
200 repo[None].forget(remove)
200 repo[None].forget(remove)
201 finally:
201 finally:
202 wlock.release()
202 wlock.release()
203
203
204 return result
204 return result
205
205
206 # For overriding mercurial.hgweb.webcommands so that largefiles will
206 # For overriding mercurial.hgweb.webcommands so that largefiles will
207 # appear at their right place in the manifests.
207 # appear at their right place in the manifests.
208 def decodepath(orig, path):
208 def decodepath(orig, path):
209 return lfutil.splitstandin(path) or path
209 return lfutil.splitstandin(path) or path
210
210
211 # -- Wrappers: modify existing commands --------------------------------
211 # -- Wrappers: modify existing commands --------------------------------
212
212
213 # Add works by going through the files that the user wanted to add and
213 # Add works by going through the files that the user wanted to add and
214 # checking if they should be added as largefiles. Then it makes a new
214 # checking if they should be added as largefiles. Then it makes a new
215 # matcher which matches only the normal files and runs the original
215 # matcher which matches only the normal files and runs the original
216 # version of add.
216 # version of add.
217 def overrideadd(orig, ui, repo, *pats, **opts):
217 def overrideadd(orig, ui, repo, *pats, **opts):
218 normal = opts.pop('normal')
218 normal = opts.pop('normal')
219 if normal:
219 if normal:
220 if opts.get('large'):
220 if opts.get('large'):
221 raise util.Abort(_('--normal cannot be used with --large'))
221 raise util.Abort(_('--normal cannot be used with --large'))
222 return orig(ui, repo, *pats, **opts)
222 return orig(ui, repo, *pats, **opts)
223 bad = addlargefiles(ui, repo, *pats, **opts)
223 bad = addlargefiles(ui, repo, *pats, **opts)
224 installnormalfilesmatchfn(repo[None].manifest())
224 installnormalfilesmatchfn(repo[None].manifest())
225 result = orig(ui, repo, *pats, **opts)
225 result = orig(ui, repo, *pats, **opts)
226 restorematchfn()
226 restorematchfn()
227
227
228 return (result == 1 or bad) and 1 or 0
228 return (result == 1 or bad) and 1 or 0
229
229
230 def overrideremove(orig, ui, repo, *pats, **opts):
230 def overrideremove(orig, ui, repo, *pats, **opts):
231 installnormalfilesmatchfn(repo[None].manifest())
231 installnormalfilesmatchfn(repo[None].manifest())
232 result = orig(ui, repo, *pats, **opts)
232 result = orig(ui, repo, *pats, **opts)
233 restorematchfn()
233 restorematchfn()
234 return removelargefiles(ui, repo, False, *pats, **opts) or result
234 return removelargefiles(ui, repo, False, *pats, **opts) or result
235
235
236 def overridestatusfn(orig, repo, rev2, **opts):
236 def overridestatusfn(orig, repo, rev2, **opts):
237 try:
237 try:
238 repo._repo.lfstatus = True
238 repo._repo.lfstatus = True
239 return orig(repo, rev2, **opts)
239 return orig(repo, rev2, **opts)
240 finally:
240 finally:
241 repo._repo.lfstatus = False
241 repo._repo.lfstatus = False
242
242
243 def overridestatus(orig, ui, repo, *pats, **opts):
243 def overridestatus(orig, ui, repo, *pats, **opts):
244 try:
244 try:
245 repo.lfstatus = True
245 repo.lfstatus = True
246 return orig(ui, repo, *pats, **opts)
246 return orig(ui, repo, *pats, **opts)
247 finally:
247 finally:
248 repo.lfstatus = False
248 repo.lfstatus = False
249
249
250 def overridedirty(orig, repo, ignoreupdate=False):
250 def overridedirty(orig, repo, ignoreupdate=False):
251 try:
251 try:
252 repo._repo.lfstatus = True
252 repo._repo.lfstatus = True
253 return orig(repo, ignoreupdate)
253 return orig(repo, ignoreupdate)
254 finally:
254 finally:
255 repo._repo.lfstatus = False
255 repo._repo.lfstatus = False
256
256
257 def overridelog(orig, ui, repo, *pats, **opts):
257 def overridelog(orig, ui, repo, *pats, **opts):
258 def overridematchandpats(ctx, pats=[], opts={}, globbed=False,
258 def overridematchandpats(ctx, pats=[], opts={}, globbed=False,
259 default='relpath'):
259 default='relpath'):
260 """Matcher that merges root directory with .hglf, suitable for log.
260 """Matcher that merges root directory with .hglf, suitable for log.
261 It is still possible to match .hglf directly.
261 It is still possible to match .hglf directly.
262 For any listed files run log on the standin too.
262 For any listed files run log on the standin too.
263 matchfn tries both the given filename and with .hglf stripped.
263 matchfn tries both the given filename and with .hglf stripped.
264 """
264 """
265 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default)
265 matchandpats = oldmatchandpats(ctx, pats, opts, globbed, default)
266 m, p = copy.copy(matchandpats)
266 m, p = copy.copy(matchandpats)
267
267
268 if m.always():
268 if m.always():
269 # We want to match everything anyway, so there's no benefit trying
269 # We want to match everything anyway, so there's no benefit trying
270 # to add standins.
270 # to add standins.
271 return matchandpats
271 return matchandpats
272
272
273 pats = set(p)
273 pats = set(p)
274 # TODO: handling of patterns in both cases below
274 # TODO: handling of patterns in both cases below
275 if m._cwd:
275 if m._cwd:
276 if os.path.isabs(m._cwd):
276 if os.path.isabs(m._cwd):
277 # TODO: handle largefile magic when invoked from other cwd
277 # TODO: handle largefile magic when invoked from other cwd
278 return matchandpats
278 return matchandpats
279 back = (m._cwd.count('/') + 1) * '../'
279 back = (m._cwd.count('/') + 1) * '../'
280 pats.update(back + lfutil.standin(m._cwd + '/' + f) for f in p)
280 pats.update(back + lfutil.standin(m._cwd + '/' + f) for f in p)
281 else:
281 else:
282 pats.update(lfutil.standin(f) for f in p)
282 pats.update(lfutil.standin(f) for f in p)
283
283
284 for i in range(0, len(m._files)):
284 for i in range(0, len(m._files)):
285 standin = lfutil.standin(m._files[i])
285 standin = lfutil.standin(m._files[i])
286 if standin in repo[ctx.node()]:
286 if standin in repo[ctx.node()]:
287 m._files[i] = standin
287 m._files[i] = standin
288 elif m._files[i] not in repo[ctx.node()]:
288 elif m._files[i] not in repo[ctx.node()]:
289 m._files.append(standin)
289 m._files.append(standin)
290 pats.add(standin)
290 pats.add(standin)
291
291
292 m._fmap = set(m._files)
292 m._fmap = set(m._files)
293 m._always = False
293 m._always = False
294 origmatchfn = m.matchfn
294 origmatchfn = m.matchfn
295 def lfmatchfn(f):
295 def lfmatchfn(f):
296 lf = lfutil.splitstandin(f)
296 lf = lfutil.splitstandin(f)
297 if lf is not None and origmatchfn(lf):
297 if lf is not None and origmatchfn(lf):
298 return True
298 return True
299 r = origmatchfn(f)
299 r = origmatchfn(f)
300 return r
300 return r
301 m.matchfn = lfmatchfn
301 m.matchfn = lfmatchfn
302
302
303 return m, pats
303 return m, pats
304
304
305 # For hg log --patch, the match object is used in two different senses:
305 # For hg log --patch, the match object is used in two different senses:
306 # (1) to determine what revisions should be printed out, and
306 # (1) to determine what revisions should be printed out, and
307 # (2) to determine what files to print out diffs for.
307 # (2) to determine what files to print out diffs for.
308 # The magic matchandpats override should be used for case (1) but not for
308 # The magic matchandpats override should be used for case (1) but not for
309 # case (2).
309 # case (2).
310 def overridemakelogfilematcher(repo, pats, opts):
310 def overridemakelogfilematcher(repo, pats, opts):
311 pctx = repo[None]
311 pctx = repo[None]
312 match, pats = oldmatchandpats(pctx, pats, opts)
312 match, pats = oldmatchandpats(pctx, pats, opts)
313 return lambda rev: match
313 return lambda rev: match
314
314
315 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
315 oldmatchandpats = installmatchandpatsfn(overridematchandpats)
316 oldmakelogfilematcher = cmdutil._makenofollowlogfilematcher
316 oldmakelogfilematcher = cmdutil._makenofollowlogfilematcher
317 setattr(cmdutil, '_makenofollowlogfilematcher', overridemakelogfilematcher)
317 setattr(cmdutil, '_makenofollowlogfilematcher', overridemakelogfilematcher)
318
318
319 try:
319 try:
320 return orig(ui, repo, *pats, **opts)
320 return orig(ui, repo, *pats, **opts)
321 finally:
321 finally:
322 restorematchandpatsfn()
322 restorematchandpatsfn()
323 setattr(cmdutil, '_makenofollowlogfilematcher', oldmakelogfilematcher)
323 setattr(cmdutil, '_makenofollowlogfilematcher', oldmakelogfilematcher)
324
324
325 def overrideverify(orig, ui, repo, *pats, **opts):
325 def overrideverify(orig, ui, repo, *pats, **opts):
326 large = opts.pop('large', False)
326 large = opts.pop('large', False)
327 all = opts.pop('lfa', False)
327 all = opts.pop('lfa', False)
328 contents = opts.pop('lfc', False)
328 contents = opts.pop('lfc', False)
329
329
330 result = orig(ui, repo, *pats, **opts)
330 result = orig(ui, repo, *pats, **opts)
331 if large or all or contents:
331 if large or all or contents:
332 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
332 result = result or lfcommands.verifylfiles(ui, repo, all, contents)
333 return result
333 return result
334
334
335 def overridedebugstate(orig, ui, repo, *pats, **opts):
335 def overridedebugstate(orig, ui, repo, *pats, **opts):
336 large = opts.pop('large', False)
336 large = opts.pop('large', False)
337 if large:
337 if large:
338 class fakerepo(object):
338 class fakerepo(object):
339 dirstate = lfutil.openlfdirstate(ui, repo)
339 dirstate = lfutil.openlfdirstate(ui, repo)
340 orig(ui, fakerepo, *pats, **opts)
340 orig(ui, fakerepo, *pats, **opts)
341 else:
341 else:
342 orig(ui, repo, *pats, **opts)
342 orig(ui, repo, *pats, **opts)
343
343
344 # Override needs to refresh standins so that update's normal merge
344 # Override needs to refresh standins so that update's normal merge
345 # will go through properly. Then the other update hook (overriding repo.update)
345 # will go through properly. Then the other update hook (overriding repo.update)
346 # will get the new files. Filemerge is also overridden so that the merge
346 # will get the new files. Filemerge is also overridden so that the merge
347 # will merge standins correctly.
347 # will merge standins correctly.
348 def overrideupdate(orig, ui, repo, *pats, **opts):
348 def overrideupdate(orig, ui, repo, *pats, **opts):
349 # Need to lock between the standins getting updated and their
349 # Need to lock between the standins getting updated and their
350 # largefiles getting updated
350 # largefiles getting updated
351 wlock = repo.wlock()
351 wlock = repo.wlock()
352 try:
352 try:
353 if opts['check']:
353 if opts['check']:
354 lfdirstate = lfutil.openlfdirstate(ui, repo)
354 lfdirstate = lfutil.openlfdirstate(ui, repo)
355 unsure, s = lfdirstate.status(
355 unsure, s = lfdirstate.status(
356 match_.always(repo.root, repo.getcwd()),
356 match_.always(repo.root, repo.getcwd()),
357 [], False, False, False)
357 [], False, False, False)
358
358
359 mod = len(s.modified) > 0
359 mod = len(s.modified) > 0
360 for lfile in unsure:
360 for lfile in unsure:
361 standin = lfutil.standin(lfile)
361 standin = lfutil.standin(lfile)
362 if repo['.'][standin].data().strip() != \
362 if repo['.'][standin].data().strip() != \
363 lfutil.hashfile(repo.wjoin(lfile)):
363 lfutil.hashfile(repo.wjoin(lfile)):
364 mod = True
364 mod = True
365 else:
365 else:
366 lfdirstate.normal(lfile)
366 lfdirstate.normal(lfile)
367 lfdirstate.write()
367 lfdirstate.write()
368 if mod:
368 if mod:
369 raise util.Abort(_('uncommitted changes'))
369 raise util.Abort(_('uncommitted changes'))
370 return orig(ui, repo, *pats, **opts)
370 return orig(ui, repo, *pats, **opts)
371 finally:
371 finally:
372 wlock.release()
372 wlock.release()
373
373
374 # Before starting the manifest merge, merge.updates will call
374 # Before starting the manifest merge, merge.updates will call
375 # _checkunknown to check if there are any files in the merged-in
375 # checkunknown to check if there are any files in the merged-in
376 # changeset that collide with unknown files in the working copy.
376 # changeset that collide with unknown files in the working copy.
377 #
377 #
378 # The largefiles are seen as unknown, so this prevents us from merging
378 # The largefiles are seen as unknown, so this prevents us from merging
379 # in a file 'foo' if we already have a largefile with the same name.
379 # in a file 'foo' if we already have a largefile with the same name.
380 #
380 #
381 # The overridden function filters the unknown files by removing any
381 # The overridden function filters the unknown files by removing any
382 # largefiles. This makes the merge proceed and we can then handle this
382 # largefiles. This makes the merge proceed and we can then handle this
383 # case further in the overridden calculateupdates function below.
383 # case further in the overridden calculateupdates function below.
384 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
384 def overridecheckunknownfile(origfn, repo, wctx, mctx, f):
385 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
385 if lfutil.standin(repo.dirstate.normalize(f)) in wctx:
386 return False
386 return False
387 return origfn(repo, wctx, mctx, f)
387 return origfn(repo, wctx, mctx, f)
388
388
389 # The manifest merge handles conflicts on the manifest level. We want
389 # The manifest merge handles conflicts on the manifest level. We want
390 # to handle changes in largefile-ness of files at this level too.
390 # to handle changes in largefile-ness of files at this level too.
391 #
391 #
392 # The strategy is to run the original calculateupdates and then process
392 # The strategy is to run the original calculateupdates and then process
393 # the action list it outputs. There are two cases we need to deal with:
393 # the action list it outputs. There are two cases we need to deal with:
394 #
394 #
395 # 1. Normal file in p1, largefile in p2. Here the largefile is
395 # 1. Normal file in p1, largefile in p2. Here the largefile is
396 # detected via its standin file, which will enter the working copy
396 # detected via its standin file, which will enter the working copy
397 # with a "get" action. It is not "merge" since the standin is all
397 # with a "get" action. It is not "merge" since the standin is all
398 # Mercurial is concerned with at this level -- the link to the
398 # Mercurial is concerned with at this level -- the link to the
399 # existing normal file is not relevant here.
399 # existing normal file is not relevant here.
400 #
400 #
401 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
401 # 2. Largefile in p1, normal file in p2. Here we get a "merge" action
402 # since the largefile will be present in the working copy and
402 # since the largefile will be present in the working copy and
403 # different from the normal file in p2. Mercurial therefore
403 # different from the normal file in p2. Mercurial therefore
404 # triggers a merge action.
404 # triggers a merge action.
405 #
405 #
406 # In both cases, we prompt the user and emit new actions to either
406 # In both cases, we prompt the user and emit new actions to either
407 # remove the standin (if the normal file was kept) or to remove the
407 # remove the standin (if the normal file was kept) or to remove the
408 # normal file and get the standin (if the largefile was kept). The
408 # normal file and get the standin (if the largefile was kept). The
409 # default prompt answer is to use the largefile version since it was
409 # default prompt answer is to use the largefile version since it was
410 # presumably changed on purpose.
410 # presumably changed on purpose.
411 #
411 #
412 # Finally, the merge.applyupdates function will then take care of
412 # Finally, the merge.applyupdates function will then take care of
413 # writing the files into the working copy and lfcommands.updatelfiles
413 # writing the files into the working copy and lfcommands.updatelfiles
414 # will update the largefiles.
414 # will update the largefiles.
415 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
415 def overridecalculateupdates(origfn, repo, p1, p2, pas, branchmerge, force,
416 partial, acceptremote, followcopies):
416 partial, acceptremote, followcopies):
417 overwrite = force and not branchmerge
417 overwrite = force and not branchmerge
418 actions = origfn(repo, p1, p2, pas, branchmerge, force, partial,
418 actions = origfn(repo, p1, p2, pas, branchmerge, force, partial,
419 acceptremote, followcopies)
419 acceptremote, followcopies)
420
420
421 if overwrite:
421 if overwrite:
422 return actions
422 return actions
423
423
424 removes = set(a[0] for a in actions['r'])
424 removes = set(a[0] for a in actions['r'])
425
425
426 newglist = []
426 newglist = []
427 lfmr = [] # LargeFiles: Mark as Removed
427 lfmr = [] # LargeFiles: Mark as Removed
428 for action in actions['g']:
428 for action in actions['g']:
429 f, args, msg = action
429 f, args, msg = action
430 splitstandin = f and lfutil.splitstandin(f)
430 splitstandin = f and lfutil.splitstandin(f)
431 if (splitstandin is not None and
431 if (splitstandin is not None and
432 splitstandin in p1 and splitstandin not in removes):
432 splitstandin in p1 and splitstandin not in removes):
433 # Case 1: normal file in the working copy, largefile in
433 # Case 1: normal file in the working copy, largefile in
434 # the second parent
434 # the second parent
435 lfile = splitstandin
435 lfile = splitstandin
436 standin = f
436 standin = f
437 msg = _('remote turned local normal file %s into a largefile\n'
437 msg = _('remote turned local normal file %s into a largefile\n'
438 'use (l)argefile or keep (n)ormal file?'
438 'use (l)argefile or keep (n)ormal file?'
439 '$$ &Largefile $$ &Normal file') % lfile
439 '$$ &Largefile $$ &Normal file') % lfile
440 if repo.ui.promptchoice(msg, 0) == 0:
440 if repo.ui.promptchoice(msg, 0) == 0:
441 actions['r'].append((lfile, None, msg))
441 actions['r'].append((lfile, None, msg))
442 newglist.append((standin, (p2.flags(standin),), msg))
442 newglist.append((standin, (p2.flags(standin),), msg))
443 else:
443 else:
444 actions['r'].append((standin, None, msg))
444 actions['r'].append((standin, None, msg))
445 elif lfutil.standin(f) in p1 and lfutil.standin(f) not in removes:
445 elif lfutil.standin(f) in p1 and lfutil.standin(f) not in removes:
446 # Case 2: largefile in the working copy, normal file in
446 # Case 2: largefile in the working copy, normal file in
447 # the second parent
447 # the second parent
448 standin = lfutil.standin(f)
448 standin = lfutil.standin(f)
449 lfile = f
449 lfile = f
450 msg = _('remote turned local largefile %s into a normal file\n'
450 msg = _('remote turned local largefile %s into a normal file\n'
451 'keep (l)argefile or use (n)ormal file?'
451 'keep (l)argefile or use (n)ormal file?'
452 '$$ &Largefile $$ &Normal file') % lfile
452 '$$ &Largefile $$ &Normal file') % lfile
453 if repo.ui.promptchoice(msg, 0) == 0:
453 if repo.ui.promptchoice(msg, 0) == 0:
454 if branchmerge:
454 if branchmerge:
455 # largefile can be restored from standin safely
455 # largefile can be restored from standin safely
456 actions['r'].append((lfile, None, msg))
456 actions['r'].append((lfile, None, msg))
457 else:
457 else:
458 # "lfile" should be marked as "removed" without
458 # "lfile" should be marked as "removed" without
459 # removal of itself
459 # removal of itself
460 lfmr.append((lfile, None, msg))
460 lfmr.append((lfile, None, msg))
461
461
462 # linear-merge should treat this largefile as 're-added'
462 # linear-merge should treat this largefile as 're-added'
463 actions['a'].append((standin, None, msg))
463 actions['a'].append((standin, None, msg))
464 else:
464 else:
465 actions['r'].append((standin, None, msg))
465 actions['r'].append((standin, None, msg))
466 newglist.append((lfile, (p2.flags(lfile),), msg))
466 newglist.append((lfile, (p2.flags(lfile),), msg))
467 else:
467 else:
468 newglist.append(action)
468 newglist.append(action)
469
469
470 newglist.sort()
470 newglist.sort()
471 actions['g'] = newglist
471 actions['g'] = newglist
472 if lfmr:
472 if lfmr:
473 lfmr.sort()
473 lfmr.sort()
474 actions['lfmr'] = lfmr
474 actions['lfmr'] = lfmr
475
475
476 return actions
476 return actions
477
477
478 def mergerecordupdates(orig, repo, actions, branchmerge):
478 def mergerecordupdates(orig, repo, actions, branchmerge):
479 if 'lfmr' in actions:
479 if 'lfmr' in actions:
480 # this should be executed before 'orig', to execute 'remove'
480 # this should be executed before 'orig', to execute 'remove'
481 # before all other actions
481 # before all other actions
482 for lfile, args, msg in actions['lfmr']:
482 for lfile, args, msg in actions['lfmr']:
483 repo.dirstate.remove(lfile)
483 repo.dirstate.remove(lfile)
484
484
485 return orig(repo, actions, branchmerge)
485 return orig(repo, actions, branchmerge)
486
486
487
487
488 # Override filemerge to prompt the user about how they wish to merge
488 # Override filemerge to prompt the user about how they wish to merge
489 # largefiles. This will handle identical edits without prompting the user.
489 # largefiles. This will handle identical edits without prompting the user.
490 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca, labels=None):
490 def overridefilemerge(origfn, repo, mynode, orig, fcd, fco, fca, labels=None):
491 if not lfutil.isstandin(orig):
491 if not lfutil.isstandin(orig):
492 return origfn(repo, mynode, orig, fcd, fco, fca, labels=labels)
492 return origfn(repo, mynode, orig, fcd, fco, fca, labels=labels)
493
493
494 ahash = fca.data().strip().lower()
494 ahash = fca.data().strip().lower()
495 dhash = fcd.data().strip().lower()
495 dhash = fcd.data().strip().lower()
496 ohash = fco.data().strip().lower()
496 ohash = fco.data().strip().lower()
497 if (ohash != ahash and
497 if (ohash != ahash and
498 ohash != dhash and
498 ohash != dhash and
499 (dhash == ahash or
499 (dhash == ahash or
500 repo.ui.promptchoice(
500 repo.ui.promptchoice(
501 _('largefile %s has a merge conflict\nancestor was %s\n'
501 _('largefile %s has a merge conflict\nancestor was %s\n'
502 'keep (l)ocal %s or\ntake (o)ther %s?'
502 'keep (l)ocal %s or\ntake (o)ther %s?'
503 '$$ &Local $$ &Other') %
503 '$$ &Local $$ &Other') %
504 (lfutil.splitstandin(orig), ahash, dhash, ohash),
504 (lfutil.splitstandin(orig), ahash, dhash, ohash),
505 0) == 1)):
505 0) == 1)):
506 repo.wwrite(fcd.path(), fco.data(), fco.flags())
506 repo.wwrite(fcd.path(), fco.data(), fco.flags())
507 return 0
507 return 0
508
508
509 # Copy first changes the matchers to match standins instead of
509 # Copy first changes the matchers to match standins instead of
510 # largefiles. Then it overrides util.copyfile in that function it
510 # largefiles. Then it overrides util.copyfile in that function it
511 # checks if the destination largefile already exists. It also keeps a
511 # checks if the destination largefile already exists. It also keeps a
512 # list of copied files so that the largefiles can be copied and the
512 # list of copied files so that the largefiles can be copied and the
513 # dirstate updated.
513 # dirstate updated.
514 def overridecopy(orig, ui, repo, pats, opts, rename=False):
514 def overridecopy(orig, ui, repo, pats, opts, rename=False):
515 # doesn't remove largefile on rename
515 # doesn't remove largefile on rename
516 if len(pats) < 2:
516 if len(pats) < 2:
517 # this isn't legal, let the original function deal with it
517 # this isn't legal, let the original function deal with it
518 return orig(ui, repo, pats, opts, rename)
518 return orig(ui, repo, pats, opts, rename)
519
519
520 def makestandin(relpath):
520 def makestandin(relpath):
521 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
521 path = pathutil.canonpath(repo.root, repo.getcwd(), relpath)
522 return os.path.join(repo.wjoin(lfutil.standin(path)))
522 return os.path.join(repo.wjoin(lfutil.standin(path)))
523
523
524 fullpats = scmutil.expandpats(pats)
524 fullpats = scmutil.expandpats(pats)
525 dest = fullpats[-1]
525 dest = fullpats[-1]
526
526
527 if os.path.isdir(dest):
527 if os.path.isdir(dest):
528 if not os.path.isdir(makestandin(dest)):
528 if not os.path.isdir(makestandin(dest)):
529 os.makedirs(makestandin(dest))
529 os.makedirs(makestandin(dest))
530 # This could copy both lfiles and normal files in one command,
530 # This could copy both lfiles and normal files in one command,
531 # but we don't want to do that. First replace their matcher to
531 # but we don't want to do that. First replace their matcher to
532 # only match normal files and run it, then replace it to just
532 # only match normal files and run it, then replace it to just
533 # match largefiles and run it again.
533 # match largefiles and run it again.
534 nonormalfiles = False
534 nonormalfiles = False
535 nolfiles = False
535 nolfiles = False
536 installnormalfilesmatchfn(repo[None].manifest())
536 installnormalfilesmatchfn(repo[None].manifest())
537 try:
537 try:
538 try:
538 try:
539 result = orig(ui, repo, pats, opts, rename)
539 result = orig(ui, repo, pats, opts, rename)
540 except util.Abort, e:
540 except util.Abort, e:
541 if str(e) != _('no files to copy'):
541 if str(e) != _('no files to copy'):
542 raise e
542 raise e
543 else:
543 else:
544 nonormalfiles = True
544 nonormalfiles = True
545 result = 0
545 result = 0
546 finally:
546 finally:
547 restorematchfn()
547 restorematchfn()
548
548
549 # The first rename can cause our current working directory to be removed.
549 # The first rename can cause our current working directory to be removed.
550 # In that case there is nothing left to copy/rename so just quit.
550 # In that case there is nothing left to copy/rename so just quit.
551 try:
551 try:
552 repo.getcwd()
552 repo.getcwd()
553 except OSError:
553 except OSError:
554 return result
554 return result
555
555
556 try:
556 try:
557 try:
557 try:
558 # When we call orig below it creates the standins but we don't add
558 # When we call orig below it creates the standins but we don't add
559 # them to the dir state until later so lock during that time.
559 # them to the dir state until later so lock during that time.
560 wlock = repo.wlock()
560 wlock = repo.wlock()
561
561
562 manifest = repo[None].manifest()
562 manifest = repo[None].manifest()
563 def overridematch(ctx, pats=[], opts={}, globbed=False,
563 def overridematch(ctx, pats=[], opts={}, globbed=False,
564 default='relpath'):
564 default='relpath'):
565 newpats = []
565 newpats = []
566 # The patterns were previously mangled to add the standin
566 # The patterns were previously mangled to add the standin
567 # directory; we need to remove that now
567 # directory; we need to remove that now
568 for pat in pats:
568 for pat in pats:
569 if match_.patkind(pat) is None and lfutil.shortname in pat:
569 if match_.patkind(pat) is None and lfutil.shortname in pat:
570 newpats.append(pat.replace(lfutil.shortname, ''))
570 newpats.append(pat.replace(lfutil.shortname, ''))
571 else:
571 else:
572 newpats.append(pat)
572 newpats.append(pat)
573 match = oldmatch(ctx, newpats, opts, globbed, default)
573 match = oldmatch(ctx, newpats, opts, globbed, default)
574 m = copy.copy(match)
574 m = copy.copy(match)
575 lfile = lambda f: lfutil.standin(f) in manifest
575 lfile = lambda f: lfutil.standin(f) in manifest
576 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
576 m._files = [lfutil.standin(f) for f in m._files if lfile(f)]
577 m._fmap = set(m._files)
577 m._fmap = set(m._files)
578 origmatchfn = m.matchfn
578 origmatchfn = m.matchfn
579 m.matchfn = lambda f: (lfutil.isstandin(f) and
579 m.matchfn = lambda f: (lfutil.isstandin(f) and
580 (f in manifest) and
580 (f in manifest) and
581 origmatchfn(lfutil.splitstandin(f)) or
581 origmatchfn(lfutil.splitstandin(f)) or
582 None)
582 None)
583 return m
583 return m
584 oldmatch = installmatchfn(overridematch)
584 oldmatch = installmatchfn(overridematch)
585 listpats = []
585 listpats = []
586 for pat in pats:
586 for pat in pats:
587 if match_.patkind(pat) is not None:
587 if match_.patkind(pat) is not None:
588 listpats.append(pat)
588 listpats.append(pat)
589 else:
589 else:
590 listpats.append(makestandin(pat))
590 listpats.append(makestandin(pat))
591
591
592 try:
592 try:
593 origcopyfile = util.copyfile
593 origcopyfile = util.copyfile
594 copiedfiles = []
594 copiedfiles = []
595 def overridecopyfile(src, dest):
595 def overridecopyfile(src, dest):
596 if (lfutil.shortname in src and
596 if (lfutil.shortname in src and
597 dest.startswith(repo.wjoin(lfutil.shortname))):
597 dest.startswith(repo.wjoin(lfutil.shortname))):
598 destlfile = dest.replace(lfutil.shortname, '')
598 destlfile = dest.replace(lfutil.shortname, '')
599 if not opts['force'] and os.path.exists(destlfile):
599 if not opts['force'] and os.path.exists(destlfile):
600 raise IOError('',
600 raise IOError('',
601 _('destination largefile already exists'))
601 _('destination largefile already exists'))
602 copiedfiles.append((src, dest))
602 copiedfiles.append((src, dest))
603 origcopyfile(src, dest)
603 origcopyfile(src, dest)
604
604
605 util.copyfile = overridecopyfile
605 util.copyfile = overridecopyfile
606 result += orig(ui, repo, listpats, opts, rename)
606 result += orig(ui, repo, listpats, opts, rename)
607 finally:
607 finally:
608 util.copyfile = origcopyfile
608 util.copyfile = origcopyfile
609
609
610 lfdirstate = lfutil.openlfdirstate(ui, repo)
610 lfdirstate = lfutil.openlfdirstate(ui, repo)
611 for (src, dest) in copiedfiles:
611 for (src, dest) in copiedfiles:
612 if (lfutil.shortname in src and
612 if (lfutil.shortname in src and
613 dest.startswith(repo.wjoin(lfutil.shortname))):
613 dest.startswith(repo.wjoin(lfutil.shortname))):
614 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
614 srclfile = src.replace(repo.wjoin(lfutil.standin('')), '')
615 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
615 destlfile = dest.replace(repo.wjoin(lfutil.standin('')), '')
616 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
616 destlfiledir = os.path.dirname(repo.wjoin(destlfile)) or '.'
617 if not os.path.isdir(destlfiledir):
617 if not os.path.isdir(destlfiledir):
618 os.makedirs(destlfiledir)
618 os.makedirs(destlfiledir)
619 if rename:
619 if rename:
620 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
620 os.rename(repo.wjoin(srclfile), repo.wjoin(destlfile))
621
621
622 # The file is gone, but this deletes any empty parent
622 # The file is gone, but this deletes any empty parent
623 # directories as a side-effect.
623 # directories as a side-effect.
624 util.unlinkpath(repo.wjoin(srclfile), True)
624 util.unlinkpath(repo.wjoin(srclfile), True)
625 lfdirstate.remove(srclfile)
625 lfdirstate.remove(srclfile)
626 else:
626 else:
627 util.copyfile(repo.wjoin(srclfile),
627 util.copyfile(repo.wjoin(srclfile),
628 repo.wjoin(destlfile))
628 repo.wjoin(destlfile))
629
629
630 lfdirstate.add(destlfile)
630 lfdirstate.add(destlfile)
631 lfdirstate.write()
631 lfdirstate.write()
632 except util.Abort, e:
632 except util.Abort, e:
633 if str(e) != _('no files to copy'):
633 if str(e) != _('no files to copy'):
634 raise e
634 raise e
635 else:
635 else:
636 nolfiles = True
636 nolfiles = True
637 finally:
637 finally:
638 restorematchfn()
638 restorematchfn()
639 wlock.release()
639 wlock.release()
640
640
641 if nolfiles and nonormalfiles:
641 if nolfiles and nonormalfiles:
642 raise util.Abort(_('no files to copy'))
642 raise util.Abort(_('no files to copy'))
643
643
644 return result
644 return result
645
645
646 # When the user calls revert, we have to be careful to not revert any
646 # When the user calls revert, we have to be careful to not revert any
647 # changes to other largefiles accidentally. This means we have to keep
647 # changes to other largefiles accidentally. This means we have to keep
648 # track of the largefiles that are being reverted so we only pull down
648 # track of the largefiles that are being reverted so we only pull down
649 # the necessary largefiles.
649 # the necessary largefiles.
650 #
650 #
651 # Standins are only updated (to match the hash of largefiles) before
651 # Standins are only updated (to match the hash of largefiles) before
652 # commits. Update the standins then run the original revert, changing
652 # commits. Update the standins then run the original revert, changing
653 # the matcher to hit standins instead of largefiles. Based on the
653 # the matcher to hit standins instead of largefiles. Based on the
654 # resulting standins update the largefiles.
654 # resulting standins update the largefiles.
655 def overriderevert(orig, ui, repo, *pats, **opts):
655 def overriderevert(orig, ui, repo, *pats, **opts):
656 # Because we put the standins in a bad state (by updating them)
656 # Because we put the standins in a bad state (by updating them)
657 # and then return them to a correct state we need to lock to
657 # and then return them to a correct state we need to lock to
658 # prevent others from changing them in their incorrect state.
658 # prevent others from changing them in their incorrect state.
659 wlock = repo.wlock()
659 wlock = repo.wlock()
660 try:
660 try:
661 lfdirstate = lfutil.openlfdirstate(ui, repo)
661 lfdirstate = lfutil.openlfdirstate(ui, repo)
662 s = lfutil.lfdirstatestatus(lfdirstate, repo)
662 s = lfutil.lfdirstatestatus(lfdirstate, repo)
663 lfdirstate.write()
663 lfdirstate.write()
664 for lfile in s.modified:
664 for lfile in s.modified:
665 lfutil.updatestandin(repo, lfutil.standin(lfile))
665 lfutil.updatestandin(repo, lfutil.standin(lfile))
666 for lfile in s.deleted:
666 for lfile in s.deleted:
667 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
667 if (os.path.exists(repo.wjoin(lfutil.standin(lfile)))):
668 os.unlink(repo.wjoin(lfutil.standin(lfile)))
668 os.unlink(repo.wjoin(lfutil.standin(lfile)))
669
669
670 oldstandins = lfutil.getstandinsstate(repo)
670 oldstandins = lfutil.getstandinsstate(repo)
671
671
672 def overridematch(ctx, pats=[], opts={}, globbed=False,
672 def overridematch(ctx, pats=[], opts={}, globbed=False,
673 default='relpath'):
673 default='relpath'):
674 match = oldmatch(ctx, pats, opts, globbed, default)
674 match = oldmatch(ctx, pats, opts, globbed, default)
675 m = copy.copy(match)
675 m = copy.copy(match)
676 def tostandin(f):
676 def tostandin(f):
677 if lfutil.standin(f) in ctx:
677 if lfutil.standin(f) in ctx:
678 return lfutil.standin(f)
678 return lfutil.standin(f)
679 elif lfutil.standin(f) in repo[None]:
679 elif lfutil.standin(f) in repo[None]:
680 return None
680 return None
681 return f
681 return f
682 m._files = [tostandin(f) for f in m._files]
682 m._files = [tostandin(f) for f in m._files]
683 m._files = [f for f in m._files if f is not None]
683 m._files = [f for f in m._files if f is not None]
684 m._fmap = set(m._files)
684 m._fmap = set(m._files)
685 origmatchfn = m.matchfn
685 origmatchfn = m.matchfn
686 def matchfn(f):
686 def matchfn(f):
687 if lfutil.isstandin(f):
687 if lfutil.isstandin(f):
688 return (origmatchfn(lfutil.splitstandin(f)) and
688 return (origmatchfn(lfutil.splitstandin(f)) and
689 (f in repo[None] or f in ctx))
689 (f in repo[None] or f in ctx))
690 return origmatchfn(f)
690 return origmatchfn(f)
691 m.matchfn = matchfn
691 m.matchfn = matchfn
692 return m
692 return m
693 oldmatch = installmatchfn(overridematch)
693 oldmatch = installmatchfn(overridematch)
694 try:
694 try:
695 orig(ui, repo, *pats, **opts)
695 orig(ui, repo, *pats, **opts)
696 finally:
696 finally:
697 restorematchfn()
697 restorematchfn()
698
698
699 newstandins = lfutil.getstandinsstate(repo)
699 newstandins = lfutil.getstandinsstate(repo)
700 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
700 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
701 # lfdirstate should be 'normallookup'-ed for updated files,
701 # lfdirstate should be 'normallookup'-ed for updated files,
702 # because reverting doesn't touch dirstate for 'normal' files
702 # because reverting doesn't touch dirstate for 'normal' files
703 # when target revision is explicitly specified: in such case,
703 # when target revision is explicitly specified: in such case,
704 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
704 # 'n' and valid timestamp in dirstate doesn't ensure 'clean'
705 # of target (standin) file.
705 # of target (standin) file.
706 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
706 lfcommands.updatelfiles(ui, repo, filelist, printmessage=False,
707 normallookup=True)
707 normallookup=True)
708
708
709 finally:
709 finally:
710 wlock.release()
710 wlock.release()
711
711
712 # after pulling changesets, we need to take some extra care to get
712 # after pulling changesets, we need to take some extra care to get
713 # largefiles updated remotely
713 # largefiles updated remotely
714 def overridepull(orig, ui, repo, source=None, **opts):
714 def overridepull(orig, ui, repo, source=None, **opts):
715 revsprepull = len(repo)
715 revsprepull = len(repo)
716 if not source:
716 if not source:
717 source = 'default'
717 source = 'default'
718 repo.lfpullsource = source
718 repo.lfpullsource = source
719 result = orig(ui, repo, source, **opts)
719 result = orig(ui, repo, source, **opts)
720 revspostpull = len(repo)
720 revspostpull = len(repo)
721 lfrevs = opts.get('lfrev', [])
721 lfrevs = opts.get('lfrev', [])
722 if opts.get('all_largefiles'):
722 if opts.get('all_largefiles'):
723 lfrevs.append('pulled()')
723 lfrevs.append('pulled()')
724 if lfrevs and revspostpull > revsprepull:
724 if lfrevs and revspostpull > revsprepull:
725 numcached = 0
725 numcached = 0
726 repo.firstpulled = revsprepull # for pulled() revset expression
726 repo.firstpulled = revsprepull # for pulled() revset expression
727 try:
727 try:
728 for rev in scmutil.revrange(repo, lfrevs):
728 for rev in scmutil.revrange(repo, lfrevs):
729 ui.note(_('pulling largefiles for revision %s\n') % rev)
729 ui.note(_('pulling largefiles for revision %s\n') % rev)
730 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
730 (cached, missing) = lfcommands.cachelfiles(ui, repo, rev)
731 numcached += len(cached)
731 numcached += len(cached)
732 finally:
732 finally:
733 del repo.firstpulled
733 del repo.firstpulled
734 ui.status(_("%d largefiles cached\n") % numcached)
734 ui.status(_("%d largefiles cached\n") % numcached)
735 return result
735 return result
736
736
737 def pulledrevsetsymbol(repo, subset, x):
737 def pulledrevsetsymbol(repo, subset, x):
738 """``pulled()``
738 """``pulled()``
739 Changesets that just has been pulled.
739 Changesets that just has been pulled.
740
740
741 Only available with largefiles from pull --lfrev expressions.
741 Only available with largefiles from pull --lfrev expressions.
742
742
743 .. container:: verbose
743 .. container:: verbose
744
744
745 Some examples:
745 Some examples:
746
746
747 - pull largefiles for all new changesets::
747 - pull largefiles for all new changesets::
748
748
749 hg pull -lfrev "pulled()"
749 hg pull -lfrev "pulled()"
750
750
751 - pull largefiles for all new branch heads::
751 - pull largefiles for all new branch heads::
752
752
753 hg pull -lfrev "head(pulled()) and not closed()"
753 hg pull -lfrev "head(pulled()) and not closed()"
754
754
755 """
755 """
756
756
757 try:
757 try:
758 firstpulled = repo.firstpulled
758 firstpulled = repo.firstpulled
759 except AttributeError:
759 except AttributeError:
760 raise util.Abort(_("pulled() only available in --lfrev"))
760 raise util.Abort(_("pulled() only available in --lfrev"))
761 return revset.baseset([r for r in subset if r >= firstpulled])
761 return revset.baseset([r for r in subset if r >= firstpulled])
762
762
763 def overrideclone(orig, ui, source, dest=None, **opts):
763 def overrideclone(orig, ui, source, dest=None, **opts):
764 d = dest
764 d = dest
765 if d is None:
765 if d is None:
766 d = hg.defaultdest(source)
766 d = hg.defaultdest(source)
767 if opts.get('all_largefiles') and not hg.islocal(d):
767 if opts.get('all_largefiles') and not hg.islocal(d):
768 raise util.Abort(_(
768 raise util.Abort(_(
769 '--all-largefiles is incompatible with non-local destination %s') %
769 '--all-largefiles is incompatible with non-local destination %s') %
770 d)
770 d)
771
771
772 return orig(ui, source, dest, **opts)
772 return orig(ui, source, dest, **opts)
773
773
774 def hgclone(orig, ui, opts, *args, **kwargs):
774 def hgclone(orig, ui, opts, *args, **kwargs):
775 result = orig(ui, opts, *args, **kwargs)
775 result = orig(ui, opts, *args, **kwargs)
776
776
777 if result is not None:
777 if result is not None:
778 sourcerepo, destrepo = result
778 sourcerepo, destrepo = result
779 repo = destrepo.local()
779 repo = destrepo.local()
780
780
781 # Caching is implicitly limited to 'rev' option, since the dest repo was
781 # Caching is implicitly limited to 'rev' option, since the dest repo was
782 # truncated at that point. The user may expect a download count with
782 # truncated at that point. The user may expect a download count with
783 # this option, so attempt whether or not this is a largefile repo.
783 # this option, so attempt whether or not this is a largefile repo.
784 if opts.get('all_largefiles'):
784 if opts.get('all_largefiles'):
785 success, missing = lfcommands.downloadlfiles(ui, repo, None)
785 success, missing = lfcommands.downloadlfiles(ui, repo, None)
786
786
787 if missing != 0:
787 if missing != 0:
788 return None
788 return None
789
789
790 return result
790 return result
791
791
792 def overriderebase(orig, ui, repo, **opts):
792 def overriderebase(orig, ui, repo, **opts):
793 resuming = opts.get('continue')
793 resuming = opts.get('continue')
794 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
794 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
795 repo._lfstatuswriters.append(lambda *msg, **opts: None)
795 repo._lfstatuswriters.append(lambda *msg, **opts: None)
796 try:
796 try:
797 return orig(ui, repo, **opts)
797 return orig(ui, repo, **opts)
798 finally:
798 finally:
799 repo._lfstatuswriters.pop()
799 repo._lfstatuswriters.pop()
800 repo._lfcommithooks.pop()
800 repo._lfcommithooks.pop()
801
801
802 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
802 def overridearchive(orig, repo, dest, node, kind, decode=True, matchfn=None,
803 prefix=None, mtime=None, subrepos=None):
803 prefix=None, mtime=None, subrepos=None):
804 # No need to lock because we are only reading history and
804 # No need to lock because we are only reading history and
805 # largefile caches, neither of which are modified.
805 # largefile caches, neither of which are modified.
806 lfcommands.cachelfiles(repo.ui, repo, node)
806 lfcommands.cachelfiles(repo.ui, repo, node)
807
807
808 if kind not in archival.archivers:
808 if kind not in archival.archivers:
809 raise util.Abort(_("unknown archive type '%s'") % kind)
809 raise util.Abort(_("unknown archive type '%s'") % kind)
810
810
811 ctx = repo[node]
811 ctx = repo[node]
812
812
813 if kind == 'files':
813 if kind == 'files':
814 if prefix:
814 if prefix:
815 raise util.Abort(
815 raise util.Abort(
816 _('cannot give prefix when archiving to files'))
816 _('cannot give prefix when archiving to files'))
817 else:
817 else:
818 prefix = archival.tidyprefix(dest, kind, prefix)
818 prefix = archival.tidyprefix(dest, kind, prefix)
819
819
820 def write(name, mode, islink, getdata):
820 def write(name, mode, islink, getdata):
821 if matchfn and not matchfn(name):
821 if matchfn and not matchfn(name):
822 return
822 return
823 data = getdata()
823 data = getdata()
824 if decode:
824 if decode:
825 data = repo.wwritedata(name, data)
825 data = repo.wwritedata(name, data)
826 archiver.addfile(prefix + name, mode, islink, data)
826 archiver.addfile(prefix + name, mode, islink, data)
827
827
828 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
828 archiver = archival.archivers[kind](dest, mtime or ctx.date()[0])
829
829
830 if repo.ui.configbool("ui", "archivemeta", True):
830 if repo.ui.configbool("ui", "archivemeta", True):
831 def metadata():
831 def metadata():
832 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
832 base = 'repo: %s\nnode: %s\nbranch: %s\n' % (
833 hex(repo.changelog.node(0)), hex(node), ctx.branch())
833 hex(repo.changelog.node(0)), hex(node), ctx.branch())
834
834
835 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
835 tags = ''.join('tag: %s\n' % t for t in ctx.tags()
836 if repo.tagtype(t) == 'global')
836 if repo.tagtype(t) == 'global')
837 if not tags:
837 if not tags:
838 repo.ui.pushbuffer()
838 repo.ui.pushbuffer()
839 opts = {'template': '{latesttag}\n{latesttagdistance}',
839 opts = {'template': '{latesttag}\n{latesttagdistance}',
840 'style': '', 'patch': None, 'git': None}
840 'style': '', 'patch': None, 'git': None}
841 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
841 cmdutil.show_changeset(repo.ui, repo, opts).show(ctx)
842 ltags, dist = repo.ui.popbuffer().split('\n')
842 ltags, dist = repo.ui.popbuffer().split('\n')
843 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
843 tags = ''.join('latesttag: %s\n' % t for t in ltags.split(':'))
844 tags += 'latesttagdistance: %s\n' % dist
844 tags += 'latesttagdistance: %s\n' % dist
845
845
846 return base + tags
846 return base + tags
847
847
848 write('.hg_archival.txt', 0644, False, metadata)
848 write('.hg_archival.txt', 0644, False, metadata)
849
849
850 for f in ctx:
850 for f in ctx:
851 ff = ctx.flags(f)
851 ff = ctx.flags(f)
852 getdata = ctx[f].data
852 getdata = ctx[f].data
853 if lfutil.isstandin(f):
853 if lfutil.isstandin(f):
854 path = lfutil.findfile(repo, getdata().strip())
854 path = lfutil.findfile(repo, getdata().strip())
855 if path is None:
855 if path is None:
856 raise util.Abort(
856 raise util.Abort(
857 _('largefile %s not found in repo store or system cache')
857 _('largefile %s not found in repo store or system cache')
858 % lfutil.splitstandin(f))
858 % lfutil.splitstandin(f))
859 f = lfutil.splitstandin(f)
859 f = lfutil.splitstandin(f)
860
860
861 def getdatafn():
861 def getdatafn():
862 fd = None
862 fd = None
863 try:
863 try:
864 fd = open(path, 'rb')
864 fd = open(path, 'rb')
865 return fd.read()
865 return fd.read()
866 finally:
866 finally:
867 if fd:
867 if fd:
868 fd.close()
868 fd.close()
869
869
870 getdata = getdatafn
870 getdata = getdatafn
871 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
871 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
872
872
873 if subrepos:
873 if subrepos:
874 for subpath in sorted(ctx.substate):
874 for subpath in sorted(ctx.substate):
875 sub = ctx.sub(subpath)
875 sub = ctx.sub(subpath)
876 submatch = match_.narrowmatcher(subpath, matchfn)
876 submatch = match_.narrowmatcher(subpath, matchfn)
877 sub.archive(repo.ui, archiver, prefix, submatch)
877 sub.archive(repo.ui, archiver, prefix, submatch)
878
878
879 archiver.done()
879 archiver.done()
880
880
881 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
881 def hgsubrepoarchive(orig, repo, ui, archiver, prefix, match=None):
882 repo._get(repo._state + ('hg',))
882 repo._get(repo._state + ('hg',))
883 rev = repo._state[1]
883 rev = repo._state[1]
884 ctx = repo._repo[rev]
884 ctx = repo._repo[rev]
885
885
886 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
886 lfcommands.cachelfiles(ui, repo._repo, ctx.node())
887
887
888 def write(name, mode, islink, getdata):
888 def write(name, mode, islink, getdata):
889 # At this point, the standin has been replaced with the largefile name,
889 # At this point, the standin has been replaced with the largefile name,
890 # so the normal matcher works here without the lfutil variants.
890 # so the normal matcher works here without the lfutil variants.
891 if match and not match(f):
891 if match and not match(f):
892 return
892 return
893 data = getdata()
893 data = getdata()
894
894
895 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
895 archiver.addfile(prefix + repo._path + '/' + name, mode, islink, data)
896
896
897 for f in ctx:
897 for f in ctx:
898 ff = ctx.flags(f)
898 ff = ctx.flags(f)
899 getdata = ctx[f].data
899 getdata = ctx[f].data
900 if lfutil.isstandin(f):
900 if lfutil.isstandin(f):
901 path = lfutil.findfile(repo._repo, getdata().strip())
901 path = lfutil.findfile(repo._repo, getdata().strip())
902 if path is None:
902 if path is None:
903 raise util.Abort(
903 raise util.Abort(
904 _('largefile %s not found in repo store or system cache')
904 _('largefile %s not found in repo store or system cache')
905 % lfutil.splitstandin(f))
905 % lfutil.splitstandin(f))
906 f = lfutil.splitstandin(f)
906 f = lfutil.splitstandin(f)
907
907
908 def getdatafn():
908 def getdatafn():
909 fd = None
909 fd = None
910 try:
910 try:
911 fd = open(os.path.join(prefix, path), 'rb')
911 fd = open(os.path.join(prefix, path), 'rb')
912 return fd.read()
912 return fd.read()
913 finally:
913 finally:
914 if fd:
914 if fd:
915 fd.close()
915 fd.close()
916
916
917 getdata = getdatafn
917 getdata = getdatafn
918
918
919 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
919 write(f, 'x' in ff and 0755 or 0644, 'l' in ff, getdata)
920
920
921 for subpath in sorted(ctx.substate):
921 for subpath in sorted(ctx.substate):
922 sub = ctx.sub(subpath)
922 sub = ctx.sub(subpath)
923 submatch = match_.narrowmatcher(subpath, match)
923 submatch = match_.narrowmatcher(subpath, match)
924 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
924 sub.archive(ui, archiver, os.path.join(prefix, repo._path) + '/',
925 submatch)
925 submatch)
926
926
927 # If a largefile is modified, the change is not reflected in its
927 # If a largefile is modified, the change is not reflected in its
928 # standin until a commit. cmdutil.bailifchanged() raises an exception
928 # standin until a commit. cmdutil.bailifchanged() raises an exception
929 # if the repo has uncommitted changes. Wrap it to also check if
929 # if the repo has uncommitted changes. Wrap it to also check if
930 # largefiles were changed. This is used by bisect and backout.
930 # largefiles were changed. This is used by bisect and backout.
931 def overridebailifchanged(orig, repo):
931 def overridebailifchanged(orig, repo):
932 orig(repo)
932 orig(repo)
933 repo.lfstatus = True
933 repo.lfstatus = True
934 s = repo.status()
934 s = repo.status()
935 repo.lfstatus = False
935 repo.lfstatus = False
936 if s.modified or s.added or s.removed or s.deleted:
936 if s.modified or s.added or s.removed or s.deleted:
937 raise util.Abort(_('uncommitted changes'))
937 raise util.Abort(_('uncommitted changes'))
938
938
939 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
939 # Fetch doesn't use cmdutil.bailifchanged so override it to add the check
940 def overridefetch(orig, ui, repo, *pats, **opts):
940 def overridefetch(orig, ui, repo, *pats, **opts):
941 repo.lfstatus = True
941 repo.lfstatus = True
942 s = repo.status()
942 s = repo.status()
943 repo.lfstatus = False
943 repo.lfstatus = False
944 if s.modified or s.added or s.removed or s.deleted:
944 if s.modified or s.added or s.removed or s.deleted:
945 raise util.Abort(_('uncommitted changes'))
945 raise util.Abort(_('uncommitted changes'))
946 return orig(ui, repo, *pats, **opts)
946 return orig(ui, repo, *pats, **opts)
947
947
948 def overrideforget(orig, ui, repo, *pats, **opts):
948 def overrideforget(orig, ui, repo, *pats, **opts):
949 installnormalfilesmatchfn(repo[None].manifest())
949 installnormalfilesmatchfn(repo[None].manifest())
950 result = orig(ui, repo, *pats, **opts)
950 result = orig(ui, repo, *pats, **opts)
951 restorematchfn()
951 restorematchfn()
952 m = scmutil.match(repo[None], pats, opts)
952 m = scmutil.match(repo[None], pats, opts)
953
953
954 try:
954 try:
955 repo.lfstatus = True
955 repo.lfstatus = True
956 s = repo.status(match=m, clean=True)
956 s = repo.status(match=m, clean=True)
957 finally:
957 finally:
958 repo.lfstatus = False
958 repo.lfstatus = False
959 forget = sorted(s.modified + s.added + s.deleted + s.clean)
959 forget = sorted(s.modified + s.added + s.deleted + s.clean)
960 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
960 forget = [f for f in forget if lfutil.standin(f) in repo[None].manifest()]
961
961
962 for f in forget:
962 for f in forget:
963 if lfutil.standin(f) not in repo.dirstate and not \
963 if lfutil.standin(f) not in repo.dirstate and not \
964 os.path.isdir(m.rel(lfutil.standin(f))):
964 os.path.isdir(m.rel(lfutil.standin(f))):
965 ui.warn(_('not removing %s: file is already untracked\n')
965 ui.warn(_('not removing %s: file is already untracked\n')
966 % m.rel(f))
966 % m.rel(f))
967 result = 1
967 result = 1
968
968
969 for f in forget:
969 for f in forget:
970 if ui.verbose or not m.exact(f):
970 if ui.verbose or not m.exact(f):
971 ui.status(_('removing %s\n') % m.rel(f))
971 ui.status(_('removing %s\n') % m.rel(f))
972
972
973 # Need to lock because standin files are deleted then removed from the
973 # Need to lock because standin files are deleted then removed from the
974 # repository and we could race in-between.
974 # repository and we could race in-between.
975 wlock = repo.wlock()
975 wlock = repo.wlock()
976 try:
976 try:
977 lfdirstate = lfutil.openlfdirstate(ui, repo)
977 lfdirstate = lfutil.openlfdirstate(ui, repo)
978 for f in forget:
978 for f in forget:
979 if lfdirstate[f] == 'a':
979 if lfdirstate[f] == 'a':
980 lfdirstate.drop(f)
980 lfdirstate.drop(f)
981 else:
981 else:
982 lfdirstate.remove(f)
982 lfdirstate.remove(f)
983 lfdirstate.write()
983 lfdirstate.write()
984 standins = [lfutil.standin(f) for f in forget]
984 standins = [lfutil.standin(f) for f in forget]
985 for f in standins:
985 for f in standins:
986 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
986 util.unlinkpath(repo.wjoin(f), ignoremissing=True)
987 repo[None].forget(standins)
987 repo[None].forget(standins)
988 finally:
988 finally:
989 wlock.release()
989 wlock.release()
990
990
991 return result
991 return result
992
992
993 def _getoutgoings(repo, other, missing, addfunc):
993 def _getoutgoings(repo, other, missing, addfunc):
994 """get pairs of filename and largefile hash in outgoing revisions
994 """get pairs of filename and largefile hash in outgoing revisions
995 in 'missing'.
995 in 'missing'.
996
996
997 largefiles already existing on 'other' repository are ignored.
997 largefiles already existing on 'other' repository are ignored.
998
998
999 'addfunc' is invoked with each unique pairs of filename and
999 'addfunc' is invoked with each unique pairs of filename and
1000 largefile hash value.
1000 largefile hash value.
1001 """
1001 """
1002 knowns = set()
1002 knowns = set()
1003 lfhashes = set()
1003 lfhashes = set()
1004 def dedup(fn, lfhash):
1004 def dedup(fn, lfhash):
1005 k = (fn, lfhash)
1005 k = (fn, lfhash)
1006 if k not in knowns:
1006 if k not in knowns:
1007 knowns.add(k)
1007 knowns.add(k)
1008 lfhashes.add(lfhash)
1008 lfhashes.add(lfhash)
1009 lfutil.getlfilestoupload(repo, missing, dedup)
1009 lfutil.getlfilestoupload(repo, missing, dedup)
1010 if lfhashes:
1010 if lfhashes:
1011 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1011 lfexists = basestore._openstore(repo, other).exists(lfhashes)
1012 for fn, lfhash in knowns:
1012 for fn, lfhash in knowns:
1013 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1013 if not lfexists[lfhash]: # lfhash doesn't exist on "other"
1014 addfunc(fn, lfhash)
1014 addfunc(fn, lfhash)
1015
1015
1016 def outgoinghook(ui, repo, other, opts, missing):
1016 def outgoinghook(ui, repo, other, opts, missing):
1017 if opts.pop('large', None):
1017 if opts.pop('large', None):
1018 lfhashes = set()
1018 lfhashes = set()
1019 if ui.debugflag:
1019 if ui.debugflag:
1020 toupload = {}
1020 toupload = {}
1021 def addfunc(fn, lfhash):
1021 def addfunc(fn, lfhash):
1022 if fn not in toupload:
1022 if fn not in toupload:
1023 toupload[fn] = []
1023 toupload[fn] = []
1024 toupload[fn].append(lfhash)
1024 toupload[fn].append(lfhash)
1025 lfhashes.add(lfhash)
1025 lfhashes.add(lfhash)
1026 def showhashes(fn):
1026 def showhashes(fn):
1027 for lfhash in sorted(toupload[fn]):
1027 for lfhash in sorted(toupload[fn]):
1028 ui.debug(' %s\n' % (lfhash))
1028 ui.debug(' %s\n' % (lfhash))
1029 else:
1029 else:
1030 toupload = set()
1030 toupload = set()
1031 def addfunc(fn, lfhash):
1031 def addfunc(fn, lfhash):
1032 toupload.add(fn)
1032 toupload.add(fn)
1033 lfhashes.add(lfhash)
1033 lfhashes.add(lfhash)
1034 def showhashes(fn):
1034 def showhashes(fn):
1035 pass
1035 pass
1036 _getoutgoings(repo, other, missing, addfunc)
1036 _getoutgoings(repo, other, missing, addfunc)
1037
1037
1038 if not toupload:
1038 if not toupload:
1039 ui.status(_('largefiles: no files to upload\n'))
1039 ui.status(_('largefiles: no files to upload\n'))
1040 else:
1040 else:
1041 ui.status(_('largefiles to upload (%d entities):\n')
1041 ui.status(_('largefiles to upload (%d entities):\n')
1042 % (len(lfhashes)))
1042 % (len(lfhashes)))
1043 for file in sorted(toupload):
1043 for file in sorted(toupload):
1044 ui.status(lfutil.splitstandin(file) + '\n')
1044 ui.status(lfutil.splitstandin(file) + '\n')
1045 showhashes(file)
1045 showhashes(file)
1046 ui.status('\n')
1046 ui.status('\n')
1047
1047
1048 def summaryremotehook(ui, repo, opts, changes):
1048 def summaryremotehook(ui, repo, opts, changes):
1049 largeopt = opts.get('large', False)
1049 largeopt = opts.get('large', False)
1050 if changes is None:
1050 if changes is None:
1051 if largeopt:
1051 if largeopt:
1052 return (False, True) # only outgoing check is needed
1052 return (False, True) # only outgoing check is needed
1053 else:
1053 else:
1054 return (False, False)
1054 return (False, False)
1055 elif largeopt:
1055 elif largeopt:
1056 url, branch, peer, outgoing = changes[1]
1056 url, branch, peer, outgoing = changes[1]
1057 if peer is None:
1057 if peer is None:
1058 # i18n: column positioning for "hg summary"
1058 # i18n: column positioning for "hg summary"
1059 ui.status(_('largefiles: (no remote repo)\n'))
1059 ui.status(_('largefiles: (no remote repo)\n'))
1060 return
1060 return
1061
1061
1062 toupload = set()
1062 toupload = set()
1063 lfhashes = set()
1063 lfhashes = set()
1064 def addfunc(fn, lfhash):
1064 def addfunc(fn, lfhash):
1065 toupload.add(fn)
1065 toupload.add(fn)
1066 lfhashes.add(lfhash)
1066 lfhashes.add(lfhash)
1067 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1067 _getoutgoings(repo, peer, outgoing.missing, addfunc)
1068
1068
1069 if not toupload:
1069 if not toupload:
1070 # i18n: column positioning for "hg summary"
1070 # i18n: column positioning for "hg summary"
1071 ui.status(_('largefiles: (no files to upload)\n'))
1071 ui.status(_('largefiles: (no files to upload)\n'))
1072 else:
1072 else:
1073 # i18n: column positioning for "hg summary"
1073 # i18n: column positioning for "hg summary"
1074 ui.status(_('largefiles: %d entities for %d files to upload\n')
1074 ui.status(_('largefiles: %d entities for %d files to upload\n')
1075 % (len(lfhashes), len(toupload)))
1075 % (len(lfhashes), len(toupload)))
1076
1076
1077 def overridesummary(orig, ui, repo, *pats, **opts):
1077 def overridesummary(orig, ui, repo, *pats, **opts):
1078 try:
1078 try:
1079 repo.lfstatus = True
1079 repo.lfstatus = True
1080 orig(ui, repo, *pats, **opts)
1080 orig(ui, repo, *pats, **opts)
1081 finally:
1081 finally:
1082 repo.lfstatus = False
1082 repo.lfstatus = False
1083
1083
1084 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1084 def scmutiladdremove(orig, repo, pats=[], opts={}, dry_run=None,
1085 similarity=None):
1085 similarity=None):
1086 if not lfutil.islfilesrepo(repo):
1086 if not lfutil.islfilesrepo(repo):
1087 return orig(repo, pats, opts, dry_run, similarity)
1087 return orig(repo, pats, opts, dry_run, similarity)
1088 # Get the list of missing largefiles so we can remove them
1088 # Get the list of missing largefiles so we can remove them
1089 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1089 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1090 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1090 unsure, s = lfdirstate.status(match_.always(repo.root, repo.getcwd()), [],
1091 False, False, False)
1091 False, False, False)
1092
1092
1093 # Call into the normal remove code, but the removing of the standin, we want
1093 # Call into the normal remove code, but the removing of the standin, we want
1094 # to have handled by original addremove. Monkey patching here makes sure
1094 # to have handled by original addremove. Monkey patching here makes sure
1095 # we don't remove the standin in the largefiles code, preventing a very
1095 # we don't remove the standin in the largefiles code, preventing a very
1096 # confused state later.
1096 # confused state later.
1097 if s.deleted:
1097 if s.deleted:
1098 m = [repo.wjoin(f) for f in s.deleted]
1098 m = [repo.wjoin(f) for f in s.deleted]
1099 removelargefiles(repo.ui, repo, True, *m, **opts)
1099 removelargefiles(repo.ui, repo, True, *m, **opts)
1100 # Call into the normal add code, and any files that *should* be added as
1100 # Call into the normal add code, and any files that *should* be added as
1101 # largefiles will be
1101 # largefiles will be
1102 addlargefiles(repo.ui, repo, *pats, **opts)
1102 addlargefiles(repo.ui, repo, *pats, **opts)
1103 # Now that we've handled largefiles, hand off to the original addremove
1103 # Now that we've handled largefiles, hand off to the original addremove
1104 # function to take care of the rest. Make sure it doesn't do anything with
1104 # function to take care of the rest. Make sure it doesn't do anything with
1105 # largefiles by installing a matcher that will ignore them.
1105 # largefiles by installing a matcher that will ignore them.
1106 installnormalfilesmatchfn(repo[None].manifest())
1106 installnormalfilesmatchfn(repo[None].manifest())
1107 result = orig(repo, pats, opts, dry_run, similarity)
1107 result = orig(repo, pats, opts, dry_run, similarity)
1108 restorematchfn()
1108 restorematchfn()
1109 return result
1109 return result
1110
1110
1111 # Calling purge with --all will cause the largefiles to be deleted.
1111 # Calling purge with --all will cause the largefiles to be deleted.
1112 # Override repo.status to prevent this from happening.
1112 # Override repo.status to prevent this from happening.
1113 def overridepurge(orig, ui, repo, *dirs, **opts):
1113 def overridepurge(orig, ui, repo, *dirs, **opts):
1114 # XXX large file status is buggy when used on repo proxy.
1114 # XXX large file status is buggy when used on repo proxy.
1115 # XXX this needs to be investigate.
1115 # XXX this needs to be investigate.
1116 repo = repo.unfiltered()
1116 repo = repo.unfiltered()
1117 oldstatus = repo.status
1117 oldstatus = repo.status
1118 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1118 def overridestatus(node1='.', node2=None, match=None, ignored=False,
1119 clean=False, unknown=False, listsubrepos=False):
1119 clean=False, unknown=False, listsubrepos=False):
1120 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1120 r = oldstatus(node1, node2, match, ignored, clean, unknown,
1121 listsubrepos)
1121 listsubrepos)
1122 lfdirstate = lfutil.openlfdirstate(ui, repo)
1122 lfdirstate = lfutil.openlfdirstate(ui, repo)
1123 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1123 unknown = [f for f in r.unknown if lfdirstate[f] == '?']
1124 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1124 ignored = [f for f in r.ignored if lfdirstate[f] == '?']
1125 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1125 return scmutil.status(r.modified, r.added, r.removed, r.deleted,
1126 unknown, ignored, r.clean)
1126 unknown, ignored, r.clean)
1127 repo.status = overridestatus
1127 repo.status = overridestatus
1128 orig(ui, repo, *dirs, **opts)
1128 orig(ui, repo, *dirs, **opts)
1129 repo.status = oldstatus
1129 repo.status = oldstatus
1130 def overriderollback(orig, ui, repo, **opts):
1130 def overriderollback(orig, ui, repo, **opts):
1131 wlock = repo.wlock()
1131 wlock = repo.wlock()
1132 try:
1132 try:
1133 before = repo.dirstate.parents()
1133 before = repo.dirstate.parents()
1134 orphans = set(f for f in repo.dirstate
1134 orphans = set(f for f in repo.dirstate
1135 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1135 if lfutil.isstandin(f) and repo.dirstate[f] != 'r')
1136 result = orig(ui, repo, **opts)
1136 result = orig(ui, repo, **opts)
1137 after = repo.dirstate.parents()
1137 after = repo.dirstate.parents()
1138 if before == after:
1138 if before == after:
1139 return result # no need to restore standins
1139 return result # no need to restore standins
1140
1140
1141 pctx = repo['.']
1141 pctx = repo['.']
1142 for f in repo.dirstate:
1142 for f in repo.dirstate:
1143 if lfutil.isstandin(f):
1143 if lfutil.isstandin(f):
1144 orphans.discard(f)
1144 orphans.discard(f)
1145 if repo.dirstate[f] == 'r':
1145 if repo.dirstate[f] == 'r':
1146 repo.wvfs.unlinkpath(f, ignoremissing=True)
1146 repo.wvfs.unlinkpath(f, ignoremissing=True)
1147 elif f in pctx:
1147 elif f in pctx:
1148 fctx = pctx[f]
1148 fctx = pctx[f]
1149 repo.wwrite(f, fctx.data(), fctx.flags())
1149 repo.wwrite(f, fctx.data(), fctx.flags())
1150 else:
1150 else:
1151 # content of standin is not so important in 'a',
1151 # content of standin is not so important in 'a',
1152 # 'm' or 'n' (coming from the 2nd parent) cases
1152 # 'm' or 'n' (coming from the 2nd parent) cases
1153 lfutil.writestandin(repo, f, '', False)
1153 lfutil.writestandin(repo, f, '', False)
1154 for standin in orphans:
1154 for standin in orphans:
1155 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1155 repo.wvfs.unlinkpath(standin, ignoremissing=True)
1156
1156
1157 lfdirstate = lfutil.openlfdirstate(ui, repo)
1157 lfdirstate = lfutil.openlfdirstate(ui, repo)
1158 orphans = set(lfdirstate)
1158 orphans = set(lfdirstate)
1159 lfiles = lfutil.listlfiles(repo)
1159 lfiles = lfutil.listlfiles(repo)
1160 for file in lfiles:
1160 for file in lfiles:
1161 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1161 lfutil.synclfdirstate(repo, lfdirstate, file, True)
1162 orphans.discard(file)
1162 orphans.discard(file)
1163 for lfile in orphans:
1163 for lfile in orphans:
1164 lfdirstate.drop(lfile)
1164 lfdirstate.drop(lfile)
1165 lfdirstate.write()
1165 lfdirstate.write()
1166 finally:
1166 finally:
1167 wlock.release()
1167 wlock.release()
1168 return result
1168 return result
1169
1169
1170 def overridetransplant(orig, ui, repo, *revs, **opts):
1170 def overridetransplant(orig, ui, repo, *revs, **opts):
1171 resuming = opts.get('continue')
1171 resuming = opts.get('continue')
1172 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1172 repo._lfcommithooks.append(lfutil.automatedcommithook(resuming))
1173 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1173 repo._lfstatuswriters.append(lambda *msg, **opts: None)
1174 try:
1174 try:
1175 result = orig(ui, repo, *revs, **opts)
1175 result = orig(ui, repo, *revs, **opts)
1176 finally:
1176 finally:
1177 repo._lfstatuswriters.pop()
1177 repo._lfstatuswriters.pop()
1178 repo._lfcommithooks.pop()
1178 repo._lfcommithooks.pop()
1179 return result
1179 return result
1180
1180
1181 def overridecat(orig, ui, repo, file1, *pats, **opts):
1181 def overridecat(orig, ui, repo, file1, *pats, **opts):
1182 ctx = scmutil.revsingle(repo, opts.get('rev'))
1182 ctx = scmutil.revsingle(repo, opts.get('rev'))
1183 err = 1
1183 err = 1
1184 notbad = set()
1184 notbad = set()
1185 m = scmutil.match(ctx, (file1,) + pats, opts)
1185 m = scmutil.match(ctx, (file1,) + pats, opts)
1186 origmatchfn = m.matchfn
1186 origmatchfn = m.matchfn
1187 def lfmatchfn(f):
1187 def lfmatchfn(f):
1188 if origmatchfn(f):
1188 if origmatchfn(f):
1189 return True
1189 return True
1190 lf = lfutil.splitstandin(f)
1190 lf = lfutil.splitstandin(f)
1191 if lf is None:
1191 if lf is None:
1192 return False
1192 return False
1193 notbad.add(lf)
1193 notbad.add(lf)
1194 return origmatchfn(lf)
1194 return origmatchfn(lf)
1195 m.matchfn = lfmatchfn
1195 m.matchfn = lfmatchfn
1196 origbadfn = m.bad
1196 origbadfn = m.bad
1197 def lfbadfn(f, msg):
1197 def lfbadfn(f, msg):
1198 if not f in notbad:
1198 if not f in notbad:
1199 origbadfn(f, msg)
1199 origbadfn(f, msg)
1200 m.bad = lfbadfn
1200 m.bad = lfbadfn
1201 for f in ctx.walk(m):
1201 for f in ctx.walk(m):
1202 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1202 fp = cmdutil.makefileobj(repo, opts.get('output'), ctx.node(),
1203 pathname=f)
1203 pathname=f)
1204 lf = lfutil.splitstandin(f)
1204 lf = lfutil.splitstandin(f)
1205 if lf is None or origmatchfn(f):
1205 if lf is None or origmatchfn(f):
1206 # duplicating unreachable code from commands.cat
1206 # duplicating unreachable code from commands.cat
1207 data = ctx[f].data()
1207 data = ctx[f].data()
1208 if opts.get('decode'):
1208 if opts.get('decode'):
1209 data = repo.wwritedata(f, data)
1209 data = repo.wwritedata(f, data)
1210 fp.write(data)
1210 fp.write(data)
1211 else:
1211 else:
1212 hash = lfutil.readstandin(repo, lf, ctx.rev())
1212 hash = lfutil.readstandin(repo, lf, ctx.rev())
1213 if not lfutil.inusercache(repo.ui, hash):
1213 if not lfutil.inusercache(repo.ui, hash):
1214 store = basestore._openstore(repo)
1214 store = basestore._openstore(repo)
1215 success, missing = store.get([(lf, hash)])
1215 success, missing = store.get([(lf, hash)])
1216 if len(success) != 1:
1216 if len(success) != 1:
1217 raise util.Abort(
1217 raise util.Abort(
1218 _('largefile %s is not in cache and could not be '
1218 _('largefile %s is not in cache and could not be '
1219 'downloaded') % lf)
1219 'downloaded') % lf)
1220 path = lfutil.usercachepath(repo.ui, hash)
1220 path = lfutil.usercachepath(repo.ui, hash)
1221 fpin = open(path, "rb")
1221 fpin = open(path, "rb")
1222 for chunk in util.filechunkiter(fpin, 128 * 1024):
1222 for chunk in util.filechunkiter(fpin, 128 * 1024):
1223 fp.write(chunk)
1223 fp.write(chunk)
1224 fpin.close()
1224 fpin.close()
1225 fp.close()
1225 fp.close()
1226 err = 0
1226 err = 0
1227 return err
1227 return err
1228
1228
1229 def mergeupdate(orig, repo, node, branchmerge, force, partial,
1229 def mergeupdate(orig, repo, node, branchmerge, force, partial,
1230 *args, **kwargs):
1230 *args, **kwargs):
1231 wlock = repo.wlock()
1231 wlock = repo.wlock()
1232 try:
1232 try:
1233 # branch | | |
1233 # branch | | |
1234 # merge | force | partial | action
1234 # merge | force | partial | action
1235 # -------+-------+---------+--------------
1235 # -------+-------+---------+--------------
1236 # x | x | x | linear-merge
1236 # x | x | x | linear-merge
1237 # o | x | x | branch-merge
1237 # o | x | x | branch-merge
1238 # x | o | x | overwrite (as clean update)
1238 # x | o | x | overwrite (as clean update)
1239 # o | o | x | force-branch-merge (*1)
1239 # o | o | x | force-branch-merge (*1)
1240 # x | x | o | (*)
1240 # x | x | o | (*)
1241 # o | x | o | (*)
1241 # o | x | o | (*)
1242 # x | o | o | overwrite (as revert)
1242 # x | o | o | overwrite (as revert)
1243 # o | o | o | (*)
1243 # o | o | o | (*)
1244 #
1244 #
1245 # (*) don't care
1245 # (*) don't care
1246 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1246 # (*1) deprecated, but used internally (e.g: "rebase --collapse")
1247
1247
1248 linearmerge = not branchmerge and not force and not partial
1248 linearmerge = not branchmerge and not force and not partial
1249
1249
1250 if linearmerge or (branchmerge and force and not partial):
1250 if linearmerge or (branchmerge and force and not partial):
1251 # update standins for linear-merge or force-branch-merge,
1251 # update standins for linear-merge or force-branch-merge,
1252 # because largefiles in the working directory may be modified
1252 # because largefiles in the working directory may be modified
1253 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1253 lfdirstate = lfutil.openlfdirstate(repo.ui, repo)
1254 unsure, s = lfdirstate.status(match_.always(repo.root,
1254 unsure, s = lfdirstate.status(match_.always(repo.root,
1255 repo.getcwd()),
1255 repo.getcwd()),
1256 [], False, False, False)
1256 [], False, False, False)
1257 for lfile in unsure + s.modified + s.added:
1257 for lfile in unsure + s.modified + s.added:
1258 lfutil.updatestandin(repo, lfutil.standin(lfile))
1258 lfutil.updatestandin(repo, lfutil.standin(lfile))
1259
1259
1260 if linearmerge:
1260 if linearmerge:
1261 # Only call updatelfiles on the standins that have changed
1261 # Only call updatelfiles on the standins that have changed
1262 # to save time
1262 # to save time
1263 oldstandins = lfutil.getstandinsstate(repo)
1263 oldstandins = lfutil.getstandinsstate(repo)
1264
1264
1265 result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
1265 result = orig(repo, node, branchmerge, force, partial, *args, **kwargs)
1266
1266
1267 filelist = None
1267 filelist = None
1268 if linearmerge:
1268 if linearmerge:
1269 newstandins = lfutil.getstandinsstate(repo)
1269 newstandins = lfutil.getstandinsstate(repo)
1270 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1270 filelist = lfutil.getlfilestoupdate(oldstandins, newstandins)
1271
1271
1272 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1272 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1273 normallookup=partial)
1273 normallookup=partial)
1274
1274
1275 return result
1275 return result
1276 finally:
1276 finally:
1277 wlock.release()
1277 wlock.release()
1278
1278
1279 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1279 def scmutilmarktouched(orig, repo, files, *args, **kwargs):
1280 result = orig(repo, files, *args, **kwargs)
1280 result = orig(repo, files, *args, **kwargs)
1281
1281
1282 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1282 filelist = [lfutil.splitstandin(f) for f in files if lfutil.isstandin(f)]
1283 if filelist:
1283 if filelist:
1284 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1284 lfcommands.updatelfiles(repo.ui, repo, filelist=filelist,
1285 printmessage=False, normallookup=True)
1285 printmessage=False, normallookup=True)
1286
1286
1287 return result
1287 return result
@@ -1,6253 +1,6253 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 from node import hex, bin, nullid, nullrev, short
8 from node import hex, bin, nullid, nullrev, short
9 from lock import release
9 from lock import release
10 from i18n import _
10 from i18n import _
11 import os, re, difflib, time, tempfile, errno, shlex
11 import os, re, difflib, time, tempfile, errno, shlex
12 import sys, socket
12 import sys, socket
13 import hg, scmutil, util, revlog, copies, error, bookmarks
13 import hg, scmutil, util, revlog, copies, error, bookmarks
14 import patch, help, encoding, templatekw, discovery
14 import patch, help, encoding, templatekw, discovery
15 import archival, changegroup, cmdutil, hbisect
15 import archival, changegroup, cmdutil, hbisect
16 import sshserver, hgweb, commandserver
16 import sshserver, hgweb, commandserver
17 import extensions
17 import extensions
18 from hgweb import server as hgweb_server
18 from hgweb import server as hgweb_server
19 import merge as mergemod
19 import merge as mergemod
20 import minirst, revset, fileset
20 import minirst, revset, fileset
21 import dagparser, context, simplemerge, graphmod, copies
21 import dagparser, context, simplemerge, graphmod, copies
22 import random
22 import random
23 import setdiscovery, treediscovery, dagutil, pvec, localrepo
23 import setdiscovery, treediscovery, dagutil, pvec, localrepo
24 import phases, obsolete, exchange
24 import phases, obsolete, exchange
25 import ui as uimod
25 import ui as uimod
26
26
27 table = {}
27 table = {}
28
28
29 command = cmdutil.command(table)
29 command = cmdutil.command(table)
30
30
31 # Space delimited list of commands that don't require local repositories.
31 # Space delimited list of commands that don't require local repositories.
32 # This should be populated by passing norepo=True into the @command decorator.
32 # This should be populated by passing norepo=True into the @command decorator.
33 norepo = ''
33 norepo = ''
34 # Space delimited list of commands that optionally require local repositories.
34 # Space delimited list of commands that optionally require local repositories.
35 # This should be populated by passing optionalrepo=True into the @command
35 # This should be populated by passing optionalrepo=True into the @command
36 # decorator.
36 # decorator.
37 optionalrepo = ''
37 optionalrepo = ''
38 # Space delimited list of commands that will examine arguments looking for
38 # Space delimited list of commands that will examine arguments looking for
39 # a repository. This should be populated by passing inferrepo=True into the
39 # a repository. This should be populated by passing inferrepo=True into the
40 # @command decorator.
40 # @command decorator.
41 inferrepo = ''
41 inferrepo = ''
42
42
43 # common command options
43 # common command options
44
44
45 globalopts = [
45 globalopts = [
46 ('R', 'repository', '',
46 ('R', 'repository', '',
47 _('repository root directory or name of overlay bundle file'),
47 _('repository root directory or name of overlay bundle file'),
48 _('REPO')),
48 _('REPO')),
49 ('', 'cwd', '',
49 ('', 'cwd', '',
50 _('change working directory'), _('DIR')),
50 _('change working directory'), _('DIR')),
51 ('y', 'noninteractive', None,
51 ('y', 'noninteractive', None,
52 _('do not prompt, automatically pick the first choice for all prompts')),
52 _('do not prompt, automatically pick the first choice for all prompts')),
53 ('q', 'quiet', None, _('suppress output')),
53 ('q', 'quiet', None, _('suppress output')),
54 ('v', 'verbose', None, _('enable additional output')),
54 ('v', 'verbose', None, _('enable additional output')),
55 ('', 'config', [],
55 ('', 'config', [],
56 _('set/override config option (use \'section.name=value\')'),
56 _('set/override config option (use \'section.name=value\')'),
57 _('CONFIG')),
57 _('CONFIG')),
58 ('', 'debug', None, _('enable debugging output')),
58 ('', 'debug', None, _('enable debugging output')),
59 ('', 'debugger', None, _('start debugger')),
59 ('', 'debugger', None, _('start debugger')),
60 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
60 ('', 'encoding', encoding.encoding, _('set the charset encoding'),
61 _('ENCODE')),
61 _('ENCODE')),
62 ('', 'encodingmode', encoding.encodingmode,
62 ('', 'encodingmode', encoding.encodingmode,
63 _('set the charset encoding mode'), _('MODE')),
63 _('set the charset encoding mode'), _('MODE')),
64 ('', 'traceback', None, _('always print a traceback on exception')),
64 ('', 'traceback', None, _('always print a traceback on exception')),
65 ('', 'time', None, _('time how long the command takes')),
65 ('', 'time', None, _('time how long the command takes')),
66 ('', 'profile', None, _('print command execution profile')),
66 ('', 'profile', None, _('print command execution profile')),
67 ('', 'version', None, _('output version information and exit')),
67 ('', 'version', None, _('output version information and exit')),
68 ('h', 'help', None, _('display help and exit')),
68 ('h', 'help', None, _('display help and exit')),
69 ('', 'hidden', False, _('consider hidden changesets')),
69 ('', 'hidden', False, _('consider hidden changesets')),
70 ]
70 ]
71
71
72 dryrunopts = [('n', 'dry-run', None,
72 dryrunopts = [('n', 'dry-run', None,
73 _('do not perform actions, just print output'))]
73 _('do not perform actions, just print output'))]
74
74
75 remoteopts = [
75 remoteopts = [
76 ('e', 'ssh', '',
76 ('e', 'ssh', '',
77 _('specify ssh command to use'), _('CMD')),
77 _('specify ssh command to use'), _('CMD')),
78 ('', 'remotecmd', '',
78 ('', 'remotecmd', '',
79 _('specify hg command to run on the remote side'), _('CMD')),
79 _('specify hg command to run on the remote side'), _('CMD')),
80 ('', 'insecure', None,
80 ('', 'insecure', None,
81 _('do not verify server certificate (ignoring web.cacerts config)')),
81 _('do not verify server certificate (ignoring web.cacerts config)')),
82 ]
82 ]
83
83
84 walkopts = [
84 walkopts = [
85 ('I', 'include', [],
85 ('I', 'include', [],
86 _('include names matching the given patterns'), _('PATTERN')),
86 _('include names matching the given patterns'), _('PATTERN')),
87 ('X', 'exclude', [],
87 ('X', 'exclude', [],
88 _('exclude names matching the given patterns'), _('PATTERN')),
88 _('exclude names matching the given patterns'), _('PATTERN')),
89 ]
89 ]
90
90
91 commitopts = [
91 commitopts = [
92 ('m', 'message', '',
92 ('m', 'message', '',
93 _('use text as commit message'), _('TEXT')),
93 _('use text as commit message'), _('TEXT')),
94 ('l', 'logfile', '',
94 ('l', 'logfile', '',
95 _('read commit message from file'), _('FILE')),
95 _('read commit message from file'), _('FILE')),
96 ]
96 ]
97
97
98 commitopts2 = [
98 commitopts2 = [
99 ('d', 'date', '',
99 ('d', 'date', '',
100 _('record the specified date as commit date'), _('DATE')),
100 _('record the specified date as commit date'), _('DATE')),
101 ('u', 'user', '',
101 ('u', 'user', '',
102 _('record the specified user as committer'), _('USER')),
102 _('record the specified user as committer'), _('USER')),
103 ]
103 ]
104
104
105 # hidden for now
105 # hidden for now
106 formatteropts = [
106 formatteropts = [
107 ('T', 'template', '',
107 ('T', 'template', '',
108 _('display with template (DEPRECATED)'), _('TEMPLATE')),
108 _('display with template (DEPRECATED)'), _('TEMPLATE')),
109 ]
109 ]
110
110
111 templateopts = [
111 templateopts = [
112 ('', 'style', '',
112 ('', 'style', '',
113 _('display using template map file (DEPRECATED)'), _('STYLE')),
113 _('display using template map file (DEPRECATED)'), _('STYLE')),
114 ('T', 'template', '',
114 ('T', 'template', '',
115 _('display with template'), _('TEMPLATE')),
115 _('display with template'), _('TEMPLATE')),
116 ]
116 ]
117
117
118 logopts = [
118 logopts = [
119 ('p', 'patch', None, _('show patch')),
119 ('p', 'patch', None, _('show patch')),
120 ('g', 'git', None, _('use git extended diff format')),
120 ('g', 'git', None, _('use git extended diff format')),
121 ('l', 'limit', '',
121 ('l', 'limit', '',
122 _('limit number of changes displayed'), _('NUM')),
122 _('limit number of changes displayed'), _('NUM')),
123 ('M', 'no-merges', None, _('do not show merges')),
123 ('M', 'no-merges', None, _('do not show merges')),
124 ('', 'stat', None, _('output diffstat-style summary of changes')),
124 ('', 'stat', None, _('output diffstat-style summary of changes')),
125 ('G', 'graph', None, _("show the revision DAG")),
125 ('G', 'graph', None, _("show the revision DAG")),
126 ] + templateopts
126 ] + templateopts
127
127
128 diffopts = [
128 diffopts = [
129 ('a', 'text', None, _('treat all files as text')),
129 ('a', 'text', None, _('treat all files as text')),
130 ('g', 'git', None, _('use git extended diff format')),
130 ('g', 'git', None, _('use git extended diff format')),
131 ('', 'nodates', None, _('omit dates from diff headers'))
131 ('', 'nodates', None, _('omit dates from diff headers'))
132 ]
132 ]
133
133
134 diffwsopts = [
134 diffwsopts = [
135 ('w', 'ignore-all-space', None,
135 ('w', 'ignore-all-space', None,
136 _('ignore white space when comparing lines')),
136 _('ignore white space when comparing lines')),
137 ('b', 'ignore-space-change', None,
137 ('b', 'ignore-space-change', None,
138 _('ignore changes in the amount of white space')),
138 _('ignore changes in the amount of white space')),
139 ('B', 'ignore-blank-lines', None,
139 ('B', 'ignore-blank-lines', None,
140 _('ignore changes whose lines are all blank')),
140 _('ignore changes whose lines are all blank')),
141 ]
141 ]
142
142
143 diffopts2 = [
143 diffopts2 = [
144 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
144 ('', 'noprefix', None, _('omit a/ and b/ prefixes from filenames')),
145 ('p', 'show-function', None, _('show which function each change is in')),
145 ('p', 'show-function', None, _('show which function each change is in')),
146 ('', 'reverse', None, _('produce a diff that undoes the changes')),
146 ('', 'reverse', None, _('produce a diff that undoes the changes')),
147 ] + diffwsopts + [
147 ] + diffwsopts + [
148 ('U', 'unified', '',
148 ('U', 'unified', '',
149 _('number of lines of context to show'), _('NUM')),
149 _('number of lines of context to show'), _('NUM')),
150 ('', 'stat', None, _('output diffstat-style summary of changes')),
150 ('', 'stat', None, _('output diffstat-style summary of changes')),
151 ]
151 ]
152
152
153 mergetoolopts = [
153 mergetoolopts = [
154 ('t', 'tool', '', _('specify merge tool')),
154 ('t', 'tool', '', _('specify merge tool')),
155 ]
155 ]
156
156
157 similarityopts = [
157 similarityopts = [
158 ('s', 'similarity', '',
158 ('s', 'similarity', '',
159 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
159 _('guess renamed files by similarity (0<=s<=100)'), _('SIMILARITY'))
160 ]
160 ]
161
161
162 subrepoopts = [
162 subrepoopts = [
163 ('S', 'subrepos', None,
163 ('S', 'subrepos', None,
164 _('recurse into subrepositories'))
164 _('recurse into subrepositories'))
165 ]
165 ]
166
166
167 # Commands start here, listed alphabetically
167 # Commands start here, listed alphabetically
168
168
169 @command('^add',
169 @command('^add',
170 walkopts + subrepoopts + dryrunopts,
170 walkopts + subrepoopts + dryrunopts,
171 _('[OPTION]... [FILE]...'),
171 _('[OPTION]... [FILE]...'),
172 inferrepo=True)
172 inferrepo=True)
173 def add(ui, repo, *pats, **opts):
173 def add(ui, repo, *pats, **opts):
174 """add the specified files on the next commit
174 """add the specified files on the next commit
175
175
176 Schedule files to be version controlled and added to the
176 Schedule files to be version controlled and added to the
177 repository.
177 repository.
178
178
179 The files will be added to the repository at the next commit. To
179 The files will be added to the repository at the next commit. To
180 undo an add before that, see :hg:`forget`.
180 undo an add before that, see :hg:`forget`.
181
181
182 If no names are given, add all files to the repository.
182 If no names are given, add all files to the repository.
183
183
184 .. container:: verbose
184 .. container:: verbose
185
185
186 An example showing how new (unknown) files are added
186 An example showing how new (unknown) files are added
187 automatically by :hg:`add`::
187 automatically by :hg:`add`::
188
188
189 $ ls
189 $ ls
190 foo.c
190 foo.c
191 $ hg status
191 $ hg status
192 ? foo.c
192 ? foo.c
193 $ hg add
193 $ hg add
194 adding foo.c
194 adding foo.c
195 $ hg status
195 $ hg status
196 A foo.c
196 A foo.c
197
197
198 Returns 0 if all files are successfully added.
198 Returns 0 if all files are successfully added.
199 """
199 """
200
200
201 m = scmutil.match(repo[None], pats, opts)
201 m = scmutil.match(repo[None], pats, opts)
202 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
202 rejected = cmdutil.add(ui, repo, m, opts.get('dry_run'),
203 opts.get('subrepos'), prefix="", explicitonly=False)
203 opts.get('subrepos'), prefix="", explicitonly=False)
204 return rejected and 1 or 0
204 return rejected and 1 or 0
205
205
206 @command('addremove',
206 @command('addremove',
207 similarityopts + walkopts + dryrunopts,
207 similarityopts + walkopts + dryrunopts,
208 _('[OPTION]... [FILE]...'),
208 _('[OPTION]... [FILE]...'),
209 inferrepo=True)
209 inferrepo=True)
210 def addremove(ui, repo, *pats, **opts):
210 def addremove(ui, repo, *pats, **opts):
211 """add all new files, delete all missing files
211 """add all new files, delete all missing files
212
212
213 Add all new files and remove all missing files from the
213 Add all new files and remove all missing files from the
214 repository.
214 repository.
215
215
216 New files are ignored if they match any of the patterns in
216 New files are ignored if they match any of the patterns in
217 ``.hgignore``. As with add, these changes take effect at the next
217 ``.hgignore``. As with add, these changes take effect at the next
218 commit.
218 commit.
219
219
220 Use the -s/--similarity option to detect renamed files. This
220 Use the -s/--similarity option to detect renamed files. This
221 option takes a percentage between 0 (disabled) and 100 (files must
221 option takes a percentage between 0 (disabled) and 100 (files must
222 be identical) as its parameter. With a parameter greater than 0,
222 be identical) as its parameter. With a parameter greater than 0,
223 this compares every removed file with every added file and records
223 this compares every removed file with every added file and records
224 those similar enough as renames. Detecting renamed files this way
224 those similar enough as renames. Detecting renamed files this way
225 can be expensive. After using this option, :hg:`status -C` can be
225 can be expensive. After using this option, :hg:`status -C` can be
226 used to check which files were identified as moved or renamed. If
226 used to check which files were identified as moved or renamed. If
227 not specified, -s/--similarity defaults to 100 and only renames of
227 not specified, -s/--similarity defaults to 100 and only renames of
228 identical files are detected.
228 identical files are detected.
229
229
230 Returns 0 if all files are successfully added.
230 Returns 0 if all files are successfully added.
231 """
231 """
232 try:
232 try:
233 sim = float(opts.get('similarity') or 100)
233 sim = float(opts.get('similarity') or 100)
234 except ValueError:
234 except ValueError:
235 raise util.Abort(_('similarity must be a number'))
235 raise util.Abort(_('similarity must be a number'))
236 if sim < 0 or sim > 100:
236 if sim < 0 or sim > 100:
237 raise util.Abort(_('similarity must be between 0 and 100'))
237 raise util.Abort(_('similarity must be between 0 and 100'))
238 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
238 return scmutil.addremove(repo, pats, opts, similarity=sim / 100.0)
239
239
240 @command('^annotate|blame',
240 @command('^annotate|blame',
241 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
241 [('r', 'rev', '', _('annotate the specified revision'), _('REV')),
242 ('', 'follow', None,
242 ('', 'follow', None,
243 _('follow copies/renames and list the filename (DEPRECATED)')),
243 _('follow copies/renames and list the filename (DEPRECATED)')),
244 ('', 'no-follow', None, _("don't follow copies and renames")),
244 ('', 'no-follow', None, _("don't follow copies and renames")),
245 ('a', 'text', None, _('treat all files as text')),
245 ('a', 'text', None, _('treat all files as text')),
246 ('u', 'user', None, _('list the author (long with -v)')),
246 ('u', 'user', None, _('list the author (long with -v)')),
247 ('f', 'file', None, _('list the filename')),
247 ('f', 'file', None, _('list the filename')),
248 ('d', 'date', None, _('list the date (short with -q)')),
248 ('d', 'date', None, _('list the date (short with -q)')),
249 ('n', 'number', None, _('list the revision number (default)')),
249 ('n', 'number', None, _('list the revision number (default)')),
250 ('c', 'changeset', None, _('list the changeset')),
250 ('c', 'changeset', None, _('list the changeset')),
251 ('l', 'line-number', None, _('show line number at the first appearance'))
251 ('l', 'line-number', None, _('show line number at the first appearance'))
252 ] + diffwsopts + walkopts + formatteropts,
252 ] + diffwsopts + walkopts + formatteropts,
253 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
253 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
254 inferrepo=True)
254 inferrepo=True)
255 def annotate(ui, repo, *pats, **opts):
255 def annotate(ui, repo, *pats, **opts):
256 """show changeset information by line for each file
256 """show changeset information by line for each file
257
257
258 List changes in files, showing the revision id responsible for
258 List changes in files, showing the revision id responsible for
259 each line
259 each line
260
260
261 This command is useful for discovering when a change was made and
261 This command is useful for discovering when a change was made and
262 by whom.
262 by whom.
263
263
264 Without the -a/--text option, annotate will avoid processing files
264 Without the -a/--text option, annotate will avoid processing files
265 it detects as binary. With -a, annotate will annotate the file
265 it detects as binary. With -a, annotate will annotate the file
266 anyway, although the results will probably be neither useful
266 anyway, although the results will probably be neither useful
267 nor desirable.
267 nor desirable.
268
268
269 Returns 0 on success.
269 Returns 0 on success.
270 """
270 """
271 if not pats:
271 if not pats:
272 raise util.Abort(_('at least one filename or pattern is required'))
272 raise util.Abort(_('at least one filename or pattern is required'))
273
273
274 if opts.get('follow'):
274 if opts.get('follow'):
275 # --follow is deprecated and now just an alias for -f/--file
275 # --follow is deprecated and now just an alias for -f/--file
276 # to mimic the behavior of Mercurial before version 1.5
276 # to mimic the behavior of Mercurial before version 1.5
277 opts['file'] = True
277 opts['file'] = True
278
278
279 fm = ui.formatter('annotate', opts)
279 fm = ui.formatter('annotate', opts)
280 datefunc = ui.quiet and util.shortdate or util.datestr
280 datefunc = ui.quiet and util.shortdate or util.datestr
281 hexfn = fm.hexfunc
281 hexfn = fm.hexfunc
282
282
283 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
283 opmap = [('user', ' ', lambda x: x[0].user(), ui.shortuser),
284 ('number', ' ', lambda x: x[0].rev(), str),
284 ('number', ' ', lambda x: x[0].rev(), str),
285 ('changeset', ' ', lambda x: hexfn(x[0].node()), str),
285 ('changeset', ' ', lambda x: hexfn(x[0].node()), str),
286 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
286 ('date', ' ', lambda x: x[0].date(), util.cachefunc(datefunc)),
287 ('file', ' ', lambda x: x[0].path(), str),
287 ('file', ' ', lambda x: x[0].path(), str),
288 ('line_number', ':', lambda x: x[1], str),
288 ('line_number', ':', lambda x: x[1], str),
289 ]
289 ]
290 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
290 fieldnamemap = {'number': 'rev', 'changeset': 'node'}
291
291
292 if (not opts.get('user') and not opts.get('changeset')
292 if (not opts.get('user') and not opts.get('changeset')
293 and not opts.get('date') and not opts.get('file')):
293 and not opts.get('date') and not opts.get('file')):
294 opts['number'] = True
294 opts['number'] = True
295
295
296 linenumber = opts.get('line_number') is not None
296 linenumber = opts.get('line_number') is not None
297 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
297 if linenumber and (not opts.get('changeset')) and (not opts.get('number')):
298 raise util.Abort(_('at least one of -n/-c is required for -l'))
298 raise util.Abort(_('at least one of -n/-c is required for -l'))
299
299
300 if fm:
300 if fm:
301 def makefunc(get, fmt):
301 def makefunc(get, fmt):
302 return get
302 return get
303 else:
303 else:
304 def makefunc(get, fmt):
304 def makefunc(get, fmt):
305 return lambda x: fmt(get(x))
305 return lambda x: fmt(get(x))
306 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
306 funcmap = [(makefunc(get, fmt), sep) for op, sep, get, fmt in opmap
307 if opts.get(op)]
307 if opts.get(op)]
308 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
308 funcmap[0] = (funcmap[0][0], '') # no separator in front of first column
309 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
309 fields = ' '.join(fieldnamemap.get(op, op) for op, sep, get, fmt in opmap
310 if opts.get(op))
310 if opts.get(op))
311
311
312 def bad(x, y):
312 def bad(x, y):
313 raise util.Abort("%s: %s" % (x, y))
313 raise util.Abort("%s: %s" % (x, y))
314
314
315 ctx = scmutil.revsingle(repo, opts.get('rev'))
315 ctx = scmutil.revsingle(repo, opts.get('rev'))
316 m = scmutil.match(ctx, pats, opts)
316 m = scmutil.match(ctx, pats, opts)
317 m.bad = bad
317 m.bad = bad
318 follow = not opts.get('no_follow')
318 follow = not opts.get('no_follow')
319 diffopts = patch.diffopts(ui, opts, section='annotate')
319 diffopts = patch.diffopts(ui, opts, section='annotate')
320 for abs in ctx.walk(m):
320 for abs in ctx.walk(m):
321 fctx = ctx[abs]
321 fctx = ctx[abs]
322 if not opts.get('text') and util.binary(fctx.data()):
322 if not opts.get('text') and util.binary(fctx.data()):
323 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
323 fm.plain(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
324 continue
324 continue
325
325
326 lines = fctx.annotate(follow=follow, linenumber=linenumber,
326 lines = fctx.annotate(follow=follow, linenumber=linenumber,
327 diffopts=diffopts)
327 diffopts=diffopts)
328 formats = []
328 formats = []
329 pieces = []
329 pieces = []
330
330
331 for f, sep in funcmap:
331 for f, sep in funcmap:
332 l = [f(n) for n, dummy in lines]
332 l = [f(n) for n, dummy in lines]
333 if l:
333 if l:
334 if fm:
334 if fm:
335 formats.append(['%s' for x in l])
335 formats.append(['%s' for x in l])
336 else:
336 else:
337 sizes = [encoding.colwidth(x) for x in l]
337 sizes = [encoding.colwidth(x) for x in l]
338 ml = max(sizes)
338 ml = max(sizes)
339 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
339 formats.append([sep + ' ' * (ml - w) + '%s' for w in sizes])
340 pieces.append(l)
340 pieces.append(l)
341
341
342 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
342 for f, p, l in zip(zip(*formats), zip(*pieces), lines):
343 fm.startitem()
343 fm.startitem()
344 fm.write(fields, "".join(f), *p)
344 fm.write(fields, "".join(f), *p)
345 fm.write('line', ": %s", l[1])
345 fm.write('line', ": %s", l[1])
346
346
347 if lines and not lines[-1][1].endswith('\n'):
347 if lines and not lines[-1][1].endswith('\n'):
348 fm.plain('\n')
348 fm.plain('\n')
349
349
350 fm.end()
350 fm.end()
351
351
352 @command('archive',
352 @command('archive',
353 [('', 'no-decode', None, _('do not pass files through decoders')),
353 [('', 'no-decode', None, _('do not pass files through decoders')),
354 ('p', 'prefix', '', _('directory prefix for files in archive'),
354 ('p', 'prefix', '', _('directory prefix for files in archive'),
355 _('PREFIX')),
355 _('PREFIX')),
356 ('r', 'rev', '', _('revision to distribute'), _('REV')),
356 ('r', 'rev', '', _('revision to distribute'), _('REV')),
357 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
357 ('t', 'type', '', _('type of distribution to create'), _('TYPE')),
358 ] + subrepoopts + walkopts,
358 ] + subrepoopts + walkopts,
359 _('[OPTION]... DEST'))
359 _('[OPTION]... DEST'))
360 def archive(ui, repo, dest, **opts):
360 def archive(ui, repo, dest, **opts):
361 '''create an unversioned archive of a repository revision
361 '''create an unversioned archive of a repository revision
362
362
363 By default, the revision used is the parent of the working
363 By default, the revision used is the parent of the working
364 directory; use -r/--rev to specify a different revision.
364 directory; use -r/--rev to specify a different revision.
365
365
366 The archive type is automatically detected based on file
366 The archive type is automatically detected based on file
367 extension (or override using -t/--type).
367 extension (or override using -t/--type).
368
368
369 .. container:: verbose
369 .. container:: verbose
370
370
371 Examples:
371 Examples:
372
372
373 - create a zip file containing the 1.0 release::
373 - create a zip file containing the 1.0 release::
374
374
375 hg archive -r 1.0 project-1.0.zip
375 hg archive -r 1.0 project-1.0.zip
376
376
377 - create a tarball excluding .hg files::
377 - create a tarball excluding .hg files::
378
378
379 hg archive project.tar.gz -X ".hg*"
379 hg archive project.tar.gz -X ".hg*"
380
380
381 Valid types are:
381 Valid types are:
382
382
383 :``files``: a directory full of files (default)
383 :``files``: a directory full of files (default)
384 :``tar``: tar archive, uncompressed
384 :``tar``: tar archive, uncompressed
385 :``tbz2``: tar archive, compressed using bzip2
385 :``tbz2``: tar archive, compressed using bzip2
386 :``tgz``: tar archive, compressed using gzip
386 :``tgz``: tar archive, compressed using gzip
387 :``uzip``: zip archive, uncompressed
387 :``uzip``: zip archive, uncompressed
388 :``zip``: zip archive, compressed using deflate
388 :``zip``: zip archive, compressed using deflate
389
389
390 The exact name of the destination archive or directory is given
390 The exact name of the destination archive or directory is given
391 using a format string; see :hg:`help export` for details.
391 using a format string; see :hg:`help export` for details.
392
392
393 Each member added to an archive file has a directory prefix
393 Each member added to an archive file has a directory prefix
394 prepended. Use -p/--prefix to specify a format string for the
394 prepended. Use -p/--prefix to specify a format string for the
395 prefix. The default is the basename of the archive, with suffixes
395 prefix. The default is the basename of the archive, with suffixes
396 removed.
396 removed.
397
397
398 Returns 0 on success.
398 Returns 0 on success.
399 '''
399 '''
400
400
401 ctx = scmutil.revsingle(repo, opts.get('rev'))
401 ctx = scmutil.revsingle(repo, opts.get('rev'))
402 if not ctx:
402 if not ctx:
403 raise util.Abort(_('no working directory: please specify a revision'))
403 raise util.Abort(_('no working directory: please specify a revision'))
404 node = ctx.node()
404 node = ctx.node()
405 dest = cmdutil.makefilename(repo, dest, node)
405 dest = cmdutil.makefilename(repo, dest, node)
406 if os.path.realpath(dest) == repo.root:
406 if os.path.realpath(dest) == repo.root:
407 raise util.Abort(_('repository root cannot be destination'))
407 raise util.Abort(_('repository root cannot be destination'))
408
408
409 kind = opts.get('type') or archival.guesskind(dest) or 'files'
409 kind = opts.get('type') or archival.guesskind(dest) or 'files'
410 prefix = opts.get('prefix')
410 prefix = opts.get('prefix')
411
411
412 if dest == '-':
412 if dest == '-':
413 if kind == 'files':
413 if kind == 'files':
414 raise util.Abort(_('cannot archive plain files to stdout'))
414 raise util.Abort(_('cannot archive plain files to stdout'))
415 dest = cmdutil.makefileobj(repo, dest)
415 dest = cmdutil.makefileobj(repo, dest)
416 if not prefix:
416 if not prefix:
417 prefix = os.path.basename(repo.root) + '-%h'
417 prefix = os.path.basename(repo.root) + '-%h'
418
418
419 prefix = cmdutil.makefilename(repo, prefix, node)
419 prefix = cmdutil.makefilename(repo, prefix, node)
420 matchfn = scmutil.match(ctx, [], opts)
420 matchfn = scmutil.match(ctx, [], opts)
421 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
421 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
422 matchfn, prefix, subrepos=opts.get('subrepos'))
422 matchfn, prefix, subrepos=opts.get('subrepos'))
423
423
424 @command('backout',
424 @command('backout',
425 [('', 'merge', None, _('merge with old dirstate parent after backout')),
425 [('', 'merge', None, _('merge with old dirstate parent after backout')),
426 ('', 'parent', '',
426 ('', 'parent', '',
427 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
427 _('parent to choose when backing out merge (DEPRECATED)'), _('REV')),
428 ('r', 'rev', '', _('revision to backout'), _('REV')),
428 ('r', 'rev', '', _('revision to backout'), _('REV')),
429 ('e', 'edit', False, _('invoke editor on commit messages')),
429 ('e', 'edit', False, _('invoke editor on commit messages')),
430 ] + mergetoolopts + walkopts + commitopts + commitopts2,
430 ] + mergetoolopts + walkopts + commitopts + commitopts2,
431 _('[OPTION]... [-r] REV'))
431 _('[OPTION]... [-r] REV'))
432 def backout(ui, repo, node=None, rev=None, **opts):
432 def backout(ui, repo, node=None, rev=None, **opts):
433 '''reverse effect of earlier changeset
433 '''reverse effect of earlier changeset
434
434
435 Prepare a new changeset with the effect of REV undone in the
435 Prepare a new changeset with the effect of REV undone in the
436 current working directory.
436 current working directory.
437
437
438 If REV is the parent of the working directory, then this new changeset
438 If REV is the parent of the working directory, then this new changeset
439 is committed automatically. Otherwise, hg needs to merge the
439 is committed automatically. Otherwise, hg needs to merge the
440 changes and the merged result is left uncommitted.
440 changes and the merged result is left uncommitted.
441
441
442 .. note::
442 .. note::
443
443
444 backout cannot be used to fix either an unwanted or
444 backout cannot be used to fix either an unwanted or
445 incorrect merge.
445 incorrect merge.
446
446
447 .. container:: verbose
447 .. container:: verbose
448
448
449 By default, the pending changeset will have one parent,
449 By default, the pending changeset will have one parent,
450 maintaining a linear history. With --merge, the pending
450 maintaining a linear history. With --merge, the pending
451 changeset will instead have two parents: the old parent of the
451 changeset will instead have two parents: the old parent of the
452 working directory and a new child of REV that simply undoes REV.
452 working directory and a new child of REV that simply undoes REV.
453
453
454 Before version 1.7, the behavior without --merge was equivalent
454 Before version 1.7, the behavior without --merge was equivalent
455 to specifying --merge followed by :hg:`update --clean .` to
455 to specifying --merge followed by :hg:`update --clean .` to
456 cancel the merge and leave the child of REV as a head to be
456 cancel the merge and leave the child of REV as a head to be
457 merged separately.
457 merged separately.
458
458
459 See :hg:`help dates` for a list of formats valid for -d/--date.
459 See :hg:`help dates` for a list of formats valid for -d/--date.
460
460
461 Returns 0 on success, 1 if nothing to backout or there are unresolved
461 Returns 0 on success, 1 if nothing to backout or there are unresolved
462 files.
462 files.
463 '''
463 '''
464 if rev and node:
464 if rev and node:
465 raise util.Abort(_("please specify just one revision"))
465 raise util.Abort(_("please specify just one revision"))
466
466
467 if not rev:
467 if not rev:
468 rev = node
468 rev = node
469
469
470 if not rev:
470 if not rev:
471 raise util.Abort(_("please specify a revision to backout"))
471 raise util.Abort(_("please specify a revision to backout"))
472
472
473 date = opts.get('date')
473 date = opts.get('date')
474 if date:
474 if date:
475 opts['date'] = util.parsedate(date)
475 opts['date'] = util.parsedate(date)
476
476
477 cmdutil.checkunfinished(repo)
477 cmdutil.checkunfinished(repo)
478 cmdutil.bailifchanged(repo)
478 cmdutil.bailifchanged(repo)
479 node = scmutil.revsingle(repo, rev).node()
479 node = scmutil.revsingle(repo, rev).node()
480
480
481 op1, op2 = repo.dirstate.parents()
481 op1, op2 = repo.dirstate.parents()
482 if not repo.changelog.isancestor(node, op1):
482 if not repo.changelog.isancestor(node, op1):
483 raise util.Abort(_('cannot backout change that is not an ancestor'))
483 raise util.Abort(_('cannot backout change that is not an ancestor'))
484
484
485 p1, p2 = repo.changelog.parents(node)
485 p1, p2 = repo.changelog.parents(node)
486 if p1 == nullid:
486 if p1 == nullid:
487 raise util.Abort(_('cannot backout a change with no parents'))
487 raise util.Abort(_('cannot backout a change with no parents'))
488 if p2 != nullid:
488 if p2 != nullid:
489 if not opts.get('parent'):
489 if not opts.get('parent'):
490 raise util.Abort(_('cannot backout a merge changeset'))
490 raise util.Abort(_('cannot backout a merge changeset'))
491 p = repo.lookup(opts['parent'])
491 p = repo.lookup(opts['parent'])
492 if p not in (p1, p2):
492 if p not in (p1, p2):
493 raise util.Abort(_('%s is not a parent of %s') %
493 raise util.Abort(_('%s is not a parent of %s') %
494 (short(p), short(node)))
494 (short(p), short(node)))
495 parent = p
495 parent = p
496 else:
496 else:
497 if opts.get('parent'):
497 if opts.get('parent'):
498 raise util.Abort(_('cannot use --parent on non-merge changeset'))
498 raise util.Abort(_('cannot use --parent on non-merge changeset'))
499 parent = p1
499 parent = p1
500
500
501 # the backout should appear on the same branch
501 # the backout should appear on the same branch
502 wlock = repo.wlock()
502 wlock = repo.wlock()
503 try:
503 try:
504 branch = repo.dirstate.branch()
504 branch = repo.dirstate.branch()
505 bheads = repo.branchheads(branch)
505 bheads = repo.branchheads(branch)
506 rctx = scmutil.revsingle(repo, hex(parent))
506 rctx = scmutil.revsingle(repo, hex(parent))
507 if not opts.get('merge') and op1 != node:
507 if not opts.get('merge') and op1 != node:
508 try:
508 try:
509 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
509 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
510 'backout')
510 'backout')
511 repo.dirstate.beginparentchange()
511 repo.dirstate.beginparentchange()
512 stats = mergemod.update(repo, parent, True, True, False,
512 stats = mergemod.update(repo, parent, True, True, False,
513 node, False)
513 node, False)
514 repo.setparents(op1, op2)
514 repo.setparents(op1, op2)
515 repo.dirstate.endparentchange()
515 repo.dirstate.endparentchange()
516 hg._showstats(repo, stats)
516 hg._showstats(repo, stats)
517 if stats[3]:
517 if stats[3]:
518 repo.ui.status(_("use 'hg resolve' to retry unresolved "
518 repo.ui.status(_("use 'hg resolve' to retry unresolved "
519 "file merges\n"))
519 "file merges\n"))
520 else:
520 else:
521 msg = _("changeset %s backed out, "
521 msg = _("changeset %s backed out, "
522 "don't forget to commit.\n")
522 "don't forget to commit.\n")
523 ui.status(msg % short(node))
523 ui.status(msg % short(node))
524 return stats[3] > 0
524 return stats[3] > 0
525 finally:
525 finally:
526 ui.setconfig('ui', 'forcemerge', '', '')
526 ui.setconfig('ui', 'forcemerge', '', '')
527 else:
527 else:
528 hg.clean(repo, node, show_stats=False)
528 hg.clean(repo, node, show_stats=False)
529 repo.dirstate.setbranch(branch)
529 repo.dirstate.setbranch(branch)
530 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
530 cmdutil.revert(ui, repo, rctx, repo.dirstate.parents())
531
531
532
532
533 def commitfunc(ui, repo, message, match, opts):
533 def commitfunc(ui, repo, message, match, opts):
534 editform = 'backout'
534 editform = 'backout'
535 e = cmdutil.getcommiteditor(editform=editform, **opts)
535 e = cmdutil.getcommiteditor(editform=editform, **opts)
536 if not message:
536 if not message:
537 # we don't translate commit messages
537 # we don't translate commit messages
538 message = "Backed out changeset %s" % short(node)
538 message = "Backed out changeset %s" % short(node)
539 e = cmdutil.getcommiteditor(edit=True, editform=editform)
539 e = cmdutil.getcommiteditor(edit=True, editform=editform)
540 return repo.commit(message, opts.get('user'), opts.get('date'),
540 return repo.commit(message, opts.get('user'), opts.get('date'),
541 match, editor=e)
541 match, editor=e)
542 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
542 newnode = cmdutil.commit(ui, repo, commitfunc, [], opts)
543 if not newnode:
543 if not newnode:
544 ui.status(_("nothing changed\n"))
544 ui.status(_("nothing changed\n"))
545 return 1
545 return 1
546 cmdutil.commitstatus(repo, newnode, branch, bheads)
546 cmdutil.commitstatus(repo, newnode, branch, bheads)
547
547
548 def nice(node):
548 def nice(node):
549 return '%d:%s' % (repo.changelog.rev(node), short(node))
549 return '%d:%s' % (repo.changelog.rev(node), short(node))
550 ui.status(_('changeset %s backs out changeset %s\n') %
550 ui.status(_('changeset %s backs out changeset %s\n') %
551 (nice(repo.changelog.tip()), nice(node)))
551 (nice(repo.changelog.tip()), nice(node)))
552 if opts.get('merge') and op1 != node:
552 if opts.get('merge') and op1 != node:
553 hg.clean(repo, op1, show_stats=False)
553 hg.clean(repo, op1, show_stats=False)
554 ui.status(_('merging with changeset %s\n')
554 ui.status(_('merging with changeset %s\n')
555 % nice(repo.changelog.tip()))
555 % nice(repo.changelog.tip()))
556 try:
556 try:
557 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
557 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
558 'backout')
558 'backout')
559 return hg.merge(repo, hex(repo.changelog.tip()))
559 return hg.merge(repo, hex(repo.changelog.tip()))
560 finally:
560 finally:
561 ui.setconfig('ui', 'forcemerge', '', '')
561 ui.setconfig('ui', 'forcemerge', '', '')
562 finally:
562 finally:
563 wlock.release()
563 wlock.release()
564 return 0
564 return 0
565
565
566 @command('bisect',
566 @command('bisect',
567 [('r', 'reset', False, _('reset bisect state')),
567 [('r', 'reset', False, _('reset bisect state')),
568 ('g', 'good', False, _('mark changeset good')),
568 ('g', 'good', False, _('mark changeset good')),
569 ('b', 'bad', False, _('mark changeset bad')),
569 ('b', 'bad', False, _('mark changeset bad')),
570 ('s', 'skip', False, _('skip testing changeset')),
570 ('s', 'skip', False, _('skip testing changeset')),
571 ('e', 'extend', False, _('extend the bisect range')),
571 ('e', 'extend', False, _('extend the bisect range')),
572 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
572 ('c', 'command', '', _('use command to check changeset state'), _('CMD')),
573 ('U', 'noupdate', False, _('do not update to target'))],
573 ('U', 'noupdate', False, _('do not update to target'))],
574 _("[-gbsr] [-U] [-c CMD] [REV]"))
574 _("[-gbsr] [-U] [-c CMD] [REV]"))
575 def bisect(ui, repo, rev=None, extra=None, command=None,
575 def bisect(ui, repo, rev=None, extra=None, command=None,
576 reset=None, good=None, bad=None, skip=None, extend=None,
576 reset=None, good=None, bad=None, skip=None, extend=None,
577 noupdate=None):
577 noupdate=None):
578 """subdivision search of changesets
578 """subdivision search of changesets
579
579
580 This command helps to find changesets which introduce problems. To
580 This command helps to find changesets which introduce problems. To
581 use, mark the earliest changeset you know exhibits the problem as
581 use, mark the earliest changeset you know exhibits the problem as
582 bad, then mark the latest changeset which is free from the problem
582 bad, then mark the latest changeset which is free from the problem
583 as good. Bisect will update your working directory to a revision
583 as good. Bisect will update your working directory to a revision
584 for testing (unless the -U/--noupdate option is specified). Once
584 for testing (unless the -U/--noupdate option is specified). Once
585 you have performed tests, mark the working directory as good or
585 you have performed tests, mark the working directory as good or
586 bad, and bisect will either update to another candidate changeset
586 bad, and bisect will either update to another candidate changeset
587 or announce that it has found the bad revision.
587 or announce that it has found the bad revision.
588
588
589 As a shortcut, you can also use the revision argument to mark a
589 As a shortcut, you can also use the revision argument to mark a
590 revision as good or bad without checking it out first.
590 revision as good or bad without checking it out first.
591
591
592 If you supply a command, it will be used for automatic bisection.
592 If you supply a command, it will be used for automatic bisection.
593 The environment variable HG_NODE will contain the ID of the
593 The environment variable HG_NODE will contain the ID of the
594 changeset being tested. The exit status of the command will be
594 changeset being tested. The exit status of the command will be
595 used to mark revisions as good or bad: status 0 means good, 125
595 used to mark revisions as good or bad: status 0 means good, 125
596 means to skip the revision, 127 (command not found) will abort the
596 means to skip the revision, 127 (command not found) will abort the
597 bisection, and any other non-zero exit status means the revision
597 bisection, and any other non-zero exit status means the revision
598 is bad.
598 is bad.
599
599
600 .. container:: verbose
600 .. container:: verbose
601
601
602 Some examples:
602 Some examples:
603
603
604 - start a bisection with known bad revision 34, and good revision 12::
604 - start a bisection with known bad revision 34, and good revision 12::
605
605
606 hg bisect --bad 34
606 hg bisect --bad 34
607 hg bisect --good 12
607 hg bisect --good 12
608
608
609 - advance the current bisection by marking current revision as good or
609 - advance the current bisection by marking current revision as good or
610 bad::
610 bad::
611
611
612 hg bisect --good
612 hg bisect --good
613 hg bisect --bad
613 hg bisect --bad
614
614
615 - mark the current revision, or a known revision, to be skipped (e.g. if
615 - mark the current revision, or a known revision, to be skipped (e.g. if
616 that revision is not usable because of another issue)::
616 that revision is not usable because of another issue)::
617
617
618 hg bisect --skip
618 hg bisect --skip
619 hg bisect --skip 23
619 hg bisect --skip 23
620
620
621 - skip all revisions that do not touch directories ``foo`` or ``bar``::
621 - skip all revisions that do not touch directories ``foo`` or ``bar``::
622
622
623 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
623 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
624
624
625 - forget the current bisection::
625 - forget the current bisection::
626
626
627 hg bisect --reset
627 hg bisect --reset
628
628
629 - use 'make && make tests' to automatically find the first broken
629 - use 'make && make tests' to automatically find the first broken
630 revision::
630 revision::
631
631
632 hg bisect --reset
632 hg bisect --reset
633 hg bisect --bad 34
633 hg bisect --bad 34
634 hg bisect --good 12
634 hg bisect --good 12
635 hg bisect --command "make && make tests"
635 hg bisect --command "make && make tests"
636
636
637 - see all changesets whose states are already known in the current
637 - see all changesets whose states are already known in the current
638 bisection::
638 bisection::
639
639
640 hg log -r "bisect(pruned)"
640 hg log -r "bisect(pruned)"
641
641
642 - see the changeset currently being bisected (especially useful
642 - see the changeset currently being bisected (especially useful
643 if running with -U/--noupdate)::
643 if running with -U/--noupdate)::
644
644
645 hg log -r "bisect(current)"
645 hg log -r "bisect(current)"
646
646
647 - see all changesets that took part in the current bisection::
647 - see all changesets that took part in the current bisection::
648
648
649 hg log -r "bisect(range)"
649 hg log -r "bisect(range)"
650
650
651 - you can even get a nice graph::
651 - you can even get a nice graph::
652
652
653 hg log --graph -r "bisect(range)"
653 hg log --graph -r "bisect(range)"
654
654
655 See :hg:`help revsets` for more about the `bisect()` keyword.
655 See :hg:`help revsets` for more about the `bisect()` keyword.
656
656
657 Returns 0 on success.
657 Returns 0 on success.
658 """
658 """
659 def extendbisectrange(nodes, good):
659 def extendbisectrange(nodes, good):
660 # bisect is incomplete when it ends on a merge node and
660 # bisect is incomplete when it ends on a merge node and
661 # one of the parent was not checked.
661 # one of the parent was not checked.
662 parents = repo[nodes[0]].parents()
662 parents = repo[nodes[0]].parents()
663 if len(parents) > 1:
663 if len(parents) > 1:
664 side = good and state['bad'] or state['good']
664 side = good and state['bad'] or state['good']
665 num = len(set(i.node() for i in parents) & set(side))
665 num = len(set(i.node() for i in parents) & set(side))
666 if num == 1:
666 if num == 1:
667 return parents[0].ancestor(parents[1])
667 return parents[0].ancestor(parents[1])
668 return None
668 return None
669
669
670 def print_result(nodes, good):
670 def print_result(nodes, good):
671 displayer = cmdutil.show_changeset(ui, repo, {})
671 displayer = cmdutil.show_changeset(ui, repo, {})
672 if len(nodes) == 1:
672 if len(nodes) == 1:
673 # narrowed it down to a single revision
673 # narrowed it down to a single revision
674 if good:
674 if good:
675 ui.write(_("The first good revision is:\n"))
675 ui.write(_("The first good revision is:\n"))
676 else:
676 else:
677 ui.write(_("The first bad revision is:\n"))
677 ui.write(_("The first bad revision is:\n"))
678 displayer.show(repo[nodes[0]])
678 displayer.show(repo[nodes[0]])
679 extendnode = extendbisectrange(nodes, good)
679 extendnode = extendbisectrange(nodes, good)
680 if extendnode is not None:
680 if extendnode is not None:
681 ui.write(_('Not all ancestors of this changeset have been'
681 ui.write(_('Not all ancestors of this changeset have been'
682 ' checked.\nUse bisect --extend to continue the '
682 ' checked.\nUse bisect --extend to continue the '
683 'bisection from\nthe common ancestor, %s.\n')
683 'bisection from\nthe common ancestor, %s.\n')
684 % extendnode)
684 % extendnode)
685 else:
685 else:
686 # multiple possible revisions
686 # multiple possible revisions
687 if good:
687 if good:
688 ui.write(_("Due to skipped revisions, the first "
688 ui.write(_("Due to skipped revisions, the first "
689 "good revision could be any of:\n"))
689 "good revision could be any of:\n"))
690 else:
690 else:
691 ui.write(_("Due to skipped revisions, the first "
691 ui.write(_("Due to skipped revisions, the first "
692 "bad revision could be any of:\n"))
692 "bad revision could be any of:\n"))
693 for n in nodes:
693 for n in nodes:
694 displayer.show(repo[n])
694 displayer.show(repo[n])
695 displayer.close()
695 displayer.close()
696
696
697 def check_state(state, interactive=True):
697 def check_state(state, interactive=True):
698 if not state['good'] or not state['bad']:
698 if not state['good'] or not state['bad']:
699 if (good or bad or skip or reset) and interactive:
699 if (good or bad or skip or reset) and interactive:
700 return
700 return
701 if not state['good']:
701 if not state['good']:
702 raise util.Abort(_('cannot bisect (no known good revisions)'))
702 raise util.Abort(_('cannot bisect (no known good revisions)'))
703 else:
703 else:
704 raise util.Abort(_('cannot bisect (no known bad revisions)'))
704 raise util.Abort(_('cannot bisect (no known bad revisions)'))
705 return True
705 return True
706
706
707 # backward compatibility
707 # backward compatibility
708 if rev in "good bad reset init".split():
708 if rev in "good bad reset init".split():
709 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
709 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
710 cmd, rev, extra = rev, extra, None
710 cmd, rev, extra = rev, extra, None
711 if cmd == "good":
711 if cmd == "good":
712 good = True
712 good = True
713 elif cmd == "bad":
713 elif cmd == "bad":
714 bad = True
714 bad = True
715 else:
715 else:
716 reset = True
716 reset = True
717 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
717 elif extra or good + bad + skip + reset + extend + bool(command) > 1:
718 raise util.Abort(_('incompatible arguments'))
718 raise util.Abort(_('incompatible arguments'))
719
719
720 cmdutil.checkunfinished(repo)
720 cmdutil.checkunfinished(repo)
721
721
722 if reset:
722 if reset:
723 p = repo.join("bisect.state")
723 p = repo.join("bisect.state")
724 if os.path.exists(p):
724 if os.path.exists(p):
725 os.unlink(p)
725 os.unlink(p)
726 return
726 return
727
727
728 state = hbisect.load_state(repo)
728 state = hbisect.load_state(repo)
729
729
730 if command:
730 if command:
731 changesets = 1
731 changesets = 1
732 if noupdate:
732 if noupdate:
733 try:
733 try:
734 node = state['current'][0]
734 node = state['current'][0]
735 except LookupError:
735 except LookupError:
736 raise util.Abort(_('current bisect revision is unknown - '
736 raise util.Abort(_('current bisect revision is unknown - '
737 'start a new bisect to fix'))
737 'start a new bisect to fix'))
738 else:
738 else:
739 node, p2 = repo.dirstate.parents()
739 node, p2 = repo.dirstate.parents()
740 if p2 != nullid:
740 if p2 != nullid:
741 raise util.Abort(_('current bisect revision is a merge'))
741 raise util.Abort(_('current bisect revision is a merge'))
742 try:
742 try:
743 while changesets:
743 while changesets:
744 # update state
744 # update state
745 state['current'] = [node]
745 state['current'] = [node]
746 hbisect.save_state(repo, state)
746 hbisect.save_state(repo, state)
747 status = ui.system(command, environ={'HG_NODE': hex(node)})
747 status = ui.system(command, environ={'HG_NODE': hex(node)})
748 if status == 125:
748 if status == 125:
749 transition = "skip"
749 transition = "skip"
750 elif status == 0:
750 elif status == 0:
751 transition = "good"
751 transition = "good"
752 # status < 0 means process was killed
752 # status < 0 means process was killed
753 elif status == 127:
753 elif status == 127:
754 raise util.Abort(_("failed to execute %s") % command)
754 raise util.Abort(_("failed to execute %s") % command)
755 elif status < 0:
755 elif status < 0:
756 raise util.Abort(_("%s killed") % command)
756 raise util.Abort(_("%s killed") % command)
757 else:
757 else:
758 transition = "bad"
758 transition = "bad"
759 ctx = scmutil.revsingle(repo, rev, node)
759 ctx = scmutil.revsingle(repo, rev, node)
760 rev = None # clear for future iterations
760 rev = None # clear for future iterations
761 state[transition].append(ctx.node())
761 state[transition].append(ctx.node())
762 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
762 ui.status(_('changeset %d:%s: %s\n') % (ctx, ctx, transition))
763 check_state(state, interactive=False)
763 check_state(state, interactive=False)
764 # bisect
764 # bisect
765 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
765 nodes, changesets, bgood = hbisect.bisect(repo.changelog, state)
766 # update to next check
766 # update to next check
767 node = nodes[0]
767 node = nodes[0]
768 if not noupdate:
768 if not noupdate:
769 cmdutil.bailifchanged(repo)
769 cmdutil.bailifchanged(repo)
770 hg.clean(repo, node, show_stats=False)
770 hg.clean(repo, node, show_stats=False)
771 finally:
771 finally:
772 state['current'] = [node]
772 state['current'] = [node]
773 hbisect.save_state(repo, state)
773 hbisect.save_state(repo, state)
774 print_result(nodes, bgood)
774 print_result(nodes, bgood)
775 return
775 return
776
776
777 # update state
777 # update state
778
778
779 if rev:
779 if rev:
780 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
780 nodes = [repo.lookup(i) for i in scmutil.revrange(repo, [rev])]
781 else:
781 else:
782 nodes = [repo.lookup('.')]
782 nodes = [repo.lookup('.')]
783
783
784 if good or bad or skip:
784 if good or bad or skip:
785 if good:
785 if good:
786 state['good'] += nodes
786 state['good'] += nodes
787 elif bad:
787 elif bad:
788 state['bad'] += nodes
788 state['bad'] += nodes
789 elif skip:
789 elif skip:
790 state['skip'] += nodes
790 state['skip'] += nodes
791 hbisect.save_state(repo, state)
791 hbisect.save_state(repo, state)
792
792
793 if not check_state(state):
793 if not check_state(state):
794 return
794 return
795
795
796 # actually bisect
796 # actually bisect
797 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
797 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
798 if extend:
798 if extend:
799 if not changesets:
799 if not changesets:
800 extendnode = extendbisectrange(nodes, good)
800 extendnode = extendbisectrange(nodes, good)
801 if extendnode is not None:
801 if extendnode is not None:
802 ui.write(_("Extending search to changeset %d:%s\n")
802 ui.write(_("Extending search to changeset %d:%s\n")
803 % (extendnode.rev(), extendnode))
803 % (extendnode.rev(), extendnode))
804 state['current'] = [extendnode.node()]
804 state['current'] = [extendnode.node()]
805 hbisect.save_state(repo, state)
805 hbisect.save_state(repo, state)
806 if noupdate:
806 if noupdate:
807 return
807 return
808 cmdutil.bailifchanged(repo)
808 cmdutil.bailifchanged(repo)
809 return hg.clean(repo, extendnode.node())
809 return hg.clean(repo, extendnode.node())
810 raise util.Abort(_("nothing to extend"))
810 raise util.Abort(_("nothing to extend"))
811
811
812 if changesets == 0:
812 if changesets == 0:
813 print_result(nodes, good)
813 print_result(nodes, good)
814 else:
814 else:
815 assert len(nodes) == 1 # only a single node can be tested next
815 assert len(nodes) == 1 # only a single node can be tested next
816 node = nodes[0]
816 node = nodes[0]
817 # compute the approximate number of remaining tests
817 # compute the approximate number of remaining tests
818 tests, size = 0, 2
818 tests, size = 0, 2
819 while size <= changesets:
819 while size <= changesets:
820 tests, size = tests + 1, size * 2
820 tests, size = tests + 1, size * 2
821 rev = repo.changelog.rev(node)
821 rev = repo.changelog.rev(node)
822 ui.write(_("Testing changeset %d:%s "
822 ui.write(_("Testing changeset %d:%s "
823 "(%d changesets remaining, ~%d tests)\n")
823 "(%d changesets remaining, ~%d tests)\n")
824 % (rev, short(node), changesets, tests))
824 % (rev, short(node), changesets, tests))
825 state['current'] = [node]
825 state['current'] = [node]
826 hbisect.save_state(repo, state)
826 hbisect.save_state(repo, state)
827 if not noupdate:
827 if not noupdate:
828 cmdutil.bailifchanged(repo)
828 cmdutil.bailifchanged(repo)
829 return hg.clean(repo, node)
829 return hg.clean(repo, node)
830
830
831 @command('bookmarks|bookmark',
831 @command('bookmarks|bookmark',
832 [('f', 'force', False, _('force')),
832 [('f', 'force', False, _('force')),
833 ('r', 'rev', '', _('revision'), _('REV')),
833 ('r', 'rev', '', _('revision'), _('REV')),
834 ('d', 'delete', False, _('delete a given bookmark')),
834 ('d', 'delete', False, _('delete a given bookmark')),
835 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
835 ('m', 'rename', '', _('rename a given bookmark'), _('NAME')),
836 ('i', 'inactive', False, _('mark a bookmark inactive')),
836 ('i', 'inactive', False, _('mark a bookmark inactive')),
837 ] + formatteropts,
837 ] + formatteropts,
838 _('hg bookmarks [OPTIONS]... [NAME]...'))
838 _('hg bookmarks [OPTIONS]... [NAME]...'))
839 def bookmark(ui, repo, *names, **opts):
839 def bookmark(ui, repo, *names, **opts):
840 '''create a new bookmark or list existing bookmarks
840 '''create a new bookmark or list existing bookmarks
841
841
842 Bookmarks are labels on changesets to help track lines of development.
842 Bookmarks are labels on changesets to help track lines of development.
843 Bookmarks are unversioned and can be moved, renamed and deleted.
843 Bookmarks are unversioned and can be moved, renamed and deleted.
844 Deleting or moving a bookmark has no effect on the associated changesets.
844 Deleting or moving a bookmark has no effect on the associated changesets.
845
845
846 Creating or updating to a bookmark causes it to be marked as 'active'.
846 Creating or updating to a bookmark causes it to be marked as 'active'.
847 The active bookmark is indicated with a '*'.
847 The active bookmark is indicated with a '*'.
848 When a commit is made, the active bookmark will advance to the new commit.
848 When a commit is made, the active bookmark will advance to the new commit.
849 A plain :hg:`update` will also advance an active bookmark, if possible.
849 A plain :hg:`update` will also advance an active bookmark, if possible.
850 Updating away from a bookmark will cause it to be deactivated.
850 Updating away from a bookmark will cause it to be deactivated.
851
851
852 Bookmarks can be pushed and pulled between repositories (see
852 Bookmarks can be pushed and pulled between repositories (see
853 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
853 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
854 diverged, a new 'divergent bookmark' of the form 'name@path' will
854 diverged, a new 'divergent bookmark' of the form 'name@path' will
855 be created. Using :hg:`merge` will resolve the divergence.
855 be created. Using :hg:`merge` will resolve the divergence.
856
856
857 A bookmark named '@' has the special property that :hg:`clone` will
857 A bookmark named '@' has the special property that :hg:`clone` will
858 check it out by default if it exists.
858 check it out by default if it exists.
859
859
860 .. container:: verbose
860 .. container:: verbose
861
861
862 Examples:
862 Examples:
863
863
864 - create an active bookmark for a new line of development::
864 - create an active bookmark for a new line of development::
865
865
866 hg book new-feature
866 hg book new-feature
867
867
868 - create an inactive bookmark as a place marker::
868 - create an inactive bookmark as a place marker::
869
869
870 hg book -i reviewed
870 hg book -i reviewed
871
871
872 - create an inactive bookmark on another changeset::
872 - create an inactive bookmark on another changeset::
873
873
874 hg book -r .^ tested
874 hg book -r .^ tested
875
875
876 - move the '@' bookmark from another branch::
876 - move the '@' bookmark from another branch::
877
877
878 hg book -f @
878 hg book -f @
879 '''
879 '''
880 force = opts.get('force')
880 force = opts.get('force')
881 rev = opts.get('rev')
881 rev = opts.get('rev')
882 delete = opts.get('delete')
882 delete = opts.get('delete')
883 rename = opts.get('rename')
883 rename = opts.get('rename')
884 inactive = opts.get('inactive')
884 inactive = opts.get('inactive')
885
885
886 def checkformat(mark):
886 def checkformat(mark):
887 mark = mark.strip()
887 mark = mark.strip()
888 if not mark:
888 if not mark:
889 raise util.Abort(_("bookmark names cannot consist entirely of "
889 raise util.Abort(_("bookmark names cannot consist entirely of "
890 "whitespace"))
890 "whitespace"))
891 scmutil.checknewlabel(repo, mark, 'bookmark')
891 scmutil.checknewlabel(repo, mark, 'bookmark')
892 return mark
892 return mark
893
893
894 def checkconflict(repo, mark, cur, force=False, target=None):
894 def checkconflict(repo, mark, cur, force=False, target=None):
895 if mark in marks and not force:
895 if mark in marks and not force:
896 if target:
896 if target:
897 if marks[mark] == target and target == cur:
897 if marks[mark] == target and target == cur:
898 # re-activating a bookmark
898 # re-activating a bookmark
899 return
899 return
900 anc = repo.changelog.ancestors([repo[target].rev()])
900 anc = repo.changelog.ancestors([repo[target].rev()])
901 bmctx = repo[marks[mark]]
901 bmctx = repo[marks[mark]]
902 divs = [repo[b].node() for b in marks
902 divs = [repo[b].node() for b in marks
903 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
903 if b.split('@', 1)[0] == mark.split('@', 1)[0]]
904
904
905 # allow resolving a single divergent bookmark even if moving
905 # allow resolving a single divergent bookmark even if moving
906 # the bookmark across branches when a revision is specified
906 # the bookmark across branches when a revision is specified
907 # that contains a divergent bookmark
907 # that contains a divergent bookmark
908 if bmctx.rev() not in anc and target in divs:
908 if bmctx.rev() not in anc and target in divs:
909 bookmarks.deletedivergent(repo, [target], mark)
909 bookmarks.deletedivergent(repo, [target], mark)
910 return
910 return
911
911
912 deletefrom = [b for b in divs
912 deletefrom = [b for b in divs
913 if repo[b].rev() in anc or b == target]
913 if repo[b].rev() in anc or b == target]
914 bookmarks.deletedivergent(repo, deletefrom, mark)
914 bookmarks.deletedivergent(repo, deletefrom, mark)
915 if bookmarks.validdest(repo, bmctx, repo[target]):
915 if bookmarks.validdest(repo, bmctx, repo[target]):
916 ui.status(_("moving bookmark '%s' forward from %s\n") %
916 ui.status(_("moving bookmark '%s' forward from %s\n") %
917 (mark, short(bmctx.node())))
917 (mark, short(bmctx.node())))
918 return
918 return
919 raise util.Abort(_("bookmark '%s' already exists "
919 raise util.Abort(_("bookmark '%s' already exists "
920 "(use -f to force)") % mark)
920 "(use -f to force)") % mark)
921 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
921 if ((mark in repo.branchmap() or mark == repo.dirstate.branch())
922 and not force):
922 and not force):
923 raise util.Abort(
923 raise util.Abort(
924 _("a bookmark cannot have the name of an existing branch"))
924 _("a bookmark cannot have the name of an existing branch"))
925
925
926 if delete and rename:
926 if delete and rename:
927 raise util.Abort(_("--delete and --rename are incompatible"))
927 raise util.Abort(_("--delete and --rename are incompatible"))
928 if delete and rev:
928 if delete and rev:
929 raise util.Abort(_("--rev is incompatible with --delete"))
929 raise util.Abort(_("--rev is incompatible with --delete"))
930 if rename and rev:
930 if rename and rev:
931 raise util.Abort(_("--rev is incompatible with --rename"))
931 raise util.Abort(_("--rev is incompatible with --rename"))
932 if not names and (delete or rev):
932 if not names and (delete or rev):
933 raise util.Abort(_("bookmark name required"))
933 raise util.Abort(_("bookmark name required"))
934
934
935 if delete or rename or names or inactive:
935 if delete or rename or names or inactive:
936 wlock = repo.wlock()
936 wlock = repo.wlock()
937 try:
937 try:
938 cur = repo.changectx('.').node()
938 cur = repo.changectx('.').node()
939 marks = repo._bookmarks
939 marks = repo._bookmarks
940 if delete:
940 if delete:
941 for mark in names:
941 for mark in names:
942 if mark not in marks:
942 if mark not in marks:
943 raise util.Abort(_("bookmark '%s' does not exist") %
943 raise util.Abort(_("bookmark '%s' does not exist") %
944 mark)
944 mark)
945 if mark == repo._bookmarkcurrent:
945 if mark == repo._bookmarkcurrent:
946 bookmarks.unsetcurrent(repo)
946 bookmarks.unsetcurrent(repo)
947 del marks[mark]
947 del marks[mark]
948 marks.write()
948 marks.write()
949
949
950 elif rename:
950 elif rename:
951 if not names:
951 if not names:
952 raise util.Abort(_("new bookmark name required"))
952 raise util.Abort(_("new bookmark name required"))
953 elif len(names) > 1:
953 elif len(names) > 1:
954 raise util.Abort(_("only one new bookmark name allowed"))
954 raise util.Abort(_("only one new bookmark name allowed"))
955 mark = checkformat(names[0])
955 mark = checkformat(names[0])
956 if rename not in marks:
956 if rename not in marks:
957 raise util.Abort(_("bookmark '%s' does not exist") % rename)
957 raise util.Abort(_("bookmark '%s' does not exist") % rename)
958 checkconflict(repo, mark, cur, force)
958 checkconflict(repo, mark, cur, force)
959 marks[mark] = marks[rename]
959 marks[mark] = marks[rename]
960 if repo._bookmarkcurrent == rename and not inactive:
960 if repo._bookmarkcurrent == rename and not inactive:
961 bookmarks.setcurrent(repo, mark)
961 bookmarks.setcurrent(repo, mark)
962 del marks[rename]
962 del marks[rename]
963 marks.write()
963 marks.write()
964
964
965 elif names:
965 elif names:
966 newact = None
966 newact = None
967 for mark in names:
967 for mark in names:
968 mark = checkformat(mark)
968 mark = checkformat(mark)
969 if newact is None:
969 if newact is None:
970 newact = mark
970 newact = mark
971 if inactive and mark == repo._bookmarkcurrent:
971 if inactive and mark == repo._bookmarkcurrent:
972 bookmarks.unsetcurrent(repo)
972 bookmarks.unsetcurrent(repo)
973 return
973 return
974 tgt = cur
974 tgt = cur
975 if rev:
975 if rev:
976 tgt = scmutil.revsingle(repo, rev).node()
976 tgt = scmutil.revsingle(repo, rev).node()
977 checkconflict(repo, mark, cur, force, tgt)
977 checkconflict(repo, mark, cur, force, tgt)
978 marks[mark] = tgt
978 marks[mark] = tgt
979 if not inactive and cur == marks[newact] and not rev:
979 if not inactive and cur == marks[newact] and not rev:
980 bookmarks.setcurrent(repo, newact)
980 bookmarks.setcurrent(repo, newact)
981 elif cur != tgt and newact == repo._bookmarkcurrent:
981 elif cur != tgt and newact == repo._bookmarkcurrent:
982 bookmarks.unsetcurrent(repo)
982 bookmarks.unsetcurrent(repo)
983 marks.write()
983 marks.write()
984
984
985 elif inactive:
985 elif inactive:
986 if len(marks) == 0:
986 if len(marks) == 0:
987 ui.status(_("no bookmarks set\n"))
987 ui.status(_("no bookmarks set\n"))
988 elif not repo._bookmarkcurrent:
988 elif not repo._bookmarkcurrent:
989 ui.status(_("no active bookmark\n"))
989 ui.status(_("no active bookmark\n"))
990 else:
990 else:
991 bookmarks.unsetcurrent(repo)
991 bookmarks.unsetcurrent(repo)
992 finally:
992 finally:
993 wlock.release()
993 wlock.release()
994 else: # show bookmarks
994 else: # show bookmarks
995 fm = ui.formatter('bookmarks', opts)
995 fm = ui.formatter('bookmarks', opts)
996 hexfn = fm.hexfunc
996 hexfn = fm.hexfunc
997 marks = repo._bookmarks
997 marks = repo._bookmarks
998 if len(marks) == 0 and not fm:
998 if len(marks) == 0 and not fm:
999 ui.status(_("no bookmarks set\n"))
999 ui.status(_("no bookmarks set\n"))
1000 for bmark, n in sorted(marks.iteritems()):
1000 for bmark, n in sorted(marks.iteritems()):
1001 current = repo._bookmarkcurrent
1001 current = repo._bookmarkcurrent
1002 if bmark == current:
1002 if bmark == current:
1003 prefix, label = '*', 'bookmarks.current'
1003 prefix, label = '*', 'bookmarks.current'
1004 else:
1004 else:
1005 prefix, label = ' ', ''
1005 prefix, label = ' ', ''
1006
1006
1007 fm.startitem()
1007 fm.startitem()
1008 if not ui.quiet:
1008 if not ui.quiet:
1009 fm.plain(' %s ' % prefix, label=label)
1009 fm.plain(' %s ' % prefix, label=label)
1010 fm.write('bookmark', '%s', bmark, label=label)
1010 fm.write('bookmark', '%s', bmark, label=label)
1011 pad = " " * (25 - encoding.colwidth(bmark))
1011 pad = " " * (25 - encoding.colwidth(bmark))
1012 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1012 fm.condwrite(not ui.quiet, 'rev node', pad + ' %d:%s',
1013 repo.changelog.rev(n), hexfn(n), label=label)
1013 repo.changelog.rev(n), hexfn(n), label=label)
1014 fm.data(active=(bmark == current))
1014 fm.data(active=(bmark == current))
1015 fm.plain('\n')
1015 fm.plain('\n')
1016 fm.end()
1016 fm.end()
1017
1017
1018 @command('branch',
1018 @command('branch',
1019 [('f', 'force', None,
1019 [('f', 'force', None,
1020 _('set branch name even if it shadows an existing branch')),
1020 _('set branch name even if it shadows an existing branch')),
1021 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1021 ('C', 'clean', None, _('reset branch name to parent branch name'))],
1022 _('[-fC] [NAME]'))
1022 _('[-fC] [NAME]'))
1023 def branch(ui, repo, label=None, **opts):
1023 def branch(ui, repo, label=None, **opts):
1024 """set or show the current branch name
1024 """set or show the current branch name
1025
1025
1026 .. note::
1026 .. note::
1027
1027
1028 Branch names are permanent and global. Use :hg:`bookmark` to create a
1028 Branch names are permanent and global. Use :hg:`bookmark` to create a
1029 light-weight bookmark instead. See :hg:`help glossary` for more
1029 light-weight bookmark instead. See :hg:`help glossary` for more
1030 information about named branches and bookmarks.
1030 information about named branches and bookmarks.
1031
1031
1032 With no argument, show the current branch name. With one argument,
1032 With no argument, show the current branch name. With one argument,
1033 set the working directory branch name (the branch will not exist
1033 set the working directory branch name (the branch will not exist
1034 in the repository until the next commit). Standard practice
1034 in the repository until the next commit). Standard practice
1035 recommends that primary development take place on the 'default'
1035 recommends that primary development take place on the 'default'
1036 branch.
1036 branch.
1037
1037
1038 Unless -f/--force is specified, branch will not let you set a
1038 Unless -f/--force is specified, branch will not let you set a
1039 branch name that already exists, even if it's inactive.
1039 branch name that already exists, even if it's inactive.
1040
1040
1041 Use -C/--clean to reset the working directory branch to that of
1041 Use -C/--clean to reset the working directory branch to that of
1042 the parent of the working directory, negating a previous branch
1042 the parent of the working directory, negating a previous branch
1043 change.
1043 change.
1044
1044
1045 Use the command :hg:`update` to switch to an existing branch. Use
1045 Use the command :hg:`update` to switch to an existing branch. Use
1046 :hg:`commit --close-branch` to mark this branch as closed.
1046 :hg:`commit --close-branch` to mark this branch as closed.
1047
1047
1048 Returns 0 on success.
1048 Returns 0 on success.
1049 """
1049 """
1050 if label:
1050 if label:
1051 label = label.strip()
1051 label = label.strip()
1052
1052
1053 if not opts.get('clean') and not label:
1053 if not opts.get('clean') and not label:
1054 ui.write("%s\n" % repo.dirstate.branch())
1054 ui.write("%s\n" % repo.dirstate.branch())
1055 return
1055 return
1056
1056
1057 wlock = repo.wlock()
1057 wlock = repo.wlock()
1058 try:
1058 try:
1059 if opts.get('clean'):
1059 if opts.get('clean'):
1060 label = repo[None].p1().branch()
1060 label = repo[None].p1().branch()
1061 repo.dirstate.setbranch(label)
1061 repo.dirstate.setbranch(label)
1062 ui.status(_('reset working directory to branch %s\n') % label)
1062 ui.status(_('reset working directory to branch %s\n') % label)
1063 elif label:
1063 elif label:
1064 if not opts.get('force') and label in repo.branchmap():
1064 if not opts.get('force') and label in repo.branchmap():
1065 if label not in [p.branch() for p in repo.parents()]:
1065 if label not in [p.branch() for p in repo.parents()]:
1066 raise util.Abort(_('a branch of the same name already'
1066 raise util.Abort(_('a branch of the same name already'
1067 ' exists'),
1067 ' exists'),
1068 # i18n: "it" refers to an existing branch
1068 # i18n: "it" refers to an existing branch
1069 hint=_("use 'hg update' to switch to it"))
1069 hint=_("use 'hg update' to switch to it"))
1070 scmutil.checknewlabel(repo, label, 'branch')
1070 scmutil.checknewlabel(repo, label, 'branch')
1071 repo.dirstate.setbranch(label)
1071 repo.dirstate.setbranch(label)
1072 ui.status(_('marked working directory as branch %s\n') % label)
1072 ui.status(_('marked working directory as branch %s\n') % label)
1073 ui.status(_('(branches are permanent and global, '
1073 ui.status(_('(branches are permanent and global, '
1074 'did you want a bookmark?)\n'))
1074 'did you want a bookmark?)\n'))
1075 finally:
1075 finally:
1076 wlock.release()
1076 wlock.release()
1077
1077
1078 @command('branches',
1078 @command('branches',
1079 [('a', 'active', False, _('show only branches that have unmerged heads')),
1079 [('a', 'active', False, _('show only branches that have unmerged heads')),
1080 ('c', 'closed', False, _('show normal and closed branches')),
1080 ('c', 'closed', False, _('show normal and closed branches')),
1081 ] + formatteropts,
1081 ] + formatteropts,
1082 _('[-ac]'))
1082 _('[-ac]'))
1083 def branches(ui, repo, active=False, closed=False, **opts):
1083 def branches(ui, repo, active=False, closed=False, **opts):
1084 """list repository named branches
1084 """list repository named branches
1085
1085
1086 List the repository's named branches, indicating which ones are
1086 List the repository's named branches, indicating which ones are
1087 inactive. If -c/--closed is specified, also list branches which have
1087 inactive. If -c/--closed is specified, also list branches which have
1088 been marked closed (see :hg:`commit --close-branch`).
1088 been marked closed (see :hg:`commit --close-branch`).
1089
1089
1090 If -a/--active is specified, only show active branches. A branch
1090 If -a/--active is specified, only show active branches. A branch
1091 is considered active if it contains repository heads.
1091 is considered active if it contains repository heads.
1092
1092
1093 Use the command :hg:`update` to switch to an existing branch.
1093 Use the command :hg:`update` to switch to an existing branch.
1094
1094
1095 Returns 0.
1095 Returns 0.
1096 """
1096 """
1097
1097
1098 fm = ui.formatter('branches', opts)
1098 fm = ui.formatter('branches', opts)
1099 hexfunc = fm.hexfunc
1099 hexfunc = fm.hexfunc
1100
1100
1101 allheads = set(repo.heads())
1101 allheads = set(repo.heads())
1102 branches = []
1102 branches = []
1103 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1103 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1104 isactive = not isclosed and bool(set(heads) & allheads)
1104 isactive = not isclosed and bool(set(heads) & allheads)
1105 branches.append((tag, repo[tip], isactive, not isclosed))
1105 branches.append((tag, repo[tip], isactive, not isclosed))
1106 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1106 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]),
1107 reverse=True)
1107 reverse=True)
1108
1108
1109 for tag, ctx, isactive, isopen in branches:
1109 for tag, ctx, isactive, isopen in branches:
1110 if active and not isactive:
1110 if active and not isactive:
1111 continue
1111 continue
1112 if isactive:
1112 if isactive:
1113 label = 'branches.active'
1113 label = 'branches.active'
1114 notice = ''
1114 notice = ''
1115 elif not isopen:
1115 elif not isopen:
1116 if not closed:
1116 if not closed:
1117 continue
1117 continue
1118 label = 'branches.closed'
1118 label = 'branches.closed'
1119 notice = _(' (closed)')
1119 notice = _(' (closed)')
1120 else:
1120 else:
1121 label = 'branches.inactive'
1121 label = 'branches.inactive'
1122 notice = _(' (inactive)')
1122 notice = _(' (inactive)')
1123 current = (tag == repo.dirstate.branch())
1123 current = (tag == repo.dirstate.branch())
1124 if current:
1124 if current:
1125 label = 'branches.current'
1125 label = 'branches.current'
1126
1126
1127 fm.startitem()
1127 fm.startitem()
1128 fm.write('branch', '%s', tag, label=label)
1128 fm.write('branch', '%s', tag, label=label)
1129 rev = ctx.rev()
1129 rev = ctx.rev()
1130 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1130 padsize = max(31 - len(str(rev)) - encoding.colwidth(tag), 0)
1131 fmt = ' ' * padsize + ' %d:%s'
1131 fmt = ' ' * padsize + ' %d:%s'
1132 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1132 fm.condwrite(not ui.quiet, 'rev node', fmt, rev, hexfunc(ctx.node()),
1133 label='log.changeset changeset.%s' % ctx.phasestr())
1133 label='log.changeset changeset.%s' % ctx.phasestr())
1134 fm.data(active=isactive, closed=not isopen, current=current)
1134 fm.data(active=isactive, closed=not isopen, current=current)
1135 if not ui.quiet:
1135 if not ui.quiet:
1136 fm.plain(notice)
1136 fm.plain(notice)
1137 fm.plain('\n')
1137 fm.plain('\n')
1138 fm.end()
1138 fm.end()
1139
1139
1140 @command('bundle',
1140 @command('bundle',
1141 [('f', 'force', None, _('run even when the destination is unrelated')),
1141 [('f', 'force', None, _('run even when the destination is unrelated')),
1142 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1142 ('r', 'rev', [], _('a changeset intended to be added to the destination'),
1143 _('REV')),
1143 _('REV')),
1144 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1144 ('b', 'branch', [], _('a specific branch you would like to bundle'),
1145 _('BRANCH')),
1145 _('BRANCH')),
1146 ('', 'base', [],
1146 ('', 'base', [],
1147 _('a base changeset assumed to be available at the destination'),
1147 _('a base changeset assumed to be available at the destination'),
1148 _('REV')),
1148 _('REV')),
1149 ('a', 'all', None, _('bundle all changesets in the repository')),
1149 ('a', 'all', None, _('bundle all changesets in the repository')),
1150 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1150 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE')),
1151 ] + remoteopts,
1151 ] + remoteopts,
1152 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1152 _('[-f] [-t TYPE] [-a] [-r REV]... [--base REV]... FILE [DEST]'))
1153 def bundle(ui, repo, fname, dest=None, **opts):
1153 def bundle(ui, repo, fname, dest=None, **opts):
1154 """create a changegroup file
1154 """create a changegroup file
1155
1155
1156 Generate a compressed changegroup file collecting changesets not
1156 Generate a compressed changegroup file collecting changesets not
1157 known to be in another repository.
1157 known to be in another repository.
1158
1158
1159 If you omit the destination repository, then hg assumes the
1159 If you omit the destination repository, then hg assumes the
1160 destination will have all the nodes you specify with --base
1160 destination will have all the nodes you specify with --base
1161 parameters. To create a bundle containing all changesets, use
1161 parameters. To create a bundle containing all changesets, use
1162 -a/--all (or --base null).
1162 -a/--all (or --base null).
1163
1163
1164 You can change compression method with the -t/--type option.
1164 You can change compression method with the -t/--type option.
1165 The available compression methods are: none, bzip2, and
1165 The available compression methods are: none, bzip2, and
1166 gzip (by default, bundles are compressed using bzip2).
1166 gzip (by default, bundles are compressed using bzip2).
1167
1167
1168 The bundle file can then be transferred using conventional means
1168 The bundle file can then be transferred using conventional means
1169 and applied to another repository with the unbundle or pull
1169 and applied to another repository with the unbundle or pull
1170 command. This is useful when direct push and pull are not
1170 command. This is useful when direct push and pull are not
1171 available or when exporting an entire repository is undesirable.
1171 available or when exporting an entire repository is undesirable.
1172
1172
1173 Applying bundles preserves all changeset contents including
1173 Applying bundles preserves all changeset contents including
1174 permissions, copy/rename information, and revision history.
1174 permissions, copy/rename information, and revision history.
1175
1175
1176 Returns 0 on success, 1 if no changes found.
1176 Returns 0 on success, 1 if no changes found.
1177 """
1177 """
1178 revs = None
1178 revs = None
1179 if 'rev' in opts:
1179 if 'rev' in opts:
1180 revs = scmutil.revrange(repo, opts['rev'])
1180 revs = scmutil.revrange(repo, opts['rev'])
1181
1181
1182 bundletype = opts.get('type', 'bzip2').lower()
1182 bundletype = opts.get('type', 'bzip2').lower()
1183 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1183 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
1184 bundletype = btypes.get(bundletype)
1184 bundletype = btypes.get(bundletype)
1185 if bundletype not in changegroup.bundletypes:
1185 if bundletype not in changegroup.bundletypes:
1186 raise util.Abort(_('unknown bundle type specified with --type'))
1186 raise util.Abort(_('unknown bundle type specified with --type'))
1187
1187
1188 if opts.get('all'):
1188 if opts.get('all'):
1189 base = ['null']
1189 base = ['null']
1190 else:
1190 else:
1191 base = scmutil.revrange(repo, opts.get('base'))
1191 base = scmutil.revrange(repo, opts.get('base'))
1192 # TODO: get desired bundlecaps from command line.
1192 # TODO: get desired bundlecaps from command line.
1193 bundlecaps = None
1193 bundlecaps = None
1194 if base:
1194 if base:
1195 if dest:
1195 if dest:
1196 raise util.Abort(_("--base is incompatible with specifying "
1196 raise util.Abort(_("--base is incompatible with specifying "
1197 "a destination"))
1197 "a destination"))
1198 common = [repo.lookup(rev) for rev in base]
1198 common = [repo.lookup(rev) for rev in base]
1199 heads = revs and map(repo.lookup, revs) or revs
1199 heads = revs and map(repo.lookup, revs) or revs
1200 cg = changegroup.getchangegroup(repo, 'bundle', heads=heads,
1200 cg = changegroup.getchangegroup(repo, 'bundle', heads=heads,
1201 common=common, bundlecaps=bundlecaps)
1201 common=common, bundlecaps=bundlecaps)
1202 outgoing = None
1202 outgoing = None
1203 else:
1203 else:
1204 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1204 dest = ui.expandpath(dest or 'default-push', dest or 'default')
1205 dest, branches = hg.parseurl(dest, opts.get('branch'))
1205 dest, branches = hg.parseurl(dest, opts.get('branch'))
1206 other = hg.peer(repo, opts, dest)
1206 other = hg.peer(repo, opts, dest)
1207 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1207 revs, checkout = hg.addbranchrevs(repo, repo, branches, revs)
1208 heads = revs and map(repo.lookup, revs) or revs
1208 heads = revs and map(repo.lookup, revs) or revs
1209 outgoing = discovery.findcommonoutgoing(repo, other,
1209 outgoing = discovery.findcommonoutgoing(repo, other,
1210 onlyheads=heads,
1210 onlyheads=heads,
1211 force=opts.get('force'),
1211 force=opts.get('force'),
1212 portable=True)
1212 portable=True)
1213 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1213 cg = changegroup.getlocalchangegroup(repo, 'bundle', outgoing,
1214 bundlecaps)
1214 bundlecaps)
1215 if not cg:
1215 if not cg:
1216 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1216 scmutil.nochangesfound(ui, repo, outgoing and outgoing.excluded)
1217 return 1
1217 return 1
1218
1218
1219 changegroup.writebundle(cg, fname, bundletype)
1219 changegroup.writebundle(cg, fname, bundletype)
1220
1220
1221 @command('cat',
1221 @command('cat',
1222 [('o', 'output', '',
1222 [('o', 'output', '',
1223 _('print output to file with formatted name'), _('FORMAT')),
1223 _('print output to file with formatted name'), _('FORMAT')),
1224 ('r', 'rev', '', _('print the given revision'), _('REV')),
1224 ('r', 'rev', '', _('print the given revision'), _('REV')),
1225 ('', 'decode', None, _('apply any matching decode filter')),
1225 ('', 'decode', None, _('apply any matching decode filter')),
1226 ] + walkopts,
1226 ] + walkopts,
1227 _('[OPTION]... FILE...'),
1227 _('[OPTION]... FILE...'),
1228 inferrepo=True)
1228 inferrepo=True)
1229 def cat(ui, repo, file1, *pats, **opts):
1229 def cat(ui, repo, file1, *pats, **opts):
1230 """output the current or given revision of files
1230 """output the current or given revision of files
1231
1231
1232 Print the specified files as they were at the given revision. If
1232 Print the specified files as they were at the given revision. If
1233 no revision is given, the parent of the working directory is used.
1233 no revision is given, the parent of the working directory is used.
1234
1234
1235 Output may be to a file, in which case the name of the file is
1235 Output may be to a file, in which case the name of the file is
1236 given using a format string. The formatting rules as follows:
1236 given using a format string. The formatting rules as follows:
1237
1237
1238 :``%%``: literal "%" character
1238 :``%%``: literal "%" character
1239 :``%s``: basename of file being printed
1239 :``%s``: basename of file being printed
1240 :``%d``: dirname of file being printed, or '.' if in repository root
1240 :``%d``: dirname of file being printed, or '.' if in repository root
1241 :``%p``: root-relative path name of file being printed
1241 :``%p``: root-relative path name of file being printed
1242 :``%H``: changeset hash (40 hexadecimal digits)
1242 :``%H``: changeset hash (40 hexadecimal digits)
1243 :``%R``: changeset revision number
1243 :``%R``: changeset revision number
1244 :``%h``: short-form changeset hash (12 hexadecimal digits)
1244 :``%h``: short-form changeset hash (12 hexadecimal digits)
1245 :``%r``: zero-padded changeset revision number
1245 :``%r``: zero-padded changeset revision number
1246 :``%b``: basename of the exporting repository
1246 :``%b``: basename of the exporting repository
1247
1247
1248 Returns 0 on success.
1248 Returns 0 on success.
1249 """
1249 """
1250 ctx = scmutil.revsingle(repo, opts.get('rev'))
1250 ctx = scmutil.revsingle(repo, opts.get('rev'))
1251 m = scmutil.match(ctx, (file1,) + pats, opts)
1251 m = scmutil.match(ctx, (file1,) + pats, opts)
1252
1252
1253 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1253 return cmdutil.cat(ui, repo, ctx, m, '', **opts)
1254
1254
1255 @command('^clone',
1255 @command('^clone',
1256 [('U', 'noupdate', None,
1256 [('U', 'noupdate', None,
1257 _('the clone will include an empty working copy (only a repository)')),
1257 _('the clone will include an empty working copy (only a repository)')),
1258 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1258 ('u', 'updaterev', '', _('revision, tag or branch to check out'), _('REV')),
1259 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1259 ('r', 'rev', [], _('include the specified changeset'), _('REV')),
1260 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1260 ('b', 'branch', [], _('clone only the specified branch'), _('BRANCH')),
1261 ('', 'pull', None, _('use pull protocol to copy metadata')),
1261 ('', 'pull', None, _('use pull protocol to copy metadata')),
1262 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1262 ('', 'uncompressed', None, _('use uncompressed transfer (fast over LAN)')),
1263 ] + remoteopts,
1263 ] + remoteopts,
1264 _('[OPTION]... SOURCE [DEST]'),
1264 _('[OPTION]... SOURCE [DEST]'),
1265 norepo=True)
1265 norepo=True)
1266 def clone(ui, source, dest=None, **opts):
1266 def clone(ui, source, dest=None, **opts):
1267 """make a copy of an existing repository
1267 """make a copy of an existing repository
1268
1268
1269 Create a copy of an existing repository in a new directory.
1269 Create a copy of an existing repository in a new directory.
1270
1270
1271 If no destination directory name is specified, it defaults to the
1271 If no destination directory name is specified, it defaults to the
1272 basename of the source.
1272 basename of the source.
1273
1273
1274 The location of the source is added to the new repository's
1274 The location of the source is added to the new repository's
1275 ``.hg/hgrc`` file, as the default to be used for future pulls.
1275 ``.hg/hgrc`` file, as the default to be used for future pulls.
1276
1276
1277 Only local paths and ``ssh://`` URLs are supported as
1277 Only local paths and ``ssh://`` URLs are supported as
1278 destinations. For ``ssh://`` destinations, no working directory or
1278 destinations. For ``ssh://`` destinations, no working directory or
1279 ``.hg/hgrc`` will be created on the remote side.
1279 ``.hg/hgrc`` will be created on the remote side.
1280
1280
1281 To pull only a subset of changesets, specify one or more revisions
1281 To pull only a subset of changesets, specify one or more revisions
1282 identifiers with -r/--rev or branches with -b/--branch. The
1282 identifiers with -r/--rev or branches with -b/--branch. The
1283 resulting clone will contain only the specified changesets and
1283 resulting clone will contain only the specified changesets and
1284 their ancestors. These options (or 'clone src#rev dest') imply
1284 their ancestors. These options (or 'clone src#rev dest') imply
1285 --pull, even for local source repositories. Note that specifying a
1285 --pull, even for local source repositories. Note that specifying a
1286 tag will include the tagged changeset but not the changeset
1286 tag will include the tagged changeset but not the changeset
1287 containing the tag.
1287 containing the tag.
1288
1288
1289 If the source repository has a bookmark called '@' set, that
1289 If the source repository has a bookmark called '@' set, that
1290 revision will be checked out in the new repository by default.
1290 revision will be checked out in the new repository by default.
1291
1291
1292 To check out a particular version, use -u/--update, or
1292 To check out a particular version, use -u/--update, or
1293 -U/--noupdate to create a clone with no working directory.
1293 -U/--noupdate to create a clone with no working directory.
1294
1294
1295 .. container:: verbose
1295 .. container:: verbose
1296
1296
1297 For efficiency, hardlinks are used for cloning whenever the
1297 For efficiency, hardlinks are used for cloning whenever the
1298 source and destination are on the same filesystem (note this
1298 source and destination are on the same filesystem (note this
1299 applies only to the repository data, not to the working
1299 applies only to the repository data, not to the working
1300 directory). Some filesystems, such as AFS, implement hardlinking
1300 directory). Some filesystems, such as AFS, implement hardlinking
1301 incorrectly, but do not report errors. In these cases, use the
1301 incorrectly, but do not report errors. In these cases, use the
1302 --pull option to avoid hardlinking.
1302 --pull option to avoid hardlinking.
1303
1303
1304 In some cases, you can clone repositories and the working
1304 In some cases, you can clone repositories and the working
1305 directory using full hardlinks with ::
1305 directory using full hardlinks with ::
1306
1306
1307 $ cp -al REPO REPOCLONE
1307 $ cp -al REPO REPOCLONE
1308
1308
1309 This is the fastest way to clone, but it is not always safe. The
1309 This is the fastest way to clone, but it is not always safe. The
1310 operation is not atomic (making sure REPO is not modified during
1310 operation is not atomic (making sure REPO is not modified during
1311 the operation is up to you) and you have to make sure your
1311 the operation is up to you) and you have to make sure your
1312 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1312 editor breaks hardlinks (Emacs and most Linux Kernel tools do
1313 so). Also, this is not compatible with certain extensions that
1313 so). Also, this is not compatible with certain extensions that
1314 place their metadata under the .hg directory, such as mq.
1314 place their metadata under the .hg directory, such as mq.
1315
1315
1316 Mercurial will update the working directory to the first applicable
1316 Mercurial will update the working directory to the first applicable
1317 revision from this list:
1317 revision from this list:
1318
1318
1319 a) null if -U or the source repository has no changesets
1319 a) null if -U or the source repository has no changesets
1320 b) if -u . and the source repository is local, the first parent of
1320 b) if -u . and the source repository is local, the first parent of
1321 the source repository's working directory
1321 the source repository's working directory
1322 c) the changeset specified with -u (if a branch name, this means the
1322 c) the changeset specified with -u (if a branch name, this means the
1323 latest head of that branch)
1323 latest head of that branch)
1324 d) the changeset specified with -r
1324 d) the changeset specified with -r
1325 e) the tipmost head specified with -b
1325 e) the tipmost head specified with -b
1326 f) the tipmost head specified with the url#branch source syntax
1326 f) the tipmost head specified with the url#branch source syntax
1327 g) the revision marked with the '@' bookmark, if present
1327 g) the revision marked with the '@' bookmark, if present
1328 h) the tipmost head of the default branch
1328 h) the tipmost head of the default branch
1329 i) tip
1329 i) tip
1330
1330
1331 Examples:
1331 Examples:
1332
1332
1333 - clone a remote repository to a new directory named hg/::
1333 - clone a remote repository to a new directory named hg/::
1334
1334
1335 hg clone http://selenic.com/hg
1335 hg clone http://selenic.com/hg
1336
1336
1337 - create a lightweight local clone::
1337 - create a lightweight local clone::
1338
1338
1339 hg clone project/ project-feature/
1339 hg clone project/ project-feature/
1340
1340
1341 - clone from an absolute path on an ssh server (note double-slash)::
1341 - clone from an absolute path on an ssh server (note double-slash)::
1342
1342
1343 hg clone ssh://user@server//home/projects/alpha/
1343 hg clone ssh://user@server//home/projects/alpha/
1344
1344
1345 - do a high-speed clone over a LAN while checking out a
1345 - do a high-speed clone over a LAN while checking out a
1346 specified version::
1346 specified version::
1347
1347
1348 hg clone --uncompressed http://server/repo -u 1.5
1348 hg clone --uncompressed http://server/repo -u 1.5
1349
1349
1350 - create a repository without changesets after a particular revision::
1350 - create a repository without changesets after a particular revision::
1351
1351
1352 hg clone -r 04e544 experimental/ good/
1352 hg clone -r 04e544 experimental/ good/
1353
1353
1354 - clone (and track) a particular named branch::
1354 - clone (and track) a particular named branch::
1355
1355
1356 hg clone http://selenic.com/hg#stable
1356 hg clone http://selenic.com/hg#stable
1357
1357
1358 See :hg:`help urls` for details on specifying URLs.
1358 See :hg:`help urls` for details on specifying URLs.
1359
1359
1360 Returns 0 on success.
1360 Returns 0 on success.
1361 """
1361 """
1362 if opts.get('noupdate') and opts.get('updaterev'):
1362 if opts.get('noupdate') and opts.get('updaterev'):
1363 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1363 raise util.Abort(_("cannot specify both --noupdate and --updaterev"))
1364
1364
1365 r = hg.clone(ui, opts, source, dest,
1365 r = hg.clone(ui, opts, source, dest,
1366 pull=opts.get('pull'),
1366 pull=opts.get('pull'),
1367 stream=opts.get('uncompressed'),
1367 stream=opts.get('uncompressed'),
1368 rev=opts.get('rev'),
1368 rev=opts.get('rev'),
1369 update=opts.get('updaterev') or not opts.get('noupdate'),
1369 update=opts.get('updaterev') or not opts.get('noupdate'),
1370 branch=opts.get('branch'))
1370 branch=opts.get('branch'))
1371
1371
1372 return r is None
1372 return r is None
1373
1373
1374 @command('^commit|ci',
1374 @command('^commit|ci',
1375 [('A', 'addremove', None,
1375 [('A', 'addremove', None,
1376 _('mark new/missing files as added/removed before committing')),
1376 _('mark new/missing files as added/removed before committing')),
1377 ('', 'close-branch', None,
1377 ('', 'close-branch', None,
1378 _('mark a branch as closed, hiding it from the branch list')),
1378 _('mark a branch as closed, hiding it from the branch list')),
1379 ('', 'amend', None, _('amend the parent of the working dir')),
1379 ('', 'amend', None, _('amend the parent of the working dir')),
1380 ('s', 'secret', None, _('use the secret phase for committing')),
1380 ('s', 'secret', None, _('use the secret phase for committing')),
1381 ('e', 'edit', None, _('invoke editor on commit messages')),
1381 ('e', 'edit', None, _('invoke editor on commit messages')),
1382 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1382 ] + walkopts + commitopts + commitopts2 + subrepoopts,
1383 _('[OPTION]... [FILE]...'),
1383 _('[OPTION]... [FILE]...'),
1384 inferrepo=True)
1384 inferrepo=True)
1385 def commit(ui, repo, *pats, **opts):
1385 def commit(ui, repo, *pats, **opts):
1386 """commit the specified files or all outstanding changes
1386 """commit the specified files or all outstanding changes
1387
1387
1388 Commit changes to the given files into the repository. Unlike a
1388 Commit changes to the given files into the repository. Unlike a
1389 centralized SCM, this operation is a local operation. See
1389 centralized SCM, this operation is a local operation. See
1390 :hg:`push` for a way to actively distribute your changes.
1390 :hg:`push` for a way to actively distribute your changes.
1391
1391
1392 If a list of files is omitted, all changes reported by :hg:`status`
1392 If a list of files is omitted, all changes reported by :hg:`status`
1393 will be committed.
1393 will be committed.
1394
1394
1395 If you are committing the result of a merge, do not provide any
1395 If you are committing the result of a merge, do not provide any
1396 filenames or -I/-X filters.
1396 filenames or -I/-X filters.
1397
1397
1398 If no commit message is specified, Mercurial starts your
1398 If no commit message is specified, Mercurial starts your
1399 configured editor where you can enter a message. In case your
1399 configured editor where you can enter a message. In case your
1400 commit fails, you will find a backup of your message in
1400 commit fails, you will find a backup of your message in
1401 ``.hg/last-message.txt``.
1401 ``.hg/last-message.txt``.
1402
1402
1403 The --amend flag can be used to amend the parent of the
1403 The --amend flag can be used to amend the parent of the
1404 working directory with a new commit that contains the changes
1404 working directory with a new commit that contains the changes
1405 in the parent in addition to those currently reported by :hg:`status`,
1405 in the parent in addition to those currently reported by :hg:`status`,
1406 if there are any. The old commit is stored in a backup bundle in
1406 if there are any. The old commit is stored in a backup bundle in
1407 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1407 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
1408 on how to restore it).
1408 on how to restore it).
1409
1409
1410 Message, user and date are taken from the amended commit unless
1410 Message, user and date are taken from the amended commit unless
1411 specified. When a message isn't specified on the command line,
1411 specified. When a message isn't specified on the command line,
1412 the editor will open with the message of the amended commit.
1412 the editor will open with the message of the amended commit.
1413
1413
1414 It is not possible to amend public changesets (see :hg:`help phases`)
1414 It is not possible to amend public changesets (see :hg:`help phases`)
1415 or changesets that have children.
1415 or changesets that have children.
1416
1416
1417 See :hg:`help dates` for a list of formats valid for -d/--date.
1417 See :hg:`help dates` for a list of formats valid for -d/--date.
1418
1418
1419 Returns 0 on success, 1 if nothing changed.
1419 Returns 0 on success, 1 if nothing changed.
1420 """
1420 """
1421 if opts.get('subrepos'):
1421 if opts.get('subrepos'):
1422 if opts.get('amend'):
1422 if opts.get('amend'):
1423 raise util.Abort(_('cannot amend with --subrepos'))
1423 raise util.Abort(_('cannot amend with --subrepos'))
1424 # Let --subrepos on the command line override config setting.
1424 # Let --subrepos on the command line override config setting.
1425 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1425 ui.setconfig('ui', 'commitsubrepos', True, 'commit')
1426
1426
1427 cmdutil.checkunfinished(repo, commit=True)
1427 cmdutil.checkunfinished(repo, commit=True)
1428
1428
1429 branch = repo[None].branch()
1429 branch = repo[None].branch()
1430 bheads = repo.branchheads(branch)
1430 bheads = repo.branchheads(branch)
1431
1431
1432 extra = {}
1432 extra = {}
1433 if opts.get('close_branch'):
1433 if opts.get('close_branch'):
1434 extra['close'] = 1
1434 extra['close'] = 1
1435
1435
1436 if not bheads:
1436 if not bheads:
1437 raise util.Abort(_('can only close branch heads'))
1437 raise util.Abort(_('can only close branch heads'))
1438 elif opts.get('amend'):
1438 elif opts.get('amend'):
1439 if repo.parents()[0].p1().branch() != branch and \
1439 if repo.parents()[0].p1().branch() != branch and \
1440 repo.parents()[0].p2().branch() != branch:
1440 repo.parents()[0].p2().branch() != branch:
1441 raise util.Abort(_('can only close branch heads'))
1441 raise util.Abort(_('can only close branch heads'))
1442
1442
1443 if opts.get('amend'):
1443 if opts.get('amend'):
1444 if ui.configbool('ui', 'commitsubrepos'):
1444 if ui.configbool('ui', 'commitsubrepos'):
1445 raise util.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1445 raise util.Abort(_('cannot amend with ui.commitsubrepos enabled'))
1446
1446
1447 old = repo['.']
1447 old = repo['.']
1448 if not old.mutable():
1448 if not old.mutable():
1449 raise util.Abort(_('cannot amend public changesets'))
1449 raise util.Abort(_('cannot amend public changesets'))
1450 if len(repo[None].parents()) > 1:
1450 if len(repo[None].parents()) > 1:
1451 raise util.Abort(_('cannot amend while merging'))
1451 raise util.Abort(_('cannot amend while merging'))
1452 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1452 allowunstable = obsolete.isenabled(repo, obsolete.allowunstableopt)
1453 if not allowunstable and old.children():
1453 if not allowunstable and old.children():
1454 raise util.Abort(_('cannot amend changeset with children'))
1454 raise util.Abort(_('cannot amend changeset with children'))
1455
1455
1456 # commitfunc is used only for temporary amend commit by cmdutil.amend
1456 # commitfunc is used only for temporary amend commit by cmdutil.amend
1457 def commitfunc(ui, repo, message, match, opts):
1457 def commitfunc(ui, repo, message, match, opts):
1458 return repo.commit(message,
1458 return repo.commit(message,
1459 opts.get('user') or old.user(),
1459 opts.get('user') or old.user(),
1460 opts.get('date') or old.date(),
1460 opts.get('date') or old.date(),
1461 match,
1461 match,
1462 extra=extra)
1462 extra=extra)
1463
1463
1464 current = repo._bookmarkcurrent
1464 current = repo._bookmarkcurrent
1465 marks = old.bookmarks()
1465 marks = old.bookmarks()
1466 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1466 node = cmdutil.amend(ui, repo, commitfunc, old, extra, pats, opts)
1467 if node == old.node():
1467 if node == old.node():
1468 ui.status(_("nothing changed\n"))
1468 ui.status(_("nothing changed\n"))
1469 return 1
1469 return 1
1470 elif marks:
1470 elif marks:
1471 ui.debug('moving bookmarks %r from %s to %s\n' %
1471 ui.debug('moving bookmarks %r from %s to %s\n' %
1472 (marks, old.hex(), hex(node)))
1472 (marks, old.hex(), hex(node)))
1473 newmarks = repo._bookmarks
1473 newmarks = repo._bookmarks
1474 for bm in marks:
1474 for bm in marks:
1475 newmarks[bm] = node
1475 newmarks[bm] = node
1476 if bm == current:
1476 if bm == current:
1477 bookmarks.setcurrent(repo, bm)
1477 bookmarks.setcurrent(repo, bm)
1478 newmarks.write()
1478 newmarks.write()
1479 else:
1479 else:
1480 def commitfunc(ui, repo, message, match, opts):
1480 def commitfunc(ui, repo, message, match, opts):
1481 backup = ui.backupconfig('phases', 'new-commit')
1481 backup = ui.backupconfig('phases', 'new-commit')
1482 baseui = repo.baseui
1482 baseui = repo.baseui
1483 basebackup = baseui.backupconfig('phases', 'new-commit')
1483 basebackup = baseui.backupconfig('phases', 'new-commit')
1484 try:
1484 try:
1485 if opts.get('secret'):
1485 if opts.get('secret'):
1486 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1486 ui.setconfig('phases', 'new-commit', 'secret', 'commit')
1487 # Propagate to subrepos
1487 # Propagate to subrepos
1488 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1488 baseui.setconfig('phases', 'new-commit', 'secret', 'commit')
1489
1489
1490 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1490 editform = cmdutil.mergeeditform(repo[None], 'commit.normal')
1491 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1491 editor = cmdutil.getcommiteditor(editform=editform, **opts)
1492 return repo.commit(message, opts.get('user'), opts.get('date'),
1492 return repo.commit(message, opts.get('user'), opts.get('date'),
1493 match,
1493 match,
1494 editor=editor,
1494 editor=editor,
1495 extra=extra)
1495 extra=extra)
1496 finally:
1496 finally:
1497 ui.restoreconfig(backup)
1497 ui.restoreconfig(backup)
1498 repo.baseui.restoreconfig(basebackup)
1498 repo.baseui.restoreconfig(basebackup)
1499
1499
1500
1500
1501 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1501 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
1502
1502
1503 if not node:
1503 if not node:
1504 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1504 stat = repo.status(match=scmutil.match(repo[None], pats, opts))
1505 if stat[3]:
1505 if stat[3]:
1506 ui.status(_("nothing changed (%d missing files, see "
1506 ui.status(_("nothing changed (%d missing files, see "
1507 "'hg status')\n") % len(stat[3]))
1507 "'hg status')\n") % len(stat[3]))
1508 else:
1508 else:
1509 ui.status(_("nothing changed\n"))
1509 ui.status(_("nothing changed\n"))
1510 return 1
1510 return 1
1511
1511
1512 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1512 cmdutil.commitstatus(repo, node, branch, bheads, opts)
1513
1513
1514 @command('config|showconfig|debugconfig',
1514 @command('config|showconfig|debugconfig',
1515 [('u', 'untrusted', None, _('show untrusted configuration options')),
1515 [('u', 'untrusted', None, _('show untrusted configuration options')),
1516 ('e', 'edit', None, _('edit user config')),
1516 ('e', 'edit', None, _('edit user config')),
1517 ('l', 'local', None, _('edit repository config')),
1517 ('l', 'local', None, _('edit repository config')),
1518 ('g', 'global', None, _('edit global config'))],
1518 ('g', 'global', None, _('edit global config'))],
1519 _('[-u] [NAME]...'),
1519 _('[-u] [NAME]...'),
1520 optionalrepo=True)
1520 optionalrepo=True)
1521 def config(ui, repo, *values, **opts):
1521 def config(ui, repo, *values, **opts):
1522 """show combined config settings from all hgrc files
1522 """show combined config settings from all hgrc files
1523
1523
1524 With no arguments, print names and values of all config items.
1524 With no arguments, print names and values of all config items.
1525
1525
1526 With one argument of the form section.name, print just the value
1526 With one argument of the form section.name, print just the value
1527 of that config item.
1527 of that config item.
1528
1528
1529 With multiple arguments, print names and values of all config
1529 With multiple arguments, print names and values of all config
1530 items with matching section names.
1530 items with matching section names.
1531
1531
1532 With --edit, start an editor on the user-level config file. With
1532 With --edit, start an editor on the user-level config file. With
1533 --global, edit the system-wide config file. With --local, edit the
1533 --global, edit the system-wide config file. With --local, edit the
1534 repository-level config file.
1534 repository-level config file.
1535
1535
1536 With --debug, the source (filename and line number) is printed
1536 With --debug, the source (filename and line number) is printed
1537 for each config item.
1537 for each config item.
1538
1538
1539 See :hg:`help config` for more information about config files.
1539 See :hg:`help config` for more information about config files.
1540
1540
1541 Returns 0 on success, 1 if NAME does not exist.
1541 Returns 0 on success, 1 if NAME does not exist.
1542
1542
1543 """
1543 """
1544
1544
1545 if opts.get('edit') or opts.get('local') or opts.get('global'):
1545 if opts.get('edit') or opts.get('local') or opts.get('global'):
1546 if opts.get('local') and opts.get('global'):
1546 if opts.get('local') and opts.get('global'):
1547 raise util.Abort(_("can't use --local and --global together"))
1547 raise util.Abort(_("can't use --local and --global together"))
1548
1548
1549 if opts.get('local'):
1549 if opts.get('local'):
1550 if not repo:
1550 if not repo:
1551 raise util.Abort(_("can't use --local outside a repository"))
1551 raise util.Abort(_("can't use --local outside a repository"))
1552 paths = [repo.join('hgrc')]
1552 paths = [repo.join('hgrc')]
1553 elif opts.get('global'):
1553 elif opts.get('global'):
1554 paths = scmutil.systemrcpath()
1554 paths = scmutil.systemrcpath()
1555 else:
1555 else:
1556 paths = scmutil.userrcpath()
1556 paths = scmutil.userrcpath()
1557
1557
1558 for f in paths:
1558 for f in paths:
1559 if os.path.exists(f):
1559 if os.path.exists(f):
1560 break
1560 break
1561 else:
1561 else:
1562 if opts.get('global'):
1562 if opts.get('global'):
1563 samplehgrc = uimod.samplehgrcs['global']
1563 samplehgrc = uimod.samplehgrcs['global']
1564 elif opts.get('local'):
1564 elif opts.get('local'):
1565 samplehgrc = uimod.samplehgrcs['local']
1565 samplehgrc = uimod.samplehgrcs['local']
1566 else:
1566 else:
1567 samplehgrc = uimod.samplehgrcs['user']
1567 samplehgrc = uimod.samplehgrcs['user']
1568
1568
1569 f = paths[0]
1569 f = paths[0]
1570 fp = open(f, "w")
1570 fp = open(f, "w")
1571 fp.write(samplehgrc)
1571 fp.write(samplehgrc)
1572 fp.close()
1572 fp.close()
1573
1573
1574 editor = ui.geteditor()
1574 editor = ui.geteditor()
1575 ui.system("%s \"%s\"" % (editor, f),
1575 ui.system("%s \"%s\"" % (editor, f),
1576 onerr=util.Abort, errprefix=_("edit failed"))
1576 onerr=util.Abort, errprefix=_("edit failed"))
1577 return
1577 return
1578
1578
1579 for f in scmutil.rcpath():
1579 for f in scmutil.rcpath():
1580 ui.debug('read config from: %s\n' % f)
1580 ui.debug('read config from: %s\n' % f)
1581 untrusted = bool(opts.get('untrusted'))
1581 untrusted = bool(opts.get('untrusted'))
1582 if values:
1582 if values:
1583 sections = [v for v in values if '.' not in v]
1583 sections = [v for v in values if '.' not in v]
1584 items = [v for v in values if '.' in v]
1584 items = [v for v in values if '.' in v]
1585 if len(items) > 1 or items and sections:
1585 if len(items) > 1 or items and sections:
1586 raise util.Abort(_('only one config item permitted'))
1586 raise util.Abort(_('only one config item permitted'))
1587 matched = False
1587 matched = False
1588 for section, name, value in ui.walkconfig(untrusted=untrusted):
1588 for section, name, value in ui.walkconfig(untrusted=untrusted):
1589 value = str(value).replace('\n', '\\n')
1589 value = str(value).replace('\n', '\\n')
1590 sectname = section + '.' + name
1590 sectname = section + '.' + name
1591 if values:
1591 if values:
1592 for v in values:
1592 for v in values:
1593 if v == section:
1593 if v == section:
1594 ui.debug('%s: ' %
1594 ui.debug('%s: ' %
1595 ui.configsource(section, name, untrusted))
1595 ui.configsource(section, name, untrusted))
1596 ui.write('%s=%s\n' % (sectname, value))
1596 ui.write('%s=%s\n' % (sectname, value))
1597 matched = True
1597 matched = True
1598 elif v == sectname:
1598 elif v == sectname:
1599 ui.debug('%s: ' %
1599 ui.debug('%s: ' %
1600 ui.configsource(section, name, untrusted))
1600 ui.configsource(section, name, untrusted))
1601 ui.write(value, '\n')
1601 ui.write(value, '\n')
1602 matched = True
1602 matched = True
1603 else:
1603 else:
1604 ui.debug('%s: ' %
1604 ui.debug('%s: ' %
1605 ui.configsource(section, name, untrusted))
1605 ui.configsource(section, name, untrusted))
1606 ui.write('%s=%s\n' % (sectname, value))
1606 ui.write('%s=%s\n' % (sectname, value))
1607 matched = True
1607 matched = True
1608 if matched:
1608 if matched:
1609 return 0
1609 return 0
1610 return 1
1610 return 1
1611
1611
1612 @command('copy|cp',
1612 @command('copy|cp',
1613 [('A', 'after', None, _('record a copy that has already occurred')),
1613 [('A', 'after', None, _('record a copy that has already occurred')),
1614 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1614 ('f', 'force', None, _('forcibly copy over an existing managed file')),
1615 ] + walkopts + dryrunopts,
1615 ] + walkopts + dryrunopts,
1616 _('[OPTION]... [SOURCE]... DEST'))
1616 _('[OPTION]... [SOURCE]... DEST'))
1617 def copy(ui, repo, *pats, **opts):
1617 def copy(ui, repo, *pats, **opts):
1618 """mark files as copied for the next commit
1618 """mark files as copied for the next commit
1619
1619
1620 Mark dest as having copies of source files. If dest is a
1620 Mark dest as having copies of source files. If dest is a
1621 directory, copies are put in that directory. If dest is a file,
1621 directory, copies are put in that directory. If dest is a file,
1622 the source must be a single file.
1622 the source must be a single file.
1623
1623
1624 By default, this command copies the contents of files as they
1624 By default, this command copies the contents of files as they
1625 exist in the working directory. If invoked with -A/--after, the
1625 exist in the working directory. If invoked with -A/--after, the
1626 operation is recorded, but no copying is performed.
1626 operation is recorded, but no copying is performed.
1627
1627
1628 This command takes effect with the next commit. To undo a copy
1628 This command takes effect with the next commit. To undo a copy
1629 before that, see :hg:`revert`.
1629 before that, see :hg:`revert`.
1630
1630
1631 Returns 0 on success, 1 if errors are encountered.
1631 Returns 0 on success, 1 if errors are encountered.
1632 """
1632 """
1633 wlock = repo.wlock(False)
1633 wlock = repo.wlock(False)
1634 try:
1634 try:
1635 return cmdutil.copy(ui, repo, pats, opts)
1635 return cmdutil.copy(ui, repo, pats, opts)
1636 finally:
1636 finally:
1637 wlock.release()
1637 wlock.release()
1638
1638
1639 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
1639 @command('debugancestor', [], _('[INDEX] REV1 REV2'), optionalrepo=True)
1640 def debugancestor(ui, repo, *args):
1640 def debugancestor(ui, repo, *args):
1641 """find the ancestor revision of two revisions in a given index"""
1641 """find the ancestor revision of two revisions in a given index"""
1642 if len(args) == 3:
1642 if len(args) == 3:
1643 index, rev1, rev2 = args
1643 index, rev1, rev2 = args
1644 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1644 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), index)
1645 lookup = r.lookup
1645 lookup = r.lookup
1646 elif len(args) == 2:
1646 elif len(args) == 2:
1647 if not repo:
1647 if not repo:
1648 raise util.Abort(_("there is no Mercurial repository here "
1648 raise util.Abort(_("there is no Mercurial repository here "
1649 "(.hg not found)"))
1649 "(.hg not found)"))
1650 rev1, rev2 = args
1650 rev1, rev2 = args
1651 r = repo.changelog
1651 r = repo.changelog
1652 lookup = repo.lookup
1652 lookup = repo.lookup
1653 else:
1653 else:
1654 raise util.Abort(_('either two or three arguments required'))
1654 raise util.Abort(_('either two or three arguments required'))
1655 a = r.ancestor(lookup(rev1), lookup(rev2))
1655 a = r.ancestor(lookup(rev1), lookup(rev2))
1656 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1656 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
1657
1657
1658 @command('debugbuilddag',
1658 @command('debugbuilddag',
1659 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1659 [('m', 'mergeable-file', None, _('add single file mergeable changes')),
1660 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1660 ('o', 'overwritten-file', None, _('add single file all revs overwrite')),
1661 ('n', 'new-file', None, _('add new file at each rev'))],
1661 ('n', 'new-file', None, _('add new file at each rev'))],
1662 _('[OPTION]... [TEXT]'))
1662 _('[OPTION]... [TEXT]'))
1663 def debugbuilddag(ui, repo, text=None,
1663 def debugbuilddag(ui, repo, text=None,
1664 mergeable_file=False,
1664 mergeable_file=False,
1665 overwritten_file=False,
1665 overwritten_file=False,
1666 new_file=False):
1666 new_file=False):
1667 """builds a repo with a given DAG from scratch in the current empty repo
1667 """builds a repo with a given DAG from scratch in the current empty repo
1668
1668
1669 The description of the DAG is read from stdin if not given on the
1669 The description of the DAG is read from stdin if not given on the
1670 command line.
1670 command line.
1671
1671
1672 Elements:
1672 Elements:
1673
1673
1674 - "+n" is a linear run of n nodes based on the current default parent
1674 - "+n" is a linear run of n nodes based on the current default parent
1675 - "." is a single node based on the current default parent
1675 - "." is a single node based on the current default parent
1676 - "$" resets the default parent to null (implied at the start);
1676 - "$" resets the default parent to null (implied at the start);
1677 otherwise the default parent is always the last node created
1677 otherwise the default parent is always the last node created
1678 - "<p" sets the default parent to the backref p
1678 - "<p" sets the default parent to the backref p
1679 - "*p" is a fork at parent p, which is a backref
1679 - "*p" is a fork at parent p, which is a backref
1680 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1680 - "*p1/p2" is a merge of parents p1 and p2, which are backrefs
1681 - "/p2" is a merge of the preceding node and p2
1681 - "/p2" is a merge of the preceding node and p2
1682 - ":tag" defines a local tag for the preceding node
1682 - ":tag" defines a local tag for the preceding node
1683 - "@branch" sets the named branch for subsequent nodes
1683 - "@branch" sets the named branch for subsequent nodes
1684 - "#...\\n" is a comment up to the end of the line
1684 - "#...\\n" is a comment up to the end of the line
1685
1685
1686 Whitespace between the above elements is ignored.
1686 Whitespace between the above elements is ignored.
1687
1687
1688 A backref is either
1688 A backref is either
1689
1689
1690 - a number n, which references the node curr-n, where curr is the current
1690 - a number n, which references the node curr-n, where curr is the current
1691 node, or
1691 node, or
1692 - the name of a local tag you placed earlier using ":tag", or
1692 - the name of a local tag you placed earlier using ":tag", or
1693 - empty to denote the default parent.
1693 - empty to denote the default parent.
1694
1694
1695 All string valued-elements are either strictly alphanumeric, or must
1695 All string valued-elements are either strictly alphanumeric, or must
1696 be enclosed in double quotes ("..."), with "\\" as escape character.
1696 be enclosed in double quotes ("..."), with "\\" as escape character.
1697 """
1697 """
1698
1698
1699 if text is None:
1699 if text is None:
1700 ui.status(_("reading DAG from stdin\n"))
1700 ui.status(_("reading DAG from stdin\n"))
1701 text = ui.fin.read()
1701 text = ui.fin.read()
1702
1702
1703 cl = repo.changelog
1703 cl = repo.changelog
1704 if len(cl) > 0:
1704 if len(cl) > 0:
1705 raise util.Abort(_('repository is not empty'))
1705 raise util.Abort(_('repository is not empty'))
1706
1706
1707 # determine number of revs in DAG
1707 # determine number of revs in DAG
1708 total = 0
1708 total = 0
1709 for type, data in dagparser.parsedag(text):
1709 for type, data in dagparser.parsedag(text):
1710 if type == 'n':
1710 if type == 'n':
1711 total += 1
1711 total += 1
1712
1712
1713 if mergeable_file:
1713 if mergeable_file:
1714 linesperrev = 2
1714 linesperrev = 2
1715 # make a file with k lines per rev
1715 # make a file with k lines per rev
1716 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1716 initialmergedlines = [str(i) for i in xrange(0, total * linesperrev)]
1717 initialmergedlines.append("")
1717 initialmergedlines.append("")
1718
1718
1719 tags = []
1719 tags = []
1720
1720
1721 lock = tr = None
1721 lock = tr = None
1722 try:
1722 try:
1723 lock = repo.lock()
1723 lock = repo.lock()
1724 tr = repo.transaction("builddag")
1724 tr = repo.transaction("builddag")
1725
1725
1726 at = -1
1726 at = -1
1727 atbranch = 'default'
1727 atbranch = 'default'
1728 nodeids = []
1728 nodeids = []
1729 id = 0
1729 id = 0
1730 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1730 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1731 for type, data in dagparser.parsedag(text):
1731 for type, data in dagparser.parsedag(text):
1732 if type == 'n':
1732 if type == 'n':
1733 ui.note(('node %s\n' % str(data)))
1733 ui.note(('node %s\n' % str(data)))
1734 id, ps = data
1734 id, ps = data
1735
1735
1736 files = []
1736 files = []
1737 fctxs = {}
1737 fctxs = {}
1738
1738
1739 p2 = None
1739 p2 = None
1740 if mergeable_file:
1740 if mergeable_file:
1741 fn = "mf"
1741 fn = "mf"
1742 p1 = repo[ps[0]]
1742 p1 = repo[ps[0]]
1743 if len(ps) > 1:
1743 if len(ps) > 1:
1744 p2 = repo[ps[1]]
1744 p2 = repo[ps[1]]
1745 pa = p1.ancestor(p2)
1745 pa = p1.ancestor(p2)
1746 base, local, other = [x[fn].data() for x in (pa, p1,
1746 base, local, other = [x[fn].data() for x in (pa, p1,
1747 p2)]
1747 p2)]
1748 m3 = simplemerge.Merge3Text(base, local, other)
1748 m3 = simplemerge.Merge3Text(base, local, other)
1749 ml = [l.strip() for l in m3.merge_lines()]
1749 ml = [l.strip() for l in m3.merge_lines()]
1750 ml.append("")
1750 ml.append("")
1751 elif at > 0:
1751 elif at > 0:
1752 ml = p1[fn].data().split("\n")
1752 ml = p1[fn].data().split("\n")
1753 else:
1753 else:
1754 ml = initialmergedlines
1754 ml = initialmergedlines
1755 ml[id * linesperrev] += " r%i" % id
1755 ml[id * linesperrev] += " r%i" % id
1756 mergedtext = "\n".join(ml)
1756 mergedtext = "\n".join(ml)
1757 files.append(fn)
1757 files.append(fn)
1758 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
1758 fctxs[fn] = context.memfilectx(repo, fn, mergedtext)
1759
1759
1760 if overwritten_file:
1760 if overwritten_file:
1761 fn = "of"
1761 fn = "of"
1762 files.append(fn)
1762 files.append(fn)
1763 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1763 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1764
1764
1765 if new_file:
1765 if new_file:
1766 fn = "nf%i" % id
1766 fn = "nf%i" % id
1767 files.append(fn)
1767 files.append(fn)
1768 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1768 fctxs[fn] = context.memfilectx(repo, fn, "r%i\n" % id)
1769 if len(ps) > 1:
1769 if len(ps) > 1:
1770 if not p2:
1770 if not p2:
1771 p2 = repo[ps[1]]
1771 p2 = repo[ps[1]]
1772 for fn in p2:
1772 for fn in p2:
1773 if fn.startswith("nf"):
1773 if fn.startswith("nf"):
1774 files.append(fn)
1774 files.append(fn)
1775 fctxs[fn] = p2[fn]
1775 fctxs[fn] = p2[fn]
1776
1776
1777 def fctxfn(repo, cx, path):
1777 def fctxfn(repo, cx, path):
1778 return fctxs.get(path)
1778 return fctxs.get(path)
1779
1779
1780 if len(ps) == 0 or ps[0] < 0:
1780 if len(ps) == 0 or ps[0] < 0:
1781 pars = [None, None]
1781 pars = [None, None]
1782 elif len(ps) == 1:
1782 elif len(ps) == 1:
1783 pars = [nodeids[ps[0]], None]
1783 pars = [nodeids[ps[0]], None]
1784 else:
1784 else:
1785 pars = [nodeids[p] for p in ps]
1785 pars = [nodeids[p] for p in ps]
1786 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1786 cx = context.memctx(repo, pars, "r%i" % id, files, fctxfn,
1787 date=(id, 0),
1787 date=(id, 0),
1788 user="debugbuilddag",
1788 user="debugbuilddag",
1789 extra={'branch': atbranch})
1789 extra={'branch': atbranch})
1790 nodeid = repo.commitctx(cx)
1790 nodeid = repo.commitctx(cx)
1791 nodeids.append(nodeid)
1791 nodeids.append(nodeid)
1792 at = id
1792 at = id
1793 elif type == 'l':
1793 elif type == 'l':
1794 id, name = data
1794 id, name = data
1795 ui.note(('tag %s\n' % name))
1795 ui.note(('tag %s\n' % name))
1796 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1796 tags.append("%s %s\n" % (hex(repo.changelog.node(id)), name))
1797 elif type == 'a':
1797 elif type == 'a':
1798 ui.note(('branch %s\n' % data))
1798 ui.note(('branch %s\n' % data))
1799 atbranch = data
1799 atbranch = data
1800 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1800 ui.progress(_('building'), id, unit=_('revisions'), total=total)
1801 tr.close()
1801 tr.close()
1802
1802
1803 if tags:
1803 if tags:
1804 repo.opener.write("localtags", "".join(tags))
1804 repo.opener.write("localtags", "".join(tags))
1805 finally:
1805 finally:
1806 ui.progress(_('building'), None)
1806 ui.progress(_('building'), None)
1807 release(tr, lock)
1807 release(tr, lock)
1808
1808
1809 @command('debugbundle',
1809 @command('debugbundle',
1810 [('a', 'all', None, _('show all details'))],
1810 [('a', 'all', None, _('show all details'))],
1811 _('FILE'),
1811 _('FILE'),
1812 norepo=True)
1812 norepo=True)
1813 def debugbundle(ui, bundlepath, all=None, **opts):
1813 def debugbundle(ui, bundlepath, all=None, **opts):
1814 """lists the contents of a bundle"""
1814 """lists the contents of a bundle"""
1815 f = hg.openpath(ui, bundlepath)
1815 f = hg.openpath(ui, bundlepath)
1816 try:
1816 try:
1817 gen = exchange.readbundle(ui, f, bundlepath)
1817 gen = exchange.readbundle(ui, f, bundlepath)
1818 if all:
1818 if all:
1819 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1819 ui.write(("format: id, p1, p2, cset, delta base, len(delta)\n"))
1820
1820
1821 def showchunks(named):
1821 def showchunks(named):
1822 ui.write("\n%s\n" % named)
1822 ui.write("\n%s\n" % named)
1823 chain = None
1823 chain = None
1824 while True:
1824 while True:
1825 chunkdata = gen.deltachunk(chain)
1825 chunkdata = gen.deltachunk(chain)
1826 if not chunkdata:
1826 if not chunkdata:
1827 break
1827 break
1828 node = chunkdata['node']
1828 node = chunkdata['node']
1829 p1 = chunkdata['p1']
1829 p1 = chunkdata['p1']
1830 p2 = chunkdata['p2']
1830 p2 = chunkdata['p2']
1831 cs = chunkdata['cs']
1831 cs = chunkdata['cs']
1832 deltabase = chunkdata['deltabase']
1832 deltabase = chunkdata['deltabase']
1833 delta = chunkdata['delta']
1833 delta = chunkdata['delta']
1834 ui.write("%s %s %s %s %s %s\n" %
1834 ui.write("%s %s %s %s %s %s\n" %
1835 (hex(node), hex(p1), hex(p2),
1835 (hex(node), hex(p1), hex(p2),
1836 hex(cs), hex(deltabase), len(delta)))
1836 hex(cs), hex(deltabase), len(delta)))
1837 chain = node
1837 chain = node
1838
1838
1839 chunkdata = gen.changelogheader()
1839 chunkdata = gen.changelogheader()
1840 showchunks("changelog")
1840 showchunks("changelog")
1841 chunkdata = gen.manifestheader()
1841 chunkdata = gen.manifestheader()
1842 showchunks("manifest")
1842 showchunks("manifest")
1843 while True:
1843 while True:
1844 chunkdata = gen.filelogheader()
1844 chunkdata = gen.filelogheader()
1845 if not chunkdata:
1845 if not chunkdata:
1846 break
1846 break
1847 fname = chunkdata['filename']
1847 fname = chunkdata['filename']
1848 showchunks(fname)
1848 showchunks(fname)
1849 else:
1849 else:
1850 chunkdata = gen.changelogheader()
1850 chunkdata = gen.changelogheader()
1851 chain = None
1851 chain = None
1852 while True:
1852 while True:
1853 chunkdata = gen.deltachunk(chain)
1853 chunkdata = gen.deltachunk(chain)
1854 if not chunkdata:
1854 if not chunkdata:
1855 break
1855 break
1856 node = chunkdata['node']
1856 node = chunkdata['node']
1857 ui.write("%s\n" % hex(node))
1857 ui.write("%s\n" % hex(node))
1858 chain = node
1858 chain = node
1859 finally:
1859 finally:
1860 f.close()
1860 f.close()
1861
1861
1862 @command('debugcheckstate', [], '')
1862 @command('debugcheckstate', [], '')
1863 def debugcheckstate(ui, repo):
1863 def debugcheckstate(ui, repo):
1864 """validate the correctness of the current dirstate"""
1864 """validate the correctness of the current dirstate"""
1865 parent1, parent2 = repo.dirstate.parents()
1865 parent1, parent2 = repo.dirstate.parents()
1866 m1 = repo[parent1].manifest()
1866 m1 = repo[parent1].manifest()
1867 m2 = repo[parent2].manifest()
1867 m2 = repo[parent2].manifest()
1868 errors = 0
1868 errors = 0
1869 for f in repo.dirstate:
1869 for f in repo.dirstate:
1870 state = repo.dirstate[f]
1870 state = repo.dirstate[f]
1871 if state in "nr" and f not in m1:
1871 if state in "nr" and f not in m1:
1872 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1872 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
1873 errors += 1
1873 errors += 1
1874 if state in "a" and f in m1:
1874 if state in "a" and f in m1:
1875 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1875 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
1876 errors += 1
1876 errors += 1
1877 if state in "m" and f not in m1 and f not in m2:
1877 if state in "m" and f not in m1 and f not in m2:
1878 ui.warn(_("%s in state %s, but not in either manifest\n") %
1878 ui.warn(_("%s in state %s, but not in either manifest\n") %
1879 (f, state))
1879 (f, state))
1880 errors += 1
1880 errors += 1
1881 for f in m1:
1881 for f in m1:
1882 state = repo.dirstate[f]
1882 state = repo.dirstate[f]
1883 if state not in "nrm":
1883 if state not in "nrm":
1884 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1884 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
1885 errors += 1
1885 errors += 1
1886 if errors:
1886 if errors:
1887 error = _(".hg/dirstate inconsistent with current parent's manifest")
1887 error = _(".hg/dirstate inconsistent with current parent's manifest")
1888 raise util.Abort(error)
1888 raise util.Abort(error)
1889
1889
1890 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1890 @command('debugcommands', [], _('[COMMAND]'), norepo=True)
1891 def debugcommands(ui, cmd='', *args):
1891 def debugcommands(ui, cmd='', *args):
1892 """list all available commands and options"""
1892 """list all available commands and options"""
1893 for cmd, vals in sorted(table.iteritems()):
1893 for cmd, vals in sorted(table.iteritems()):
1894 cmd = cmd.split('|')[0].strip('^')
1894 cmd = cmd.split('|')[0].strip('^')
1895 opts = ', '.join([i[1] for i in vals[1]])
1895 opts = ', '.join([i[1] for i in vals[1]])
1896 ui.write('%s: %s\n' % (cmd, opts))
1896 ui.write('%s: %s\n' % (cmd, opts))
1897
1897
1898 @command('debugcomplete',
1898 @command('debugcomplete',
1899 [('o', 'options', None, _('show the command options'))],
1899 [('o', 'options', None, _('show the command options'))],
1900 _('[-o] CMD'),
1900 _('[-o] CMD'),
1901 norepo=True)
1901 norepo=True)
1902 def debugcomplete(ui, cmd='', **opts):
1902 def debugcomplete(ui, cmd='', **opts):
1903 """returns the completion list associated with the given command"""
1903 """returns the completion list associated with the given command"""
1904
1904
1905 if opts.get('options'):
1905 if opts.get('options'):
1906 options = []
1906 options = []
1907 otables = [globalopts]
1907 otables = [globalopts]
1908 if cmd:
1908 if cmd:
1909 aliases, entry = cmdutil.findcmd(cmd, table, False)
1909 aliases, entry = cmdutil.findcmd(cmd, table, False)
1910 otables.append(entry[1])
1910 otables.append(entry[1])
1911 for t in otables:
1911 for t in otables:
1912 for o in t:
1912 for o in t:
1913 if "(DEPRECATED)" in o[3]:
1913 if "(DEPRECATED)" in o[3]:
1914 continue
1914 continue
1915 if o[0]:
1915 if o[0]:
1916 options.append('-%s' % o[0])
1916 options.append('-%s' % o[0])
1917 options.append('--%s' % o[1])
1917 options.append('--%s' % o[1])
1918 ui.write("%s\n" % "\n".join(options))
1918 ui.write("%s\n" % "\n".join(options))
1919 return
1919 return
1920
1920
1921 cmdlist = cmdutil.findpossible(cmd, table)
1921 cmdlist = cmdutil.findpossible(cmd, table)
1922 if ui.verbose:
1922 if ui.verbose:
1923 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1923 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
1924 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1924 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
1925
1925
1926 @command('debugdag',
1926 @command('debugdag',
1927 [('t', 'tags', None, _('use tags as labels')),
1927 [('t', 'tags', None, _('use tags as labels')),
1928 ('b', 'branches', None, _('annotate with branch names')),
1928 ('b', 'branches', None, _('annotate with branch names')),
1929 ('', 'dots', None, _('use dots for runs')),
1929 ('', 'dots', None, _('use dots for runs')),
1930 ('s', 'spaces', None, _('separate elements by spaces'))],
1930 ('s', 'spaces', None, _('separate elements by spaces'))],
1931 _('[OPTION]... [FILE [REV]...]'),
1931 _('[OPTION]... [FILE [REV]...]'),
1932 optionalrepo=True)
1932 optionalrepo=True)
1933 def debugdag(ui, repo, file_=None, *revs, **opts):
1933 def debugdag(ui, repo, file_=None, *revs, **opts):
1934 """format the changelog or an index DAG as a concise textual description
1934 """format the changelog or an index DAG as a concise textual description
1935
1935
1936 If you pass a revlog index, the revlog's DAG is emitted. If you list
1936 If you pass a revlog index, the revlog's DAG is emitted. If you list
1937 revision numbers, they get labeled in the output as rN.
1937 revision numbers, they get labeled in the output as rN.
1938
1938
1939 Otherwise, the changelog DAG of the current repo is emitted.
1939 Otherwise, the changelog DAG of the current repo is emitted.
1940 """
1940 """
1941 spaces = opts.get('spaces')
1941 spaces = opts.get('spaces')
1942 dots = opts.get('dots')
1942 dots = opts.get('dots')
1943 if file_:
1943 if file_:
1944 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1944 rlog = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
1945 revs = set((int(r) for r in revs))
1945 revs = set((int(r) for r in revs))
1946 def events():
1946 def events():
1947 for r in rlog:
1947 for r in rlog:
1948 yield 'n', (r, list(p for p in rlog.parentrevs(r)
1948 yield 'n', (r, list(p for p in rlog.parentrevs(r)
1949 if p != -1))
1949 if p != -1))
1950 if r in revs:
1950 if r in revs:
1951 yield 'l', (r, "r%i" % r)
1951 yield 'l', (r, "r%i" % r)
1952 elif repo:
1952 elif repo:
1953 cl = repo.changelog
1953 cl = repo.changelog
1954 tags = opts.get('tags')
1954 tags = opts.get('tags')
1955 branches = opts.get('branches')
1955 branches = opts.get('branches')
1956 if tags:
1956 if tags:
1957 labels = {}
1957 labels = {}
1958 for l, n in repo.tags().items():
1958 for l, n in repo.tags().items():
1959 labels.setdefault(cl.rev(n), []).append(l)
1959 labels.setdefault(cl.rev(n), []).append(l)
1960 def events():
1960 def events():
1961 b = "default"
1961 b = "default"
1962 for r in cl:
1962 for r in cl:
1963 if branches:
1963 if branches:
1964 newb = cl.read(cl.node(r))[5]['branch']
1964 newb = cl.read(cl.node(r))[5]['branch']
1965 if newb != b:
1965 if newb != b:
1966 yield 'a', newb
1966 yield 'a', newb
1967 b = newb
1967 b = newb
1968 yield 'n', (r, list(p for p in cl.parentrevs(r)
1968 yield 'n', (r, list(p for p in cl.parentrevs(r)
1969 if p != -1))
1969 if p != -1))
1970 if tags:
1970 if tags:
1971 ls = labels.get(r)
1971 ls = labels.get(r)
1972 if ls:
1972 if ls:
1973 for l in ls:
1973 for l in ls:
1974 yield 'l', (r, l)
1974 yield 'l', (r, l)
1975 else:
1975 else:
1976 raise util.Abort(_('need repo for changelog dag'))
1976 raise util.Abort(_('need repo for changelog dag'))
1977
1977
1978 for line in dagparser.dagtextlines(events(),
1978 for line in dagparser.dagtextlines(events(),
1979 addspaces=spaces,
1979 addspaces=spaces,
1980 wraplabels=True,
1980 wraplabels=True,
1981 wrapannotations=True,
1981 wrapannotations=True,
1982 wrapnonlinear=dots,
1982 wrapnonlinear=dots,
1983 usedots=dots,
1983 usedots=dots,
1984 maxlinewidth=70):
1984 maxlinewidth=70):
1985 ui.write(line)
1985 ui.write(line)
1986 ui.write("\n")
1986 ui.write("\n")
1987
1987
1988 @command('debugdata',
1988 @command('debugdata',
1989 [('c', 'changelog', False, _('open changelog')),
1989 [('c', 'changelog', False, _('open changelog')),
1990 ('m', 'manifest', False, _('open manifest'))],
1990 ('m', 'manifest', False, _('open manifest'))],
1991 _('-c|-m|FILE REV'))
1991 _('-c|-m|FILE REV'))
1992 def debugdata(ui, repo, file_, rev=None, **opts):
1992 def debugdata(ui, repo, file_, rev=None, **opts):
1993 """dump the contents of a data file revision"""
1993 """dump the contents of a data file revision"""
1994 if opts.get('changelog') or opts.get('manifest'):
1994 if opts.get('changelog') or opts.get('manifest'):
1995 file_, rev = None, file_
1995 file_, rev = None, file_
1996 elif rev is None:
1996 elif rev is None:
1997 raise error.CommandError('debugdata', _('invalid arguments'))
1997 raise error.CommandError('debugdata', _('invalid arguments'))
1998 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1998 r = cmdutil.openrevlog(repo, 'debugdata', file_, opts)
1999 try:
1999 try:
2000 ui.write(r.revision(r.lookup(rev)))
2000 ui.write(r.revision(r.lookup(rev)))
2001 except KeyError:
2001 except KeyError:
2002 raise util.Abort(_('invalid revision identifier %s') % rev)
2002 raise util.Abort(_('invalid revision identifier %s') % rev)
2003
2003
2004 @command('debugdate',
2004 @command('debugdate',
2005 [('e', 'extended', None, _('try extended date formats'))],
2005 [('e', 'extended', None, _('try extended date formats'))],
2006 _('[-e] DATE [RANGE]'),
2006 _('[-e] DATE [RANGE]'),
2007 norepo=True, optionalrepo=True)
2007 norepo=True, optionalrepo=True)
2008 def debugdate(ui, date, range=None, **opts):
2008 def debugdate(ui, date, range=None, **opts):
2009 """parse and display a date"""
2009 """parse and display a date"""
2010 if opts["extended"]:
2010 if opts["extended"]:
2011 d = util.parsedate(date, util.extendeddateformats)
2011 d = util.parsedate(date, util.extendeddateformats)
2012 else:
2012 else:
2013 d = util.parsedate(date)
2013 d = util.parsedate(date)
2014 ui.write(("internal: %s %s\n") % d)
2014 ui.write(("internal: %s %s\n") % d)
2015 ui.write(("standard: %s\n") % util.datestr(d))
2015 ui.write(("standard: %s\n") % util.datestr(d))
2016 if range:
2016 if range:
2017 m = util.matchdate(range)
2017 m = util.matchdate(range)
2018 ui.write(("match: %s\n") % m(d[0]))
2018 ui.write(("match: %s\n") % m(d[0]))
2019
2019
2020 @command('debugdiscovery',
2020 @command('debugdiscovery',
2021 [('', 'old', None, _('use old-style discovery')),
2021 [('', 'old', None, _('use old-style discovery')),
2022 ('', 'nonheads', None,
2022 ('', 'nonheads', None,
2023 _('use old-style discovery with non-heads included')),
2023 _('use old-style discovery with non-heads included')),
2024 ] + remoteopts,
2024 ] + remoteopts,
2025 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
2025 _('[-l REV] [-r REV] [-b BRANCH]... [OTHER]'))
2026 def debugdiscovery(ui, repo, remoteurl="default", **opts):
2026 def debugdiscovery(ui, repo, remoteurl="default", **opts):
2027 """runs the changeset discovery protocol in isolation"""
2027 """runs the changeset discovery protocol in isolation"""
2028 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
2028 remoteurl, branches = hg.parseurl(ui.expandpath(remoteurl),
2029 opts.get('branch'))
2029 opts.get('branch'))
2030 remote = hg.peer(repo, opts, remoteurl)
2030 remote = hg.peer(repo, opts, remoteurl)
2031 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
2031 ui.status(_('comparing with %s\n') % util.hidepassword(remoteurl))
2032
2032
2033 # make sure tests are repeatable
2033 # make sure tests are repeatable
2034 random.seed(12323)
2034 random.seed(12323)
2035
2035
2036 def doit(localheads, remoteheads, remote=remote):
2036 def doit(localheads, remoteheads, remote=remote):
2037 if opts.get('old'):
2037 if opts.get('old'):
2038 if localheads:
2038 if localheads:
2039 raise util.Abort('cannot use localheads with old style '
2039 raise util.Abort('cannot use localheads with old style '
2040 'discovery')
2040 'discovery')
2041 if not util.safehasattr(remote, 'branches'):
2041 if not util.safehasattr(remote, 'branches'):
2042 # enable in-client legacy support
2042 # enable in-client legacy support
2043 remote = localrepo.locallegacypeer(remote.local())
2043 remote = localrepo.locallegacypeer(remote.local())
2044 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
2044 common, _in, hds = treediscovery.findcommonincoming(repo, remote,
2045 force=True)
2045 force=True)
2046 common = set(common)
2046 common = set(common)
2047 if not opts.get('nonheads'):
2047 if not opts.get('nonheads'):
2048 ui.write(("unpruned common: %s\n") %
2048 ui.write(("unpruned common: %s\n") %
2049 " ".join(sorted(short(n) for n in common)))
2049 " ".join(sorted(short(n) for n in common)))
2050 dag = dagutil.revlogdag(repo.changelog)
2050 dag = dagutil.revlogdag(repo.changelog)
2051 all = dag.ancestorset(dag.internalizeall(common))
2051 all = dag.ancestorset(dag.internalizeall(common))
2052 common = dag.externalizeall(dag.headsetofconnecteds(all))
2052 common = dag.externalizeall(dag.headsetofconnecteds(all))
2053 else:
2053 else:
2054 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
2054 common, any, hds = setdiscovery.findcommonheads(ui, repo, remote)
2055 common = set(common)
2055 common = set(common)
2056 rheads = set(hds)
2056 rheads = set(hds)
2057 lheads = set(repo.heads())
2057 lheads = set(repo.heads())
2058 ui.write(("common heads: %s\n") %
2058 ui.write(("common heads: %s\n") %
2059 " ".join(sorted(short(n) for n in common)))
2059 " ".join(sorted(short(n) for n in common)))
2060 if lheads <= common:
2060 if lheads <= common:
2061 ui.write(("local is subset\n"))
2061 ui.write(("local is subset\n"))
2062 elif rheads <= common:
2062 elif rheads <= common:
2063 ui.write(("remote is subset\n"))
2063 ui.write(("remote is subset\n"))
2064
2064
2065 serverlogs = opts.get('serverlog')
2065 serverlogs = opts.get('serverlog')
2066 if serverlogs:
2066 if serverlogs:
2067 for filename in serverlogs:
2067 for filename in serverlogs:
2068 logfile = open(filename, 'r')
2068 logfile = open(filename, 'r')
2069 try:
2069 try:
2070 line = logfile.readline()
2070 line = logfile.readline()
2071 while line:
2071 while line:
2072 parts = line.strip().split(';')
2072 parts = line.strip().split(';')
2073 op = parts[1]
2073 op = parts[1]
2074 if op == 'cg':
2074 if op == 'cg':
2075 pass
2075 pass
2076 elif op == 'cgss':
2076 elif op == 'cgss':
2077 doit(parts[2].split(' '), parts[3].split(' '))
2077 doit(parts[2].split(' '), parts[3].split(' '))
2078 elif op == 'unb':
2078 elif op == 'unb':
2079 doit(parts[3].split(' '), parts[2].split(' '))
2079 doit(parts[3].split(' '), parts[2].split(' '))
2080 line = logfile.readline()
2080 line = logfile.readline()
2081 finally:
2081 finally:
2082 logfile.close()
2082 logfile.close()
2083
2083
2084 else:
2084 else:
2085 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2085 remoterevs, _checkout = hg.addbranchrevs(repo, remote, branches,
2086 opts.get('remote_head'))
2086 opts.get('remote_head'))
2087 localrevs = opts.get('local_head')
2087 localrevs = opts.get('local_head')
2088 doit(localrevs, remoterevs)
2088 doit(localrevs, remoterevs)
2089
2089
2090 @command('debugfileset',
2090 @command('debugfileset',
2091 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2091 [('r', 'rev', '', _('apply the filespec on this revision'), _('REV'))],
2092 _('[-r REV] FILESPEC'))
2092 _('[-r REV] FILESPEC'))
2093 def debugfileset(ui, repo, expr, **opts):
2093 def debugfileset(ui, repo, expr, **opts):
2094 '''parse and apply a fileset specification'''
2094 '''parse and apply a fileset specification'''
2095 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2095 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
2096 if ui.verbose:
2096 if ui.verbose:
2097 tree = fileset.parse(expr)[0]
2097 tree = fileset.parse(expr)[0]
2098 ui.note(tree, "\n")
2098 ui.note(tree, "\n")
2099
2099
2100 for f in ctx.getfileset(expr):
2100 for f in ctx.getfileset(expr):
2101 ui.write("%s\n" % f)
2101 ui.write("%s\n" % f)
2102
2102
2103 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2103 @command('debugfsinfo', [], _('[PATH]'), norepo=True)
2104 def debugfsinfo(ui, path="."):
2104 def debugfsinfo(ui, path="."):
2105 """show information detected about current filesystem"""
2105 """show information detected about current filesystem"""
2106 util.writefile('.debugfsinfo', '')
2106 util.writefile('.debugfsinfo', '')
2107 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2107 ui.write(('exec: %s\n') % (util.checkexec(path) and 'yes' or 'no'))
2108 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2108 ui.write(('symlink: %s\n') % (util.checklink(path) and 'yes' or 'no'))
2109 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2109 ui.write(('hardlink: %s\n') % (util.checknlink(path) and 'yes' or 'no'))
2110 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2110 ui.write(('case-sensitive: %s\n') % (util.checkcase('.debugfsinfo')
2111 and 'yes' or 'no'))
2111 and 'yes' or 'no'))
2112 os.unlink('.debugfsinfo')
2112 os.unlink('.debugfsinfo')
2113
2113
2114 @command('debuggetbundle',
2114 @command('debuggetbundle',
2115 [('H', 'head', [], _('id of head node'), _('ID')),
2115 [('H', 'head', [], _('id of head node'), _('ID')),
2116 ('C', 'common', [], _('id of common node'), _('ID')),
2116 ('C', 'common', [], _('id of common node'), _('ID')),
2117 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2117 ('t', 'type', 'bzip2', _('bundle compression type to use'), _('TYPE'))],
2118 _('REPO FILE [-H|-C ID]...'),
2118 _('REPO FILE [-H|-C ID]...'),
2119 norepo=True)
2119 norepo=True)
2120 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2120 def debuggetbundle(ui, repopath, bundlepath, head=None, common=None, **opts):
2121 """retrieves a bundle from a repo
2121 """retrieves a bundle from a repo
2122
2122
2123 Every ID must be a full-length hex node id string. Saves the bundle to the
2123 Every ID must be a full-length hex node id string. Saves the bundle to the
2124 given file.
2124 given file.
2125 """
2125 """
2126 repo = hg.peer(ui, opts, repopath)
2126 repo = hg.peer(ui, opts, repopath)
2127 if not repo.capable('getbundle'):
2127 if not repo.capable('getbundle'):
2128 raise util.Abort("getbundle() not supported by target repository")
2128 raise util.Abort("getbundle() not supported by target repository")
2129 args = {}
2129 args = {}
2130 if common:
2130 if common:
2131 args['common'] = [bin(s) for s in common]
2131 args['common'] = [bin(s) for s in common]
2132 if head:
2132 if head:
2133 args['heads'] = [bin(s) for s in head]
2133 args['heads'] = [bin(s) for s in head]
2134 # TODO: get desired bundlecaps from command line.
2134 # TODO: get desired bundlecaps from command line.
2135 args['bundlecaps'] = None
2135 args['bundlecaps'] = None
2136 bundle = repo.getbundle('debug', **args)
2136 bundle = repo.getbundle('debug', **args)
2137
2137
2138 bundletype = opts.get('type', 'bzip2').lower()
2138 bundletype = opts.get('type', 'bzip2').lower()
2139 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
2139 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
2140 bundletype = btypes.get(bundletype)
2140 bundletype = btypes.get(bundletype)
2141 if bundletype not in changegroup.bundletypes:
2141 if bundletype not in changegroup.bundletypes:
2142 raise util.Abort(_('unknown bundle type specified with --type'))
2142 raise util.Abort(_('unknown bundle type specified with --type'))
2143 changegroup.writebundle(bundle, bundlepath, bundletype)
2143 changegroup.writebundle(bundle, bundlepath, bundletype)
2144
2144
2145 @command('debugignore', [], '')
2145 @command('debugignore', [], '')
2146 def debugignore(ui, repo, *values, **opts):
2146 def debugignore(ui, repo, *values, **opts):
2147 """display the combined ignore pattern"""
2147 """display the combined ignore pattern"""
2148 ignore = repo.dirstate._ignore
2148 ignore = repo.dirstate._ignore
2149 includepat = getattr(ignore, 'includepat', None)
2149 includepat = getattr(ignore, 'includepat', None)
2150 if includepat is not None:
2150 if includepat is not None:
2151 ui.write("%s\n" % includepat)
2151 ui.write("%s\n" % includepat)
2152 else:
2152 else:
2153 raise util.Abort(_("no ignore patterns found"))
2153 raise util.Abort(_("no ignore patterns found"))
2154
2154
2155 @command('debugindex',
2155 @command('debugindex',
2156 [('c', 'changelog', False, _('open changelog')),
2156 [('c', 'changelog', False, _('open changelog')),
2157 ('m', 'manifest', False, _('open manifest')),
2157 ('m', 'manifest', False, _('open manifest')),
2158 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2158 ('f', 'format', 0, _('revlog format'), _('FORMAT'))],
2159 _('[-f FORMAT] -c|-m|FILE'),
2159 _('[-f FORMAT] -c|-m|FILE'),
2160 optionalrepo=True)
2160 optionalrepo=True)
2161 def debugindex(ui, repo, file_=None, **opts):
2161 def debugindex(ui, repo, file_=None, **opts):
2162 """dump the contents of an index file"""
2162 """dump the contents of an index file"""
2163 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2163 r = cmdutil.openrevlog(repo, 'debugindex', file_, opts)
2164 format = opts.get('format', 0)
2164 format = opts.get('format', 0)
2165 if format not in (0, 1):
2165 if format not in (0, 1):
2166 raise util.Abort(_("unknown format %d") % format)
2166 raise util.Abort(_("unknown format %d") % format)
2167
2167
2168 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2168 generaldelta = r.version & revlog.REVLOGGENERALDELTA
2169 if generaldelta:
2169 if generaldelta:
2170 basehdr = ' delta'
2170 basehdr = ' delta'
2171 else:
2171 else:
2172 basehdr = ' base'
2172 basehdr = ' base'
2173
2173
2174 if format == 0:
2174 if format == 0:
2175 ui.write(" rev offset length " + basehdr + " linkrev"
2175 ui.write(" rev offset length " + basehdr + " linkrev"
2176 " nodeid p1 p2\n")
2176 " nodeid p1 p2\n")
2177 elif format == 1:
2177 elif format == 1:
2178 ui.write(" rev flag offset length"
2178 ui.write(" rev flag offset length"
2179 " size " + basehdr + " link p1 p2"
2179 " size " + basehdr + " link p1 p2"
2180 " nodeid\n")
2180 " nodeid\n")
2181
2181
2182 for i in r:
2182 for i in r:
2183 node = r.node(i)
2183 node = r.node(i)
2184 if generaldelta:
2184 if generaldelta:
2185 base = r.deltaparent(i)
2185 base = r.deltaparent(i)
2186 else:
2186 else:
2187 base = r.chainbase(i)
2187 base = r.chainbase(i)
2188 if format == 0:
2188 if format == 0:
2189 try:
2189 try:
2190 pp = r.parents(node)
2190 pp = r.parents(node)
2191 except Exception:
2191 except Exception:
2192 pp = [nullid, nullid]
2192 pp = [nullid, nullid]
2193 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2193 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
2194 i, r.start(i), r.length(i), base, r.linkrev(i),
2194 i, r.start(i), r.length(i), base, r.linkrev(i),
2195 short(node), short(pp[0]), short(pp[1])))
2195 short(node), short(pp[0]), short(pp[1])))
2196 elif format == 1:
2196 elif format == 1:
2197 pr = r.parentrevs(i)
2197 pr = r.parentrevs(i)
2198 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2198 ui.write("% 6d %04x % 8d % 8d % 8d % 6d % 6d % 6d % 6d %s\n" % (
2199 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2199 i, r.flags(i), r.start(i), r.length(i), r.rawsize(i),
2200 base, r.linkrev(i), pr[0], pr[1], short(node)))
2200 base, r.linkrev(i), pr[0], pr[1], short(node)))
2201
2201
2202 @command('debugindexdot', [], _('FILE'), optionalrepo=True)
2202 @command('debugindexdot', [], _('FILE'), optionalrepo=True)
2203 def debugindexdot(ui, repo, file_):
2203 def debugindexdot(ui, repo, file_):
2204 """dump an index DAG as a graphviz dot file"""
2204 """dump an index DAG as a graphviz dot file"""
2205 r = None
2205 r = None
2206 if repo:
2206 if repo:
2207 filelog = repo.file(file_)
2207 filelog = repo.file(file_)
2208 if len(filelog):
2208 if len(filelog):
2209 r = filelog
2209 r = filelog
2210 if not r:
2210 if not r:
2211 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2211 r = revlog.revlog(scmutil.opener(os.getcwd(), audit=False), file_)
2212 ui.write(("digraph G {\n"))
2212 ui.write(("digraph G {\n"))
2213 for i in r:
2213 for i in r:
2214 node = r.node(i)
2214 node = r.node(i)
2215 pp = r.parents(node)
2215 pp = r.parents(node)
2216 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2216 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
2217 if pp[1] != nullid:
2217 if pp[1] != nullid:
2218 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2218 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
2219 ui.write("}\n")
2219 ui.write("}\n")
2220
2220
2221 @command('debuginstall', [], '', norepo=True)
2221 @command('debuginstall', [], '', norepo=True)
2222 def debuginstall(ui):
2222 def debuginstall(ui):
2223 '''test Mercurial installation
2223 '''test Mercurial installation
2224
2224
2225 Returns 0 on success.
2225 Returns 0 on success.
2226 '''
2226 '''
2227
2227
2228 def writetemp(contents):
2228 def writetemp(contents):
2229 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2229 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
2230 f = os.fdopen(fd, "wb")
2230 f = os.fdopen(fd, "wb")
2231 f.write(contents)
2231 f.write(contents)
2232 f.close()
2232 f.close()
2233 return name
2233 return name
2234
2234
2235 problems = 0
2235 problems = 0
2236
2236
2237 # encoding
2237 # encoding
2238 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2238 ui.status(_("checking encoding (%s)...\n") % encoding.encoding)
2239 try:
2239 try:
2240 encoding.fromlocal("test")
2240 encoding.fromlocal("test")
2241 except util.Abort, inst:
2241 except util.Abort, inst:
2242 ui.write(" %s\n" % inst)
2242 ui.write(" %s\n" % inst)
2243 ui.write(_(" (check that your locale is properly set)\n"))
2243 ui.write(_(" (check that your locale is properly set)\n"))
2244 problems += 1
2244 problems += 1
2245
2245
2246 # Python
2246 # Python
2247 ui.status(_("checking Python executable (%s)\n") % sys.executable)
2247 ui.status(_("checking Python executable (%s)\n") % sys.executable)
2248 ui.status(_("checking Python version (%s)\n")
2248 ui.status(_("checking Python version (%s)\n")
2249 % ("%s.%s.%s" % sys.version_info[:3]))
2249 % ("%s.%s.%s" % sys.version_info[:3]))
2250 ui.status(_("checking Python lib (%s)...\n")
2250 ui.status(_("checking Python lib (%s)...\n")
2251 % os.path.dirname(os.__file__))
2251 % os.path.dirname(os.__file__))
2252
2252
2253 # compiled modules
2253 # compiled modules
2254 ui.status(_("checking installed modules (%s)...\n")
2254 ui.status(_("checking installed modules (%s)...\n")
2255 % os.path.dirname(__file__))
2255 % os.path.dirname(__file__))
2256 try:
2256 try:
2257 import bdiff, mpatch, base85, osutil
2257 import bdiff, mpatch, base85, osutil
2258 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2258 dir(bdiff), dir(mpatch), dir(base85), dir(osutil) # quiet pyflakes
2259 except Exception, inst:
2259 except Exception, inst:
2260 ui.write(" %s\n" % inst)
2260 ui.write(" %s\n" % inst)
2261 ui.write(_(" One or more extensions could not be found"))
2261 ui.write(_(" One or more extensions could not be found"))
2262 ui.write(_(" (check that you compiled the extensions)\n"))
2262 ui.write(_(" (check that you compiled the extensions)\n"))
2263 problems += 1
2263 problems += 1
2264
2264
2265 # templates
2265 # templates
2266 import templater
2266 import templater
2267 p = templater.templatepaths()
2267 p = templater.templatepaths()
2268 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2268 ui.status(_("checking templates (%s)...\n") % ' '.join(p))
2269 if p:
2269 if p:
2270 m = templater.templatepath("map-cmdline.default")
2270 m = templater.templatepath("map-cmdline.default")
2271 if m:
2271 if m:
2272 # template found, check if it is working
2272 # template found, check if it is working
2273 try:
2273 try:
2274 templater.templater(m)
2274 templater.templater(m)
2275 except Exception, inst:
2275 except Exception, inst:
2276 ui.write(" %s\n" % inst)
2276 ui.write(" %s\n" % inst)
2277 p = None
2277 p = None
2278 else:
2278 else:
2279 ui.write(_(" template 'default' not found\n"))
2279 ui.write(_(" template 'default' not found\n"))
2280 p = None
2280 p = None
2281 else:
2281 else:
2282 ui.write(_(" no template directories found\n"))
2282 ui.write(_(" no template directories found\n"))
2283 if not p:
2283 if not p:
2284 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2284 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
2285 problems += 1
2285 problems += 1
2286
2286
2287 # editor
2287 # editor
2288 ui.status(_("checking commit editor...\n"))
2288 ui.status(_("checking commit editor...\n"))
2289 editor = ui.geteditor()
2289 editor = ui.geteditor()
2290 cmdpath = util.findexe(shlex.split(editor)[0])
2290 cmdpath = util.findexe(shlex.split(editor)[0])
2291 if not cmdpath:
2291 if not cmdpath:
2292 if editor == 'vi':
2292 if editor == 'vi':
2293 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2293 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
2294 ui.write(_(" (specify a commit editor in your configuration"
2294 ui.write(_(" (specify a commit editor in your configuration"
2295 " file)\n"))
2295 " file)\n"))
2296 else:
2296 else:
2297 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2297 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
2298 ui.write(_(" (specify a commit editor in your configuration"
2298 ui.write(_(" (specify a commit editor in your configuration"
2299 " file)\n"))
2299 " file)\n"))
2300 problems += 1
2300 problems += 1
2301
2301
2302 # check username
2302 # check username
2303 ui.status(_("checking username...\n"))
2303 ui.status(_("checking username...\n"))
2304 try:
2304 try:
2305 ui.username()
2305 ui.username()
2306 except util.Abort, e:
2306 except util.Abort, e:
2307 ui.write(" %s\n" % e)
2307 ui.write(" %s\n" % e)
2308 ui.write(_(" (specify a username in your configuration file)\n"))
2308 ui.write(_(" (specify a username in your configuration file)\n"))
2309 problems += 1
2309 problems += 1
2310
2310
2311 if not problems:
2311 if not problems:
2312 ui.status(_("no problems detected\n"))
2312 ui.status(_("no problems detected\n"))
2313 else:
2313 else:
2314 ui.write(_("%s problems detected,"
2314 ui.write(_("%s problems detected,"
2315 " please check your install!\n") % problems)
2315 " please check your install!\n") % problems)
2316
2316
2317 return problems
2317 return problems
2318
2318
2319 @command('debugknown', [], _('REPO ID...'), norepo=True)
2319 @command('debugknown', [], _('REPO ID...'), norepo=True)
2320 def debugknown(ui, repopath, *ids, **opts):
2320 def debugknown(ui, repopath, *ids, **opts):
2321 """test whether node ids are known to a repo
2321 """test whether node ids are known to a repo
2322
2322
2323 Every ID must be a full-length hex node id string. Returns a list of 0s
2323 Every ID must be a full-length hex node id string. Returns a list of 0s
2324 and 1s indicating unknown/known.
2324 and 1s indicating unknown/known.
2325 """
2325 """
2326 repo = hg.peer(ui, opts, repopath)
2326 repo = hg.peer(ui, opts, repopath)
2327 if not repo.capable('known'):
2327 if not repo.capable('known'):
2328 raise util.Abort("known() not supported by target repository")
2328 raise util.Abort("known() not supported by target repository")
2329 flags = repo.known([bin(s) for s in ids])
2329 flags = repo.known([bin(s) for s in ids])
2330 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2330 ui.write("%s\n" % ("".join([f and "1" or "0" for f in flags])))
2331
2331
2332 @command('debuglabelcomplete', [], _('LABEL...'))
2332 @command('debuglabelcomplete', [], _('LABEL...'))
2333 def debuglabelcomplete(ui, repo, *args):
2333 def debuglabelcomplete(ui, repo, *args):
2334 '''complete "labels" - tags, open branch names, bookmark names'''
2334 '''complete "labels" - tags, open branch names, bookmark names'''
2335
2335
2336 labels = set()
2336 labels = set()
2337 labels.update(t[0] for t in repo.tagslist())
2337 labels.update(t[0] for t in repo.tagslist())
2338 labels.update(repo._bookmarks.keys())
2338 labels.update(repo._bookmarks.keys())
2339 labels.update(tag for (tag, heads, tip, closed)
2339 labels.update(tag for (tag, heads, tip, closed)
2340 in repo.branchmap().iterbranches() if not closed)
2340 in repo.branchmap().iterbranches() if not closed)
2341 completions = set()
2341 completions = set()
2342 if not args:
2342 if not args:
2343 args = ['']
2343 args = ['']
2344 for a in args:
2344 for a in args:
2345 completions.update(l for l in labels if l.startswith(a))
2345 completions.update(l for l in labels if l.startswith(a))
2346 ui.write('\n'.join(sorted(completions)))
2346 ui.write('\n'.join(sorted(completions)))
2347 ui.write('\n')
2347 ui.write('\n')
2348
2348
2349 @command('debuglocks',
2349 @command('debuglocks',
2350 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2350 [('L', 'force-lock', None, _('free the store lock (DANGEROUS)')),
2351 ('W', 'force-wlock', None,
2351 ('W', 'force-wlock', None,
2352 _('free the working state lock (DANGEROUS)'))],
2352 _('free the working state lock (DANGEROUS)'))],
2353 _('[OPTION]...'))
2353 _('[OPTION]...'))
2354 def debuglocks(ui, repo, **opts):
2354 def debuglocks(ui, repo, **opts):
2355 """show or modify state of locks
2355 """show or modify state of locks
2356
2356
2357 By default, this command will show which locks are held. This
2357 By default, this command will show which locks are held. This
2358 includes the user and process holding the lock, the amount of time
2358 includes the user and process holding the lock, the amount of time
2359 the lock has been held, and the machine name where the process is
2359 the lock has been held, and the machine name where the process is
2360 running if it's not local.
2360 running if it's not local.
2361
2361
2362 Locks protect the integrity of Mercurial's data, so should be
2362 Locks protect the integrity of Mercurial's data, so should be
2363 treated with care. System crashes or other interruptions may cause
2363 treated with care. System crashes or other interruptions may cause
2364 locks to not be properly released, though Mercurial will usually
2364 locks to not be properly released, though Mercurial will usually
2365 detect and remove such stale locks automatically.
2365 detect and remove such stale locks automatically.
2366
2366
2367 However, detecting stale locks may not always be possible (for
2367 However, detecting stale locks may not always be possible (for
2368 instance, on a shared filesystem). Removing locks may also be
2368 instance, on a shared filesystem). Removing locks may also be
2369 blocked by filesystem permissions.
2369 blocked by filesystem permissions.
2370
2370
2371 Returns 0 if no locks are held.
2371 Returns 0 if no locks are held.
2372
2372
2373 """
2373 """
2374
2374
2375 if opts.get('force_lock'):
2375 if opts.get('force_lock'):
2376 repo.svfs.unlink('lock')
2376 repo.svfs.unlink('lock')
2377 if opts.get('force_wlock'):
2377 if opts.get('force_wlock'):
2378 repo.vfs.unlink('wlock')
2378 repo.vfs.unlink('wlock')
2379 if opts.get('force_lock') or opts.get('force_lock'):
2379 if opts.get('force_lock') or opts.get('force_lock'):
2380 return 0
2380 return 0
2381
2381
2382 now = time.time()
2382 now = time.time()
2383 held = 0
2383 held = 0
2384
2384
2385 def report(vfs, name, method):
2385 def report(vfs, name, method):
2386 # this causes stale locks to get reaped for more accurate reporting
2386 # this causes stale locks to get reaped for more accurate reporting
2387 try:
2387 try:
2388 l = method(False)
2388 l = method(False)
2389 except error.LockHeld:
2389 except error.LockHeld:
2390 l = None
2390 l = None
2391
2391
2392 if l:
2392 if l:
2393 l.release()
2393 l.release()
2394 else:
2394 else:
2395 try:
2395 try:
2396 stat = repo.svfs.lstat(name)
2396 stat = repo.svfs.lstat(name)
2397 age = now - stat.st_mtime
2397 age = now - stat.st_mtime
2398 user = util.username(stat.st_uid)
2398 user = util.username(stat.st_uid)
2399 locker = vfs.readlock(name)
2399 locker = vfs.readlock(name)
2400 if ":" in locker:
2400 if ":" in locker:
2401 host, pid = locker.split(':')
2401 host, pid = locker.split(':')
2402 if host == socket.gethostname():
2402 if host == socket.gethostname():
2403 locker = 'user %s, process %s' % (user, pid)
2403 locker = 'user %s, process %s' % (user, pid)
2404 else:
2404 else:
2405 locker = 'user %s, process %s, host %s' \
2405 locker = 'user %s, process %s, host %s' \
2406 % (user, pid, host)
2406 % (user, pid, host)
2407 ui.write("%-6s %s (%ds)\n" % (name + ":", locker, age))
2407 ui.write("%-6s %s (%ds)\n" % (name + ":", locker, age))
2408 return 1
2408 return 1
2409 except OSError, e:
2409 except OSError, e:
2410 if e.errno != errno.ENOENT:
2410 if e.errno != errno.ENOENT:
2411 raise
2411 raise
2412
2412
2413 ui.write("%-6s free\n" % (name + ":"))
2413 ui.write("%-6s free\n" % (name + ":"))
2414 return 0
2414 return 0
2415
2415
2416 held += report(repo.svfs, "lock", repo.lock)
2416 held += report(repo.svfs, "lock", repo.lock)
2417 held += report(repo.vfs, "wlock", repo.wlock)
2417 held += report(repo.vfs, "wlock", repo.wlock)
2418
2418
2419 return held
2419 return held
2420
2420
2421 @command('debugobsolete',
2421 @command('debugobsolete',
2422 [('', 'flags', 0, _('markers flag')),
2422 [('', 'flags', 0, _('markers flag')),
2423 ('', 'record-parents', False,
2423 ('', 'record-parents', False,
2424 _('record parent information for the precursor')),
2424 _('record parent information for the precursor')),
2425 ('r', 'rev', [], _('display markers relevant to REV')),
2425 ('r', 'rev', [], _('display markers relevant to REV')),
2426 ] + commitopts2,
2426 ] + commitopts2,
2427 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2427 _('[OBSOLETED [REPLACEMENT] [REPL... ]'))
2428 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2428 def debugobsolete(ui, repo, precursor=None, *successors, **opts):
2429 """create arbitrary obsolete marker
2429 """create arbitrary obsolete marker
2430
2430
2431 With no arguments, displays the list of obsolescence markers."""
2431 With no arguments, displays the list of obsolescence markers."""
2432
2432
2433 def parsenodeid(s):
2433 def parsenodeid(s):
2434 try:
2434 try:
2435 # We do not use revsingle/revrange functions here to accept
2435 # We do not use revsingle/revrange functions here to accept
2436 # arbitrary node identifiers, possibly not present in the
2436 # arbitrary node identifiers, possibly not present in the
2437 # local repository.
2437 # local repository.
2438 n = bin(s)
2438 n = bin(s)
2439 if len(n) != len(nullid):
2439 if len(n) != len(nullid):
2440 raise TypeError()
2440 raise TypeError()
2441 return n
2441 return n
2442 except TypeError:
2442 except TypeError:
2443 raise util.Abort('changeset references must be full hexadecimal '
2443 raise util.Abort('changeset references must be full hexadecimal '
2444 'node identifiers')
2444 'node identifiers')
2445
2445
2446 if precursor is not None:
2446 if precursor is not None:
2447 if opts['rev']:
2447 if opts['rev']:
2448 raise util.Abort('cannot select revision when creating marker')
2448 raise util.Abort('cannot select revision when creating marker')
2449 metadata = {}
2449 metadata = {}
2450 metadata['user'] = opts['user'] or ui.username()
2450 metadata['user'] = opts['user'] or ui.username()
2451 succs = tuple(parsenodeid(succ) for succ in successors)
2451 succs = tuple(parsenodeid(succ) for succ in successors)
2452 l = repo.lock()
2452 l = repo.lock()
2453 try:
2453 try:
2454 tr = repo.transaction('debugobsolete')
2454 tr = repo.transaction('debugobsolete')
2455 try:
2455 try:
2456 try:
2456 try:
2457 date = opts.get('date')
2457 date = opts.get('date')
2458 if date:
2458 if date:
2459 date = util.parsedate(date)
2459 date = util.parsedate(date)
2460 else:
2460 else:
2461 date = None
2461 date = None
2462 prec = parsenodeid(precursor)
2462 prec = parsenodeid(precursor)
2463 parents = None
2463 parents = None
2464 if opts['record_parents']:
2464 if opts['record_parents']:
2465 if prec not in repo.unfiltered():
2465 if prec not in repo.unfiltered():
2466 raise util.Abort('cannot used --record-parents on '
2466 raise util.Abort('cannot used --record-parents on '
2467 'unknown changesets')
2467 'unknown changesets')
2468 parents = repo.unfiltered()[prec].parents()
2468 parents = repo.unfiltered()[prec].parents()
2469 parents = tuple(p.node() for p in parents)
2469 parents = tuple(p.node() for p in parents)
2470 repo.obsstore.create(tr, prec, succs, opts['flags'],
2470 repo.obsstore.create(tr, prec, succs, opts['flags'],
2471 parents=parents, date=date,
2471 parents=parents, date=date,
2472 metadata=metadata)
2472 metadata=metadata)
2473 tr.close()
2473 tr.close()
2474 except ValueError, exc:
2474 except ValueError, exc:
2475 raise util.Abort(_('bad obsmarker input: %s') % exc)
2475 raise util.Abort(_('bad obsmarker input: %s') % exc)
2476 finally:
2476 finally:
2477 tr.release()
2477 tr.release()
2478 finally:
2478 finally:
2479 l.release()
2479 l.release()
2480 else:
2480 else:
2481 if opts['rev']:
2481 if opts['rev']:
2482 revs = scmutil.revrange(repo, opts['rev'])
2482 revs = scmutil.revrange(repo, opts['rev'])
2483 nodes = [repo[r].node() for r in revs]
2483 nodes = [repo[r].node() for r in revs]
2484 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2484 markers = list(obsolete.getmarkers(repo, nodes=nodes))
2485 markers.sort(key=lambda x: x._data)
2485 markers.sort(key=lambda x: x._data)
2486 else:
2486 else:
2487 markers = obsolete.getmarkers(repo)
2487 markers = obsolete.getmarkers(repo)
2488
2488
2489 for m in markers:
2489 for m in markers:
2490 cmdutil.showmarker(ui, m)
2490 cmdutil.showmarker(ui, m)
2491
2491
2492 @command('debugpathcomplete',
2492 @command('debugpathcomplete',
2493 [('f', 'full', None, _('complete an entire path')),
2493 [('f', 'full', None, _('complete an entire path')),
2494 ('n', 'normal', None, _('show only normal files')),
2494 ('n', 'normal', None, _('show only normal files')),
2495 ('a', 'added', None, _('show only added files')),
2495 ('a', 'added', None, _('show only added files')),
2496 ('r', 'removed', None, _('show only removed files'))],
2496 ('r', 'removed', None, _('show only removed files'))],
2497 _('FILESPEC...'))
2497 _('FILESPEC...'))
2498 def debugpathcomplete(ui, repo, *specs, **opts):
2498 def debugpathcomplete(ui, repo, *specs, **opts):
2499 '''complete part or all of a tracked path
2499 '''complete part or all of a tracked path
2500
2500
2501 This command supports shells that offer path name completion. It
2501 This command supports shells that offer path name completion. It
2502 currently completes only files already known to the dirstate.
2502 currently completes only files already known to the dirstate.
2503
2503
2504 Completion extends only to the next path segment unless
2504 Completion extends only to the next path segment unless
2505 --full is specified, in which case entire paths are used.'''
2505 --full is specified, in which case entire paths are used.'''
2506
2506
2507 def complete(path, acceptable):
2507 def complete(path, acceptable):
2508 dirstate = repo.dirstate
2508 dirstate = repo.dirstate
2509 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2509 spec = os.path.normpath(os.path.join(os.getcwd(), path))
2510 rootdir = repo.root + os.sep
2510 rootdir = repo.root + os.sep
2511 if spec != repo.root and not spec.startswith(rootdir):
2511 if spec != repo.root and not spec.startswith(rootdir):
2512 return [], []
2512 return [], []
2513 if os.path.isdir(spec):
2513 if os.path.isdir(spec):
2514 spec += '/'
2514 spec += '/'
2515 spec = spec[len(rootdir):]
2515 spec = spec[len(rootdir):]
2516 fixpaths = os.sep != '/'
2516 fixpaths = os.sep != '/'
2517 if fixpaths:
2517 if fixpaths:
2518 spec = spec.replace(os.sep, '/')
2518 spec = spec.replace(os.sep, '/')
2519 speclen = len(spec)
2519 speclen = len(spec)
2520 fullpaths = opts['full']
2520 fullpaths = opts['full']
2521 files, dirs = set(), set()
2521 files, dirs = set(), set()
2522 adddir, addfile = dirs.add, files.add
2522 adddir, addfile = dirs.add, files.add
2523 for f, st in dirstate.iteritems():
2523 for f, st in dirstate.iteritems():
2524 if f.startswith(spec) and st[0] in acceptable:
2524 if f.startswith(spec) and st[0] in acceptable:
2525 if fixpaths:
2525 if fixpaths:
2526 f = f.replace('/', os.sep)
2526 f = f.replace('/', os.sep)
2527 if fullpaths:
2527 if fullpaths:
2528 addfile(f)
2528 addfile(f)
2529 continue
2529 continue
2530 s = f.find(os.sep, speclen)
2530 s = f.find(os.sep, speclen)
2531 if s >= 0:
2531 if s >= 0:
2532 adddir(f[:s])
2532 adddir(f[:s])
2533 else:
2533 else:
2534 addfile(f)
2534 addfile(f)
2535 return files, dirs
2535 return files, dirs
2536
2536
2537 acceptable = ''
2537 acceptable = ''
2538 if opts['normal']:
2538 if opts['normal']:
2539 acceptable += 'nm'
2539 acceptable += 'nm'
2540 if opts['added']:
2540 if opts['added']:
2541 acceptable += 'a'
2541 acceptable += 'a'
2542 if opts['removed']:
2542 if opts['removed']:
2543 acceptable += 'r'
2543 acceptable += 'r'
2544 cwd = repo.getcwd()
2544 cwd = repo.getcwd()
2545 if not specs:
2545 if not specs:
2546 specs = ['.']
2546 specs = ['.']
2547
2547
2548 files, dirs = set(), set()
2548 files, dirs = set(), set()
2549 for spec in specs:
2549 for spec in specs:
2550 f, d = complete(spec, acceptable or 'nmar')
2550 f, d = complete(spec, acceptable or 'nmar')
2551 files.update(f)
2551 files.update(f)
2552 dirs.update(d)
2552 dirs.update(d)
2553 files.update(dirs)
2553 files.update(dirs)
2554 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2554 ui.write('\n'.join(repo.pathto(p, cwd) for p in sorted(files)))
2555 ui.write('\n')
2555 ui.write('\n')
2556
2556
2557 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2557 @command('debugpushkey', [], _('REPO NAMESPACE [KEY OLD NEW]'), norepo=True)
2558 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2558 def debugpushkey(ui, repopath, namespace, *keyinfo, **opts):
2559 '''access the pushkey key/value protocol
2559 '''access the pushkey key/value protocol
2560
2560
2561 With two args, list the keys in the given namespace.
2561 With two args, list the keys in the given namespace.
2562
2562
2563 With five args, set a key to new if it currently is set to old.
2563 With five args, set a key to new if it currently is set to old.
2564 Reports success or failure.
2564 Reports success or failure.
2565 '''
2565 '''
2566
2566
2567 target = hg.peer(ui, {}, repopath)
2567 target = hg.peer(ui, {}, repopath)
2568 if keyinfo:
2568 if keyinfo:
2569 key, old, new = keyinfo
2569 key, old, new = keyinfo
2570 r = target.pushkey(namespace, key, old, new)
2570 r = target.pushkey(namespace, key, old, new)
2571 ui.status(str(r) + '\n')
2571 ui.status(str(r) + '\n')
2572 return not r
2572 return not r
2573 else:
2573 else:
2574 for k, v in sorted(target.listkeys(namespace).iteritems()):
2574 for k, v in sorted(target.listkeys(namespace).iteritems()):
2575 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2575 ui.write("%s\t%s\n" % (k.encode('string-escape'),
2576 v.encode('string-escape')))
2576 v.encode('string-escape')))
2577
2577
2578 @command('debugpvec', [], _('A B'))
2578 @command('debugpvec', [], _('A B'))
2579 def debugpvec(ui, repo, a, b=None):
2579 def debugpvec(ui, repo, a, b=None):
2580 ca = scmutil.revsingle(repo, a)
2580 ca = scmutil.revsingle(repo, a)
2581 cb = scmutil.revsingle(repo, b)
2581 cb = scmutil.revsingle(repo, b)
2582 pa = pvec.ctxpvec(ca)
2582 pa = pvec.ctxpvec(ca)
2583 pb = pvec.ctxpvec(cb)
2583 pb = pvec.ctxpvec(cb)
2584 if pa == pb:
2584 if pa == pb:
2585 rel = "="
2585 rel = "="
2586 elif pa > pb:
2586 elif pa > pb:
2587 rel = ">"
2587 rel = ">"
2588 elif pa < pb:
2588 elif pa < pb:
2589 rel = "<"
2589 rel = "<"
2590 elif pa | pb:
2590 elif pa | pb:
2591 rel = "|"
2591 rel = "|"
2592 ui.write(_("a: %s\n") % pa)
2592 ui.write(_("a: %s\n") % pa)
2593 ui.write(_("b: %s\n") % pb)
2593 ui.write(_("b: %s\n") % pb)
2594 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2594 ui.write(_("depth(a): %d depth(b): %d\n") % (pa._depth, pb._depth))
2595 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2595 ui.write(_("delta: %d hdist: %d distance: %d relation: %s\n") %
2596 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2596 (abs(pa._depth - pb._depth), pvec._hamming(pa._vec, pb._vec),
2597 pa.distance(pb), rel))
2597 pa.distance(pb), rel))
2598
2598
2599 @command('debugrebuilddirstate|debugrebuildstate',
2599 @command('debugrebuilddirstate|debugrebuildstate',
2600 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2600 [('r', 'rev', '', _('revision to rebuild to'), _('REV'))],
2601 _('[-r REV]'))
2601 _('[-r REV]'))
2602 def debugrebuilddirstate(ui, repo, rev):
2602 def debugrebuilddirstate(ui, repo, rev):
2603 """rebuild the dirstate as it would look like for the given revision
2603 """rebuild the dirstate as it would look like for the given revision
2604
2604
2605 If no revision is specified the first current parent will be used.
2605 If no revision is specified the first current parent will be used.
2606
2606
2607 The dirstate will be set to the files of the given revision.
2607 The dirstate will be set to the files of the given revision.
2608 The actual working directory content or existing dirstate
2608 The actual working directory content or existing dirstate
2609 information such as adds or removes is not considered.
2609 information such as adds or removes is not considered.
2610
2610
2611 One use of this command is to make the next :hg:`status` invocation
2611 One use of this command is to make the next :hg:`status` invocation
2612 check the actual file content.
2612 check the actual file content.
2613 """
2613 """
2614 ctx = scmutil.revsingle(repo, rev)
2614 ctx = scmutil.revsingle(repo, rev)
2615 wlock = repo.wlock()
2615 wlock = repo.wlock()
2616 try:
2616 try:
2617 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2617 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
2618 finally:
2618 finally:
2619 wlock.release()
2619 wlock.release()
2620
2620
2621 @command('debugrename',
2621 @command('debugrename',
2622 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2622 [('r', 'rev', '', _('revision to debug'), _('REV'))],
2623 _('[-r REV] FILE'))
2623 _('[-r REV] FILE'))
2624 def debugrename(ui, repo, file1, *pats, **opts):
2624 def debugrename(ui, repo, file1, *pats, **opts):
2625 """dump rename information"""
2625 """dump rename information"""
2626
2626
2627 ctx = scmutil.revsingle(repo, opts.get('rev'))
2627 ctx = scmutil.revsingle(repo, opts.get('rev'))
2628 m = scmutil.match(ctx, (file1,) + pats, opts)
2628 m = scmutil.match(ctx, (file1,) + pats, opts)
2629 for abs in ctx.walk(m):
2629 for abs in ctx.walk(m):
2630 fctx = ctx[abs]
2630 fctx = ctx[abs]
2631 o = fctx.filelog().renamed(fctx.filenode())
2631 o = fctx.filelog().renamed(fctx.filenode())
2632 rel = m.rel(abs)
2632 rel = m.rel(abs)
2633 if o:
2633 if o:
2634 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2634 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
2635 else:
2635 else:
2636 ui.write(_("%s not renamed\n") % rel)
2636 ui.write(_("%s not renamed\n") % rel)
2637
2637
2638 @command('debugrevlog',
2638 @command('debugrevlog',
2639 [('c', 'changelog', False, _('open changelog')),
2639 [('c', 'changelog', False, _('open changelog')),
2640 ('m', 'manifest', False, _('open manifest')),
2640 ('m', 'manifest', False, _('open manifest')),
2641 ('d', 'dump', False, _('dump index data'))],
2641 ('d', 'dump', False, _('dump index data'))],
2642 _('-c|-m|FILE'),
2642 _('-c|-m|FILE'),
2643 optionalrepo=True)
2643 optionalrepo=True)
2644 def debugrevlog(ui, repo, file_=None, **opts):
2644 def debugrevlog(ui, repo, file_=None, **opts):
2645 """show data and statistics about a revlog"""
2645 """show data and statistics about a revlog"""
2646 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2646 r = cmdutil.openrevlog(repo, 'debugrevlog', file_, opts)
2647
2647
2648 if opts.get("dump"):
2648 if opts.get("dump"):
2649 numrevs = len(r)
2649 numrevs = len(r)
2650 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2650 ui.write("# rev p1rev p2rev start end deltastart base p1 p2"
2651 " rawsize totalsize compression heads chainlen\n")
2651 " rawsize totalsize compression heads chainlen\n")
2652 ts = 0
2652 ts = 0
2653 heads = set()
2653 heads = set()
2654
2654
2655 for rev in xrange(numrevs):
2655 for rev in xrange(numrevs):
2656 dbase = r.deltaparent(rev)
2656 dbase = r.deltaparent(rev)
2657 if dbase == -1:
2657 if dbase == -1:
2658 dbase = rev
2658 dbase = rev
2659 cbase = r.chainbase(rev)
2659 cbase = r.chainbase(rev)
2660 clen = r.chainlen(rev)
2660 clen = r.chainlen(rev)
2661 p1, p2 = r.parentrevs(rev)
2661 p1, p2 = r.parentrevs(rev)
2662 rs = r.rawsize(rev)
2662 rs = r.rawsize(rev)
2663 ts = ts + rs
2663 ts = ts + rs
2664 heads -= set(r.parentrevs(rev))
2664 heads -= set(r.parentrevs(rev))
2665 heads.add(rev)
2665 heads.add(rev)
2666 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2666 ui.write("%5d %5d %5d %5d %5d %10d %4d %4d %4d %7d %9d "
2667 "%11d %5d %8d\n" %
2667 "%11d %5d %8d\n" %
2668 (rev, p1, p2, r.start(rev), r.end(rev),
2668 (rev, p1, p2, r.start(rev), r.end(rev),
2669 r.start(dbase), r.start(cbase),
2669 r.start(dbase), r.start(cbase),
2670 r.start(p1), r.start(p2),
2670 r.start(p1), r.start(p2),
2671 rs, ts, ts / r.end(rev), len(heads), clen))
2671 rs, ts, ts / r.end(rev), len(heads), clen))
2672 return 0
2672 return 0
2673
2673
2674 v = r.version
2674 v = r.version
2675 format = v & 0xFFFF
2675 format = v & 0xFFFF
2676 flags = []
2676 flags = []
2677 gdelta = False
2677 gdelta = False
2678 if v & revlog.REVLOGNGINLINEDATA:
2678 if v & revlog.REVLOGNGINLINEDATA:
2679 flags.append('inline')
2679 flags.append('inline')
2680 if v & revlog.REVLOGGENERALDELTA:
2680 if v & revlog.REVLOGGENERALDELTA:
2681 gdelta = True
2681 gdelta = True
2682 flags.append('generaldelta')
2682 flags.append('generaldelta')
2683 if not flags:
2683 if not flags:
2684 flags = ['(none)']
2684 flags = ['(none)']
2685
2685
2686 nummerges = 0
2686 nummerges = 0
2687 numfull = 0
2687 numfull = 0
2688 numprev = 0
2688 numprev = 0
2689 nump1 = 0
2689 nump1 = 0
2690 nump2 = 0
2690 nump2 = 0
2691 numother = 0
2691 numother = 0
2692 nump1prev = 0
2692 nump1prev = 0
2693 nump2prev = 0
2693 nump2prev = 0
2694 chainlengths = []
2694 chainlengths = []
2695
2695
2696 datasize = [None, 0, 0L]
2696 datasize = [None, 0, 0L]
2697 fullsize = [None, 0, 0L]
2697 fullsize = [None, 0, 0L]
2698 deltasize = [None, 0, 0L]
2698 deltasize = [None, 0, 0L]
2699
2699
2700 def addsize(size, l):
2700 def addsize(size, l):
2701 if l[0] is None or size < l[0]:
2701 if l[0] is None or size < l[0]:
2702 l[0] = size
2702 l[0] = size
2703 if size > l[1]:
2703 if size > l[1]:
2704 l[1] = size
2704 l[1] = size
2705 l[2] += size
2705 l[2] += size
2706
2706
2707 numrevs = len(r)
2707 numrevs = len(r)
2708 for rev in xrange(numrevs):
2708 for rev in xrange(numrevs):
2709 p1, p2 = r.parentrevs(rev)
2709 p1, p2 = r.parentrevs(rev)
2710 delta = r.deltaparent(rev)
2710 delta = r.deltaparent(rev)
2711 if format > 0:
2711 if format > 0:
2712 addsize(r.rawsize(rev), datasize)
2712 addsize(r.rawsize(rev), datasize)
2713 if p2 != nullrev:
2713 if p2 != nullrev:
2714 nummerges += 1
2714 nummerges += 1
2715 size = r.length(rev)
2715 size = r.length(rev)
2716 if delta == nullrev:
2716 if delta == nullrev:
2717 chainlengths.append(0)
2717 chainlengths.append(0)
2718 numfull += 1
2718 numfull += 1
2719 addsize(size, fullsize)
2719 addsize(size, fullsize)
2720 else:
2720 else:
2721 chainlengths.append(chainlengths[delta] + 1)
2721 chainlengths.append(chainlengths[delta] + 1)
2722 addsize(size, deltasize)
2722 addsize(size, deltasize)
2723 if delta == rev - 1:
2723 if delta == rev - 1:
2724 numprev += 1
2724 numprev += 1
2725 if delta == p1:
2725 if delta == p1:
2726 nump1prev += 1
2726 nump1prev += 1
2727 elif delta == p2:
2727 elif delta == p2:
2728 nump2prev += 1
2728 nump2prev += 1
2729 elif delta == p1:
2729 elif delta == p1:
2730 nump1 += 1
2730 nump1 += 1
2731 elif delta == p2:
2731 elif delta == p2:
2732 nump2 += 1
2732 nump2 += 1
2733 elif delta != nullrev:
2733 elif delta != nullrev:
2734 numother += 1
2734 numother += 1
2735
2735
2736 # Adjust size min value for empty cases
2736 # Adjust size min value for empty cases
2737 for size in (datasize, fullsize, deltasize):
2737 for size in (datasize, fullsize, deltasize):
2738 if size[0] is None:
2738 if size[0] is None:
2739 size[0] = 0
2739 size[0] = 0
2740
2740
2741 numdeltas = numrevs - numfull
2741 numdeltas = numrevs - numfull
2742 numoprev = numprev - nump1prev - nump2prev
2742 numoprev = numprev - nump1prev - nump2prev
2743 totalrawsize = datasize[2]
2743 totalrawsize = datasize[2]
2744 datasize[2] /= numrevs
2744 datasize[2] /= numrevs
2745 fulltotal = fullsize[2]
2745 fulltotal = fullsize[2]
2746 fullsize[2] /= numfull
2746 fullsize[2] /= numfull
2747 deltatotal = deltasize[2]
2747 deltatotal = deltasize[2]
2748 if numrevs - numfull > 0:
2748 if numrevs - numfull > 0:
2749 deltasize[2] /= numrevs - numfull
2749 deltasize[2] /= numrevs - numfull
2750 totalsize = fulltotal + deltatotal
2750 totalsize = fulltotal + deltatotal
2751 avgchainlen = sum(chainlengths) / numrevs
2751 avgchainlen = sum(chainlengths) / numrevs
2752 compratio = totalrawsize / totalsize
2752 compratio = totalrawsize / totalsize
2753
2753
2754 basedfmtstr = '%%%dd\n'
2754 basedfmtstr = '%%%dd\n'
2755 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2755 basepcfmtstr = '%%%dd %s(%%5.2f%%%%)\n'
2756
2756
2757 def dfmtstr(max):
2757 def dfmtstr(max):
2758 return basedfmtstr % len(str(max))
2758 return basedfmtstr % len(str(max))
2759 def pcfmtstr(max, padding=0):
2759 def pcfmtstr(max, padding=0):
2760 return basepcfmtstr % (len(str(max)), ' ' * padding)
2760 return basepcfmtstr % (len(str(max)), ' ' * padding)
2761
2761
2762 def pcfmt(value, total):
2762 def pcfmt(value, total):
2763 return (value, 100 * float(value) / total)
2763 return (value, 100 * float(value) / total)
2764
2764
2765 ui.write(('format : %d\n') % format)
2765 ui.write(('format : %d\n') % format)
2766 ui.write(('flags : %s\n') % ', '.join(flags))
2766 ui.write(('flags : %s\n') % ', '.join(flags))
2767
2767
2768 ui.write('\n')
2768 ui.write('\n')
2769 fmt = pcfmtstr(totalsize)
2769 fmt = pcfmtstr(totalsize)
2770 fmt2 = dfmtstr(totalsize)
2770 fmt2 = dfmtstr(totalsize)
2771 ui.write(('revisions : ') + fmt2 % numrevs)
2771 ui.write(('revisions : ') + fmt2 % numrevs)
2772 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2772 ui.write((' merges : ') + fmt % pcfmt(nummerges, numrevs))
2773 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2773 ui.write((' normal : ') + fmt % pcfmt(numrevs - nummerges, numrevs))
2774 ui.write(('revisions : ') + fmt2 % numrevs)
2774 ui.write(('revisions : ') + fmt2 % numrevs)
2775 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2775 ui.write((' full : ') + fmt % pcfmt(numfull, numrevs))
2776 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2776 ui.write((' deltas : ') + fmt % pcfmt(numdeltas, numrevs))
2777 ui.write(('revision size : ') + fmt2 % totalsize)
2777 ui.write(('revision size : ') + fmt2 % totalsize)
2778 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2778 ui.write((' full : ') + fmt % pcfmt(fulltotal, totalsize))
2779 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2779 ui.write((' deltas : ') + fmt % pcfmt(deltatotal, totalsize))
2780
2780
2781 ui.write('\n')
2781 ui.write('\n')
2782 fmt = dfmtstr(max(avgchainlen, compratio))
2782 fmt = dfmtstr(max(avgchainlen, compratio))
2783 ui.write(('avg chain length : ') + fmt % avgchainlen)
2783 ui.write(('avg chain length : ') + fmt % avgchainlen)
2784 ui.write(('compression ratio : ') + fmt % compratio)
2784 ui.write(('compression ratio : ') + fmt % compratio)
2785
2785
2786 if format > 0:
2786 if format > 0:
2787 ui.write('\n')
2787 ui.write('\n')
2788 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2788 ui.write(('uncompressed data size (min/max/avg) : %d / %d / %d\n')
2789 % tuple(datasize))
2789 % tuple(datasize))
2790 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2790 ui.write(('full revision size (min/max/avg) : %d / %d / %d\n')
2791 % tuple(fullsize))
2791 % tuple(fullsize))
2792 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2792 ui.write(('delta size (min/max/avg) : %d / %d / %d\n')
2793 % tuple(deltasize))
2793 % tuple(deltasize))
2794
2794
2795 if numdeltas > 0:
2795 if numdeltas > 0:
2796 ui.write('\n')
2796 ui.write('\n')
2797 fmt = pcfmtstr(numdeltas)
2797 fmt = pcfmtstr(numdeltas)
2798 fmt2 = pcfmtstr(numdeltas, 4)
2798 fmt2 = pcfmtstr(numdeltas, 4)
2799 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2799 ui.write(('deltas against prev : ') + fmt % pcfmt(numprev, numdeltas))
2800 if numprev > 0:
2800 if numprev > 0:
2801 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2801 ui.write((' where prev = p1 : ') + fmt2 % pcfmt(nump1prev,
2802 numprev))
2802 numprev))
2803 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2803 ui.write((' where prev = p2 : ') + fmt2 % pcfmt(nump2prev,
2804 numprev))
2804 numprev))
2805 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2805 ui.write((' other : ') + fmt2 % pcfmt(numoprev,
2806 numprev))
2806 numprev))
2807 if gdelta:
2807 if gdelta:
2808 ui.write(('deltas against p1 : ')
2808 ui.write(('deltas against p1 : ')
2809 + fmt % pcfmt(nump1, numdeltas))
2809 + fmt % pcfmt(nump1, numdeltas))
2810 ui.write(('deltas against p2 : ')
2810 ui.write(('deltas against p2 : ')
2811 + fmt % pcfmt(nump2, numdeltas))
2811 + fmt % pcfmt(nump2, numdeltas))
2812 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2812 ui.write(('deltas against other : ') + fmt % pcfmt(numother,
2813 numdeltas))
2813 numdeltas))
2814
2814
2815 @command('debugrevspec',
2815 @command('debugrevspec',
2816 [('', 'optimize', None, _('print parsed tree after optimizing'))],
2816 [('', 'optimize', None, _('print parsed tree after optimizing'))],
2817 ('REVSPEC'))
2817 ('REVSPEC'))
2818 def debugrevspec(ui, repo, expr, **opts):
2818 def debugrevspec(ui, repo, expr, **opts):
2819 """parse and apply a revision specification
2819 """parse and apply a revision specification
2820
2820
2821 Use --verbose to print the parsed tree before and after aliases
2821 Use --verbose to print the parsed tree before and after aliases
2822 expansion.
2822 expansion.
2823 """
2823 """
2824 if ui.verbose:
2824 if ui.verbose:
2825 tree = revset.parse(expr)[0]
2825 tree = revset.parse(expr)[0]
2826 ui.note(revset.prettyformat(tree), "\n")
2826 ui.note(revset.prettyformat(tree), "\n")
2827 newtree = revset.findaliases(ui, tree)
2827 newtree = revset.findaliases(ui, tree)
2828 if newtree != tree:
2828 if newtree != tree:
2829 ui.note(revset.prettyformat(newtree), "\n")
2829 ui.note(revset.prettyformat(newtree), "\n")
2830 if opts["optimize"]:
2830 if opts["optimize"]:
2831 weight, optimizedtree = revset.optimize(newtree, True)
2831 weight, optimizedtree = revset.optimize(newtree, True)
2832 ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
2832 ui.note("* optimized:\n", revset.prettyformat(optimizedtree), "\n")
2833 func = revset.match(ui, expr)
2833 func = revset.match(ui, expr)
2834 for c in func(repo, revset.spanset(repo)):
2834 for c in func(repo, revset.spanset(repo)):
2835 ui.write("%s\n" % c)
2835 ui.write("%s\n" % c)
2836
2836
2837 @command('debugsetparents', [], _('REV1 [REV2]'))
2837 @command('debugsetparents', [], _('REV1 [REV2]'))
2838 def debugsetparents(ui, repo, rev1, rev2=None):
2838 def debugsetparents(ui, repo, rev1, rev2=None):
2839 """manually set the parents of the current working directory
2839 """manually set the parents of the current working directory
2840
2840
2841 This is useful for writing repository conversion tools, but should
2841 This is useful for writing repository conversion tools, but should
2842 be used with care.
2842 be used with care.
2843
2843
2844 Returns 0 on success.
2844 Returns 0 on success.
2845 """
2845 """
2846
2846
2847 r1 = scmutil.revsingle(repo, rev1).node()
2847 r1 = scmutil.revsingle(repo, rev1).node()
2848 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2848 r2 = scmutil.revsingle(repo, rev2, 'null').node()
2849
2849
2850 wlock = repo.wlock()
2850 wlock = repo.wlock()
2851 try:
2851 try:
2852 repo.dirstate.beginparentchange()
2852 repo.dirstate.beginparentchange()
2853 repo.setparents(r1, r2)
2853 repo.setparents(r1, r2)
2854 repo.dirstate.endparentchange()
2854 repo.dirstate.endparentchange()
2855 finally:
2855 finally:
2856 wlock.release()
2856 wlock.release()
2857
2857
2858 @command('debugdirstate|debugstate',
2858 @command('debugdirstate|debugstate',
2859 [('', 'nodates', None, _('do not display the saved mtime')),
2859 [('', 'nodates', None, _('do not display the saved mtime')),
2860 ('', 'datesort', None, _('sort by saved mtime'))],
2860 ('', 'datesort', None, _('sort by saved mtime'))],
2861 _('[OPTION]...'))
2861 _('[OPTION]...'))
2862 def debugstate(ui, repo, nodates=None, datesort=None):
2862 def debugstate(ui, repo, nodates=None, datesort=None):
2863 """show the contents of the current dirstate"""
2863 """show the contents of the current dirstate"""
2864 timestr = ""
2864 timestr = ""
2865 showdate = not nodates
2865 showdate = not nodates
2866 if datesort:
2866 if datesort:
2867 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2867 keyfunc = lambda x: (x[1][3], x[0]) # sort by mtime, then by filename
2868 else:
2868 else:
2869 keyfunc = None # sort by filename
2869 keyfunc = None # sort by filename
2870 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2870 for file_, ent in sorted(repo.dirstate._map.iteritems(), key=keyfunc):
2871 if showdate:
2871 if showdate:
2872 if ent[3] == -1:
2872 if ent[3] == -1:
2873 # Pad or slice to locale representation
2873 # Pad or slice to locale representation
2874 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2874 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ",
2875 time.localtime(0)))
2875 time.localtime(0)))
2876 timestr = 'unset'
2876 timestr = 'unset'
2877 timestr = (timestr[:locale_len] +
2877 timestr = (timestr[:locale_len] +
2878 ' ' * (locale_len - len(timestr)))
2878 ' ' * (locale_len - len(timestr)))
2879 else:
2879 else:
2880 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2880 timestr = time.strftime("%Y-%m-%d %H:%M:%S ",
2881 time.localtime(ent[3]))
2881 time.localtime(ent[3]))
2882 if ent[1] & 020000:
2882 if ent[1] & 020000:
2883 mode = 'lnk'
2883 mode = 'lnk'
2884 else:
2884 else:
2885 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2885 mode = '%3o' % (ent[1] & 0777 & ~util.umask)
2886 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2886 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
2887 for f in repo.dirstate.copies():
2887 for f in repo.dirstate.copies():
2888 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2888 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
2889
2889
2890 @command('debugsub',
2890 @command('debugsub',
2891 [('r', 'rev', '',
2891 [('r', 'rev', '',
2892 _('revision to check'), _('REV'))],
2892 _('revision to check'), _('REV'))],
2893 _('[-r REV] [REV]'))
2893 _('[-r REV] [REV]'))
2894 def debugsub(ui, repo, rev=None):
2894 def debugsub(ui, repo, rev=None):
2895 ctx = scmutil.revsingle(repo, rev, None)
2895 ctx = scmutil.revsingle(repo, rev, None)
2896 for k, v in sorted(ctx.substate.items()):
2896 for k, v in sorted(ctx.substate.items()):
2897 ui.write(('path %s\n') % k)
2897 ui.write(('path %s\n') % k)
2898 ui.write((' source %s\n') % v[0])
2898 ui.write((' source %s\n') % v[0])
2899 ui.write((' revision %s\n') % v[1])
2899 ui.write((' revision %s\n') % v[1])
2900
2900
2901 @command('debugsuccessorssets',
2901 @command('debugsuccessorssets',
2902 [],
2902 [],
2903 _('[REV]'))
2903 _('[REV]'))
2904 def debugsuccessorssets(ui, repo, *revs):
2904 def debugsuccessorssets(ui, repo, *revs):
2905 """show set of successors for revision
2905 """show set of successors for revision
2906
2906
2907 A successors set of changeset A is a consistent group of revisions that
2907 A successors set of changeset A is a consistent group of revisions that
2908 succeed A. It contains non-obsolete changesets only.
2908 succeed A. It contains non-obsolete changesets only.
2909
2909
2910 In most cases a changeset A has a single successors set containing a single
2910 In most cases a changeset A has a single successors set containing a single
2911 successor (changeset A replaced by A').
2911 successor (changeset A replaced by A').
2912
2912
2913 A changeset that is made obsolete with no successors are called "pruned".
2913 A changeset that is made obsolete with no successors are called "pruned".
2914 Such changesets have no successors sets at all.
2914 Such changesets have no successors sets at all.
2915
2915
2916 A changeset that has been "split" will have a successors set containing
2916 A changeset that has been "split" will have a successors set containing
2917 more than one successor.
2917 more than one successor.
2918
2918
2919 A changeset that has been rewritten in multiple different ways is called
2919 A changeset that has been rewritten in multiple different ways is called
2920 "divergent". Such changesets have multiple successor sets (each of which
2920 "divergent". Such changesets have multiple successor sets (each of which
2921 may also be split, i.e. have multiple successors).
2921 may also be split, i.e. have multiple successors).
2922
2922
2923 Results are displayed as follows::
2923 Results are displayed as follows::
2924
2924
2925 <rev1>
2925 <rev1>
2926 <successors-1A>
2926 <successors-1A>
2927 <rev2>
2927 <rev2>
2928 <successors-2A>
2928 <successors-2A>
2929 <successors-2B1> <successors-2B2> <successors-2B3>
2929 <successors-2B1> <successors-2B2> <successors-2B3>
2930
2930
2931 Here rev2 has two possible (i.e. divergent) successors sets. The first
2931 Here rev2 has two possible (i.e. divergent) successors sets. The first
2932 holds one element, whereas the second holds three (i.e. the changeset has
2932 holds one element, whereas the second holds three (i.e. the changeset has
2933 been split).
2933 been split).
2934 """
2934 """
2935 # passed to successorssets caching computation from one call to another
2935 # passed to successorssets caching computation from one call to another
2936 cache = {}
2936 cache = {}
2937 ctx2str = str
2937 ctx2str = str
2938 node2str = short
2938 node2str = short
2939 if ui.debug():
2939 if ui.debug():
2940 def ctx2str(ctx):
2940 def ctx2str(ctx):
2941 return ctx.hex()
2941 return ctx.hex()
2942 node2str = hex
2942 node2str = hex
2943 for rev in scmutil.revrange(repo, revs):
2943 for rev in scmutil.revrange(repo, revs):
2944 ctx = repo[rev]
2944 ctx = repo[rev]
2945 ui.write('%s\n'% ctx2str(ctx))
2945 ui.write('%s\n'% ctx2str(ctx))
2946 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
2946 for succsset in obsolete.successorssets(repo, ctx.node(), cache):
2947 if succsset:
2947 if succsset:
2948 ui.write(' ')
2948 ui.write(' ')
2949 ui.write(node2str(succsset[0]))
2949 ui.write(node2str(succsset[0]))
2950 for node in succsset[1:]:
2950 for node in succsset[1:]:
2951 ui.write(' ')
2951 ui.write(' ')
2952 ui.write(node2str(node))
2952 ui.write(node2str(node))
2953 ui.write('\n')
2953 ui.write('\n')
2954
2954
2955 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
2955 @command('debugwalk', walkopts, _('[OPTION]... [FILE]...'), inferrepo=True)
2956 def debugwalk(ui, repo, *pats, **opts):
2956 def debugwalk(ui, repo, *pats, **opts):
2957 """show how files match on given patterns"""
2957 """show how files match on given patterns"""
2958 m = scmutil.match(repo[None], pats, opts)
2958 m = scmutil.match(repo[None], pats, opts)
2959 items = list(repo.walk(m))
2959 items = list(repo.walk(m))
2960 if not items:
2960 if not items:
2961 return
2961 return
2962 f = lambda fn: fn
2962 f = lambda fn: fn
2963 if ui.configbool('ui', 'slash') and os.sep != '/':
2963 if ui.configbool('ui', 'slash') and os.sep != '/':
2964 f = lambda fn: util.normpath(fn)
2964 f = lambda fn: util.normpath(fn)
2965 fmt = 'f %%-%ds %%-%ds %%s' % (
2965 fmt = 'f %%-%ds %%-%ds %%s' % (
2966 max([len(abs) for abs in items]),
2966 max([len(abs) for abs in items]),
2967 max([len(m.rel(abs)) for abs in items]))
2967 max([len(m.rel(abs)) for abs in items]))
2968 for abs in items:
2968 for abs in items:
2969 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2969 line = fmt % (abs, f(m.rel(abs)), m.exact(abs) and 'exact' or '')
2970 ui.write("%s\n" % line.rstrip())
2970 ui.write("%s\n" % line.rstrip())
2971
2971
2972 @command('debugwireargs',
2972 @command('debugwireargs',
2973 [('', 'three', '', 'three'),
2973 [('', 'three', '', 'three'),
2974 ('', 'four', '', 'four'),
2974 ('', 'four', '', 'four'),
2975 ('', 'five', '', 'five'),
2975 ('', 'five', '', 'five'),
2976 ] + remoteopts,
2976 ] + remoteopts,
2977 _('REPO [OPTIONS]... [ONE [TWO]]'),
2977 _('REPO [OPTIONS]... [ONE [TWO]]'),
2978 norepo=True)
2978 norepo=True)
2979 def debugwireargs(ui, repopath, *vals, **opts):
2979 def debugwireargs(ui, repopath, *vals, **opts):
2980 repo = hg.peer(ui, opts, repopath)
2980 repo = hg.peer(ui, opts, repopath)
2981 for opt in remoteopts:
2981 for opt in remoteopts:
2982 del opts[opt[1]]
2982 del opts[opt[1]]
2983 args = {}
2983 args = {}
2984 for k, v in opts.iteritems():
2984 for k, v in opts.iteritems():
2985 if v:
2985 if v:
2986 args[k] = v
2986 args[k] = v
2987 # run twice to check that we don't mess up the stream for the next command
2987 # run twice to check that we don't mess up the stream for the next command
2988 res1 = repo.debugwireargs(*vals, **args)
2988 res1 = repo.debugwireargs(*vals, **args)
2989 res2 = repo.debugwireargs(*vals, **args)
2989 res2 = repo.debugwireargs(*vals, **args)
2990 ui.write("%s\n" % res1)
2990 ui.write("%s\n" % res1)
2991 if res1 != res2:
2991 if res1 != res2:
2992 ui.warn("%s\n" % res2)
2992 ui.warn("%s\n" % res2)
2993
2993
2994 @command('^diff',
2994 @command('^diff',
2995 [('r', 'rev', [], _('revision'), _('REV')),
2995 [('r', 'rev', [], _('revision'), _('REV')),
2996 ('c', 'change', '', _('change made by revision'), _('REV'))
2996 ('c', 'change', '', _('change made by revision'), _('REV'))
2997 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2997 ] + diffopts + diffopts2 + walkopts + subrepoopts,
2998 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2998 _('[OPTION]... ([-c REV] | [-r REV1 [-r REV2]]) [FILE]...'),
2999 inferrepo=True)
2999 inferrepo=True)
3000 def diff(ui, repo, *pats, **opts):
3000 def diff(ui, repo, *pats, **opts):
3001 """diff repository (or selected files)
3001 """diff repository (or selected files)
3002
3002
3003 Show differences between revisions for the specified files.
3003 Show differences between revisions for the specified files.
3004
3004
3005 Differences between files are shown using the unified diff format.
3005 Differences between files are shown using the unified diff format.
3006
3006
3007 .. note::
3007 .. note::
3008
3008
3009 diff may generate unexpected results for merges, as it will
3009 diff may generate unexpected results for merges, as it will
3010 default to comparing against the working directory's first
3010 default to comparing against the working directory's first
3011 parent changeset if no revisions are specified.
3011 parent changeset if no revisions are specified.
3012
3012
3013 When two revision arguments are given, then changes are shown
3013 When two revision arguments are given, then changes are shown
3014 between those revisions. If only one revision is specified then
3014 between those revisions. If only one revision is specified then
3015 that revision is compared to the working directory, and, when no
3015 that revision is compared to the working directory, and, when no
3016 revisions are specified, the working directory files are compared
3016 revisions are specified, the working directory files are compared
3017 to its parent.
3017 to its parent.
3018
3018
3019 Alternatively you can specify -c/--change with a revision to see
3019 Alternatively you can specify -c/--change with a revision to see
3020 the changes in that changeset relative to its first parent.
3020 the changes in that changeset relative to its first parent.
3021
3021
3022 Without the -a/--text option, diff will avoid generating diffs of
3022 Without the -a/--text option, diff will avoid generating diffs of
3023 files it detects as binary. With -a, diff will generate a diff
3023 files it detects as binary. With -a, diff will generate a diff
3024 anyway, probably with undesirable results.
3024 anyway, probably with undesirable results.
3025
3025
3026 Use the -g/--git option to generate diffs in the git extended diff
3026 Use the -g/--git option to generate diffs in the git extended diff
3027 format. For more information, read :hg:`help diffs`.
3027 format. For more information, read :hg:`help diffs`.
3028
3028
3029 .. container:: verbose
3029 .. container:: verbose
3030
3030
3031 Examples:
3031 Examples:
3032
3032
3033 - compare a file in the current working directory to its parent::
3033 - compare a file in the current working directory to its parent::
3034
3034
3035 hg diff foo.c
3035 hg diff foo.c
3036
3036
3037 - compare two historical versions of a directory, with rename info::
3037 - compare two historical versions of a directory, with rename info::
3038
3038
3039 hg diff --git -r 1.0:1.2 lib/
3039 hg diff --git -r 1.0:1.2 lib/
3040
3040
3041 - get change stats relative to the last change on some date::
3041 - get change stats relative to the last change on some date::
3042
3042
3043 hg diff --stat -r "date('may 2')"
3043 hg diff --stat -r "date('may 2')"
3044
3044
3045 - diff all newly-added files that contain a keyword::
3045 - diff all newly-added files that contain a keyword::
3046
3046
3047 hg diff "set:added() and grep(GNU)"
3047 hg diff "set:added() and grep(GNU)"
3048
3048
3049 - compare a revision and its parents::
3049 - compare a revision and its parents::
3050
3050
3051 hg diff -c 9353 # compare against first parent
3051 hg diff -c 9353 # compare against first parent
3052 hg diff -r 9353^:9353 # same using revset syntax
3052 hg diff -r 9353^:9353 # same using revset syntax
3053 hg diff -r 9353^2:9353 # compare against the second parent
3053 hg diff -r 9353^2:9353 # compare against the second parent
3054
3054
3055 Returns 0 on success.
3055 Returns 0 on success.
3056 """
3056 """
3057
3057
3058 revs = opts.get('rev')
3058 revs = opts.get('rev')
3059 change = opts.get('change')
3059 change = opts.get('change')
3060 stat = opts.get('stat')
3060 stat = opts.get('stat')
3061 reverse = opts.get('reverse')
3061 reverse = opts.get('reverse')
3062
3062
3063 if revs and change:
3063 if revs and change:
3064 msg = _('cannot specify --rev and --change at the same time')
3064 msg = _('cannot specify --rev and --change at the same time')
3065 raise util.Abort(msg)
3065 raise util.Abort(msg)
3066 elif change:
3066 elif change:
3067 node2 = scmutil.revsingle(repo, change, None).node()
3067 node2 = scmutil.revsingle(repo, change, None).node()
3068 node1 = repo[node2].p1().node()
3068 node1 = repo[node2].p1().node()
3069 else:
3069 else:
3070 node1, node2 = scmutil.revpair(repo, revs)
3070 node1, node2 = scmutil.revpair(repo, revs)
3071
3071
3072 if reverse:
3072 if reverse:
3073 node1, node2 = node2, node1
3073 node1, node2 = node2, node1
3074
3074
3075 diffopts = patch.diffopts(ui, opts)
3075 diffopts = patch.diffopts(ui, opts)
3076 m = scmutil.match(repo[node2], pats, opts)
3076 m = scmutil.match(repo[node2], pats, opts)
3077 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3077 cmdutil.diffordiffstat(ui, repo, diffopts, node1, node2, m, stat=stat,
3078 listsubrepos=opts.get('subrepos'))
3078 listsubrepos=opts.get('subrepos'))
3079
3079
3080 @command('^export',
3080 @command('^export',
3081 [('o', 'output', '',
3081 [('o', 'output', '',
3082 _('print output to file with formatted name'), _('FORMAT')),
3082 _('print output to file with formatted name'), _('FORMAT')),
3083 ('', 'switch-parent', None, _('diff against the second parent')),
3083 ('', 'switch-parent', None, _('diff against the second parent')),
3084 ('r', 'rev', [], _('revisions to export'), _('REV')),
3084 ('r', 'rev', [], _('revisions to export'), _('REV')),
3085 ] + diffopts,
3085 ] + diffopts,
3086 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3086 _('[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'))
3087 def export(ui, repo, *changesets, **opts):
3087 def export(ui, repo, *changesets, **opts):
3088 """dump the header and diffs for one or more changesets
3088 """dump the header and diffs for one or more changesets
3089
3089
3090 Print the changeset header and diffs for one or more revisions.
3090 Print the changeset header and diffs for one or more revisions.
3091 If no revision is given, the parent of the working directory is used.
3091 If no revision is given, the parent of the working directory is used.
3092
3092
3093 The information shown in the changeset header is: author, date,
3093 The information shown in the changeset header is: author, date,
3094 branch name (if non-default), changeset hash, parent(s) and commit
3094 branch name (if non-default), changeset hash, parent(s) and commit
3095 comment.
3095 comment.
3096
3096
3097 .. note::
3097 .. note::
3098
3098
3099 export may generate unexpected diff output for merge
3099 export may generate unexpected diff output for merge
3100 changesets, as it will compare the merge changeset against its
3100 changesets, as it will compare the merge changeset against its
3101 first parent only.
3101 first parent only.
3102
3102
3103 Output may be to a file, in which case the name of the file is
3103 Output may be to a file, in which case the name of the file is
3104 given using a format string. The formatting rules are as follows:
3104 given using a format string. The formatting rules are as follows:
3105
3105
3106 :``%%``: literal "%" character
3106 :``%%``: literal "%" character
3107 :``%H``: changeset hash (40 hexadecimal digits)
3107 :``%H``: changeset hash (40 hexadecimal digits)
3108 :``%N``: number of patches being generated
3108 :``%N``: number of patches being generated
3109 :``%R``: changeset revision number
3109 :``%R``: changeset revision number
3110 :``%b``: basename of the exporting repository
3110 :``%b``: basename of the exporting repository
3111 :``%h``: short-form changeset hash (12 hexadecimal digits)
3111 :``%h``: short-form changeset hash (12 hexadecimal digits)
3112 :``%m``: first line of the commit message (only alphanumeric characters)
3112 :``%m``: first line of the commit message (only alphanumeric characters)
3113 :``%n``: zero-padded sequence number, starting at 1
3113 :``%n``: zero-padded sequence number, starting at 1
3114 :``%r``: zero-padded changeset revision number
3114 :``%r``: zero-padded changeset revision number
3115
3115
3116 Without the -a/--text option, export will avoid generating diffs
3116 Without the -a/--text option, export will avoid generating diffs
3117 of files it detects as binary. With -a, export will generate a
3117 of files it detects as binary. With -a, export will generate a
3118 diff anyway, probably with undesirable results.
3118 diff anyway, probably with undesirable results.
3119
3119
3120 Use the -g/--git option to generate diffs in the git extended diff
3120 Use the -g/--git option to generate diffs in the git extended diff
3121 format. See :hg:`help diffs` for more information.
3121 format. See :hg:`help diffs` for more information.
3122
3122
3123 With the --switch-parent option, the diff will be against the
3123 With the --switch-parent option, the diff will be against the
3124 second parent. It can be useful to review a merge.
3124 second parent. It can be useful to review a merge.
3125
3125
3126 .. container:: verbose
3126 .. container:: verbose
3127
3127
3128 Examples:
3128 Examples:
3129
3129
3130 - use export and import to transplant a bugfix to the current
3130 - use export and import to transplant a bugfix to the current
3131 branch::
3131 branch::
3132
3132
3133 hg export -r 9353 | hg import -
3133 hg export -r 9353 | hg import -
3134
3134
3135 - export all the changesets between two revisions to a file with
3135 - export all the changesets between two revisions to a file with
3136 rename information::
3136 rename information::
3137
3137
3138 hg export --git -r 123:150 > changes.txt
3138 hg export --git -r 123:150 > changes.txt
3139
3139
3140 - split outgoing changes into a series of patches with
3140 - split outgoing changes into a series of patches with
3141 descriptive names::
3141 descriptive names::
3142
3142
3143 hg export -r "outgoing()" -o "%n-%m.patch"
3143 hg export -r "outgoing()" -o "%n-%m.patch"
3144
3144
3145 Returns 0 on success.
3145 Returns 0 on success.
3146 """
3146 """
3147 changesets += tuple(opts.get('rev', []))
3147 changesets += tuple(opts.get('rev', []))
3148 if not changesets:
3148 if not changesets:
3149 changesets = ['.']
3149 changesets = ['.']
3150 revs = scmutil.revrange(repo, changesets)
3150 revs = scmutil.revrange(repo, changesets)
3151 if not revs:
3151 if not revs:
3152 raise util.Abort(_("export requires at least one changeset"))
3152 raise util.Abort(_("export requires at least one changeset"))
3153 if len(revs) > 1:
3153 if len(revs) > 1:
3154 ui.note(_('exporting patches:\n'))
3154 ui.note(_('exporting patches:\n'))
3155 else:
3155 else:
3156 ui.note(_('exporting patch:\n'))
3156 ui.note(_('exporting patch:\n'))
3157 cmdutil.export(repo, revs, template=opts.get('output'),
3157 cmdutil.export(repo, revs, template=opts.get('output'),
3158 switch_parent=opts.get('switch_parent'),
3158 switch_parent=opts.get('switch_parent'),
3159 opts=patch.diffopts(ui, opts))
3159 opts=patch.diffopts(ui, opts))
3160
3160
3161 @command('files',
3161 @command('files',
3162 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3162 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
3163 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3163 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
3164 ] + walkopts + formatteropts,
3164 ] + walkopts + formatteropts,
3165 _('[OPTION]... [PATTERN]...'))
3165 _('[OPTION]... [PATTERN]...'))
3166 def files(ui, repo, *pats, **opts):
3166 def files(ui, repo, *pats, **opts):
3167 """list tracked files
3167 """list tracked files
3168
3168
3169 Print files under Mercurial control in the working directory or
3169 Print files under Mercurial control in the working directory or
3170 specified revision whose names match the given patterns (excluding
3170 specified revision whose names match the given patterns (excluding
3171 removed files).
3171 removed files).
3172
3172
3173 If no patterns are given to match, this command prints the names
3173 If no patterns are given to match, this command prints the names
3174 of all files under Mercurial control in the working copy.
3174 of all files under Mercurial control in the working copy.
3175
3175
3176 .. container:: verbose
3176 .. container:: verbose
3177
3177
3178 Examples:
3178 Examples:
3179
3179
3180 - list all files under the current directory::
3180 - list all files under the current directory::
3181
3181
3182 hg files .
3182 hg files .
3183
3183
3184 - shows sizes and flags for current revision::
3184 - shows sizes and flags for current revision::
3185
3185
3186 hg files -vr .
3186 hg files -vr .
3187
3187
3188 - list all files named README::
3188 - list all files named README::
3189
3189
3190 hg files -I "**/README"
3190 hg files -I "**/README"
3191
3191
3192 - list all binary files::
3192 - list all binary files::
3193
3193
3194 hg files "set:binary()"
3194 hg files "set:binary()"
3195
3195
3196 - find files containing a regular expression::
3196 - find files containing a regular expression::
3197
3197
3198 hg files "set:grep('bob')"
3198 hg files "set:grep('bob')"
3199
3199
3200 - search tracked file contents with xargs and grep::
3200 - search tracked file contents with xargs and grep::
3201
3201
3202 hg files -0 | xargs -0 grep foo
3202 hg files -0 | xargs -0 grep foo
3203
3203
3204 See :hg:`help pattern` and :hg:`help filesets` for more information
3204 See :hg:`help pattern` and :hg:`help filesets` for more information
3205 on specifying file patterns.
3205 on specifying file patterns.
3206
3206
3207 Returns 0 if a match is found, 1 otherwise.
3207 Returns 0 if a match is found, 1 otherwise.
3208
3208
3209 """
3209 """
3210 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3210 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
3211 rev = ctx.rev()
3211 rev = ctx.rev()
3212 ret = 1
3212 ret = 1
3213
3213
3214 end = '\n'
3214 end = '\n'
3215 if opts.get('print0'):
3215 if opts.get('print0'):
3216 end = '\0'
3216 end = '\0'
3217 fm = ui.formatter('files', opts)
3217 fm = ui.formatter('files', opts)
3218 fmt = '%s' + end
3218 fmt = '%s' + end
3219
3219
3220 m = scmutil.match(ctx, pats, opts)
3220 m = scmutil.match(ctx, pats, opts)
3221 ds = repo.dirstate
3221 ds = repo.dirstate
3222 for f in ctx.matches(m):
3222 for f in ctx.matches(m):
3223 if rev is None and ds[f] == 'r':
3223 if rev is None and ds[f] == 'r':
3224 continue
3224 continue
3225 fm.startitem()
3225 fm.startitem()
3226 if ui.verbose:
3226 if ui.verbose:
3227 fc = ctx[f]
3227 fc = ctx[f]
3228 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
3228 fm.write('size flags', '% 10d % 1s ', fc.size(), fc.flags())
3229 fm.data(abspath=f)
3229 fm.data(abspath=f)
3230 fm.write('path', fmt, m.rel(f))
3230 fm.write('path', fmt, m.rel(f))
3231 ret = 0
3231 ret = 0
3232
3232
3233 fm.end()
3233 fm.end()
3234
3234
3235 return ret
3235 return ret
3236
3236
3237 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3237 @command('^forget', walkopts, _('[OPTION]... FILE...'), inferrepo=True)
3238 def forget(ui, repo, *pats, **opts):
3238 def forget(ui, repo, *pats, **opts):
3239 """forget the specified files on the next commit
3239 """forget the specified files on the next commit
3240
3240
3241 Mark the specified files so they will no longer be tracked
3241 Mark the specified files so they will no longer be tracked
3242 after the next commit.
3242 after the next commit.
3243
3243
3244 This only removes files from the current branch, not from the
3244 This only removes files from the current branch, not from the
3245 entire project history, and it does not delete them from the
3245 entire project history, and it does not delete them from the
3246 working directory.
3246 working directory.
3247
3247
3248 To undo a forget before the next commit, see :hg:`add`.
3248 To undo a forget before the next commit, see :hg:`add`.
3249
3249
3250 .. container:: verbose
3250 .. container:: verbose
3251
3251
3252 Examples:
3252 Examples:
3253
3253
3254 - forget newly-added binary files::
3254 - forget newly-added binary files::
3255
3255
3256 hg forget "set:added() and binary()"
3256 hg forget "set:added() and binary()"
3257
3257
3258 - forget files that would be excluded by .hgignore::
3258 - forget files that would be excluded by .hgignore::
3259
3259
3260 hg forget "set:hgignore()"
3260 hg forget "set:hgignore()"
3261
3261
3262 Returns 0 on success.
3262 Returns 0 on success.
3263 """
3263 """
3264
3264
3265 if not pats:
3265 if not pats:
3266 raise util.Abort(_('no files specified'))
3266 raise util.Abort(_('no files specified'))
3267
3267
3268 m = scmutil.match(repo[None], pats, opts)
3268 m = scmutil.match(repo[None], pats, opts)
3269 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3269 rejected = cmdutil.forget(ui, repo, m, prefix="", explicitonly=False)[0]
3270 return rejected and 1 or 0
3270 return rejected and 1 or 0
3271
3271
3272 @command(
3272 @command(
3273 'graft',
3273 'graft',
3274 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3274 [('r', 'rev', [], _('revisions to graft'), _('REV')),
3275 ('c', 'continue', False, _('resume interrupted graft')),
3275 ('c', 'continue', False, _('resume interrupted graft')),
3276 ('e', 'edit', False, _('invoke editor on commit messages')),
3276 ('e', 'edit', False, _('invoke editor on commit messages')),
3277 ('', 'log', None, _('append graft info to log message')),
3277 ('', 'log', None, _('append graft info to log message')),
3278 ('f', 'force', False, _('force graft')),
3278 ('f', 'force', False, _('force graft')),
3279 ('D', 'currentdate', False,
3279 ('D', 'currentdate', False,
3280 _('record the current date as commit date')),
3280 _('record the current date as commit date')),
3281 ('U', 'currentuser', False,
3281 ('U', 'currentuser', False,
3282 _('record the current user as committer'), _('DATE'))]
3282 _('record the current user as committer'), _('DATE'))]
3283 + commitopts2 + mergetoolopts + dryrunopts,
3283 + commitopts2 + mergetoolopts + dryrunopts,
3284 _('[OPTION]... [-r] REV...'))
3284 _('[OPTION]... [-r] REV...'))
3285 def graft(ui, repo, *revs, **opts):
3285 def graft(ui, repo, *revs, **opts):
3286 '''copy changes from other branches onto the current branch
3286 '''copy changes from other branches onto the current branch
3287
3287
3288 This command uses Mercurial's merge logic to copy individual
3288 This command uses Mercurial's merge logic to copy individual
3289 changes from other branches without merging branches in the
3289 changes from other branches without merging branches in the
3290 history graph. This is sometimes known as 'backporting' or
3290 history graph. This is sometimes known as 'backporting' or
3291 'cherry-picking'. By default, graft will copy user, date, and
3291 'cherry-picking'. By default, graft will copy user, date, and
3292 description from the source changesets.
3292 description from the source changesets.
3293
3293
3294 Changesets that are ancestors of the current revision, that have
3294 Changesets that are ancestors of the current revision, that have
3295 already been grafted, or that are merges will be skipped.
3295 already been grafted, or that are merges will be skipped.
3296
3296
3297 If --log is specified, log messages will have a comment appended
3297 If --log is specified, log messages will have a comment appended
3298 of the form::
3298 of the form::
3299
3299
3300 (grafted from CHANGESETHASH)
3300 (grafted from CHANGESETHASH)
3301
3301
3302 If --force is specified, revisions will be grafted even if they
3302 If --force is specified, revisions will be grafted even if they
3303 are already ancestors of or have been grafted to the destination.
3303 are already ancestors of or have been grafted to the destination.
3304 This is useful when the revisions have since been backed out.
3304 This is useful when the revisions have since been backed out.
3305
3305
3306 If a graft merge results in conflicts, the graft process is
3306 If a graft merge results in conflicts, the graft process is
3307 interrupted so that the current merge can be manually resolved.
3307 interrupted so that the current merge can be manually resolved.
3308 Once all conflicts are addressed, the graft process can be
3308 Once all conflicts are addressed, the graft process can be
3309 continued with the -c/--continue option.
3309 continued with the -c/--continue option.
3310
3310
3311 .. note::
3311 .. note::
3312
3312
3313 The -c/--continue option does not reapply earlier options, except
3313 The -c/--continue option does not reapply earlier options, except
3314 for --force.
3314 for --force.
3315
3315
3316 .. container:: verbose
3316 .. container:: verbose
3317
3317
3318 Examples:
3318 Examples:
3319
3319
3320 - copy a single change to the stable branch and edit its description::
3320 - copy a single change to the stable branch and edit its description::
3321
3321
3322 hg update stable
3322 hg update stable
3323 hg graft --edit 9393
3323 hg graft --edit 9393
3324
3324
3325 - graft a range of changesets with one exception, updating dates::
3325 - graft a range of changesets with one exception, updating dates::
3326
3326
3327 hg graft -D "2085::2093 and not 2091"
3327 hg graft -D "2085::2093 and not 2091"
3328
3328
3329 - continue a graft after resolving conflicts::
3329 - continue a graft after resolving conflicts::
3330
3330
3331 hg graft -c
3331 hg graft -c
3332
3332
3333 - show the source of a grafted changeset::
3333 - show the source of a grafted changeset::
3334
3334
3335 hg log --debug -r .
3335 hg log --debug -r .
3336
3336
3337 See :hg:`help revisions` and :hg:`help revsets` for more about
3337 See :hg:`help revisions` and :hg:`help revsets` for more about
3338 specifying revisions.
3338 specifying revisions.
3339
3339
3340 Returns 0 on successful completion.
3340 Returns 0 on successful completion.
3341 '''
3341 '''
3342
3342
3343 revs = list(revs)
3343 revs = list(revs)
3344 revs.extend(opts['rev'])
3344 revs.extend(opts['rev'])
3345
3345
3346 if not opts.get('user') and opts.get('currentuser'):
3346 if not opts.get('user') and opts.get('currentuser'):
3347 opts['user'] = ui.username()
3347 opts['user'] = ui.username()
3348 if not opts.get('date') and opts.get('currentdate'):
3348 if not opts.get('date') and opts.get('currentdate'):
3349 opts['date'] = "%d %d" % util.makedate()
3349 opts['date'] = "%d %d" % util.makedate()
3350
3350
3351 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3351 editor = cmdutil.getcommiteditor(editform='graft', **opts)
3352
3352
3353 cont = False
3353 cont = False
3354 if opts['continue']:
3354 if opts['continue']:
3355 cont = True
3355 cont = True
3356 if revs:
3356 if revs:
3357 raise util.Abort(_("can't specify --continue and revisions"))
3357 raise util.Abort(_("can't specify --continue and revisions"))
3358 # read in unfinished revisions
3358 # read in unfinished revisions
3359 try:
3359 try:
3360 nodes = repo.opener.read('graftstate').splitlines()
3360 nodes = repo.opener.read('graftstate').splitlines()
3361 revs = [repo[node].rev() for node in nodes]
3361 revs = [repo[node].rev() for node in nodes]
3362 except IOError, inst:
3362 except IOError, inst:
3363 if inst.errno != errno.ENOENT:
3363 if inst.errno != errno.ENOENT:
3364 raise
3364 raise
3365 raise util.Abort(_("no graft state found, can't continue"))
3365 raise util.Abort(_("no graft state found, can't continue"))
3366 else:
3366 else:
3367 cmdutil.checkunfinished(repo)
3367 cmdutil.checkunfinished(repo)
3368 cmdutil.bailifchanged(repo)
3368 cmdutil.bailifchanged(repo)
3369 if not revs:
3369 if not revs:
3370 raise util.Abort(_('no revisions specified'))
3370 raise util.Abort(_('no revisions specified'))
3371 revs = scmutil.revrange(repo, revs)
3371 revs = scmutil.revrange(repo, revs)
3372
3372
3373 skipped = set()
3373 skipped = set()
3374 # check for merges
3374 # check for merges
3375 for rev in repo.revs('%ld and merge()', revs):
3375 for rev in repo.revs('%ld and merge()', revs):
3376 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3376 ui.warn(_('skipping ungraftable merge revision %s\n') % rev)
3377 skipped.add(rev)
3377 skipped.add(rev)
3378 revs = [r for r in revs if r not in skipped]
3378 revs = [r for r in revs if r not in skipped]
3379 if not revs:
3379 if not revs:
3380 return -1
3380 return -1
3381
3381
3382 # Don't check in the --continue case, in effect retaining --force across
3382 # Don't check in the --continue case, in effect retaining --force across
3383 # --continues. That's because without --force, any revisions we decided to
3383 # --continues. That's because without --force, any revisions we decided to
3384 # skip would have been filtered out here, so they wouldn't have made their
3384 # skip would have been filtered out here, so they wouldn't have made their
3385 # way to the graftstate. With --force, any revisions we would have otherwise
3385 # way to the graftstate. With --force, any revisions we would have otherwise
3386 # skipped would not have been filtered out, and if they hadn't been applied
3386 # skipped would not have been filtered out, and if they hadn't been applied
3387 # already, they'd have been in the graftstate.
3387 # already, they'd have been in the graftstate.
3388 if not (cont or opts.get('force')):
3388 if not (cont or opts.get('force')):
3389 # check for ancestors of dest branch
3389 # check for ancestors of dest branch
3390 crev = repo['.'].rev()
3390 crev = repo['.'].rev()
3391 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3391 ancestors = repo.changelog.ancestors([crev], inclusive=True)
3392 # Cannot use x.remove(y) on smart set, this has to be a list.
3392 # Cannot use x.remove(y) on smart set, this has to be a list.
3393 # XXX make this lazy in the future
3393 # XXX make this lazy in the future
3394 revs = list(revs)
3394 revs = list(revs)
3395 # don't mutate while iterating, create a copy
3395 # don't mutate while iterating, create a copy
3396 for rev in list(revs):
3396 for rev in list(revs):
3397 if rev in ancestors:
3397 if rev in ancestors:
3398 ui.warn(_('skipping ancestor revision %s\n') % rev)
3398 ui.warn(_('skipping ancestor revision %s\n') % rev)
3399 # XXX remove on list is slow
3399 # XXX remove on list is slow
3400 revs.remove(rev)
3400 revs.remove(rev)
3401 if not revs:
3401 if not revs:
3402 return -1
3402 return -1
3403
3403
3404 # analyze revs for earlier grafts
3404 # analyze revs for earlier grafts
3405 ids = {}
3405 ids = {}
3406 for ctx in repo.set("%ld", revs):
3406 for ctx in repo.set("%ld", revs):
3407 ids[ctx.hex()] = ctx.rev()
3407 ids[ctx.hex()] = ctx.rev()
3408 n = ctx.extra().get('source')
3408 n = ctx.extra().get('source')
3409 if n:
3409 if n:
3410 ids[n] = ctx.rev()
3410 ids[n] = ctx.rev()
3411
3411
3412 # check ancestors for earlier grafts
3412 # check ancestors for earlier grafts
3413 ui.debug('scanning for duplicate grafts\n')
3413 ui.debug('scanning for duplicate grafts\n')
3414
3414
3415 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3415 for rev in repo.changelog.findmissingrevs(revs, [crev]):
3416 ctx = repo[rev]
3416 ctx = repo[rev]
3417 n = ctx.extra().get('source')
3417 n = ctx.extra().get('source')
3418 if n in ids:
3418 if n in ids:
3419 try:
3419 try:
3420 r = repo[n].rev()
3420 r = repo[n].rev()
3421 except error.RepoLookupError:
3421 except error.RepoLookupError:
3422 r = None
3422 r = None
3423 if r in revs:
3423 if r in revs:
3424 ui.warn(_('skipping revision %s (already grafted to %s)\n')
3424 ui.warn(_('skipping revision %s (already grafted to %s)\n')
3425 % (r, rev))
3425 % (r, rev))
3426 revs.remove(r)
3426 revs.remove(r)
3427 elif ids[n] in revs:
3427 elif ids[n] in revs:
3428 if r is None:
3428 if r is None:
3429 ui.warn(_('skipping already grafted revision %s '
3429 ui.warn(_('skipping already grafted revision %s '
3430 '(%s also has unknown origin %s)\n')
3430 '(%s also has unknown origin %s)\n')
3431 % (ids[n], rev, n))
3431 % (ids[n], rev, n))
3432 else:
3432 else:
3433 ui.warn(_('skipping already grafted revision %s '
3433 ui.warn(_('skipping already grafted revision %s '
3434 '(%s also has origin %d)\n')
3434 '(%s also has origin %d)\n')
3435 % (ids[n], rev, r))
3435 % (ids[n], rev, r))
3436 revs.remove(ids[n])
3436 revs.remove(ids[n])
3437 elif ctx.hex() in ids:
3437 elif ctx.hex() in ids:
3438 r = ids[ctx.hex()]
3438 r = ids[ctx.hex()]
3439 ui.warn(_('skipping already grafted revision %s '
3439 ui.warn(_('skipping already grafted revision %s '
3440 '(was grafted from %d)\n') % (r, rev))
3440 '(was grafted from %d)\n') % (r, rev))
3441 revs.remove(r)
3441 revs.remove(r)
3442 if not revs:
3442 if not revs:
3443 return -1
3443 return -1
3444
3444
3445 wlock = repo.wlock()
3445 wlock = repo.wlock()
3446 try:
3446 try:
3447 for pos, ctx in enumerate(repo.set("%ld", revs)):
3447 for pos, ctx in enumerate(repo.set("%ld", revs)):
3448
3448
3449 ui.status(_('grafting revision %s\n') % ctx.rev())
3449 ui.status(_('grafting revision %s\n') % ctx.rev())
3450 if opts.get('dry_run'):
3450 if opts.get('dry_run'):
3451 continue
3451 continue
3452
3452
3453 source = ctx.extra().get('source')
3453 source = ctx.extra().get('source')
3454 if not source:
3454 if not source:
3455 source = ctx.hex()
3455 source = ctx.hex()
3456 extra = {'source': source}
3456 extra = {'source': source}
3457 user = ctx.user()
3457 user = ctx.user()
3458 if opts.get('user'):
3458 if opts.get('user'):
3459 user = opts['user']
3459 user = opts['user']
3460 date = ctx.date()
3460 date = ctx.date()
3461 if opts.get('date'):
3461 if opts.get('date'):
3462 date = opts['date']
3462 date = opts['date']
3463 message = ctx.description()
3463 message = ctx.description()
3464 if opts.get('log'):
3464 if opts.get('log'):
3465 message += '\n(grafted from %s)' % ctx.hex()
3465 message += '\n(grafted from %s)' % ctx.hex()
3466
3466
3467 # we don't merge the first commit when continuing
3467 # we don't merge the first commit when continuing
3468 if not cont:
3468 if not cont:
3469 # perform the graft merge with p1(rev) as 'ancestor'
3469 # perform the graft merge with p1(rev) as 'ancestor'
3470 try:
3470 try:
3471 # ui.forcemerge is an internal variable, do not document
3471 # ui.forcemerge is an internal variable, do not document
3472 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3472 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
3473 'graft')
3473 'graft')
3474 stats = mergemod.graft(repo, ctx, ctx.p1(),
3474 stats = mergemod.graft(repo, ctx, ctx.p1(),
3475 ['local', 'graft'])
3475 ['local', 'graft'])
3476 finally:
3476 finally:
3477 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3477 repo.ui.setconfig('ui', 'forcemerge', '', 'graft')
3478 # report any conflicts
3478 # report any conflicts
3479 if stats and stats[3] > 0:
3479 if stats and stats[3] > 0:
3480 # write out state for --continue
3480 # write out state for --continue
3481 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3481 nodelines = [repo[rev].hex() + "\n" for rev in revs[pos:]]
3482 repo.opener.write('graftstate', ''.join(nodelines))
3482 repo.opener.write('graftstate', ''.join(nodelines))
3483 raise util.Abort(
3483 raise util.Abort(
3484 _("unresolved conflicts, can't continue"),
3484 _("unresolved conflicts, can't continue"),
3485 hint=_('use hg resolve and hg graft --continue'))
3485 hint=_('use hg resolve and hg graft --continue'))
3486 else:
3486 else:
3487 cont = False
3487 cont = False
3488
3488
3489 # commit
3489 # commit
3490 node = repo.commit(text=message, user=user,
3490 node = repo.commit(text=message, user=user,
3491 date=date, extra=extra, editor=editor)
3491 date=date, extra=extra, editor=editor)
3492 if node is None:
3492 if node is None:
3493 ui.status(_('graft for revision %s is empty\n') % ctx.rev())
3493 ui.status(_('graft for revision %s is empty\n') % ctx.rev())
3494 finally:
3494 finally:
3495 wlock.release()
3495 wlock.release()
3496
3496
3497 # remove state when we complete successfully
3497 # remove state when we complete successfully
3498 if not opts.get('dry_run'):
3498 if not opts.get('dry_run'):
3499 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3499 util.unlinkpath(repo.join('graftstate'), ignoremissing=True)
3500
3500
3501 return 0
3501 return 0
3502
3502
3503 @command('grep',
3503 @command('grep',
3504 [('0', 'print0', None, _('end fields with NUL')),
3504 [('0', 'print0', None, _('end fields with NUL')),
3505 ('', 'all', None, _('print all revisions that match')),
3505 ('', 'all', None, _('print all revisions that match')),
3506 ('a', 'text', None, _('treat all files as text')),
3506 ('a', 'text', None, _('treat all files as text')),
3507 ('f', 'follow', None,
3507 ('f', 'follow', None,
3508 _('follow changeset history,'
3508 _('follow changeset history,'
3509 ' or file history across copies and renames')),
3509 ' or file history across copies and renames')),
3510 ('i', 'ignore-case', None, _('ignore case when matching')),
3510 ('i', 'ignore-case', None, _('ignore case when matching')),
3511 ('l', 'files-with-matches', None,
3511 ('l', 'files-with-matches', None,
3512 _('print only filenames and revisions that match')),
3512 _('print only filenames and revisions that match')),
3513 ('n', 'line-number', None, _('print matching line numbers')),
3513 ('n', 'line-number', None, _('print matching line numbers')),
3514 ('r', 'rev', [],
3514 ('r', 'rev', [],
3515 _('only search files changed within revision range'), _('REV')),
3515 _('only search files changed within revision range'), _('REV')),
3516 ('u', 'user', None, _('list the author (long with -v)')),
3516 ('u', 'user', None, _('list the author (long with -v)')),
3517 ('d', 'date', None, _('list the date (short with -q)')),
3517 ('d', 'date', None, _('list the date (short with -q)')),
3518 ] + walkopts,
3518 ] + walkopts,
3519 _('[OPTION]... PATTERN [FILE]...'),
3519 _('[OPTION]... PATTERN [FILE]...'),
3520 inferrepo=True)
3520 inferrepo=True)
3521 def grep(ui, repo, pattern, *pats, **opts):
3521 def grep(ui, repo, pattern, *pats, **opts):
3522 """search for a pattern in specified files and revisions
3522 """search for a pattern in specified files and revisions
3523
3523
3524 Search revisions of files for a regular expression.
3524 Search revisions of files for a regular expression.
3525
3525
3526 This command behaves differently than Unix grep. It only accepts
3526 This command behaves differently than Unix grep. It only accepts
3527 Python/Perl regexps. It searches repository history, not the
3527 Python/Perl regexps. It searches repository history, not the
3528 working directory. It always prints the revision number in which a
3528 working directory. It always prints the revision number in which a
3529 match appears.
3529 match appears.
3530
3530
3531 By default, grep only prints output for the first revision of a
3531 By default, grep only prints output for the first revision of a
3532 file in which it finds a match. To get it to print every revision
3532 file in which it finds a match. To get it to print every revision
3533 that contains a change in match status ("-" for a match that
3533 that contains a change in match status ("-" for a match that
3534 becomes a non-match, or "+" for a non-match that becomes a match),
3534 becomes a non-match, or "+" for a non-match that becomes a match),
3535 use the --all flag.
3535 use the --all flag.
3536
3536
3537 Returns 0 if a match is found, 1 otherwise.
3537 Returns 0 if a match is found, 1 otherwise.
3538 """
3538 """
3539 reflags = re.M
3539 reflags = re.M
3540 if opts.get('ignore_case'):
3540 if opts.get('ignore_case'):
3541 reflags |= re.I
3541 reflags |= re.I
3542 try:
3542 try:
3543 regexp = util.re.compile(pattern, reflags)
3543 regexp = util.re.compile(pattern, reflags)
3544 except re.error, inst:
3544 except re.error, inst:
3545 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3545 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
3546 return 1
3546 return 1
3547 sep, eol = ':', '\n'
3547 sep, eol = ':', '\n'
3548 if opts.get('print0'):
3548 if opts.get('print0'):
3549 sep = eol = '\0'
3549 sep = eol = '\0'
3550
3550
3551 getfile = util.lrucachefunc(repo.file)
3551 getfile = util.lrucachefunc(repo.file)
3552
3552
3553 def matchlines(body):
3553 def matchlines(body):
3554 begin = 0
3554 begin = 0
3555 linenum = 0
3555 linenum = 0
3556 while begin < len(body):
3556 while begin < len(body):
3557 match = regexp.search(body, begin)
3557 match = regexp.search(body, begin)
3558 if not match:
3558 if not match:
3559 break
3559 break
3560 mstart, mend = match.span()
3560 mstart, mend = match.span()
3561 linenum += body.count('\n', begin, mstart) + 1
3561 linenum += body.count('\n', begin, mstart) + 1
3562 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3562 lstart = body.rfind('\n', begin, mstart) + 1 or begin
3563 begin = body.find('\n', mend) + 1 or len(body) + 1
3563 begin = body.find('\n', mend) + 1 or len(body) + 1
3564 lend = begin - 1
3564 lend = begin - 1
3565 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3565 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
3566
3566
3567 class linestate(object):
3567 class linestate(object):
3568 def __init__(self, line, linenum, colstart, colend):
3568 def __init__(self, line, linenum, colstart, colend):
3569 self.line = line
3569 self.line = line
3570 self.linenum = linenum
3570 self.linenum = linenum
3571 self.colstart = colstart
3571 self.colstart = colstart
3572 self.colend = colend
3572 self.colend = colend
3573
3573
3574 def __hash__(self):
3574 def __hash__(self):
3575 return hash((self.linenum, self.line))
3575 return hash((self.linenum, self.line))
3576
3576
3577 def __eq__(self, other):
3577 def __eq__(self, other):
3578 return self.line == other.line
3578 return self.line == other.line
3579
3579
3580 def __iter__(self):
3580 def __iter__(self):
3581 yield (self.line[:self.colstart], '')
3581 yield (self.line[:self.colstart], '')
3582 yield (self.line[self.colstart:self.colend], 'grep.match')
3582 yield (self.line[self.colstart:self.colend], 'grep.match')
3583 rest = self.line[self.colend:]
3583 rest = self.line[self.colend:]
3584 while rest != '':
3584 while rest != '':
3585 match = regexp.search(rest)
3585 match = regexp.search(rest)
3586 if not match:
3586 if not match:
3587 yield (rest, '')
3587 yield (rest, '')
3588 break
3588 break
3589 mstart, mend = match.span()
3589 mstart, mend = match.span()
3590 yield (rest[:mstart], '')
3590 yield (rest[:mstart], '')
3591 yield (rest[mstart:mend], 'grep.match')
3591 yield (rest[mstart:mend], 'grep.match')
3592 rest = rest[mend:]
3592 rest = rest[mend:]
3593
3593
3594 matches = {}
3594 matches = {}
3595 copies = {}
3595 copies = {}
3596 def grepbody(fn, rev, body):
3596 def grepbody(fn, rev, body):
3597 matches[rev].setdefault(fn, [])
3597 matches[rev].setdefault(fn, [])
3598 m = matches[rev][fn]
3598 m = matches[rev][fn]
3599 for lnum, cstart, cend, line in matchlines(body):
3599 for lnum, cstart, cend, line in matchlines(body):
3600 s = linestate(line, lnum, cstart, cend)
3600 s = linestate(line, lnum, cstart, cend)
3601 m.append(s)
3601 m.append(s)
3602
3602
3603 def difflinestates(a, b):
3603 def difflinestates(a, b):
3604 sm = difflib.SequenceMatcher(None, a, b)
3604 sm = difflib.SequenceMatcher(None, a, b)
3605 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3605 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
3606 if tag == 'insert':
3606 if tag == 'insert':
3607 for i in xrange(blo, bhi):
3607 for i in xrange(blo, bhi):
3608 yield ('+', b[i])
3608 yield ('+', b[i])
3609 elif tag == 'delete':
3609 elif tag == 'delete':
3610 for i in xrange(alo, ahi):
3610 for i in xrange(alo, ahi):
3611 yield ('-', a[i])
3611 yield ('-', a[i])
3612 elif tag == 'replace':
3612 elif tag == 'replace':
3613 for i in xrange(alo, ahi):
3613 for i in xrange(alo, ahi):
3614 yield ('-', a[i])
3614 yield ('-', a[i])
3615 for i in xrange(blo, bhi):
3615 for i in xrange(blo, bhi):
3616 yield ('+', b[i])
3616 yield ('+', b[i])
3617
3617
3618 def display(fn, ctx, pstates, states):
3618 def display(fn, ctx, pstates, states):
3619 rev = ctx.rev()
3619 rev = ctx.rev()
3620 datefunc = ui.quiet and util.shortdate or util.datestr
3620 datefunc = ui.quiet and util.shortdate or util.datestr
3621 found = False
3621 found = False
3622 @util.cachefunc
3622 @util.cachefunc
3623 def binary():
3623 def binary():
3624 flog = getfile(fn)
3624 flog = getfile(fn)
3625 return util.binary(flog.read(ctx.filenode(fn)))
3625 return util.binary(flog.read(ctx.filenode(fn)))
3626
3626
3627 if opts.get('all'):
3627 if opts.get('all'):
3628 iter = difflinestates(pstates, states)
3628 iter = difflinestates(pstates, states)
3629 else:
3629 else:
3630 iter = [('', l) for l in states]
3630 iter = [('', l) for l in states]
3631 for change, l in iter:
3631 for change, l in iter:
3632 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3632 cols = [(fn, 'grep.filename'), (str(rev), 'grep.rev')]
3633
3633
3634 if opts.get('line_number'):
3634 if opts.get('line_number'):
3635 cols.append((str(l.linenum), 'grep.linenumber'))
3635 cols.append((str(l.linenum), 'grep.linenumber'))
3636 if opts.get('all'):
3636 if opts.get('all'):
3637 cols.append((change, 'grep.change'))
3637 cols.append((change, 'grep.change'))
3638 if opts.get('user'):
3638 if opts.get('user'):
3639 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3639 cols.append((ui.shortuser(ctx.user()), 'grep.user'))
3640 if opts.get('date'):
3640 if opts.get('date'):
3641 cols.append((datefunc(ctx.date()), 'grep.date'))
3641 cols.append((datefunc(ctx.date()), 'grep.date'))
3642 for col, label in cols[:-1]:
3642 for col, label in cols[:-1]:
3643 ui.write(col, label=label)
3643 ui.write(col, label=label)
3644 ui.write(sep, label='grep.sep')
3644 ui.write(sep, label='grep.sep')
3645 ui.write(cols[-1][0], label=cols[-1][1])
3645 ui.write(cols[-1][0], label=cols[-1][1])
3646 if not opts.get('files_with_matches'):
3646 if not opts.get('files_with_matches'):
3647 ui.write(sep, label='grep.sep')
3647 ui.write(sep, label='grep.sep')
3648 if not opts.get('text') and binary():
3648 if not opts.get('text') and binary():
3649 ui.write(" Binary file matches")
3649 ui.write(" Binary file matches")
3650 else:
3650 else:
3651 for s, label in l:
3651 for s, label in l:
3652 ui.write(s, label=label)
3652 ui.write(s, label=label)
3653 ui.write(eol)
3653 ui.write(eol)
3654 found = True
3654 found = True
3655 if opts.get('files_with_matches'):
3655 if opts.get('files_with_matches'):
3656 break
3656 break
3657 return found
3657 return found
3658
3658
3659 skip = {}
3659 skip = {}
3660 revfiles = {}
3660 revfiles = {}
3661 matchfn = scmutil.match(repo[None], pats, opts)
3661 matchfn = scmutil.match(repo[None], pats, opts)
3662 found = False
3662 found = False
3663 follow = opts.get('follow')
3663 follow = opts.get('follow')
3664
3664
3665 def prep(ctx, fns):
3665 def prep(ctx, fns):
3666 rev = ctx.rev()
3666 rev = ctx.rev()
3667 pctx = ctx.p1()
3667 pctx = ctx.p1()
3668 parent = pctx.rev()
3668 parent = pctx.rev()
3669 matches.setdefault(rev, {})
3669 matches.setdefault(rev, {})
3670 matches.setdefault(parent, {})
3670 matches.setdefault(parent, {})
3671 files = revfiles.setdefault(rev, [])
3671 files = revfiles.setdefault(rev, [])
3672 for fn in fns:
3672 for fn in fns:
3673 flog = getfile(fn)
3673 flog = getfile(fn)
3674 try:
3674 try:
3675 fnode = ctx.filenode(fn)
3675 fnode = ctx.filenode(fn)
3676 except error.LookupError:
3676 except error.LookupError:
3677 continue
3677 continue
3678
3678
3679 copied = flog.renamed(fnode)
3679 copied = flog.renamed(fnode)
3680 copy = follow and copied and copied[0]
3680 copy = follow and copied and copied[0]
3681 if copy:
3681 if copy:
3682 copies.setdefault(rev, {})[fn] = copy
3682 copies.setdefault(rev, {})[fn] = copy
3683 if fn in skip:
3683 if fn in skip:
3684 if copy:
3684 if copy:
3685 skip[copy] = True
3685 skip[copy] = True
3686 continue
3686 continue
3687 files.append(fn)
3687 files.append(fn)
3688
3688
3689 if fn not in matches[rev]:
3689 if fn not in matches[rev]:
3690 grepbody(fn, rev, flog.read(fnode))
3690 grepbody(fn, rev, flog.read(fnode))
3691
3691
3692 pfn = copy or fn
3692 pfn = copy or fn
3693 if pfn not in matches[parent]:
3693 if pfn not in matches[parent]:
3694 try:
3694 try:
3695 fnode = pctx.filenode(pfn)
3695 fnode = pctx.filenode(pfn)
3696 grepbody(pfn, parent, flog.read(fnode))
3696 grepbody(pfn, parent, flog.read(fnode))
3697 except error.LookupError:
3697 except error.LookupError:
3698 pass
3698 pass
3699
3699
3700 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3700 for ctx in cmdutil.walkchangerevs(repo, matchfn, opts, prep):
3701 rev = ctx.rev()
3701 rev = ctx.rev()
3702 parent = ctx.p1().rev()
3702 parent = ctx.p1().rev()
3703 for fn in sorted(revfiles.get(rev, [])):
3703 for fn in sorted(revfiles.get(rev, [])):
3704 states = matches[rev][fn]
3704 states = matches[rev][fn]
3705 copy = copies.get(rev, {}).get(fn)
3705 copy = copies.get(rev, {}).get(fn)
3706 if fn in skip:
3706 if fn in skip:
3707 if copy:
3707 if copy:
3708 skip[copy] = True
3708 skip[copy] = True
3709 continue
3709 continue
3710 pstates = matches.get(parent, {}).get(copy or fn, [])
3710 pstates = matches.get(parent, {}).get(copy or fn, [])
3711 if pstates or states:
3711 if pstates or states:
3712 r = display(fn, ctx, pstates, states)
3712 r = display(fn, ctx, pstates, states)
3713 found = found or r
3713 found = found or r
3714 if r and not opts.get('all'):
3714 if r and not opts.get('all'):
3715 skip[fn] = True
3715 skip[fn] = True
3716 if copy:
3716 if copy:
3717 skip[copy] = True
3717 skip[copy] = True
3718 del matches[rev]
3718 del matches[rev]
3719 del revfiles[rev]
3719 del revfiles[rev]
3720
3720
3721 return not found
3721 return not found
3722
3722
3723 @command('heads',
3723 @command('heads',
3724 [('r', 'rev', '',
3724 [('r', 'rev', '',
3725 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3725 _('show only heads which are descendants of STARTREV'), _('STARTREV')),
3726 ('t', 'topo', False, _('show topological heads only')),
3726 ('t', 'topo', False, _('show topological heads only')),
3727 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3727 ('a', 'active', False, _('show active branchheads only (DEPRECATED)')),
3728 ('c', 'closed', False, _('show normal and closed branch heads')),
3728 ('c', 'closed', False, _('show normal and closed branch heads')),
3729 ] + templateopts,
3729 ] + templateopts,
3730 _('[-ct] [-r STARTREV] [REV]...'))
3730 _('[-ct] [-r STARTREV] [REV]...'))
3731 def heads(ui, repo, *branchrevs, **opts):
3731 def heads(ui, repo, *branchrevs, **opts):
3732 """show branch heads
3732 """show branch heads
3733
3733
3734 With no arguments, show all open branch heads in the repository.
3734 With no arguments, show all open branch heads in the repository.
3735 Branch heads are changesets that have no descendants on the
3735 Branch heads are changesets that have no descendants on the
3736 same branch. They are where development generally takes place and
3736 same branch. They are where development generally takes place and
3737 are the usual targets for update and merge operations.
3737 are the usual targets for update and merge operations.
3738
3738
3739 If one or more REVs are given, only open branch heads on the
3739 If one or more REVs are given, only open branch heads on the
3740 branches associated with the specified changesets are shown. This
3740 branches associated with the specified changesets are shown. This
3741 means that you can use :hg:`heads .` to see the heads on the
3741 means that you can use :hg:`heads .` to see the heads on the
3742 currently checked-out branch.
3742 currently checked-out branch.
3743
3743
3744 If -c/--closed is specified, also show branch heads marked closed
3744 If -c/--closed is specified, also show branch heads marked closed
3745 (see :hg:`commit --close-branch`).
3745 (see :hg:`commit --close-branch`).
3746
3746
3747 If STARTREV is specified, only those heads that are descendants of
3747 If STARTREV is specified, only those heads that are descendants of
3748 STARTREV will be displayed.
3748 STARTREV will be displayed.
3749
3749
3750 If -t/--topo is specified, named branch mechanics will be ignored and only
3750 If -t/--topo is specified, named branch mechanics will be ignored and only
3751 topological heads (changesets with no children) will be shown.
3751 topological heads (changesets with no children) will be shown.
3752
3752
3753 Returns 0 if matching heads are found, 1 if not.
3753 Returns 0 if matching heads are found, 1 if not.
3754 """
3754 """
3755
3755
3756 start = None
3756 start = None
3757 if 'rev' in opts:
3757 if 'rev' in opts:
3758 start = scmutil.revsingle(repo, opts['rev'], None).node()
3758 start = scmutil.revsingle(repo, opts['rev'], None).node()
3759
3759
3760 if opts.get('topo'):
3760 if opts.get('topo'):
3761 heads = [repo[h] for h in repo.heads(start)]
3761 heads = [repo[h] for h in repo.heads(start)]
3762 else:
3762 else:
3763 heads = []
3763 heads = []
3764 for branch in repo.branchmap():
3764 for branch in repo.branchmap():
3765 heads += repo.branchheads(branch, start, opts.get('closed'))
3765 heads += repo.branchheads(branch, start, opts.get('closed'))
3766 heads = [repo[h] for h in heads]
3766 heads = [repo[h] for h in heads]
3767
3767
3768 if branchrevs:
3768 if branchrevs:
3769 branches = set(repo[br].branch() for br in branchrevs)
3769 branches = set(repo[br].branch() for br in branchrevs)
3770 heads = [h for h in heads if h.branch() in branches]
3770 heads = [h for h in heads if h.branch() in branches]
3771
3771
3772 if opts.get('active') and branchrevs:
3772 if opts.get('active') and branchrevs:
3773 dagheads = repo.heads(start)
3773 dagheads = repo.heads(start)
3774 heads = [h for h in heads if h.node() in dagheads]
3774 heads = [h for h in heads if h.node() in dagheads]
3775
3775
3776 if branchrevs:
3776 if branchrevs:
3777 haveheads = set(h.branch() for h in heads)
3777 haveheads = set(h.branch() for h in heads)
3778 if branches - haveheads:
3778 if branches - haveheads:
3779 headless = ', '.join(b for b in branches - haveheads)
3779 headless = ', '.join(b for b in branches - haveheads)
3780 msg = _('no open branch heads found on branches %s')
3780 msg = _('no open branch heads found on branches %s')
3781 if opts.get('rev'):
3781 if opts.get('rev'):
3782 msg += _(' (started at %s)') % opts['rev']
3782 msg += _(' (started at %s)') % opts['rev']
3783 ui.warn((msg + '\n') % headless)
3783 ui.warn((msg + '\n') % headless)
3784
3784
3785 if not heads:
3785 if not heads:
3786 return 1
3786 return 1
3787
3787
3788 heads = sorted(heads, key=lambda x: -x.rev())
3788 heads = sorted(heads, key=lambda x: -x.rev())
3789 displayer = cmdutil.show_changeset(ui, repo, opts)
3789 displayer = cmdutil.show_changeset(ui, repo, opts)
3790 for ctx in heads:
3790 for ctx in heads:
3791 displayer.show(ctx)
3791 displayer.show(ctx)
3792 displayer.close()
3792 displayer.close()
3793
3793
3794 @command('help',
3794 @command('help',
3795 [('e', 'extension', None, _('show only help for extensions')),
3795 [('e', 'extension', None, _('show only help for extensions')),
3796 ('c', 'command', None, _('show only help for commands')),
3796 ('c', 'command', None, _('show only help for commands')),
3797 ('k', 'keyword', '', _('show topics matching keyword')),
3797 ('k', 'keyword', '', _('show topics matching keyword')),
3798 ],
3798 ],
3799 _('[-ec] [TOPIC]'),
3799 _('[-ec] [TOPIC]'),
3800 norepo=True)
3800 norepo=True)
3801 def help_(ui, name=None, **opts):
3801 def help_(ui, name=None, **opts):
3802 """show help for a given topic or a help overview
3802 """show help for a given topic or a help overview
3803
3803
3804 With no arguments, print a list of commands with short help messages.
3804 With no arguments, print a list of commands with short help messages.
3805
3805
3806 Given a topic, extension, or command name, print help for that
3806 Given a topic, extension, or command name, print help for that
3807 topic.
3807 topic.
3808
3808
3809 Returns 0 if successful.
3809 Returns 0 if successful.
3810 """
3810 """
3811
3811
3812 textwidth = min(ui.termwidth(), 80) - 2
3812 textwidth = min(ui.termwidth(), 80) - 2
3813
3813
3814 keep = []
3814 keep = []
3815 if ui.verbose:
3815 if ui.verbose:
3816 keep.append('verbose')
3816 keep.append('verbose')
3817 if sys.platform.startswith('win'):
3817 if sys.platform.startswith('win'):
3818 keep.append('windows')
3818 keep.append('windows')
3819 elif sys.platform == 'OpenVMS':
3819 elif sys.platform == 'OpenVMS':
3820 keep.append('vms')
3820 keep.append('vms')
3821 elif sys.platform == 'plan9':
3821 elif sys.platform == 'plan9':
3822 keep.append('plan9')
3822 keep.append('plan9')
3823 else:
3823 else:
3824 keep.append('unix')
3824 keep.append('unix')
3825 keep.append(sys.platform.lower())
3825 keep.append(sys.platform.lower())
3826
3826
3827 section = None
3827 section = None
3828 if name and '.' in name:
3828 if name and '.' in name:
3829 name, section = name.split('.', 1)
3829 name, section = name.split('.', 1)
3830
3830
3831 text = help.help_(ui, name, **opts)
3831 text = help.help_(ui, name, **opts)
3832
3832
3833 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3833 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3834 section=section)
3834 section=section)
3835 if section and not formatted:
3835 if section and not formatted:
3836 raise util.Abort(_("help section not found"))
3836 raise util.Abort(_("help section not found"))
3837
3837
3838 if 'verbose' in pruned:
3838 if 'verbose' in pruned:
3839 keep.append('omitted')
3839 keep.append('omitted')
3840 else:
3840 else:
3841 keep.append('notomitted')
3841 keep.append('notomitted')
3842 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3842 formatted, pruned = minirst.format(text, textwidth, keep=keep,
3843 section=section)
3843 section=section)
3844 ui.write(formatted)
3844 ui.write(formatted)
3845
3845
3846
3846
3847 @command('identify|id',
3847 @command('identify|id',
3848 [('r', 'rev', '',
3848 [('r', 'rev', '',
3849 _('identify the specified revision'), _('REV')),
3849 _('identify the specified revision'), _('REV')),
3850 ('n', 'num', None, _('show local revision number')),
3850 ('n', 'num', None, _('show local revision number')),
3851 ('i', 'id', None, _('show global revision id')),
3851 ('i', 'id', None, _('show global revision id')),
3852 ('b', 'branch', None, _('show branch')),
3852 ('b', 'branch', None, _('show branch')),
3853 ('t', 'tags', None, _('show tags')),
3853 ('t', 'tags', None, _('show tags')),
3854 ('B', 'bookmarks', None, _('show bookmarks')),
3854 ('B', 'bookmarks', None, _('show bookmarks')),
3855 ] + remoteopts,
3855 ] + remoteopts,
3856 _('[-nibtB] [-r REV] [SOURCE]'),
3856 _('[-nibtB] [-r REV] [SOURCE]'),
3857 optionalrepo=True)
3857 optionalrepo=True)
3858 def identify(ui, repo, source=None, rev=None,
3858 def identify(ui, repo, source=None, rev=None,
3859 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3859 num=None, id=None, branch=None, tags=None, bookmarks=None, **opts):
3860 """identify the working copy or specified revision
3860 """identify the working copy or specified revision
3861
3861
3862 Print a summary identifying the repository state at REV using one or
3862 Print a summary identifying the repository state at REV using one or
3863 two parent hash identifiers, followed by a "+" if the working
3863 two parent hash identifiers, followed by a "+" if the working
3864 directory has uncommitted changes, the branch name (if not default),
3864 directory has uncommitted changes, the branch name (if not default),
3865 a list of tags, and a list of bookmarks.
3865 a list of tags, and a list of bookmarks.
3866
3866
3867 When REV is not given, print a summary of the current state of the
3867 When REV is not given, print a summary of the current state of the
3868 repository.
3868 repository.
3869
3869
3870 Specifying a path to a repository root or Mercurial bundle will
3870 Specifying a path to a repository root or Mercurial bundle will
3871 cause lookup to operate on that repository/bundle.
3871 cause lookup to operate on that repository/bundle.
3872
3872
3873 .. container:: verbose
3873 .. container:: verbose
3874
3874
3875 Examples:
3875 Examples:
3876
3876
3877 - generate a build identifier for the working directory::
3877 - generate a build identifier for the working directory::
3878
3878
3879 hg id --id > build-id.dat
3879 hg id --id > build-id.dat
3880
3880
3881 - find the revision corresponding to a tag::
3881 - find the revision corresponding to a tag::
3882
3882
3883 hg id -n -r 1.3
3883 hg id -n -r 1.3
3884
3884
3885 - check the most recent revision of a remote repository::
3885 - check the most recent revision of a remote repository::
3886
3886
3887 hg id -r tip http://selenic.com/hg/
3887 hg id -r tip http://selenic.com/hg/
3888
3888
3889 Returns 0 if successful.
3889 Returns 0 if successful.
3890 """
3890 """
3891
3891
3892 if not repo and not source:
3892 if not repo and not source:
3893 raise util.Abort(_("there is no Mercurial repository here "
3893 raise util.Abort(_("there is no Mercurial repository here "
3894 "(.hg not found)"))
3894 "(.hg not found)"))
3895
3895
3896 hexfunc = ui.debugflag and hex or short
3896 hexfunc = ui.debugflag and hex or short
3897 default = not (num or id or branch or tags or bookmarks)
3897 default = not (num or id or branch or tags or bookmarks)
3898 output = []
3898 output = []
3899 revs = []
3899 revs = []
3900
3900
3901 if source:
3901 if source:
3902 source, branches = hg.parseurl(ui.expandpath(source))
3902 source, branches = hg.parseurl(ui.expandpath(source))
3903 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3903 peer = hg.peer(repo or ui, opts, source) # only pass ui when no repo
3904 repo = peer.local()
3904 repo = peer.local()
3905 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3905 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3906
3906
3907 if not repo:
3907 if not repo:
3908 if num or branch or tags:
3908 if num or branch or tags:
3909 raise util.Abort(
3909 raise util.Abort(
3910 _("can't query remote revision number, branch, or tags"))
3910 _("can't query remote revision number, branch, or tags"))
3911 if not rev and revs:
3911 if not rev and revs:
3912 rev = revs[0]
3912 rev = revs[0]
3913 if not rev:
3913 if not rev:
3914 rev = "tip"
3914 rev = "tip"
3915
3915
3916 remoterev = peer.lookup(rev)
3916 remoterev = peer.lookup(rev)
3917 if default or id:
3917 if default or id:
3918 output = [hexfunc(remoterev)]
3918 output = [hexfunc(remoterev)]
3919
3919
3920 def getbms():
3920 def getbms():
3921 bms = []
3921 bms = []
3922
3922
3923 if 'bookmarks' in peer.listkeys('namespaces'):
3923 if 'bookmarks' in peer.listkeys('namespaces'):
3924 hexremoterev = hex(remoterev)
3924 hexremoterev = hex(remoterev)
3925 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3925 bms = [bm for bm, bmr in peer.listkeys('bookmarks').iteritems()
3926 if bmr == hexremoterev]
3926 if bmr == hexremoterev]
3927
3927
3928 return sorted(bms)
3928 return sorted(bms)
3929
3929
3930 if bookmarks:
3930 if bookmarks:
3931 output.extend(getbms())
3931 output.extend(getbms())
3932 elif default and not ui.quiet:
3932 elif default and not ui.quiet:
3933 # multiple bookmarks for a single parent separated by '/'
3933 # multiple bookmarks for a single parent separated by '/'
3934 bm = '/'.join(getbms())
3934 bm = '/'.join(getbms())
3935 if bm:
3935 if bm:
3936 output.append(bm)
3936 output.append(bm)
3937 else:
3937 else:
3938 if not rev:
3938 if not rev:
3939 ctx = repo[None]
3939 ctx = repo[None]
3940 parents = ctx.parents()
3940 parents = ctx.parents()
3941 changed = ""
3941 changed = ""
3942 if default or id or num:
3942 if default or id or num:
3943 if (util.any(repo.status())
3943 if (util.any(repo.status())
3944 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
3944 or util.any(ctx.sub(s).dirty() for s in ctx.substate)):
3945 changed = '+'
3945 changed = '+'
3946 if default or id:
3946 if default or id:
3947 output = ["%s%s" %
3947 output = ["%s%s" %
3948 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3948 ('+'.join([hexfunc(p.node()) for p in parents]), changed)]
3949 if num:
3949 if num:
3950 output.append("%s%s" %
3950 output.append("%s%s" %
3951 ('+'.join([str(p.rev()) for p in parents]), changed))
3951 ('+'.join([str(p.rev()) for p in parents]), changed))
3952 else:
3952 else:
3953 ctx = scmutil.revsingle(repo, rev)
3953 ctx = scmutil.revsingle(repo, rev)
3954 if default or id:
3954 if default or id:
3955 output = [hexfunc(ctx.node())]
3955 output = [hexfunc(ctx.node())]
3956 if num:
3956 if num:
3957 output.append(str(ctx.rev()))
3957 output.append(str(ctx.rev()))
3958
3958
3959 if default and not ui.quiet:
3959 if default and not ui.quiet:
3960 b = ctx.branch()
3960 b = ctx.branch()
3961 if b != 'default':
3961 if b != 'default':
3962 output.append("(%s)" % b)
3962 output.append("(%s)" % b)
3963
3963
3964 # multiple tags for a single parent separated by '/'
3964 # multiple tags for a single parent separated by '/'
3965 t = '/'.join(ctx.tags())
3965 t = '/'.join(ctx.tags())
3966 if t:
3966 if t:
3967 output.append(t)
3967 output.append(t)
3968
3968
3969 # multiple bookmarks for a single parent separated by '/'
3969 # multiple bookmarks for a single parent separated by '/'
3970 bm = '/'.join(ctx.bookmarks())
3970 bm = '/'.join(ctx.bookmarks())
3971 if bm:
3971 if bm:
3972 output.append(bm)
3972 output.append(bm)
3973 else:
3973 else:
3974 if branch:
3974 if branch:
3975 output.append(ctx.branch())
3975 output.append(ctx.branch())
3976
3976
3977 if tags:
3977 if tags:
3978 output.extend(ctx.tags())
3978 output.extend(ctx.tags())
3979
3979
3980 if bookmarks:
3980 if bookmarks:
3981 output.extend(ctx.bookmarks())
3981 output.extend(ctx.bookmarks())
3982
3982
3983 ui.write("%s\n" % ' '.join(output))
3983 ui.write("%s\n" % ' '.join(output))
3984
3984
3985 @command('import|patch',
3985 @command('import|patch',
3986 [('p', 'strip', 1,
3986 [('p', 'strip', 1,
3987 _('directory strip option for patch. This has the same '
3987 _('directory strip option for patch. This has the same '
3988 'meaning as the corresponding patch option'), _('NUM')),
3988 'meaning as the corresponding patch option'), _('NUM')),
3989 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3989 ('b', 'base', '', _('base path (DEPRECATED)'), _('PATH')),
3990 ('e', 'edit', False, _('invoke editor on commit messages')),
3990 ('e', 'edit', False, _('invoke editor on commit messages')),
3991 ('f', 'force', None,
3991 ('f', 'force', None,
3992 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3992 _('skip check for outstanding uncommitted changes (DEPRECATED)')),
3993 ('', 'no-commit', None,
3993 ('', 'no-commit', None,
3994 _("don't commit, just update the working directory")),
3994 _("don't commit, just update the working directory")),
3995 ('', 'bypass', None,
3995 ('', 'bypass', None,
3996 _("apply patch without touching the working directory")),
3996 _("apply patch without touching the working directory")),
3997 ('', 'partial', None,
3997 ('', 'partial', None,
3998 _('commit even if some hunks fail')),
3998 _('commit even if some hunks fail')),
3999 ('', 'exact', None,
3999 ('', 'exact', None,
4000 _('apply patch to the nodes from which it was generated')),
4000 _('apply patch to the nodes from which it was generated')),
4001 ('', 'import-branch', None,
4001 ('', 'import-branch', None,
4002 _('use any branch information in patch (implied by --exact)'))] +
4002 _('use any branch information in patch (implied by --exact)'))] +
4003 commitopts + commitopts2 + similarityopts,
4003 commitopts + commitopts2 + similarityopts,
4004 _('[OPTION]... PATCH...'))
4004 _('[OPTION]... PATCH...'))
4005 def import_(ui, repo, patch1=None, *patches, **opts):
4005 def import_(ui, repo, patch1=None, *patches, **opts):
4006 """import an ordered set of patches
4006 """import an ordered set of patches
4007
4007
4008 Import a list of patches and commit them individually (unless
4008 Import a list of patches and commit them individually (unless
4009 --no-commit is specified).
4009 --no-commit is specified).
4010
4010
4011 Because import first applies changes to the working directory,
4011 Because import first applies changes to the working directory,
4012 import will abort if there are outstanding changes.
4012 import will abort if there are outstanding changes.
4013
4013
4014 You can import a patch straight from a mail message. Even patches
4014 You can import a patch straight from a mail message. Even patches
4015 as attachments work (to use the body part, it must have type
4015 as attachments work (to use the body part, it must have type
4016 text/plain or text/x-patch). From and Subject headers of email
4016 text/plain or text/x-patch). From and Subject headers of email
4017 message are used as default committer and commit message. All
4017 message are used as default committer and commit message. All
4018 text/plain body parts before first diff are added to commit
4018 text/plain body parts before first diff are added to commit
4019 message.
4019 message.
4020
4020
4021 If the imported patch was generated by :hg:`export`, user and
4021 If the imported patch was generated by :hg:`export`, user and
4022 description from patch override values from message headers and
4022 description from patch override values from message headers and
4023 body. Values given on command line with -m/--message and -u/--user
4023 body. Values given on command line with -m/--message and -u/--user
4024 override these.
4024 override these.
4025
4025
4026 If --exact is specified, import will set the working directory to
4026 If --exact is specified, import will set the working directory to
4027 the parent of each patch before applying it, and will abort if the
4027 the parent of each patch before applying it, and will abort if the
4028 resulting changeset has a different ID than the one recorded in
4028 resulting changeset has a different ID than the one recorded in
4029 the patch. This may happen due to character set problems or other
4029 the patch. This may happen due to character set problems or other
4030 deficiencies in the text patch format.
4030 deficiencies in the text patch format.
4031
4031
4032 Use --bypass to apply and commit patches directly to the
4032 Use --bypass to apply and commit patches directly to the
4033 repository, not touching the working directory. Without --exact,
4033 repository, not touching the working directory. Without --exact,
4034 patches will be applied on top of the working directory parent
4034 patches will be applied on top of the working directory parent
4035 revision.
4035 revision.
4036
4036
4037 With -s/--similarity, hg will attempt to discover renames and
4037 With -s/--similarity, hg will attempt to discover renames and
4038 copies in the patch in the same way as :hg:`addremove`.
4038 copies in the patch in the same way as :hg:`addremove`.
4039
4039
4040 Use --partial to ensure a changeset will be created from the patch
4040 Use --partial to ensure a changeset will be created from the patch
4041 even if some hunks fail to apply. Hunks that fail to apply will be
4041 even if some hunks fail to apply. Hunks that fail to apply will be
4042 written to a <target-file>.rej file. Conflicts can then be resolved
4042 written to a <target-file>.rej file. Conflicts can then be resolved
4043 by hand before :hg:`commit --amend` is run to update the created
4043 by hand before :hg:`commit --amend` is run to update the created
4044 changeset. This flag exists to let people import patches that
4044 changeset. This flag exists to let people import patches that
4045 partially apply without losing the associated metadata (author,
4045 partially apply without losing the associated metadata (author,
4046 date, description, ...). Note that when none of the hunk applies
4046 date, description, ...). Note that when none of the hunk applies
4047 cleanly, :hg:`import --partial` will create an empty changeset,
4047 cleanly, :hg:`import --partial` will create an empty changeset,
4048 importing only the patch metadata.
4048 importing only the patch metadata.
4049
4049
4050 To read a patch from standard input, use "-" as the patch name. If
4050 To read a patch from standard input, use "-" as the patch name. If
4051 a URL is specified, the patch will be downloaded from it.
4051 a URL is specified, the patch will be downloaded from it.
4052 See :hg:`help dates` for a list of formats valid for -d/--date.
4052 See :hg:`help dates` for a list of formats valid for -d/--date.
4053
4053
4054 .. container:: verbose
4054 .. container:: verbose
4055
4055
4056 Examples:
4056 Examples:
4057
4057
4058 - import a traditional patch from a website and detect renames::
4058 - import a traditional patch from a website and detect renames::
4059
4059
4060 hg import -s 80 http://example.com/bugfix.patch
4060 hg import -s 80 http://example.com/bugfix.patch
4061
4061
4062 - import a changeset from an hgweb server::
4062 - import a changeset from an hgweb server::
4063
4063
4064 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
4064 hg import http://www.selenic.com/hg/rev/5ca8c111e9aa
4065
4065
4066 - import all the patches in an Unix-style mbox::
4066 - import all the patches in an Unix-style mbox::
4067
4067
4068 hg import incoming-patches.mbox
4068 hg import incoming-patches.mbox
4069
4069
4070 - attempt to exactly restore an exported changeset (not always
4070 - attempt to exactly restore an exported changeset (not always
4071 possible)::
4071 possible)::
4072
4072
4073 hg import --exact proposed-fix.patch
4073 hg import --exact proposed-fix.patch
4074
4074
4075 Returns 0 on success, 1 on partial success (see --partial).
4075 Returns 0 on success, 1 on partial success (see --partial).
4076 """
4076 """
4077
4077
4078 if not patch1:
4078 if not patch1:
4079 raise util.Abort(_('need at least one patch to import'))
4079 raise util.Abort(_('need at least one patch to import'))
4080
4080
4081 patches = (patch1,) + patches
4081 patches = (patch1,) + patches
4082
4082
4083 date = opts.get('date')
4083 date = opts.get('date')
4084 if date:
4084 if date:
4085 opts['date'] = util.parsedate(date)
4085 opts['date'] = util.parsedate(date)
4086
4086
4087 update = not opts.get('bypass')
4087 update = not opts.get('bypass')
4088 if not update and opts.get('no_commit'):
4088 if not update and opts.get('no_commit'):
4089 raise util.Abort(_('cannot use --no-commit with --bypass'))
4089 raise util.Abort(_('cannot use --no-commit with --bypass'))
4090 try:
4090 try:
4091 sim = float(opts.get('similarity') or 0)
4091 sim = float(opts.get('similarity') or 0)
4092 except ValueError:
4092 except ValueError:
4093 raise util.Abort(_('similarity must be a number'))
4093 raise util.Abort(_('similarity must be a number'))
4094 if sim < 0 or sim > 100:
4094 if sim < 0 or sim > 100:
4095 raise util.Abort(_('similarity must be between 0 and 100'))
4095 raise util.Abort(_('similarity must be between 0 and 100'))
4096 if sim and not update:
4096 if sim and not update:
4097 raise util.Abort(_('cannot use --similarity with --bypass'))
4097 raise util.Abort(_('cannot use --similarity with --bypass'))
4098 if opts.get('exact') and opts.get('edit'):
4098 if opts.get('exact') and opts.get('edit'):
4099 raise util.Abort(_('cannot use --exact with --edit'))
4099 raise util.Abort(_('cannot use --exact with --edit'))
4100
4100
4101 if update:
4101 if update:
4102 cmdutil.checkunfinished(repo)
4102 cmdutil.checkunfinished(repo)
4103 if (opts.get('exact') or not opts.get('force')) and update:
4103 if (opts.get('exact') or not opts.get('force')) and update:
4104 cmdutil.bailifchanged(repo)
4104 cmdutil.bailifchanged(repo)
4105
4105
4106 base = opts["base"]
4106 base = opts["base"]
4107 wlock = lock = tr = None
4107 wlock = lock = tr = None
4108 msgs = []
4108 msgs = []
4109 ret = 0
4109 ret = 0
4110
4110
4111
4111
4112 try:
4112 try:
4113 try:
4113 try:
4114 wlock = repo.wlock()
4114 wlock = repo.wlock()
4115 repo.dirstate.beginparentchange()
4115 repo.dirstate.beginparentchange()
4116 if not opts.get('no_commit'):
4116 if not opts.get('no_commit'):
4117 lock = repo.lock()
4117 lock = repo.lock()
4118 tr = repo.transaction('import')
4118 tr = repo.transaction('import')
4119 parents = repo.parents()
4119 parents = repo.parents()
4120 for patchurl in patches:
4120 for patchurl in patches:
4121 if patchurl == '-':
4121 if patchurl == '-':
4122 ui.status(_('applying patch from stdin\n'))
4122 ui.status(_('applying patch from stdin\n'))
4123 patchfile = ui.fin
4123 patchfile = ui.fin
4124 patchurl = 'stdin' # for error message
4124 patchurl = 'stdin' # for error message
4125 else:
4125 else:
4126 patchurl = os.path.join(base, patchurl)
4126 patchurl = os.path.join(base, patchurl)
4127 ui.status(_('applying %s\n') % patchurl)
4127 ui.status(_('applying %s\n') % patchurl)
4128 patchfile = hg.openpath(ui, patchurl)
4128 patchfile = hg.openpath(ui, patchurl)
4129
4129
4130 haspatch = False
4130 haspatch = False
4131 for hunk in patch.split(patchfile):
4131 for hunk in patch.split(patchfile):
4132 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4132 (msg, node, rej) = cmdutil.tryimportone(ui, repo, hunk,
4133 parents, opts,
4133 parents, opts,
4134 msgs, hg.clean)
4134 msgs, hg.clean)
4135 if msg:
4135 if msg:
4136 haspatch = True
4136 haspatch = True
4137 ui.note(msg + '\n')
4137 ui.note(msg + '\n')
4138 if update or opts.get('exact'):
4138 if update or opts.get('exact'):
4139 parents = repo.parents()
4139 parents = repo.parents()
4140 else:
4140 else:
4141 parents = [repo[node]]
4141 parents = [repo[node]]
4142 if rej:
4142 if rej:
4143 ui.write_err(_("patch applied partially\n"))
4143 ui.write_err(_("patch applied partially\n"))
4144 ui.write_err(_("(fix the .rej files and run "
4144 ui.write_err(_("(fix the .rej files and run "
4145 "`hg commit --amend`)\n"))
4145 "`hg commit --amend`)\n"))
4146 ret = 1
4146 ret = 1
4147 break
4147 break
4148
4148
4149 if not haspatch:
4149 if not haspatch:
4150 raise util.Abort(_('%s: no diffs found') % patchurl)
4150 raise util.Abort(_('%s: no diffs found') % patchurl)
4151
4151
4152 if tr:
4152 if tr:
4153 tr.close()
4153 tr.close()
4154 if msgs:
4154 if msgs:
4155 repo.savecommitmessage('\n* * *\n'.join(msgs))
4155 repo.savecommitmessage('\n* * *\n'.join(msgs))
4156 repo.dirstate.endparentchange()
4156 repo.dirstate.endparentchange()
4157 return ret
4157 return ret
4158 except: # re-raises
4158 except: # re-raises
4159 # wlock.release() indirectly calls dirstate.write(): since
4159 # wlock.release() indirectly calls dirstate.write(): since
4160 # we're crashing, we do not want to change the working dir
4160 # we're crashing, we do not want to change the working dir
4161 # parent after all, so make sure it writes nothing
4161 # parent after all, so make sure it writes nothing
4162 repo.dirstate.invalidate()
4162 repo.dirstate.invalidate()
4163 raise
4163 raise
4164 finally:
4164 finally:
4165 if tr:
4165 if tr:
4166 tr.release()
4166 tr.release()
4167 release(lock, wlock)
4167 release(lock, wlock)
4168
4168
4169 @command('incoming|in',
4169 @command('incoming|in',
4170 [('f', 'force', None,
4170 [('f', 'force', None,
4171 _('run even if remote repository is unrelated')),
4171 _('run even if remote repository is unrelated')),
4172 ('n', 'newest-first', None, _('show newest record first')),
4172 ('n', 'newest-first', None, _('show newest record first')),
4173 ('', 'bundle', '',
4173 ('', 'bundle', '',
4174 _('file to store the bundles into'), _('FILE')),
4174 _('file to store the bundles into'), _('FILE')),
4175 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4175 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4176 ('B', 'bookmarks', False, _("compare bookmarks")),
4176 ('B', 'bookmarks', False, _("compare bookmarks")),
4177 ('b', 'branch', [],
4177 ('b', 'branch', [],
4178 _('a specific branch you would like to pull'), _('BRANCH')),
4178 _('a specific branch you would like to pull'), _('BRANCH')),
4179 ] + logopts + remoteopts + subrepoopts,
4179 ] + logopts + remoteopts + subrepoopts,
4180 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4180 _('[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'))
4181 def incoming(ui, repo, source="default", **opts):
4181 def incoming(ui, repo, source="default", **opts):
4182 """show new changesets found in source
4182 """show new changesets found in source
4183
4183
4184 Show new changesets found in the specified path/URL or the default
4184 Show new changesets found in the specified path/URL or the default
4185 pull location. These are the changesets that would have been pulled
4185 pull location. These are the changesets that would have been pulled
4186 if a pull at the time you issued this command.
4186 if a pull at the time you issued this command.
4187
4187
4188 For remote repository, using --bundle avoids downloading the
4188 For remote repository, using --bundle avoids downloading the
4189 changesets twice if the incoming is followed by a pull.
4189 changesets twice if the incoming is followed by a pull.
4190
4190
4191 See pull for valid source format details.
4191 See pull for valid source format details.
4192
4192
4193 .. container:: verbose
4193 .. container:: verbose
4194
4194
4195 Examples:
4195 Examples:
4196
4196
4197 - show incoming changes with patches and full description::
4197 - show incoming changes with patches and full description::
4198
4198
4199 hg incoming -vp
4199 hg incoming -vp
4200
4200
4201 - show incoming changes excluding merges, store a bundle::
4201 - show incoming changes excluding merges, store a bundle::
4202
4202
4203 hg in -vpM --bundle incoming.hg
4203 hg in -vpM --bundle incoming.hg
4204 hg pull incoming.hg
4204 hg pull incoming.hg
4205
4205
4206 - briefly list changes inside a bundle::
4206 - briefly list changes inside a bundle::
4207
4207
4208 hg in changes.hg -T "{desc|firstline}\\n"
4208 hg in changes.hg -T "{desc|firstline}\\n"
4209
4209
4210 Returns 0 if there are incoming changes, 1 otherwise.
4210 Returns 0 if there are incoming changes, 1 otherwise.
4211 """
4211 """
4212 if opts.get('graph'):
4212 if opts.get('graph'):
4213 cmdutil.checkunsupportedgraphflags([], opts)
4213 cmdutil.checkunsupportedgraphflags([], opts)
4214 def display(other, chlist, displayer):
4214 def display(other, chlist, displayer):
4215 revdag = cmdutil.graphrevs(other, chlist, opts)
4215 revdag = cmdutil.graphrevs(other, chlist, opts)
4216 showparents = [ctx.node() for ctx in repo[None].parents()]
4216 showparents = [ctx.node() for ctx in repo[None].parents()]
4217 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4217 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4218 graphmod.asciiedges)
4218 graphmod.asciiedges)
4219
4219
4220 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4220 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4221 return 0
4221 return 0
4222
4222
4223 if opts.get('bundle') and opts.get('subrepos'):
4223 if opts.get('bundle') and opts.get('subrepos'):
4224 raise util.Abort(_('cannot combine --bundle and --subrepos'))
4224 raise util.Abort(_('cannot combine --bundle and --subrepos'))
4225
4225
4226 if opts.get('bookmarks'):
4226 if opts.get('bookmarks'):
4227 source, branches = hg.parseurl(ui.expandpath(source),
4227 source, branches = hg.parseurl(ui.expandpath(source),
4228 opts.get('branch'))
4228 opts.get('branch'))
4229 other = hg.peer(repo, opts, source)
4229 other = hg.peer(repo, opts, source)
4230 if 'bookmarks' not in other.listkeys('namespaces'):
4230 if 'bookmarks' not in other.listkeys('namespaces'):
4231 ui.warn(_("remote doesn't support bookmarks\n"))
4231 ui.warn(_("remote doesn't support bookmarks\n"))
4232 return 0
4232 return 0
4233 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4233 ui.status(_('comparing with %s\n') % util.hidepassword(source))
4234 return bookmarks.diff(ui, repo, other)
4234 return bookmarks.diff(ui, repo, other)
4235
4235
4236 repo._subtoppath = ui.expandpath(source)
4236 repo._subtoppath = ui.expandpath(source)
4237 try:
4237 try:
4238 return hg.incoming(ui, repo, source, opts)
4238 return hg.incoming(ui, repo, source, opts)
4239 finally:
4239 finally:
4240 del repo._subtoppath
4240 del repo._subtoppath
4241
4241
4242
4242
4243 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4243 @command('^init', remoteopts, _('[-e CMD] [--remotecmd CMD] [DEST]'),
4244 norepo=True)
4244 norepo=True)
4245 def init(ui, dest=".", **opts):
4245 def init(ui, dest=".", **opts):
4246 """create a new repository in the given directory
4246 """create a new repository in the given directory
4247
4247
4248 Initialize a new repository in the given directory. If the given
4248 Initialize a new repository in the given directory. If the given
4249 directory does not exist, it will be created.
4249 directory does not exist, it will be created.
4250
4250
4251 If no directory is given, the current directory is used.
4251 If no directory is given, the current directory is used.
4252
4252
4253 It is possible to specify an ``ssh://`` URL as the destination.
4253 It is possible to specify an ``ssh://`` URL as the destination.
4254 See :hg:`help urls` for more information.
4254 See :hg:`help urls` for more information.
4255
4255
4256 Returns 0 on success.
4256 Returns 0 on success.
4257 """
4257 """
4258 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4258 hg.peer(ui, opts, ui.expandpath(dest), create=True)
4259
4259
4260 @command('locate',
4260 @command('locate',
4261 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4261 [('r', 'rev', '', _('search the repository as it is in REV'), _('REV')),
4262 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4262 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
4263 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4263 ('f', 'fullpath', None, _('print complete paths from the filesystem root')),
4264 ] + walkopts,
4264 ] + walkopts,
4265 _('[OPTION]... [PATTERN]...'))
4265 _('[OPTION]... [PATTERN]...'))
4266 def locate(ui, repo, *pats, **opts):
4266 def locate(ui, repo, *pats, **opts):
4267 """locate files matching specific patterns (DEPRECATED)
4267 """locate files matching specific patterns (DEPRECATED)
4268
4268
4269 Print files under Mercurial control in the working directory whose
4269 Print files under Mercurial control in the working directory whose
4270 names match the given patterns.
4270 names match the given patterns.
4271
4271
4272 By default, this command searches all directories in the working
4272 By default, this command searches all directories in the working
4273 directory. To search just the current directory and its
4273 directory. To search just the current directory and its
4274 subdirectories, use "--include .".
4274 subdirectories, use "--include .".
4275
4275
4276 If no patterns are given to match, this command prints the names
4276 If no patterns are given to match, this command prints the names
4277 of all files under Mercurial control in the working directory.
4277 of all files under Mercurial control in the working directory.
4278
4278
4279 If you want to feed the output of this command into the "xargs"
4279 If you want to feed the output of this command into the "xargs"
4280 command, use the -0 option to both this command and "xargs". This
4280 command, use the -0 option to both this command and "xargs". This
4281 will avoid the problem of "xargs" treating single filenames that
4281 will avoid the problem of "xargs" treating single filenames that
4282 contain whitespace as multiple filenames.
4282 contain whitespace as multiple filenames.
4283
4283
4284 See :hg:`help files` for a more versatile command.
4284 See :hg:`help files` for a more versatile command.
4285
4285
4286 Returns 0 if a match is found, 1 otherwise.
4286 Returns 0 if a match is found, 1 otherwise.
4287 """
4287 """
4288 end = opts.get('print0') and '\0' or '\n'
4288 end = opts.get('print0') and '\0' or '\n'
4289 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4289 rev = scmutil.revsingle(repo, opts.get('rev'), None).node()
4290
4290
4291 ret = 1
4291 ret = 1
4292 ctx = repo[rev]
4292 ctx = repo[rev]
4293 m = scmutil.match(ctx, pats, opts, default='relglob')
4293 m = scmutil.match(ctx, pats, opts, default='relglob')
4294 m.bad = lambda x, y: False
4294 m.bad = lambda x, y: False
4295
4295
4296 for abs in ctx.matches(m):
4296 for abs in ctx.matches(m):
4297 if opts.get('fullpath'):
4297 if opts.get('fullpath'):
4298 ui.write(repo.wjoin(abs), end)
4298 ui.write(repo.wjoin(abs), end)
4299 else:
4299 else:
4300 ui.write(((pats and m.rel(abs)) or abs), end)
4300 ui.write(((pats and m.rel(abs)) or abs), end)
4301 ret = 0
4301 ret = 0
4302
4302
4303 return ret
4303 return ret
4304
4304
4305 @command('^log|history',
4305 @command('^log|history',
4306 [('f', 'follow', None,
4306 [('f', 'follow', None,
4307 _('follow changeset history, or file history across copies and renames')),
4307 _('follow changeset history, or file history across copies and renames')),
4308 ('', 'follow-first', None,
4308 ('', 'follow-first', None,
4309 _('only follow the first parent of merge changesets (DEPRECATED)')),
4309 _('only follow the first parent of merge changesets (DEPRECATED)')),
4310 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4310 ('d', 'date', '', _('show revisions matching date spec'), _('DATE')),
4311 ('C', 'copies', None, _('show copied files')),
4311 ('C', 'copies', None, _('show copied files')),
4312 ('k', 'keyword', [],
4312 ('k', 'keyword', [],
4313 _('do case-insensitive search for a given text'), _('TEXT')),
4313 _('do case-insensitive search for a given text'), _('TEXT')),
4314 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4314 ('r', 'rev', [], _('show the specified revision or revset'), _('REV')),
4315 ('', 'removed', None, _('include revisions where files were removed')),
4315 ('', 'removed', None, _('include revisions where files were removed')),
4316 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4316 ('m', 'only-merges', None, _('show only merges (DEPRECATED)')),
4317 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4317 ('u', 'user', [], _('revisions committed by user'), _('USER')),
4318 ('', 'only-branch', [],
4318 ('', 'only-branch', [],
4319 _('show only changesets within the given named branch (DEPRECATED)'),
4319 _('show only changesets within the given named branch (DEPRECATED)'),
4320 _('BRANCH')),
4320 _('BRANCH')),
4321 ('b', 'branch', [],
4321 ('b', 'branch', [],
4322 _('show changesets within the given named branch'), _('BRANCH')),
4322 _('show changesets within the given named branch'), _('BRANCH')),
4323 ('P', 'prune', [],
4323 ('P', 'prune', [],
4324 _('do not display revision or any of its ancestors'), _('REV')),
4324 _('do not display revision or any of its ancestors'), _('REV')),
4325 ] + logopts + walkopts,
4325 ] + logopts + walkopts,
4326 _('[OPTION]... [FILE]'),
4326 _('[OPTION]... [FILE]'),
4327 inferrepo=True)
4327 inferrepo=True)
4328 def log(ui, repo, *pats, **opts):
4328 def log(ui, repo, *pats, **opts):
4329 """show revision history of entire repository or files
4329 """show revision history of entire repository or files
4330
4330
4331 Print the revision history of the specified files or the entire
4331 Print the revision history of the specified files or the entire
4332 project.
4332 project.
4333
4333
4334 If no revision range is specified, the default is ``tip:0`` unless
4334 If no revision range is specified, the default is ``tip:0`` unless
4335 --follow is set, in which case the working directory parent is
4335 --follow is set, in which case the working directory parent is
4336 used as the starting revision.
4336 used as the starting revision.
4337
4337
4338 File history is shown without following rename or copy history of
4338 File history is shown without following rename or copy history of
4339 files. Use -f/--follow with a filename to follow history across
4339 files. Use -f/--follow with a filename to follow history across
4340 renames and copies. --follow without a filename will only show
4340 renames and copies. --follow without a filename will only show
4341 ancestors or descendants of the starting revision.
4341 ancestors or descendants of the starting revision.
4342
4342
4343 By default this command prints revision number and changeset id,
4343 By default this command prints revision number and changeset id,
4344 tags, non-trivial parents, user, date and time, and a summary for
4344 tags, non-trivial parents, user, date and time, and a summary for
4345 each commit. When the -v/--verbose switch is used, the list of
4345 each commit. When the -v/--verbose switch is used, the list of
4346 changed files and full commit message are shown.
4346 changed files and full commit message are shown.
4347
4347
4348 With --graph the revisions are shown as an ASCII art DAG with the most
4348 With --graph the revisions are shown as an ASCII art DAG with the most
4349 recent changeset at the top.
4349 recent changeset at the top.
4350 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4350 'o' is a changeset, '@' is a working directory parent, 'x' is obsolete,
4351 and '+' represents a fork where the changeset from the lines below is a
4351 and '+' represents a fork where the changeset from the lines below is a
4352 parent of the 'o' merge on the same line.
4352 parent of the 'o' merge on the same line.
4353
4353
4354 .. note::
4354 .. note::
4355
4355
4356 log -p/--patch may generate unexpected diff output for merge
4356 log -p/--patch may generate unexpected diff output for merge
4357 changesets, as it will only compare the merge changeset against
4357 changesets, as it will only compare the merge changeset against
4358 its first parent. Also, only files different from BOTH parents
4358 its first parent. Also, only files different from BOTH parents
4359 will appear in files:.
4359 will appear in files:.
4360
4360
4361 .. note::
4361 .. note::
4362
4362
4363 for performance reasons, log FILE may omit duplicate changes
4363 for performance reasons, log FILE may omit duplicate changes
4364 made on branches and will not show removals or mode changes. To
4364 made on branches and will not show removals or mode changes. To
4365 see all such changes, use the --removed switch.
4365 see all such changes, use the --removed switch.
4366
4366
4367 .. container:: verbose
4367 .. container:: verbose
4368
4368
4369 Some examples:
4369 Some examples:
4370
4370
4371 - changesets with full descriptions and file lists::
4371 - changesets with full descriptions and file lists::
4372
4372
4373 hg log -v
4373 hg log -v
4374
4374
4375 - changesets ancestral to the working directory::
4375 - changesets ancestral to the working directory::
4376
4376
4377 hg log -f
4377 hg log -f
4378
4378
4379 - last 10 commits on the current branch::
4379 - last 10 commits on the current branch::
4380
4380
4381 hg log -l 10 -b .
4381 hg log -l 10 -b .
4382
4382
4383 - changesets showing all modifications of a file, including removals::
4383 - changesets showing all modifications of a file, including removals::
4384
4384
4385 hg log --removed file.c
4385 hg log --removed file.c
4386
4386
4387 - all changesets that touch a directory, with diffs, excluding merges::
4387 - all changesets that touch a directory, with diffs, excluding merges::
4388
4388
4389 hg log -Mp lib/
4389 hg log -Mp lib/
4390
4390
4391 - all revision numbers that match a keyword::
4391 - all revision numbers that match a keyword::
4392
4392
4393 hg log -k bug --template "{rev}\\n"
4393 hg log -k bug --template "{rev}\\n"
4394
4394
4395 - list available log templates::
4395 - list available log templates::
4396
4396
4397 hg log -T list
4397 hg log -T list
4398
4398
4399 - check if a given changeset is included in a tagged release::
4399 - check if a given changeset is included in a tagged release::
4400
4400
4401 hg log -r "a21ccf and ancestor(1.9)"
4401 hg log -r "a21ccf and ancestor(1.9)"
4402
4402
4403 - find all changesets by some user in a date range::
4403 - find all changesets by some user in a date range::
4404
4404
4405 hg log -k alice -d "may 2008 to jul 2008"
4405 hg log -k alice -d "may 2008 to jul 2008"
4406
4406
4407 - summary of all changesets after the last tag::
4407 - summary of all changesets after the last tag::
4408
4408
4409 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4409 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4410
4410
4411 See :hg:`help dates` for a list of formats valid for -d/--date.
4411 See :hg:`help dates` for a list of formats valid for -d/--date.
4412
4412
4413 See :hg:`help revisions` and :hg:`help revsets` for more about
4413 See :hg:`help revisions` and :hg:`help revsets` for more about
4414 specifying revisions.
4414 specifying revisions.
4415
4415
4416 See :hg:`help templates` for more about pre-packaged styles and
4416 See :hg:`help templates` for more about pre-packaged styles and
4417 specifying custom templates.
4417 specifying custom templates.
4418
4418
4419 Returns 0 on success.
4419 Returns 0 on success.
4420
4420
4421 """
4421 """
4422 if opts.get('graph'):
4422 if opts.get('graph'):
4423 return cmdutil.graphlog(ui, repo, *pats, **opts)
4423 return cmdutil.graphlog(ui, repo, *pats, **opts)
4424
4424
4425 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4425 revs, expr, filematcher = cmdutil.getlogrevs(repo, pats, opts)
4426 limit = cmdutil.loglimit(opts)
4426 limit = cmdutil.loglimit(opts)
4427 count = 0
4427 count = 0
4428
4428
4429 getrenamed = None
4429 getrenamed = None
4430 if opts.get('copies'):
4430 if opts.get('copies'):
4431 endrev = None
4431 endrev = None
4432 if opts.get('rev'):
4432 if opts.get('rev'):
4433 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4433 endrev = scmutil.revrange(repo, opts.get('rev')).max() + 1
4434 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4434 getrenamed = templatekw.getrenamedfn(repo, endrev=endrev)
4435
4435
4436 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4436 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4437 for rev in revs:
4437 for rev in revs:
4438 if count == limit:
4438 if count == limit:
4439 break
4439 break
4440 ctx = repo[rev]
4440 ctx = repo[rev]
4441 copies = None
4441 copies = None
4442 if getrenamed is not None and rev:
4442 if getrenamed is not None and rev:
4443 copies = []
4443 copies = []
4444 for fn in ctx.files():
4444 for fn in ctx.files():
4445 rename = getrenamed(fn, rev)
4445 rename = getrenamed(fn, rev)
4446 if rename:
4446 if rename:
4447 copies.append((fn, rename[0]))
4447 copies.append((fn, rename[0]))
4448 revmatchfn = filematcher and filematcher(ctx.rev()) or None
4448 revmatchfn = filematcher and filematcher(ctx.rev()) or None
4449 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4449 displayer.show(ctx, copies=copies, matchfn=revmatchfn)
4450 if displayer.flush(rev):
4450 if displayer.flush(rev):
4451 count += 1
4451 count += 1
4452
4452
4453 displayer.close()
4453 displayer.close()
4454
4454
4455 @command('manifest',
4455 @command('manifest',
4456 [('r', 'rev', '', _('revision to display'), _('REV')),
4456 [('r', 'rev', '', _('revision to display'), _('REV')),
4457 ('', 'all', False, _("list files from all revisions"))]
4457 ('', 'all', False, _("list files from all revisions"))]
4458 + formatteropts,
4458 + formatteropts,
4459 _('[-r REV]'))
4459 _('[-r REV]'))
4460 def manifest(ui, repo, node=None, rev=None, **opts):
4460 def manifest(ui, repo, node=None, rev=None, **opts):
4461 """output the current or given revision of the project manifest
4461 """output the current or given revision of the project manifest
4462
4462
4463 Print a list of version controlled files for the given revision.
4463 Print a list of version controlled files for the given revision.
4464 If no revision is given, the first parent of the working directory
4464 If no revision is given, the first parent of the working directory
4465 is used, or the null revision if no revision is checked out.
4465 is used, or the null revision if no revision is checked out.
4466
4466
4467 With -v, print file permissions, symlink and executable bits.
4467 With -v, print file permissions, symlink and executable bits.
4468 With --debug, print file revision hashes.
4468 With --debug, print file revision hashes.
4469
4469
4470 If option --all is specified, the list of all files from all revisions
4470 If option --all is specified, the list of all files from all revisions
4471 is printed. This includes deleted and renamed files.
4471 is printed. This includes deleted and renamed files.
4472
4472
4473 Returns 0 on success.
4473 Returns 0 on success.
4474 """
4474 """
4475
4475
4476 fm = ui.formatter('manifest', opts)
4476 fm = ui.formatter('manifest', opts)
4477
4477
4478 if opts.get('all'):
4478 if opts.get('all'):
4479 if rev or node:
4479 if rev or node:
4480 raise util.Abort(_("can't specify a revision with --all"))
4480 raise util.Abort(_("can't specify a revision with --all"))
4481
4481
4482 res = []
4482 res = []
4483 prefix = "data/"
4483 prefix = "data/"
4484 suffix = ".i"
4484 suffix = ".i"
4485 plen = len(prefix)
4485 plen = len(prefix)
4486 slen = len(suffix)
4486 slen = len(suffix)
4487 lock = repo.lock()
4487 lock = repo.lock()
4488 try:
4488 try:
4489 for fn, b, size in repo.store.datafiles():
4489 for fn, b, size in repo.store.datafiles():
4490 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4490 if size != 0 and fn[-slen:] == suffix and fn[:plen] == prefix:
4491 res.append(fn[plen:-slen])
4491 res.append(fn[plen:-slen])
4492 finally:
4492 finally:
4493 lock.release()
4493 lock.release()
4494 for f in res:
4494 for f in res:
4495 fm.startitem()
4495 fm.startitem()
4496 fm.write("path", '%s\n', f)
4496 fm.write("path", '%s\n', f)
4497 fm.end()
4497 fm.end()
4498 return
4498 return
4499
4499
4500 if rev and node:
4500 if rev and node:
4501 raise util.Abort(_("please specify just one revision"))
4501 raise util.Abort(_("please specify just one revision"))
4502
4502
4503 if not node:
4503 if not node:
4504 node = rev
4504 node = rev
4505
4505
4506 char = {'l': '@', 'x': '*', '': ''}
4506 char = {'l': '@', 'x': '*', '': ''}
4507 mode = {'l': '644', 'x': '755', '': '644'}
4507 mode = {'l': '644', 'x': '755', '': '644'}
4508 ctx = scmutil.revsingle(repo, node)
4508 ctx = scmutil.revsingle(repo, node)
4509 mf = ctx.manifest()
4509 mf = ctx.manifest()
4510 for f in ctx:
4510 for f in ctx:
4511 fm.startitem()
4511 fm.startitem()
4512 fl = ctx[f].flags()
4512 fl = ctx[f].flags()
4513 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4513 fm.condwrite(ui.debugflag, 'hash', '%s ', hex(mf[f]))
4514 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4514 fm.condwrite(ui.verbose, 'mode type', '%s %1s ', mode[fl], char[fl])
4515 fm.write('path', '%s\n', f)
4515 fm.write('path', '%s\n', f)
4516 fm.end()
4516 fm.end()
4517
4517
4518 @command('^merge',
4518 @command('^merge',
4519 [('f', 'force', None,
4519 [('f', 'force', None,
4520 _('force a merge including outstanding changes (DEPRECATED)')),
4520 _('force a merge including outstanding changes (DEPRECATED)')),
4521 ('r', 'rev', '', _('revision to merge'), _('REV')),
4521 ('r', 'rev', '', _('revision to merge'), _('REV')),
4522 ('P', 'preview', None,
4522 ('P', 'preview', None,
4523 _('review revisions to merge (no merge is performed)'))
4523 _('review revisions to merge (no merge is performed)'))
4524 ] + mergetoolopts,
4524 ] + mergetoolopts,
4525 _('[-P] [-f] [[-r] REV]'))
4525 _('[-P] [-f] [[-r] REV]'))
4526 def merge(ui, repo, node=None, **opts):
4526 def merge(ui, repo, node=None, **opts):
4527 """merge working directory with another revision
4527 """merge working directory with another revision
4528
4528
4529 The current working directory is updated with all changes made in
4529 The current working directory is updated with all changes made in
4530 the requested revision since the last common predecessor revision.
4530 the requested revision since the last common predecessor revision.
4531
4531
4532 Files that changed between either parent are marked as changed for
4532 Files that changed between either parent are marked as changed for
4533 the next commit and a commit must be performed before any further
4533 the next commit and a commit must be performed before any further
4534 updates to the repository are allowed. The next commit will have
4534 updates to the repository are allowed. The next commit will have
4535 two parents.
4535 two parents.
4536
4536
4537 ``--tool`` can be used to specify the merge tool used for file
4537 ``--tool`` can be used to specify the merge tool used for file
4538 merges. It overrides the HGMERGE environment variable and your
4538 merges. It overrides the HGMERGE environment variable and your
4539 configuration files. See :hg:`help merge-tools` for options.
4539 configuration files. See :hg:`help merge-tools` for options.
4540
4540
4541 If no revision is specified, the working directory's parent is a
4541 If no revision is specified, the working directory's parent is a
4542 head revision, and the current branch contains exactly one other
4542 head revision, and the current branch contains exactly one other
4543 head, the other head is merged with by default. Otherwise, an
4543 head, the other head is merged with by default. Otherwise, an
4544 explicit revision with which to merge with must be provided.
4544 explicit revision with which to merge with must be provided.
4545
4545
4546 :hg:`resolve` must be used to resolve unresolved files.
4546 :hg:`resolve` must be used to resolve unresolved files.
4547
4547
4548 To undo an uncommitted merge, use :hg:`update --clean .` which
4548 To undo an uncommitted merge, use :hg:`update --clean .` which
4549 will check out a clean copy of the original merge parent, losing
4549 will check out a clean copy of the original merge parent, losing
4550 all changes.
4550 all changes.
4551
4551
4552 Returns 0 on success, 1 if there are unresolved files.
4552 Returns 0 on success, 1 if there are unresolved files.
4553 """
4553 """
4554
4554
4555 if opts.get('rev') and node:
4555 if opts.get('rev') and node:
4556 raise util.Abort(_("please specify just one revision"))
4556 raise util.Abort(_("please specify just one revision"))
4557 if not node:
4557 if not node:
4558 node = opts.get('rev')
4558 node = opts.get('rev')
4559
4559
4560 if node:
4560 if node:
4561 node = scmutil.revsingle(repo, node).node()
4561 node = scmutil.revsingle(repo, node).node()
4562
4562
4563 if not node and repo._bookmarkcurrent:
4563 if not node and repo._bookmarkcurrent:
4564 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4564 bmheads = repo.bookmarkheads(repo._bookmarkcurrent)
4565 curhead = repo[repo._bookmarkcurrent].node()
4565 curhead = repo[repo._bookmarkcurrent].node()
4566 if len(bmheads) == 2:
4566 if len(bmheads) == 2:
4567 if curhead == bmheads[0]:
4567 if curhead == bmheads[0]:
4568 node = bmheads[1]
4568 node = bmheads[1]
4569 else:
4569 else:
4570 node = bmheads[0]
4570 node = bmheads[0]
4571 elif len(bmheads) > 2:
4571 elif len(bmheads) > 2:
4572 raise util.Abort(_("multiple matching bookmarks to merge - "
4572 raise util.Abort(_("multiple matching bookmarks to merge - "
4573 "please merge with an explicit rev or bookmark"),
4573 "please merge with an explicit rev or bookmark"),
4574 hint=_("run 'hg heads' to see all heads"))
4574 hint=_("run 'hg heads' to see all heads"))
4575 elif len(bmheads) <= 1:
4575 elif len(bmheads) <= 1:
4576 raise util.Abort(_("no matching bookmark to merge - "
4576 raise util.Abort(_("no matching bookmark to merge - "
4577 "please merge with an explicit rev or bookmark"),
4577 "please merge with an explicit rev or bookmark"),
4578 hint=_("run 'hg heads' to see all heads"))
4578 hint=_("run 'hg heads' to see all heads"))
4579
4579
4580 if not node and not repo._bookmarkcurrent:
4580 if not node and not repo._bookmarkcurrent:
4581 branch = repo[None].branch()
4581 branch = repo[None].branch()
4582 bheads = repo.branchheads(branch)
4582 bheads = repo.branchheads(branch)
4583 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4583 nbhs = [bh for bh in bheads if not repo[bh].bookmarks()]
4584
4584
4585 if len(nbhs) > 2:
4585 if len(nbhs) > 2:
4586 raise util.Abort(_("branch '%s' has %d heads - "
4586 raise util.Abort(_("branch '%s' has %d heads - "
4587 "please merge with an explicit rev")
4587 "please merge with an explicit rev")
4588 % (branch, len(bheads)),
4588 % (branch, len(bheads)),
4589 hint=_("run 'hg heads .' to see heads"))
4589 hint=_("run 'hg heads .' to see heads"))
4590
4590
4591 parent = repo.dirstate.p1()
4591 parent = repo.dirstate.p1()
4592 if len(nbhs) <= 1:
4592 if len(nbhs) <= 1:
4593 if len(bheads) > 1:
4593 if len(bheads) > 1:
4594 raise util.Abort(_("heads are bookmarked - "
4594 raise util.Abort(_("heads are bookmarked - "
4595 "please merge with an explicit rev"),
4595 "please merge with an explicit rev"),
4596 hint=_("run 'hg heads' to see all heads"))
4596 hint=_("run 'hg heads' to see all heads"))
4597 if len(repo.heads()) > 1:
4597 if len(repo.heads()) > 1:
4598 raise util.Abort(_("branch '%s' has one head - "
4598 raise util.Abort(_("branch '%s' has one head - "
4599 "please merge with an explicit rev")
4599 "please merge with an explicit rev")
4600 % branch,
4600 % branch,
4601 hint=_("run 'hg heads' to see all heads"))
4601 hint=_("run 'hg heads' to see all heads"))
4602 msg, hint = _('nothing to merge'), None
4602 msg, hint = _('nothing to merge'), None
4603 if parent != repo.lookup(branch):
4603 if parent != repo.lookup(branch):
4604 hint = _("use 'hg update' instead")
4604 hint = _("use 'hg update' instead")
4605 raise util.Abort(msg, hint=hint)
4605 raise util.Abort(msg, hint=hint)
4606
4606
4607 if parent not in bheads:
4607 if parent not in bheads:
4608 raise util.Abort(_('working directory not at a head revision'),
4608 raise util.Abort(_('working directory not at a head revision'),
4609 hint=_("use 'hg update' or merge with an "
4609 hint=_("use 'hg update' or merge with an "
4610 "explicit revision"))
4610 "explicit revision"))
4611 if parent == nbhs[0]:
4611 if parent == nbhs[0]:
4612 node = nbhs[-1]
4612 node = nbhs[-1]
4613 else:
4613 else:
4614 node = nbhs[0]
4614 node = nbhs[0]
4615
4615
4616 if opts.get('preview'):
4616 if opts.get('preview'):
4617 # find nodes that are ancestors of p2 but not of p1
4617 # find nodes that are ancestors of p2 but not of p1
4618 p1 = repo.lookup('.')
4618 p1 = repo.lookup('.')
4619 p2 = repo.lookup(node)
4619 p2 = repo.lookup(node)
4620 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4620 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4621
4621
4622 displayer = cmdutil.show_changeset(ui, repo, opts)
4622 displayer = cmdutil.show_changeset(ui, repo, opts)
4623 for node in nodes:
4623 for node in nodes:
4624 displayer.show(repo[node])
4624 displayer.show(repo[node])
4625 displayer.close()
4625 displayer.close()
4626 return 0
4626 return 0
4627
4627
4628 try:
4628 try:
4629 # ui.forcemerge is an internal variable, do not document
4629 # ui.forcemerge is an internal variable, do not document
4630 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4630 repo.ui.setconfig('ui', 'forcemerge', opts.get('tool', ''), 'merge')
4631 return hg.merge(repo, node, force=opts.get('force'))
4631 return hg.merge(repo, node, force=opts.get('force'))
4632 finally:
4632 finally:
4633 ui.setconfig('ui', 'forcemerge', '', 'merge')
4633 ui.setconfig('ui', 'forcemerge', '', 'merge')
4634
4634
4635 @command('outgoing|out',
4635 @command('outgoing|out',
4636 [('f', 'force', None, _('run even when the destination is unrelated')),
4636 [('f', 'force', None, _('run even when the destination is unrelated')),
4637 ('r', 'rev', [],
4637 ('r', 'rev', [],
4638 _('a changeset intended to be included in the destination'), _('REV')),
4638 _('a changeset intended to be included in the destination'), _('REV')),
4639 ('n', 'newest-first', None, _('show newest record first')),
4639 ('n', 'newest-first', None, _('show newest record first')),
4640 ('B', 'bookmarks', False, _('compare bookmarks')),
4640 ('B', 'bookmarks', False, _('compare bookmarks')),
4641 ('b', 'branch', [], _('a specific branch you would like to push'),
4641 ('b', 'branch', [], _('a specific branch you would like to push'),
4642 _('BRANCH')),
4642 _('BRANCH')),
4643 ] + logopts + remoteopts + subrepoopts,
4643 ] + logopts + remoteopts + subrepoopts,
4644 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4644 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]'))
4645 def outgoing(ui, repo, dest=None, **opts):
4645 def outgoing(ui, repo, dest=None, **opts):
4646 """show changesets not found in the destination
4646 """show changesets not found in the destination
4647
4647
4648 Show changesets not found in the specified destination repository
4648 Show changesets not found in the specified destination repository
4649 or the default push location. These are the changesets that would
4649 or the default push location. These are the changesets that would
4650 be pushed if a push was requested.
4650 be pushed if a push was requested.
4651
4651
4652 See pull for details of valid destination formats.
4652 See pull for details of valid destination formats.
4653
4653
4654 Returns 0 if there are outgoing changes, 1 otherwise.
4654 Returns 0 if there are outgoing changes, 1 otherwise.
4655 """
4655 """
4656 if opts.get('graph'):
4656 if opts.get('graph'):
4657 cmdutil.checkunsupportedgraphflags([], opts)
4657 cmdutil.checkunsupportedgraphflags([], opts)
4658 o, other = hg._outgoing(ui, repo, dest, opts)
4658 o, other = hg._outgoing(ui, repo, dest, opts)
4659 if not o:
4659 if not o:
4660 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4660 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4661 return
4661 return
4662
4662
4663 revdag = cmdutil.graphrevs(repo, o, opts)
4663 revdag = cmdutil.graphrevs(repo, o, opts)
4664 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4664 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=True)
4665 showparents = [ctx.node() for ctx in repo[None].parents()]
4665 showparents = [ctx.node() for ctx in repo[None].parents()]
4666 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4666 cmdutil.displaygraph(ui, revdag, displayer, showparents,
4667 graphmod.asciiedges)
4667 graphmod.asciiedges)
4668 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4668 cmdutil.outgoinghooks(ui, repo, other, opts, o)
4669 return 0
4669 return 0
4670
4670
4671 if opts.get('bookmarks'):
4671 if opts.get('bookmarks'):
4672 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4672 dest = ui.expandpath(dest or 'default-push', dest or 'default')
4673 dest, branches = hg.parseurl(dest, opts.get('branch'))
4673 dest, branches = hg.parseurl(dest, opts.get('branch'))
4674 other = hg.peer(repo, opts, dest)
4674 other = hg.peer(repo, opts, dest)
4675 if 'bookmarks' not in other.listkeys('namespaces'):
4675 if 'bookmarks' not in other.listkeys('namespaces'):
4676 ui.warn(_("remote doesn't support bookmarks\n"))
4676 ui.warn(_("remote doesn't support bookmarks\n"))
4677 return 0
4677 return 0
4678 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4678 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
4679 return bookmarks.diff(ui, other, repo)
4679 return bookmarks.diff(ui, other, repo)
4680
4680
4681 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4681 repo._subtoppath = ui.expandpath(dest or 'default-push', dest or 'default')
4682 try:
4682 try:
4683 return hg.outgoing(ui, repo, dest, opts)
4683 return hg.outgoing(ui, repo, dest, opts)
4684 finally:
4684 finally:
4685 del repo._subtoppath
4685 del repo._subtoppath
4686
4686
4687 @command('parents',
4687 @command('parents',
4688 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4688 [('r', 'rev', '', _('show parents of the specified revision'), _('REV')),
4689 ] + templateopts,
4689 ] + templateopts,
4690 _('[-r REV] [FILE]'),
4690 _('[-r REV] [FILE]'),
4691 inferrepo=True)
4691 inferrepo=True)
4692 def parents(ui, repo, file_=None, **opts):
4692 def parents(ui, repo, file_=None, **opts):
4693 """show the parents of the working directory or revision (DEPRECATED)
4693 """show the parents of the working directory or revision (DEPRECATED)
4694
4694
4695 Print the working directory's parent revisions. If a revision is
4695 Print the working directory's parent revisions. If a revision is
4696 given via -r/--rev, the parent of that revision will be printed.
4696 given via -r/--rev, the parent of that revision will be printed.
4697 If a file argument is given, the revision in which the file was
4697 If a file argument is given, the revision in which the file was
4698 last changed (before the working directory revision or the
4698 last changed (before the working directory revision or the
4699 argument to --rev if given) is printed.
4699 argument to --rev if given) is printed.
4700
4700
4701 See :hg:`summary` and :hg:`help revsets` for related information.
4701 See :hg:`summary` and :hg:`help revsets` for related information.
4702
4702
4703 Returns 0 on success.
4703 Returns 0 on success.
4704 """
4704 """
4705
4705
4706 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4706 ctx = scmutil.revsingle(repo, opts.get('rev'), None)
4707
4707
4708 if file_:
4708 if file_:
4709 m = scmutil.match(ctx, (file_,), opts)
4709 m = scmutil.match(ctx, (file_,), opts)
4710 if m.anypats() or len(m.files()) != 1:
4710 if m.anypats() or len(m.files()) != 1:
4711 raise util.Abort(_('can only specify an explicit filename'))
4711 raise util.Abort(_('can only specify an explicit filename'))
4712 file_ = m.files()[0]
4712 file_ = m.files()[0]
4713 filenodes = []
4713 filenodes = []
4714 for cp in ctx.parents():
4714 for cp in ctx.parents():
4715 if not cp:
4715 if not cp:
4716 continue
4716 continue
4717 try:
4717 try:
4718 filenodes.append(cp.filenode(file_))
4718 filenodes.append(cp.filenode(file_))
4719 except error.LookupError:
4719 except error.LookupError:
4720 pass
4720 pass
4721 if not filenodes:
4721 if not filenodes:
4722 raise util.Abort(_("'%s' not found in manifest!") % file_)
4722 raise util.Abort(_("'%s' not found in manifest!") % file_)
4723 p = []
4723 p = []
4724 for fn in filenodes:
4724 for fn in filenodes:
4725 fctx = repo.filectx(file_, fileid=fn)
4725 fctx = repo.filectx(file_, fileid=fn)
4726 p.append(fctx.node())
4726 p.append(fctx.node())
4727 else:
4727 else:
4728 p = [cp.node() for cp in ctx.parents()]
4728 p = [cp.node() for cp in ctx.parents()]
4729
4729
4730 displayer = cmdutil.show_changeset(ui, repo, opts)
4730 displayer = cmdutil.show_changeset(ui, repo, opts)
4731 for n in p:
4731 for n in p:
4732 if n != nullid:
4732 if n != nullid:
4733 displayer.show(repo[n])
4733 displayer.show(repo[n])
4734 displayer.close()
4734 displayer.close()
4735
4735
4736 @command('paths', [], _('[NAME]'), optionalrepo=True)
4736 @command('paths', [], _('[NAME]'), optionalrepo=True)
4737 def paths(ui, repo, search=None):
4737 def paths(ui, repo, search=None):
4738 """show aliases for remote repositories
4738 """show aliases for remote repositories
4739
4739
4740 Show definition of symbolic path name NAME. If no name is given,
4740 Show definition of symbolic path name NAME. If no name is given,
4741 show definition of all available names.
4741 show definition of all available names.
4742
4742
4743 Option -q/--quiet suppresses all output when searching for NAME
4743 Option -q/--quiet suppresses all output when searching for NAME
4744 and shows only the path names when listing all definitions.
4744 and shows only the path names when listing all definitions.
4745
4745
4746 Path names are defined in the [paths] section of your
4746 Path names are defined in the [paths] section of your
4747 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4747 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4748 repository, ``.hg/hgrc`` is used, too.
4748 repository, ``.hg/hgrc`` is used, too.
4749
4749
4750 The path names ``default`` and ``default-push`` have a special
4750 The path names ``default`` and ``default-push`` have a special
4751 meaning. When performing a push or pull operation, they are used
4751 meaning. When performing a push or pull operation, they are used
4752 as fallbacks if no location is specified on the command-line.
4752 as fallbacks if no location is specified on the command-line.
4753 When ``default-push`` is set, it will be used for push and
4753 When ``default-push`` is set, it will be used for push and
4754 ``default`` will be used for pull; otherwise ``default`` is used
4754 ``default`` will be used for pull; otherwise ``default`` is used
4755 as the fallback for both. When cloning a repository, the clone
4755 as the fallback for both. When cloning a repository, the clone
4756 source is written as ``default`` in ``.hg/hgrc``. Note that
4756 source is written as ``default`` in ``.hg/hgrc``. Note that
4757 ``default`` and ``default-push`` apply to all inbound (e.g.
4757 ``default`` and ``default-push`` apply to all inbound (e.g.
4758 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4758 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email` and
4759 :hg:`bundle`) operations.
4759 :hg:`bundle`) operations.
4760
4760
4761 See :hg:`help urls` for more information.
4761 See :hg:`help urls` for more information.
4762
4762
4763 Returns 0 on success.
4763 Returns 0 on success.
4764 """
4764 """
4765 if search:
4765 if search:
4766 for name, path in ui.configitems("paths"):
4766 for name, path in ui.configitems("paths"):
4767 if name == search:
4767 if name == search:
4768 ui.status("%s\n" % util.hidepassword(path))
4768 ui.status("%s\n" % util.hidepassword(path))
4769 return
4769 return
4770 if not ui.quiet:
4770 if not ui.quiet:
4771 ui.warn(_("not found!\n"))
4771 ui.warn(_("not found!\n"))
4772 return 1
4772 return 1
4773 else:
4773 else:
4774 for name, path in ui.configitems("paths"):
4774 for name, path in ui.configitems("paths"):
4775 if ui.quiet:
4775 if ui.quiet:
4776 ui.write("%s\n" % name)
4776 ui.write("%s\n" % name)
4777 else:
4777 else:
4778 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4778 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
4779
4779
4780 @command('phase',
4780 @command('phase',
4781 [('p', 'public', False, _('set changeset phase to public')),
4781 [('p', 'public', False, _('set changeset phase to public')),
4782 ('d', 'draft', False, _('set changeset phase to draft')),
4782 ('d', 'draft', False, _('set changeset phase to draft')),
4783 ('s', 'secret', False, _('set changeset phase to secret')),
4783 ('s', 'secret', False, _('set changeset phase to secret')),
4784 ('f', 'force', False, _('allow to move boundary backward')),
4784 ('f', 'force', False, _('allow to move boundary backward')),
4785 ('r', 'rev', [], _('target revision'), _('REV')),
4785 ('r', 'rev', [], _('target revision'), _('REV')),
4786 ],
4786 ],
4787 _('[-p|-d|-s] [-f] [-r] REV...'))
4787 _('[-p|-d|-s] [-f] [-r] REV...'))
4788 def phase(ui, repo, *revs, **opts):
4788 def phase(ui, repo, *revs, **opts):
4789 """set or show the current phase name
4789 """set or show the current phase name
4790
4790
4791 With no argument, show the phase name of specified revisions.
4791 With no argument, show the phase name of specified revisions.
4792
4792
4793 With one of -p/--public, -d/--draft or -s/--secret, change the
4793 With one of -p/--public, -d/--draft or -s/--secret, change the
4794 phase value of the specified revisions.
4794 phase value of the specified revisions.
4795
4795
4796 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4796 Unless -f/--force is specified, :hg:`phase` won't move changeset from a
4797 lower phase to an higher phase. Phases are ordered as follows::
4797 lower phase to an higher phase. Phases are ordered as follows::
4798
4798
4799 public < draft < secret
4799 public < draft < secret
4800
4800
4801 Returns 0 on success, 1 if no phases were changed or some could not
4801 Returns 0 on success, 1 if no phases were changed or some could not
4802 be changed.
4802 be changed.
4803 """
4803 """
4804 # search for a unique phase argument
4804 # search for a unique phase argument
4805 targetphase = None
4805 targetphase = None
4806 for idx, name in enumerate(phases.phasenames):
4806 for idx, name in enumerate(phases.phasenames):
4807 if opts[name]:
4807 if opts[name]:
4808 if targetphase is not None:
4808 if targetphase is not None:
4809 raise util.Abort(_('only one phase can be specified'))
4809 raise util.Abort(_('only one phase can be specified'))
4810 targetphase = idx
4810 targetphase = idx
4811
4811
4812 # look for specified revision
4812 # look for specified revision
4813 revs = list(revs)
4813 revs = list(revs)
4814 revs.extend(opts['rev'])
4814 revs.extend(opts['rev'])
4815 if not revs:
4815 if not revs:
4816 raise util.Abort(_('no revisions specified'))
4816 raise util.Abort(_('no revisions specified'))
4817
4817
4818 revs = scmutil.revrange(repo, revs)
4818 revs = scmutil.revrange(repo, revs)
4819
4819
4820 lock = None
4820 lock = None
4821 ret = 0
4821 ret = 0
4822 if targetphase is None:
4822 if targetphase is None:
4823 # display
4823 # display
4824 for r in revs:
4824 for r in revs:
4825 ctx = repo[r]
4825 ctx = repo[r]
4826 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4826 ui.write('%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4827 else:
4827 else:
4828 tr = None
4828 tr = None
4829 lock = repo.lock()
4829 lock = repo.lock()
4830 try:
4830 try:
4831 tr = repo.transaction("phase")
4831 tr = repo.transaction("phase")
4832 # set phase
4832 # set phase
4833 if not revs:
4833 if not revs:
4834 raise util.Abort(_('empty revision set'))
4834 raise util.Abort(_('empty revision set'))
4835 nodes = [repo[r].node() for r in revs]
4835 nodes = [repo[r].node() for r in revs]
4836 # moving revision from public to draft may hide them
4836 # moving revision from public to draft may hide them
4837 # We have to check result on an unfiltered repository
4837 # We have to check result on an unfiltered repository
4838 unfi = repo.unfiltered()
4838 unfi = repo.unfiltered()
4839 getphase = unfi._phasecache.phase
4839 getphase = unfi._phasecache.phase
4840 olddata = [getphase(unfi, r) for r in unfi]
4840 olddata = [getphase(unfi, r) for r in unfi]
4841 phases.advanceboundary(repo, tr, targetphase, nodes)
4841 phases.advanceboundary(repo, tr, targetphase, nodes)
4842 if opts['force']:
4842 if opts['force']:
4843 phases.retractboundary(repo, tr, targetphase, nodes)
4843 phases.retractboundary(repo, tr, targetphase, nodes)
4844 tr.close()
4844 tr.close()
4845 finally:
4845 finally:
4846 if tr is not None:
4846 if tr is not None:
4847 tr.release()
4847 tr.release()
4848 lock.release()
4848 lock.release()
4849 getphase = unfi._phasecache.phase
4849 getphase = unfi._phasecache.phase
4850 newdata = [getphase(unfi, r) for r in unfi]
4850 newdata = [getphase(unfi, r) for r in unfi]
4851 changes = sum(newdata[r] != olddata[r] for r in unfi)
4851 changes = sum(newdata[r] != olddata[r] for r in unfi)
4852 cl = unfi.changelog
4852 cl = unfi.changelog
4853 rejected = [n for n in nodes
4853 rejected = [n for n in nodes
4854 if newdata[cl.rev(n)] < targetphase]
4854 if newdata[cl.rev(n)] < targetphase]
4855 if rejected:
4855 if rejected:
4856 ui.warn(_('cannot move %i changesets to a higher '
4856 ui.warn(_('cannot move %i changesets to a higher '
4857 'phase, use --force\n') % len(rejected))
4857 'phase, use --force\n') % len(rejected))
4858 ret = 1
4858 ret = 1
4859 if changes:
4859 if changes:
4860 msg = _('phase changed for %i changesets\n') % changes
4860 msg = _('phase changed for %i changesets\n') % changes
4861 if ret:
4861 if ret:
4862 ui.status(msg)
4862 ui.status(msg)
4863 else:
4863 else:
4864 ui.note(msg)
4864 ui.note(msg)
4865 else:
4865 else:
4866 ui.warn(_('no phases changed\n'))
4866 ui.warn(_('no phases changed\n'))
4867 ret = 1
4867 ret = 1
4868 return ret
4868 return ret
4869
4869
4870 def postincoming(ui, repo, modheads, optupdate, checkout):
4870 def postincoming(ui, repo, modheads, optupdate, checkout):
4871 if modheads == 0:
4871 if modheads == 0:
4872 return
4872 return
4873 if optupdate:
4873 if optupdate:
4874 checkout, movemarkfrom = bookmarks.calculateupdate(ui, repo, checkout)
4874 checkout, movemarkfrom = bookmarks.calculateupdate(ui, repo, checkout)
4875 try:
4875 try:
4876 ret = hg.update(repo, checkout)
4876 ret = hg.update(repo, checkout)
4877 except util.Abort, inst:
4877 except util.Abort, inst:
4878 ui.warn(_("not updating: %s\n") % str(inst))
4878 ui.warn(_("not updating: %s\n") % str(inst))
4879 if inst.hint:
4879 if inst.hint:
4880 ui.warn(_("(%s)\n") % inst.hint)
4880 ui.warn(_("(%s)\n") % inst.hint)
4881 return 0
4881 return 0
4882 if not ret and not checkout:
4882 if not ret and not checkout:
4883 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4883 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
4884 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4884 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
4885 return ret
4885 return ret
4886 if modheads > 1:
4886 if modheads > 1:
4887 currentbranchheads = len(repo.branchheads())
4887 currentbranchheads = len(repo.branchheads())
4888 if currentbranchheads == modheads:
4888 if currentbranchheads == modheads:
4889 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4889 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
4890 elif currentbranchheads > 1:
4890 elif currentbranchheads > 1:
4891 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4891 ui.status(_("(run 'hg heads .' to see heads, 'hg merge' to "
4892 "merge)\n"))
4892 "merge)\n"))
4893 else:
4893 else:
4894 ui.status(_("(run 'hg heads' to see heads)\n"))
4894 ui.status(_("(run 'hg heads' to see heads)\n"))
4895 else:
4895 else:
4896 ui.status(_("(run 'hg update' to get a working copy)\n"))
4896 ui.status(_("(run 'hg update' to get a working copy)\n"))
4897
4897
4898 @command('^pull',
4898 @command('^pull',
4899 [('u', 'update', None,
4899 [('u', 'update', None,
4900 _('update to new branch head if changesets were pulled')),
4900 _('update to new branch head if changesets were pulled')),
4901 ('f', 'force', None, _('run even when remote repository is unrelated')),
4901 ('f', 'force', None, _('run even when remote repository is unrelated')),
4902 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4902 ('r', 'rev', [], _('a remote changeset intended to be added'), _('REV')),
4903 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4903 ('B', 'bookmark', [], _("bookmark to pull"), _('BOOKMARK')),
4904 ('b', 'branch', [], _('a specific branch you would like to pull'),
4904 ('b', 'branch', [], _('a specific branch you would like to pull'),
4905 _('BRANCH')),
4905 _('BRANCH')),
4906 ] + remoteopts,
4906 ] + remoteopts,
4907 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4907 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]'))
4908 def pull(ui, repo, source="default", **opts):
4908 def pull(ui, repo, source="default", **opts):
4909 """pull changes from the specified source
4909 """pull changes from the specified source
4910
4910
4911 Pull changes from a remote repository to a local one.
4911 Pull changes from a remote repository to a local one.
4912
4912
4913 This finds all changes from the repository at the specified path
4913 This finds all changes from the repository at the specified path
4914 or URL and adds them to a local repository (the current one unless
4914 or URL and adds them to a local repository (the current one unless
4915 -R is specified). By default, this does not update the copy of the
4915 -R is specified). By default, this does not update the copy of the
4916 project in the working directory.
4916 project in the working directory.
4917
4917
4918 Use :hg:`incoming` if you want to see what would have been added
4918 Use :hg:`incoming` if you want to see what would have been added
4919 by a pull at the time you issued this command. If you then decide
4919 by a pull at the time you issued this command. If you then decide
4920 to add those changes to the repository, you should use :hg:`pull
4920 to add those changes to the repository, you should use :hg:`pull
4921 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4921 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
4922
4922
4923 If SOURCE is omitted, the 'default' path will be used.
4923 If SOURCE is omitted, the 'default' path will be used.
4924 See :hg:`help urls` for more information.
4924 See :hg:`help urls` for more information.
4925
4925
4926 Returns 0 on success, 1 if an update had unresolved files.
4926 Returns 0 on success, 1 if an update had unresolved files.
4927 """
4927 """
4928 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4928 source, branches = hg.parseurl(ui.expandpath(source), opts.get('branch'))
4929 other = hg.peer(repo, opts, source)
4929 other = hg.peer(repo, opts, source)
4930 try:
4930 try:
4931 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4931 ui.status(_('pulling from %s\n') % util.hidepassword(source))
4932 revs, checkout = hg.addbranchrevs(repo, other, branches,
4932 revs, checkout = hg.addbranchrevs(repo, other, branches,
4933 opts.get('rev'))
4933 opts.get('rev'))
4934
4934
4935 remotebookmarks = other.listkeys('bookmarks')
4935 remotebookmarks = other.listkeys('bookmarks')
4936
4936
4937 if opts.get('bookmark'):
4937 if opts.get('bookmark'):
4938 if not revs:
4938 if not revs:
4939 revs = []
4939 revs = []
4940 for b in opts['bookmark']:
4940 for b in opts['bookmark']:
4941 if b not in remotebookmarks:
4941 if b not in remotebookmarks:
4942 raise util.Abort(_('remote bookmark %s not found!') % b)
4942 raise util.Abort(_('remote bookmark %s not found!') % b)
4943 revs.append(remotebookmarks[b])
4943 revs.append(remotebookmarks[b])
4944
4944
4945 if revs:
4945 if revs:
4946 try:
4946 try:
4947 revs = [other.lookup(rev) for rev in revs]
4947 revs = [other.lookup(rev) for rev in revs]
4948 except error.CapabilityError:
4948 except error.CapabilityError:
4949 err = _("other repository doesn't support revision lookup, "
4949 err = _("other repository doesn't support revision lookup, "
4950 "so a rev cannot be specified.")
4950 "so a rev cannot be specified.")
4951 raise util.Abort(err)
4951 raise util.Abort(err)
4952
4952
4953 modheads = exchange.pull(repo, other, heads=revs,
4953 modheads = exchange.pull(repo, other, heads=revs,
4954 force=opts.get('force'),
4954 force=opts.get('force'),
4955 bookmarks=opts.get('bookmark', ())).cgresult
4955 bookmarks=opts.get('bookmark', ())).cgresult
4956 if checkout:
4956 if checkout:
4957 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4957 checkout = str(repo.changelog.rev(other.lookup(checkout)))
4958 repo._subtoppath = source
4958 repo._subtoppath = source
4959 try:
4959 try:
4960 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4960 ret = postincoming(ui, repo, modheads, opts.get('update'), checkout)
4961
4961
4962 finally:
4962 finally:
4963 del repo._subtoppath
4963 del repo._subtoppath
4964
4964
4965 finally:
4965 finally:
4966 other.close()
4966 other.close()
4967 return ret
4967 return ret
4968
4968
4969 @command('^push',
4969 @command('^push',
4970 [('f', 'force', None, _('force push')),
4970 [('f', 'force', None, _('force push')),
4971 ('r', 'rev', [],
4971 ('r', 'rev', [],
4972 _('a changeset intended to be included in the destination'),
4972 _('a changeset intended to be included in the destination'),
4973 _('REV')),
4973 _('REV')),
4974 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4974 ('B', 'bookmark', [], _("bookmark to push"), _('BOOKMARK')),
4975 ('b', 'branch', [],
4975 ('b', 'branch', [],
4976 _('a specific branch you would like to push'), _('BRANCH')),
4976 _('a specific branch you would like to push'), _('BRANCH')),
4977 ('', 'new-branch', False, _('allow pushing a new branch')),
4977 ('', 'new-branch', False, _('allow pushing a new branch')),
4978 ] + remoteopts,
4978 ] + remoteopts,
4979 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4979 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]'))
4980 def push(ui, repo, dest=None, **opts):
4980 def push(ui, repo, dest=None, **opts):
4981 """push changes to the specified destination
4981 """push changes to the specified destination
4982
4982
4983 Push changesets from the local repository to the specified
4983 Push changesets from the local repository to the specified
4984 destination.
4984 destination.
4985
4985
4986 This operation is symmetrical to pull: it is identical to a pull
4986 This operation is symmetrical to pull: it is identical to a pull
4987 in the destination repository from the current one.
4987 in the destination repository from the current one.
4988
4988
4989 By default, push will not allow creation of new heads at the
4989 By default, push will not allow creation of new heads at the
4990 destination, since multiple heads would make it unclear which head
4990 destination, since multiple heads would make it unclear which head
4991 to use. In this situation, it is recommended to pull and merge
4991 to use. In this situation, it is recommended to pull and merge
4992 before pushing.
4992 before pushing.
4993
4993
4994 Use --new-branch if you want to allow push to create a new named
4994 Use --new-branch if you want to allow push to create a new named
4995 branch that is not present at the destination. This allows you to
4995 branch that is not present at the destination. This allows you to
4996 only create a new branch without forcing other changes.
4996 only create a new branch without forcing other changes.
4997
4997
4998 .. note::
4998 .. note::
4999
4999
5000 Extra care should be taken with the -f/--force option,
5000 Extra care should be taken with the -f/--force option,
5001 which will push all new heads on all branches, an action which will
5001 which will push all new heads on all branches, an action which will
5002 almost always cause confusion for collaborators.
5002 almost always cause confusion for collaborators.
5003
5003
5004 If -r/--rev is used, the specified revision and all its ancestors
5004 If -r/--rev is used, the specified revision and all its ancestors
5005 will be pushed to the remote repository.
5005 will be pushed to the remote repository.
5006
5006
5007 If -B/--bookmark is used, the specified bookmarked revision, its
5007 If -B/--bookmark is used, the specified bookmarked revision, its
5008 ancestors, and the bookmark will be pushed to the remote
5008 ancestors, and the bookmark will be pushed to the remote
5009 repository.
5009 repository.
5010
5010
5011 Please see :hg:`help urls` for important details about ``ssh://``
5011 Please see :hg:`help urls` for important details about ``ssh://``
5012 URLs. If DESTINATION is omitted, a default path will be used.
5012 URLs. If DESTINATION is omitted, a default path will be used.
5013
5013
5014 Returns 0 if push was successful, 1 if nothing to push.
5014 Returns 0 if push was successful, 1 if nothing to push.
5015 """
5015 """
5016
5016
5017 if opts.get('bookmark'):
5017 if opts.get('bookmark'):
5018 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5018 ui.setconfig('bookmarks', 'pushing', opts['bookmark'], 'push')
5019 for b in opts['bookmark']:
5019 for b in opts['bookmark']:
5020 # translate -B options to -r so changesets get pushed
5020 # translate -B options to -r so changesets get pushed
5021 if b in repo._bookmarks:
5021 if b in repo._bookmarks:
5022 opts.setdefault('rev', []).append(b)
5022 opts.setdefault('rev', []).append(b)
5023 else:
5023 else:
5024 # if we try to push a deleted bookmark, translate it to null
5024 # if we try to push a deleted bookmark, translate it to null
5025 # this lets simultaneous -r, -b options continue working
5025 # this lets simultaneous -r, -b options continue working
5026 opts.setdefault('rev', []).append("null")
5026 opts.setdefault('rev', []).append("null")
5027
5027
5028 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5028 dest = ui.expandpath(dest or 'default-push', dest or 'default')
5029 dest, branches = hg.parseurl(dest, opts.get('branch'))
5029 dest, branches = hg.parseurl(dest, opts.get('branch'))
5030 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5030 ui.status(_('pushing to %s\n') % util.hidepassword(dest))
5031 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5031 revs, checkout = hg.addbranchrevs(repo, repo, branches, opts.get('rev'))
5032 try:
5032 try:
5033 other = hg.peer(repo, opts, dest)
5033 other = hg.peer(repo, opts, dest)
5034 except error.RepoError:
5034 except error.RepoError:
5035 if dest == "default-push":
5035 if dest == "default-push":
5036 raise util.Abort(_("default repository not configured!"),
5036 raise util.Abort(_("default repository not configured!"),
5037 hint=_('see the "path" section in "hg help config"'))
5037 hint=_('see the "path" section in "hg help config"'))
5038 else:
5038 else:
5039 raise
5039 raise
5040
5040
5041 if revs:
5041 if revs:
5042 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5042 revs = [repo.lookup(r) for r in scmutil.revrange(repo, revs)]
5043
5043
5044 repo._subtoppath = dest
5044 repo._subtoppath = dest
5045 try:
5045 try:
5046 # push subrepos depth-first for coherent ordering
5046 # push subrepos depth-first for coherent ordering
5047 c = repo['']
5047 c = repo['']
5048 subs = c.substate # only repos that are committed
5048 subs = c.substate # only repos that are committed
5049 for s in sorted(subs):
5049 for s in sorted(subs):
5050 result = c.sub(s).push(opts)
5050 result = c.sub(s).push(opts)
5051 if result == 0:
5051 if result == 0:
5052 return not result
5052 return not result
5053 finally:
5053 finally:
5054 del repo._subtoppath
5054 del repo._subtoppath
5055 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5055 pushop = exchange.push(repo, other, opts.get('force'), revs=revs,
5056 newbranch=opts.get('new_branch'),
5056 newbranch=opts.get('new_branch'),
5057 bookmarks=opts.get('bookmark', ()))
5057 bookmarks=opts.get('bookmark', ()))
5058
5058
5059 result = not pushop.cgresult
5059 result = not pushop.cgresult
5060
5060
5061 if pushop.bkresult is not None:
5061 if pushop.bkresult is not None:
5062 if pushop.bkresult == 2:
5062 if pushop.bkresult == 2:
5063 result = 2
5063 result = 2
5064 elif not result and pushop.bkresult:
5064 elif not result and pushop.bkresult:
5065 result = 2
5065 result = 2
5066
5066
5067 return result
5067 return result
5068
5068
5069 @command('recover', [])
5069 @command('recover', [])
5070 def recover(ui, repo):
5070 def recover(ui, repo):
5071 """roll back an interrupted transaction
5071 """roll back an interrupted transaction
5072
5072
5073 Recover from an interrupted commit or pull.
5073 Recover from an interrupted commit or pull.
5074
5074
5075 This command tries to fix the repository status after an
5075 This command tries to fix the repository status after an
5076 interrupted operation. It should only be necessary when Mercurial
5076 interrupted operation. It should only be necessary when Mercurial
5077 suggests it.
5077 suggests it.
5078
5078
5079 Returns 0 if successful, 1 if nothing to recover or verify fails.
5079 Returns 0 if successful, 1 if nothing to recover or verify fails.
5080 """
5080 """
5081 if repo.recover():
5081 if repo.recover():
5082 return hg.verify(repo)
5082 return hg.verify(repo)
5083 return 1
5083 return 1
5084
5084
5085 @command('^remove|rm',
5085 @command('^remove|rm',
5086 [('A', 'after', None, _('record delete for missing files')),
5086 [('A', 'after', None, _('record delete for missing files')),
5087 ('f', 'force', None,
5087 ('f', 'force', None,
5088 _('remove (and delete) file even if added or modified')),
5088 _('remove (and delete) file even if added or modified')),
5089 ] + walkopts,
5089 ] + walkopts,
5090 _('[OPTION]... FILE...'),
5090 _('[OPTION]... FILE...'),
5091 inferrepo=True)
5091 inferrepo=True)
5092 def remove(ui, repo, *pats, **opts):
5092 def remove(ui, repo, *pats, **opts):
5093 """remove the specified files on the next commit
5093 """remove the specified files on the next commit
5094
5094
5095 Schedule the indicated files for removal from the current branch.
5095 Schedule the indicated files for removal from the current branch.
5096
5096
5097 This command schedules the files to be removed at the next commit.
5097 This command schedules the files to be removed at the next commit.
5098 To undo a remove before that, see :hg:`revert`. To undo added
5098 To undo a remove before that, see :hg:`revert`. To undo added
5099 files, see :hg:`forget`.
5099 files, see :hg:`forget`.
5100
5100
5101 .. container:: verbose
5101 .. container:: verbose
5102
5102
5103 -A/--after can be used to remove only files that have already
5103 -A/--after can be used to remove only files that have already
5104 been deleted, -f/--force can be used to force deletion, and -Af
5104 been deleted, -f/--force can be used to force deletion, and -Af
5105 can be used to remove files from the next revision without
5105 can be used to remove files from the next revision without
5106 deleting them from the working directory.
5106 deleting them from the working directory.
5107
5107
5108 The following table details the behavior of remove for different
5108 The following table details the behavior of remove for different
5109 file states (columns) and option combinations (rows). The file
5109 file states (columns) and option combinations (rows). The file
5110 states are Added [A], Clean [C], Modified [M] and Missing [!]
5110 states are Added [A], Clean [C], Modified [M] and Missing [!]
5111 (as reported by :hg:`status`). The actions are Warn, Remove
5111 (as reported by :hg:`status`). The actions are Warn, Remove
5112 (from branch) and Delete (from disk):
5112 (from branch) and Delete (from disk):
5113
5113
5114 ========= == == == ==
5114 ========= == == == ==
5115 opt/state A C M !
5115 opt/state A C M !
5116 ========= == == == ==
5116 ========= == == == ==
5117 none W RD W R
5117 none W RD W R
5118 -f R RD RD R
5118 -f R RD RD R
5119 -A W W W R
5119 -A W W W R
5120 -Af R R R R
5120 -Af R R R R
5121 ========= == == == ==
5121 ========= == == == ==
5122
5122
5123 Note that remove never deletes files in Added [A] state from the
5123 Note that remove never deletes files in Added [A] state from the
5124 working directory, not even if option --force is specified.
5124 working directory, not even if option --force is specified.
5125
5125
5126 Returns 0 on success, 1 if any warnings encountered.
5126 Returns 0 on success, 1 if any warnings encountered.
5127 """
5127 """
5128
5128
5129 after, force = opts.get('after'), opts.get('force')
5129 after, force = opts.get('after'), opts.get('force')
5130 if not pats and not after:
5130 if not pats and not after:
5131 raise util.Abort(_('no files specified'))
5131 raise util.Abort(_('no files specified'))
5132
5132
5133 m = scmutil.match(repo[None], pats, opts)
5133 m = scmutil.match(repo[None], pats, opts)
5134 return cmdutil.remove(ui, repo, m, after, force)
5134 return cmdutil.remove(ui, repo, m, after, force)
5135
5135
5136 @command('rename|move|mv',
5136 @command('rename|move|mv',
5137 [('A', 'after', None, _('record a rename that has already occurred')),
5137 [('A', 'after', None, _('record a rename that has already occurred')),
5138 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5138 ('f', 'force', None, _('forcibly copy over an existing managed file')),
5139 ] + walkopts + dryrunopts,
5139 ] + walkopts + dryrunopts,
5140 _('[OPTION]... SOURCE... DEST'))
5140 _('[OPTION]... SOURCE... DEST'))
5141 def rename(ui, repo, *pats, **opts):
5141 def rename(ui, repo, *pats, **opts):
5142 """rename files; equivalent of copy + remove
5142 """rename files; equivalent of copy + remove
5143
5143
5144 Mark dest as copies of sources; mark sources for deletion. If dest
5144 Mark dest as copies of sources; mark sources for deletion. If dest
5145 is a directory, copies are put in that directory. If dest is a
5145 is a directory, copies are put in that directory. If dest is a
5146 file, there can only be one source.
5146 file, there can only be one source.
5147
5147
5148 By default, this command copies the contents of files as they
5148 By default, this command copies the contents of files as they
5149 exist in the working directory. If invoked with -A/--after, the
5149 exist in the working directory. If invoked with -A/--after, the
5150 operation is recorded, but no copying is performed.
5150 operation is recorded, but no copying is performed.
5151
5151
5152 This command takes effect at the next commit. To undo a rename
5152 This command takes effect at the next commit. To undo a rename
5153 before that, see :hg:`revert`.
5153 before that, see :hg:`revert`.
5154
5154
5155 Returns 0 on success, 1 if errors are encountered.
5155 Returns 0 on success, 1 if errors are encountered.
5156 """
5156 """
5157 wlock = repo.wlock(False)
5157 wlock = repo.wlock(False)
5158 try:
5158 try:
5159 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5159 return cmdutil.copy(ui, repo, pats, opts, rename=True)
5160 finally:
5160 finally:
5161 wlock.release()
5161 wlock.release()
5162
5162
5163 @command('resolve',
5163 @command('resolve',
5164 [('a', 'all', None, _('select all unresolved files')),
5164 [('a', 'all', None, _('select all unresolved files')),
5165 ('l', 'list', None, _('list state of files needing merge')),
5165 ('l', 'list', None, _('list state of files needing merge')),
5166 ('m', 'mark', None, _('mark files as resolved')),
5166 ('m', 'mark', None, _('mark files as resolved')),
5167 ('u', 'unmark', None, _('mark files as unresolved')),
5167 ('u', 'unmark', None, _('mark files as unresolved')),
5168 ('n', 'no-status', None, _('hide status prefix'))]
5168 ('n', 'no-status', None, _('hide status prefix'))]
5169 + mergetoolopts + walkopts,
5169 + mergetoolopts + walkopts,
5170 _('[OPTION]... [FILE]...'),
5170 _('[OPTION]... [FILE]...'),
5171 inferrepo=True)
5171 inferrepo=True)
5172 def resolve(ui, repo, *pats, **opts):
5172 def resolve(ui, repo, *pats, **opts):
5173 """redo merges or set/view the merge status of files
5173 """redo merges or set/view the merge status of files
5174
5174
5175 Merges with unresolved conflicts are often the result of
5175 Merges with unresolved conflicts are often the result of
5176 non-interactive merging using the ``internal:merge`` configuration
5176 non-interactive merging using the ``internal:merge`` configuration
5177 setting, or a command-line merge tool like ``diff3``. The resolve
5177 setting, or a command-line merge tool like ``diff3``. The resolve
5178 command is used to manage the files involved in a merge, after
5178 command is used to manage the files involved in a merge, after
5179 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5179 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5180 working directory must have two parents). See :hg:`help
5180 working directory must have two parents). See :hg:`help
5181 merge-tools` for information on configuring merge tools.
5181 merge-tools` for information on configuring merge tools.
5182
5182
5183 The resolve command can be used in the following ways:
5183 The resolve command can be used in the following ways:
5184
5184
5185 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5185 - :hg:`resolve [--tool TOOL] FILE...`: attempt to re-merge the specified
5186 files, discarding any previous merge attempts. Re-merging is not
5186 files, discarding any previous merge attempts. Re-merging is not
5187 performed for files already marked as resolved. Use ``--all/-a``
5187 performed for files already marked as resolved. Use ``--all/-a``
5188 to select all unresolved files. ``--tool`` can be used to specify
5188 to select all unresolved files. ``--tool`` can be used to specify
5189 the merge tool used for the given files. It overrides the HGMERGE
5189 the merge tool used for the given files. It overrides the HGMERGE
5190 environment variable and your configuration files. Previous file
5190 environment variable and your configuration files. Previous file
5191 contents are saved with a ``.orig`` suffix.
5191 contents are saved with a ``.orig`` suffix.
5192
5192
5193 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5193 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5194 (e.g. after having manually fixed-up the files). The default is
5194 (e.g. after having manually fixed-up the files). The default is
5195 to mark all unresolved files.
5195 to mark all unresolved files.
5196
5196
5197 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5197 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5198 default is to mark all resolved files.
5198 default is to mark all resolved files.
5199
5199
5200 - :hg:`resolve -l`: list files which had or still have conflicts.
5200 - :hg:`resolve -l`: list files which had or still have conflicts.
5201 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5201 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5202
5202
5203 Note that Mercurial will not let you commit files with unresolved
5203 Note that Mercurial will not let you commit files with unresolved
5204 merge conflicts. You must use :hg:`resolve -m ...` before you can
5204 merge conflicts. You must use :hg:`resolve -m ...` before you can
5205 commit after a conflicting merge.
5205 commit after a conflicting merge.
5206
5206
5207 Returns 0 on success, 1 if any files fail a resolve attempt.
5207 Returns 0 on success, 1 if any files fail a resolve attempt.
5208 """
5208 """
5209
5209
5210 all, mark, unmark, show, nostatus = \
5210 all, mark, unmark, show, nostatus = \
5211 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
5211 [opts.get(o) for o in 'all mark unmark list no_status'.split()]
5212
5212
5213 if (show and (mark or unmark)) or (mark and unmark):
5213 if (show and (mark or unmark)) or (mark and unmark):
5214 raise util.Abort(_("too many options specified"))
5214 raise util.Abort(_("too many options specified"))
5215 if pats and all:
5215 if pats and all:
5216 raise util.Abort(_("can't specify --all and patterns"))
5216 raise util.Abort(_("can't specify --all and patterns"))
5217 if not (all or pats or show or mark or unmark):
5217 if not (all or pats or show or mark or unmark):
5218 raise util.Abort(_('no files or directories specified'),
5218 raise util.Abort(_('no files or directories specified'),
5219 hint=('use --all to remerge all files'))
5219 hint=('use --all to remerge all files'))
5220
5220
5221 wlock = repo.wlock()
5221 wlock = repo.wlock()
5222 try:
5222 try:
5223 ms = mergemod.mergestate(repo)
5223 ms = mergemod.mergestate(repo)
5224
5224
5225 if not (ms.active() or repo.dirstate.p2() != nullid) and not show:
5225 if not (ms.active() or repo.dirstate.p2() != nullid) and not show:
5226 raise util.Abort(
5226 raise util.Abort(
5227 _('resolve command not applicable when not merging'))
5227 _('resolve command not applicable when not merging'))
5228
5228
5229 m = scmutil.match(repo[None], pats, opts)
5229 m = scmutil.match(repo[None], pats, opts)
5230 ret = 0
5230 ret = 0
5231 didwork = False
5231 didwork = False
5232
5232
5233 for f in ms:
5233 for f in ms:
5234 if not m(f):
5234 if not m(f):
5235 continue
5235 continue
5236
5236
5237 didwork = True
5237 didwork = True
5238
5238
5239 if show:
5239 if show:
5240 if nostatus:
5240 if nostatus:
5241 ui.write("%s\n" % f)
5241 ui.write("%s\n" % f)
5242 else:
5242 else:
5243 ui.write("%s %s\n" % (ms[f].upper(), f),
5243 ui.write("%s %s\n" % (ms[f].upper(), f),
5244 label='resolve.' +
5244 label='resolve.' +
5245 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
5245 {'u': 'unresolved', 'r': 'resolved'}[ms[f]])
5246 elif mark:
5246 elif mark:
5247 ms.mark(f, "r")
5247 ms.mark(f, "r")
5248 elif unmark:
5248 elif unmark:
5249 ms.mark(f, "u")
5249 ms.mark(f, "u")
5250 else:
5250 else:
5251 wctx = repo[None]
5251 wctx = repo[None]
5252
5252
5253 # backup pre-resolve (merge uses .orig for its own purposes)
5253 # backup pre-resolve (merge uses .orig for its own purposes)
5254 a = repo.wjoin(f)
5254 a = repo.wjoin(f)
5255 util.copyfile(a, a + ".resolve")
5255 util.copyfile(a, a + ".resolve")
5256
5256
5257 try:
5257 try:
5258 # resolve file
5258 # resolve file
5259 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5259 ui.setconfig('ui', 'forcemerge', opts.get('tool', ''),
5260 'resolve')
5260 'resolve')
5261 if ms.resolve(f, wctx):
5261 if ms.resolve(f, wctx):
5262 ret = 1
5262 ret = 1
5263 finally:
5263 finally:
5264 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5264 ui.setconfig('ui', 'forcemerge', '', 'resolve')
5265 ms.commit()
5265 ms.commit()
5266
5266
5267 # replace filemerge's .orig file with our resolve file
5267 # replace filemerge's .orig file with our resolve file
5268 util.rename(a + ".resolve", a + ".orig")
5268 util.rename(a + ".resolve", a + ".orig")
5269
5269
5270 ms.commit()
5270 ms.commit()
5271
5271
5272 if not didwork and pats:
5272 if not didwork and pats:
5273 ui.warn(_("arguments do not match paths that need resolving\n"))
5273 ui.warn(_("arguments do not match paths that need resolving\n"))
5274
5274
5275 finally:
5275 finally:
5276 wlock.release()
5276 wlock.release()
5277
5277
5278 # Nudge users into finishing an unfinished operation. We don't print
5278 # Nudge users into finishing an unfinished operation. We don't print
5279 # this with the list/show operation because we want list/show to remain
5279 # this with the list/show operation because we want list/show to remain
5280 # machine readable.
5280 # machine readable.
5281 if not list(ms.unresolved()) and not show:
5281 if not list(ms.unresolved()) and not show:
5282 ui.status(_('(no more unresolved files)\n'))
5282 ui.status(_('(no more unresolved files)\n'))
5283
5283
5284 return ret
5284 return ret
5285
5285
5286 @command('revert',
5286 @command('revert',
5287 [('a', 'all', None, _('revert all changes when no arguments given')),
5287 [('a', 'all', None, _('revert all changes when no arguments given')),
5288 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5288 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
5289 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5289 ('r', 'rev', '', _('revert to the specified revision'), _('REV')),
5290 ('C', 'no-backup', None, _('do not save backup copies of files')),
5290 ('C', 'no-backup', None, _('do not save backup copies of files')),
5291 ] + walkopts + dryrunopts,
5291 ] + walkopts + dryrunopts,
5292 _('[OPTION]... [-r REV] [NAME]...'))
5292 _('[OPTION]... [-r REV] [NAME]...'))
5293 def revert(ui, repo, *pats, **opts):
5293 def revert(ui, repo, *pats, **opts):
5294 """restore files to their checkout state
5294 """restore files to their checkout state
5295
5295
5296 .. note::
5296 .. note::
5297
5297
5298 To check out earlier revisions, you should use :hg:`update REV`.
5298 To check out earlier revisions, you should use :hg:`update REV`.
5299 To cancel an uncommitted merge (and lose your changes),
5299 To cancel an uncommitted merge (and lose your changes),
5300 use :hg:`update --clean .`.
5300 use :hg:`update --clean .`.
5301
5301
5302 With no revision specified, revert the specified files or directories
5302 With no revision specified, revert the specified files or directories
5303 to the contents they had in the parent of the working directory.
5303 to the contents they had in the parent of the working directory.
5304 This restores the contents of files to an unmodified
5304 This restores the contents of files to an unmodified
5305 state and unschedules adds, removes, copies, and renames. If the
5305 state and unschedules adds, removes, copies, and renames. If the
5306 working directory has two parents, you must explicitly specify a
5306 working directory has two parents, you must explicitly specify a
5307 revision.
5307 revision.
5308
5308
5309 Using the -r/--rev or -d/--date options, revert the given files or
5309 Using the -r/--rev or -d/--date options, revert the given files or
5310 directories to their states as of a specific revision. Because
5310 directories to their states as of a specific revision. Because
5311 revert does not change the working directory parents, this will
5311 revert does not change the working directory parents, this will
5312 cause these files to appear modified. This can be helpful to "back
5312 cause these files to appear modified. This can be helpful to "back
5313 out" some or all of an earlier change. See :hg:`backout` for a
5313 out" some or all of an earlier change. See :hg:`backout` for a
5314 related method.
5314 related method.
5315
5315
5316 Modified files are saved with a .orig suffix before reverting.
5316 Modified files are saved with a .orig suffix before reverting.
5317 To disable these backups, use --no-backup.
5317 To disable these backups, use --no-backup.
5318
5318
5319 See :hg:`help dates` for a list of formats valid for -d/--date.
5319 See :hg:`help dates` for a list of formats valid for -d/--date.
5320
5320
5321 Returns 0 on success.
5321 Returns 0 on success.
5322 """
5322 """
5323
5323
5324 if opts.get("date"):
5324 if opts.get("date"):
5325 if opts.get("rev"):
5325 if opts.get("rev"):
5326 raise util.Abort(_("you can't specify a revision and a date"))
5326 raise util.Abort(_("you can't specify a revision and a date"))
5327 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5327 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
5328
5328
5329 parent, p2 = repo.dirstate.parents()
5329 parent, p2 = repo.dirstate.parents()
5330 if not opts.get('rev') and p2 != nullid:
5330 if not opts.get('rev') and p2 != nullid:
5331 # revert after merge is a trap for new users (issue2915)
5331 # revert after merge is a trap for new users (issue2915)
5332 raise util.Abort(_('uncommitted merge with no revision specified'),
5332 raise util.Abort(_('uncommitted merge with no revision specified'),
5333 hint=_('use "hg update" or see "hg help revert"'))
5333 hint=_('use "hg update" or see "hg help revert"'))
5334
5334
5335 ctx = scmutil.revsingle(repo, opts.get('rev'))
5335 ctx = scmutil.revsingle(repo, opts.get('rev'))
5336
5336
5337 if not pats and not opts.get('all'):
5337 if not pats and not opts.get('all'):
5338 msg = _("no files or directories specified")
5338 msg = _("no files or directories specified")
5339 if p2 != nullid:
5339 if p2 != nullid:
5340 hint = _("uncommitted merge, use --all to discard all changes,"
5340 hint = _("uncommitted merge, use --all to discard all changes,"
5341 " or 'hg update -C .' to abort the merge")
5341 " or 'hg update -C .' to abort the merge")
5342 raise util.Abort(msg, hint=hint)
5342 raise util.Abort(msg, hint=hint)
5343 dirty = util.any(repo.status())
5343 dirty = util.any(repo.status())
5344 node = ctx.node()
5344 node = ctx.node()
5345 if node != parent:
5345 if node != parent:
5346 if dirty:
5346 if dirty:
5347 hint = _("uncommitted changes, use --all to discard all"
5347 hint = _("uncommitted changes, use --all to discard all"
5348 " changes, or 'hg update %s' to update") % ctx.rev()
5348 " changes, or 'hg update %s' to update") % ctx.rev()
5349 else:
5349 else:
5350 hint = _("use --all to revert all files,"
5350 hint = _("use --all to revert all files,"
5351 " or 'hg update %s' to update") % ctx.rev()
5351 " or 'hg update %s' to update") % ctx.rev()
5352 elif dirty:
5352 elif dirty:
5353 hint = _("uncommitted changes, use --all to discard all changes")
5353 hint = _("uncommitted changes, use --all to discard all changes")
5354 else:
5354 else:
5355 hint = _("use --all to revert all files")
5355 hint = _("use --all to revert all files")
5356 raise util.Abort(msg, hint=hint)
5356 raise util.Abort(msg, hint=hint)
5357
5357
5358 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5358 return cmdutil.revert(ui, repo, ctx, (parent, p2), *pats, **opts)
5359
5359
5360 @command('rollback', dryrunopts +
5360 @command('rollback', dryrunopts +
5361 [('f', 'force', False, _('ignore safety measures'))])
5361 [('f', 'force', False, _('ignore safety measures'))])
5362 def rollback(ui, repo, **opts):
5362 def rollback(ui, repo, **opts):
5363 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5363 """roll back the last transaction (DANGEROUS) (DEPRECATED)
5364
5364
5365 Please use :hg:`commit --amend` instead of rollback to correct
5365 Please use :hg:`commit --amend` instead of rollback to correct
5366 mistakes in the last commit.
5366 mistakes in the last commit.
5367
5367
5368 This command should be used with care. There is only one level of
5368 This command should be used with care. There is only one level of
5369 rollback, and there is no way to undo a rollback. It will also
5369 rollback, and there is no way to undo a rollback. It will also
5370 restore the dirstate at the time of the last transaction, losing
5370 restore the dirstate at the time of the last transaction, losing
5371 any dirstate changes since that time. This command does not alter
5371 any dirstate changes since that time. This command does not alter
5372 the working directory.
5372 the working directory.
5373
5373
5374 Transactions are used to encapsulate the effects of all commands
5374 Transactions are used to encapsulate the effects of all commands
5375 that create new changesets or propagate existing changesets into a
5375 that create new changesets or propagate existing changesets into a
5376 repository.
5376 repository.
5377
5377
5378 .. container:: verbose
5378 .. container:: verbose
5379
5379
5380 For example, the following commands are transactional, and their
5380 For example, the following commands are transactional, and their
5381 effects can be rolled back:
5381 effects can be rolled back:
5382
5382
5383 - commit
5383 - commit
5384 - import
5384 - import
5385 - pull
5385 - pull
5386 - push (with this repository as the destination)
5386 - push (with this repository as the destination)
5387 - unbundle
5387 - unbundle
5388
5388
5389 To avoid permanent data loss, rollback will refuse to rollback a
5389 To avoid permanent data loss, rollback will refuse to rollback a
5390 commit transaction if it isn't checked out. Use --force to
5390 commit transaction if it isn't checked out. Use --force to
5391 override this protection.
5391 override this protection.
5392
5392
5393 This command is not intended for use on public repositories. Once
5393 This command is not intended for use on public repositories. Once
5394 changes are visible for pull by other users, rolling a transaction
5394 changes are visible for pull by other users, rolling a transaction
5395 back locally is ineffective (someone else may already have pulled
5395 back locally is ineffective (someone else may already have pulled
5396 the changes). Furthermore, a race is possible with readers of the
5396 the changes). Furthermore, a race is possible with readers of the
5397 repository; for example an in-progress pull from the repository
5397 repository; for example an in-progress pull from the repository
5398 may fail if a rollback is performed.
5398 may fail if a rollback is performed.
5399
5399
5400 Returns 0 on success, 1 if no rollback data is available.
5400 Returns 0 on success, 1 if no rollback data is available.
5401 """
5401 """
5402 return repo.rollback(dryrun=opts.get('dry_run'),
5402 return repo.rollback(dryrun=opts.get('dry_run'),
5403 force=opts.get('force'))
5403 force=opts.get('force'))
5404
5404
5405 @command('root', [])
5405 @command('root', [])
5406 def root(ui, repo):
5406 def root(ui, repo):
5407 """print the root (top) of the current working directory
5407 """print the root (top) of the current working directory
5408
5408
5409 Print the root directory of the current repository.
5409 Print the root directory of the current repository.
5410
5410
5411 Returns 0 on success.
5411 Returns 0 on success.
5412 """
5412 """
5413 ui.write(repo.root + "\n")
5413 ui.write(repo.root + "\n")
5414
5414
5415 @command('^serve',
5415 @command('^serve',
5416 [('A', 'accesslog', '', _('name of access log file to write to'),
5416 [('A', 'accesslog', '', _('name of access log file to write to'),
5417 _('FILE')),
5417 _('FILE')),
5418 ('d', 'daemon', None, _('run server in background')),
5418 ('d', 'daemon', None, _('run server in background')),
5419 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('FILE')),
5419 ('', 'daemon-pipefds', '', _('used internally by daemon mode'), _('FILE')),
5420 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5420 ('E', 'errorlog', '', _('name of error log file to write to'), _('FILE')),
5421 # use string type, then we can check if something was passed
5421 # use string type, then we can check if something was passed
5422 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5422 ('p', 'port', '', _('port to listen on (default: 8000)'), _('PORT')),
5423 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5423 ('a', 'address', '', _('address to listen on (default: all interfaces)'),
5424 _('ADDR')),
5424 _('ADDR')),
5425 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5425 ('', 'prefix', '', _('prefix path to serve from (default: server root)'),
5426 _('PREFIX')),
5426 _('PREFIX')),
5427 ('n', 'name', '',
5427 ('n', 'name', '',
5428 _('name to show in web pages (default: working directory)'), _('NAME')),
5428 _('name to show in web pages (default: working directory)'), _('NAME')),
5429 ('', 'web-conf', '',
5429 ('', 'web-conf', '',
5430 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5430 _('name of the hgweb config file (see "hg help hgweb")'), _('FILE')),
5431 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5431 ('', 'webdir-conf', '', _('name of the hgweb config file (DEPRECATED)'),
5432 _('FILE')),
5432 _('FILE')),
5433 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5433 ('', 'pid-file', '', _('name of file to write process ID to'), _('FILE')),
5434 ('', 'stdio', None, _('for remote clients')),
5434 ('', 'stdio', None, _('for remote clients')),
5435 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5435 ('', 'cmdserver', '', _('for remote clients'), _('MODE')),
5436 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5436 ('t', 'templates', '', _('web templates to use'), _('TEMPLATE')),
5437 ('', 'style', '', _('template style to use'), _('STYLE')),
5437 ('', 'style', '', _('template style to use'), _('STYLE')),
5438 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5438 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
5439 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5439 ('', 'certificate', '', _('SSL certificate file'), _('FILE'))],
5440 _('[OPTION]...'),
5440 _('[OPTION]...'),
5441 optionalrepo=True)
5441 optionalrepo=True)
5442 def serve(ui, repo, **opts):
5442 def serve(ui, repo, **opts):
5443 """start stand-alone webserver
5443 """start stand-alone webserver
5444
5444
5445 Start a local HTTP repository browser and pull server. You can use
5445 Start a local HTTP repository browser and pull server. You can use
5446 this for ad-hoc sharing and browsing of repositories. It is
5446 this for ad-hoc sharing and browsing of repositories. It is
5447 recommended to use a real web server to serve a repository for
5447 recommended to use a real web server to serve a repository for
5448 longer periods of time.
5448 longer periods of time.
5449
5449
5450 Please note that the server does not implement access control.
5450 Please note that the server does not implement access control.
5451 This means that, by default, anybody can read from the server and
5451 This means that, by default, anybody can read from the server and
5452 nobody can write to it by default. Set the ``web.allow_push``
5452 nobody can write to it by default. Set the ``web.allow_push``
5453 option to ``*`` to allow everybody to push to the server. You
5453 option to ``*`` to allow everybody to push to the server. You
5454 should use a real web server if you need to authenticate users.
5454 should use a real web server if you need to authenticate users.
5455
5455
5456 By default, the server logs accesses to stdout and errors to
5456 By default, the server logs accesses to stdout and errors to
5457 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5457 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
5458 files.
5458 files.
5459
5459
5460 To have the server choose a free port number to listen on, specify
5460 To have the server choose a free port number to listen on, specify
5461 a port number of 0; in this case, the server will print the port
5461 a port number of 0; in this case, the server will print the port
5462 number it uses.
5462 number it uses.
5463
5463
5464 Returns 0 on success.
5464 Returns 0 on success.
5465 """
5465 """
5466
5466
5467 if opts["stdio"] and opts["cmdserver"]:
5467 if opts["stdio"] and opts["cmdserver"]:
5468 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5468 raise util.Abort(_("cannot use --stdio with --cmdserver"))
5469
5469
5470 if opts["stdio"]:
5470 if opts["stdio"]:
5471 if repo is None:
5471 if repo is None:
5472 raise error.RepoError(_("there is no Mercurial repository here"
5472 raise error.RepoError(_("there is no Mercurial repository here"
5473 " (.hg not found)"))
5473 " (.hg not found)"))
5474 s = sshserver.sshserver(ui, repo)
5474 s = sshserver.sshserver(ui, repo)
5475 s.serve_forever()
5475 s.serve_forever()
5476
5476
5477 if opts["cmdserver"]:
5477 if opts["cmdserver"]:
5478 service = commandserver.createservice(ui, repo, opts)
5478 service = commandserver.createservice(ui, repo, opts)
5479 return cmdutil.service(opts, initfn=service.init, runfn=service.run)
5479 return cmdutil.service(opts, initfn=service.init, runfn=service.run)
5480
5480
5481 # this way we can check if something was given in the command-line
5481 # this way we can check if something was given in the command-line
5482 if opts.get('port'):
5482 if opts.get('port'):
5483 opts['port'] = util.getport(opts.get('port'))
5483 opts['port'] = util.getport(opts.get('port'))
5484
5484
5485 baseui = repo and repo.baseui or ui
5485 baseui = repo and repo.baseui or ui
5486 optlist = ("name templates style address port prefix ipv6"
5486 optlist = ("name templates style address port prefix ipv6"
5487 " accesslog errorlog certificate encoding")
5487 " accesslog errorlog certificate encoding")
5488 for o in optlist.split():
5488 for o in optlist.split():
5489 val = opts.get(o, '')
5489 val = opts.get(o, '')
5490 if val in (None, ''): # should check against default options instead
5490 if val in (None, ''): # should check against default options instead
5491 continue
5491 continue
5492 baseui.setconfig("web", o, val, 'serve')
5492 baseui.setconfig("web", o, val, 'serve')
5493 if repo and repo.ui != baseui:
5493 if repo and repo.ui != baseui:
5494 repo.ui.setconfig("web", o, val, 'serve')
5494 repo.ui.setconfig("web", o, val, 'serve')
5495
5495
5496 o = opts.get('web_conf') or opts.get('webdir_conf')
5496 o = opts.get('web_conf') or opts.get('webdir_conf')
5497 if not o:
5497 if not o:
5498 if not repo:
5498 if not repo:
5499 raise error.RepoError(_("there is no Mercurial repository"
5499 raise error.RepoError(_("there is no Mercurial repository"
5500 " here (.hg not found)"))
5500 " here (.hg not found)"))
5501 o = repo
5501 o = repo
5502
5502
5503 app = hgweb.hgweb(o, baseui=baseui)
5503 app = hgweb.hgweb(o, baseui=baseui)
5504 service = httpservice(ui, app, opts)
5504 service = httpservice(ui, app, opts)
5505 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5505 cmdutil.service(opts, initfn=service.init, runfn=service.run)
5506
5506
5507 class httpservice(object):
5507 class httpservice(object):
5508 def __init__(self, ui, app, opts):
5508 def __init__(self, ui, app, opts):
5509 self.ui = ui
5509 self.ui = ui
5510 self.app = app
5510 self.app = app
5511 self.opts = opts
5511 self.opts = opts
5512
5512
5513 def init(self):
5513 def init(self):
5514 util.setsignalhandler()
5514 util.setsignalhandler()
5515 self.httpd = hgweb_server.create_server(self.ui, self.app)
5515 self.httpd = hgweb_server.create_server(self.ui, self.app)
5516
5516
5517 if self.opts['port'] and not self.ui.verbose:
5517 if self.opts['port'] and not self.ui.verbose:
5518 return
5518 return
5519
5519
5520 if self.httpd.prefix:
5520 if self.httpd.prefix:
5521 prefix = self.httpd.prefix.strip('/') + '/'
5521 prefix = self.httpd.prefix.strip('/') + '/'
5522 else:
5522 else:
5523 prefix = ''
5523 prefix = ''
5524
5524
5525 port = ':%d' % self.httpd.port
5525 port = ':%d' % self.httpd.port
5526 if port == ':80':
5526 if port == ':80':
5527 port = ''
5527 port = ''
5528
5528
5529 bindaddr = self.httpd.addr
5529 bindaddr = self.httpd.addr
5530 if bindaddr == '0.0.0.0':
5530 if bindaddr == '0.0.0.0':
5531 bindaddr = '*'
5531 bindaddr = '*'
5532 elif ':' in bindaddr: # IPv6
5532 elif ':' in bindaddr: # IPv6
5533 bindaddr = '[%s]' % bindaddr
5533 bindaddr = '[%s]' % bindaddr
5534
5534
5535 fqaddr = self.httpd.fqaddr
5535 fqaddr = self.httpd.fqaddr
5536 if ':' in fqaddr:
5536 if ':' in fqaddr:
5537 fqaddr = '[%s]' % fqaddr
5537 fqaddr = '[%s]' % fqaddr
5538 if self.opts['port']:
5538 if self.opts['port']:
5539 write = self.ui.status
5539 write = self.ui.status
5540 else:
5540 else:
5541 write = self.ui.write
5541 write = self.ui.write
5542 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5542 write(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
5543 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5543 (fqaddr, port, prefix, bindaddr, self.httpd.port))
5544 self.ui.flush() # avoid buffering of status message
5544 self.ui.flush() # avoid buffering of status message
5545
5545
5546 def run(self):
5546 def run(self):
5547 self.httpd.serve_forever()
5547 self.httpd.serve_forever()
5548
5548
5549
5549
5550 @command('^status|st',
5550 @command('^status|st',
5551 [('A', 'all', None, _('show status of all files')),
5551 [('A', 'all', None, _('show status of all files')),
5552 ('m', 'modified', None, _('show only modified files')),
5552 ('m', 'modified', None, _('show only modified files')),
5553 ('a', 'added', None, _('show only added files')),
5553 ('a', 'added', None, _('show only added files')),
5554 ('r', 'removed', None, _('show only removed files')),
5554 ('r', 'removed', None, _('show only removed files')),
5555 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5555 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
5556 ('c', 'clean', None, _('show only files without changes')),
5556 ('c', 'clean', None, _('show only files without changes')),
5557 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5557 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
5558 ('i', 'ignored', None, _('show only ignored files')),
5558 ('i', 'ignored', None, _('show only ignored files')),
5559 ('n', 'no-status', None, _('hide status prefix')),
5559 ('n', 'no-status', None, _('hide status prefix')),
5560 ('C', 'copies', None, _('show source of copied files')),
5560 ('C', 'copies', None, _('show source of copied files')),
5561 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5561 ('0', 'print0', None, _('end filenames with NUL, for use with xargs')),
5562 ('', 'rev', [], _('show difference from revision'), _('REV')),
5562 ('', 'rev', [], _('show difference from revision'), _('REV')),
5563 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5563 ('', 'change', '', _('list the changed files of a revision'), _('REV')),
5564 ] + walkopts + subrepoopts + formatteropts,
5564 ] + walkopts + subrepoopts + formatteropts,
5565 _('[OPTION]... [FILE]...'),
5565 _('[OPTION]... [FILE]...'),
5566 inferrepo=True)
5566 inferrepo=True)
5567 def status(ui, repo, *pats, **opts):
5567 def status(ui, repo, *pats, **opts):
5568 """show changed files in the working directory
5568 """show changed files in the working directory
5569
5569
5570 Show status of files in the repository. If names are given, only
5570 Show status of files in the repository. If names are given, only
5571 files that match are shown. Files that are clean or ignored or
5571 files that match are shown. Files that are clean or ignored or
5572 the source of a copy/move operation, are not listed unless
5572 the source of a copy/move operation, are not listed unless
5573 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5573 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
5574 Unless options described with "show only ..." are given, the
5574 Unless options described with "show only ..." are given, the
5575 options -mardu are used.
5575 options -mardu are used.
5576
5576
5577 Option -q/--quiet hides untracked (unknown and ignored) files
5577 Option -q/--quiet hides untracked (unknown and ignored) files
5578 unless explicitly requested with -u/--unknown or -i/--ignored.
5578 unless explicitly requested with -u/--unknown or -i/--ignored.
5579
5579
5580 .. note::
5580 .. note::
5581
5581
5582 status may appear to disagree with diff if permissions have
5582 status may appear to disagree with diff if permissions have
5583 changed or a merge has occurred. The standard diff format does
5583 changed or a merge has occurred. The standard diff format does
5584 not report permission changes and diff only reports changes
5584 not report permission changes and diff only reports changes
5585 relative to one merge parent.
5585 relative to one merge parent.
5586
5586
5587 If one revision is given, it is used as the base revision.
5587 If one revision is given, it is used as the base revision.
5588 If two revisions are given, the differences between them are
5588 If two revisions are given, the differences between them are
5589 shown. The --change option can also be used as a shortcut to list
5589 shown. The --change option can also be used as a shortcut to list
5590 the changed files of a revision from its first parent.
5590 the changed files of a revision from its first parent.
5591
5591
5592 The codes used to show the status of files are::
5592 The codes used to show the status of files are::
5593
5593
5594 M = modified
5594 M = modified
5595 A = added
5595 A = added
5596 R = removed
5596 R = removed
5597 C = clean
5597 C = clean
5598 ! = missing (deleted by non-hg command, but still tracked)
5598 ! = missing (deleted by non-hg command, but still tracked)
5599 ? = not tracked
5599 ? = not tracked
5600 I = ignored
5600 I = ignored
5601 = origin of the previous file (with --copies)
5601 = origin of the previous file (with --copies)
5602
5602
5603 .. container:: verbose
5603 .. container:: verbose
5604
5604
5605 Examples:
5605 Examples:
5606
5606
5607 - show changes in the working directory relative to a
5607 - show changes in the working directory relative to a
5608 changeset::
5608 changeset::
5609
5609
5610 hg status --rev 9353
5610 hg status --rev 9353
5611
5611
5612 - show all changes including copies in an existing changeset::
5612 - show all changes including copies in an existing changeset::
5613
5613
5614 hg status --copies --change 9353
5614 hg status --copies --change 9353
5615
5615
5616 - get a NUL separated list of added files, suitable for xargs::
5616 - get a NUL separated list of added files, suitable for xargs::
5617
5617
5618 hg status -an0
5618 hg status -an0
5619
5619
5620 Returns 0 on success.
5620 Returns 0 on success.
5621 """
5621 """
5622
5622
5623 revs = opts.get('rev')
5623 revs = opts.get('rev')
5624 change = opts.get('change')
5624 change = opts.get('change')
5625
5625
5626 if revs and change:
5626 if revs and change:
5627 msg = _('cannot specify --rev and --change at the same time')
5627 msg = _('cannot specify --rev and --change at the same time')
5628 raise util.Abort(msg)
5628 raise util.Abort(msg)
5629 elif change:
5629 elif change:
5630 node2 = scmutil.revsingle(repo, change, None).node()
5630 node2 = scmutil.revsingle(repo, change, None).node()
5631 node1 = repo[node2].p1().node()
5631 node1 = repo[node2].p1().node()
5632 else:
5632 else:
5633 node1, node2 = scmutil.revpair(repo, revs)
5633 node1, node2 = scmutil.revpair(repo, revs)
5634
5634
5635 cwd = (pats and repo.getcwd()) or ''
5635 cwd = (pats and repo.getcwd()) or ''
5636 end = opts.get('print0') and '\0' or '\n'
5636 end = opts.get('print0') and '\0' or '\n'
5637 copy = {}
5637 copy = {}
5638 states = 'modified added removed deleted unknown ignored clean'.split()
5638 states = 'modified added removed deleted unknown ignored clean'.split()
5639 show = [k for k in states if opts.get(k)]
5639 show = [k for k in states if opts.get(k)]
5640 if opts.get('all'):
5640 if opts.get('all'):
5641 show += ui.quiet and (states[:4] + ['clean']) or states
5641 show += ui.quiet and (states[:4] + ['clean']) or states
5642 if not show:
5642 if not show:
5643 show = ui.quiet and states[:4] or states[:5]
5643 show = ui.quiet and states[:4] or states[:5]
5644
5644
5645 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5645 stat = repo.status(node1, node2, scmutil.match(repo[node2], pats, opts),
5646 'ignored' in show, 'clean' in show, 'unknown' in show,
5646 'ignored' in show, 'clean' in show, 'unknown' in show,
5647 opts.get('subrepos'))
5647 opts.get('subrepos'))
5648 changestates = zip(states, 'MAR!?IC', stat)
5648 changestates = zip(states, 'MAR!?IC', stat)
5649
5649
5650 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5650 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
5651 copy = copies.pathcopies(repo[node1], repo[node2])
5651 copy = copies.pathcopies(repo[node1], repo[node2])
5652
5652
5653 fm = ui.formatter('status', opts)
5653 fm = ui.formatter('status', opts)
5654 fmt = '%s' + end
5654 fmt = '%s' + end
5655 showchar = not opts.get('no_status')
5655 showchar = not opts.get('no_status')
5656
5656
5657 for state, char, files in changestates:
5657 for state, char, files in changestates:
5658 if state in show:
5658 if state in show:
5659 label = 'status.' + state
5659 label = 'status.' + state
5660 for f in files:
5660 for f in files:
5661 fm.startitem()
5661 fm.startitem()
5662 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5662 fm.condwrite(showchar, 'status', '%s ', char, label=label)
5663 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5663 fm.write('path', fmt, repo.pathto(f, cwd), label=label)
5664 if f in copy:
5664 if f in copy:
5665 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5665 fm.write("copy", ' %s' + end, repo.pathto(copy[f], cwd),
5666 label='status.copied')
5666 label='status.copied')
5667 fm.end()
5667 fm.end()
5668
5668
5669 @command('^summary|sum',
5669 @command('^summary|sum',
5670 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5670 [('', 'remote', None, _('check for push and pull'))], '[--remote]')
5671 def summary(ui, repo, **opts):
5671 def summary(ui, repo, **opts):
5672 """summarize working directory state
5672 """summarize working directory state
5673
5673
5674 This generates a brief summary of the working directory state,
5674 This generates a brief summary of the working directory state,
5675 including parents, branch, commit status, and available updates.
5675 including parents, branch, commit status, and available updates.
5676
5676
5677 With the --remote option, this will check the default paths for
5677 With the --remote option, this will check the default paths for
5678 incoming and outgoing changes. This can be time-consuming.
5678 incoming and outgoing changes. This can be time-consuming.
5679
5679
5680 Returns 0 on success.
5680 Returns 0 on success.
5681 """
5681 """
5682
5682
5683 ctx = repo[None]
5683 ctx = repo[None]
5684 parents = ctx.parents()
5684 parents = ctx.parents()
5685 pnode = parents[0].node()
5685 pnode = parents[0].node()
5686 marks = []
5686 marks = []
5687
5687
5688 for p in parents:
5688 for p in parents:
5689 # label with log.changeset (instead of log.parent) since this
5689 # label with log.changeset (instead of log.parent) since this
5690 # shows a working directory parent *changeset*:
5690 # shows a working directory parent *changeset*:
5691 # i18n: column positioning for "hg summary"
5691 # i18n: column positioning for "hg summary"
5692 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5692 ui.write(_('parent: %d:%s ') % (p.rev(), str(p)),
5693 label='log.changeset changeset.%s' % p.phasestr())
5693 label='log.changeset changeset.%s' % p.phasestr())
5694 ui.write(' '.join(p.tags()), label='log.tag')
5694 ui.write(' '.join(p.tags()), label='log.tag')
5695 if p.bookmarks():
5695 if p.bookmarks():
5696 marks.extend(p.bookmarks())
5696 marks.extend(p.bookmarks())
5697 if p.rev() == -1:
5697 if p.rev() == -1:
5698 if not len(repo):
5698 if not len(repo):
5699 ui.write(_(' (empty repository)'))
5699 ui.write(_(' (empty repository)'))
5700 else:
5700 else:
5701 ui.write(_(' (no revision checked out)'))
5701 ui.write(_(' (no revision checked out)'))
5702 ui.write('\n')
5702 ui.write('\n')
5703 if p.description():
5703 if p.description():
5704 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5704 ui.status(' ' + p.description().splitlines()[0].strip() + '\n',
5705 label='log.summary')
5705 label='log.summary')
5706
5706
5707 branch = ctx.branch()
5707 branch = ctx.branch()
5708 bheads = repo.branchheads(branch)
5708 bheads = repo.branchheads(branch)
5709 # i18n: column positioning for "hg summary"
5709 # i18n: column positioning for "hg summary"
5710 m = _('branch: %s\n') % branch
5710 m = _('branch: %s\n') % branch
5711 if branch != 'default':
5711 if branch != 'default':
5712 ui.write(m, label='log.branch')
5712 ui.write(m, label='log.branch')
5713 else:
5713 else:
5714 ui.status(m, label='log.branch')
5714 ui.status(m, label='log.branch')
5715
5715
5716 if marks:
5716 if marks:
5717 current = repo._bookmarkcurrent
5717 current = repo._bookmarkcurrent
5718 # i18n: column positioning for "hg summary"
5718 # i18n: column positioning for "hg summary"
5719 ui.write(_('bookmarks:'), label='log.bookmark')
5719 ui.write(_('bookmarks:'), label='log.bookmark')
5720 if current is not None:
5720 if current is not None:
5721 if current in marks:
5721 if current in marks:
5722 ui.write(' *' + current, label='bookmarks.current')
5722 ui.write(' *' + current, label='bookmarks.current')
5723 marks.remove(current)
5723 marks.remove(current)
5724 else:
5724 else:
5725 ui.write(' [%s]' % current, label='bookmarks.current')
5725 ui.write(' [%s]' % current, label='bookmarks.current')
5726 for m in marks:
5726 for m in marks:
5727 ui.write(' ' + m, label='log.bookmark')
5727 ui.write(' ' + m, label='log.bookmark')
5728 ui.write('\n', label='log.bookmark')
5728 ui.write('\n', label='log.bookmark')
5729
5729
5730 status = repo.status(unknown=True)
5730 status = repo.status(unknown=True)
5731
5731
5732 c = repo.dirstate.copies()
5732 c = repo.dirstate.copies()
5733 copied, renamed = [], []
5733 copied, renamed = [], []
5734 for d, s in c.iteritems():
5734 for d, s in c.iteritems():
5735 if s in status.removed:
5735 if s in status.removed:
5736 status.removed.remove(s)
5736 status.removed.remove(s)
5737 renamed.append(d)
5737 renamed.append(d)
5738 else:
5738 else:
5739 copied.append(d)
5739 copied.append(d)
5740 if d in status.added:
5740 if d in status.added:
5741 status.added.remove(d)
5741 status.added.remove(d)
5742
5742
5743 ms = mergemod.mergestate(repo)
5743 ms = mergemod.mergestate(repo)
5744 unresolved = [f for f in ms if ms[f] == 'u']
5744 unresolved = [f for f in ms if ms[f] == 'u']
5745
5745
5746 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5746 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
5747
5747
5748 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5748 labels = [(ui.label(_('%d modified'), 'status.modified'), status.modified),
5749 (ui.label(_('%d added'), 'status.added'), status.added),
5749 (ui.label(_('%d added'), 'status.added'), status.added),
5750 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5750 (ui.label(_('%d removed'), 'status.removed'), status.removed),
5751 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5751 (ui.label(_('%d renamed'), 'status.copied'), renamed),
5752 (ui.label(_('%d copied'), 'status.copied'), copied),
5752 (ui.label(_('%d copied'), 'status.copied'), copied),
5753 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5753 (ui.label(_('%d deleted'), 'status.deleted'), status.deleted),
5754 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5754 (ui.label(_('%d unknown'), 'status.unknown'), status.unknown),
5755 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5755 (ui.label(_('%d unresolved'), 'resolve.unresolved'), unresolved),
5756 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5756 (ui.label(_('%d subrepos'), 'status.modified'), subs)]
5757 t = []
5757 t = []
5758 for l, s in labels:
5758 for l, s in labels:
5759 if s:
5759 if s:
5760 t.append(l % len(s))
5760 t.append(l % len(s))
5761
5761
5762 t = ', '.join(t)
5762 t = ', '.join(t)
5763 cleanworkdir = False
5763 cleanworkdir = False
5764
5764
5765 if repo.vfs.exists('updatestate'):
5765 if repo.vfs.exists('updatestate'):
5766 t += _(' (interrupted update)')
5766 t += _(' (interrupted update)')
5767 elif len(parents) > 1:
5767 elif len(parents) > 1:
5768 t += _(' (merge)')
5768 t += _(' (merge)')
5769 elif branch != parents[0].branch():
5769 elif branch != parents[0].branch():
5770 t += _(' (new branch)')
5770 t += _(' (new branch)')
5771 elif (parents[0].closesbranch() and
5771 elif (parents[0].closesbranch() and
5772 pnode in repo.branchheads(branch, closed=True)):
5772 pnode in repo.branchheads(branch, closed=True)):
5773 t += _(' (head closed)')
5773 t += _(' (head closed)')
5774 elif not (status.modified or status.added or status.removed or renamed or
5774 elif not (status.modified or status.added or status.removed or renamed or
5775 copied or subs):
5775 copied or subs):
5776 t += _(' (clean)')
5776 t += _(' (clean)')
5777 cleanworkdir = True
5777 cleanworkdir = True
5778 elif pnode not in bheads:
5778 elif pnode not in bheads:
5779 t += _(' (new branch head)')
5779 t += _(' (new branch head)')
5780
5780
5781 if cleanworkdir:
5781 if cleanworkdir:
5782 # i18n: column positioning for "hg summary"
5782 # i18n: column positioning for "hg summary"
5783 ui.status(_('commit: %s\n') % t.strip())
5783 ui.status(_('commit: %s\n') % t.strip())
5784 else:
5784 else:
5785 # i18n: column positioning for "hg summary"
5785 # i18n: column positioning for "hg summary"
5786 ui.write(_('commit: %s\n') % t.strip())
5786 ui.write(_('commit: %s\n') % t.strip())
5787
5787
5788 # all ancestors of branch heads - all ancestors of parent = new csets
5788 # all ancestors of branch heads - all ancestors of parent = new csets
5789 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5789 new = len(repo.changelog.findmissing([pctx.node() for pctx in parents],
5790 bheads))
5790 bheads))
5791
5791
5792 if new == 0:
5792 if new == 0:
5793 # i18n: column positioning for "hg summary"
5793 # i18n: column positioning for "hg summary"
5794 ui.status(_('update: (current)\n'))
5794 ui.status(_('update: (current)\n'))
5795 elif pnode not in bheads:
5795 elif pnode not in bheads:
5796 # i18n: column positioning for "hg summary"
5796 # i18n: column positioning for "hg summary"
5797 ui.write(_('update: %d new changesets (update)\n') % new)
5797 ui.write(_('update: %d new changesets (update)\n') % new)
5798 else:
5798 else:
5799 # i18n: column positioning for "hg summary"
5799 # i18n: column positioning for "hg summary"
5800 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5800 ui.write(_('update: %d new changesets, %d branch heads (merge)\n') %
5801 (new, len(bheads)))
5801 (new, len(bheads)))
5802
5802
5803 cmdutil.summaryhooks(ui, repo)
5803 cmdutil.summaryhooks(ui, repo)
5804
5804
5805 if opts.get('remote'):
5805 if opts.get('remote'):
5806 needsincoming, needsoutgoing = True, True
5806 needsincoming, needsoutgoing = True, True
5807 else:
5807 else:
5808 needsincoming, needsoutgoing = False, False
5808 needsincoming, needsoutgoing = False, False
5809 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5809 for i, o in cmdutil.summaryremotehooks(ui, repo, opts, None):
5810 if i:
5810 if i:
5811 needsincoming = True
5811 needsincoming = True
5812 if o:
5812 if o:
5813 needsoutgoing = True
5813 needsoutgoing = True
5814 if not needsincoming and not needsoutgoing:
5814 if not needsincoming and not needsoutgoing:
5815 return
5815 return
5816
5816
5817 def getincoming():
5817 def getincoming():
5818 source, branches = hg.parseurl(ui.expandpath('default'))
5818 source, branches = hg.parseurl(ui.expandpath('default'))
5819 sbranch = branches[0]
5819 sbranch = branches[0]
5820 try:
5820 try:
5821 other = hg.peer(repo, {}, source)
5821 other = hg.peer(repo, {}, source)
5822 except error.RepoError:
5822 except error.RepoError:
5823 if opts.get('remote'):
5823 if opts.get('remote'):
5824 raise
5824 raise
5825 return source, sbranch, None, None, None
5825 return source, sbranch, None, None, None
5826 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5826 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
5827 if revs:
5827 if revs:
5828 revs = [other.lookup(rev) for rev in revs]
5828 revs = [other.lookup(rev) for rev in revs]
5829 ui.debug('comparing with %s\n' % util.hidepassword(source))
5829 ui.debug('comparing with %s\n' % util.hidepassword(source))
5830 repo.ui.pushbuffer()
5830 repo.ui.pushbuffer()
5831 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5831 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
5832 repo.ui.popbuffer()
5832 repo.ui.popbuffer()
5833 return source, sbranch, other, commoninc, commoninc[1]
5833 return source, sbranch, other, commoninc, commoninc[1]
5834
5834
5835 if needsincoming:
5835 if needsincoming:
5836 source, sbranch, sother, commoninc, incoming = getincoming()
5836 source, sbranch, sother, commoninc, incoming = getincoming()
5837 else:
5837 else:
5838 source = sbranch = sother = commoninc = incoming = None
5838 source = sbranch = sother = commoninc = incoming = None
5839
5839
5840 def getoutgoing():
5840 def getoutgoing():
5841 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5841 dest, branches = hg.parseurl(ui.expandpath('default-push', 'default'))
5842 dbranch = branches[0]
5842 dbranch = branches[0]
5843 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5843 revs, checkout = hg.addbranchrevs(repo, repo, branches, None)
5844 if source != dest:
5844 if source != dest:
5845 try:
5845 try:
5846 dother = hg.peer(repo, {}, dest)
5846 dother = hg.peer(repo, {}, dest)
5847 except error.RepoError:
5847 except error.RepoError:
5848 if opts.get('remote'):
5848 if opts.get('remote'):
5849 raise
5849 raise
5850 return dest, dbranch, None, None
5850 return dest, dbranch, None, None
5851 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5851 ui.debug('comparing with %s\n' % util.hidepassword(dest))
5852 elif sother is None:
5852 elif sother is None:
5853 # there is no explicit destination peer, but source one is invalid
5853 # there is no explicit destination peer, but source one is invalid
5854 return dest, dbranch, None, None
5854 return dest, dbranch, None, None
5855 else:
5855 else:
5856 dother = sother
5856 dother = sother
5857 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5857 if (source != dest or (sbranch is not None and sbranch != dbranch)):
5858 common = None
5858 common = None
5859 else:
5859 else:
5860 common = commoninc
5860 common = commoninc
5861 if revs:
5861 if revs:
5862 revs = [repo.lookup(rev) for rev in revs]
5862 revs = [repo.lookup(rev) for rev in revs]
5863 repo.ui.pushbuffer()
5863 repo.ui.pushbuffer()
5864 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5864 outgoing = discovery.findcommonoutgoing(repo, dother, onlyheads=revs,
5865 commoninc=common)
5865 commoninc=common)
5866 repo.ui.popbuffer()
5866 repo.ui.popbuffer()
5867 return dest, dbranch, dother, outgoing
5867 return dest, dbranch, dother, outgoing
5868
5868
5869 if needsoutgoing:
5869 if needsoutgoing:
5870 dest, dbranch, dother, outgoing = getoutgoing()
5870 dest, dbranch, dother, outgoing = getoutgoing()
5871 else:
5871 else:
5872 dest = dbranch = dother = outgoing = None
5872 dest = dbranch = dother = outgoing = None
5873
5873
5874 if opts.get('remote'):
5874 if opts.get('remote'):
5875 t = []
5875 t = []
5876 if incoming:
5876 if incoming:
5877 t.append(_('1 or more incoming'))
5877 t.append(_('1 or more incoming'))
5878 o = outgoing.missing
5878 o = outgoing.missing
5879 if o:
5879 if o:
5880 t.append(_('%d outgoing') % len(o))
5880 t.append(_('%d outgoing') % len(o))
5881 other = dother or sother
5881 other = dother or sother
5882 if 'bookmarks' in other.listkeys('namespaces'):
5882 if 'bookmarks' in other.listkeys('namespaces'):
5883 lmarks = repo.listkeys('bookmarks')
5883 lmarks = repo.listkeys('bookmarks')
5884 rmarks = other.listkeys('bookmarks')
5884 rmarks = other.listkeys('bookmarks')
5885 diff = set(rmarks) - set(lmarks)
5885 diff = set(rmarks) - set(lmarks)
5886 if len(diff) > 0:
5886 if len(diff) > 0:
5887 t.append(_('%d incoming bookmarks') % len(diff))
5887 t.append(_('%d incoming bookmarks') % len(diff))
5888 diff = set(lmarks) - set(rmarks)
5888 diff = set(lmarks) - set(rmarks)
5889 if len(diff) > 0:
5889 if len(diff) > 0:
5890 t.append(_('%d outgoing bookmarks') % len(diff))
5890 t.append(_('%d outgoing bookmarks') % len(diff))
5891
5891
5892 if t:
5892 if t:
5893 # i18n: column positioning for "hg summary"
5893 # i18n: column positioning for "hg summary"
5894 ui.write(_('remote: %s\n') % (', '.join(t)))
5894 ui.write(_('remote: %s\n') % (', '.join(t)))
5895 else:
5895 else:
5896 # i18n: column positioning for "hg summary"
5896 # i18n: column positioning for "hg summary"
5897 ui.status(_('remote: (synced)\n'))
5897 ui.status(_('remote: (synced)\n'))
5898
5898
5899 cmdutil.summaryremotehooks(ui, repo, opts,
5899 cmdutil.summaryremotehooks(ui, repo, opts,
5900 ((source, sbranch, sother, commoninc),
5900 ((source, sbranch, sother, commoninc),
5901 (dest, dbranch, dother, outgoing)))
5901 (dest, dbranch, dother, outgoing)))
5902
5902
5903 @command('tag',
5903 @command('tag',
5904 [('f', 'force', None, _('force tag')),
5904 [('f', 'force', None, _('force tag')),
5905 ('l', 'local', None, _('make the tag local')),
5905 ('l', 'local', None, _('make the tag local')),
5906 ('r', 'rev', '', _('revision to tag'), _('REV')),
5906 ('r', 'rev', '', _('revision to tag'), _('REV')),
5907 ('', 'remove', None, _('remove a tag')),
5907 ('', 'remove', None, _('remove a tag')),
5908 # -l/--local is already there, commitopts cannot be used
5908 # -l/--local is already there, commitopts cannot be used
5909 ('e', 'edit', None, _('invoke editor on commit messages')),
5909 ('e', 'edit', None, _('invoke editor on commit messages')),
5910 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5910 ('m', 'message', '', _('use text as commit message'), _('TEXT')),
5911 ] + commitopts2,
5911 ] + commitopts2,
5912 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5912 _('[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'))
5913 def tag(ui, repo, name1, *names, **opts):
5913 def tag(ui, repo, name1, *names, **opts):
5914 """add one or more tags for the current or given revision
5914 """add one or more tags for the current or given revision
5915
5915
5916 Name a particular revision using <name>.
5916 Name a particular revision using <name>.
5917
5917
5918 Tags are used to name particular revisions of the repository and are
5918 Tags are used to name particular revisions of the repository and are
5919 very useful to compare different revisions, to go back to significant
5919 very useful to compare different revisions, to go back to significant
5920 earlier versions or to mark branch points as releases, etc. Changing
5920 earlier versions or to mark branch points as releases, etc. Changing
5921 an existing tag is normally disallowed; use -f/--force to override.
5921 an existing tag is normally disallowed; use -f/--force to override.
5922
5922
5923 If no revision is given, the parent of the working directory is
5923 If no revision is given, the parent of the working directory is
5924 used.
5924 used.
5925
5925
5926 To facilitate version control, distribution, and merging of tags,
5926 To facilitate version control, distribution, and merging of tags,
5927 they are stored as a file named ".hgtags" which is managed similarly
5927 they are stored as a file named ".hgtags" which is managed similarly
5928 to other project files and can be hand-edited if necessary. This
5928 to other project files and can be hand-edited if necessary. This
5929 also means that tagging creates a new commit. The file
5929 also means that tagging creates a new commit. The file
5930 ".hg/localtags" is used for local tags (not shared among
5930 ".hg/localtags" is used for local tags (not shared among
5931 repositories).
5931 repositories).
5932
5932
5933 Tag commits are usually made at the head of a branch. If the parent
5933 Tag commits are usually made at the head of a branch. If the parent
5934 of the working directory is not a branch head, :hg:`tag` aborts; use
5934 of the working directory is not a branch head, :hg:`tag` aborts; use
5935 -f/--force to force the tag commit to be based on a non-head
5935 -f/--force to force the tag commit to be based on a non-head
5936 changeset.
5936 changeset.
5937
5937
5938 See :hg:`help dates` for a list of formats valid for -d/--date.
5938 See :hg:`help dates` for a list of formats valid for -d/--date.
5939
5939
5940 Since tag names have priority over branch names during revision
5940 Since tag names have priority over branch names during revision
5941 lookup, using an existing branch name as a tag name is discouraged.
5941 lookup, using an existing branch name as a tag name is discouraged.
5942
5942
5943 Returns 0 on success.
5943 Returns 0 on success.
5944 """
5944 """
5945 wlock = lock = None
5945 wlock = lock = None
5946 try:
5946 try:
5947 wlock = repo.wlock()
5947 wlock = repo.wlock()
5948 lock = repo.lock()
5948 lock = repo.lock()
5949 rev_ = "."
5949 rev_ = "."
5950 names = [t.strip() for t in (name1,) + names]
5950 names = [t.strip() for t in (name1,) + names]
5951 if len(names) != len(set(names)):
5951 if len(names) != len(set(names)):
5952 raise util.Abort(_('tag names must be unique'))
5952 raise util.Abort(_('tag names must be unique'))
5953 for n in names:
5953 for n in names:
5954 scmutil.checknewlabel(repo, n, 'tag')
5954 scmutil.checknewlabel(repo, n, 'tag')
5955 if not n:
5955 if not n:
5956 raise util.Abort(_('tag names cannot consist entirely of '
5956 raise util.Abort(_('tag names cannot consist entirely of '
5957 'whitespace'))
5957 'whitespace'))
5958 if opts.get('rev') and opts.get('remove'):
5958 if opts.get('rev') and opts.get('remove'):
5959 raise util.Abort(_("--rev and --remove are incompatible"))
5959 raise util.Abort(_("--rev and --remove are incompatible"))
5960 if opts.get('rev'):
5960 if opts.get('rev'):
5961 rev_ = opts['rev']
5961 rev_ = opts['rev']
5962 message = opts.get('message')
5962 message = opts.get('message')
5963 if opts.get('remove'):
5963 if opts.get('remove'):
5964 expectedtype = opts.get('local') and 'local' or 'global'
5964 expectedtype = opts.get('local') and 'local' or 'global'
5965 for n in names:
5965 for n in names:
5966 if not repo.tagtype(n):
5966 if not repo.tagtype(n):
5967 raise util.Abort(_("tag '%s' does not exist") % n)
5967 raise util.Abort(_("tag '%s' does not exist") % n)
5968 if repo.tagtype(n) != expectedtype:
5968 if repo.tagtype(n) != expectedtype:
5969 if expectedtype == 'global':
5969 if expectedtype == 'global':
5970 raise util.Abort(_("tag '%s' is not a global tag") % n)
5970 raise util.Abort(_("tag '%s' is not a global tag") % n)
5971 else:
5971 else:
5972 raise util.Abort(_("tag '%s' is not a local tag") % n)
5972 raise util.Abort(_("tag '%s' is not a local tag") % n)
5973 rev_ = nullid
5973 rev_ = nullid
5974 if not message:
5974 if not message:
5975 # we don't translate commit messages
5975 # we don't translate commit messages
5976 message = 'Removed tag %s' % ', '.join(names)
5976 message = 'Removed tag %s' % ', '.join(names)
5977 elif not opts.get('force'):
5977 elif not opts.get('force'):
5978 for n in names:
5978 for n in names:
5979 if n in repo.tags():
5979 if n in repo.tags():
5980 raise util.Abort(_("tag '%s' already exists "
5980 raise util.Abort(_("tag '%s' already exists "
5981 "(use -f to force)") % n)
5981 "(use -f to force)") % n)
5982 if not opts.get('local'):
5982 if not opts.get('local'):
5983 p1, p2 = repo.dirstate.parents()
5983 p1, p2 = repo.dirstate.parents()
5984 if p2 != nullid:
5984 if p2 != nullid:
5985 raise util.Abort(_('uncommitted merge'))
5985 raise util.Abort(_('uncommitted merge'))
5986 bheads = repo.branchheads()
5986 bheads = repo.branchheads()
5987 if not opts.get('force') and bheads and p1 not in bheads:
5987 if not opts.get('force') and bheads and p1 not in bheads:
5988 raise util.Abort(_('not at a branch head (use -f to force)'))
5988 raise util.Abort(_('not at a branch head (use -f to force)'))
5989 r = scmutil.revsingle(repo, rev_).node()
5989 r = scmutil.revsingle(repo, rev_).node()
5990
5990
5991 if not message:
5991 if not message:
5992 # we don't translate commit messages
5992 # we don't translate commit messages
5993 message = ('Added tag %s for changeset %s' %
5993 message = ('Added tag %s for changeset %s' %
5994 (', '.join(names), short(r)))
5994 (', '.join(names), short(r)))
5995
5995
5996 date = opts.get('date')
5996 date = opts.get('date')
5997 if date:
5997 if date:
5998 date = util.parsedate(date)
5998 date = util.parsedate(date)
5999
5999
6000 if opts.get('remove'):
6000 if opts.get('remove'):
6001 editform = 'tag.remove'
6001 editform = 'tag.remove'
6002 else:
6002 else:
6003 editform = 'tag.add'
6003 editform = 'tag.add'
6004 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6004 editor = cmdutil.getcommiteditor(editform=editform, **opts)
6005
6005
6006 # don't allow tagging the null rev
6006 # don't allow tagging the null rev
6007 if (not opts.get('remove') and
6007 if (not opts.get('remove') and
6008 scmutil.revsingle(repo, rev_).rev() == nullrev):
6008 scmutil.revsingle(repo, rev_).rev() == nullrev):
6009 raise util.Abort(_("cannot tag null revision"))
6009 raise util.Abort(_("cannot tag null revision"))
6010
6010
6011 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6011 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date,
6012 editor=editor)
6012 editor=editor)
6013 finally:
6013 finally:
6014 release(lock, wlock)
6014 release(lock, wlock)
6015
6015
6016 @command('tags', formatteropts, '')
6016 @command('tags', formatteropts, '')
6017 def tags(ui, repo, **opts):
6017 def tags(ui, repo, **opts):
6018 """list repository tags
6018 """list repository tags
6019
6019
6020 This lists both regular and local tags. When the -v/--verbose
6020 This lists both regular and local tags. When the -v/--verbose
6021 switch is used, a third column "local" is printed for local tags.
6021 switch is used, a third column "local" is printed for local tags.
6022
6022
6023 Returns 0 on success.
6023 Returns 0 on success.
6024 """
6024 """
6025
6025
6026 fm = ui.formatter('tags', opts)
6026 fm = ui.formatter('tags', opts)
6027 hexfunc = fm.hexfunc
6027 hexfunc = fm.hexfunc
6028 tagtype = ""
6028 tagtype = ""
6029
6029
6030 for t, n in reversed(repo.tagslist()):
6030 for t, n in reversed(repo.tagslist()):
6031 hn = hexfunc(n)
6031 hn = hexfunc(n)
6032 label = 'tags.normal'
6032 label = 'tags.normal'
6033 tagtype = ''
6033 tagtype = ''
6034 if repo.tagtype(t) == 'local':
6034 if repo.tagtype(t) == 'local':
6035 label = 'tags.local'
6035 label = 'tags.local'
6036 tagtype = 'local'
6036 tagtype = 'local'
6037
6037
6038 fm.startitem()
6038 fm.startitem()
6039 fm.write('tag', '%s', t, label=label)
6039 fm.write('tag', '%s', t, label=label)
6040 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6040 fmt = " " * (30 - encoding.colwidth(t)) + ' %5d:%s'
6041 fm.condwrite(not ui.quiet, 'rev node', fmt,
6041 fm.condwrite(not ui.quiet, 'rev node', fmt,
6042 repo.changelog.rev(n), hn, label=label)
6042 repo.changelog.rev(n), hn, label=label)
6043 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6043 fm.condwrite(ui.verbose and tagtype, 'type', ' %s',
6044 tagtype, label=label)
6044 tagtype, label=label)
6045 fm.plain('\n')
6045 fm.plain('\n')
6046 fm.end()
6046 fm.end()
6047
6047
6048 @command('tip',
6048 @command('tip',
6049 [('p', 'patch', None, _('show patch')),
6049 [('p', 'patch', None, _('show patch')),
6050 ('g', 'git', None, _('use git extended diff format')),
6050 ('g', 'git', None, _('use git extended diff format')),
6051 ] + templateopts,
6051 ] + templateopts,
6052 _('[-p] [-g]'))
6052 _('[-p] [-g]'))
6053 def tip(ui, repo, **opts):
6053 def tip(ui, repo, **opts):
6054 """show the tip revision (DEPRECATED)
6054 """show the tip revision (DEPRECATED)
6055
6055
6056 The tip revision (usually just called the tip) is the changeset
6056 The tip revision (usually just called the tip) is the changeset
6057 most recently added to the repository (and therefore the most
6057 most recently added to the repository (and therefore the most
6058 recently changed head).
6058 recently changed head).
6059
6059
6060 If you have just made a commit, that commit will be the tip. If
6060 If you have just made a commit, that commit will be the tip. If
6061 you have just pulled changes from another repository, the tip of
6061 you have just pulled changes from another repository, the tip of
6062 that repository becomes the current tip. The "tip" tag is special
6062 that repository becomes the current tip. The "tip" tag is special
6063 and cannot be renamed or assigned to a different changeset.
6063 and cannot be renamed or assigned to a different changeset.
6064
6064
6065 This command is deprecated, please use :hg:`heads` instead.
6065 This command is deprecated, please use :hg:`heads` instead.
6066
6066
6067 Returns 0 on success.
6067 Returns 0 on success.
6068 """
6068 """
6069 displayer = cmdutil.show_changeset(ui, repo, opts)
6069 displayer = cmdutil.show_changeset(ui, repo, opts)
6070 displayer.show(repo['tip'])
6070 displayer.show(repo['tip'])
6071 displayer.close()
6071 displayer.close()
6072
6072
6073 @command('unbundle',
6073 @command('unbundle',
6074 [('u', 'update', None,
6074 [('u', 'update', None,
6075 _('update to new branch head if changesets were unbundled'))],
6075 _('update to new branch head if changesets were unbundled'))],
6076 _('[-u] FILE...'))
6076 _('[-u] FILE...'))
6077 def unbundle(ui, repo, fname1, *fnames, **opts):
6077 def unbundle(ui, repo, fname1, *fnames, **opts):
6078 """apply one or more changegroup files
6078 """apply one or more changegroup files
6079
6079
6080 Apply one or more compressed changegroup files generated by the
6080 Apply one or more compressed changegroup files generated by the
6081 bundle command.
6081 bundle command.
6082
6082
6083 Returns 0 on success, 1 if an update has unresolved files.
6083 Returns 0 on success, 1 if an update has unresolved files.
6084 """
6084 """
6085 fnames = (fname1,) + fnames
6085 fnames = (fname1,) + fnames
6086
6086
6087 lock = repo.lock()
6087 lock = repo.lock()
6088 try:
6088 try:
6089 for fname in fnames:
6089 for fname in fnames:
6090 f = hg.openpath(ui, fname)
6090 f = hg.openpath(ui, fname)
6091 gen = exchange.readbundle(ui, f, fname)
6091 gen = exchange.readbundle(ui, f, fname)
6092 modheads = changegroup.addchangegroup(repo, gen, 'unbundle',
6092 modheads = changegroup.addchangegroup(repo, gen, 'unbundle',
6093 'bundle:' + fname)
6093 'bundle:' + fname)
6094 finally:
6094 finally:
6095 lock.release()
6095 lock.release()
6096
6096
6097 return postincoming(ui, repo, modheads, opts.get('update'), None)
6097 return postincoming(ui, repo, modheads, opts.get('update'), None)
6098
6098
6099 @command('^update|up|checkout|co',
6099 @command('^update|up|checkout|co',
6100 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6100 [('C', 'clean', None, _('discard uncommitted changes (no backup)')),
6101 ('c', 'check', None,
6101 ('c', 'check', None,
6102 _('update across branches if no uncommitted changes')),
6102 _('update across branches if no uncommitted changes')),
6103 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6103 ('d', 'date', '', _('tipmost revision matching date'), _('DATE')),
6104 ('r', 'rev', '', _('revision'), _('REV'))
6104 ('r', 'rev', '', _('revision'), _('REV'))
6105 ] + mergetoolopts,
6105 ] + mergetoolopts,
6106 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6106 _('[-c] [-C] [-d DATE] [[-r] REV]'))
6107 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6107 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False,
6108 tool=None):
6108 tool=None):
6109 """update working directory (or switch revisions)
6109 """update working directory (or switch revisions)
6110
6110
6111 Update the repository's working directory to the specified
6111 Update the repository's working directory to the specified
6112 changeset. If no changeset is specified, update to the tip of the
6112 changeset. If no changeset is specified, update to the tip of the
6113 current named branch and move the current bookmark (see :hg:`help
6113 current named branch and move the current bookmark (see :hg:`help
6114 bookmarks`).
6114 bookmarks`).
6115
6115
6116 Update sets the working directory's parent revision to the specified
6116 Update sets the working directory's parent revision to the specified
6117 changeset (see :hg:`help parents`).
6117 changeset (see :hg:`help parents`).
6118
6118
6119 If the changeset is not a descendant or ancestor of the working
6119 If the changeset is not a descendant or ancestor of the working
6120 directory's parent, the update is aborted. With the -c/--check
6120 directory's parent, the update is aborted. With the -c/--check
6121 option, the working directory is checked for uncommitted changes; if
6121 option, the working directory is checked for uncommitted changes; if
6122 none are found, the working directory is updated to the specified
6122 none are found, the working directory is updated to the specified
6123 changeset.
6123 changeset.
6124
6124
6125 .. container:: verbose
6125 .. container:: verbose
6126
6126
6127 The following rules apply when the working directory contains
6127 The following rules apply when the working directory contains
6128 uncommitted changes:
6128 uncommitted changes:
6129
6129
6130 1. If neither -c/--check nor -C/--clean is specified, and if
6130 1. If neither -c/--check nor -C/--clean is specified, and if
6131 the requested changeset is an ancestor or descendant of
6131 the requested changeset is an ancestor or descendant of
6132 the working directory's parent, the uncommitted changes
6132 the working directory's parent, the uncommitted changes
6133 are merged into the requested changeset and the merged
6133 are merged into the requested changeset and the merged
6134 result is left uncommitted. If the requested changeset is
6134 result is left uncommitted. If the requested changeset is
6135 not an ancestor or descendant (that is, it is on another
6135 not an ancestor or descendant (that is, it is on another
6136 branch), the update is aborted and the uncommitted changes
6136 branch), the update is aborted and the uncommitted changes
6137 are preserved.
6137 are preserved.
6138
6138
6139 2. With the -c/--check option, the update is aborted and the
6139 2. With the -c/--check option, the update is aborted and the
6140 uncommitted changes are preserved.
6140 uncommitted changes are preserved.
6141
6141
6142 3. With the -C/--clean option, uncommitted changes are discarded and
6142 3. With the -C/--clean option, uncommitted changes are discarded and
6143 the working directory is updated to the requested changeset.
6143 the working directory is updated to the requested changeset.
6144
6144
6145 To cancel an uncommitted merge (and lose your changes), use
6145 To cancel an uncommitted merge (and lose your changes), use
6146 :hg:`update --clean .`.
6146 :hg:`update --clean .`.
6147
6147
6148 Use null as the changeset to remove the working directory (like
6148 Use null as the changeset to remove the working directory (like
6149 :hg:`clone -U`).
6149 :hg:`clone -U`).
6150
6150
6151 If you want to revert just one file to an older revision, use
6151 If you want to revert just one file to an older revision, use
6152 :hg:`revert [-r REV] NAME`.
6152 :hg:`revert [-r REV] NAME`.
6153
6153
6154 See :hg:`help dates` for a list of formats valid for -d/--date.
6154 See :hg:`help dates` for a list of formats valid for -d/--date.
6155
6155
6156 Returns 0 on success, 1 if there are unresolved files.
6156 Returns 0 on success, 1 if there are unresolved files.
6157 """
6157 """
6158 if rev and node:
6158 if rev and node:
6159 raise util.Abort(_("please specify just one revision"))
6159 raise util.Abort(_("please specify just one revision"))
6160
6160
6161 if rev is None or rev == '':
6161 if rev is None or rev == '':
6162 rev = node
6162 rev = node
6163
6163
6164 cmdutil.clearunfinished(repo)
6164 cmdutil.clearunfinished(repo)
6165
6165
6166 # with no argument, we also move the current bookmark, if any
6166 # with no argument, we also move the current bookmark, if any
6167 rev, movemarkfrom = bookmarks.calculateupdate(ui, repo, rev)
6167 rev, movemarkfrom = bookmarks.calculateupdate(ui, repo, rev)
6168
6168
6169 # if we defined a bookmark, we have to remember the original bookmark name
6169 # if we defined a bookmark, we have to remember the original bookmark name
6170 brev = rev
6170 brev = rev
6171 rev = scmutil.revsingle(repo, rev, rev).rev()
6171 rev = scmutil.revsingle(repo, rev, rev).rev()
6172
6172
6173 if check and clean:
6173 if check and clean:
6174 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
6174 raise util.Abort(_("cannot specify both -c/--check and -C/--clean"))
6175
6175
6176 if date:
6176 if date:
6177 if rev is not None:
6177 if rev is not None:
6178 raise util.Abort(_("you can't specify a revision and a date"))
6178 raise util.Abort(_("you can't specify a revision and a date"))
6179 rev = cmdutil.finddate(ui, repo, date)
6179 rev = cmdutil.finddate(ui, repo, date)
6180
6180
6181 if check:
6181 if check:
6182 c = repo[None]
6182 c = repo[None]
6183 if c.dirty(merge=False, branch=False, missing=True):
6183 if c.dirty(merge=False, branch=False, missing=True):
6184 raise util.Abort(_("uncommitted changes"))
6184 raise util.Abort(_("uncommitted changes"))
6185 if rev is None:
6185 if rev is None:
6186 rev = repo[repo[None].branch()].rev()
6186 rev = repo[repo[None].branch()].rev()
6187 mergemod._checkunknown(repo, repo[None], repo[rev])
6187 mergemod.checkunknown(repo, repo[None], repo[rev])
6188
6188
6189 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
6189 repo.ui.setconfig('ui', 'forcemerge', tool, 'update')
6190
6190
6191 if clean:
6191 if clean:
6192 ret = hg.clean(repo, rev)
6192 ret = hg.clean(repo, rev)
6193 else:
6193 else:
6194 ret = hg.update(repo, rev)
6194 ret = hg.update(repo, rev)
6195
6195
6196 if not ret and movemarkfrom:
6196 if not ret and movemarkfrom:
6197 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
6197 if bookmarks.update(repo, [movemarkfrom], repo['.'].node()):
6198 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
6198 ui.status(_("updating bookmark %s\n") % repo._bookmarkcurrent)
6199 elif brev in repo._bookmarks:
6199 elif brev in repo._bookmarks:
6200 bookmarks.setcurrent(repo, brev)
6200 bookmarks.setcurrent(repo, brev)
6201 ui.status(_("(activating bookmark %s)\n") % brev)
6201 ui.status(_("(activating bookmark %s)\n") % brev)
6202 elif brev:
6202 elif brev:
6203 if repo._bookmarkcurrent:
6203 if repo._bookmarkcurrent:
6204 ui.status(_("(leaving bookmark %s)\n") %
6204 ui.status(_("(leaving bookmark %s)\n") %
6205 repo._bookmarkcurrent)
6205 repo._bookmarkcurrent)
6206 bookmarks.unsetcurrent(repo)
6206 bookmarks.unsetcurrent(repo)
6207
6207
6208 return ret
6208 return ret
6209
6209
6210 @command('verify', [])
6210 @command('verify', [])
6211 def verify(ui, repo):
6211 def verify(ui, repo):
6212 """verify the integrity of the repository
6212 """verify the integrity of the repository
6213
6213
6214 Verify the integrity of the current repository.
6214 Verify the integrity of the current repository.
6215
6215
6216 This will perform an extensive check of the repository's
6216 This will perform an extensive check of the repository's
6217 integrity, validating the hashes and checksums of each entry in
6217 integrity, validating the hashes and checksums of each entry in
6218 the changelog, manifest, and tracked files, as well as the
6218 the changelog, manifest, and tracked files, as well as the
6219 integrity of their crosslinks and indices.
6219 integrity of their crosslinks and indices.
6220
6220
6221 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
6221 Please see http://mercurial.selenic.com/wiki/RepositoryCorruption
6222 for more information about recovery from corruption of the
6222 for more information about recovery from corruption of the
6223 repository.
6223 repository.
6224
6224
6225 Returns 0 on success, 1 if errors are encountered.
6225 Returns 0 on success, 1 if errors are encountered.
6226 """
6226 """
6227 return hg.verify(repo)
6227 return hg.verify(repo)
6228
6228
6229 @command('version', [], norepo=True)
6229 @command('version', [], norepo=True)
6230 def version_(ui):
6230 def version_(ui):
6231 """output version and copyright information"""
6231 """output version and copyright information"""
6232 ui.write(_("Mercurial Distributed SCM (version %s)\n")
6232 ui.write(_("Mercurial Distributed SCM (version %s)\n")
6233 % util.version())
6233 % util.version())
6234 ui.status(_(
6234 ui.status(_(
6235 "(see http://mercurial.selenic.com for more information)\n"
6235 "(see http://mercurial.selenic.com for more information)\n"
6236 "\nCopyright (C) 2005-2014 Matt Mackall and others\n"
6236 "\nCopyright (C) 2005-2014 Matt Mackall and others\n"
6237 "This is free software; see the source for copying conditions. "
6237 "This is free software; see the source for copying conditions. "
6238 "There is NO\nwarranty; "
6238 "There is NO\nwarranty; "
6239 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6239 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
6240 ))
6240 ))
6241
6241
6242 ui.note(_("\nEnabled extensions:\n\n"))
6242 ui.note(_("\nEnabled extensions:\n\n"))
6243 if ui.verbose:
6243 if ui.verbose:
6244 # format names and versions into columns
6244 # format names and versions into columns
6245 names = []
6245 names = []
6246 vers = []
6246 vers = []
6247 for name, module in extensions.extensions():
6247 for name, module in extensions.extensions():
6248 names.append(name)
6248 names.append(name)
6249 vers.append(extensions.moduleversion(module))
6249 vers.append(extensions.moduleversion(module))
6250 if names:
6250 if names:
6251 maxnamelen = max(len(n) for n in names)
6251 maxnamelen = max(len(n) for n in names)
6252 for i, name in enumerate(names):
6252 for i, name in enumerate(names):
6253 ui.write(" %-*s %s\n" % (maxnamelen, name, vers[i]))
6253 ui.write(" %-*s %s\n" % (maxnamelen, name, vers[i]))
@@ -1,1160 +1,1160 b''
1 # merge.py - directory-level update/merge handling for Mercurial
1 # merge.py - directory-level update/merge handling for Mercurial
2 #
2 #
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2 or any later version.
6 # GNU General Public License version 2 or any later version.
7
7
8 import struct
8 import struct
9
9
10 from node import nullid, nullrev, hex, bin
10 from node import nullid, nullrev, hex, bin
11 from i18n import _
11 from i18n import _
12 from mercurial import obsolete
12 from mercurial import obsolete
13 import error as errormod, util, filemerge, copies, subrepo, worker
13 import error as errormod, util, filemerge, copies, subrepo, worker
14 import errno, os, shutil
14 import errno, os, shutil
15
15
16 _pack = struct.pack
16 _pack = struct.pack
17 _unpack = struct.unpack
17 _unpack = struct.unpack
18
18
19 def _droponode(data):
19 def _droponode(data):
20 # used for compatibility for v1
20 # used for compatibility for v1
21 bits = data.split("\0")
21 bits = data.split("\0")
22 bits = bits[:-2] + bits[-1:]
22 bits = bits[:-2] + bits[-1:]
23 return "\0".join(bits)
23 return "\0".join(bits)
24
24
25 class mergestate(object):
25 class mergestate(object):
26 '''track 3-way merge state of individual files
26 '''track 3-way merge state of individual files
27
27
28 it is stored on disk when needed. Two file are used, one with an old
28 it is stored on disk when needed. Two file are used, one with an old
29 format, one with a new format. Both contains similar data, but the new
29 format, one with a new format. Both contains similar data, but the new
30 format can store new kind of field.
30 format can store new kind of field.
31
31
32 Current new format is a list of arbitrary record of the form:
32 Current new format is a list of arbitrary record of the form:
33
33
34 [type][length][content]
34 [type][length][content]
35
35
36 Type is a single character, length is a 4 bytes integer, content is an
36 Type is a single character, length is a 4 bytes integer, content is an
37 arbitrary suites of bytes of length `length`.
37 arbitrary suites of bytes of length `length`.
38
38
39 Type should be a letter. Capital letter are mandatory record, Mercurial
39 Type should be a letter. Capital letter are mandatory record, Mercurial
40 should abort if they are unknown. lower case record can be safely ignored.
40 should abort if they are unknown. lower case record can be safely ignored.
41
41
42 Currently known record:
42 Currently known record:
43
43
44 L: the node of the "local" part of the merge (hexified version)
44 L: the node of the "local" part of the merge (hexified version)
45 O: the node of the "other" part of the merge (hexified version)
45 O: the node of the "other" part of the merge (hexified version)
46 F: a file to be merged entry
46 F: a file to be merged entry
47 '''
47 '''
48 statepathv1 = "merge/state"
48 statepathv1 = "merge/state"
49 statepathv2 = "merge/state2"
49 statepathv2 = "merge/state2"
50
50
51 def __init__(self, repo):
51 def __init__(self, repo):
52 self._repo = repo
52 self._repo = repo
53 self._dirty = False
53 self._dirty = False
54 self._read()
54 self._read()
55
55
56 def reset(self, node=None, other=None):
56 def reset(self, node=None, other=None):
57 self._state = {}
57 self._state = {}
58 self._local = None
58 self._local = None
59 self._other = None
59 self._other = None
60 if node:
60 if node:
61 self._local = node
61 self._local = node
62 self._other = other
62 self._other = other
63 shutil.rmtree(self._repo.join("merge"), True)
63 shutil.rmtree(self._repo.join("merge"), True)
64 self._dirty = False
64 self._dirty = False
65
65
66 def _read(self):
66 def _read(self):
67 """Analyse each record content to restore a serialized state from disk
67 """Analyse each record content to restore a serialized state from disk
68
68
69 This function process "record" entry produced by the de-serialization
69 This function process "record" entry produced by the de-serialization
70 of on disk file.
70 of on disk file.
71 """
71 """
72 self._state = {}
72 self._state = {}
73 self._local = None
73 self._local = None
74 self._other = None
74 self._other = None
75 records = self._readrecords()
75 records = self._readrecords()
76 for rtype, record in records:
76 for rtype, record in records:
77 if rtype == 'L':
77 if rtype == 'L':
78 self._local = bin(record)
78 self._local = bin(record)
79 elif rtype == 'O':
79 elif rtype == 'O':
80 self._other = bin(record)
80 self._other = bin(record)
81 elif rtype == "F":
81 elif rtype == "F":
82 bits = record.split("\0")
82 bits = record.split("\0")
83 self._state[bits[0]] = bits[1:]
83 self._state[bits[0]] = bits[1:]
84 elif not rtype.islower():
84 elif not rtype.islower():
85 raise util.Abort(_('unsupported merge state record: %s')
85 raise util.Abort(_('unsupported merge state record: %s')
86 % rtype)
86 % rtype)
87 self._dirty = False
87 self._dirty = False
88
88
89 def _readrecords(self):
89 def _readrecords(self):
90 """Read merge state from disk and return a list of record (TYPE, data)
90 """Read merge state from disk and return a list of record (TYPE, data)
91
91
92 We read data from both v1 and v2 files and decide which one to use.
92 We read data from both v1 and v2 files and decide which one to use.
93
93
94 V1 has been used by version prior to 2.9.1 and contains less data than
94 V1 has been used by version prior to 2.9.1 and contains less data than
95 v2. We read both versions and check if no data in v2 contradicts
95 v2. We read both versions and check if no data in v2 contradicts
96 v1. If there is not contradiction we can safely assume that both v1
96 v1. If there is not contradiction we can safely assume that both v1
97 and v2 were written at the same time and use the extract data in v2. If
97 and v2 were written at the same time and use the extract data in v2. If
98 there is contradiction we ignore v2 content as we assume an old version
98 there is contradiction we ignore v2 content as we assume an old version
99 of Mercurial has overwritten the mergestate file and left an old v2
99 of Mercurial has overwritten the mergestate file and left an old v2
100 file around.
100 file around.
101
101
102 returns list of record [(TYPE, data), ...]"""
102 returns list of record [(TYPE, data), ...]"""
103 v1records = self._readrecordsv1()
103 v1records = self._readrecordsv1()
104 v2records = self._readrecordsv2()
104 v2records = self._readrecordsv2()
105 oldv2 = set() # old format version of v2 record
105 oldv2 = set() # old format version of v2 record
106 for rec in v2records:
106 for rec in v2records:
107 if rec[0] == 'L':
107 if rec[0] == 'L':
108 oldv2.add(rec)
108 oldv2.add(rec)
109 elif rec[0] == 'F':
109 elif rec[0] == 'F':
110 # drop the onode data (not contained in v1)
110 # drop the onode data (not contained in v1)
111 oldv2.add(('F', _droponode(rec[1])))
111 oldv2.add(('F', _droponode(rec[1])))
112 for rec in v1records:
112 for rec in v1records:
113 if rec not in oldv2:
113 if rec not in oldv2:
114 # v1 file is newer than v2 file, use it
114 # v1 file is newer than v2 file, use it
115 # we have to infer the "other" changeset of the merge
115 # we have to infer the "other" changeset of the merge
116 # we cannot do better than that with v1 of the format
116 # we cannot do better than that with v1 of the format
117 mctx = self._repo[None].parents()[-1]
117 mctx = self._repo[None].parents()[-1]
118 v1records.append(('O', mctx.hex()))
118 v1records.append(('O', mctx.hex()))
119 # add place holder "other" file node information
119 # add place holder "other" file node information
120 # nobody is using it yet so we do no need to fetch the data
120 # nobody is using it yet so we do no need to fetch the data
121 # if mctx was wrong `mctx[bits[-2]]` may fails.
121 # if mctx was wrong `mctx[bits[-2]]` may fails.
122 for idx, r in enumerate(v1records):
122 for idx, r in enumerate(v1records):
123 if r[0] == 'F':
123 if r[0] == 'F':
124 bits = r[1].split("\0")
124 bits = r[1].split("\0")
125 bits.insert(-2, '')
125 bits.insert(-2, '')
126 v1records[idx] = (r[0], "\0".join(bits))
126 v1records[idx] = (r[0], "\0".join(bits))
127 return v1records
127 return v1records
128 else:
128 else:
129 return v2records
129 return v2records
130
130
131 def _readrecordsv1(self):
131 def _readrecordsv1(self):
132 """read on disk merge state for version 1 file
132 """read on disk merge state for version 1 file
133
133
134 returns list of record [(TYPE, data), ...]
134 returns list of record [(TYPE, data), ...]
135
135
136 Note: the "F" data from this file are one entry short
136 Note: the "F" data from this file are one entry short
137 (no "other file node" entry)
137 (no "other file node" entry)
138 """
138 """
139 records = []
139 records = []
140 try:
140 try:
141 f = self._repo.opener(self.statepathv1)
141 f = self._repo.opener(self.statepathv1)
142 for i, l in enumerate(f):
142 for i, l in enumerate(f):
143 if i == 0:
143 if i == 0:
144 records.append(('L', l[:-1]))
144 records.append(('L', l[:-1]))
145 else:
145 else:
146 records.append(('F', l[:-1]))
146 records.append(('F', l[:-1]))
147 f.close()
147 f.close()
148 except IOError, err:
148 except IOError, err:
149 if err.errno != errno.ENOENT:
149 if err.errno != errno.ENOENT:
150 raise
150 raise
151 return records
151 return records
152
152
153 def _readrecordsv2(self):
153 def _readrecordsv2(self):
154 """read on disk merge state for version 2 file
154 """read on disk merge state for version 2 file
155
155
156 returns list of record [(TYPE, data), ...]
156 returns list of record [(TYPE, data), ...]
157 """
157 """
158 records = []
158 records = []
159 try:
159 try:
160 f = self._repo.opener(self.statepathv2)
160 f = self._repo.opener(self.statepathv2)
161 data = f.read()
161 data = f.read()
162 off = 0
162 off = 0
163 end = len(data)
163 end = len(data)
164 while off < end:
164 while off < end:
165 rtype = data[off]
165 rtype = data[off]
166 off += 1
166 off += 1
167 length = _unpack('>I', data[off:(off + 4)])[0]
167 length = _unpack('>I', data[off:(off + 4)])[0]
168 off += 4
168 off += 4
169 record = data[off:(off + length)]
169 record = data[off:(off + length)]
170 off += length
170 off += length
171 records.append((rtype, record))
171 records.append((rtype, record))
172 f.close()
172 f.close()
173 except IOError, err:
173 except IOError, err:
174 if err.errno != errno.ENOENT:
174 if err.errno != errno.ENOENT:
175 raise
175 raise
176 return records
176 return records
177
177
178 def active(self):
178 def active(self):
179 """Whether mergestate is active.
179 """Whether mergestate is active.
180
180
181 Returns True if there appears to be mergestate. This is a rough proxy
181 Returns True if there appears to be mergestate. This is a rough proxy
182 for "is a merge in progress."
182 for "is a merge in progress."
183 """
183 """
184 # Check local variables before looking at filesystem for performance
184 # Check local variables before looking at filesystem for performance
185 # reasons.
185 # reasons.
186 return bool(self._local) or bool(self._state) or \
186 return bool(self._local) or bool(self._state) or \
187 self._repo.opener.exists(self.statepathv1) or \
187 self._repo.opener.exists(self.statepathv1) or \
188 self._repo.opener.exists(self.statepathv2)
188 self._repo.opener.exists(self.statepathv2)
189
189
190 def commit(self):
190 def commit(self):
191 """Write current state on disk (if necessary)"""
191 """Write current state on disk (if necessary)"""
192 if self._dirty:
192 if self._dirty:
193 records = []
193 records = []
194 records.append(("L", hex(self._local)))
194 records.append(("L", hex(self._local)))
195 records.append(("O", hex(self._other)))
195 records.append(("O", hex(self._other)))
196 for d, v in self._state.iteritems():
196 for d, v in self._state.iteritems():
197 records.append(("F", "\0".join([d] + v)))
197 records.append(("F", "\0".join([d] + v)))
198 self._writerecords(records)
198 self._writerecords(records)
199 self._dirty = False
199 self._dirty = False
200
200
201 def _writerecords(self, records):
201 def _writerecords(self, records):
202 """Write current state on disk (both v1 and v2)"""
202 """Write current state on disk (both v1 and v2)"""
203 self._writerecordsv1(records)
203 self._writerecordsv1(records)
204 self._writerecordsv2(records)
204 self._writerecordsv2(records)
205
205
206 def _writerecordsv1(self, records):
206 def _writerecordsv1(self, records):
207 """Write current state on disk in a version 1 file"""
207 """Write current state on disk in a version 1 file"""
208 f = self._repo.opener(self.statepathv1, "w")
208 f = self._repo.opener(self.statepathv1, "w")
209 irecords = iter(records)
209 irecords = iter(records)
210 lrecords = irecords.next()
210 lrecords = irecords.next()
211 assert lrecords[0] == 'L'
211 assert lrecords[0] == 'L'
212 f.write(hex(self._local) + "\n")
212 f.write(hex(self._local) + "\n")
213 for rtype, data in irecords:
213 for rtype, data in irecords:
214 if rtype == "F":
214 if rtype == "F":
215 f.write("%s\n" % _droponode(data))
215 f.write("%s\n" % _droponode(data))
216 f.close()
216 f.close()
217
217
218 def _writerecordsv2(self, records):
218 def _writerecordsv2(self, records):
219 """Write current state on disk in a version 2 file"""
219 """Write current state on disk in a version 2 file"""
220 f = self._repo.opener(self.statepathv2, "w")
220 f = self._repo.opener(self.statepathv2, "w")
221 for key, data in records:
221 for key, data in records:
222 assert len(key) == 1
222 assert len(key) == 1
223 format = ">sI%is" % len(data)
223 format = ">sI%is" % len(data)
224 f.write(_pack(format, key, len(data), data))
224 f.write(_pack(format, key, len(data), data))
225 f.close()
225 f.close()
226
226
227 def add(self, fcl, fco, fca, fd):
227 def add(self, fcl, fco, fca, fd):
228 """add a new (potentially?) conflicting file the merge state
228 """add a new (potentially?) conflicting file the merge state
229 fcl: file context for local,
229 fcl: file context for local,
230 fco: file context for remote,
230 fco: file context for remote,
231 fca: file context for ancestors,
231 fca: file context for ancestors,
232 fd: file path of the resulting merge.
232 fd: file path of the resulting merge.
233
233
234 note: also write the local version to the `.hg/merge` directory.
234 note: also write the local version to the `.hg/merge` directory.
235 """
235 """
236 hash = util.sha1(fcl.path()).hexdigest()
236 hash = util.sha1(fcl.path()).hexdigest()
237 self._repo.opener.write("merge/" + hash, fcl.data())
237 self._repo.opener.write("merge/" + hash, fcl.data())
238 self._state[fd] = ['u', hash, fcl.path(),
238 self._state[fd] = ['u', hash, fcl.path(),
239 fca.path(), hex(fca.filenode()),
239 fca.path(), hex(fca.filenode()),
240 fco.path(), hex(fco.filenode()),
240 fco.path(), hex(fco.filenode()),
241 fcl.flags()]
241 fcl.flags()]
242 self._dirty = True
242 self._dirty = True
243
243
244 def __contains__(self, dfile):
244 def __contains__(self, dfile):
245 return dfile in self._state
245 return dfile in self._state
246
246
247 def __getitem__(self, dfile):
247 def __getitem__(self, dfile):
248 return self._state[dfile][0]
248 return self._state[dfile][0]
249
249
250 def __iter__(self):
250 def __iter__(self):
251 return iter(sorted(self._state))
251 return iter(sorted(self._state))
252
252
253 def files(self):
253 def files(self):
254 return self._state.keys()
254 return self._state.keys()
255
255
256 def mark(self, dfile, state):
256 def mark(self, dfile, state):
257 self._state[dfile][0] = state
257 self._state[dfile][0] = state
258 self._dirty = True
258 self._dirty = True
259
259
260 def unresolved(self):
260 def unresolved(self):
261 """Obtain the paths of unresolved files."""
261 """Obtain the paths of unresolved files."""
262
262
263 for f, entry in self._state.items():
263 for f, entry in self._state.items():
264 if entry[0] == 'u':
264 if entry[0] == 'u':
265 yield f
265 yield f
266
266
267 def resolve(self, dfile, wctx, labels=None):
267 def resolve(self, dfile, wctx, labels=None):
268 """rerun merge process for file path `dfile`"""
268 """rerun merge process for file path `dfile`"""
269 if self[dfile] == 'r':
269 if self[dfile] == 'r':
270 return 0
270 return 0
271 stateentry = self._state[dfile]
271 stateentry = self._state[dfile]
272 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
272 state, hash, lfile, afile, anode, ofile, onode, flags = stateentry
273 octx = self._repo[self._other]
273 octx = self._repo[self._other]
274 fcd = wctx[dfile]
274 fcd = wctx[dfile]
275 fco = octx[ofile]
275 fco = octx[ofile]
276 fca = self._repo.filectx(afile, fileid=anode)
276 fca = self._repo.filectx(afile, fileid=anode)
277 # "premerge" x flags
277 # "premerge" x flags
278 flo = fco.flags()
278 flo = fco.flags()
279 fla = fca.flags()
279 fla = fca.flags()
280 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
280 if 'x' in flags + flo + fla and 'l' not in flags + flo + fla:
281 if fca.node() == nullid:
281 if fca.node() == nullid:
282 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
282 self._repo.ui.warn(_('warning: cannot merge flags for %s\n') %
283 afile)
283 afile)
284 elif flags == fla:
284 elif flags == fla:
285 flags = flo
285 flags = flo
286 # restore local
286 # restore local
287 f = self._repo.opener("merge/" + hash)
287 f = self._repo.opener("merge/" + hash)
288 self._repo.wwrite(dfile, f.read(), flags)
288 self._repo.wwrite(dfile, f.read(), flags)
289 f.close()
289 f.close()
290 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca,
290 r = filemerge.filemerge(self._repo, self._local, lfile, fcd, fco, fca,
291 labels=labels)
291 labels=labels)
292 if r is None:
292 if r is None:
293 # no real conflict
293 # no real conflict
294 del self._state[dfile]
294 del self._state[dfile]
295 self._dirty = True
295 self._dirty = True
296 elif not r:
296 elif not r:
297 self.mark(dfile, 'r')
297 self.mark(dfile, 'r')
298 return r
298 return r
299
299
300 def _checkunknownfile(repo, wctx, mctx, f):
300 def _checkunknownfile(repo, wctx, mctx, f):
301 return (not repo.dirstate._ignore(f)
301 return (not repo.dirstate._ignore(f)
302 and os.path.isfile(repo.wjoin(f))
302 and os.path.isfile(repo.wjoin(f))
303 and repo.wopener.audit.check(f)
303 and repo.wopener.audit.check(f)
304 and repo.dirstate.normalize(f) not in repo.dirstate
304 and repo.dirstate.normalize(f) not in repo.dirstate
305 and mctx[f].cmp(wctx[f]))
305 and mctx[f].cmp(wctx[f]))
306
306
307 def _checkunknown(repo, wctx, mctx):
307 def checkunknown(repo, wctx, mctx):
308 "check for collisions between unknown files and files in mctx"
308 "check for collisions between unknown files and files in mctx"
309
309
310 error = False
310 error = False
311 for f in mctx:
311 for f in mctx:
312 if f not in wctx and _checkunknownfile(repo, wctx, mctx, f):
312 if f not in wctx and _checkunknownfile(repo, wctx, mctx, f):
313 error = True
313 error = True
314 wctx._repo.ui.warn(_("%s: untracked file differs\n") % f)
314 wctx._repo.ui.warn(_("%s: untracked file differs\n") % f)
315 if error:
315 if error:
316 raise util.Abort(_("untracked files in working directory differ "
316 raise util.Abort(_("untracked files in working directory differ "
317 "from files in requested revision"))
317 "from files in requested revision"))
318
318
319 def _forgetremoved(wctx, mctx, branchmerge):
319 def _forgetremoved(wctx, mctx, branchmerge):
320 """
320 """
321 Forget removed files
321 Forget removed files
322
322
323 If we're jumping between revisions (as opposed to merging), and if
323 If we're jumping between revisions (as opposed to merging), and if
324 neither the working directory nor the target rev has the file,
324 neither the working directory nor the target rev has the file,
325 then we need to remove it from the dirstate, to prevent the
325 then we need to remove it from the dirstate, to prevent the
326 dirstate from listing the file when it is no longer in the
326 dirstate from listing the file when it is no longer in the
327 manifest.
327 manifest.
328
328
329 If we're merging, and the other revision has removed a file
329 If we're merging, and the other revision has removed a file
330 that is not present in the working directory, we need to mark it
330 that is not present in the working directory, we need to mark it
331 as removed.
331 as removed.
332 """
332 """
333
333
334 ractions = []
334 ractions = []
335 factions = xactions = []
335 factions = xactions = []
336 if branchmerge:
336 if branchmerge:
337 xactions = ractions
337 xactions = ractions
338 for f in wctx.deleted():
338 for f in wctx.deleted():
339 if f not in mctx:
339 if f not in mctx:
340 xactions.append((f, None, "forget deleted"))
340 xactions.append((f, None, "forget deleted"))
341
341
342 if not branchmerge:
342 if not branchmerge:
343 for f in wctx.removed():
343 for f in wctx.removed():
344 if f not in mctx:
344 if f not in mctx:
345 factions.append((f, None, "forget removed"))
345 factions.append((f, None, "forget removed"))
346
346
347 return ractions, factions
347 return ractions, factions
348
348
349 def _checkcollision(repo, wmf, actions):
349 def _checkcollision(repo, wmf, actions):
350 # build provisional merged manifest up
350 # build provisional merged manifest up
351 pmmf = set(wmf)
351 pmmf = set(wmf)
352
352
353 if actions:
353 if actions:
354 # k, dr, e and rd are no-op
354 # k, dr, e and rd are no-op
355 for m in 'a', 'f', 'g', 'cd', 'dc':
355 for m in 'a', 'f', 'g', 'cd', 'dc':
356 for f, args, msg in actions[m]:
356 for f, args, msg in actions[m]:
357 pmmf.add(f)
357 pmmf.add(f)
358 for f, args, msg in actions['r']:
358 for f, args, msg in actions['r']:
359 pmmf.discard(f)
359 pmmf.discard(f)
360 for f, args, msg in actions['dm']:
360 for f, args, msg in actions['dm']:
361 f2, flags = args
361 f2, flags = args
362 pmmf.discard(f2)
362 pmmf.discard(f2)
363 pmmf.add(f)
363 pmmf.add(f)
364 for f, args, msg in actions['dg']:
364 for f, args, msg in actions['dg']:
365 f2, flags = args
365 f2, flags = args
366 pmmf.add(f)
366 pmmf.add(f)
367 for f, args, msg in actions['m']:
367 for f, args, msg in actions['m']:
368 f1, f2, fa, move, anc = args
368 f1, f2, fa, move, anc = args
369 if move:
369 if move:
370 pmmf.discard(f1)
370 pmmf.discard(f1)
371 pmmf.add(f)
371 pmmf.add(f)
372
372
373 # check case-folding collision in provisional merged manifest
373 # check case-folding collision in provisional merged manifest
374 foldmap = {}
374 foldmap = {}
375 for f in sorted(pmmf):
375 for f in sorted(pmmf):
376 fold = util.normcase(f)
376 fold = util.normcase(f)
377 if fold in foldmap:
377 if fold in foldmap:
378 raise util.Abort(_("case-folding collision between %s and %s")
378 raise util.Abort(_("case-folding collision between %s and %s")
379 % (f, foldmap[fold]))
379 % (f, foldmap[fold]))
380 foldmap[fold] = f
380 foldmap[fold] = f
381
381
382 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
382 def manifestmerge(repo, wctx, p2, pa, branchmerge, force, partial,
383 acceptremote, followcopies):
383 acceptremote, followcopies):
384 """
384 """
385 Merge p1 and p2 with ancestor pa and generate merge action list
385 Merge p1 and p2 with ancestor pa and generate merge action list
386
386
387 branchmerge and force are as passed in to update
387 branchmerge and force are as passed in to update
388 partial = function to filter file lists
388 partial = function to filter file lists
389 acceptremote = accept the incoming changes without prompting
389 acceptremote = accept the incoming changes without prompting
390 """
390 """
391
391
392 actions = dict((m, []) for m in 'a f g cd dc r dm dg m dr e rd k'.split())
392 actions = dict((m, []) for m in 'a f g cd dc r dm dg m dr e rd k'.split())
393 copy, movewithdir = {}, {}
393 copy, movewithdir = {}, {}
394
394
395 # manifests fetched in order are going to be faster, so prime the caches
395 # manifests fetched in order are going to be faster, so prime the caches
396 [x.manifest() for x in
396 [x.manifest() for x in
397 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
397 sorted(wctx.parents() + [p2, pa], key=lambda x: x.rev())]
398
398
399 if followcopies:
399 if followcopies:
400 ret = copies.mergecopies(repo, wctx, p2, pa)
400 ret = copies.mergecopies(repo, wctx, p2, pa)
401 copy, movewithdir, diverge, renamedelete = ret
401 copy, movewithdir, diverge, renamedelete = ret
402 for of, fl in diverge.iteritems():
402 for of, fl in diverge.iteritems():
403 actions['dr'].append((of, (fl,), "divergent renames"))
403 actions['dr'].append((of, (fl,), "divergent renames"))
404 for of, fl in renamedelete.iteritems():
404 for of, fl in renamedelete.iteritems():
405 actions['rd'].append((of, (fl,), "rename and delete"))
405 actions['rd'].append((of, (fl,), "rename and delete"))
406
406
407 repo.ui.note(_("resolving manifests\n"))
407 repo.ui.note(_("resolving manifests\n"))
408 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
408 repo.ui.debug(" branchmerge: %s, force: %s, partial: %s\n"
409 % (bool(branchmerge), bool(force), bool(partial)))
409 % (bool(branchmerge), bool(force), bool(partial)))
410 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
410 repo.ui.debug(" ancestor: %s, local: %s, remote: %s\n" % (pa, wctx, p2))
411
411
412 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
412 m1, m2, ma = wctx.manifest(), p2.manifest(), pa.manifest()
413 copied = set(copy.values())
413 copied = set(copy.values())
414 copied.update(movewithdir.values())
414 copied.update(movewithdir.values())
415
415
416 if '.hgsubstate' in m1:
416 if '.hgsubstate' in m1:
417 # check whether sub state is modified
417 # check whether sub state is modified
418 for s in sorted(wctx.substate):
418 for s in sorted(wctx.substate):
419 if wctx.sub(s).dirty():
419 if wctx.sub(s).dirty():
420 m1['.hgsubstate'] += "+"
420 m1['.hgsubstate'] += "+"
421 break
421 break
422
422
423 aborts = []
423 aborts = []
424 # Compare manifests
424 # Compare manifests
425 diff = m1.diff(m2)
425 diff = m1.diff(m2)
426
426
427 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
427 for f, ((n1, fl1), (n2, fl2)) in diff.iteritems():
428 if partial and not partial(f):
428 if partial and not partial(f):
429 continue
429 continue
430 if n1 and n2:
430 if n1 and n2:
431 fa = f
431 fa = f
432 a = ma.get(f, nullid)
432 a = ma.get(f, nullid)
433 if a == nullid:
433 if a == nullid:
434 fa = copy.get(f, f)
434 fa = copy.get(f, f)
435 # Note: f as default is wrong - we can't really make a 3-way
435 # Note: f as default is wrong - we can't really make a 3-way
436 # merge without an ancestor file.
436 # merge without an ancestor file.
437 fla = ma.flags(fa)
437 fla = ma.flags(fa)
438 nol = 'l' not in fl1 + fl2 + fla
438 nol = 'l' not in fl1 + fl2 + fla
439 if n2 == a and fl2 == fla:
439 if n2 == a and fl2 == fla:
440 actions['k'].append((f, (), "keep")) # remote unchanged
440 actions['k'].append((f, (), "keep")) # remote unchanged
441 elif n1 == a and fl1 == fla: # local unchanged - use remote
441 elif n1 == a and fl1 == fla: # local unchanged - use remote
442 if n1 == n2: # optimization: keep local content
442 if n1 == n2: # optimization: keep local content
443 actions['e'].append((f, (fl2,), "update permissions"))
443 actions['e'].append((f, (fl2,), "update permissions"))
444 else:
444 else:
445 actions['g'].append((f, (fl2,), "remote is newer"))
445 actions['g'].append((f, (fl2,), "remote is newer"))
446 elif nol and n2 == a: # remote only changed 'x'
446 elif nol and n2 == a: # remote only changed 'x'
447 actions['e'].append((f, (fl2,), "update permissions"))
447 actions['e'].append((f, (fl2,), "update permissions"))
448 elif nol and n1 == a: # local only changed 'x'
448 elif nol and n1 == a: # local only changed 'x'
449 actions['g'].append((f, (fl1,), "remote is newer"))
449 actions['g'].append((f, (fl1,), "remote is newer"))
450 else: # both changed something
450 else: # both changed something
451 actions['m'].append((f, (f, f, fa, False, pa.node()),
451 actions['m'].append((f, (f, f, fa, False, pa.node()),
452 "versions differ"))
452 "versions differ"))
453 elif f in copied: # files we'll deal with on m2 side
453 elif f in copied: # files we'll deal with on m2 side
454 pass
454 pass
455 elif n1 and f in movewithdir: # directory rename, move local
455 elif n1 and f in movewithdir: # directory rename, move local
456 f2 = movewithdir[f]
456 f2 = movewithdir[f]
457 actions['dm'].append((f2, (f, fl1),
457 actions['dm'].append((f2, (f, fl1),
458 "remote directory rename - move from " + f))
458 "remote directory rename - move from " + f))
459 elif n1 and f in copy:
459 elif n1 and f in copy:
460 f2 = copy[f]
460 f2 = copy[f]
461 actions['m'].append((f, (f, f2, f2, False, pa.node()),
461 actions['m'].append((f, (f, f2, f2, False, pa.node()),
462 "local copied/moved from " + f2))
462 "local copied/moved from " + f2))
463 elif n1 and f in ma: # clean, a different, no remote
463 elif n1 and f in ma: # clean, a different, no remote
464 if n1 != ma[f]:
464 if n1 != ma[f]:
465 if acceptremote:
465 if acceptremote:
466 actions['r'].append((f, None, "remote delete"))
466 actions['r'].append((f, None, "remote delete"))
467 else:
467 else:
468 actions['cd'].append((f, None, "prompt changed/deleted"))
468 actions['cd'].append((f, None, "prompt changed/deleted"))
469 elif n1[20:] == "a": # added, no remote
469 elif n1[20:] == "a": # added, no remote
470 actions['f'].append((f, None, "remote deleted"))
470 actions['f'].append((f, None, "remote deleted"))
471 else:
471 else:
472 actions['r'].append((f, None, "other deleted"))
472 actions['r'].append((f, None, "other deleted"))
473 elif n2 and f in movewithdir:
473 elif n2 and f in movewithdir:
474 f2 = movewithdir[f]
474 f2 = movewithdir[f]
475 actions['dg'].append((f2, (f, fl2),
475 actions['dg'].append((f2, (f, fl2),
476 "local directory rename - get from " + f))
476 "local directory rename - get from " + f))
477 elif n2 and f in copy:
477 elif n2 and f in copy:
478 f2 = copy[f]
478 f2 = copy[f]
479 if f2 in m2:
479 if f2 in m2:
480 actions['m'].append((f, (f2, f, f2, False, pa.node()),
480 actions['m'].append((f, (f2, f, f2, False, pa.node()),
481 "remote copied from " + f2))
481 "remote copied from " + f2))
482 else:
482 else:
483 actions['m'].append((f, (f2, f, f2, True, pa.node()),
483 actions['m'].append((f, (f2, f, f2, True, pa.node()),
484 "remote moved from " + f2))
484 "remote moved from " + f2))
485 elif n2 and f not in ma:
485 elif n2 and f not in ma:
486 # local unknown, remote created: the logic is described by the
486 # local unknown, remote created: the logic is described by the
487 # following table:
487 # following table:
488 #
488 #
489 # force branchmerge different | action
489 # force branchmerge different | action
490 # n * n | get
490 # n * n | get
491 # n * y | abort
491 # n * y | abort
492 # y n * | get
492 # y n * | get
493 # y y n | get
493 # y y n | get
494 # y y y | merge
494 # y y y | merge
495 #
495 #
496 # Checking whether the files are different is expensive, so we
496 # Checking whether the files are different is expensive, so we
497 # don't do that when we can avoid it.
497 # don't do that when we can avoid it.
498 if force and not branchmerge:
498 if force and not branchmerge:
499 actions['g'].append((f, (fl2,), "remote created"))
499 actions['g'].append((f, (fl2,), "remote created"))
500 else:
500 else:
501 different = _checkunknownfile(repo, wctx, p2, f)
501 different = _checkunknownfile(repo, wctx, p2, f)
502 if force and branchmerge and different:
502 if force and branchmerge and different:
503 # FIXME: This is wrong - f is not in ma ...
503 # FIXME: This is wrong - f is not in ma ...
504 actions['m'].append((f, (f, f, f, False, pa.node()),
504 actions['m'].append((f, (f, f, f, False, pa.node()),
505 "remote differs from untracked local"))
505 "remote differs from untracked local"))
506 elif not force and different:
506 elif not force and different:
507 aborts.append((f, "ud"))
507 aborts.append((f, "ud"))
508 else:
508 else:
509 actions['g'].append((f, (fl2,), "remote created"))
509 actions['g'].append((f, (fl2,), "remote created"))
510 elif n2 and n2 != ma[f]:
510 elif n2 and n2 != ma[f]:
511 different = _checkunknownfile(repo, wctx, p2, f)
511 different = _checkunknownfile(repo, wctx, p2, f)
512 if not force and different:
512 if not force and different:
513 aborts.append((f, "ud"))
513 aborts.append((f, "ud"))
514 else:
514 else:
515 # if different: old untracked f may be overwritten and lost
515 # if different: old untracked f may be overwritten and lost
516 if acceptremote:
516 if acceptremote:
517 actions['g'].append((f, (m2.flags(f),),
517 actions['g'].append((f, (m2.flags(f),),
518 "remote recreating"))
518 "remote recreating"))
519 else:
519 else:
520 actions['dc'].append((f, (m2.flags(f),),
520 actions['dc'].append((f, (m2.flags(f),),
521 "prompt deleted/changed"))
521 "prompt deleted/changed"))
522
522
523 for f, m in sorted(aborts):
523 for f, m in sorted(aborts):
524 if m == "ud":
524 if m == "ud":
525 repo.ui.warn(_("%s: untracked file differs\n") % f)
525 repo.ui.warn(_("%s: untracked file differs\n") % f)
526 else: assert False, m
526 else: assert False, m
527 if aborts:
527 if aborts:
528 raise util.Abort(_("untracked files in working directory differ "
528 raise util.Abort(_("untracked files in working directory differ "
529 "from files in requested revision"))
529 "from files in requested revision"))
530
530
531 if not util.checkcase(repo.path):
531 if not util.checkcase(repo.path):
532 # check collision between files only in p2 for clean update
532 # check collision between files only in p2 for clean update
533 if (not branchmerge and
533 if (not branchmerge and
534 (force or not wctx.dirty(missing=True, branch=False))):
534 (force or not wctx.dirty(missing=True, branch=False))):
535 _checkcollision(repo, m2, None)
535 _checkcollision(repo, m2, None)
536 else:
536 else:
537 _checkcollision(repo, m1, actions)
537 _checkcollision(repo, m1, actions)
538
538
539 return actions
539 return actions
540
540
541 def batchremove(repo, actions):
541 def batchremove(repo, actions):
542 """apply removes to the working directory
542 """apply removes to the working directory
543
543
544 yields tuples for progress updates
544 yields tuples for progress updates
545 """
545 """
546 verbose = repo.ui.verbose
546 verbose = repo.ui.verbose
547 unlink = util.unlinkpath
547 unlink = util.unlinkpath
548 wjoin = repo.wjoin
548 wjoin = repo.wjoin
549 audit = repo.wopener.audit
549 audit = repo.wopener.audit
550 i = 0
550 i = 0
551 for f, args, msg in actions:
551 for f, args, msg in actions:
552 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
552 repo.ui.debug(" %s: %s -> r\n" % (f, msg))
553 if verbose:
553 if verbose:
554 repo.ui.note(_("removing %s\n") % f)
554 repo.ui.note(_("removing %s\n") % f)
555 audit(f)
555 audit(f)
556 try:
556 try:
557 unlink(wjoin(f), ignoremissing=True)
557 unlink(wjoin(f), ignoremissing=True)
558 except OSError, inst:
558 except OSError, inst:
559 repo.ui.warn(_("update failed to remove %s: %s!\n") %
559 repo.ui.warn(_("update failed to remove %s: %s!\n") %
560 (f, inst.strerror))
560 (f, inst.strerror))
561 if i == 100:
561 if i == 100:
562 yield i, f
562 yield i, f
563 i = 0
563 i = 0
564 i += 1
564 i += 1
565 if i > 0:
565 if i > 0:
566 yield i, f
566 yield i, f
567
567
568 def batchget(repo, mctx, actions):
568 def batchget(repo, mctx, actions):
569 """apply gets to the working directory
569 """apply gets to the working directory
570
570
571 mctx is the context to get from
571 mctx is the context to get from
572
572
573 yields tuples for progress updates
573 yields tuples for progress updates
574 """
574 """
575 verbose = repo.ui.verbose
575 verbose = repo.ui.verbose
576 fctx = mctx.filectx
576 fctx = mctx.filectx
577 wwrite = repo.wwrite
577 wwrite = repo.wwrite
578 i = 0
578 i = 0
579 for f, args, msg in actions:
579 for f, args, msg in actions:
580 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
580 repo.ui.debug(" %s: %s -> g\n" % (f, msg))
581 if verbose:
581 if verbose:
582 repo.ui.note(_("getting %s\n") % f)
582 repo.ui.note(_("getting %s\n") % f)
583 wwrite(f, fctx(f).data(), args[0])
583 wwrite(f, fctx(f).data(), args[0])
584 if i == 100:
584 if i == 100:
585 yield i, f
585 yield i, f
586 i = 0
586 i = 0
587 i += 1
587 i += 1
588 if i > 0:
588 if i > 0:
589 yield i, f
589 yield i, f
590
590
591 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
591 def applyupdates(repo, actions, wctx, mctx, overwrite, labels=None):
592 """apply the merge action list to the working directory
592 """apply the merge action list to the working directory
593
593
594 wctx is the working copy context
594 wctx is the working copy context
595 mctx is the context to be merged into the working copy
595 mctx is the context to be merged into the working copy
596
596
597 Return a tuple of counts (updated, merged, removed, unresolved) that
597 Return a tuple of counts (updated, merged, removed, unresolved) that
598 describes how many files were affected by the update.
598 describes how many files were affected by the update.
599 """
599 """
600
600
601 updated, merged, removed, unresolved = 0, 0, 0, 0
601 updated, merged, removed, unresolved = 0, 0, 0, 0
602 ms = mergestate(repo)
602 ms = mergestate(repo)
603 ms.reset(wctx.p1().node(), mctx.node())
603 ms.reset(wctx.p1().node(), mctx.node())
604 moves = []
604 moves = []
605 for m, l in actions.items():
605 for m, l in actions.items():
606 l.sort()
606 l.sort()
607
607
608 # prescan for merges
608 # prescan for merges
609 for f, args, msg in actions['m']:
609 for f, args, msg in actions['m']:
610 f1, f2, fa, move, anc = args
610 f1, f2, fa, move, anc = args
611 if f == '.hgsubstate': # merged internally
611 if f == '.hgsubstate': # merged internally
612 continue
612 continue
613 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
613 repo.ui.debug(" preserving %s for resolve of %s\n" % (f1, f))
614 fcl = wctx[f1]
614 fcl = wctx[f1]
615 fco = mctx[f2]
615 fco = mctx[f2]
616 actx = repo[anc]
616 actx = repo[anc]
617 if fa in actx:
617 if fa in actx:
618 fca = actx[fa]
618 fca = actx[fa]
619 else:
619 else:
620 fca = repo.filectx(f1, fileid=nullrev)
620 fca = repo.filectx(f1, fileid=nullrev)
621 ms.add(fcl, fco, fca, f)
621 ms.add(fcl, fco, fca, f)
622 if f1 != f and move:
622 if f1 != f and move:
623 moves.append(f1)
623 moves.append(f1)
624
624
625 audit = repo.wopener.audit
625 audit = repo.wopener.audit
626 _updating = _('updating')
626 _updating = _('updating')
627 _files = _('files')
627 _files = _('files')
628 progress = repo.ui.progress
628 progress = repo.ui.progress
629
629
630 # remove renamed files after safely stored
630 # remove renamed files after safely stored
631 for f in moves:
631 for f in moves:
632 if os.path.lexists(repo.wjoin(f)):
632 if os.path.lexists(repo.wjoin(f)):
633 repo.ui.debug("removing %s\n" % f)
633 repo.ui.debug("removing %s\n" % f)
634 audit(f)
634 audit(f)
635 util.unlinkpath(repo.wjoin(f))
635 util.unlinkpath(repo.wjoin(f))
636
636
637 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
637 numupdates = sum(len(l) for m, l in actions.items() if m != 'k')
638
638
639 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
639 if [a for a in actions['r'] if a[0] == '.hgsubstate']:
640 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
640 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
641
641
642 # remove in parallel (must come first)
642 # remove in parallel (must come first)
643 z = 0
643 z = 0
644 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
644 prog = worker.worker(repo.ui, 0.001, batchremove, (repo,), actions['r'])
645 for i, item in prog:
645 for i, item in prog:
646 z += i
646 z += i
647 progress(_updating, z, item=item, total=numupdates, unit=_files)
647 progress(_updating, z, item=item, total=numupdates, unit=_files)
648 removed = len(actions['r'])
648 removed = len(actions['r'])
649
649
650 # get in parallel
650 # get in parallel
651 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
651 prog = worker.worker(repo.ui, 0.001, batchget, (repo, mctx), actions['g'])
652 for i, item in prog:
652 for i, item in prog:
653 z += i
653 z += i
654 progress(_updating, z, item=item, total=numupdates, unit=_files)
654 progress(_updating, z, item=item, total=numupdates, unit=_files)
655 updated = len(actions['g'])
655 updated = len(actions['g'])
656
656
657 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
657 if [a for a in actions['g'] if a[0] == '.hgsubstate']:
658 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
658 subrepo.submerge(repo, wctx, mctx, wctx, overwrite)
659
659
660 # forget (manifest only, just log it) (must come first)
660 # forget (manifest only, just log it) (must come first)
661 for f, args, msg in actions['f']:
661 for f, args, msg in actions['f']:
662 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
662 repo.ui.debug(" %s: %s -> f\n" % (f, msg))
663 z += 1
663 z += 1
664 progress(_updating, z, item=f, total=numupdates, unit=_files)
664 progress(_updating, z, item=f, total=numupdates, unit=_files)
665
665
666 # re-add (manifest only, just log it)
666 # re-add (manifest only, just log it)
667 for f, args, msg in actions['a']:
667 for f, args, msg in actions['a']:
668 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
668 repo.ui.debug(" %s: %s -> a\n" % (f, msg))
669 z += 1
669 z += 1
670 progress(_updating, z, item=f, total=numupdates, unit=_files)
670 progress(_updating, z, item=f, total=numupdates, unit=_files)
671
671
672 # keep (noop, just log it)
672 # keep (noop, just log it)
673 for f, args, msg in actions['k']:
673 for f, args, msg in actions['k']:
674 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
674 repo.ui.debug(" %s: %s -> k\n" % (f, msg))
675 # no progress
675 # no progress
676
676
677 # merge
677 # merge
678 for f, args, msg in actions['m']:
678 for f, args, msg in actions['m']:
679 repo.ui.debug(" %s: %s -> m\n" % (f, msg))
679 repo.ui.debug(" %s: %s -> m\n" % (f, msg))
680 z += 1
680 z += 1
681 progress(_updating, z, item=f, total=numupdates, unit=_files)
681 progress(_updating, z, item=f, total=numupdates, unit=_files)
682 f1, f2, fa, move, anc = args
682 f1, f2, fa, move, anc = args
683 if f == '.hgsubstate': # subrepo states need updating
683 if f == '.hgsubstate': # subrepo states need updating
684 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
684 subrepo.submerge(repo, wctx, mctx, wctx.ancestor(mctx),
685 overwrite)
685 overwrite)
686 continue
686 continue
687 audit(f)
687 audit(f)
688 r = ms.resolve(f, wctx, labels=labels)
688 r = ms.resolve(f, wctx, labels=labels)
689 if r is not None and r > 0:
689 if r is not None and r > 0:
690 unresolved += 1
690 unresolved += 1
691 else:
691 else:
692 if r is None:
692 if r is None:
693 updated += 1
693 updated += 1
694 else:
694 else:
695 merged += 1
695 merged += 1
696
696
697 # directory rename, move local
697 # directory rename, move local
698 for f, args, msg in actions['dm']:
698 for f, args, msg in actions['dm']:
699 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
699 repo.ui.debug(" %s: %s -> dm\n" % (f, msg))
700 z += 1
700 z += 1
701 progress(_updating, z, item=f, total=numupdates, unit=_files)
701 progress(_updating, z, item=f, total=numupdates, unit=_files)
702 f0, flags = args
702 f0, flags = args
703 repo.ui.note(_("moving %s to %s\n") % (f0, f))
703 repo.ui.note(_("moving %s to %s\n") % (f0, f))
704 audit(f)
704 audit(f)
705 repo.wwrite(f, wctx.filectx(f0).data(), flags)
705 repo.wwrite(f, wctx.filectx(f0).data(), flags)
706 util.unlinkpath(repo.wjoin(f0))
706 util.unlinkpath(repo.wjoin(f0))
707 updated += 1
707 updated += 1
708
708
709 # local directory rename, get
709 # local directory rename, get
710 for f, args, msg in actions['dg']:
710 for f, args, msg in actions['dg']:
711 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
711 repo.ui.debug(" %s: %s -> dg\n" % (f, msg))
712 z += 1
712 z += 1
713 progress(_updating, z, item=f, total=numupdates, unit=_files)
713 progress(_updating, z, item=f, total=numupdates, unit=_files)
714 f0, flags = args
714 f0, flags = args
715 repo.ui.note(_("getting %s to %s\n") % (f0, f))
715 repo.ui.note(_("getting %s to %s\n") % (f0, f))
716 repo.wwrite(f, mctx.filectx(f0).data(), flags)
716 repo.wwrite(f, mctx.filectx(f0).data(), flags)
717 updated += 1
717 updated += 1
718
718
719 # divergent renames
719 # divergent renames
720 for f, args, msg in actions['dr']:
720 for f, args, msg in actions['dr']:
721 repo.ui.debug(" %s: %s -> dr\n" % (f, msg))
721 repo.ui.debug(" %s: %s -> dr\n" % (f, msg))
722 z += 1
722 z += 1
723 progress(_updating, z, item=f, total=numupdates, unit=_files)
723 progress(_updating, z, item=f, total=numupdates, unit=_files)
724 fl, = args
724 fl, = args
725 repo.ui.warn(_("note: possible conflict - %s was renamed "
725 repo.ui.warn(_("note: possible conflict - %s was renamed "
726 "multiple times to:\n") % f)
726 "multiple times to:\n") % f)
727 for nf in fl:
727 for nf in fl:
728 repo.ui.warn(" %s\n" % nf)
728 repo.ui.warn(" %s\n" % nf)
729
729
730 # rename and delete
730 # rename and delete
731 for f, args, msg in actions['rd']:
731 for f, args, msg in actions['rd']:
732 repo.ui.debug(" %s: %s -> rd\n" % (f, msg))
732 repo.ui.debug(" %s: %s -> rd\n" % (f, msg))
733 z += 1
733 z += 1
734 progress(_updating, z, item=f, total=numupdates, unit=_files)
734 progress(_updating, z, item=f, total=numupdates, unit=_files)
735 fl, = args
735 fl, = args
736 repo.ui.warn(_("note: possible conflict - %s was deleted "
736 repo.ui.warn(_("note: possible conflict - %s was deleted "
737 "and renamed to:\n") % f)
737 "and renamed to:\n") % f)
738 for nf in fl:
738 for nf in fl:
739 repo.ui.warn(" %s\n" % nf)
739 repo.ui.warn(" %s\n" % nf)
740
740
741 # exec
741 # exec
742 for f, args, msg in actions['e']:
742 for f, args, msg in actions['e']:
743 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
743 repo.ui.debug(" %s: %s -> e\n" % (f, msg))
744 z += 1
744 z += 1
745 progress(_updating, z, item=f, total=numupdates, unit=_files)
745 progress(_updating, z, item=f, total=numupdates, unit=_files)
746 flags, = args
746 flags, = args
747 audit(f)
747 audit(f)
748 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
748 util.setflags(repo.wjoin(f), 'l' in flags, 'x' in flags)
749 updated += 1
749 updated += 1
750
750
751 ms.commit()
751 ms.commit()
752 progress(_updating, None, total=numupdates, unit=_files)
752 progress(_updating, None, total=numupdates, unit=_files)
753
753
754 return updated, merged, removed, unresolved
754 return updated, merged, removed, unresolved
755
755
756 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
756 def calculateupdates(repo, wctx, mctx, ancestors, branchmerge, force, partial,
757 acceptremote, followcopies):
757 acceptremote, followcopies):
758 "Calculate the actions needed to merge mctx into wctx using ancestors"
758 "Calculate the actions needed to merge mctx into wctx using ancestors"
759
759
760 if len(ancestors) == 1: # default
760 if len(ancestors) == 1: # default
761 actions = manifestmerge(repo, wctx, mctx, ancestors[0],
761 actions = manifestmerge(repo, wctx, mctx, ancestors[0],
762 branchmerge, force,
762 branchmerge, force,
763 partial, acceptremote, followcopies)
763 partial, acceptremote, followcopies)
764
764
765 else: # only when merge.preferancestor=* - the default
765 else: # only when merge.preferancestor=* - the default
766 repo.ui.note(
766 repo.ui.note(
767 _("note: merging %s and %s using bids from ancestors %s\n") %
767 _("note: merging %s and %s using bids from ancestors %s\n") %
768 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
768 (wctx, mctx, _(' and ').join(str(anc) for anc in ancestors)))
769
769
770 # Call for bids
770 # Call for bids
771 fbids = {} # mapping filename to bids (action method to list af actions)
771 fbids = {} # mapping filename to bids (action method to list af actions)
772 for ancestor in ancestors:
772 for ancestor in ancestors:
773 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
773 repo.ui.note(_('\ncalculating bids for ancestor %s\n') % ancestor)
774 actions = manifestmerge(repo, wctx, mctx, ancestor,
774 actions = manifestmerge(repo, wctx, mctx, ancestor,
775 branchmerge, force,
775 branchmerge, force,
776 partial, acceptremote, followcopies)
776 partial, acceptremote, followcopies)
777 for m, l in sorted(actions.items()):
777 for m, l in sorted(actions.items()):
778 for a in l:
778 for a in l:
779 f, args, msg = a
779 f, args, msg = a
780 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
780 repo.ui.debug(' %s: %s -> %s\n' % (f, msg, m))
781 if f in fbids:
781 if f in fbids:
782 d = fbids[f]
782 d = fbids[f]
783 if m in d:
783 if m in d:
784 d[m].append(a)
784 d[m].append(a)
785 else:
785 else:
786 d[m] = [a]
786 d[m] = [a]
787 else:
787 else:
788 fbids[f] = {m: [a]}
788 fbids[f] = {m: [a]}
789
789
790 # Pick the best bid for each file
790 # Pick the best bid for each file
791 repo.ui.note(_('\nauction for merging merge bids\n'))
791 repo.ui.note(_('\nauction for merging merge bids\n'))
792 actions = dict((m, []) for m in actions.keys())
792 actions = dict((m, []) for m in actions.keys())
793 for f, bids in sorted(fbids.items()):
793 for f, bids in sorted(fbids.items()):
794 # bids is a mapping from action method to list af actions
794 # bids is a mapping from action method to list af actions
795 # Consensus?
795 # Consensus?
796 if len(bids) == 1: # all bids are the same kind of method
796 if len(bids) == 1: # all bids are the same kind of method
797 m, l = bids.items()[0]
797 m, l = bids.items()[0]
798 if util.all(a == l[0] for a in l[1:]): # len(bids) is > 1
798 if util.all(a == l[0] for a in l[1:]): # len(bids) is > 1
799 repo.ui.note(" %s: consensus for %s\n" % (f, m))
799 repo.ui.note(" %s: consensus for %s\n" % (f, m))
800 actions[m].append(l[0])
800 actions[m].append(l[0])
801 continue
801 continue
802 # If keep is an option, just do it.
802 # If keep is an option, just do it.
803 if "k" in bids:
803 if "k" in bids:
804 repo.ui.note(" %s: picking 'keep' action\n" % f)
804 repo.ui.note(" %s: picking 'keep' action\n" % f)
805 actions['k'].append(bids["k"][0])
805 actions['k'].append(bids["k"][0])
806 continue
806 continue
807 # If there are gets and they all agree [how could they not?], do it.
807 # If there are gets and they all agree [how could they not?], do it.
808 if "g" in bids:
808 if "g" in bids:
809 ga0 = bids["g"][0]
809 ga0 = bids["g"][0]
810 if util.all(a == ga0 for a in bids["g"][1:]):
810 if util.all(a == ga0 for a in bids["g"][1:]):
811 repo.ui.note(" %s: picking 'get' action\n" % f)
811 repo.ui.note(" %s: picking 'get' action\n" % f)
812 actions['g'].append(ga0)
812 actions['g'].append(ga0)
813 continue
813 continue
814 # TODO: Consider other simple actions such as mode changes
814 # TODO: Consider other simple actions such as mode changes
815 # Handle inefficient democrazy.
815 # Handle inefficient democrazy.
816 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
816 repo.ui.note(_(' %s: multiple bids for merge action:\n') % f)
817 for m, l in sorted(bids.items()):
817 for m, l in sorted(bids.items()):
818 for _f, args, msg in l:
818 for _f, args, msg in l:
819 repo.ui.note(' %s -> %s\n' % (msg, m))
819 repo.ui.note(' %s -> %s\n' % (msg, m))
820 # Pick random action. TODO: Instead, prompt user when resolving
820 # Pick random action. TODO: Instead, prompt user when resolving
821 m, l = bids.items()[0]
821 m, l = bids.items()[0]
822 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
822 repo.ui.warn(_(' %s: ambiguous merge - picked %s action\n') %
823 (f, m))
823 (f, m))
824 actions[m].append(l[0])
824 actions[m].append(l[0])
825 continue
825 continue
826 repo.ui.note(_('end of auction\n\n'))
826 repo.ui.note(_('end of auction\n\n'))
827
827
828 # Prompt and create actions. TODO: Move this towards resolve phase.
828 # Prompt and create actions. TODO: Move this towards resolve phase.
829 for f, args, msg in actions['cd']:
829 for f, args, msg in actions['cd']:
830 if repo.ui.promptchoice(
830 if repo.ui.promptchoice(
831 _("local changed %s which remote deleted\n"
831 _("local changed %s which remote deleted\n"
832 "use (c)hanged version or (d)elete?"
832 "use (c)hanged version or (d)elete?"
833 "$$ &Changed $$ &Delete") % f, 0):
833 "$$ &Changed $$ &Delete") % f, 0):
834 actions['r'].append((f, None, "prompt delete"))
834 actions['r'].append((f, None, "prompt delete"))
835 else:
835 else:
836 actions['a'].append((f, None, "prompt keep"))
836 actions['a'].append((f, None, "prompt keep"))
837 del actions['cd'][:]
837 del actions['cd'][:]
838
838
839 for f, args, msg in actions['dc']:
839 for f, args, msg in actions['dc']:
840 flags, = args
840 flags, = args
841 if repo.ui.promptchoice(
841 if repo.ui.promptchoice(
842 _("remote changed %s which local deleted\n"
842 _("remote changed %s which local deleted\n"
843 "use (c)hanged version or leave (d)eleted?"
843 "use (c)hanged version or leave (d)eleted?"
844 "$$ &Changed $$ &Deleted") % f, 0) == 0:
844 "$$ &Changed $$ &Deleted") % f, 0) == 0:
845 actions['g'].append((f, (flags,), "prompt recreating"))
845 actions['g'].append((f, (flags,), "prompt recreating"))
846 del actions['dc'][:]
846 del actions['dc'][:]
847
847
848 if wctx.rev() is None:
848 if wctx.rev() is None:
849 ractions, factions = _forgetremoved(wctx, mctx, branchmerge)
849 ractions, factions = _forgetremoved(wctx, mctx, branchmerge)
850 actions['r'].extend(ractions)
850 actions['r'].extend(ractions)
851 actions['f'].extend(factions)
851 actions['f'].extend(factions)
852
852
853 return actions
853 return actions
854
854
855 def recordupdates(repo, actions, branchmerge):
855 def recordupdates(repo, actions, branchmerge):
856 "record merge actions to the dirstate"
856 "record merge actions to the dirstate"
857 # remove (must come first)
857 # remove (must come first)
858 for f, args, msg in actions['r']:
858 for f, args, msg in actions['r']:
859 if branchmerge:
859 if branchmerge:
860 repo.dirstate.remove(f)
860 repo.dirstate.remove(f)
861 else:
861 else:
862 repo.dirstate.drop(f)
862 repo.dirstate.drop(f)
863
863
864 # forget (must come first)
864 # forget (must come first)
865 for f, args, msg in actions['f']:
865 for f, args, msg in actions['f']:
866 repo.dirstate.drop(f)
866 repo.dirstate.drop(f)
867
867
868 # re-add
868 # re-add
869 for f, args, msg in actions['a']:
869 for f, args, msg in actions['a']:
870 if not branchmerge:
870 if not branchmerge:
871 repo.dirstate.add(f)
871 repo.dirstate.add(f)
872
872
873 # exec change
873 # exec change
874 for f, args, msg in actions['e']:
874 for f, args, msg in actions['e']:
875 repo.dirstate.normallookup(f)
875 repo.dirstate.normallookup(f)
876
876
877 # keep
877 # keep
878 for f, args, msg in actions['k']:
878 for f, args, msg in actions['k']:
879 pass
879 pass
880
880
881 # get
881 # get
882 for f, args, msg in actions['g']:
882 for f, args, msg in actions['g']:
883 if branchmerge:
883 if branchmerge:
884 repo.dirstate.otherparent(f)
884 repo.dirstate.otherparent(f)
885 else:
885 else:
886 repo.dirstate.normal(f)
886 repo.dirstate.normal(f)
887
887
888 # merge
888 # merge
889 for f, args, msg in actions['m']:
889 for f, args, msg in actions['m']:
890 f1, f2, fa, move, anc = args
890 f1, f2, fa, move, anc = args
891 if branchmerge:
891 if branchmerge:
892 # We've done a branch merge, mark this file as merged
892 # We've done a branch merge, mark this file as merged
893 # so that we properly record the merger later
893 # so that we properly record the merger later
894 repo.dirstate.merge(f)
894 repo.dirstate.merge(f)
895 if f1 != f2: # copy/rename
895 if f1 != f2: # copy/rename
896 if move:
896 if move:
897 repo.dirstate.remove(f1)
897 repo.dirstate.remove(f1)
898 if f1 != f:
898 if f1 != f:
899 repo.dirstate.copy(f1, f)
899 repo.dirstate.copy(f1, f)
900 else:
900 else:
901 repo.dirstate.copy(f2, f)
901 repo.dirstate.copy(f2, f)
902 else:
902 else:
903 # We've update-merged a locally modified file, so
903 # We've update-merged a locally modified file, so
904 # we set the dirstate to emulate a normal checkout
904 # we set the dirstate to emulate a normal checkout
905 # of that file some time in the past. Thus our
905 # of that file some time in the past. Thus our
906 # merge will appear as a normal local file
906 # merge will appear as a normal local file
907 # modification.
907 # modification.
908 if f2 == f: # file not locally copied/moved
908 if f2 == f: # file not locally copied/moved
909 repo.dirstate.normallookup(f)
909 repo.dirstate.normallookup(f)
910 if move:
910 if move:
911 repo.dirstate.drop(f1)
911 repo.dirstate.drop(f1)
912
912
913 # directory rename, move local
913 # directory rename, move local
914 for f, args, msg in actions['dm']:
914 for f, args, msg in actions['dm']:
915 f0, flag = args
915 f0, flag = args
916 if f0 not in repo.dirstate:
916 if f0 not in repo.dirstate:
917 # untracked file moved
917 # untracked file moved
918 continue
918 continue
919 if branchmerge:
919 if branchmerge:
920 repo.dirstate.add(f)
920 repo.dirstate.add(f)
921 repo.dirstate.remove(f0)
921 repo.dirstate.remove(f0)
922 repo.dirstate.copy(f0, f)
922 repo.dirstate.copy(f0, f)
923 else:
923 else:
924 repo.dirstate.normal(f)
924 repo.dirstate.normal(f)
925 repo.dirstate.drop(f0)
925 repo.dirstate.drop(f0)
926
926
927 # directory rename, get
927 # directory rename, get
928 for f, args, msg in actions['dg']:
928 for f, args, msg in actions['dg']:
929 f0, flag = args
929 f0, flag = args
930 if branchmerge:
930 if branchmerge:
931 repo.dirstate.add(f)
931 repo.dirstate.add(f)
932 repo.dirstate.copy(f0, f)
932 repo.dirstate.copy(f0, f)
933 else:
933 else:
934 repo.dirstate.normal(f)
934 repo.dirstate.normal(f)
935
935
936 def update(repo, node, branchmerge, force, partial, ancestor=None,
936 def update(repo, node, branchmerge, force, partial, ancestor=None,
937 mergeancestor=False, labels=None):
937 mergeancestor=False, labels=None):
938 """
938 """
939 Perform a merge between the working directory and the given node
939 Perform a merge between the working directory and the given node
940
940
941 node = the node to update to, or None if unspecified
941 node = the node to update to, or None if unspecified
942 branchmerge = whether to merge between branches
942 branchmerge = whether to merge between branches
943 force = whether to force branch merging or file overwriting
943 force = whether to force branch merging or file overwriting
944 partial = a function to filter file lists (dirstate not updated)
944 partial = a function to filter file lists (dirstate not updated)
945 mergeancestor = whether it is merging with an ancestor. If true,
945 mergeancestor = whether it is merging with an ancestor. If true,
946 we should accept the incoming changes for any prompts that occur.
946 we should accept the incoming changes for any prompts that occur.
947 If false, merging with an ancestor (fast-forward) is only allowed
947 If false, merging with an ancestor (fast-forward) is only allowed
948 between different named branches. This flag is used by rebase extension
948 between different named branches. This flag is used by rebase extension
949 as a temporary fix and should be avoided in general.
949 as a temporary fix and should be avoided in general.
950
950
951 The table below shows all the behaviors of the update command
951 The table below shows all the behaviors of the update command
952 given the -c and -C or no options, whether the working directory
952 given the -c and -C or no options, whether the working directory
953 is dirty, whether a revision is specified, and the relationship of
953 is dirty, whether a revision is specified, and the relationship of
954 the parent rev to the target rev (linear, on the same named
954 the parent rev to the target rev (linear, on the same named
955 branch, or on another named branch).
955 branch, or on another named branch).
956
956
957 This logic is tested by test-update-branches.t.
957 This logic is tested by test-update-branches.t.
958
958
959 -c -C dirty rev | linear same cross
959 -c -C dirty rev | linear same cross
960 n n n n | ok (1) x
960 n n n n | ok (1) x
961 n n n y | ok ok ok
961 n n n y | ok ok ok
962 n n y n | merge (2) (2)
962 n n y n | merge (2) (2)
963 n n y y | merge (3) (3)
963 n n y y | merge (3) (3)
964 n y * * | --- discard ---
964 n y * * | --- discard ---
965 y n y * | --- (4) ---
965 y n y * | --- (4) ---
966 y n n * | --- ok ---
966 y n n * | --- ok ---
967 y y * * | --- (5) ---
967 y y * * | --- (5) ---
968
968
969 x = can't happen
969 x = can't happen
970 * = don't-care
970 * = don't-care
971 1 = abort: not a linear update (merge or update --check to force update)
971 1 = abort: not a linear update (merge or update --check to force update)
972 2 = abort: uncommitted changes (commit and merge, or update --clean to
972 2 = abort: uncommitted changes (commit and merge, or update --clean to
973 discard changes)
973 discard changes)
974 3 = abort: uncommitted changes (commit or update --clean to discard changes)
974 3 = abort: uncommitted changes (commit or update --clean to discard changes)
975 4 = abort: uncommitted changes (checked in commands.py)
975 4 = abort: uncommitted changes (checked in commands.py)
976 5 = incompatible options (checked in commands.py)
976 5 = incompatible options (checked in commands.py)
977
977
978 Return the same tuple as applyupdates().
978 Return the same tuple as applyupdates().
979 """
979 """
980
980
981 onode = node
981 onode = node
982 wlock = repo.wlock()
982 wlock = repo.wlock()
983 try:
983 try:
984 wc = repo[None]
984 wc = repo[None]
985 pl = wc.parents()
985 pl = wc.parents()
986 p1 = pl[0]
986 p1 = pl[0]
987 pas = [None]
987 pas = [None]
988 if ancestor:
988 if ancestor:
989 pas = [repo[ancestor]]
989 pas = [repo[ancestor]]
990
990
991 if node is None:
991 if node is None:
992 # Here is where we should consider bookmarks, divergent bookmarks,
992 # Here is where we should consider bookmarks, divergent bookmarks,
993 # foreground changesets (successors), and tip of current branch;
993 # foreground changesets (successors), and tip of current branch;
994 # but currently we are only checking the branch tips.
994 # but currently we are only checking the branch tips.
995 try:
995 try:
996 node = repo.branchtip(wc.branch())
996 node = repo.branchtip(wc.branch())
997 except errormod.RepoLookupError:
997 except errormod.RepoLookupError:
998 if wc.branch() == "default": # no default branch!
998 if wc.branch() == "default": # no default branch!
999 node = repo.lookup("tip") # update to tip
999 node = repo.lookup("tip") # update to tip
1000 else:
1000 else:
1001 raise util.Abort(_("branch %s not found") % wc.branch())
1001 raise util.Abort(_("branch %s not found") % wc.branch())
1002
1002
1003 if p1.obsolete() and not p1.children():
1003 if p1.obsolete() and not p1.children():
1004 # allow updating to successors
1004 # allow updating to successors
1005 successors = obsolete.successorssets(repo, p1.node())
1005 successors = obsolete.successorssets(repo, p1.node())
1006
1006
1007 # behavior of certain cases is as follows,
1007 # behavior of certain cases is as follows,
1008 #
1008 #
1009 # divergent changesets: update to highest rev, similar to what
1009 # divergent changesets: update to highest rev, similar to what
1010 # is currently done when there are more than one head
1010 # is currently done when there are more than one head
1011 # (i.e. 'tip')
1011 # (i.e. 'tip')
1012 #
1012 #
1013 # replaced changesets: same as divergent except we know there
1013 # replaced changesets: same as divergent except we know there
1014 # is no conflict
1014 # is no conflict
1015 #
1015 #
1016 # pruned changeset: no update is done; though, we could
1016 # pruned changeset: no update is done; though, we could
1017 # consider updating to the first non-obsolete parent,
1017 # consider updating to the first non-obsolete parent,
1018 # similar to what is current done for 'hg prune'
1018 # similar to what is current done for 'hg prune'
1019
1019
1020 if successors:
1020 if successors:
1021 # flatten the list here handles both divergent (len > 1)
1021 # flatten the list here handles both divergent (len > 1)
1022 # and the usual case (len = 1)
1022 # and the usual case (len = 1)
1023 successors = [n for sub in successors for n in sub]
1023 successors = [n for sub in successors for n in sub]
1024
1024
1025 # get the max revision for the given successors set,
1025 # get the max revision for the given successors set,
1026 # i.e. the 'tip' of a set
1026 # i.e. the 'tip' of a set
1027 node = repo.revs("max(%ln)", successors).first()
1027 node = repo.revs("max(%ln)", successors).first()
1028 pas = [p1]
1028 pas = [p1]
1029
1029
1030 overwrite = force and not branchmerge
1030 overwrite = force and not branchmerge
1031
1031
1032 p2 = repo[node]
1032 p2 = repo[node]
1033 if pas[0] is None:
1033 if pas[0] is None:
1034 if repo.ui.config("merge", "preferancestor", '*') == '*':
1034 if repo.ui.config("merge", "preferancestor", '*') == '*':
1035 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1035 cahs = repo.changelog.commonancestorsheads(p1.node(), p2.node())
1036 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1036 pas = [repo[anc] for anc in (sorted(cahs) or [nullid])]
1037 else:
1037 else:
1038 pas = [p1.ancestor(p2, warn=branchmerge)]
1038 pas = [p1.ancestor(p2, warn=branchmerge)]
1039
1039
1040 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1040 fp1, fp2, xp1, xp2 = p1.node(), p2.node(), str(p1), str(p2)
1041
1041
1042 ### check phase
1042 ### check phase
1043 if not overwrite and len(pl) > 1:
1043 if not overwrite and len(pl) > 1:
1044 raise util.Abort(_("outstanding uncommitted merge"))
1044 raise util.Abort(_("outstanding uncommitted merge"))
1045 if branchmerge:
1045 if branchmerge:
1046 if pas == [p2]:
1046 if pas == [p2]:
1047 raise util.Abort(_("merging with a working directory ancestor"
1047 raise util.Abort(_("merging with a working directory ancestor"
1048 " has no effect"))
1048 " has no effect"))
1049 elif pas == [p1]:
1049 elif pas == [p1]:
1050 if not mergeancestor and p1.branch() == p2.branch():
1050 if not mergeancestor and p1.branch() == p2.branch():
1051 raise util.Abort(_("nothing to merge"),
1051 raise util.Abort(_("nothing to merge"),
1052 hint=_("use 'hg update' "
1052 hint=_("use 'hg update' "
1053 "or check 'hg heads'"))
1053 "or check 'hg heads'"))
1054 if not force and (wc.files() or wc.deleted()):
1054 if not force and (wc.files() or wc.deleted()):
1055 raise util.Abort(_("uncommitted changes"),
1055 raise util.Abort(_("uncommitted changes"),
1056 hint=_("use 'hg status' to list changes"))
1056 hint=_("use 'hg status' to list changes"))
1057 for s in sorted(wc.substate):
1057 for s in sorted(wc.substate):
1058 if wc.sub(s).dirty():
1058 if wc.sub(s).dirty():
1059 raise util.Abort(_("uncommitted changes in "
1059 raise util.Abort(_("uncommitted changes in "
1060 "subrepository '%s'") % s)
1060 "subrepository '%s'") % s)
1061
1061
1062 elif not overwrite:
1062 elif not overwrite:
1063 if p1 == p2: # no-op update
1063 if p1 == p2: # no-op update
1064 # call the hooks and exit early
1064 # call the hooks and exit early
1065 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1065 repo.hook('preupdate', throw=True, parent1=xp2, parent2='')
1066 repo.hook('update', parent1=xp2, parent2='', error=0)
1066 repo.hook('update', parent1=xp2, parent2='', error=0)
1067 return 0, 0, 0, 0
1067 return 0, 0, 0, 0
1068
1068
1069 if pas not in ([p1], [p2]): # nonlinear
1069 if pas not in ([p1], [p2]): # nonlinear
1070 dirty = wc.dirty(missing=True)
1070 dirty = wc.dirty(missing=True)
1071 if dirty or onode is None:
1071 if dirty or onode is None:
1072 # Branching is a bit strange to ensure we do the minimal
1072 # Branching is a bit strange to ensure we do the minimal
1073 # amount of call to obsolete.background.
1073 # amount of call to obsolete.background.
1074 foreground = obsolete.foreground(repo, [p1.node()])
1074 foreground = obsolete.foreground(repo, [p1.node()])
1075 # note: the <node> variable contains a random identifier
1075 # note: the <node> variable contains a random identifier
1076 if repo[node].node() in foreground:
1076 if repo[node].node() in foreground:
1077 pas = [p1] # allow updating to successors
1077 pas = [p1] # allow updating to successors
1078 elif dirty:
1078 elif dirty:
1079 msg = _("uncommitted changes")
1079 msg = _("uncommitted changes")
1080 if onode is None:
1080 if onode is None:
1081 hint = _("commit and merge, or update --clean to"
1081 hint = _("commit and merge, or update --clean to"
1082 " discard changes")
1082 " discard changes")
1083 else:
1083 else:
1084 hint = _("commit or update --clean to discard"
1084 hint = _("commit or update --clean to discard"
1085 " changes")
1085 " changes")
1086 raise util.Abort(msg, hint=hint)
1086 raise util.Abort(msg, hint=hint)
1087 else: # node is none
1087 else: # node is none
1088 msg = _("not a linear update")
1088 msg = _("not a linear update")
1089 hint = _("merge or update --check to force update")
1089 hint = _("merge or update --check to force update")
1090 raise util.Abort(msg, hint=hint)
1090 raise util.Abort(msg, hint=hint)
1091 else:
1091 else:
1092 # Allow jumping branches if clean and specific rev given
1092 # Allow jumping branches if clean and specific rev given
1093 pas = [p1]
1093 pas = [p1]
1094
1094
1095 followcopies = False
1095 followcopies = False
1096 if overwrite:
1096 if overwrite:
1097 pas = [wc]
1097 pas = [wc]
1098 elif pas == [p2]: # backwards
1098 elif pas == [p2]: # backwards
1099 pas = [wc.p1()]
1099 pas = [wc.p1()]
1100 elif not branchmerge and not wc.dirty(missing=True):
1100 elif not branchmerge and not wc.dirty(missing=True):
1101 pass
1101 pass
1102 elif pas[0] and repo.ui.configbool("merge", "followcopies", True):
1102 elif pas[0] and repo.ui.configbool("merge", "followcopies", True):
1103 followcopies = True
1103 followcopies = True
1104
1104
1105 ### calculate phase
1105 ### calculate phase
1106 actions = calculateupdates(repo, wc, p2, pas, branchmerge, force,
1106 actions = calculateupdates(repo, wc, p2, pas, branchmerge, force,
1107 partial, mergeancestor, followcopies)
1107 partial, mergeancestor, followcopies)
1108
1108
1109 ### apply phase
1109 ### apply phase
1110 if not branchmerge: # just jump to the new rev
1110 if not branchmerge: # just jump to the new rev
1111 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1111 fp1, fp2, xp1, xp2 = fp2, nullid, xp2, ''
1112 if not partial:
1112 if not partial:
1113 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1113 repo.hook('preupdate', throw=True, parent1=xp1, parent2=xp2)
1114 # note that we're in the middle of an update
1114 # note that we're in the middle of an update
1115 repo.vfs.write('updatestate', p2.hex())
1115 repo.vfs.write('updatestate', p2.hex())
1116
1116
1117 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1117 stats = applyupdates(repo, actions, wc, p2, overwrite, labels=labels)
1118
1118
1119 if not partial:
1119 if not partial:
1120 repo.dirstate.beginparentchange()
1120 repo.dirstate.beginparentchange()
1121 repo.setparents(fp1, fp2)
1121 repo.setparents(fp1, fp2)
1122 recordupdates(repo, actions, branchmerge)
1122 recordupdates(repo, actions, branchmerge)
1123 # update completed, clear state
1123 # update completed, clear state
1124 util.unlink(repo.join('updatestate'))
1124 util.unlink(repo.join('updatestate'))
1125
1125
1126 if not branchmerge:
1126 if not branchmerge:
1127 repo.dirstate.setbranch(p2.branch())
1127 repo.dirstate.setbranch(p2.branch())
1128 repo.dirstate.endparentchange()
1128 repo.dirstate.endparentchange()
1129 finally:
1129 finally:
1130 wlock.release()
1130 wlock.release()
1131
1131
1132 if not partial:
1132 if not partial:
1133 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1133 repo.hook('update', parent1=xp1, parent2=xp2, error=stats[3])
1134 return stats
1134 return stats
1135
1135
1136 def graft(repo, ctx, pctx, labels):
1136 def graft(repo, ctx, pctx, labels):
1137 """Do a graft-like merge.
1137 """Do a graft-like merge.
1138
1138
1139 This is a merge where the merge ancestor is chosen such that one
1139 This is a merge where the merge ancestor is chosen such that one
1140 or more changesets are grafted onto the current changeset. In
1140 or more changesets are grafted onto the current changeset. In
1141 addition to the merge, this fixes up the dirstate to include only
1141 addition to the merge, this fixes up the dirstate to include only
1142 a single parent and tries to duplicate any renames/copies
1142 a single parent and tries to duplicate any renames/copies
1143 appropriately.
1143 appropriately.
1144
1144
1145 ctx - changeset to rebase
1145 ctx - changeset to rebase
1146 pctx - merge base, usually ctx.p1()
1146 pctx - merge base, usually ctx.p1()
1147 labels - merge labels eg ['local', 'graft']
1147 labels - merge labels eg ['local', 'graft']
1148
1148
1149 """
1149 """
1150
1150
1151 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1151 stats = update(repo, ctx.node(), True, True, False, pctx.node(),
1152 labels=labels)
1152 labels=labels)
1153 # drop the second merge parent
1153 # drop the second merge parent
1154 repo.dirstate.beginparentchange()
1154 repo.dirstate.beginparentchange()
1155 repo.setparents(repo['.'].node(), nullid)
1155 repo.setparents(repo['.'].node(), nullid)
1156 repo.dirstate.write()
1156 repo.dirstate.write()
1157 # fix up dirstate for copies and renames
1157 # fix up dirstate for copies and renames
1158 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1158 copies.duplicatecopies(repo, ctx.rev(), pctx.rev())
1159 repo.dirstate.endparentchange()
1159 repo.dirstate.endparentchange()
1160 return stats
1160 return stats
General Comments 0
You need to be logged in to leave comments. Login now