##// END OF EJS Templates
graft: add a `--to` flag grafting in memory...
marmoute -
r53402:68dc6cec default
parent child Browse files
Show More
@@ -1,326 +1,413
1 1 # graft.py - implementation of the graft command
2 2
3 3 from __future__ import annotations
4 4
5 5 import typing
6 6
7 7 from typing import (
8 8 Any,
9 9 Tuple,
10 10 )
11 11
12 12 from ..i18n import _
13 13
14 from .. import cmdutil, error, logcmdutil, merge as mergemod, state as statemod
14 from .. import (
15 cmdutil,
16 context,
17 error,
18 logcmdutil,
19 merge as mergemod,
20 state as statemod,
21 )
15 22
16 23
17 24 if typing.TYPE_CHECKING:
18 25 _ActionT = str
19 26 _CmdArgsT = Any # TODO: (statedata, revs, editor, cont, dry_run, tool)
20 27
21 28
22 29 def cmd_graft(ui, repo, *revs, **opts) -> int:
23 30 """implement the graft command as defined in mercurial/commands.py"""
24 31 ret = _process_args(ui, repo, *revs, **opts)
25 32 action, graftstate, args = ret
26 33 if action == "ERROR":
27 34 return -1
28 35 elif action == "ABORT":
29 36 assert args is None
30 37 return cmdutil.abortgraft(ui, repo, graftstate)
31 38 elif action == "STOP":
32 39 assert args is None
33 40 return _stopgraft(ui, repo, graftstate)
34 41 elif action == "GRAFT":
35 42 return _graft_revisions(ui, repo, graftstate, *args)
43 elif action == "GRAFT-TO":
44 return _graft_revisions_in_memory(ui, repo, graftstate, *args)
36 45 else:
37 raise error.ProgrammingError('unknown action: %s' % action)
46 msg = b'unknown action: %s' % action.encode('ascii')
47 raise error.ProgrammingError(msg)
38 48
39 49
40 50 def _process_args(
41 51 ui, repo, *revs, **opts
42 52 ) -> Tuple[_ActionT, statemod.cmdstate | None, _CmdArgsT | None]:
43 53 """process the graft command argument to figure out what to do
44 54
45 55 This also filter the selected revision to skip the one that cannot be graft
46 56 or were already grafted.
47 57 """
48 58 if revs and opts.get('rev'):
49 59 ui.warn(
50 60 _(
51 61 b'warning: inconsistent use of --rev might give unexpected '
52 62 b'revision ordering!\n'
53 63 )
54 64 )
55 65
56 66 revs = list(revs)
57 67 revs.extend(opts.get('rev'))
58 68 # a dict of data to be stored in state file
59 69 statedata = {}
60 70 # list of new nodes created by ongoing graft
61 71 statedata[b'newnodes'] = []
62 72
63 73 # argument incompatible with followup from an interrupted operation
64 commit_args = ['edit', 'log', 'user', 'date', 'currentdate', 'currentuser']
74 commit_args = [
75 'edit',
76 'log',
77 'user',
78 'date',
79 'currentdate',
80 'currentuser',
81 'to',
82 ]
65 83 nofollow_args = commit_args + ['base', 'rev']
66 84
67 85 arg_compatibilities = [
68 86 ('no_commit', commit_args),
87 ('continue', ['to']),
69 88 ('stop', nofollow_args),
70 89 ('abort', nofollow_args),
71 90 ]
72 91 cmdutil.check_at_most_one_arg(opts, 'abort', 'stop', 'continue')
73 92 for arg, incompatible in arg_compatibilities:
74 93 cmdutil.check_incompatible_arguments(opts, arg, incompatible)
75 94
76 95 cmdutil.resolve_commit_options(ui, opts)
77 96
78 97 cont = False
79 98
80 99 graftstate = statemod.cmdstate(repo, b'graftstate')
81 100
101 if opts.get('to'):
102 toctx = logcmdutil.revsingle(repo, opts['to'], None)
103 statedata[b'to'] = toctx.hex()
104 else:
105 toctx = repo[None].p1()
106
82 107 if opts.get('stop'):
83 108 return "STOP", graftstate, None
84 109 elif opts.get('abort'):
85 110 return "ABORT", graftstate, None
86 111 elif opts.get('continue'):
87 112 cont = True
88 113 if revs:
89 114 raise error.InputError(_(b"can't specify --continue and revisions"))
90 115 # read in unfinished revisions
91 116 if graftstate.exists():
92 117 statedata = cmdutil.readgraftstate(repo, graftstate)
93 118 if statedata.get(b'no_commit'):
94 119 opts['no_commit'] = statedata.get(b'no_commit')
95 120 if statedata.get(b'base'):
96 121 opts['base'] = statedata.get(b'base')
97 122 nodes = statedata[b'nodes']
98 123 revs = [repo[node].rev() for node in nodes]
99 124 else:
100 125 cmdutil.wrongtooltocontinue(repo, _(b'graft'))
101 126 elif not revs:
102 127 raise error.InputError(_(b'no revisions specified'))
103 128 else:
104 129 cmdutil.checkunfinished(repo)
105 cmdutil.bailifchanged(repo)
130 if not opts.get('to'):
131 cmdutil.bailifchanged(repo)
106 132 revs = logcmdutil.revrange(repo, revs)
107 133
108 134 for o in (
109 135 b'date',
110 136 b'user',
111 137 b'log',
112 138 b'no_commit',
113 139 b'dry_run',
114 140 ):
115 141 v = opts.get(o.decode('ascii'))
116 142 # if statedata is already set, it comes from --continue and test says
117 143 # we should honor them above the options (which seems weird).
118 144 if v and o not in statedata:
119 145 statedata[o] = v
120 146
121 147 skipped = set()
122 148 basectx = None
123 149 if opts.get('base'):
124 150 basectx = logcmdutil.revsingle(repo, opts['base'], None)
125 151 statedata[b'base'] = basectx.hex()
126 152 if basectx is None:
127 153 # check for merges
128 154 for rev in repo.revs(b'%ld and merge()', revs):
129 155 ui.warn(_(b'skipping ungraftable merge revision %d\n') % rev)
130 156 skipped.add(rev)
131 157 revs = [r for r in revs if r not in skipped]
132 158 if not revs:
133 159 return "ERROR", None, None
134 160 if basectx is not None and len(revs) != 1:
135 161 raise error.InputError(_(b'only one revision allowed with --base'))
136 162
137 163 # Don't check in the --continue case, in effect retaining --force across
138 164 # --continues. That's because without --force, any revisions we decided to
139 165 # skip would have been filtered out here, so they wouldn't have made their
140 166 # way to the graftstate. With --force, any revisions we would have otherwise
141 167 # skipped would not have been filtered out, and if they hadn't been applied
142 168 # already, they'd have been in the graftstate.
143 169 if not (cont or opts.get('force')) and basectx is None:
144 170 # check for ancestors of dest branch
145 ancestors = repo.revs(b'%ld & (::.)', revs)
171 ancestors = repo.revs(b'%ld & (::%d)', revs, toctx.rev())
146 172 for rev in ancestors:
147 173 ui.warn(_(b'skipping ancestor revision %d:%s\n') % (rev, repo[rev]))
148 174
149 175 revs = [r for r in revs if r not in ancestors]
150 176
151 177 if not revs:
152 178 return "ERROR", None, None
153 179
154 180 # analyze revs for earlier grafts
155 181 ids = {}
156 182 for ctx in repo.set(b"%ld", revs):
157 183 ids[ctx.hex()] = ctx.rev()
158 184 n = ctx.extra().get(b'source')
159 185 if n:
160 186 ids[n] = ctx.rev()
161 187
162 188 # check ancestors for earlier grafts
163 189 ui.debug(b'scanning for duplicate grafts\n')
164 190
165 191 # The only changesets we can be sure doesn't contain grafts of any
166 192 # revs, are the ones that are common ancestors of *all* revs:
167 for rev in repo.revs(b'only(%d,ancestor(%ld))', repo[b'.'].rev(), revs):
193 for rev in repo.revs(b'only(%d,ancestor(%ld))', toctx.rev(), revs):
168 194 ctx = repo[rev]
169 195 n = ctx.extra().get(b'source')
170 196 if n in ids:
171 197 try:
172 198 r = repo[n].rev()
173 199 except error.RepoLookupError:
174 200 r = None
175 201 if r in revs:
176 202 ui.warn(
177 203 _(
178 204 b'skipping revision %d:%s '
179 205 b'(already grafted to %d:%s)\n'
180 206 )
181 207 % (r, repo[r], rev, ctx)
182 208 )
183 209 revs.remove(r)
184 210 elif ids[n] in revs:
185 211 if r is None:
186 212 ui.warn(
187 213 _(
188 214 b'skipping already grafted revision %d:%s '
189 215 b'(%d:%s also has unknown origin %s)\n'
190 216 )
191 217 % (ids[n], repo[ids[n]], rev, ctx, n[:12])
192 218 )
193 219 else:
194 220 ui.warn(
195 221 _(
196 222 b'skipping already grafted revision %d:%s '
197 223 b'(%d:%s also has origin %d:%s)\n'
198 224 )
199 225 % (ids[n], repo[ids[n]], rev, ctx, r, n[:12])
200 226 )
201 227 revs.remove(ids[n])
202 228 elif ctx.hex() in ids:
203 229 r = ids[ctx.hex()]
204 230 if r in revs:
205 231 ui.warn(
206 232 _(
207 233 b'skipping already grafted revision %d:%s '
208 234 b'(was grafted from %d:%s)\n'
209 235 )
210 236 % (r, repo[r], rev, ctx)
211 237 )
212 238 revs.remove(r)
213 239 if not revs:
214 240 return "ERROR", None, None
215 241
216 242 editor = cmdutil.getcommiteditor(editform=b'graft', **opts)
217 243 dry_run = bool(opts.get("dry_run"))
218 244 tool = opts.get('tool', b'')
245 if opts.get("to"):
246 return "GRAFT-TO", graftstate, (statedata, revs, editor, dry_run, tool)
219 247 return "GRAFT", graftstate, (statedata, revs, editor, cont, dry_run, tool)
220 248
221 249
222 250 def _build_progress(ui, repo, ctx):
223 251 rev_sum = b'%d:%s "%s"' % (
224 252 ctx.rev(),
225 253 ctx,
226 254 ctx.description().split(b'\n', 1)[0],
227 255 )
228 256 names = repo.nodetags(ctx.node()) + repo.nodebookmarks(ctx.node())
229 257 if names:
230 258 rev_sum += b' (%s)' % b' '.join(names)
231 259 return _(b'grafting %s\n') % rev_sum
232 260
233 261
234 262 def _build_meta(ui, repo, ctx, statedata):
235 263 source = ctx.extra().get(b'source')
236 264 extra = {}
237 265 if source:
238 266 extra[b'source'] = source
239 267 extra[b'intermediate-source'] = ctx.hex()
240 268 else:
241 269 extra[b'source'] = ctx.hex()
242 270 user = statedata.get(b'user', ctx.user())
243 271 date = statedata.get(b'date', ctx.date())
244 272 message = ctx.description()
245 273 if statedata.get(b'log'):
246 274 message += b'\n(grafted from %s)' % ctx.hex()
247 275 return (user, date, message, extra)
248 276
249 277
278 def _graft_revisions_in_memory(
279 ui,
280 repo,
281 graftstate,
282 statedata,
283 revs,
284 editor,
285 dry_run,
286 tool=b'',
287 ):
288 """graft revisions in memory
289
290 Abort on unresolved conflicts.
291 """
292 with repo.lock(), repo.transaction(b"graft"):
293 target = repo[statedata[b"to"]]
294 for r in revs:
295 ctx = repo[r]
296 ui.status(_build_progress(ui, repo, ctx))
297 if dry_run:
298 # we might want to actually perform the grafting to detect
299 # potential conflict in the dry run.
300 continue
301 wctx = context.overlayworkingctx(repo)
302 wctx.setbase(target)
303 if b'base' in statedata:
304 base = repo[statedata[b'base']]
305 else:
306 base = ctx.p1()
307
308 (user, date, message, extra) = _build_meta(ui, repo, ctx, statedata)
309
310 # perform the graft merge with p1(rev) as 'ancestor'
311 try:
312 overrides = {(b'ui', b'forcemerge'): tool}
313 with ui.configoverride(overrides, b'graft'):
314 mergemod.graft(
315 repo,
316 ctx,
317 base,
318 wctx=wctx,
319 )
320 except error.InMemoryMergeConflictsError as e:
321 raise error.Abort(
322 b'cannot graft in memory: merge conflicts',
323 hint=_(bytes(e)),
324 )
325 mctx = wctx.tomemctx(
326 message,
327 user=user,
328 date=date,
329 extra=extra,
330 editor=editor,
331 )
332 node = repo.commitctx(mctx)
333 target = repo[node]
334 return 0
335
336
250 337 def _graft_revisions(
251 338 ui,
252 339 repo,
253 340 graftstate,
254 341 statedata,
255 342 revs,
256 343 editor,
257 344 cont=False,
258 345 dry_run=False,
259 346 tool=b'',
260 347 ):
261 348 """actually graft some revisions"""
262 349 for pos, ctx in enumerate(repo.set(b"%ld", revs)):
263 350 ui.status(_build_progress(ui, repo, ctx))
264 351 if dry_run:
265 352 continue
266 353
267 354 (user, date, message, extra) = _build_meta(ui, repo, ctx, statedata)
268 355
269 356 # we don't merge the first commit when continuing
270 357 if not cont:
271 358 # perform the graft merge with p1(rev) as 'ancestor'
272 359 overrides = {(b'ui', b'forcemerge'): tool}
273 360 if b'base' in statedata:
274 361 base = repo[statedata[b'base']]
275 362 else:
276 363 base = ctx.p1()
277 364 with ui.configoverride(overrides, b'graft'):
278 365 stats = mergemod.graft(
279 366 repo, ctx, base, [b'local', b'graft', b'parent of graft']
280 367 )
281 368 # report any conflicts
282 369 if stats.unresolvedcount > 0:
283 370 # write out state for --continue
284 371 nodes = [repo[rev].hex() for rev in revs[pos:]]
285 372 statedata[b'nodes'] = nodes
286 373 stateversion = 1
287 374 graftstate.save(stateversion, statedata)
288 375 ui.error(_(b"abort: unresolved conflicts, can't continue\n"))
289 376 ui.error(_(b"(use 'hg resolve' and 'hg graft --continue')\n"))
290 377 return 1
291 378 else:
292 379 cont = False
293 380
294 381 # commit if --no-commit is false
295 382 if not statedata.get(b'no_commit'):
296 383 node = repo.commit(
297 384 text=message, user=user, date=date, extra=extra, editor=editor
298 385 )
299 386 if node is None:
300 387 ui.warn(
301 388 _(b'note: graft of %d:%s created no changes to commit\n')
302 389 % (ctx.rev(), ctx)
303 390 )
304 391 # checking that newnodes exist because old state files won't have it
305 392 elif statedata.get(b'newnodes') is not None:
306 393 nn = statedata[b'newnodes']
307 394 assert isinstance(nn, list) # list of bytes
308 395 nn.append(node)
309 396
310 397 # remove state when we complete successfully
311 398 if not dry_run:
312 399 graftstate.delete()
313 400
314 401 return 0
315 402
316 403
317 404 def _stopgraft(ui, repo, graftstate):
318 405 """stop the interrupted graft"""
319 406 if not graftstate.exists():
320 407 raise error.StateError(_(b"no interrupted graft found"))
321 408 pctx = repo[b'.']
322 409 mergemod.clean_update(pctx)
323 410 graftstate.delete()
324 411 ui.status(_(b"stopped the interrupted graft\n"))
325 412 ui.status(_(b"working directory is now at %s\n") % pctx.hex()[:12])
326 413 return 0
@@ -1,7670 +1,7686
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Olivia Mackall <olivia@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import annotations
9 9
10 10 import os
11 11 import re
12 12 import sys
13 13
14 14 from .i18n import _
15 15 from .node import (
16 16 hex,
17 17 nullid,
18 18 nullrev,
19 19 short,
20 20 wdirrev,
21 21 )
22 22 from . import (
23 23 admin_commands as admin_commands_mod,
24 24 archival,
25 25 bookmarks,
26 26 bundle2,
27 27 bundlecaches,
28 28 changegroup,
29 29 cmdutil,
30 30 copies,
31 31 debugcommands as debugcommandsmod,
32 32 destutil,
33 33 diffutil,
34 34 discovery,
35 35 encoding,
36 36 error,
37 37 exchange,
38 38 extensions,
39 39 filemerge,
40 40 formatter,
41 41 graphmod,
42 42 grep as grepmod,
43 43 hbisect,
44 44 help,
45 45 hg,
46 46 logcmdutil,
47 47 merge as mergemod,
48 48 mergestate as mergestatemod,
49 49 narrowspec,
50 50 obsolete,
51 51 obsutil,
52 52 patch,
53 53 phases,
54 54 pycompat,
55 55 registrar,
56 56 revsetlang,
57 57 rewriteutil,
58 58 scmutil,
59 59 server,
60 60 shelve as shelvemod,
61 61 state as statemod,
62 62 tags as tagsmod,
63 63 util,
64 64 verify as verifymod,
65 65 wireprotoserver,
66 66 )
67 67
68 68 from .cmd_impls import graft as graft_impl
69 69 from .configuration import (
70 70 command as config_command,
71 71 )
72 72 from .utils import (
73 73 dateutil,
74 74 procutil,
75 75 stringutil,
76 76 urlutil,
77 77 )
78 78
79 79 table = {}
80 80 table.update(debugcommandsmod.command._table)
81 81 table.update(admin_commands_mod.command._table)
82 82
83 83 command = registrar.command(table)
84 84 INTENT_READONLY = registrar.INTENT_READONLY
85 85
86 86 # common command options
87 87
88 88 globalopts = [
89 89 (
90 90 b'R',
91 91 b'repository',
92 92 b'',
93 93 _(b'repository root directory or name of overlay bundle file'),
94 94 _(b'REPO'),
95 95 ),
96 96 (b'', b'cwd', b'', _(b'change working directory'), _(b'DIR')),
97 97 (
98 98 b'y',
99 99 b'noninteractive',
100 100 None,
101 101 _(
102 102 b'do not prompt, automatically pick the first choice for all prompts'
103 103 ),
104 104 ),
105 105 (b'q', b'quiet', None, _(b'suppress output')),
106 106 (b'v', b'verbose', None, _(b'enable additional output')),
107 107 (
108 108 b'',
109 109 b'color',
110 110 b'',
111 111 # i18n: 'always', 'auto', 'never', and 'debug' are keywords
112 112 # and should not be translated
113 113 _(b"when to colorize (boolean, always, auto, never, or debug)"),
114 114 _(b'TYPE'),
115 115 ),
116 116 (
117 117 b'',
118 118 b'config',
119 119 [],
120 120 _(b'set/override config option (use \'section.name=value\')'),
121 121 _(b'CONFIG'),
122 122 ),
123 123 (b'', b'debug', None, _(b'enable debugging output')),
124 124 (b'', b'debugger', None, _(b'start debugger')),
125 125 (
126 126 b'',
127 127 b'encoding',
128 128 encoding.encoding,
129 129 _(b'set the charset encoding'),
130 130 _(b'ENCODE'),
131 131 ),
132 132 (
133 133 b'',
134 134 b'encodingmode',
135 135 encoding.encodingmode,
136 136 _(b'set the charset encoding mode'),
137 137 _(b'MODE'),
138 138 ),
139 139 (b'', b'traceback', None, _(b'always print a traceback on exception')),
140 140 (b'', b'time', None, _(b'time how long the command takes')),
141 141 (b'', b'profile', None, _(b'print command execution profile')),
142 142 (b'', b'version', None, _(b'output version information and exit')),
143 143 (b'h', b'help', None, _(b'display help and exit')),
144 144 (b'', b'hidden', False, _(b'consider hidden changesets')),
145 145 (
146 146 b'',
147 147 b'pager',
148 148 b'auto',
149 149 _(b"when to paginate (boolean, always, auto, or never)"),
150 150 _(b'TYPE'),
151 151 ),
152 152 ]
153 153
154 154 dryrunopts = cmdutil.dryrunopts
155 155 remoteopts = cmdutil.remoteopts
156 156 walkopts = cmdutil.walkopts
157 157 commitopts = cmdutil.commitopts
158 158 commitopts2 = cmdutil.commitopts2
159 159 commitopts3 = cmdutil.commitopts3
160 160 formatteropts = cmdutil.formatteropts
161 161 templateopts = cmdutil.templateopts
162 162 logopts = cmdutil.logopts
163 163 diffopts = cmdutil.diffopts
164 164 diffwsopts = cmdutil.diffwsopts
165 165 diffopts2 = cmdutil.diffopts2
166 166 mergetoolopts = cmdutil.mergetoolopts
167 167 similarityopts = cmdutil.similarityopts
168 168 subrepoopts = cmdutil.subrepoopts
169 169 debugrevlogopts = cmdutil.debugrevlogopts
170 170
171 171 # Commands start here, listed alphabetically
172 172
173 173
174 174 @command(
175 175 b'abort',
176 176 dryrunopts,
177 177 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
178 178 helpbasic=True,
179 179 )
180 180 def abort(ui, repo, **opts):
181 181 """abort an unfinished operation (EXPERIMENTAL)
182 182
183 183 Aborts a multistep operation like graft, histedit, rebase, merge,
184 184 and unshelve if they are in an unfinished state.
185 185
186 186 use --dry-run/-n to dry run the command.
187 187 """
188 188 dryrun = opts.get('dry_run')
189 189 abortstate = cmdutil.getunfinishedstate(repo)
190 190 if not abortstate:
191 191 raise error.StateError(_(b'no operation in progress'))
192 192 if not abortstate.abortfunc:
193 193 raise error.InputError(
194 194 (
195 195 _(b"%s in progress but does not support 'hg abort'")
196 196 % (abortstate._opname)
197 197 ),
198 198 hint=abortstate.hint(),
199 199 )
200 200 if dryrun:
201 201 ui.status(
202 202 _(b'%s in progress, will be aborted\n') % (abortstate._opname)
203 203 )
204 204 return
205 205 return abortstate.abortfunc(ui, repo)
206 206
207 207
208 208 @command(
209 209 b'add',
210 210 walkopts + subrepoopts + dryrunopts,
211 211 _(b'[OPTION]... [FILE]...'),
212 212 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
213 213 helpbasic=True,
214 214 inferrepo=True,
215 215 )
216 216 def add(ui, repo, *pats, **opts):
217 217 """add the specified files on the next commit
218 218
219 219 Schedule files to be version controlled and added to the
220 220 repository.
221 221
222 222 The files will be added to the repository at the next commit. To
223 223 undo an add before that, see :hg:`forget`.
224 224
225 225 If no names are given, add all files to the repository (except
226 226 files matching ``.hgignore``).
227 227
228 228 .. container:: verbose
229 229
230 230 Examples:
231 231
232 232 - New (unknown) files are added
233 233 automatically by :hg:`add`::
234 234
235 235 $ ls
236 236 foo.c
237 237 $ hg status
238 238 ? foo.c
239 239 $ hg add
240 240 adding foo.c
241 241 $ hg status
242 242 A foo.c
243 243
244 244 - Specific files to be added can be specified::
245 245
246 246 $ ls
247 247 bar.c foo.c
248 248 $ hg status
249 249 ? bar.c
250 250 ? foo.c
251 251 $ hg add bar.c
252 252 $ hg status
253 253 A bar.c
254 254 ? foo.c
255 255
256 256 Returns 0 if all files are successfully added.
257 257 """
258 258
259 259 with repo.wlock(), repo.dirstate.changing_files(repo):
260 260 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
261 261 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
262 262 rejected = cmdutil.add(ui, repo, m, b"", uipathfn, False, **opts)
263 263 return rejected and 1 or 0
264 264
265 265
266 266 @command(
267 267 b'addremove',
268 268 similarityopts + subrepoopts + walkopts + dryrunopts,
269 269 _(b'[OPTION]... [FILE]...'),
270 270 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
271 271 inferrepo=True,
272 272 )
273 273 def addremove(ui, repo, *pats, **opts):
274 274 """add all new files, delete all missing files
275 275
276 276 Add all new files and remove all missing files from the
277 277 repository.
278 278
279 279 Unless names are given, new files are ignored if they match any of
280 280 the patterns in ``.hgignore``. As with add, these changes take
281 281 effect at the next commit.
282 282
283 283 Use the -s/--similarity option to detect renamed files. This
284 284 option takes a percentage between 0 (disabled) and 100 (files must
285 285 be identical) as its parameter. With a parameter greater than 0,
286 286 this compares every removed file with every added file and records
287 287 those similar enough as renames. Detecting renamed files this way
288 288 can be expensive. After using this option, :hg:`status -C` can be
289 289 used to check which files were identified as moved or renamed. If
290 290 not specified, -s/--similarity defaults to 100 and only renames of
291 291 identical files are detected.
292 292
293 293 .. container:: verbose
294 294
295 295 Examples:
296 296
297 297 - A number of files (bar.c and foo.c) are new,
298 298 while foobar.c has been removed (without using :hg:`remove`)
299 299 from the repository::
300 300
301 301 $ ls
302 302 bar.c foo.c
303 303 $ hg status
304 304 ! foobar.c
305 305 ? bar.c
306 306 ? foo.c
307 307 $ hg addremove
308 308 adding bar.c
309 309 adding foo.c
310 310 removing foobar.c
311 311 $ hg status
312 312 A bar.c
313 313 A foo.c
314 314 R foobar.c
315 315
316 316 - A file foobar.c was moved to foo.c without using :hg:`rename`.
317 317 Afterwards, it was edited slightly::
318 318
319 319 $ ls
320 320 foo.c
321 321 $ hg status
322 322 ! foobar.c
323 323 ? foo.c
324 324 $ hg addremove --similarity 90
325 325 removing foobar.c
326 326 adding foo.c
327 327 recording removal of foobar.c as rename to foo.c (94% similar)
328 328 $ hg status -C
329 329 A foo.c
330 330 foobar.c
331 331 R foobar.c
332 332
333 333 Returns 0 if all files are successfully added.
334 334 """
335 335 opts = pycompat.byteskwargs(opts)
336 336 if not opts.get(b'similarity'):
337 337 opts[b'similarity'] = b'100'
338 338 with repo.wlock(), repo.dirstate.changing_files(repo):
339 339 matcher = scmutil.match(repo[None], pats, opts)
340 340 relative = scmutil.anypats(pats, opts)
341 341 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=relative)
342 342 return scmutil.addremove(repo, matcher, b"", uipathfn, opts)
343 343
344 344
345 345 @command(
346 346 b'annotate|blame',
347 347 [
348 348 (b'r', b'rev', b'', _(b'annotate the specified revision'), _(b'REV')),
349 349 (
350 350 b'',
351 351 b'follow',
352 352 None,
353 353 _(b'follow copies/renames and list the filename (DEPRECATED)'),
354 354 ),
355 355 (b'', b'no-follow', None, _(b"don't follow copies and renames")),
356 356 (b'a', b'text', None, _(b'treat all files as text')),
357 357 (b'u', b'user', None, _(b'list the author (long with -v)')),
358 358 (b'f', b'file', None, _(b'list the filename')),
359 359 (b'd', b'date', None, _(b'list the date (short with -q)')),
360 360 (b'n', b'number', None, _(b'list the revision number (default)')),
361 361 (b'c', b'changeset', None, _(b'list the changeset')),
362 362 (
363 363 b'l',
364 364 b'line-number',
365 365 None,
366 366 _(b'show line number at the first appearance'),
367 367 ),
368 368 (
369 369 b'',
370 370 b'skip',
371 371 [],
372 372 _(b'revset to not display (EXPERIMENTAL)'),
373 373 _(b'REV'),
374 374 ),
375 375 (
376 376 b'L',
377 377 b'line-range',
378 378 [],
379 379 _(b'follow line range of specified file (EXPERIMENTAL)'),
380 380 _(b'FILE,RANGE'),
381 381 ),
382 382 ]
383 383 + diffwsopts
384 384 + walkopts
385 385 + formatteropts,
386 386 _(b'[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...'),
387 387 helpcategory=command.CATEGORY_FILE_CONTENTS,
388 388 helpbasic=True,
389 389 inferrepo=True,
390 390 )
391 391 def annotate(ui, repo, *pats, **opts):
392 392 """show changeset information by line for each file
393 393
394 394 List changes in files, showing the revision id responsible for
395 395 each line.
396 396
397 397 This command is useful for discovering when a change was made and
398 398 by whom.
399 399
400 400 If you include --file, --user, or --date, the revision number is
401 401 suppressed unless you also include --number.
402 402
403 403 Without the -a/--text option, annotate will avoid processing files
404 404 it detects as binary. With -a, annotate will annotate the file
405 405 anyway, although the results will probably be neither useful
406 406 nor desirable.
407 407
408 408 .. container:: verbose
409 409
410 410 Use -L/--line-range FILE,M:N options to filter the output to the lines
411 411 from M to N in FILE. This option is incompatible with --no-follow and
412 412 cannot be combined with file pattern arguments. When combined with --rev
413 413 the line ranges refer to the state of the file at the requested revision.
414 414
415 415 .. container:: verbose
416 416
417 417 Template:
418 418
419 419 The following keywords are supported in addition to the common template
420 420 keywords and functions. See also :hg:`help templates`.
421 421
422 422 :lines: List of lines with annotation data.
423 423 :path: String. Repository-absolute path of the specified file.
424 424
425 425 And each entry of ``{lines}`` provides the following sub-keywords in
426 426 addition to ``{date}``, ``{node}``, ``{rev}``, ``{user}``, etc.
427 427
428 428 :line: String. Line content.
429 429 :lineno: Integer. Line number at that revision.
430 430 :path: String. Repository-absolute path of the file at that revision.
431 431
432 432 See :hg:`help templates.operators` for the list expansion syntax.
433 433
434 434 Returns 0 on success.
435 435 """
436 436 opts = pycompat.byteskwargs(opts)
437 437
438 438 linerange = opts.get(b'line_range')
439 439
440 440 if linerange and opts.get(b'no_follow'):
441 441 raise error.InputError(
442 442 _(b'--line-range is incompatible with --no-follow')
443 443 )
444 444
445 445 if pats and linerange:
446 446 raise error.InputError(
447 447 _(b'cannot combine filename or pattern and --line-range')
448 448 )
449 449
450 450 if not pats and not linerange:
451 451 raise error.InputError(
452 452 _(b'at least one filename or pattern is required')
453 453 )
454 454
455 455 if opts.get(b'follow'):
456 456 # --follow is deprecated and now just an alias for -f/--file
457 457 # to mimic the behavior of Mercurial before version 1.5
458 458 opts[b'file'] = True
459 459
460 460 if (
461 461 not opts.get(b'user')
462 462 and not opts.get(b'changeset')
463 463 and not opts.get(b'date')
464 464 and not opts.get(b'file')
465 465 ):
466 466 opts[b'number'] = True
467 467
468 468 linenumber = opts.get(b'line_number') is not None
469 469 if (
470 470 linenumber
471 471 and (not opts.get(b'changeset'))
472 472 and (not opts.get(b'number'))
473 473 ):
474 474 raise error.InputError(_(b'at least one of -n/-c is required for -l'))
475 475
476 476 rev = opts.get(b'rev')
477 477 if rev:
478 478 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
479 479 ctx = logcmdutil.revsingle(repo, rev)
480 480
481 481 if not pats:
482 482 pats = [
483 483 fname
484 484 for fname, _ranges in logcmdutil._parselinerangeopt(repo, opts)
485 485 ]
486 486
487 487 ui.pager(b'annotate')
488 488 rootfm = ui.formatter(b'annotate', opts)
489 489 if ui.debugflag:
490 490 shorthex = pycompat.identity
491 491 else:
492 492
493 493 def shorthex(h):
494 494 return h[:12]
495 495
496 496 if ui.quiet:
497 497 datefunc = dateutil.shortdate
498 498 else:
499 499 datefunc = dateutil.datestr
500 500 if ctx.rev() is None:
501 501 if opts.get(b'changeset'):
502 502 # omit "+" suffix which is appended to node hex
503 503 def formatrev(rev):
504 504 if rev == wdirrev:
505 505 return b'%d' % ctx.p1().rev()
506 506 else:
507 507 return b'%d' % rev
508 508
509 509 else:
510 510
511 511 def formatrev(rev):
512 512 if rev == wdirrev:
513 513 return b'%d+' % ctx.p1().rev()
514 514 else:
515 515 return b'%d ' % rev
516 516
517 517 def formathex(h):
518 518 if h == repo.nodeconstants.wdirhex:
519 519 return b'%s+' % shorthex(hex(ctx.p1().node()))
520 520 else:
521 521 return b'%s ' % shorthex(h)
522 522
523 523 else:
524 524 formatrev = b'%d'.__mod__
525 525 formathex = shorthex
526 526
527 527 opmap = [
528 528 (b'user', b' ', lambda x: x.fctx.user(), ui.shortuser),
529 529 (b'rev', b' ', lambda x: scmutil.intrev(x.fctx), formatrev),
530 530 (b'node', b' ', lambda x: hex(scmutil.binnode(x.fctx)), formathex),
531 531 (b'date', b' ', lambda x: x.fctx.date(), util.cachefunc(datefunc)),
532 532 (b'path', b' ', lambda x: x.fctx.path(), pycompat.bytestr),
533 533 (b'lineno', b':', lambda x: x.lineno, pycompat.bytestr),
534 534 ]
535 535 opnamemap = {
536 536 b'rev': b'number',
537 537 b'node': b'changeset',
538 538 b'path': b'file',
539 539 b'lineno': b'line_number',
540 540 }
541 541
542 542 if rootfm.isplain():
543 543
544 544 def makefunc(get, fmt):
545 545 return lambda x: fmt(get(x))
546 546
547 547 else:
548 548
549 549 def makefunc(get, fmt):
550 550 return get
551 551
552 552 datahint = rootfm.datahint()
553 553 funcmap = [
554 554 (makefunc(get, fmt), sep)
555 555 for fn, sep, get, fmt in opmap
556 556 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
557 557 ]
558 558 funcmap[0] = (funcmap[0][0], b'') # no separator in front of first column
559 559 fields = b' '.join(
560 560 fn
561 561 for fn, sep, get, fmt in opmap
562 562 if opts.get(opnamemap.get(fn, fn)) or fn in datahint
563 563 )
564 564
565 565 def bad(x, y):
566 566 raise error.InputError(b"%s: %s" % (x, y))
567 567
568 568 m = scmutil.match(ctx, pats, opts, badfn=bad)
569 569
570 570 follow = not opts.get(b'no_follow')
571 571 diffopts = patch.difffeatureopts(
572 572 ui, opts, section=b'annotate', whitespace=True
573 573 )
574 574 skiprevs = opts.get(b'skip')
575 575 if skiprevs:
576 576 skiprevs = logcmdutil.revrange(repo, skiprevs)
577 577
578 578 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
579 579 for abs in ctx.walk(m):
580 580 fctx = ctx[abs]
581 581 rootfm.startitem()
582 582 rootfm.data(path=abs)
583 583 if not opts.get(b'text') and fctx.isbinary():
584 584 rootfm.plain(_(b"%s: binary file\n") % uipathfn(abs))
585 585 continue
586 586
587 587 fm = rootfm.nested(b'lines', tmpl=b'{rev}: {line}')
588 588 lines = fctx.annotate(
589 589 follow=follow, skiprevs=skiprevs, diffopts=diffopts
590 590 )
591 591 if linerange:
592 592 _fname, (line_start, line_end) = list(
593 593 logcmdutil._parselinerangeopt(repo, opts)
594 594 )[0]
595 595 lines = [
596 596 line
597 597 for no, line in enumerate(lines)
598 598 if line_start <= no < line_end
599 599 ]
600 600
601 601 if not lines:
602 602 fm.end()
603 603 continue
604 604 formats = []
605 605 pieces = []
606 606
607 607 for f, sep in funcmap:
608 608 l = [f(n) for n in lines]
609 609 if fm.isplain():
610 610 sizes = [encoding.colwidth(x) for x in l]
611 611 ml = max(sizes)
612 612 formats.append([sep + b' ' * (ml - w) + b'%s' for w in sizes])
613 613 else:
614 614 formats.append([b'%s'] * len(l))
615 615 pieces.append(l)
616 616
617 617 for f, p, n in zip(zip(*formats), zip(*pieces), lines):
618 618 fm.startitem()
619 619 fm.context(fctx=n.fctx)
620 620 fm.write(fields, b"".join(f), *p)
621 621 if n.skip:
622 622 fmt = b"* %s"
623 623 else:
624 624 fmt = b": %s"
625 625 fm.write(b'line', fmt, n.text)
626 626
627 627 if not lines[-1].text.endswith(b'\n'):
628 628 fm.plain(b'\n')
629 629 fm.end()
630 630
631 631 rootfm.end()
632 632
633 633
634 634 @command(
635 635 b'archive',
636 636 [
637 637 (b'', b'no-decode', None, _(b'do not pass files through decoders')),
638 638 (
639 639 b'p',
640 640 b'prefix',
641 641 b'',
642 642 _(b'directory prefix for files in archive'),
643 643 _(b'PREFIX'),
644 644 ),
645 645 (b'r', b'rev', b'', _(b'revision to distribute'), _(b'REV')),
646 646 (b't', b'type', b'', _(b'type of distribution to create'), _(b'TYPE')),
647 647 ]
648 648 + subrepoopts
649 649 + walkopts,
650 650 _(b'[OPTION]... DEST'),
651 651 helpcategory=command.CATEGORY_IMPORT_EXPORT,
652 652 )
653 653 def archive(ui, repo, dest, **opts):
654 654 """create an unversioned archive of a repository revision
655 655
656 656 By default, the revision used is the parent of the working
657 657 directory; use -r/--rev to specify a different revision.
658 658
659 659 The archive type is automatically detected based on file
660 660 extension (to override, use -t/--type).
661 661
662 662 .. container:: verbose
663 663
664 664 Examples:
665 665
666 666 - create a zip file containing the 1.0 release::
667 667
668 668 hg archive -r 1.0 project-1.0.zip
669 669
670 670 - create a tarball excluding .hg files::
671 671
672 672 hg archive project.tar.gz -X ".hg*"
673 673
674 674 Valid types are:
675 675
676 676 :``files``: a directory full of files (default)
677 677 :``tar``: tar archive, uncompressed
678 678 :``tbz2``: tar archive, compressed using bzip2
679 679 :``tgz``: tar archive, compressed using gzip
680 680 :``txz``: tar archive, compressed using lzma (only in Python 3)
681 681 :``uzip``: zip archive, uncompressed
682 682 :``zip``: zip archive, compressed using deflate
683 683
684 684 The exact name of the destination archive or directory is given
685 685 using a format string; see :hg:`help export` for details.
686 686
687 687 Each member added to an archive file has a directory prefix
688 688 prepended. Use -p/--prefix to specify a format string for the
689 689 prefix. The default is the basename of the archive, with suffixes
690 690 removed.
691 691
692 692 Returns 0 on success.
693 693 """
694 694
695 695 rev = opts.get('rev')
696 696 if rev:
697 697 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
698 698 ctx = logcmdutil.revsingle(repo, rev)
699 699 if not ctx:
700 700 raise error.InputError(
701 701 _(b'no working directory: please specify a revision')
702 702 )
703 703 node = ctx.node()
704 704 dest = cmdutil.makefilename(ctx, dest)
705 705 if os.path.realpath(dest) == repo.root:
706 706 raise error.InputError(_(b'repository root cannot be destination'))
707 707
708 708 kind = opts.get('type') or archival.guesskind(dest) or b'files'
709 709 prefix = opts.get('prefix')
710 710
711 711 if dest == b'-':
712 712 if kind == b'files':
713 713 raise error.InputError(_(b'cannot archive plain files to stdout'))
714 714 realdest = dest
715 715 dest = lambda: cmdutil.makefileobj(ctx, realdest)
716 716 if not prefix:
717 717 prefix = os.path.basename(repo.root) + b'-%h'
718 718
719 719 prefix = cmdutil.makefilename(ctx, prefix)
720 720 match = scmutil.match(ctx, [], pycompat.byteskwargs(opts))
721 721 archival.archive(
722 722 repo,
723 723 dest,
724 724 node,
725 725 kind,
726 726 not opts.get('no_decode'),
727 727 match,
728 728 prefix,
729 729 subrepos=opts.get('subrepos'),
730 730 )
731 731
732 732
733 733 @command(
734 734 b'backout',
735 735 [
736 736 (
737 737 b'',
738 738 b'merge',
739 739 None,
740 740 _(b'merge with old dirstate parent after backout'),
741 741 ),
742 742 (
743 743 b'',
744 744 b'commit',
745 745 None,
746 746 _(b'commit if no conflicts were encountered (DEPRECATED)'),
747 747 ),
748 748 (b'', b'no-commit', None, _(b'do not commit')),
749 749 (
750 750 b'',
751 751 b'parent',
752 752 b'',
753 753 _(b'parent to choose when backing out merge (DEPRECATED)'),
754 754 _(b'REV'),
755 755 ),
756 756 (b'r', b'rev', b'', _(b'revision to backout'), _(b'REV')),
757 757 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
758 758 ]
759 759 + mergetoolopts
760 760 + walkopts
761 761 + commitopts
762 762 + commitopts2,
763 763 _(b'[OPTION]... [-r] REV'),
764 764 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
765 765 )
766 766 def backout(ui, repo, node=None, rev=None, **opts):
767 767 """reverse effect of earlier changeset
768 768
769 769 Prepare a new changeset with the effect of REV undone in the
770 770 current working directory. If no conflicts were encountered,
771 771 it will be committed immediately.
772 772
773 773 If REV is the parent of the working directory, then this new changeset
774 774 is committed automatically (unless --no-commit is specified).
775 775
776 776 .. note::
777 777
778 778 :hg:`backout` cannot be used to fix either an unwanted or
779 779 incorrect merge.
780 780
781 781 .. container:: verbose
782 782
783 783 Examples:
784 784
785 785 - Reverse the effect of the parent of the working directory.
786 786 This backout will be committed immediately::
787 787
788 788 hg backout -r .
789 789
790 790 - Reverse the effect of previous bad revision 23::
791 791
792 792 hg backout -r 23
793 793
794 794 - Reverse the effect of previous bad revision 23 and
795 795 leave changes uncommitted::
796 796
797 797 hg backout -r 23 --no-commit
798 798 hg commit -m "Backout revision 23"
799 799
800 800 By default, the pending changeset will have one parent,
801 801 maintaining a linear history. With --merge, the pending
802 802 changeset will instead have two parents: the old parent of the
803 803 working directory and a new child of REV that simply undoes REV.
804 804
805 805 Before version 1.7, the behavior without --merge was equivalent
806 806 to specifying --merge followed by :hg:`update --clean .` to
807 807 cancel the merge and leave the child of REV as a head to be
808 808 merged separately.
809 809
810 810 See :hg:`help dates` for a list of formats valid for -d/--date.
811 811
812 812 See :hg:`help revert` for a way to restore files to the state
813 813 of another revision.
814 814
815 815 Returns 0 on success, 1 if nothing to backout or there are unresolved
816 816 files.
817 817 """
818 818 with repo.wlock(), repo.lock():
819 819 return _dobackout(ui, repo, node, rev, **opts)
820 820
821 821
822 822 def _dobackout(ui, repo, node=None, rev=None, **opts):
823 823 cmdutil.check_incompatible_arguments(opts, 'no_commit', ['commit', 'merge'])
824 824
825 825 if rev and node:
826 826 raise error.InputError(_(b"please specify just one revision"))
827 827
828 828 if not rev:
829 829 rev = node
830 830
831 831 if not rev:
832 832 raise error.InputError(_(b"please specify a revision to backout"))
833 833
834 834 date = opts.get('date')
835 835 if date:
836 836 opts['date'] = dateutil.parsedate(date)
837 837
838 838 cmdutil.checkunfinished(repo)
839 839 cmdutil.bailifchanged(repo)
840 840 ctx = logcmdutil.revsingle(repo, rev)
841 841 node = ctx.node()
842 842
843 843 op1, op2 = repo.dirstate.parents()
844 844 if not repo.changelog.isancestor(node, op1):
845 845 raise error.InputError(
846 846 _(b'cannot backout change that is not an ancestor')
847 847 )
848 848
849 849 p1, p2 = repo.changelog.parents(node)
850 850 if p1 == repo.nullid:
851 851 raise error.InputError(_(b'cannot backout a change with no parents'))
852 852 if p2 != repo.nullid:
853 853 if not opts.get('parent'):
854 854 raise error.InputError(_(b'cannot backout a merge changeset'))
855 855 p = repo.lookup(opts['parent'])
856 856 if p not in (p1, p2):
857 857 raise error.InputError(
858 858 _(b'%s is not a parent of %s') % (short(p), short(node))
859 859 )
860 860 parent = p
861 861 else:
862 862 if opts.get('parent'):
863 863 raise error.InputError(
864 864 _(b'cannot use --parent on non-merge changeset')
865 865 )
866 866 parent = p1
867 867
868 868 # the backout should appear on the same branch
869 869 branch = repo.dirstate.branch()
870 870 bheads = repo.branchheads(branch)
871 871 rctx = scmutil.revsingle(repo, hex(parent))
872 872 if not opts.get('merge') and op1 != node:
873 873 with repo.transaction(b"backout"):
874 874 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
875 875 with ui.configoverride(overrides, b'backout'):
876 876 stats = mergemod.back_out(ctx, parent=repo[parent])
877 877 repo.setparents(op1, op2)
878 878 hg._showstats(repo, stats)
879 879 if stats.unresolvedcount:
880 880 repo.ui.status(
881 881 _(b"use 'hg resolve' to retry unresolved file merges\n")
882 882 )
883 883 return 1
884 884 else:
885 885 hg.clean(repo, node, show_stats=False)
886 886 repo.dirstate.setbranch(branch, repo.currenttransaction())
887 887 cmdutil.revert(ui, repo, rctx)
888 888
889 889 if opts.get('no_commit'):
890 890 msg = _(b"changeset %s backed out, don't forget to commit.\n")
891 891 ui.status(msg % short(node))
892 892 return 0
893 893
894 894 def commitfunc(ui, repo, message, match, opts):
895 895 editform = b'backout'
896 896 e = cmdutil.getcommiteditor(
897 897 editform=editform, **pycompat.strkwargs(opts)
898 898 )
899 899 if not message:
900 900 # we don't translate commit messages
901 901 message = b"Backed out changeset %s" % short(node)
902 902 e = cmdutil.getcommiteditor(edit=True, editform=editform)
903 903 return repo.commit(
904 904 message, opts.get(b'user'), opts.get(b'date'), match, editor=e
905 905 )
906 906
907 907 # save to detect changes
908 908 tip = repo.changelog.tip()
909 909
910 910 newnode = cmdutil.commit(
911 911 ui, repo, commitfunc, [], pycompat.byteskwargs(opts)
912 912 )
913 913 if not newnode:
914 914 ui.status(_(b"nothing changed\n"))
915 915 return 1
916 916 cmdutil.commitstatus(repo, newnode, branch, bheads, tip)
917 917
918 918 def nice(node):
919 919 return b'%d:%s' % (repo.changelog.rev(node), short(node))
920 920
921 921 ui.status(
922 922 _(b'changeset %s backs out changeset %s\n')
923 923 % (nice(newnode), nice(node))
924 924 )
925 925 if opts.get('merge') and op1 != node:
926 926 hg.clean(repo, op1, show_stats=False)
927 927 ui.status(_(b'merging with changeset %s\n') % nice(newnode))
928 928 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
929 929 with ui.configoverride(overrides, b'backout'):
930 930 return hg.merge(repo[b'tip'])
931 931 return 0
932 932
933 933
934 934 @command(
935 935 b'bisect',
936 936 [
937 937 (b'r', b'reset', False, _(b'reset bisect state')),
938 938 (b'g', b'good', False, _(b'mark changeset good')),
939 939 (b'b', b'bad', False, _(b'mark changeset bad')),
940 940 (b's', b'skip', False, _(b'skip testing changeset')),
941 941 (b'e', b'extend', False, _(b'extend the bisect range')),
942 942 (
943 943 b'c',
944 944 b'command',
945 945 b'',
946 946 _(b'use command to check changeset state'),
947 947 _(b'CMD'),
948 948 ),
949 949 (b'U', b'noupdate', False, _(b'do not update to target')),
950 950 ],
951 951 _(b"[-gbsr] [-U] [-c CMD] [REV]"),
952 952 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
953 953 )
954 954 def bisect(
955 955 ui,
956 956 repo,
957 957 positional_1=None,
958 958 positional_2=None,
959 959 command=None,
960 960 reset=None,
961 961 good=None,
962 962 bad=None,
963 963 skip=None,
964 964 extend=None,
965 965 noupdate=None,
966 966 ):
967 967 """subdivision search of changesets
968 968
969 969 This command helps to find changesets which introduce problems. To
970 970 use, mark the earliest changeset you know exhibits the problem as
971 971 bad, then mark the latest changeset which is free from the problem
972 972 as good. Bisect will update your working directory to a revision
973 973 for testing (unless the -U/--noupdate option is specified). Once
974 974 you have performed tests, mark the working directory as good or
975 975 bad, and bisect will either update to another candidate changeset
976 976 or announce that it has found the bad revision.
977 977
978 978 As a shortcut, you can also use the revision argument to mark a
979 979 revision as good or bad without checking it out first.
980 980
981 981 If you supply a command, it will be used for automatic bisection.
982 982 The environment variable HG_NODE will contain the ID of the
983 983 changeset being tested. The exit status of the command will be
984 984 used to mark revisions as good or bad: status 0 means good, 125
985 985 means to skip the revision, 127 (command not found) will abort the
986 986 bisection, and any other non-zero exit status means the revision
987 987 is bad.
988 988
989 989 .. container:: verbose
990 990
991 991 Some examples:
992 992
993 993 - start a bisection with known bad revision 34, and good revision 12::
994 994
995 995 hg bisect --bad 34
996 996 hg bisect --good 12
997 997
998 998 - advance the current bisection by marking current revision as good or
999 999 bad::
1000 1000
1001 1001 hg bisect --good
1002 1002 hg bisect --bad
1003 1003
1004 1004 - mark the current revision, or a known revision, to be skipped (e.g. if
1005 1005 that revision is not usable because of another issue)::
1006 1006
1007 1007 hg bisect --skip
1008 1008 hg bisect --skip 23
1009 1009
1010 1010 - skip all revisions that do not touch directories ``foo`` or ``bar``::
1011 1011
1012 1012 hg bisect --skip "!( file('path:foo') & file('path:bar') )"
1013 1013
1014 1014 - forget the current bisection::
1015 1015
1016 1016 hg bisect --reset
1017 1017
1018 1018 - use 'make && make tests' to automatically find the first broken
1019 1019 revision::
1020 1020
1021 1021 hg bisect --reset
1022 1022 hg bisect --bad 34
1023 1023 hg bisect --good 12
1024 1024 hg bisect --command "make && make tests"
1025 1025
1026 1026 - see all changesets whose states are already known in the current
1027 1027 bisection::
1028 1028
1029 1029 hg log -r "bisect(pruned)"
1030 1030
1031 1031 - see the changeset currently being bisected (especially useful
1032 1032 if running with -U/--noupdate)::
1033 1033
1034 1034 hg log -r "bisect(current)"
1035 1035
1036 1036 - see all changesets that took part in the current bisection::
1037 1037
1038 1038 hg log -r "bisect(range)"
1039 1039
1040 1040 - you can even get a nice graph::
1041 1041
1042 1042 hg log --graph -r "bisect(range)"
1043 1043
1044 1044 See :hg:`help revisions.bisect` for more about the `bisect()` predicate.
1045 1045
1046 1046 Returns 0 on success.
1047 1047 """
1048 1048 rev = []
1049 1049 # backward compatibility
1050 1050 if positional_1 in (b"good", b"bad", b"reset", b"init"):
1051 1051 ui.warn(_(b"(use of 'hg bisect <cmd>' is deprecated)\n"))
1052 1052 cmd = positional_1
1053 1053 rev.append(positional_2)
1054 1054 if cmd == b"good":
1055 1055 good = True
1056 1056 elif cmd == b"bad":
1057 1057 bad = True
1058 1058 else:
1059 1059 reset = True
1060 1060 elif positional_2:
1061 1061 raise error.InputError(_(b'incompatible arguments'))
1062 1062 elif positional_1 is not None:
1063 1063 rev.append(positional_1)
1064 1064
1065 1065 incompatibles = {
1066 1066 b'--bad': bad,
1067 1067 b'--command': bool(command),
1068 1068 b'--extend': extend,
1069 1069 b'--good': good,
1070 1070 b'--reset': reset,
1071 1071 b'--skip': skip,
1072 1072 }
1073 1073
1074 1074 enabled = [x for x in incompatibles if incompatibles[x]]
1075 1075
1076 1076 if len(enabled) > 1:
1077 1077 raise error.InputError(
1078 1078 _(b'%s and %s are incompatible') % tuple(sorted(enabled)[0:2])
1079 1079 )
1080 1080
1081 1081 if reset:
1082 1082 hbisect.resetstate(repo)
1083 1083 return
1084 1084
1085 1085 state = hbisect.load_state(repo)
1086 1086
1087 1087 if rev:
1088 1088 revs = logcmdutil.revrange(repo, rev)
1089 1089 goodnodes = state[b'good']
1090 1090 badnodes = state[b'bad']
1091 1091 if goodnodes and badnodes:
1092 1092 candidates = repo.revs(b'(%ln)::(%ln)', goodnodes, badnodes)
1093 1093 candidates += repo.revs(b'(%ln)::(%ln)', badnodes, goodnodes)
1094 1094 revs = candidates & revs
1095 1095 nodes = [repo.changelog.node(i) for i in revs]
1096 1096 else:
1097 1097 nodes = [repo.lookup(b'.')]
1098 1098
1099 1099 # update state
1100 1100 if good or bad or skip:
1101 1101 if good:
1102 1102 state[b'good'] += nodes
1103 1103 elif bad:
1104 1104 state[b'bad'] += nodes
1105 1105 elif skip:
1106 1106 state[b'skip'] += nodes
1107 1107 hbisect.save_state(repo, state)
1108 1108 if not (state[b'good'] and state[b'bad']):
1109 1109 return
1110 1110
1111 1111 def mayupdate(repo, node, show_stats=True):
1112 1112 """common used update sequence"""
1113 1113 if noupdate:
1114 1114 return
1115 1115 cmdutil.checkunfinished(repo)
1116 1116 cmdutil.bailifchanged(repo)
1117 1117 return hg.clean(repo, node, show_stats=show_stats)
1118 1118
1119 1119 displayer = logcmdutil.changesetdisplayer(ui, repo, {})
1120 1120
1121 1121 if command:
1122 1122 changesets = 1
1123 1123 if noupdate:
1124 1124 try:
1125 1125 node = state[b'current'][0]
1126 1126 except LookupError:
1127 1127 raise error.StateError(
1128 1128 _(
1129 1129 b'current bisect revision is unknown - '
1130 1130 b'start a new bisect to fix'
1131 1131 )
1132 1132 )
1133 1133 else:
1134 1134 node, p2 = repo.dirstate.parents()
1135 1135 if p2 != repo.nullid:
1136 1136 raise error.StateError(_(b'current bisect revision is a merge'))
1137 1137 if rev:
1138 1138 if not nodes:
1139 1139 raise error.InputError(_(b'empty revision set'))
1140 1140 node = repo[nodes[-1]].node()
1141 1141 with hbisect.restore_state(repo, state, node):
1142 1142 while changesets:
1143 1143 # update state
1144 1144 state[b'current'] = [node]
1145 1145 hbisect.save_state(repo, state)
1146 1146 status = ui.system(
1147 1147 command,
1148 1148 environ={b'HG_NODE': hex(node)},
1149 1149 blockedtag=b'bisect_check',
1150 1150 )
1151 1151 if status == 125:
1152 1152 transition = b"skip"
1153 1153 elif status == 0:
1154 1154 transition = b"good"
1155 1155 # status < 0 means process was killed
1156 1156 elif status == 127:
1157 1157 raise error.Abort(_(b"failed to execute %s") % command)
1158 1158 elif status < 0:
1159 1159 raise error.Abort(_(b"%s killed") % command)
1160 1160 else:
1161 1161 transition = b"bad"
1162 1162 state[transition].append(node)
1163 1163 ctx = repo[node]
1164 1164 summary = cmdutil.format_changeset_summary(ui, ctx, b'bisect')
1165 1165 ui.status(_(b'changeset %s: %s\n') % (summary, transition))
1166 1166 hbisect.checkstate(state)
1167 1167 # bisect
1168 1168 nodes, changesets, bgood = hbisect.bisect(repo, state)
1169 1169 # update to next check
1170 1170 node = nodes[0]
1171 1171 mayupdate(repo, node, show_stats=False)
1172 1172 hbisect.printresult(ui, repo, state, displayer, nodes, bgood)
1173 1173 return
1174 1174
1175 1175 hbisect.checkstate(state)
1176 1176
1177 1177 # actually bisect
1178 1178 nodes, changesets, good = hbisect.bisect(repo, state)
1179 1179 if extend:
1180 1180 if not changesets:
1181 1181 extendctx = hbisect.extendrange(repo, state, nodes, good)
1182 1182 if extendctx is not None:
1183 1183 ui.write(
1184 1184 _(b"Extending search to changeset %s\n")
1185 1185 % cmdutil.format_changeset_summary(ui, extendctx, b'bisect')
1186 1186 )
1187 1187 state[b'current'] = [extendctx.node()]
1188 1188 hbisect.save_state(repo, state)
1189 1189 return mayupdate(repo, extendctx.node())
1190 1190 raise error.StateError(_(b"nothing to extend"))
1191 1191
1192 1192 if changesets == 0:
1193 1193 hbisect.printresult(ui, repo, state, displayer, nodes, good)
1194 1194 else:
1195 1195 assert len(nodes) == 1 # only a single node can be tested next
1196 1196 node = nodes[0]
1197 1197 # compute the approximate number of remaining tests
1198 1198 tests, size = 0, 2
1199 1199 while size <= changesets:
1200 1200 tests, size = tests + 1, size * 2
1201 1201 rev = repo.changelog.rev(node)
1202 1202 summary = cmdutil.format_changeset_summary(ui, repo[rev], b'bisect')
1203 1203 ui.write(
1204 1204 _(
1205 1205 b"Testing changeset %s "
1206 1206 b"(%d changesets remaining, ~%d tests)\n"
1207 1207 )
1208 1208 % (summary, changesets, tests)
1209 1209 )
1210 1210 state[b'current'] = [node]
1211 1211 hbisect.save_state(repo, state)
1212 1212 return mayupdate(repo, node)
1213 1213
1214 1214
1215 1215 @command(
1216 1216 b'bookmarks|bookmark',
1217 1217 [
1218 1218 (b'f', b'force', False, _(b'force')),
1219 1219 (b'r', b'rev', b'', _(b'revision for bookmark action'), _(b'REV')),
1220 1220 (b'd', b'delete', False, _(b'delete a given bookmark')),
1221 1221 (b'm', b'rename', b'', _(b'rename a given bookmark'), _(b'OLD')),
1222 1222 (b'i', b'inactive', False, _(b'mark a bookmark inactive')),
1223 1223 (b'l', b'list', False, _(b'list existing bookmarks')),
1224 1224 ]
1225 1225 + formatteropts,
1226 1226 _(b'hg bookmarks [OPTIONS]... [NAME]...'),
1227 1227 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1228 1228 )
1229 1229 def bookmark(ui, repo, *names, **opts):
1230 1230 """create a new bookmark or list existing bookmarks
1231 1231
1232 1232 Bookmarks are labels on changesets to help track lines of development.
1233 1233 Bookmarks are unversioned and can be moved, renamed and deleted.
1234 1234 Deleting or moving a bookmark has no effect on the associated changesets.
1235 1235
1236 1236 Creating or updating to a bookmark causes it to be marked as 'active'.
1237 1237 The active bookmark is indicated with a '*'.
1238 1238 When a commit is made, the active bookmark will advance to the new commit.
1239 1239 A plain :hg:`update` will also advance an active bookmark, if possible.
1240 1240 Updating away from a bookmark will cause it to be deactivated.
1241 1241
1242 1242 Bookmarks can be pushed and pulled between repositories (see
1243 1243 :hg:`help push` and :hg:`help pull`). If a shared bookmark has
1244 1244 diverged, a new 'divergent bookmark' of the form 'name@path' will
1245 1245 be created. Using :hg:`merge` will resolve the divergence.
1246 1246
1247 1247 Specifying bookmark as '.' to -m/-d/-l options is equivalent to specifying
1248 1248 the active bookmark's name.
1249 1249
1250 1250 A bookmark named '@' has the special property that :hg:`clone` will
1251 1251 check it out by default if it exists.
1252 1252
1253 1253 .. container:: verbose
1254 1254
1255 1255 Template:
1256 1256
1257 1257 The following keywords are supported in addition to the common template
1258 1258 keywords and functions such as ``{bookmark}``. See also
1259 1259 :hg:`help templates`.
1260 1260
1261 1261 :active: Boolean. True if the bookmark is active.
1262 1262
1263 1263 Examples:
1264 1264
1265 1265 - create an active bookmark for a new line of development::
1266 1266
1267 1267 hg book new-feature
1268 1268
1269 1269 - create an inactive bookmark as a place marker::
1270 1270
1271 1271 hg book -i reviewed
1272 1272
1273 1273 - create an inactive bookmark on another changeset::
1274 1274
1275 1275 hg book -r .^ tested
1276 1276
1277 1277 - rename bookmark turkey to dinner::
1278 1278
1279 1279 hg book -m turkey dinner
1280 1280
1281 1281 - move the '@' bookmark from another branch::
1282 1282
1283 1283 hg book -f @
1284 1284
1285 1285 - print only the active bookmark name::
1286 1286
1287 1287 hg book -ql .
1288 1288 """
1289 1289 force = opts.get('force')
1290 1290 rev = opts.get('rev')
1291 1291 inactive = opts.get('inactive') # meaning add/rename to inactive bookmark
1292 1292
1293 1293 action = cmdutil.check_at_most_one_arg(opts, 'delete', 'rename', 'list')
1294 1294 if action:
1295 1295 cmdutil.check_incompatible_arguments(opts, action, ['rev'])
1296 1296 elif names or rev:
1297 1297 action = 'add'
1298 1298 elif inactive:
1299 1299 action = 'inactive' # meaning deactivate
1300 1300 else:
1301 1301 action = 'list'
1302 1302
1303 1303 cmdutil.check_incompatible_arguments(opts, 'inactive', ['delete', 'list'])
1304 1304 if not names and action in {'add', 'delete'}:
1305 1305 raise error.InputError(_(b"bookmark name required"))
1306 1306
1307 1307 if action in {'add', 'delete', 'rename', 'inactive'}:
1308 1308 with repo.wlock(), repo.lock(), repo.transaction(b'bookmark') as tr:
1309 1309 if action == 'delete':
1310 1310 names = pycompat.maplist(repo._bookmarks.expandname, names)
1311 1311 bookmarks.delete(repo, tr, names)
1312 1312 elif action == 'rename':
1313 1313 if not names:
1314 1314 raise error.InputError(_(b"new bookmark name required"))
1315 1315 elif len(names) > 1:
1316 1316 raise error.InputError(
1317 1317 _(b"only one new bookmark name allowed")
1318 1318 )
1319 1319 oldname = repo._bookmarks.expandname(opts['rename'])
1320 1320 bookmarks.rename(repo, tr, oldname, names[0], force, inactive)
1321 1321 elif action == 'add':
1322 1322 bookmarks.addbookmarks(repo, tr, names, rev, force, inactive)
1323 1323 elif action == 'inactive':
1324 1324 if len(repo._bookmarks) == 0:
1325 1325 ui.status(_(b"no bookmarks set\n"))
1326 1326 elif not repo._activebookmark:
1327 1327 ui.status(_(b"no active bookmark\n"))
1328 1328 else:
1329 1329 bookmarks.deactivate(repo)
1330 1330 elif action == 'list':
1331 1331 names = pycompat.maplist(repo._bookmarks.expandname, names)
1332 1332 with ui.formatter(b'bookmarks', pycompat.byteskwargs(opts)) as fm:
1333 1333 bookmarks.printbookmarks(ui, repo, fm, names)
1334 1334 else:
1335 1335 raise error.ProgrammingError(
1336 1336 b'invalid action: %s' % pycompat.sysbytes(action)
1337 1337 )
1338 1338
1339 1339
1340 1340 @command(
1341 1341 b'branch',
1342 1342 [
1343 1343 (
1344 1344 b'f',
1345 1345 b'force',
1346 1346 None,
1347 1347 _(b'set branch name even if it shadows an existing branch'),
1348 1348 ),
1349 1349 (b'C', b'clean', None, _(b'reset branch name to parent branch name')),
1350 1350 (
1351 1351 b'r',
1352 1352 b'rev',
1353 1353 [],
1354 1354 _(b'change branches of the given revs (EXPERIMENTAL)'),
1355 1355 ),
1356 1356 ],
1357 1357 _(b'[-fC] [NAME]'),
1358 1358 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1359 1359 )
1360 1360 def branch(ui, repo, label=None, **opts):
1361 1361 """set or show the current branch name
1362 1362
1363 1363 .. note::
1364 1364
1365 1365 Branch names are permanent and global. Use :hg:`bookmark` to create a
1366 1366 light-weight bookmark instead. See :hg:`help glossary` for more
1367 1367 information about named branches and bookmarks.
1368 1368
1369 1369 With no argument, show the current branch name. With one argument,
1370 1370 set the working directory branch name (the branch will not exist
1371 1371 in the repository until the next commit). Standard practice
1372 1372 recommends that primary development take place on the 'default'
1373 1373 branch.
1374 1374
1375 1375 Unless -f/--force is specified, branch will not let you set a
1376 1376 branch name that already exists.
1377 1377
1378 1378 Use -C/--clean to reset the working directory branch to that of
1379 1379 the parent of the working directory, negating a previous branch
1380 1380 change.
1381 1381
1382 1382 Use the command :hg:`update` to switch to an existing branch. Use
1383 1383 :hg:`commit --close-branch` to mark this branch head as closed.
1384 1384 When all heads of a branch are closed, the branch will be
1385 1385 considered closed.
1386 1386
1387 1387 Returns 0 on success.
1388 1388 """
1389 1389 revs = opts.get('rev')
1390 1390 if label:
1391 1391 label = label.strip()
1392 1392
1393 1393 if not opts.get('clean') and not label:
1394 1394 if revs:
1395 1395 raise error.InputError(
1396 1396 _(b"no branch name specified for the revisions")
1397 1397 )
1398 1398 ui.write(b"%s\n" % repo.dirstate.branch())
1399 1399 return
1400 1400
1401 1401 with repo.wlock():
1402 1402 if opts.get('clean'):
1403 1403 label = repo[b'.'].branch()
1404 1404 repo.dirstate.setbranch(label, repo.currenttransaction())
1405 1405 ui.status(_(b'reset working directory to branch %s\n') % label)
1406 1406 elif label:
1407 1407 scmutil.checknewlabel(repo, label, b'branch')
1408 1408 if revs:
1409 1409 return cmdutil.changebranch(ui, repo, revs, label, **opts)
1410 1410
1411 1411 if not opts.get('force') and label in repo.branchmap():
1412 1412 if label not in [p.branch() for p in repo[None].parents()]:
1413 1413 raise error.InputError(
1414 1414 _(b'a branch of the same name already exists'),
1415 1415 # i18n: "it" refers to an existing branch
1416 1416 hint=_(b"use 'hg update' to switch to it"),
1417 1417 )
1418 1418
1419 1419 repo.dirstate.setbranch(label, repo.currenttransaction())
1420 1420 ui.status(_(b'marked working directory as branch %s\n') % label)
1421 1421
1422 1422 # find any open named branches aside from default
1423 1423 for n, h, t, c in repo.branchmap().iterbranches():
1424 1424 if n != b"default" and not c:
1425 1425 return 0
1426 1426 ui.status(
1427 1427 _(
1428 1428 b'(branches are permanent and global, '
1429 1429 b'did you want a bookmark?)\n'
1430 1430 )
1431 1431 )
1432 1432
1433 1433
1434 1434 @command(
1435 1435 b'branches',
1436 1436 [
1437 1437 (
1438 1438 b'a',
1439 1439 b'active',
1440 1440 False,
1441 1441 _(b'show only branches that have unmerged heads (DEPRECATED)'),
1442 1442 ),
1443 1443 (b'c', b'closed', False, _(b'show normal and closed branches')),
1444 1444 (b'r', b'rev', [], _(b'show branch name(s) of the given rev')),
1445 1445 ]
1446 1446 + formatteropts,
1447 1447 _(b'[-c]'),
1448 1448 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
1449 1449 intents={INTENT_READONLY},
1450 1450 )
1451 1451 def branches(ui, repo, active=False, closed=False, **opts):
1452 1452 """list repository named branches
1453 1453
1454 1454 List the repository's named branches, indicating which ones are
1455 1455 inactive. If -c/--closed is specified, also list branches which have
1456 1456 been marked closed (see :hg:`commit --close-branch`).
1457 1457
1458 1458 Use the command :hg:`update` to switch to an existing branch.
1459 1459
1460 1460 .. container:: verbose
1461 1461
1462 1462 Template:
1463 1463
1464 1464 The following keywords are supported in addition to the common template
1465 1465 keywords and functions such as ``{branch}``. See also
1466 1466 :hg:`help templates`.
1467 1467
1468 1468 :active: Boolean. True if the branch is active.
1469 1469 :closed: Boolean. True if the branch is closed.
1470 1470 :current: Boolean. True if it is the current branch.
1471 1471
1472 1472 Returns 0.
1473 1473 """
1474 1474
1475 1475 revs = opts.get('rev')
1476 1476 selectedbranches = None
1477 1477 if revs:
1478 1478 revs = logcmdutil.revrange(repo, revs)
1479 1479 getbi = repo.revbranchcache().branchinfo
1480 1480 selectedbranches = {getbi(r)[0] for r in revs}
1481 1481
1482 1482 ui.pager(b'branches')
1483 1483 fm = ui.formatter(b'branches', pycompat.byteskwargs(opts))
1484 1484 hexfunc = fm.hexfunc
1485 1485
1486 1486 allheads = set(repo.heads())
1487 1487 branches = []
1488 1488 for tag, heads, tip, isclosed in repo.branchmap().iterbranches():
1489 1489 if selectedbranches is not None and tag not in selectedbranches:
1490 1490 continue
1491 1491 isactive = False
1492 1492 if not isclosed:
1493 1493 openheads = set(repo.branchmap().iteropen(heads))
1494 1494 isactive = bool(openheads & allheads)
1495 1495 branches.append((tag, repo[tip], isactive, not isclosed))
1496 1496 branches.sort(key=lambda i: (i[2], i[1].rev(), i[0], i[3]), reverse=True)
1497 1497
1498 1498 for tag, ctx, isactive, isopen in branches:
1499 1499 if active and not isactive:
1500 1500 continue
1501 1501 if isactive:
1502 1502 label = b'branches.active'
1503 1503 notice = b''
1504 1504 elif not isopen:
1505 1505 if not closed:
1506 1506 continue
1507 1507 label = b'branches.closed'
1508 1508 notice = _(b' (closed)')
1509 1509 else:
1510 1510 label = b'branches.inactive'
1511 1511 notice = _(b' (inactive)')
1512 1512 current = tag == repo.dirstate.branch()
1513 1513 if current:
1514 1514 label = b'branches.current'
1515 1515
1516 1516 fm.startitem()
1517 1517 fm.write(b'branch', b'%s', tag, label=label)
1518 1518 rev = ctx.rev()
1519 1519 padsize = max(31 - len(b"%d" % rev) - encoding.colwidth(tag), 0)
1520 1520 fmt = b' ' * padsize + b' %d:%s'
1521 1521 fm.condwrite(
1522 1522 not ui.quiet,
1523 1523 b'rev node',
1524 1524 fmt,
1525 1525 rev,
1526 1526 hexfunc(ctx.node()),
1527 1527 label=b'log.changeset changeset.%s' % ctx.phasestr(),
1528 1528 )
1529 1529 fm.context(ctx=ctx)
1530 1530 fm.data(active=isactive, closed=not isopen, current=current)
1531 1531 if not ui.quiet:
1532 1532 fm.plain(notice)
1533 1533 fm.plain(b'\n')
1534 1534 fm.end()
1535 1535
1536 1536
1537 1537 @command(
1538 1538 b'bundle',
1539 1539 [
1540 1540 (
1541 1541 b'',
1542 1542 b'exact',
1543 1543 None,
1544 1544 _(b'compute the base from the revision specified'),
1545 1545 ),
1546 1546 (
1547 1547 b'f',
1548 1548 b'force',
1549 1549 None,
1550 1550 _(b'run even when the destination is unrelated'),
1551 1551 ),
1552 1552 (
1553 1553 b'r',
1554 1554 b'rev',
1555 1555 [],
1556 1556 _(b'a changeset intended to be added to the destination'),
1557 1557 _(b'REV'),
1558 1558 ),
1559 1559 (
1560 1560 b'b',
1561 1561 b'branch',
1562 1562 [],
1563 1563 _(b'a specific branch you would like to bundle'),
1564 1564 _(b'BRANCH'),
1565 1565 ),
1566 1566 (
1567 1567 b'',
1568 1568 b'base',
1569 1569 [],
1570 1570 _(b'a base changeset assumed to be available at the destination'),
1571 1571 _(b'REV'),
1572 1572 ),
1573 1573 (b'a', b'all', None, _(b'bundle all changesets in the repository')),
1574 1574 (
1575 1575 b't',
1576 1576 b'type',
1577 1577 b'bzip2',
1578 1578 _(b'bundle compression type to use'),
1579 1579 _(b'TYPE'),
1580 1580 ),
1581 1581 ]
1582 1582 + remoteopts,
1583 1583 _(b'[-f] [-t BUNDLESPEC] [-a] [-r REV]... [--base REV]... FILE [DEST]...'),
1584 1584 helpcategory=command.CATEGORY_IMPORT_EXPORT,
1585 1585 )
1586 1586 def bundle(ui, repo, fname, *dests, **opts):
1587 1587 """create a bundle file
1588 1588
1589 1589 Generate a bundle file containing data to be transferred to another
1590 1590 repository.
1591 1591
1592 1592 To create a bundle containing all changesets, use -a/--all
1593 1593 (or --base null). Otherwise, hg assumes the destination will have
1594 1594 all the nodes you specify with --base parameters. Otherwise, hg
1595 1595 will assume the repository has all the nodes in destination, or
1596 1596 default-push/default if no destination is specified, where destination
1597 1597 is the repositories you provide through DEST option.
1598 1598
1599 1599 You can change bundle format with the -t/--type option. See
1600 1600 :hg:`help bundlespec` for documentation on this format. By default,
1601 1601 the most appropriate format is used and compression defaults to
1602 1602 bzip2.
1603 1603
1604 1604 The bundle file can then be transferred using conventional means
1605 1605 and applied to another repository with the unbundle or pull
1606 1606 command. This is useful when direct push and pull are not
1607 1607 available or when exporting an entire repository is undesirable.
1608 1608
1609 1609 Applying bundles preserves all changeset contents including
1610 1610 permissions, copy/rename information, and revision history.
1611 1611
1612 1612 Returns 0 on success, 1 if no changes found.
1613 1613 """
1614 1614
1615 1615 revs = None
1616 1616 if 'rev' in opts:
1617 1617 revstrings = opts['rev']
1618 1618 revs = logcmdutil.revrange(repo, revstrings)
1619 1619 if revstrings and not revs:
1620 1620 raise error.InputError(_(b'no commits to bundle'))
1621 1621
1622 1622 bundletype = opts.get('type', b'bzip2').lower()
1623 1623 try:
1624 1624 bundlespec = bundlecaches.parsebundlespec(
1625 1625 repo, bundletype, strict=False
1626 1626 )
1627 1627 except error.UnsupportedBundleSpecification as e:
1628 1628 raise error.InputError(
1629 1629 pycompat.bytestr(e),
1630 1630 hint=_(b"see 'hg help bundlespec' for supported values for --type"),
1631 1631 )
1632 1632
1633 1633 has_changegroup = bundlespec.params.get(b"changegroup", False)
1634 1634 cgversion = bundlespec.params[b"cg.version"]
1635 1635
1636 1636 # Packed bundles are a pseudo bundle format for now.
1637 1637 if cgversion == b's1':
1638 1638 raise error.InputError(
1639 1639 _(b'packed bundles cannot be produced by "hg bundle"'),
1640 1640 hint=_(b"use 'hg debugcreatestreamclonebundle'"),
1641 1641 )
1642 1642 base_opt = opts.get('base')
1643 1643 if opts.get('all'):
1644 1644 if dests:
1645 1645 raise error.InputError(
1646 1646 _(b"--all is incompatible with specifying destinations")
1647 1647 )
1648 1648 if base_opt:
1649 1649 ui.warn(_(b"ignoring --base because --all was specified\n"))
1650 1650 if opts.get('exact'):
1651 1651 ui.warn(_(b"ignoring --exact because --all was specified\n"))
1652 1652 base = [nullrev]
1653 1653 elif opts.get('exact'):
1654 1654 if dests:
1655 1655 raise error.InputError(
1656 1656 _(b"--exact is incompatible with specifying destinations")
1657 1657 )
1658 1658 if base_opt:
1659 1659 ui.warn(_(b"ignoring --base because --exact was specified\n"))
1660 1660 base = repo.revs(b'parents(%ld) - %ld', revs, revs)
1661 1661 if not base:
1662 1662 base = [nullrev]
1663 1663 elif base_opt:
1664 1664 base = logcmdutil.revrange(repo, base_opt)
1665 1665 if not base:
1666 1666 # base specified, but nothing was selected
1667 1667 base = [nullrev]
1668 1668 else:
1669 1669 base = None
1670 1670 supported_cg_versions = changegroup.supportedoutgoingversions(repo)
1671 1671 if has_changegroup and cgversion not in supported_cg_versions:
1672 1672 raise error.Abort(
1673 1673 _(b"repository does not support bundle version %s") % cgversion
1674 1674 )
1675 1675
1676 1676 if base is not None:
1677 1677 if dests:
1678 1678 raise error.InputError(
1679 1679 _(b"--base is incompatible with specifying destinations")
1680 1680 )
1681 1681 cl = repo.changelog
1682 1682 common = [cl.node(rev) for rev in base]
1683 1683 heads = [cl.node(r) for r in revs] if revs else None
1684 1684 outgoing = discovery.outgoing(repo, common, heads)
1685 1685 missing = outgoing.missing
1686 1686 excluded = outgoing.excluded
1687 1687 else:
1688 1688 missing = set()
1689 1689 excluded = set()
1690 1690 for path in urlutil.get_push_paths(repo, ui, dests):
1691 1691 other = hg.peer(repo, pycompat.byteskwargs(opts), path)
1692 1692 if revs is not None:
1693 1693 hex_revs = [repo[r].hex() for r in revs]
1694 1694 else:
1695 1695 hex_revs = None
1696 1696 branches = (path.branch, [])
1697 1697 head_revs, checkout = hg.addbranchrevs(
1698 1698 repo, repo, branches, hex_revs
1699 1699 )
1700 1700 heads = (
1701 1701 head_revs
1702 1702 and pycompat.maplist(repo.lookup, head_revs)
1703 1703 or head_revs
1704 1704 )
1705 1705 outgoing = discovery.findcommonoutgoing(
1706 1706 repo,
1707 1707 other,
1708 1708 onlyheads=heads,
1709 1709 force=opts.get('force'),
1710 1710 portable=True,
1711 1711 )
1712 1712 missing.update(outgoing.missing)
1713 1713 excluded.update(outgoing.excluded)
1714 1714
1715 1715 if not missing:
1716 1716 scmutil.nochangesfound(ui, repo, not base and excluded)
1717 1717 return 1
1718 1718
1719 1719 # internal changeset are internal implementation details that should not
1720 1720 # leave the repository. Bundling with `hg bundle` create such risk.
1721 1721 bundled_internal = repo.revs(b"%ln and _internal()", missing)
1722 1722 if bundled_internal:
1723 1723 msg = _(b"cannot bundle internal changesets")
1724 1724 hint = _(b"%d internal changesets selected") % len(bundled_internal)
1725 1725 raise error.Abort(msg, hint=hint)
1726 1726
1727 1727 if heads:
1728 1728 outgoing = discovery.outgoing(
1729 1729 repo, missingroots=missing, ancestorsof=heads
1730 1730 )
1731 1731 else:
1732 1732 outgoing = discovery.outgoing(repo, missingroots=missing)
1733 1733 outgoing.excluded = sorted(excluded)
1734 1734
1735 1735 if cgversion == b'01': # bundle1
1736 1736 bversion = b'HG10' + bundlespec.wirecompression
1737 1737 bcompression = None
1738 1738 elif cgversion in (b'02', b'03'):
1739 1739 bversion = b'HG20'
1740 1740 bcompression = bundlespec.wirecompression
1741 1741 else:
1742 1742 raise error.ProgrammingError(
1743 1743 b'bundle: unexpected changegroup version %s' % cgversion
1744 1744 )
1745 1745
1746 1746 # TODO compression options should be derived from bundlespec parsing.
1747 1747 # This is a temporary hack to allow adjusting bundle compression
1748 1748 # level without a) formalizing the bundlespec changes to declare it
1749 1749 # b) introducing a command flag.
1750 1750 compopts = {}
1751 1751 complevel = ui.configint(
1752 1752 b'experimental', b'bundlecomplevel.' + bundlespec.compression
1753 1753 )
1754 1754 if complevel is None:
1755 1755 complevel = ui.configint(b'experimental', b'bundlecomplevel')
1756 1756 if complevel is not None:
1757 1757 compopts[b'level'] = complevel
1758 1758
1759 1759 compthreads = ui.configint(
1760 1760 b'experimental', b'bundlecompthreads.' + bundlespec.compression
1761 1761 )
1762 1762 if compthreads is None:
1763 1763 compthreads = ui.configint(b'experimental', b'bundlecompthreads')
1764 1764 if compthreads is not None:
1765 1765 compopts[b'threads'] = compthreads
1766 1766
1767 1767 # Bundling of obsmarker and phases is optional as not all clients
1768 1768 # support the necessary features.
1769 1769 cfg = ui.configbool
1770 1770 obsolescence_cfg = cfg(b'experimental', b'evolution.bundle-obsmarker')
1771 1771 bundlespec.set_param(b'obsolescence', obsolescence_cfg, overwrite=False)
1772 1772 obs_mand_cfg = cfg(b'experimental', b'evolution.bundle-obsmarker:mandatory')
1773 1773 bundlespec.set_param(
1774 1774 b'obsolescence-mandatory', obs_mand_cfg, overwrite=False
1775 1775 )
1776 1776 if not bundlespec.params.get(b'phases', False):
1777 1777 phases_cfg = cfg(b'experimental', b'bundle-phases')
1778 1778 bundlespec.set_param(b'phases', phases_cfg, overwrite=False)
1779 1779
1780 1780 bundle2.writenewbundle(
1781 1781 ui,
1782 1782 repo,
1783 1783 b'bundle',
1784 1784 fname,
1785 1785 bversion,
1786 1786 outgoing,
1787 1787 bundlespec.params,
1788 1788 compression=bcompression,
1789 1789 compopts=compopts,
1790 1790 )
1791 1791
1792 1792
1793 1793 @command(
1794 1794 b'cat',
1795 1795 [
1796 1796 (
1797 1797 b'o',
1798 1798 b'output',
1799 1799 b'',
1800 1800 _(b'print output to file with formatted name'),
1801 1801 _(b'FORMAT'),
1802 1802 ),
1803 1803 (b'r', b'rev', b'', _(b'print the given revision'), _(b'REV')),
1804 1804 (b'', b'decode', None, _(b'apply any matching decode filter')),
1805 1805 ]
1806 1806 + walkopts
1807 1807 + formatteropts,
1808 1808 _(b'[OPTION]... FILE...'),
1809 1809 helpcategory=command.CATEGORY_FILE_CONTENTS,
1810 1810 inferrepo=True,
1811 1811 intents={INTENT_READONLY},
1812 1812 )
1813 1813 def cat(ui, repo, file1, *pats, **opts):
1814 1814 """output the current or given revision of files
1815 1815
1816 1816 Print the specified files as they were at the given revision. If
1817 1817 no revision is given, the parent of the working directory is used.
1818 1818
1819 1819 Output may be to a file, in which case the name of the file is
1820 1820 given using a template string. See :hg:`help templates`. In addition
1821 1821 to the common template keywords, the following formatting rules are
1822 1822 supported:
1823 1823
1824 1824 :``%%``: literal "%" character
1825 1825 :``%s``: basename of file being printed
1826 1826 :``%d``: dirname of file being printed, or '.' if in repository root
1827 1827 :``%p``: root-relative path name of file being printed
1828 1828 :``%H``: changeset hash (40 hexadecimal digits)
1829 1829 :``%R``: changeset revision number
1830 1830 :``%h``: short-form changeset hash (12 hexadecimal digits)
1831 1831 :``%r``: zero-padded changeset revision number
1832 1832 :``%b``: basename of the exporting repository
1833 1833 :``\\``: literal "\\" character
1834 1834
1835 1835 .. container:: verbose
1836 1836
1837 1837 Template:
1838 1838
1839 1839 The following keywords are supported in addition to the common template
1840 1840 keywords and functions. See also :hg:`help templates`.
1841 1841
1842 1842 :data: String. File content.
1843 1843 :path: String. Repository-absolute path of the file.
1844 1844
1845 1845 Returns 0 on success.
1846 1846 """
1847 1847 rev = opts.get('rev')
1848 1848 if rev:
1849 1849 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
1850 1850 ctx = logcmdutil.revsingle(repo, rev)
1851 1851 m = scmutil.match(ctx, (file1,) + pats, pycompat.byteskwargs(opts))
1852 1852 fntemplate = opts.pop('output', b'')
1853 1853 if cmdutil.isstdiofilename(fntemplate):
1854 1854 fntemplate = b''
1855 1855
1856 1856 if fntemplate:
1857 1857 fm = formatter.nullformatter(ui, b'cat', pycompat.byteskwargs(opts))
1858 1858 else:
1859 1859 ui.pager(b'cat')
1860 1860 fm = ui.formatter(b'cat', pycompat.byteskwargs(opts))
1861 1861 with fm:
1862 1862 return cmdutil.cat(ui, repo, ctx, m, fm, fntemplate, b'', **opts)
1863 1863
1864 1864
1865 1865 @command(
1866 1866 b'clone',
1867 1867 [
1868 1868 (
1869 1869 b'U',
1870 1870 b'noupdate',
1871 1871 None,
1872 1872 _(
1873 1873 b'the clone will include an empty working '
1874 1874 b'directory (only a repository)'
1875 1875 ),
1876 1876 ),
1877 1877 (
1878 1878 b'u',
1879 1879 b'updaterev',
1880 1880 b'',
1881 1881 _(b'revision, tag, or branch to check out'),
1882 1882 _(b'REV'),
1883 1883 ),
1884 1884 (
1885 1885 b'r',
1886 1886 b'rev',
1887 1887 [],
1888 1888 _(
1889 1889 b'do not clone everything, but include this changeset'
1890 1890 b' and its ancestors'
1891 1891 ),
1892 1892 _(b'REV'),
1893 1893 ),
1894 1894 (
1895 1895 b'b',
1896 1896 b'branch',
1897 1897 [],
1898 1898 _(
1899 1899 b'do not clone everything, but include this branch\'s'
1900 1900 b' changesets and their ancestors'
1901 1901 ),
1902 1902 _(b'BRANCH'),
1903 1903 ),
1904 1904 (b'', b'pull', None, _(b'use pull protocol to copy metadata')),
1905 1905 (b'', b'uncompressed', None, _(b'an alias to --stream (DEPRECATED)')),
1906 1906 (b'', b'stream', None, _(b'clone with minimal data processing')),
1907 1907 ]
1908 1908 + remoteopts,
1909 1909 _(b'[OPTION]... SOURCE [DEST]'),
1910 1910 helpcategory=command.CATEGORY_REPO_CREATION,
1911 1911 helpbasic=True,
1912 1912 norepo=True,
1913 1913 )
1914 1914 def clone(ui, source, dest=None, **opts):
1915 1915 """make a copy of an existing repository
1916 1916
1917 1917 Create a copy of an existing repository in a new directory.
1918 1918
1919 1919 If no destination directory name is specified, it defaults to the
1920 1920 basename of the source.
1921 1921
1922 1922 The location of the source is added to the new repository's
1923 1923 ``.hg/hgrc`` file, as the default to be used for future pulls.
1924 1924
1925 1925 Only local paths and ``ssh://`` URLs are supported as
1926 1926 destinations. For ``ssh://`` destinations, no working directory or
1927 1927 ``.hg/hgrc`` will be created on the remote side.
1928 1928
1929 1929 If the source repository has a bookmark called '@' set, that
1930 1930 revision will be checked out in the new repository by default.
1931 1931
1932 1932 To check out a particular version, use -u/--update, or
1933 1933 -U/--noupdate to create a clone with no working directory.
1934 1934
1935 1935 To pull only a subset of changesets, specify one or more revisions
1936 1936 identifiers with -r/--rev or branches with -b/--branch. The
1937 1937 resulting clone will contain only the specified changesets and
1938 1938 their ancestors. These options (or 'clone src#rev dest') imply
1939 1939 --pull, even for local source repositories.
1940 1940
1941 1941 In normal clone mode, the remote normalizes repository data into a common
1942 1942 exchange format and the receiving end translates this data into its local
1943 1943 storage format. --stream activates a different clone mode that essentially
1944 1944 copies repository files from the remote with minimal data processing. This
1945 1945 significantly reduces the CPU cost of a clone both remotely and locally.
1946 1946 However, it often increases the transferred data size by 30-40%. This can
1947 1947 result in substantially faster clones where I/O throughput is plentiful,
1948 1948 especially for larger repositories. A side-effect of --stream clones is
1949 1949 that storage settings and requirements on the remote are applied locally:
1950 1950 a modern client may inherit legacy or inefficient storage used by the
1951 1951 remote or a legacy Mercurial client may not be able to clone from a
1952 1952 modern Mercurial remote.
1953 1953
1954 1954 .. note::
1955 1955
1956 1956 Specifying a tag will include the tagged changeset but not the
1957 1957 changeset containing the tag.
1958 1958
1959 1959 .. container:: verbose
1960 1960
1961 1961 For efficiency, hardlinks are used for cloning whenever the
1962 1962 source and destination are on the same filesystem (note this
1963 1963 applies only to the repository data, not to the working
1964 1964 directory). Some filesystems, such as AFS, implement hardlinking
1965 1965 incorrectly, but do not report errors. In these cases, use the
1966 1966 --pull option to avoid hardlinking.
1967 1967
1968 1968 Mercurial will update the working directory to the first applicable
1969 1969 revision from this list:
1970 1970
1971 1971 a) null if -U or the source repository has no changesets
1972 1972 b) if -u . and the source repository is local, the first parent of
1973 1973 the source repository's working directory
1974 1974 c) the changeset specified with -u (if a branch name, this means the
1975 1975 latest head of that branch)
1976 1976 d) the changeset specified with -r
1977 1977 e) the tipmost head specified with -b
1978 1978 f) the tipmost head specified with the url#branch source syntax
1979 1979 g) the revision marked with the '@' bookmark, if present
1980 1980 h) the tipmost head of the default branch
1981 1981 i) tip
1982 1982
1983 1983 When cloning from servers that support it, Mercurial may fetch
1984 1984 pre-generated data from a server-advertised URL or inline from the
1985 1985 same stream. When this is done, hooks operating on incoming changesets
1986 1986 and changegroups may fire more than once, once for each pre-generated
1987 1987 bundle and as well as for any additional remaining data. In addition,
1988 1988 if an error occurs, the repository may be rolled back to a partial
1989 1989 clone. This behavior may change in future releases.
1990 1990 See :hg:`help -e clonebundles` for more.
1991 1991
1992 1992 Examples:
1993 1993
1994 1994 - clone a remote repository to a new directory named hg/::
1995 1995
1996 1996 hg clone https://www.mercurial-scm.org/repo/hg/
1997 1997
1998 1998 - create a lightweight local clone::
1999 1999
2000 2000 hg clone project/ project-feature/
2001 2001
2002 2002 - clone from an absolute path on an ssh server (note double-slash)::
2003 2003
2004 2004 hg clone ssh://user@server//home/projects/alpha/
2005 2005
2006 2006 - do a streaming clone while checking out a specified version::
2007 2007
2008 2008 hg clone --stream http://server/repo -u 1.5
2009 2009
2010 2010 - create a repository without changesets after a particular revision::
2011 2011
2012 2012 hg clone -r 04e544 experimental/ good/
2013 2013
2014 2014 - clone (and track) a particular named branch::
2015 2015
2016 2016 hg clone https://www.mercurial-scm.org/repo/hg/#stable
2017 2017
2018 2018 See :hg:`help urls` for details on specifying URLs.
2019 2019
2020 2020 Returns 0 on success.
2021 2021 """
2022 2022 cmdutil.check_at_most_one_arg(opts, 'noupdate', 'updaterev')
2023 2023
2024 2024 # --include/--exclude can come from narrow or sparse.
2025 2025 includepats, excludepats = None, None
2026 2026
2027 2027 # hg.clone() differentiates between None and an empty set. So make sure
2028 2028 # patterns are sets if narrow is requested without patterns.
2029 2029 if opts.get('narrow'):
2030 2030 includepats = set()
2031 2031 excludepats = set()
2032 2032
2033 2033 if opts.get('include'):
2034 2034 includepats = narrowspec.parsepatterns(opts.get('include'))
2035 2035 if opts.get('exclude'):
2036 2036 excludepats = narrowspec.parsepatterns(opts.get('exclude'))
2037 2037
2038 2038 r = hg.clone(
2039 2039 ui,
2040 2040 pycompat.byteskwargs(opts),
2041 2041 source,
2042 2042 dest,
2043 2043 pull=opts.get('pull'),
2044 2044 stream=opts.get('stream') or opts.get('uncompressed'),
2045 2045 revs=opts.get('rev'),
2046 2046 update=opts.get('updaterev') or not opts.get('noupdate'),
2047 2047 branch=opts.get('branch'),
2048 2048 shareopts=opts.get('shareopts'),
2049 2049 storeincludepats=includepats,
2050 2050 storeexcludepats=excludepats,
2051 2051 depth=opts.get('depth') or None,
2052 2052 )
2053 2053
2054 2054 return r is None
2055 2055
2056 2056
2057 2057 @command(
2058 2058 b'commit|ci',
2059 2059 [
2060 2060 (
2061 2061 b'A',
2062 2062 b'addremove',
2063 2063 None,
2064 2064 _(b'mark new/missing files as added/removed before committing'),
2065 2065 ),
2066 2066 (b'', b'close-branch', None, _(b'mark a branch head as closed')),
2067 2067 (b'', b'amend', None, _(b'amend the parent of the working directory')),
2068 2068 (b's', b'secret', None, _(b'use the secret phase for committing')),
2069 2069 (b'', b'draft', None, _(b'use the draft phase for committing')),
2070 2070 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
2071 2071 (
2072 2072 b'',
2073 2073 b'force-close-branch',
2074 2074 None,
2075 2075 _(b'forcibly close branch from a non-head changeset (ADVANCED)'),
2076 2076 ),
2077 2077 (b'i', b'interactive', None, _(b'use interactive mode')),
2078 2078 ]
2079 2079 + walkopts
2080 2080 + commitopts
2081 2081 + commitopts2
2082 2082 + subrepoopts,
2083 2083 _(b'[OPTION]... [FILE]...'),
2084 2084 helpcategory=command.CATEGORY_COMMITTING,
2085 2085 helpbasic=True,
2086 2086 inferrepo=True,
2087 2087 )
2088 2088 def commit(ui, repo, *pats, **opts):
2089 2089 """commit the specified files or all outstanding changes
2090 2090
2091 2091 Commit changes to the given files into the repository. Unlike a
2092 2092 centralized SCM, this operation is a local operation. See
2093 2093 :hg:`push` for a way to actively distribute your changes.
2094 2094
2095 2095 If a list of files is omitted, all changes reported by :hg:`status`
2096 2096 will be committed.
2097 2097
2098 2098 If you are committing the result of a merge, do not provide any
2099 2099 filenames or -I/-X filters.
2100 2100
2101 2101 If no commit message is specified, Mercurial starts your
2102 2102 configured editor where you can enter a message. In case your
2103 2103 commit fails, you will find a backup of your message in
2104 2104 ``.hg/last-message.txt``.
2105 2105
2106 2106 The --close-branch flag can be used to mark the current branch
2107 2107 head closed. When all heads of a branch are closed, the branch
2108 2108 will be considered closed and no longer listed.
2109 2109
2110 2110 The --amend flag can be used to amend the parent of the
2111 2111 working directory with a new commit that contains the changes
2112 2112 in the parent in addition to those currently reported by :hg:`status`,
2113 2113 if there are any. The old commit is stored in a backup bundle in
2114 2114 ``.hg/strip-backup`` (see :hg:`help bundle` and :hg:`help unbundle`
2115 2115 on how to restore it).
2116 2116
2117 2117 Message, user and date are taken from the amended commit unless
2118 2118 specified. When a message isn't specified on the command line,
2119 2119 the editor will open with the message of the amended commit.
2120 2120
2121 2121 It is not possible to amend public changesets (see :hg:`help phases`)
2122 2122 or changesets that have children.
2123 2123
2124 2124 See :hg:`help dates` for a list of formats valid for -d/--date.
2125 2125
2126 2126 Returns 0 on success, 1 if nothing changed.
2127 2127
2128 2128 .. container:: verbose
2129 2129
2130 2130 Examples:
2131 2131
2132 2132 - commit all files ending in .py::
2133 2133
2134 2134 hg commit --include "set:**.py"
2135 2135
2136 2136 - commit all non-binary files::
2137 2137
2138 2138 hg commit --exclude "set:binary()"
2139 2139
2140 2140 - amend the current commit and set the date to now::
2141 2141
2142 2142 hg commit --amend --date now
2143 2143 """
2144 2144 cmdutil.check_at_most_one_arg(opts, 'draft', 'secret')
2145 2145 cmdutil.check_incompatible_arguments(opts, 'subrepos', ['amend'])
2146 2146 with repo.wlock(), repo.lock():
2147 2147 return _docommit(ui, repo, *pats, **opts)
2148 2148
2149 2149
2150 2150 def _docommit(ui, repo, *pats, **opts):
2151 2151 if opts.get('interactive'):
2152 2152 opts.pop('interactive')
2153 2153 ret = cmdutil.dorecord(
2154 2154 ui, repo, commit, None, False, cmdutil.recordfilter, *pats, **opts
2155 2155 )
2156 2156 # ret can be 0 (no changes to record) or the value returned by
2157 2157 # commit(), 1 if nothing changed or None on success.
2158 2158 return 1 if ret == 0 else ret
2159 2159
2160 2160 if opts.get('subrepos'):
2161 2161 # Let --subrepos on the command line override config setting.
2162 2162 ui.setconfig(b'ui', b'commitsubrepos', True, b'commit')
2163 2163
2164 2164 cmdutil.checkunfinished(repo, commit=True)
2165 2165
2166 2166 branch = repo[None].branch()
2167 2167 bheads = repo.branchheads(branch)
2168 2168 tip = repo.changelog.tip()
2169 2169
2170 2170 extra = {}
2171 2171 if opts.get('close_branch') or opts.get('force_close_branch'):
2172 2172 extra[b'close'] = b'1'
2173 2173
2174 2174 if repo[b'.'].closesbranch():
2175 2175 # Not ideal, but let us do an extra status early to prevent early
2176 2176 # bail out.
2177 2177 matcher = scmutil.match(
2178 2178 repo[None], pats, pycompat.byteskwargs(opts)
2179 2179 )
2180 2180 s = repo.status(match=matcher)
2181 2181 if s.modified or s.added or s.removed:
2182 2182 bheads = repo.branchheads(branch, closed=True)
2183 2183 else:
2184 2184 msg = _(b'current revision is already a branch closing head')
2185 2185 raise error.InputError(msg)
2186 2186
2187 2187 if not bheads:
2188 2188 raise error.InputError(
2189 2189 _(b'branch "%s" has no heads to close') % branch
2190 2190 )
2191 2191 elif (
2192 2192 branch == repo[b'.'].branch()
2193 2193 and repo[b'.'].node() not in bheads
2194 2194 and not opts.get('force_close_branch')
2195 2195 ):
2196 2196 hint = _(
2197 2197 b'use --force-close-branch to close branch from a non-head'
2198 2198 b' changeset'
2199 2199 )
2200 2200 raise error.InputError(_(b'can only close branch heads'), hint=hint)
2201 2201 elif opts.get('amend'):
2202 2202 if (
2203 2203 repo[b'.'].p1().branch() != branch
2204 2204 and repo[b'.'].p2().branch() != branch
2205 2205 ):
2206 2206 raise error.InputError(_(b'can only close branch heads'))
2207 2207
2208 2208 if opts.get('amend'):
2209 2209 if ui.configbool(b'ui', b'commitsubrepos'):
2210 2210 raise error.InputError(
2211 2211 _(b'cannot amend with ui.commitsubrepos enabled')
2212 2212 )
2213 2213
2214 2214 old = repo[b'.']
2215 2215 rewriteutil.precheck(repo, [old.rev()], b'amend')
2216 2216
2217 2217 # Currently histedit gets confused if an amend happens while histedit
2218 2218 # is in progress. Since we have a checkunfinished command, we are
2219 2219 # temporarily honoring it.
2220 2220 #
2221 2221 # Note: eventually this guard will be removed. Please do not expect
2222 2222 # this behavior to remain.
2223 2223 if not obsolete.isenabled(repo, obsolete.createmarkersopt):
2224 2224 cmdutil.checkunfinished(repo)
2225 2225
2226 2226 node = cmdutil.amend(ui, repo, old, extra, pats, opts)
2227 2227 if node == old.node():
2228 2228 ui.status(_(b"nothing changed\n"))
2229 2229 return 1
2230 2230 else:
2231 2231
2232 2232 def commitfunc(ui, repo, message, match, opts):
2233 2233 overrides = {}
2234 2234 if opts.get(b'secret'):
2235 2235 overrides[(b'phases', b'new-commit')] = b'secret'
2236 2236 elif opts.get(b'draft'):
2237 2237 overrides[(b'phases', b'new-commit')] = b'draft'
2238 2238
2239 2239 baseui = repo.baseui
2240 2240 with baseui.configoverride(overrides, b'commit'):
2241 2241 with ui.configoverride(overrides, b'commit'):
2242 2242 editform = cmdutil.mergeeditform(
2243 2243 repo[None], b'commit.normal'
2244 2244 )
2245 2245 editor = cmdutil.getcommiteditor(
2246 2246 editform=editform, **pycompat.strkwargs(opts)
2247 2247 )
2248 2248 return repo.commit(
2249 2249 message,
2250 2250 opts.get(b'user'),
2251 2251 opts.get(b'date'),
2252 2252 match,
2253 2253 editor=editor,
2254 2254 extra=extra,
2255 2255 )
2256 2256
2257 2257 node = cmdutil.commit(
2258 2258 ui, repo, commitfunc, pats, pycompat.byteskwargs(opts)
2259 2259 )
2260 2260
2261 2261 if not node:
2262 2262 stat = cmdutil.postcommitstatus(
2263 2263 repo, pats, pycompat.byteskwargs(opts)
2264 2264 )
2265 2265 if stat.deleted:
2266 2266 ui.status(
2267 2267 _(
2268 2268 b"nothing changed (%d missing files, see "
2269 2269 b"'hg status')\n"
2270 2270 )
2271 2271 % len(stat.deleted)
2272 2272 )
2273 2273 else:
2274 2274 ui.status(_(b"nothing changed\n"))
2275 2275 return 1
2276 2276
2277 2277 cmdutil.commitstatus(repo, node, branch, bheads, tip, **opts)
2278 2278
2279 2279 if not ui.quiet and ui.configbool(b'commands', b'commit.post-status'):
2280 2280 status(
2281 2281 ui,
2282 2282 repo,
2283 2283 modified=True,
2284 2284 added=True,
2285 2285 removed=True,
2286 2286 deleted=True,
2287 2287 unknown=True,
2288 2288 subrepos=opts.get('subrepos'),
2289 2289 )
2290 2290
2291 2291
2292 2292 @command(
2293 2293 b'config|showconfig|debugconfig',
2294 2294 [
2295 2295 (b'u', b'untrusted', None, _(b'show untrusted configuration options')),
2296 2296 # This is experimental because we need
2297 2297 # * reasonable behavior around aliases,
2298 2298 # * decide if we display [debug] [experimental] and [devel] section par
2299 2299 # default
2300 2300 # * some way to display "generic" config entry (the one matching
2301 2301 # regexp,
2302 2302 # * proper display of the different value type
2303 2303 # * a better way to handle <DYNAMIC> values (and variable types),
2304 2304 # * maybe some type information ?
2305 2305 (
2306 2306 b'',
2307 2307 b'exp-all-known',
2308 2308 None,
2309 2309 _(b'show all known config option (EXPERIMENTAL)'),
2310 2310 ),
2311 2311 (b'e', b'edit', None, _(b'edit user config')),
2312 2312 (b'l', b'local', None, _(b'edit repository config')),
2313 2313 (b'', b'source', None, _(b'show source of configuration value')),
2314 2314 (
2315 2315 b'',
2316 2316 b'shared',
2317 2317 None,
2318 2318 _(b'edit shared source repository config (EXPERIMENTAL)'),
2319 2319 ),
2320 2320 (b'', b'non-shared', None, _(b'edit non shared config (EXPERIMENTAL)')),
2321 2321 (b'g', b'global', None, _(b'edit global config')),
2322 2322 ]
2323 2323 + formatteropts,
2324 2324 _(b'[-u] [NAME]...'),
2325 2325 helpcategory=command.CATEGORY_HELP,
2326 2326 optionalrepo=True,
2327 2327 intents={INTENT_READONLY},
2328 2328 )
2329 2329 def config(ui, repo, *values, **opts):
2330 2330 """show combined config settings from all hgrc files
2331 2331
2332 2332 With no arguments, print names and values of all config items.
2333 2333
2334 2334 With one argument of the form section.name, print just the value
2335 2335 of that config item.
2336 2336
2337 2337 With multiple arguments, print names and values of all config
2338 2338 items with matching section names or section.names.
2339 2339
2340 2340 With --edit, start an editor on the user-level config file. With
2341 2341 --global, edit the system-wide config file. With --local, edit the
2342 2342 repository-level config file.
2343 2343
2344 2344 With --source, the source (filename and line number) is printed
2345 2345 for each config item.
2346 2346
2347 2347 See :hg:`help config` for more information about config files.
2348 2348
2349 2349 .. container:: verbose
2350 2350
2351 2351 --non-shared flag is used to edit `.hg/hgrc-not-shared` config file.
2352 2352 This file is not shared across shares when in share-safe mode.
2353 2353
2354 2354 Template:
2355 2355
2356 2356 The following keywords are supported. See also :hg:`help templates`.
2357 2357
2358 2358 :name: String. Config name.
2359 2359 :source: String. Filename and line number where the item is defined.
2360 2360 :value: String. Config value.
2361 2361
2362 2362 The --shared flag can be used to edit the config file of shared source
2363 2363 repository. It only works when you have shared using the experimental
2364 2364 share safe feature.
2365 2365
2366 2366 Returns 0 on success, 1 if NAME does not exist.
2367 2367
2368 2368 """
2369 2369 edit_level = config_command.find_edit_level(ui, repo, opts)
2370 2370 if edit_level is not None:
2371 2371 return config_command.edit_config(ui, repo, edit_level)
2372 2372
2373 2373 ui.pager(b'config')
2374 2374 config_command.show_component(ui, repo)
2375 2375
2376 2376 matched = config_command.show_config(
2377 2377 ui,
2378 2378 repo,
2379 2379 value_filters=values,
2380 2380 formatter_options=pycompat.byteskwargs(opts),
2381 2381 untrusted=bool(opts.get('untrusted')),
2382 2382 all_known=bool(opts.get('exp_all_known')),
2383 2383 show_source=bool(ui.debugflag or opts.get('source')),
2384 2384 )
2385 2385 if matched:
2386 2386 return 0
2387 2387 return 1
2388 2388
2389 2389
2390 2390 @command(
2391 2391 b'continue',
2392 2392 dryrunopts,
2393 2393 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
2394 2394 helpbasic=True,
2395 2395 )
2396 2396 def continuecmd(ui, repo, **opts):
2397 2397 """resumes an interrupted operation (EXPERIMENTAL)
2398 2398
2399 2399 Finishes a multistep operation like graft, histedit, rebase, merge,
2400 2400 and unshelve if they are in an interrupted state.
2401 2401
2402 2402 use --dry-run/-n to dry run the command.
2403 2403 """
2404 2404 dryrun = opts.get('dry_run')
2405 2405 contstate = cmdutil.getunfinishedstate(repo)
2406 2406 if not contstate:
2407 2407 raise error.StateError(_(b'no operation in progress'))
2408 2408 if not contstate.continuefunc:
2409 2409 raise error.StateError(
2410 2410 (
2411 2411 _(b"%s in progress but does not support 'hg continue'")
2412 2412 % (contstate._opname)
2413 2413 ),
2414 2414 hint=contstate.continuemsg(),
2415 2415 )
2416 2416 if dryrun:
2417 2417 ui.status(_(b'%s in progress, will be resumed\n') % (contstate._opname))
2418 2418 return
2419 2419 return contstate.continuefunc(ui, repo)
2420 2420
2421 2421
2422 2422 @command(
2423 2423 b'copy|cp',
2424 2424 [
2425 2425 (b'', b'forget', None, _(b'unmark a destination file as copied')),
2426 2426 (b'A', b'after', None, _(b'record a copy that has already occurred')),
2427 2427 (
2428 2428 b'',
2429 2429 b'at-rev',
2430 2430 b'',
2431 2431 _(b'(un)mark copies in the given revision (EXPERIMENTAL)'),
2432 2432 _(b'REV'),
2433 2433 ),
2434 2434 (
2435 2435 b'f',
2436 2436 b'force',
2437 2437 None,
2438 2438 _(b'forcibly copy over an existing managed file'),
2439 2439 ),
2440 2440 ]
2441 2441 + walkopts
2442 2442 + dryrunopts,
2443 2443 _(b'[OPTION]... (SOURCE... DEST | --forget DEST...)'),
2444 2444 helpcategory=command.CATEGORY_FILE_CONTENTS,
2445 2445 )
2446 2446 def copy(ui, repo, *pats, **opts):
2447 2447 """mark files as copied for the next commit
2448 2448
2449 2449 Mark dest as having copies of source files. If dest is a
2450 2450 directory, copies are put in that directory. If dest is a file,
2451 2451 the source must be a single file.
2452 2452
2453 2453 By default, this command copies the contents of files as they
2454 2454 exist in the working directory. If invoked with -A/--after, the
2455 2455 operation is recorded, but no copying is performed.
2456 2456
2457 2457 To undo marking a destination file as copied, use --forget. With that
2458 2458 option, all given (positional) arguments are unmarked as copies. The
2459 2459 destination file(s) will be left in place (still tracked). Note that
2460 2460 :hg:`copy --forget` behaves the same way as :hg:`rename --forget`.
2461 2461
2462 2462 This command takes effect with the next commit by default.
2463 2463
2464 2464 Returns 0 on success, 1 if errors are encountered.
2465 2465 """
2466 2466
2467 2467 context = lambda repo: repo.dirstate.changing_files(repo)
2468 2468 rev = opts.get('at_rev')
2469 2469
2470 2470 if rev:
2471 2471 ctx = logcmdutil.revsingle(repo, rev)
2472 2472 if ctx.rev() is not None:
2473 2473
2474 2474 def context(repo):
2475 2475 return util.nullcontextmanager()
2476 2476
2477 2477 opts['at_rev'] = ctx.rev()
2478 2478 with repo.wlock(), context(repo):
2479 2479 return cmdutil.copy(ui, repo, pats, pycompat.byteskwargs(opts))
2480 2480
2481 2481
2482 2482 @command(
2483 2483 b'debugcommands',
2484 2484 [],
2485 2485 _(b'[COMMAND]'),
2486 2486 helpcategory=command.CATEGORY_HELP,
2487 2487 norepo=True,
2488 2488 )
2489 2489 def debugcommands(ui, cmd=b'', *args):
2490 2490 """list all available commands and options"""
2491 2491 for cmd, vals in sorted(table.items()):
2492 2492 cmd = cmd.split(b'|')[0]
2493 2493 opts = b', '.join([i[1] for i in vals[1]])
2494 2494 ui.write(b'%s: %s\n' % (cmd, opts))
2495 2495
2496 2496
2497 2497 @command(
2498 2498 b'debugcomplete',
2499 2499 [(b'o', b'options', None, _(b'show the command options'))],
2500 2500 _(b'[-o] CMD'),
2501 2501 helpcategory=command.CATEGORY_HELP,
2502 2502 norepo=True,
2503 2503 )
2504 2504 def debugcomplete(ui, cmd=b'', **opts):
2505 2505 """returns the completion list associated with the given command"""
2506 2506
2507 2507 if opts.get('options'):
2508 2508 options = []
2509 2509 otables = [globalopts]
2510 2510 if cmd:
2511 2511 aliases, entry = cmdutil.findcmd(cmd, table, False)
2512 2512 otables.append(entry[1])
2513 2513 for t in otables:
2514 2514 for o in t:
2515 2515 if b"(DEPRECATED)" in o[3]:
2516 2516 continue
2517 2517 if o[0]:
2518 2518 options.append(b'-%s' % o[0])
2519 2519 options.append(b'--%s' % o[1])
2520 2520 ui.write(b"%s\n" % b"\n".join(options))
2521 2521 return
2522 2522
2523 2523 cmdlist, unused_allcmds = cmdutil.findpossible(cmd, table)
2524 2524 if ui.verbose:
2525 2525 cmdlist = [b' '.join(c[0]) for c in cmdlist.values()]
2526 2526 ui.write(b"%s\n" % b"\n".join(sorted(cmdlist)))
2527 2527
2528 2528
2529 2529 @command(
2530 2530 b'diff',
2531 2531 [
2532 2532 (b'r', b'rev', [], _(b'revision (DEPRECATED)'), _(b'REV')),
2533 2533 (b'', b'from', b'', _(b'revision to diff from'), _(b'REV1')),
2534 2534 (b'', b'to', b'', _(b'revision to diff to'), _(b'REV2')),
2535 2535 (b'c', b'change', b'', _(b'change made by revision'), _(b'REV')),
2536 2536 ]
2537 2537 + diffopts
2538 2538 + diffopts2
2539 2539 + walkopts
2540 2540 + subrepoopts,
2541 2541 _(b'[OPTION]... ([-c REV] | [--from REV1] [--to REV2]) [FILE]...'),
2542 2542 helpcategory=command.CATEGORY_FILE_CONTENTS,
2543 2543 helpbasic=True,
2544 2544 inferrepo=True,
2545 2545 intents={INTENT_READONLY},
2546 2546 )
2547 2547 def diff(ui, repo, *pats, **opts):
2548 2548 """diff repository (or selected files)
2549 2549
2550 2550 Show differences between revisions for the specified files.
2551 2551
2552 2552 Differences between files are shown using the unified diff format.
2553 2553
2554 2554 .. note::
2555 2555
2556 2556 :hg:`diff` may generate unexpected results for merges, as it will
2557 2557 default to comparing against the working directory's first
2558 2558 parent changeset if no revisions are specified. To diff against the
2559 2559 conflict regions, you can use `--config diff.merge=yes`.
2560 2560
2561 2561 By default, the working directory files are compared to its first parent. To
2562 2562 see the differences from another revision, use --from. To see the difference
2563 2563 to another revision, use --to. For example, :hg:`diff --from .^` will show
2564 2564 the differences from the working copy's grandparent to the working copy,
2565 2565 :hg:`diff --to .` will show the diff from the working copy to its parent
2566 2566 (i.e. the reverse of the default), and :hg:`diff --from 1.0 --to 1.2` will
2567 2567 show the diff between those two revisions.
2568 2568
2569 2569 Alternatively you can specify -c/--change with a revision to see the changes
2570 2570 in that changeset relative to its first parent (i.e. :hg:`diff -c 42` is
2571 2571 equivalent to :hg:`diff --from 42^ --to 42`)
2572 2572
2573 2573 Without the -a/--text option, diff will avoid generating diffs of
2574 2574 files it detects as binary. With -a, diff will generate a diff
2575 2575 anyway, probably with undesirable results.
2576 2576
2577 2577 Use the -g/--git option to generate diffs in the git extended diff
2578 2578 format. For more information, read :hg:`help diffs`.
2579 2579
2580 2580 .. container:: verbose
2581 2581
2582 2582 Examples:
2583 2583
2584 2584 - compare a file in the current working directory to its parent::
2585 2585
2586 2586 hg diff foo.c
2587 2587
2588 2588 - compare two historical versions of a directory, with rename info::
2589 2589
2590 2590 hg diff --git --from 1.0 --to 1.2 lib/
2591 2591
2592 2592 - get change stats relative to the last change on some date::
2593 2593
2594 2594 hg diff --stat --from "date('may 2')"
2595 2595
2596 2596 - diff all newly-added files that contain a keyword::
2597 2597
2598 2598 hg diff "set:added() and grep(GNU)"
2599 2599
2600 2600 - compare a revision and its parents::
2601 2601
2602 2602 hg diff -c 9353 # compare against first parent
2603 2603 hg diff --from 9353^ --to 9353 # same using revset syntax
2604 2604 hg diff --from 9353^2 --to 9353 # compare against the second parent
2605 2605
2606 2606 Returns 0 on success.
2607 2607 """
2608 2608
2609 2609 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
2610 2610 opts = pycompat.byteskwargs(opts)
2611 2611 revs = opts.get(b'rev')
2612 2612 change = opts.get(b'change')
2613 2613 from_rev = opts.get(b'from')
2614 2614 to_rev = opts.get(b'to')
2615 2615 stat = opts.get(b'stat')
2616 2616 reverse = opts.get(b'reverse')
2617 2617
2618 2618 cmdutil.check_incompatible_arguments(opts, b'from', [b'rev', b'change'])
2619 2619 cmdutil.check_incompatible_arguments(opts, b'to', [b'rev', b'change'])
2620 2620 if change:
2621 2621 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
2622 2622 ctx2 = logcmdutil.revsingle(repo, change, None)
2623 2623 ctx1 = diffutil.diff_parent(ctx2)
2624 2624 elif from_rev or to_rev:
2625 2625 repo = scmutil.unhidehashlikerevs(
2626 2626 repo, [from_rev] + [to_rev], b'nowarn'
2627 2627 )
2628 2628 ctx1 = logcmdutil.revsingle(repo, from_rev, None)
2629 2629 ctx2 = logcmdutil.revsingle(repo, to_rev, None)
2630 2630 else:
2631 2631 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
2632 2632 ctx1, ctx2 = logcmdutil.revpair(repo, revs)
2633 2633
2634 2634 if reverse:
2635 2635 ctxleft = ctx2
2636 2636 ctxright = ctx1
2637 2637 else:
2638 2638 ctxleft = ctx1
2639 2639 ctxright = ctx2
2640 2640
2641 2641 diffopts = patch.diffallopts(ui, opts)
2642 2642 m = scmutil.match(ctx2, pats, opts)
2643 2643 m = repo.narrowmatch(m)
2644 2644 ui.pager(b'diff')
2645 2645 logcmdutil.diffordiffstat(
2646 2646 ui,
2647 2647 repo,
2648 2648 diffopts,
2649 2649 ctxleft,
2650 2650 ctxright,
2651 2651 m,
2652 2652 stat=stat,
2653 2653 listsubrepos=opts.get(b'subrepos'),
2654 2654 root=opts.get(b'root'),
2655 2655 )
2656 2656
2657 2657
2658 2658 @command(
2659 2659 b'export',
2660 2660 [
2661 2661 (
2662 2662 b'B',
2663 2663 b'bookmark',
2664 2664 b'',
2665 2665 _(b'export changes only reachable by given bookmark'),
2666 2666 _(b'BOOKMARK'),
2667 2667 ),
2668 2668 (
2669 2669 b'o',
2670 2670 b'output',
2671 2671 b'',
2672 2672 _(b'print output to file with formatted name'),
2673 2673 _(b'FORMAT'),
2674 2674 ),
2675 2675 (b'', b'switch-parent', None, _(b'diff against the second parent')),
2676 2676 (b'r', b'rev', [], _(b'revisions to export'), _(b'REV')),
2677 2677 ]
2678 2678 + diffopts
2679 2679 + formatteropts,
2680 2680 _(b'[OPTION]... [-o OUTFILESPEC] [-r] [REV]...'),
2681 2681 helpcategory=command.CATEGORY_IMPORT_EXPORT,
2682 2682 helpbasic=True,
2683 2683 intents={INTENT_READONLY},
2684 2684 )
2685 2685 def export(ui, repo, *changesets, **opts):
2686 2686 """dump the header and diffs for one or more changesets
2687 2687
2688 2688 Print the changeset header and diffs for one or more revisions.
2689 2689 If no revision is given, the parent of the working directory is used.
2690 2690
2691 2691 The information shown in the changeset header is: author, date,
2692 2692 branch name (if non-default), changeset hash, parent(s) and commit
2693 2693 comment.
2694 2694
2695 2695 .. note::
2696 2696
2697 2697 :hg:`export` may generate unexpected diff output for merge
2698 2698 changesets, as it will compare the merge changeset against its
2699 2699 first parent only.
2700 2700
2701 2701 Output may be to a file, in which case the name of the file is
2702 2702 given using a template string. See :hg:`help templates`. In addition
2703 2703 to the common template keywords, the following formatting rules are
2704 2704 supported:
2705 2705
2706 2706 :``%%``: literal "%" character
2707 2707 :``%H``: changeset hash (40 hexadecimal digits)
2708 2708 :``%N``: number of patches being generated
2709 2709 :``%R``: changeset revision number
2710 2710 :``%b``: basename of the exporting repository
2711 2711 :``%h``: short-form changeset hash (12 hexadecimal digits)
2712 2712 :``%m``: first line of the commit message (only alphanumeric characters)
2713 2713 :``%n``: zero-padded sequence number, starting at 1
2714 2714 :``%r``: zero-padded changeset revision number
2715 2715 :``\\``: literal "\\" character
2716 2716
2717 2717 Without the -a/--text option, export will avoid generating diffs
2718 2718 of files it detects as binary. With -a, export will generate a
2719 2719 diff anyway, probably with undesirable results.
2720 2720
2721 2721 With -B/--bookmark changesets reachable by the given bookmark are
2722 2722 selected.
2723 2723
2724 2724 Use the -g/--git option to generate diffs in the git extended diff
2725 2725 format. See :hg:`help diffs` for more information.
2726 2726
2727 2727 With the --switch-parent option, the diff will be against the
2728 2728 second parent. It can be useful to review a merge.
2729 2729
2730 2730 .. container:: verbose
2731 2731
2732 2732 Template:
2733 2733
2734 2734 The following keywords are supported in addition to the common template
2735 2735 keywords and functions. See also :hg:`help templates`.
2736 2736
2737 2737 :diff: String. Diff content.
2738 2738 :parents: List of strings. Parent nodes of the changeset.
2739 2739
2740 2740 Examples:
2741 2741
2742 2742 - use export and import to transplant a bugfix to the current
2743 2743 branch::
2744 2744
2745 2745 hg export -r 9353 | hg import -
2746 2746
2747 2747 - export all the changesets between two revisions to a file with
2748 2748 rename information::
2749 2749
2750 2750 hg export --git -r 123:150 > changes.txt
2751 2751
2752 2752 - split outgoing changes into a series of patches with
2753 2753 descriptive names::
2754 2754
2755 2755 hg export -r "outgoing()" -o "%n-%m.patch"
2756 2756
2757 2757 Returns 0 on success.
2758 2758 """
2759 2759 opts = pycompat.byteskwargs(opts)
2760 2760 bookmark = opts.get(b'bookmark')
2761 2761 changesets += tuple(opts.get(b'rev', []))
2762 2762
2763 2763 cmdutil.check_at_most_one_arg(opts, b'rev', b'bookmark')
2764 2764
2765 2765 if bookmark:
2766 2766 if bookmark not in repo._bookmarks:
2767 2767 raise error.InputError(_(b"bookmark '%s' not found") % bookmark)
2768 2768
2769 2769 revs = scmutil.bookmarkrevs(repo, bookmark)
2770 2770 else:
2771 2771 if not changesets:
2772 2772 changesets = [b'.']
2773 2773
2774 2774 repo = scmutil.unhidehashlikerevs(repo, changesets, b'nowarn')
2775 2775 revs = logcmdutil.revrange(repo, changesets)
2776 2776
2777 2777 if not revs:
2778 2778 raise error.InputError(_(b"export requires at least one changeset"))
2779 2779 if len(revs) > 1:
2780 2780 ui.note(_(b'exporting patches:\n'))
2781 2781 else:
2782 2782 ui.note(_(b'exporting patch:\n'))
2783 2783
2784 2784 fntemplate = opts.get(b'output')
2785 2785 if cmdutil.isstdiofilename(fntemplate):
2786 2786 fntemplate = b''
2787 2787
2788 2788 if fntemplate:
2789 2789 fm = formatter.nullformatter(ui, b'export', opts)
2790 2790 else:
2791 2791 ui.pager(b'export')
2792 2792 fm = ui.formatter(b'export', opts)
2793 2793 with fm:
2794 2794 cmdutil.export(
2795 2795 repo,
2796 2796 revs,
2797 2797 fm,
2798 2798 fntemplate=fntemplate,
2799 2799 switch_parent=opts.get(b'switch_parent'),
2800 2800 opts=patch.diffallopts(ui, opts),
2801 2801 )
2802 2802
2803 2803
2804 2804 @command(
2805 2805 b'files',
2806 2806 [
2807 2807 (
2808 2808 b'r',
2809 2809 b'rev',
2810 2810 b'',
2811 2811 _(b'search the repository as it is in REV'),
2812 2812 _(b'REV'),
2813 2813 ),
2814 2814 (
2815 2815 b'0',
2816 2816 b'print0',
2817 2817 None,
2818 2818 _(b'end filenames with NUL, for use with xargs'),
2819 2819 ),
2820 2820 ]
2821 2821 + walkopts
2822 2822 + formatteropts
2823 2823 + subrepoopts,
2824 2824 _(b'[OPTION]... [FILE]...'),
2825 2825 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2826 2826 intents={INTENT_READONLY},
2827 2827 )
2828 2828 def files(ui, repo, *pats, **opts):
2829 2829 """list tracked files
2830 2830
2831 2831 Print files under Mercurial control in the working directory or
2832 2832 specified revision for given files (excluding removed files).
2833 2833 Files can be specified as filenames or filesets.
2834 2834
2835 2835 If no files are given to match, this command prints the names
2836 2836 of all files under Mercurial control.
2837 2837
2838 2838 .. container:: verbose
2839 2839
2840 2840 Template:
2841 2841
2842 2842 The following keywords are supported in addition to the common template
2843 2843 keywords and functions. See also :hg:`help templates`.
2844 2844
2845 2845 :flags: String. Character denoting file's symlink and executable bits.
2846 2846 :path: String. Repository-absolute path of the file.
2847 2847 :size: Integer. Size of the file in bytes.
2848 2848
2849 2849 Examples:
2850 2850
2851 2851 - list all files under the current directory::
2852 2852
2853 2853 hg files .
2854 2854
2855 2855 - shows sizes and flags for current revision::
2856 2856
2857 2857 hg files -vr .
2858 2858
2859 2859 - list all files named README::
2860 2860
2861 2861 hg files -I "**/README"
2862 2862
2863 2863 - list all binary files::
2864 2864
2865 2865 hg files "set:binary()"
2866 2866
2867 2867 - find files containing a regular expression::
2868 2868
2869 2869 hg files "set:grep('bob')"
2870 2870
2871 2871 - search tracked file contents with xargs and grep::
2872 2872
2873 2873 hg files -0 | xargs -0 grep foo
2874 2874
2875 2875 See :hg:`help patterns` and :hg:`help filesets` for more information
2876 2876 on specifying file patterns.
2877 2877
2878 2878 Returns 0 if a match is found, 1 otherwise.
2879 2879
2880 2880 """
2881 2881
2882 2882 opts = pycompat.byteskwargs(opts)
2883 2883 rev = opts.get(b'rev')
2884 2884 if rev:
2885 2885 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
2886 2886 ctx = logcmdutil.revsingle(repo, rev, None)
2887 2887
2888 2888 end = b'\n'
2889 2889 if opts.get(b'print0'):
2890 2890 end = b'\0'
2891 2891 fmt = b'%s' + end
2892 2892
2893 2893 m = scmutil.match(ctx, pats, opts)
2894 2894 ui.pager(b'files')
2895 2895 uipathfn = scmutil.getuipathfn(ctx.repo(), legacyrelativevalue=True)
2896 2896 with ui.formatter(b'files', opts) as fm:
2897 2897 return cmdutil.files(
2898 2898 ui, ctx, m, uipathfn, fm, fmt, opts.get(b'subrepos')
2899 2899 )
2900 2900
2901 2901
2902 2902 @command(
2903 2903 b'forget',
2904 2904 [
2905 2905 (b'i', b'interactive', None, _(b'use interactive mode')),
2906 2906 ]
2907 2907 + walkopts
2908 2908 + dryrunopts,
2909 2909 _(b'[OPTION]... FILE...'),
2910 2910 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
2911 2911 helpbasic=True,
2912 2912 inferrepo=True,
2913 2913 )
2914 2914 def forget(ui, repo, *pats, **opts):
2915 2915 """forget the specified files on the next commit
2916 2916
2917 2917 Mark the specified files so they will no longer be tracked
2918 2918 after the next commit.
2919 2919
2920 2920 This only removes files from the current branch, not from the
2921 2921 entire project history, and it does not delete them from the
2922 2922 working directory.
2923 2923
2924 2924 To delete the file from the working directory, see :hg:`remove`.
2925 2925
2926 2926 To undo a forget before the next commit, see :hg:`add`.
2927 2927
2928 2928 .. container:: verbose
2929 2929
2930 2930 Examples:
2931 2931
2932 2932 - forget newly-added binary files::
2933 2933
2934 2934 hg forget "set:added() and binary()"
2935 2935
2936 2936 - forget files that would be excluded by .hgignore::
2937 2937
2938 2938 hg forget "set:hgignore()"
2939 2939
2940 2940 Returns 0 on success.
2941 2941 """
2942 2942
2943 2943 if not pats:
2944 2944 raise error.InputError(_(b'no files specified'))
2945 2945
2946 2946 with repo.wlock(), repo.dirstate.changing_files(repo):
2947 2947 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
2948 2948 dryrun, interactive = opts.get('dry_run'), opts.get('interactive')
2949 2949 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
2950 2950 rejected = cmdutil.forget(
2951 2951 ui,
2952 2952 repo,
2953 2953 m,
2954 2954 prefix=b"",
2955 2955 uipathfn=uipathfn,
2956 2956 explicitonly=False,
2957 2957 dryrun=dryrun,
2958 2958 interactive=interactive,
2959 2959 )[0]
2960 2960 return rejected and 1 or 0
2961 2961
2962 2962
2963 2963 @command(
2964 2964 b'graft',
2965 2965 [
2966 2966 (b'r', b'rev', [], _(b'revisions to graft'), _(b'REV')),
2967 2967 (
2968 2968 b'',
2969 2969 b'base',
2970 2970 b'',
2971 2971 _(b'base revision when doing the graft merge (ADVANCED)'),
2972 2972 _(b'REV'),
2973 2973 ),
2974 (
2975 b'',
2976 b'to',
2977 b'',
2978 _(b'graft to this destination, in memory (EXPERIMENTAL)'),
2979 ),
2974 2980 (b'c', b'continue', False, _(b'resume interrupted graft')),
2975 2981 (b'', b'stop', False, _(b'stop interrupted graft')),
2976 2982 (b'', b'abort', False, _(b'abort interrupted graft')),
2977 2983 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
2978 2984 (b'', b'log', None, _(b'append graft info to log message')),
2979 2985 (
2980 2986 b'',
2981 2987 b'no-commit',
2982 2988 None,
2983 2989 _(b"don't commit, just apply the changes in working directory"),
2984 2990 ),
2985 2991 (b'f', b'force', False, _(b'force graft')),
2986 2992 (
2987 2993 b'D',
2988 2994 b'currentdate',
2989 2995 False,
2990 2996 _(b'record the current date as commit date'),
2991 2997 ),
2992 2998 (
2993 2999 b'U',
2994 3000 b'currentuser',
2995 3001 False,
2996 3002 _(b'record the current user as committer'),
2997 3003 ),
2998 3004 ]
2999 3005 + commitopts2
3000 3006 + mergetoolopts
3001 3007 + dryrunopts,
3002 3008 _(b'[OPTION]... [-r REV]... REV...'),
3003 3009 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
3004 3010 )
3005 3011 def graft(ui, repo, *revs, **opts):
3006 3012 """copy changes from other branches onto the current branch
3007 3013
3008 3014 This command uses Mercurial's merge logic to copy individual
3009 3015 changes from other branches without merging branches in the
3010 3016 history graph. This is sometimes known as 'backporting' or
3011 3017 'cherry-picking'. By default, graft will copy user, date, and
3012 3018 description from the source changesets.
3013 3019
3014 3020 Changesets that are ancestors of the current revision, that have
3015 3021 already been grafted, or that are merges will be skipped.
3016 3022
3017 3023 If --log is specified, log messages will have a comment appended
3018 3024 of the form::
3019 3025
3020 3026 (grafted from CHANGESETHASH)
3021 3027
3022 3028 If --force is specified, revisions will be grafted even if they
3023 3029 are already ancestors of, or have been grafted to, the destination.
3024 3030 This is useful when the revisions have since been backed out.
3025 3031
3026 3032 If a graft merge results in conflicts, the graft process is
3027 3033 interrupted so that the current merge can be manually resolved.
3028 3034 Once all conflicts are addressed, the graft process can be
3029 3035 continued with the -c/--continue option.
3030 3036
3031 3037 The -c/--continue option reapplies all the earlier options.
3032 3038
3033 3039 .. container:: verbose
3034 3040
3035 3041 The --base option exposes more of how graft internally uses merge with a
3036 3042 custom base revision. --base can be used to specify another ancestor than
3037 3043 the first and only parent.
3038 3044
3039 3045 The command::
3040 3046
3041 3047 hg graft -r 345 --base 234
3042 3048
3043 3049 is thus pretty much the same as::
3044 3050
3045 3051 hg diff --from 234 --to 345 | hg import
3046 3052
3047 3053 but using merge to resolve conflicts and track moved files.
3048 3054
3049 3055 The result of a merge can thus be backported as a single commit by
3050 3056 specifying one of the merge parents as base, and thus effectively
3051 3057 grafting the changes from the other side.
3052 3058
3053 3059 It is also possible to collapse multiple changesets and clean up history
3054 3060 by specifying another ancestor as base, much like rebase --collapse
3055 3061 --keep.
3056 3062
3057 3063 The commit message can be tweaked after the fact using commit --amend .
3058 3064
3059 3065 For using non-ancestors as the base to backout changes, see the backout
3060 3066 command and the hidden --parent option.
3061 3067
3062 3068 .. container:: verbose
3063 3069
3070 The experimental --to option allow to graft a revision in memory,
3071 independently from the working copy. Merge conflict are not currenly
3072 supported and the operation will be aborted if the configured tool
3073 cannot handle the conflict that might be encountered.
3074
3075 As the operation is performence in memory, the on disk file will not be
3076 modified and some hooks might not be run.
3077
3078 .. container:: verbose
3079
3064 3080 Examples:
3065 3081
3066 3082 - copy a single change to the stable branch and edit its description::
3067 3083
3068 3084 hg update stable
3069 3085 hg graft --edit 9393
3070 3086
3071 3087 - graft a range of changesets with one exception, updating dates::
3072 3088
3073 3089 hg graft -D "2085::2093 and not 2091"
3074 3090
3075 3091 - continue a graft after resolving conflicts::
3076 3092
3077 3093 hg graft -c
3078 3094
3079 3095 - show the source of a grafted changeset::
3080 3096
3081 3097 hg log --debug -r .
3082 3098
3083 3099 - show revisions sorted by date::
3084 3100
3085 3101 hg log -r "sort(all(), date)"
3086 3102
3087 3103 - backport the result of a merge as a single commit::
3088 3104
3089 3105 hg graft -r 123 --base 123^
3090 3106
3091 3107 - land a feature branch as one changeset::
3092 3108
3093 3109 hg up -cr default
3094 3110 hg graft -r featureX --base "ancestor('featureX', 'default')"
3095 3111
3096 3112 See :hg:`help revisions` for more about specifying revisions.
3097 3113
3098 3114 Returns 0 on successful completion, 1 if there are unresolved files.
3099 3115 """
3100 3116 with repo.wlock():
3101 3117 return graft_impl.cmd_graft(ui, repo, *revs, **opts)
3102 3118
3103 3119
3104 3120 statemod.addunfinished(
3105 3121 b'graft',
3106 3122 fname=b'graftstate',
3107 3123 clearable=True,
3108 3124 stopflag=True,
3109 3125 continueflag=True,
3110 3126 abortfunc=cmdutil.hgabortgraft,
3111 3127 cmdhint=_(b"use 'hg graft --continue' or 'hg graft --stop' to stop"),
3112 3128 )
3113 3129
3114 3130
3115 3131 @command(
3116 3132 b'grep',
3117 3133 [
3118 3134 (b'0', b'print0', None, _(b'end fields with NUL')),
3119 3135 (b'', b'all', None, _(b'an alias to --diff (DEPRECATED)')),
3120 3136 (
3121 3137 b'',
3122 3138 b'diff',
3123 3139 None,
3124 3140 _(
3125 3141 b'search revision differences for when the pattern was added '
3126 3142 b'or removed'
3127 3143 ),
3128 3144 ),
3129 3145 (b'a', b'text', None, _(b'treat all files as text')),
3130 3146 (
3131 3147 b'f',
3132 3148 b'follow',
3133 3149 None,
3134 3150 _(
3135 3151 b'follow changeset history,'
3136 3152 b' or file history across copies and renames'
3137 3153 ),
3138 3154 ),
3139 3155 (b'i', b'ignore-case', None, _(b'ignore case when matching')),
3140 3156 (
3141 3157 b'l',
3142 3158 b'files-with-matches',
3143 3159 None,
3144 3160 _(b'print only filenames and revisions that match'),
3145 3161 ),
3146 3162 (b'n', b'line-number', None, _(b'print matching line numbers')),
3147 3163 (
3148 3164 b'r',
3149 3165 b'rev',
3150 3166 [],
3151 3167 _(b'search files changed within revision range'),
3152 3168 _(b'REV'),
3153 3169 ),
3154 3170 (
3155 3171 b'',
3156 3172 b'all-files',
3157 3173 None,
3158 3174 _(
3159 3175 b'include all files in the changeset while grepping (DEPRECATED)'
3160 3176 ),
3161 3177 ),
3162 3178 (b'u', b'user', None, _(b'list the author (long with -v)')),
3163 3179 (b'd', b'date', None, _(b'list the date (short with -q)')),
3164 3180 ]
3165 3181 + formatteropts
3166 3182 + walkopts,
3167 3183 _(b'[--diff] [OPTION]... PATTERN [FILE]...'),
3168 3184 helpcategory=command.CATEGORY_FILE_CONTENTS,
3169 3185 inferrepo=True,
3170 3186 intents={INTENT_READONLY},
3171 3187 )
3172 3188 def grep(ui, repo, pattern, *pats, **opts):
3173 3189 """search for a pattern in specified files
3174 3190
3175 3191 Search the working directory or revision history for a regular
3176 3192 expression in the specified files for the entire repository.
3177 3193
3178 3194 By default, grep searches the repository files in the working
3179 3195 directory and prints the files where it finds a match. To specify
3180 3196 historical revisions instead of the working directory, use the
3181 3197 --rev flag.
3182 3198
3183 3199 To search instead historical revision differences that contains a
3184 3200 change in match status ("-" for a match that becomes a non-match,
3185 3201 or "+" for a non-match that becomes a match), use the --diff flag.
3186 3202
3187 3203 PATTERN can be any Python (roughly Perl-compatible) regular
3188 3204 expression.
3189 3205
3190 3206 If no FILEs are specified and the --rev flag isn't supplied, all
3191 3207 files in the working directory are searched. When using the --rev
3192 3208 flag and specifying FILEs, use the --follow argument to also
3193 3209 follow the specified FILEs across renames and copies.
3194 3210
3195 3211 .. container:: verbose
3196 3212
3197 3213 Template:
3198 3214
3199 3215 The following keywords are supported in addition to the common template
3200 3216 keywords and functions. See also :hg:`help templates`.
3201 3217
3202 3218 :change: String. Character denoting insertion ``+`` or removal ``-``.
3203 3219 Available if ``--diff`` is specified.
3204 3220 :lineno: Integer. Line number of the match.
3205 3221 :path: String. Repository-absolute path of the file.
3206 3222 :texts: List of text chunks.
3207 3223
3208 3224 And each entry of ``{texts}`` provides the following sub-keywords.
3209 3225
3210 3226 :matched: Boolean. True if the chunk matches the specified pattern.
3211 3227 :text: String. Chunk content.
3212 3228
3213 3229 See :hg:`help templates.operators` for the list expansion syntax.
3214 3230
3215 3231 Returns 0 if a match is found, 1 otherwise.
3216 3232
3217 3233 """
3218 3234 cmdutil.check_incompatible_arguments(opts, 'all_files', ['all', 'diff'])
3219 3235
3220 3236 diff = opts.get('all') or opts.get('diff')
3221 3237 follow = opts.get('follow')
3222 3238 if opts.get('all_files') is None and not diff:
3223 3239 opts['all_files'] = True
3224 3240 plaingrep = (
3225 3241 opts.get('all_files') and not opts.get('rev') and not opts.get('follow')
3226 3242 )
3227 3243 all_files = opts.get('all_files')
3228 3244 if plaingrep:
3229 3245 opts['rev'] = [b'wdir()']
3230 3246
3231 3247 reflags = re.M
3232 3248 if opts.get('ignore_case'):
3233 3249 reflags |= re.I
3234 3250 try:
3235 3251 regexp = util.re.compile(pattern, reflags)
3236 3252 except re.error as inst:
3237 3253 ui.warn(
3238 3254 _(b"grep: invalid match pattern: %s\n")
3239 3255 % stringutil.forcebytestr(inst)
3240 3256 )
3241 3257 return 1
3242 3258 sep, eol = b':', b'\n'
3243 3259 if opts.get('print0'):
3244 3260 sep = eol = b'\0'
3245 3261
3246 3262 searcher = grepmod.grepsearcher(
3247 3263 ui, repo, regexp, all_files=all_files, diff=diff, follow=follow
3248 3264 )
3249 3265
3250 3266 getfile = searcher._getfile
3251 3267
3252 3268 uipathfn = scmutil.getuipathfn(repo)
3253 3269
3254 3270 def display(fm, fn, ctx, pstates, states):
3255 3271 rev = scmutil.intrev(ctx)
3256 3272 if fm.isplain():
3257 3273 formatuser = ui.shortuser
3258 3274 else:
3259 3275 formatuser = pycompat.bytestr
3260 3276 if ui.quiet:
3261 3277 datefmt = b'%Y-%m-%d'
3262 3278 else:
3263 3279 datefmt = b'%a %b %d %H:%M:%S %Y %1%2'
3264 3280 found = False
3265 3281
3266 3282 @util.cachefunc
3267 3283 def binary():
3268 3284 flog = getfile(fn)
3269 3285 try:
3270 3286 return stringutil.binary(flog.read(ctx.filenode(fn)))
3271 3287 except error.WdirUnsupported:
3272 3288 return ctx[fn].isbinary()
3273 3289
3274 3290 fieldnamemap = {b'linenumber': b'lineno'}
3275 3291 if diff:
3276 3292 iter = grepmod.difflinestates(pstates, states)
3277 3293 else:
3278 3294 iter = [(b'', l) for l in states]
3279 3295 for change, l in iter:
3280 3296 fm.startitem()
3281 3297 fm.context(ctx=ctx)
3282 3298 fm.data(node=fm.hexfunc(scmutil.binnode(ctx)), path=fn)
3283 3299 fm.plain(uipathfn(fn), label=b'grep.filename')
3284 3300
3285 3301 cols = [
3286 3302 (b'rev', b'%d', rev, not plaingrep, b''),
3287 3303 (
3288 3304 b'linenumber',
3289 3305 b'%d',
3290 3306 l.linenum,
3291 3307 opts.get('line_number'),
3292 3308 b'',
3293 3309 ),
3294 3310 ]
3295 3311 if diff:
3296 3312 cols.append(
3297 3313 (
3298 3314 b'change',
3299 3315 b'%s',
3300 3316 change,
3301 3317 True,
3302 3318 b'grep.inserted '
3303 3319 if change == b'+'
3304 3320 else b'grep.deleted ',
3305 3321 )
3306 3322 )
3307 3323 cols.extend(
3308 3324 [
3309 3325 (
3310 3326 b'user',
3311 3327 b'%s',
3312 3328 formatuser(ctx.user()),
3313 3329 opts.get('user'),
3314 3330 b'',
3315 3331 ),
3316 3332 (
3317 3333 b'date',
3318 3334 b'%s',
3319 3335 fm.formatdate(ctx.date(), datefmt),
3320 3336 opts.get('date'),
3321 3337 b'',
3322 3338 ),
3323 3339 ]
3324 3340 )
3325 3341 for name, fmt, data, cond, extra_label in cols:
3326 3342 if cond:
3327 3343 fm.plain(sep, label=b'grep.sep')
3328 3344 field = fieldnamemap.get(name, name)
3329 3345 label = extra_label + (b'grep.%s' % name)
3330 3346 fm.condwrite(cond, field, fmt, data, label=label)
3331 3347 if not opts.get('files_with_matches'):
3332 3348 fm.plain(sep, label=b'grep.sep')
3333 3349 if not opts.get('text') and binary():
3334 3350 fm.plain(_(b" Binary file matches"))
3335 3351 else:
3336 3352 displaymatches(fm.nested(b'texts', tmpl=b'{text}'), l)
3337 3353 fm.plain(eol)
3338 3354 found = True
3339 3355 if opts.get('files_with_matches'):
3340 3356 break
3341 3357 return found
3342 3358
3343 3359 def displaymatches(fm, l):
3344 3360 p = 0
3345 3361 for s, e in l.findpos(regexp):
3346 3362 if p < s:
3347 3363 fm.startitem()
3348 3364 fm.write(b'text', b'%s', l.line[p:s])
3349 3365 fm.data(matched=False)
3350 3366 fm.startitem()
3351 3367 fm.write(b'text', b'%s', l.line[s:e], label=b'grep.match')
3352 3368 fm.data(matched=True)
3353 3369 p = e
3354 3370 if p < len(l.line):
3355 3371 fm.startitem()
3356 3372 fm.write(b'text', b'%s', l.line[p:])
3357 3373 fm.data(matched=False)
3358 3374 fm.end()
3359 3375
3360 3376 found = False
3361 3377
3362 3378 wopts = logcmdutil.walkopts(
3363 3379 pats=pats,
3364 3380 opts=pycompat.byteskwargs(opts),
3365 3381 revspec=opts['rev'],
3366 3382 include_pats=opts['include'],
3367 3383 exclude_pats=opts['exclude'],
3368 3384 follow=follow,
3369 3385 force_changelog_traversal=all_files,
3370 3386 filter_revisions_by_pats=not all_files,
3371 3387 )
3372 3388 revs, makefilematcher = logcmdutil.makewalker(repo, wopts)
3373 3389
3374 3390 ui.pager(b'grep')
3375 3391 fm = ui.formatter(b'grep', pycompat.byteskwargs(opts))
3376 3392 for fn, ctx, pstates, states in searcher.searchfiles(revs, makefilematcher):
3377 3393 r = display(fm, fn, ctx, pstates, states)
3378 3394 found = found or r
3379 3395 if r and not diff and not all_files:
3380 3396 searcher.skipfile(fn, ctx.rev())
3381 3397 fm.end()
3382 3398
3383 3399 return not found
3384 3400
3385 3401
3386 3402 @command(
3387 3403 b'heads',
3388 3404 [
3389 3405 (
3390 3406 b'r',
3391 3407 b'rev',
3392 3408 b'',
3393 3409 _(b'show only heads which are descendants of STARTREV'),
3394 3410 _(b'STARTREV'),
3395 3411 ),
3396 3412 (b't', b'topo', False, _(b'show topological heads only')),
3397 3413 (
3398 3414 b'a',
3399 3415 b'active',
3400 3416 False,
3401 3417 _(b'show active branchheads only (DEPRECATED)'),
3402 3418 ),
3403 3419 (b'c', b'closed', False, _(b'show normal and closed branch heads')),
3404 3420 ]
3405 3421 + templateopts,
3406 3422 _(b'[-ct] [-r STARTREV] [REV]...'),
3407 3423 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3408 3424 intents={INTENT_READONLY},
3409 3425 )
3410 3426 def heads(ui, repo, *branchrevs, **opts):
3411 3427 """show branch heads
3412 3428
3413 3429 With no arguments, show all open branch heads in the repository.
3414 3430 Branch heads are changesets that have no descendants on the
3415 3431 same branch. They are where development generally takes place and
3416 3432 are the usual targets for update and merge operations.
3417 3433
3418 3434 If one or more REVs are given, only open branch heads on the
3419 3435 branches associated with the specified changesets are shown. This
3420 3436 means that you can use :hg:`heads .` to see the heads on the
3421 3437 currently checked-out branch.
3422 3438
3423 3439 If -c/--closed is specified, also show branch heads marked closed
3424 3440 (see :hg:`commit --close-branch`).
3425 3441
3426 3442 If STARTREV is specified, only those heads that are descendants of
3427 3443 STARTREV will be displayed.
3428 3444
3429 3445 If -t/--topo is specified, named branch mechanics will be ignored and only
3430 3446 topological heads (changesets with no children) will be shown.
3431 3447
3432 3448 Returns 0 if matching heads are found, 1 if not.
3433 3449 """
3434 3450
3435 3451 start = None
3436 3452 rev = opts.get('rev')
3437 3453 if rev:
3438 3454 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3439 3455 start = logcmdutil.revsingle(repo, rev, None).node()
3440 3456
3441 3457 if opts.get('topo'):
3442 3458 heads = [repo[h] for h in repo.heads(start)]
3443 3459 else:
3444 3460 heads = []
3445 3461 for branch in repo.branchmap():
3446 3462 heads += repo.branchheads(branch, start, opts.get('closed'))
3447 3463 heads = [repo[h] for h in heads]
3448 3464
3449 3465 if branchrevs:
3450 3466 branches = {
3451 3467 repo[r].branch() for r in logcmdutil.revrange(repo, branchrevs)
3452 3468 }
3453 3469 heads = [h for h in heads if h.branch() in branches]
3454 3470
3455 3471 if opts.get('active') and branchrevs:
3456 3472 dagheads = repo.heads(start)
3457 3473 heads = [h for h in heads if h.node() in dagheads]
3458 3474
3459 3475 if branchrevs:
3460 3476 haveheads = {h.branch() for h in heads}
3461 3477 if branches - haveheads:
3462 3478 headless = b', '.join(b for b in branches - haveheads)
3463 3479 msg = _(b'no open branch heads found on branches %s')
3464 3480 if opts.get('rev'):
3465 3481 msg += _(b' (started at %s)') % opts['rev']
3466 3482 ui.warn((msg + b'\n') % headless)
3467 3483
3468 3484 if not heads:
3469 3485 return 1
3470 3486
3471 3487 ui.pager(b'heads')
3472 3488 heads = sorted(heads, key=lambda x: -(x.rev()))
3473 3489 displayer = logcmdutil.changesetdisplayer(
3474 3490 ui, repo, pycompat.byteskwargs(opts)
3475 3491 )
3476 3492 for ctx in heads:
3477 3493 displayer.show(ctx)
3478 3494 displayer.close()
3479 3495
3480 3496
3481 3497 @command(
3482 3498 b'help',
3483 3499 [
3484 3500 (b'e', b'extension', None, _(b'show only help for extensions')),
3485 3501 (b'c', b'command', None, _(b'show only help for commands')),
3486 3502 (b'k', b'keyword', None, _(b'show topics matching keyword')),
3487 3503 (
3488 3504 b's',
3489 3505 b'system',
3490 3506 [],
3491 3507 _(b'show help for specific platform(s)'),
3492 3508 _(b'PLATFORM'),
3493 3509 ),
3494 3510 ],
3495 3511 _(b'[-eck] [-s PLATFORM] [TOPIC]'),
3496 3512 helpcategory=command.CATEGORY_HELP,
3497 3513 norepo=True,
3498 3514 intents={INTENT_READONLY},
3499 3515 )
3500 3516 def help_(ui, name=None, **opts):
3501 3517 """show help for a given topic or a help overview
3502 3518
3503 3519 With no arguments, print a list of commands with short help messages.
3504 3520
3505 3521 Given a topic, extension, or command name, print help for that
3506 3522 topic.
3507 3523
3508 3524 Returns 0 if successful.
3509 3525 """
3510 3526
3511 3527 keep = opts.get('system') or []
3512 3528 if len(keep) == 0:
3513 3529 if pycompat.sysplatform.startswith(b'win'):
3514 3530 keep.append(b'windows')
3515 3531 elif pycompat.sysplatform == b'OpenVMS':
3516 3532 keep.append(b'vms')
3517 3533 elif pycompat.sysplatform == b'plan9':
3518 3534 keep.append(b'plan9')
3519 3535 else:
3520 3536 keep.append(b'unix')
3521 3537 keep.append(pycompat.sysplatform.lower())
3522 3538 if ui.verbose:
3523 3539 keep.append(b'verbose')
3524 3540
3525 3541 commands = sys.modules[__name__]
3526 3542 formatted = help.formattedhelp(ui, commands, name, keep=keep, **opts)
3527 3543 ui.pager(b'help')
3528 3544 ui.write(formatted)
3529 3545
3530 3546
3531 3547 @command(
3532 3548 b'identify|id',
3533 3549 [
3534 3550 (b'r', b'rev', b'', _(b'identify the specified revision'), _(b'REV')),
3535 3551 (b'n', b'num', None, _(b'show local revision number')),
3536 3552 (b'i', b'id', None, _(b'show global revision id')),
3537 3553 (b'b', b'branch', None, _(b'show branch')),
3538 3554 (b't', b'tags', None, _(b'show tags')),
3539 3555 (b'B', b'bookmarks', None, _(b'show bookmarks')),
3540 3556 ]
3541 3557 + remoteopts
3542 3558 + formatteropts,
3543 3559 _(b'[-nibtB] [-r REV] [SOURCE]'),
3544 3560 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
3545 3561 optionalrepo=True,
3546 3562 intents={INTENT_READONLY},
3547 3563 )
3548 3564 def identify(
3549 3565 ui,
3550 3566 repo,
3551 3567 source=None,
3552 3568 rev=None,
3553 3569 num=None,
3554 3570 id=None,
3555 3571 branch=None,
3556 3572 tags=None,
3557 3573 bookmarks=None,
3558 3574 **opts,
3559 3575 ):
3560 3576 """identify the working directory or specified revision
3561 3577
3562 3578 Print a summary identifying the repository state at REV using one or
3563 3579 two parent hash identifiers, followed by a "+" if the working
3564 3580 directory has uncommitted changes, the branch name (if not default),
3565 3581 a list of tags, and a list of bookmarks.
3566 3582
3567 3583 When REV is not given, print a summary of the current state of the
3568 3584 repository including the working directory. Specify -r. to get information
3569 3585 of the working directory parent without scanning uncommitted changes.
3570 3586
3571 3587 Specifying a path to a repository root or Mercurial bundle will
3572 3588 cause lookup to operate on that repository/bundle.
3573 3589
3574 3590 .. container:: verbose
3575 3591
3576 3592 Template:
3577 3593
3578 3594 The following keywords are supported in addition to the common template
3579 3595 keywords and functions. See also :hg:`help templates`.
3580 3596
3581 3597 :dirty: String. Character ``+`` denoting if the working directory has
3582 3598 uncommitted changes.
3583 3599 :id: String. One or two nodes, optionally followed by ``+``.
3584 3600 :parents: List of strings. Parent nodes of the changeset.
3585 3601
3586 3602 Examples:
3587 3603
3588 3604 - generate a build identifier for the working directory::
3589 3605
3590 3606 hg id --id > build-id.dat
3591 3607
3592 3608 - find the revision corresponding to a tag::
3593 3609
3594 3610 hg id -n -r 1.3
3595 3611
3596 3612 - check the most recent revision of a remote repository::
3597 3613
3598 3614 hg id -r tip https://www.mercurial-scm.org/repo/hg/
3599 3615
3600 3616 See :hg:`log` for generating more information about specific revisions,
3601 3617 including full hash identifiers.
3602 3618
3603 3619 Returns 0 if successful.
3604 3620 """
3605 3621
3606 3622 opts = pycompat.byteskwargs(opts)
3607 3623 if not repo and not source:
3608 3624 raise error.InputError(
3609 3625 _(b"there is no Mercurial repository here (.hg not found)")
3610 3626 )
3611 3627
3612 3628 default = not (num or id or branch or tags or bookmarks)
3613 3629 output = []
3614 3630 revs = []
3615 3631
3616 3632 peer = None
3617 3633 try:
3618 3634 if source:
3619 3635 path = urlutil.get_unique_pull_path_obj(b'identify', ui, source)
3620 3636 # only pass ui when no repo
3621 3637 peer = hg.peer(repo or ui, opts, path)
3622 3638 repo = peer.local()
3623 3639 branches = (path.branch, [])
3624 3640 revs, checkout = hg.addbranchrevs(repo, peer, branches, None)
3625 3641
3626 3642 fm = ui.formatter(b'identify', opts)
3627 3643 fm.startitem()
3628 3644
3629 3645 if not repo:
3630 3646 if num or branch or tags:
3631 3647 raise error.InputError(
3632 3648 _(b"can't query remote revision number, branch, or tags")
3633 3649 )
3634 3650 if not rev and revs:
3635 3651 rev = revs[0]
3636 3652 if not rev:
3637 3653 rev = b"tip"
3638 3654
3639 3655 remoterev = peer.lookup(rev)
3640 3656 hexrev = fm.hexfunc(remoterev)
3641 3657 if default or id:
3642 3658 output = [hexrev]
3643 3659 fm.data(id=hexrev)
3644 3660
3645 3661 @util.cachefunc
3646 3662 def getbms():
3647 3663 bms = []
3648 3664
3649 3665 if b'bookmarks' in peer.listkeys(b'namespaces'):
3650 3666 hexremoterev = hex(remoterev)
3651 3667 bms = [
3652 3668 bm
3653 3669 for bm, bmr in peer.listkeys(b'bookmarks').items()
3654 3670 if bmr == hexremoterev
3655 3671 ]
3656 3672
3657 3673 return sorted(bms)
3658 3674
3659 3675 if fm.isplain():
3660 3676 if bookmarks:
3661 3677 output.extend(getbms())
3662 3678 elif default and not ui.quiet:
3663 3679 # multiple bookmarks for a single parent separated by '/'
3664 3680 bm = b'/'.join(getbms())
3665 3681 if bm:
3666 3682 output.append(bm)
3667 3683 else:
3668 3684 fm.data(node=hex(remoterev))
3669 3685 if bookmarks or b'bookmarks' in fm.datahint():
3670 3686 fm.data(bookmarks=fm.formatlist(getbms(), name=b'bookmark'))
3671 3687 else:
3672 3688 if rev:
3673 3689 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
3674 3690 ctx = logcmdutil.revsingle(repo, rev, None)
3675 3691
3676 3692 if ctx.rev() is None:
3677 3693 ctx = repo[None]
3678 3694 parents = ctx.parents()
3679 3695 taglist = []
3680 3696 for p in parents:
3681 3697 taglist.extend(p.tags())
3682 3698
3683 3699 dirty = b""
3684 3700 if ctx.dirty(missing=True, merge=False, branch=False):
3685 3701 dirty = b'+'
3686 3702 fm.data(dirty=dirty)
3687 3703
3688 3704 hexoutput = [fm.hexfunc(p.node()) for p in parents]
3689 3705 if default or id:
3690 3706 output = [b"%s%s" % (b'+'.join(hexoutput), dirty)]
3691 3707 fm.data(id=b"%s%s" % (b'+'.join(hexoutput), dirty))
3692 3708
3693 3709 if num:
3694 3710 numoutput = [b"%d" % p.rev() for p in parents]
3695 3711 output.append(b"%s%s" % (b'+'.join(numoutput), dirty))
3696 3712
3697 3713 fm.data(
3698 3714 parents=fm.formatlist(
3699 3715 [fm.hexfunc(p.node()) for p in parents], name=b'node'
3700 3716 )
3701 3717 )
3702 3718 else:
3703 3719 hexoutput = fm.hexfunc(ctx.node())
3704 3720 if default or id:
3705 3721 output = [hexoutput]
3706 3722 fm.data(id=hexoutput)
3707 3723
3708 3724 if num:
3709 3725 output.append(pycompat.bytestr(ctx.rev()))
3710 3726 taglist = ctx.tags()
3711 3727
3712 3728 if default and not ui.quiet:
3713 3729 b = ctx.branch()
3714 3730 if b != b'default':
3715 3731 output.append(b"(%s)" % b)
3716 3732
3717 3733 # multiple tags for a single parent separated by '/'
3718 3734 t = b'/'.join(taglist)
3719 3735 if t:
3720 3736 output.append(t)
3721 3737
3722 3738 # multiple bookmarks for a single parent separated by '/'
3723 3739 bm = b'/'.join(ctx.bookmarks())
3724 3740 if bm:
3725 3741 output.append(bm)
3726 3742 else:
3727 3743 if branch:
3728 3744 output.append(ctx.branch())
3729 3745
3730 3746 if tags:
3731 3747 output.extend(taglist)
3732 3748
3733 3749 if bookmarks:
3734 3750 output.extend(ctx.bookmarks())
3735 3751
3736 3752 fm.data(node=ctx.hex())
3737 3753 fm.data(branch=ctx.branch())
3738 3754 fm.data(tags=fm.formatlist(taglist, name=b'tag', sep=b':'))
3739 3755 fm.data(bookmarks=fm.formatlist(ctx.bookmarks(), name=b'bookmark'))
3740 3756 fm.context(ctx=ctx)
3741 3757
3742 3758 fm.plain(b"%s\n" % b' '.join(output))
3743 3759 fm.end()
3744 3760 finally:
3745 3761 if peer:
3746 3762 peer.close()
3747 3763
3748 3764
3749 3765 @command(
3750 3766 b'import|patch',
3751 3767 [
3752 3768 (
3753 3769 b'p',
3754 3770 b'strip',
3755 3771 1,
3756 3772 _(
3757 3773 b'directory strip option for patch. This has the same '
3758 3774 b'meaning as the corresponding patch option'
3759 3775 ),
3760 3776 _(b'NUM'),
3761 3777 ),
3762 3778 (b'b', b'base', b'', _(b'base path (DEPRECATED)'), _(b'PATH')),
3763 3779 (b'', b'secret', None, _(b'use the secret phase for committing')),
3764 3780 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
3765 3781 (
3766 3782 b'f',
3767 3783 b'force',
3768 3784 None,
3769 3785 _(b'skip check for outstanding uncommitted changes (DEPRECATED)'),
3770 3786 ),
3771 3787 (
3772 3788 b'',
3773 3789 b'no-commit',
3774 3790 None,
3775 3791 _(b"don't commit, just update the working directory"),
3776 3792 ),
3777 3793 (
3778 3794 b'',
3779 3795 b'bypass',
3780 3796 None,
3781 3797 _(b"apply patch without touching the working directory"),
3782 3798 ),
3783 3799 (b'', b'partial', None, _(b'commit even if some hunks fail')),
3784 3800 (b'', b'exact', None, _(b'abort if patch would apply lossily')),
3785 3801 (b'', b'prefix', b'', _(b'apply patch to subdirectory'), _(b'DIR')),
3786 3802 (
3787 3803 b'',
3788 3804 b'import-branch',
3789 3805 None,
3790 3806 _(b'use any branch information in patch (implied by --exact)'),
3791 3807 ),
3792 3808 ]
3793 3809 + commitopts
3794 3810 + commitopts2
3795 3811 + similarityopts,
3796 3812 _(b'[OPTION]... PATCH...'),
3797 3813 helpcategory=command.CATEGORY_IMPORT_EXPORT,
3798 3814 )
3799 3815 def import_(ui, repo, patch1=None, *patches, **opts):
3800 3816 """import an ordered set of patches
3801 3817
3802 3818 Import a list of patches and commit them individually (unless
3803 3819 --no-commit is specified).
3804 3820
3805 3821 To read a patch from standard input (stdin), use "-" as the patch
3806 3822 name. If a URL is specified, the patch will be downloaded from
3807 3823 there.
3808 3824
3809 3825 Import first applies changes to the working directory (unless
3810 3826 --bypass is specified), import will abort if there are outstanding
3811 3827 changes.
3812 3828
3813 3829 Use --bypass to apply and commit patches directly to the
3814 3830 repository, without affecting the working directory. Without
3815 3831 --exact, patches will be applied on top of the working directory
3816 3832 parent revision.
3817 3833
3818 3834 You can import a patch straight from a mail message. Even patches
3819 3835 as attachments work (to use the body part, it must have type
3820 3836 text/plain or text/x-patch). From and Subject headers of email
3821 3837 message are used as default committer and commit message. All
3822 3838 text/plain body parts before first diff are added to the commit
3823 3839 message.
3824 3840
3825 3841 If the imported patch was generated by :hg:`export`, user and
3826 3842 description from patch override values from message headers and
3827 3843 body. Values given on command line with -m/--message and -u/--user
3828 3844 override these.
3829 3845
3830 3846 If --exact is specified, import will set the working directory to
3831 3847 the parent of each patch before applying it, and will abort if the
3832 3848 resulting changeset has a different ID than the one recorded in
3833 3849 the patch. This will guard against various ways that portable
3834 3850 patch formats and mail systems might fail to transfer Mercurial
3835 3851 data or metadata. See :hg:`bundle` for lossless transmission.
3836 3852
3837 3853 Use --partial to ensure a changeset will be created from the patch
3838 3854 even if some hunks fail to apply. Hunks that fail to apply will be
3839 3855 written to a <target-file>.rej file. Conflicts can then be resolved
3840 3856 by hand before :hg:`commit --amend` is run to update the created
3841 3857 changeset. This flag exists to let people import patches that
3842 3858 partially apply without losing the associated metadata (author,
3843 3859 date, description, ...).
3844 3860
3845 3861 .. note::
3846 3862
3847 3863 When no hunks apply cleanly, :hg:`import --partial` will create
3848 3864 an empty changeset, importing only the patch metadata.
3849 3865
3850 3866 With -s/--similarity, hg will attempt to discover renames and
3851 3867 copies in the patch in the same way as :hg:`addremove`.
3852 3868
3853 3869 It is possible to use external patch programs to perform the patch
3854 3870 by setting the ``ui.patch`` configuration option. For the default
3855 3871 internal tool, the fuzz can also be configured via ``patch.fuzz``.
3856 3872 See :hg:`help config` for more information about configuration
3857 3873 files and how to use these options.
3858 3874
3859 3875 See :hg:`help dates` for a list of formats valid for -d/--date.
3860 3876
3861 3877 .. container:: verbose
3862 3878
3863 3879 Examples:
3864 3880
3865 3881 - import a traditional patch from a website and detect renames::
3866 3882
3867 3883 hg import -s 80 http://example.com/bugfix.patch
3868 3884
3869 3885 - import a changeset from an hgweb server::
3870 3886
3871 3887 hg import https://www.mercurial-scm.org/repo/hg/rev/5ca8c111e9aa
3872 3888
3873 3889 - import all the patches in an Unix-style mbox::
3874 3890
3875 3891 hg import incoming-patches.mbox
3876 3892
3877 3893 - import patches from stdin::
3878 3894
3879 3895 hg import -
3880 3896
3881 3897 - attempt to exactly restore an exported changeset (not always
3882 3898 possible)::
3883 3899
3884 3900 hg import --exact proposed-fix.patch
3885 3901
3886 3902 - use an external tool to apply a patch which is too fuzzy for
3887 3903 the default internal tool.
3888 3904
3889 3905 hg import --config ui.patch="patch --merge" fuzzy.patch
3890 3906
3891 3907 - change the default fuzzing from 2 to a less strict 7
3892 3908
3893 3909 hg import --config ui.fuzz=7 fuzz.patch
3894 3910
3895 3911 Returns 0 on success, 1 on partial success (see --partial).
3896 3912 """
3897 3913
3898 3914 cmdutil.check_incompatible_arguments(
3899 3915 opts, 'no_commit', ['bypass', 'secret']
3900 3916 )
3901 3917 cmdutil.check_incompatible_arguments(opts, 'exact', ['edit', 'prefix'])
3902 3918
3903 3919 if not patch1:
3904 3920 raise error.InputError(_(b'need at least one patch to import'))
3905 3921
3906 3922 patches = (patch1,) + patches
3907 3923
3908 3924 date = opts.get('date')
3909 3925 if date:
3910 3926 opts['date'] = dateutil.parsedate(date)
3911 3927
3912 3928 exact = opts.get('exact')
3913 3929 update = not opts.get('bypass')
3914 3930 try:
3915 3931 sim = float(opts.get('similarity') or 0)
3916 3932 except ValueError:
3917 3933 raise error.InputError(_(b'similarity must be a number'))
3918 3934 if sim < 0 or sim > 100:
3919 3935 raise error.InputError(_(b'similarity must be between 0 and 100'))
3920 3936 if sim and not update:
3921 3937 raise error.InputError(_(b'cannot use --similarity with --bypass'))
3922 3938
3923 3939 base = opts["base"]
3924 3940 msgs = []
3925 3941 ret = 0
3926 3942
3927 3943 with repo.wlock():
3928 3944 if update:
3929 3945 cmdutil.checkunfinished(repo)
3930 3946 if exact or not opts.get('force'):
3931 3947 cmdutil.bailifchanged(repo)
3932 3948
3933 3949 if not opts.get('no_commit'):
3934 3950 lock = repo.lock
3935 3951 tr = lambda: repo.transaction(b'import')
3936 3952 else:
3937 3953 lock = util.nullcontextmanager
3938 3954 tr = util.nullcontextmanager
3939 3955 with lock(), tr():
3940 3956 parents = repo[None].parents()
3941 3957 for patchurl in patches:
3942 3958 if patchurl == b'-':
3943 3959 ui.status(_(b'applying patch from stdin\n'))
3944 3960 patchfile = ui.fin
3945 3961 patchurl = b'stdin' # for error message
3946 3962 else:
3947 3963 patchurl = os.path.join(base, patchurl)
3948 3964 ui.status(_(b'applying %s\n') % patchurl)
3949 3965 patchfile = hg.openpath(ui, patchurl, sendaccept=False)
3950 3966
3951 3967 haspatch = False
3952 3968 for hunk in patch.split(patchfile):
3953 3969 with patch.extract(ui, hunk) as patchdata:
3954 3970 msg, node, rej = cmdutil.tryimportone(
3955 3971 ui,
3956 3972 repo,
3957 3973 patchdata,
3958 3974 parents,
3959 3975 pycompat.byteskwargs(opts),
3960 3976 msgs,
3961 3977 hg.clean,
3962 3978 )
3963 3979 if msg:
3964 3980 haspatch = True
3965 3981 ui.note(msg + b'\n')
3966 3982 if update or exact:
3967 3983 parents = repo[None].parents()
3968 3984 else:
3969 3985 parents = [repo[node]]
3970 3986 if rej:
3971 3987 ui.write_err(_(b"patch applied partially\n"))
3972 3988 ui.write_err(
3973 3989 _(
3974 3990 b"(fix the .rej files and run "
3975 3991 b"`hg commit --amend`)\n"
3976 3992 )
3977 3993 )
3978 3994 ret = 1
3979 3995 break
3980 3996
3981 3997 if not haspatch:
3982 3998 raise error.InputError(_(b'%s: no diffs found') % patchurl)
3983 3999
3984 4000 if msgs:
3985 4001 repo.savecommitmessage(b'\n* * *\n'.join(msgs))
3986 4002 return ret
3987 4003
3988 4004
3989 4005 @command(
3990 4006 b'incoming|in',
3991 4007 [
3992 4008 (
3993 4009 b'f',
3994 4010 b'force',
3995 4011 None,
3996 4012 _(b'run even if remote repository is unrelated'),
3997 4013 ),
3998 4014 (b'n', b'newest-first', None, _(b'show newest record first')),
3999 4015 (b'', b'bundle', b'', _(b'file to store the bundles into'), _(b'FILE')),
4000 4016 (
4001 4017 b'r',
4002 4018 b'rev',
4003 4019 [],
4004 4020 _(b'a remote changeset intended to be added'),
4005 4021 _(b'REV'),
4006 4022 ),
4007 4023 (b'B', b'bookmarks', False, _(b"compare bookmarks")),
4008 4024 (
4009 4025 b'b',
4010 4026 b'branch',
4011 4027 [],
4012 4028 _(b'a specific branch you would like to pull'),
4013 4029 _(b'BRANCH'),
4014 4030 ),
4015 4031 ]
4016 4032 + logopts
4017 4033 + remoteopts
4018 4034 + subrepoopts,
4019 4035 _(b'[-p] [-n] [-M] [-f] [-r REV]... [--bundle FILENAME] [SOURCE]'),
4020 4036 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4021 4037 )
4022 4038 def incoming(ui, repo, source=b"default", **opts):
4023 4039 """show new changesets found in source
4024 4040
4025 4041 Show new changesets found in the specified path/URL or the default
4026 4042 pull location. These are the changesets that would have been pulled
4027 4043 by :hg:`pull` at the time you issued this command.
4028 4044
4029 4045 See pull for valid source format details.
4030 4046
4031 4047 .. container:: verbose
4032 4048
4033 4049 With -B/--bookmarks, the result of bookmark comparison between
4034 4050 local and remote repositories is displayed. With -v/--verbose,
4035 4051 status is also displayed for each bookmark like below::
4036 4052
4037 4053 BM1 01234567890a added
4038 4054 BM2 1234567890ab advanced
4039 4055 BM3 234567890abc diverged
4040 4056 BM4 34567890abcd changed
4041 4057
4042 4058 The action taken locally when pulling depends on the
4043 4059 status of each bookmark:
4044 4060
4045 4061 :``added``: pull will create it
4046 4062 :``advanced``: pull will update it
4047 4063 :``diverged``: pull will create a divergent bookmark
4048 4064 :``changed``: result depends on remote changesets
4049 4065
4050 4066 From the point of view of pulling behavior, bookmark
4051 4067 existing only in the remote repository are treated as ``added``,
4052 4068 even if it is in fact locally deleted.
4053 4069
4054 4070 .. container:: verbose
4055 4071
4056 4072 For remote repository, using --bundle avoids downloading the
4057 4073 changesets twice if the incoming is followed by a pull.
4058 4074
4059 4075 Examples:
4060 4076
4061 4077 - show incoming changes with patches and full description::
4062 4078
4063 4079 hg incoming -vp
4064 4080
4065 4081 - show incoming changes excluding merges, store a bundle::
4066 4082
4067 4083 hg in -vpM --bundle incoming.hg
4068 4084 hg pull incoming.hg
4069 4085
4070 4086 - briefly list changes inside a bundle::
4071 4087
4072 4088 hg in changes.hg -T "{desc|firstline}\\n"
4073 4089
4074 4090 Returns 0 if there are incoming changes, 1 otherwise.
4075 4091 """
4076 4092 opts = pycompat.byteskwargs(opts)
4077 4093 if opts.get(b'graph'):
4078 4094 logcmdutil.checkunsupportedgraphflags([], opts)
4079 4095
4080 4096 def display(other, chlist, displayer):
4081 4097 revdag = logcmdutil.graphrevs(other, chlist, opts)
4082 4098 logcmdutil.displaygraph(
4083 4099 ui, repo, revdag, displayer, graphmod.asciiedges
4084 4100 )
4085 4101
4086 4102 hg._incoming(display, lambda: 1, ui, repo, source, opts, buffered=True)
4087 4103 return 0
4088 4104
4089 4105 cmdutil.check_incompatible_arguments(opts, b'subrepos', [b'bundle'])
4090 4106
4091 4107 if opts.get(b'bookmarks'):
4092 4108 srcs = urlutil.get_pull_paths(repo, ui, [source])
4093 4109 for path in srcs:
4094 4110 # XXX the "branches" options are not used. Should it be used?
4095 4111 other = hg.peer(repo, opts, path)
4096 4112 try:
4097 4113 if b'bookmarks' not in other.listkeys(b'namespaces'):
4098 4114 ui.warn(_(b"remote doesn't support bookmarks\n"))
4099 4115 return 0
4100 4116 ui.pager(b'incoming')
4101 4117 ui.status(
4102 4118 _(b'comparing with %s\n') % urlutil.hidepassword(path.loc)
4103 4119 )
4104 4120 return bookmarks.incoming(
4105 4121 ui, repo, other, mode=path.bookmarks_mode
4106 4122 )
4107 4123 finally:
4108 4124 other.close()
4109 4125
4110 4126 return hg.incoming(ui, repo, source, opts)
4111 4127
4112 4128
4113 4129 @command(
4114 4130 b'init',
4115 4131 remoteopts,
4116 4132 _(b'[-e CMD] [--remotecmd CMD] [DEST]'),
4117 4133 helpcategory=command.CATEGORY_REPO_CREATION,
4118 4134 helpbasic=True,
4119 4135 norepo=True,
4120 4136 )
4121 4137 def init(ui, dest=b".", **opts):
4122 4138 """create a new repository in the given directory
4123 4139
4124 4140 Initialize a new repository in the given directory. If the given
4125 4141 directory does not exist, it will be created.
4126 4142
4127 4143 If no directory is given, the current directory is used.
4128 4144
4129 4145 It is possible to specify an ``ssh://`` URL as the destination.
4130 4146 See :hg:`help urls` for more information.
4131 4147
4132 4148 Returns 0 on success.
4133 4149 """
4134 4150 opts = pycompat.byteskwargs(opts)
4135 4151 path = urlutil.get_clone_path_obj(ui, dest)
4136 4152 peer = hg.peer(ui, opts, path, create=True)
4137 4153 peer.close()
4138 4154
4139 4155
4140 4156 @command(
4141 4157 b'locate',
4142 4158 [
4143 4159 (
4144 4160 b'r',
4145 4161 b'rev',
4146 4162 b'',
4147 4163 _(b'search the repository as it is in REV'),
4148 4164 _(b'REV'),
4149 4165 ),
4150 4166 (
4151 4167 b'0',
4152 4168 b'print0',
4153 4169 None,
4154 4170 _(b'end filenames with NUL, for use with xargs'),
4155 4171 ),
4156 4172 (
4157 4173 b'f',
4158 4174 b'fullpath',
4159 4175 None,
4160 4176 _(b'print complete paths from the filesystem root'),
4161 4177 ),
4162 4178 ]
4163 4179 + walkopts,
4164 4180 _(b'[OPTION]... [PATTERN]...'),
4165 4181 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
4166 4182 )
4167 4183 def locate(ui, repo, *pats, **opts):
4168 4184 """locate files matching specific patterns (DEPRECATED)
4169 4185
4170 4186 Print files under Mercurial control in the working directory whose
4171 4187 names match the given patterns.
4172 4188
4173 4189 By default, this command searches all directories in the working
4174 4190 directory. To search just the current directory and its
4175 4191 subdirectories, use "--include .".
4176 4192
4177 4193 If no patterns are given to match, this command prints the names
4178 4194 of all files under Mercurial control in the working directory.
4179 4195
4180 4196 If you want to feed the output of this command into the "xargs"
4181 4197 command, use the -0 option to both this command and "xargs". This
4182 4198 will avoid the problem of "xargs" treating single filenames that
4183 4199 contain whitespace as multiple filenames.
4184 4200
4185 4201 See :hg:`help files` for a more versatile command.
4186 4202
4187 4203 Returns 0 if a match is found, 1 otherwise.
4188 4204 """
4189 4205 if opts.get('print0'):
4190 4206 end = b'\0'
4191 4207 else:
4192 4208 end = b'\n'
4193 4209 ctx = logcmdutil.revsingle(repo, opts.get('rev'), None)
4194 4210
4195 4211 ret = 1
4196 4212 m = scmutil.match(
4197 4213 ctx,
4198 4214 pats,
4199 4215 pycompat.byteskwargs(opts),
4200 4216 default=b'relglob',
4201 4217 badfn=lambda x, y: False,
4202 4218 )
4203 4219
4204 4220 ui.pager(b'locate')
4205 4221 if ctx.rev() is None:
4206 4222 # When run on the working copy, "locate" includes removed files, so
4207 4223 # we get the list of files from the dirstate.
4208 4224 filesgen = sorted(repo.dirstate.matches(m))
4209 4225 else:
4210 4226 filesgen = ctx.matches(m)
4211 4227 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=bool(pats))
4212 4228 for abs in filesgen:
4213 4229 if opts.get('fullpath'):
4214 4230 ui.write(repo.wjoin(abs), end)
4215 4231 else:
4216 4232 ui.write(uipathfn(abs), end)
4217 4233 ret = 0
4218 4234
4219 4235 return ret
4220 4236
4221 4237
4222 4238 @command(
4223 4239 b'log|history',
4224 4240 [
4225 4241 (
4226 4242 b'f',
4227 4243 b'follow',
4228 4244 None,
4229 4245 _(
4230 4246 b'follow changeset history, or file history across copies and renames'
4231 4247 ),
4232 4248 ),
4233 4249 (
4234 4250 b'',
4235 4251 b'follow-first',
4236 4252 None,
4237 4253 _(b'only follow the first parent of merge changesets (DEPRECATED)'),
4238 4254 ),
4239 4255 (
4240 4256 b'd',
4241 4257 b'date',
4242 4258 b'',
4243 4259 _(b'show revisions matching date spec'),
4244 4260 _(b'DATE'),
4245 4261 ),
4246 4262 (b'C', b'copies', None, _(b'show copied files')),
4247 4263 (
4248 4264 b'k',
4249 4265 b'keyword',
4250 4266 [],
4251 4267 _(b'do case-insensitive search for a given text'),
4252 4268 _(b'TEXT'),
4253 4269 ),
4254 4270 (
4255 4271 b'r',
4256 4272 b'rev',
4257 4273 [],
4258 4274 _(b'revisions to select or follow from'),
4259 4275 _(b'REV'),
4260 4276 ),
4261 4277 (
4262 4278 b'L',
4263 4279 b'line-range',
4264 4280 [],
4265 4281 _(b'follow line range of specified file (EXPERIMENTAL)'),
4266 4282 _(b'FILE,RANGE'),
4267 4283 ),
4268 4284 (
4269 4285 b'',
4270 4286 b'removed',
4271 4287 None,
4272 4288 _(b'include revisions where files were removed'),
4273 4289 ),
4274 4290 (
4275 4291 b'm',
4276 4292 b'only-merges',
4277 4293 None,
4278 4294 _(b'show only merges (DEPRECATED) (use -r "merge()" instead)'),
4279 4295 ),
4280 4296 (b'u', b'user', [], _(b'revisions committed by user'), _(b'USER')),
4281 4297 (
4282 4298 b'',
4283 4299 b'only-branch',
4284 4300 [],
4285 4301 _(
4286 4302 b'show only changesets within the given named branch (DEPRECATED)'
4287 4303 ),
4288 4304 _(b'BRANCH'),
4289 4305 ),
4290 4306 (
4291 4307 b'b',
4292 4308 b'branch',
4293 4309 [],
4294 4310 _(b'show changesets within the given named branch'),
4295 4311 _(b'BRANCH'),
4296 4312 ),
4297 4313 (
4298 4314 b'B',
4299 4315 b'bookmark',
4300 4316 [],
4301 4317 _(b"show changesets within the given bookmark"),
4302 4318 _(b'BOOKMARK'),
4303 4319 ),
4304 4320 (
4305 4321 b'P',
4306 4322 b'prune',
4307 4323 [],
4308 4324 _(b'do not display revision or any of its ancestors'),
4309 4325 _(b'REV'),
4310 4326 ),
4311 4327 ]
4312 4328 + logopts
4313 4329 + walkopts,
4314 4330 _(b'[OPTION]... [FILE]'),
4315 4331 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4316 4332 helpbasic=True,
4317 4333 inferrepo=True,
4318 4334 intents={INTENT_READONLY},
4319 4335 )
4320 4336 def log(ui, repo, *pats, **opts):
4321 4337 """show revision history of entire repository or files
4322 4338
4323 4339 Print the revision history of the specified files or the entire
4324 4340 project.
4325 4341
4326 4342 If no revision range is specified, the default is ``tip:0`` unless
4327 4343 --follow is set.
4328 4344
4329 4345 File history is shown without following rename or copy history of
4330 4346 files. Use -f/--follow with a filename to follow history across
4331 4347 renames and copies. --follow without a filename will only show
4332 4348 ancestors of the starting revisions. The starting revisions can be
4333 4349 specified by -r/--rev, which default to the working directory parent.
4334 4350
4335 4351 By default this command prints revision number and changeset id,
4336 4352 tags, non-trivial parents, user, date and time, and a summary for
4337 4353 each commit. When the -v/--verbose switch is used, the list of
4338 4354 changed files and full commit message are shown.
4339 4355
4340 4356 With --graph the revisions are shown as an ASCII art DAG with the most
4341 4357 recent changeset at the top.
4342 4358 'o' is a changeset, '@' is a working directory parent, '%' is a changeset
4343 4359 involved in an unresolved merge conflict, '_' closes a branch,
4344 4360 'x' is obsolete, '*' is unstable, and '+' represents a fork where the
4345 4361 changeset from the lines below is a parent of the 'o' merge on the same
4346 4362 line.
4347 4363 Paths in the DAG are represented with '|', '/' and so forth. ':' in place
4348 4364 of a '|' indicates one or more revisions in a path are omitted.
4349 4365
4350 4366 .. container:: verbose
4351 4367
4352 4368 Use -L/--line-range FILE,M:N options to follow the history of lines
4353 4369 from M to N in FILE. With -p/--patch only diff hunks affecting
4354 4370 specified line range will be shown. This option requires --follow;
4355 4371 it can be specified multiple times. Currently, this option is not
4356 4372 compatible with --graph. This option is experimental.
4357 4373
4358 4374 .. note::
4359 4375
4360 4376 :hg:`log --patch` may generate unexpected diff output for merge
4361 4377 changesets, as it will only compare the merge changeset against
4362 4378 its first parent. Also, only files different from BOTH parents
4363 4379 will appear in files:.
4364 4380
4365 4381 .. note::
4366 4382
4367 4383 For performance reasons, :hg:`log FILE` may omit duplicate changes
4368 4384 made on branches and will not show removals or mode changes. To
4369 4385 see all such changes, use the --removed switch.
4370 4386
4371 4387 .. container:: verbose
4372 4388
4373 4389 .. note::
4374 4390
4375 4391 The history resulting from -L/--line-range options depends on diff
4376 4392 options; for instance if white-spaces are ignored, respective changes
4377 4393 with only white-spaces in specified line range will not be listed.
4378 4394
4379 4395 .. container:: verbose
4380 4396
4381 4397 Some examples:
4382 4398
4383 4399 - changesets with full descriptions and file lists::
4384 4400
4385 4401 hg log -v
4386 4402
4387 4403 - changesets ancestral to the working directory::
4388 4404
4389 4405 hg log -f
4390 4406
4391 4407 - last 10 commits on the current branch::
4392 4408
4393 4409 hg log -l 10 -b .
4394 4410
4395 4411 - changesets showing all modifications of a file, including removals::
4396 4412
4397 4413 hg log --removed file.c
4398 4414
4399 4415 - all changesets that touch a directory, with diffs, excluding merges::
4400 4416
4401 4417 hg log -Mp lib/
4402 4418
4403 4419 - all revision numbers that match a keyword::
4404 4420
4405 4421 hg log -k bug --template "{rev}\\n"
4406 4422
4407 4423 - the full hash identifier of the working directory parent::
4408 4424
4409 4425 hg log -r . --template "{node}\\n"
4410 4426
4411 4427 - list available log templates::
4412 4428
4413 4429 hg log -T list
4414 4430
4415 4431 - check if a given changeset is included in a tagged release::
4416 4432
4417 4433 hg log -r "a21ccf and ancestor(1.9)"
4418 4434
4419 4435 - find all changesets by some user in a date range::
4420 4436
4421 4437 hg log -k alice -d "may 2008 to jul 2008"
4422 4438
4423 4439 - summary of all changesets after the last tag::
4424 4440
4425 4441 hg log -r "last(tagged())::" --template "{desc|firstline}\\n"
4426 4442
4427 4443 - changesets touching lines 13 to 23 for file.c::
4428 4444
4429 4445 hg log -L file.c,13:23
4430 4446
4431 4447 - changesets touching lines 13 to 23 for file.c and lines 2 to 6 of
4432 4448 main.c with patch::
4433 4449
4434 4450 hg log -L file.c,13:23 -L main.c,2:6 -p
4435 4451
4436 4452 See :hg:`help dates` for a list of formats valid for -d/--date.
4437 4453
4438 4454 See :hg:`help revisions` for more about specifying and ordering
4439 4455 revisions.
4440 4456
4441 4457 See :hg:`help templates` for more about pre-packaged styles and
4442 4458 specifying custom templates. The default template used by the log
4443 4459 command can be customized via the ``command-templates.log`` configuration
4444 4460 setting.
4445 4461
4446 4462 Returns 0 on success.
4447 4463
4448 4464 """
4449 4465 opts = pycompat.byteskwargs(opts)
4450 4466 linerange = opts.get(b'line_range')
4451 4467
4452 4468 if linerange and not opts.get(b'follow'):
4453 4469 raise error.InputError(_(b'--line-range requires --follow'))
4454 4470
4455 4471 if linerange and pats:
4456 4472 # TODO: take pats as patterns with no line-range filter
4457 4473 raise error.InputError(
4458 4474 _(b'FILE arguments are not compatible with --line-range option')
4459 4475 )
4460 4476
4461 4477 repo = scmutil.unhidehashlikerevs(repo, opts.get(b'rev'), b'nowarn')
4462 4478 walk_opts = logcmdutil.parseopts(ui, pats, opts)
4463 4479 revs, differ = logcmdutil.getrevs(repo, walk_opts)
4464 4480 if linerange:
4465 4481 # TODO: should follow file history from logcmdutil._initialrevs(),
4466 4482 # then filter the result by logcmdutil._makerevset() and --limit
4467 4483 revs, differ = logcmdutil.getlinerangerevs(repo, revs, opts)
4468 4484
4469 4485 getcopies = None
4470 4486 if opts.get(b'copies'):
4471 4487 endrev = None
4472 4488 if revs:
4473 4489 endrev = revs.max() + 1
4474 4490 getcopies = scmutil.getcopiesfn(repo, endrev=endrev)
4475 4491
4476 4492 ui.pager(b'log')
4477 4493 displayer = logcmdutil.changesetdisplayer(
4478 4494 ui, repo, opts, differ, buffered=True
4479 4495 )
4480 4496 if opts.get(b'graph'):
4481 4497 displayfn = logcmdutil.displaygraphrevs
4482 4498 else:
4483 4499 displayfn = logcmdutil.displayrevs
4484 4500 displayfn(ui, repo, revs, displayer, getcopies)
4485 4501
4486 4502
4487 4503 @command(
4488 4504 b'manifest',
4489 4505 [
4490 4506 (b'r', b'rev', b'', _(b'revision to display'), _(b'REV')),
4491 4507 (b'', b'all', False, _(b"list files from all revisions")),
4492 4508 ]
4493 4509 + formatteropts,
4494 4510 _(b'[-r REV]'),
4495 4511 helpcategory=command.CATEGORY_MAINTENANCE,
4496 4512 intents={INTENT_READONLY},
4497 4513 )
4498 4514 def manifest(ui, repo, node=None, rev=None, **opts):
4499 4515 """output the current or given revision of the project manifest
4500 4516
4501 4517 Print a list of version controlled files for the given revision.
4502 4518 If no revision is given, the first parent of the working directory
4503 4519 is used, or the null revision if no revision is checked out.
4504 4520
4505 4521 With -v, print file permissions, symlink and executable bits.
4506 4522 With --debug, print file revision hashes.
4507 4523
4508 4524 If option --all is specified, the list of all files from all revisions
4509 4525 is printed. This includes deleted and renamed files.
4510 4526
4511 4527 Returns 0 on success.
4512 4528 """
4513 4529 fm = ui.formatter(b'manifest', pycompat.byteskwargs(opts))
4514 4530
4515 4531 if opts.get('all'):
4516 4532 if rev or node:
4517 4533 raise error.InputError(_(b"can't specify a revision with --all"))
4518 4534
4519 4535 res = set()
4520 4536 for rev in repo:
4521 4537 ctx = repo[rev]
4522 4538 res |= set(ctx.files())
4523 4539
4524 4540 ui.pager(b'manifest')
4525 4541 for f in sorted(res):
4526 4542 fm.startitem()
4527 4543 fm.write(b"path", b'%s\n', f)
4528 4544 fm.end()
4529 4545 return
4530 4546
4531 4547 if rev and node:
4532 4548 raise error.InputError(_(b"please specify just one revision"))
4533 4549
4534 4550 if not node:
4535 4551 node = rev
4536 4552
4537 4553 char = {b'l': b'@', b'x': b'*', b'': b'', b't': b'd'}
4538 4554 mode = {b'l': b'644', b'x': b'755', b'': b'644', b't': b'755'}
4539 4555 if node:
4540 4556 repo = scmutil.unhidehashlikerevs(repo, [node], b'nowarn')
4541 4557 ctx = logcmdutil.revsingle(repo, node)
4542 4558 mf = ctx.manifest()
4543 4559 ui.pager(b'manifest')
4544 4560 for f in ctx:
4545 4561 fm.startitem()
4546 4562 fm.context(ctx=ctx)
4547 4563 fl = ctx[f].flags()
4548 4564 fm.condwrite(ui.debugflag, b'hash', b'%s ', hex(mf[f]))
4549 4565 fm.condwrite(ui.verbose, b'mode type', b'%s %1s ', mode[fl], char[fl])
4550 4566 fm.write(b'path', b'%s\n', f)
4551 4567 fm.end()
4552 4568
4553 4569
4554 4570 @command(
4555 4571 b'merge',
4556 4572 [
4557 4573 (
4558 4574 b'f',
4559 4575 b'force',
4560 4576 None,
4561 4577 _(b'force a merge including outstanding changes (DEPRECATED)'),
4562 4578 ),
4563 4579 (b'r', b'rev', b'', _(b'revision to merge'), _(b'REV')),
4564 4580 (
4565 4581 b'P',
4566 4582 b'preview',
4567 4583 None,
4568 4584 _(b'review revisions to merge (no merge is performed)'),
4569 4585 ),
4570 4586 (b'', b'abort', None, _(b'abort the ongoing merge')),
4571 4587 ]
4572 4588 + mergetoolopts,
4573 4589 _(b'[-P] [[-r] REV]'),
4574 4590 helpcategory=command.CATEGORY_CHANGE_MANAGEMENT,
4575 4591 helpbasic=True,
4576 4592 )
4577 4593 def merge(ui, repo, node=None, **opts):
4578 4594 """merge another revision into working directory
4579 4595
4580 4596 The current working directory is updated with all changes made in
4581 4597 the requested revision since the last common predecessor revision.
4582 4598
4583 4599 Files that changed between either parent are marked as changed for
4584 4600 the next commit and a commit must be performed before any further
4585 4601 updates to the repository are allowed. The next commit will have
4586 4602 two parents.
4587 4603
4588 4604 ``--tool`` can be used to specify the merge tool used for file
4589 4605 merges. It overrides the HGMERGE environment variable and your
4590 4606 configuration files. See :hg:`help merge-tools` for options.
4591 4607
4592 4608 If no revision is specified, the working directory's parent is a
4593 4609 head revision, and the current branch contains exactly one other
4594 4610 head, the other head is merged with by default. Otherwise, an
4595 4611 explicit revision with which to merge must be provided.
4596 4612
4597 4613 See :hg:`help resolve` for information on handling file conflicts.
4598 4614
4599 4615 To undo an uncommitted merge, use :hg:`merge --abort` which
4600 4616 will check out a clean copy of the original merge parent, losing
4601 4617 all changes.
4602 4618
4603 4619 Returns 0 on success, 1 if there are unresolved files.
4604 4620 """
4605 4621
4606 4622 abort = opts.get('abort')
4607 4623 if abort and repo.dirstate.p2() == repo.nullid:
4608 4624 cmdutil.wrongtooltocontinue(repo, _(b'merge'))
4609 4625 cmdutil.check_incompatible_arguments(opts, 'abort', ['rev', 'preview'])
4610 4626 if abort:
4611 4627 state = cmdutil.getunfinishedstate(repo)
4612 4628 if state and state._opname != b'merge':
4613 4629 raise error.StateError(
4614 4630 _(b'cannot abort merge with %s in progress') % (state._opname),
4615 4631 hint=state.hint(),
4616 4632 )
4617 4633 if node:
4618 4634 raise error.InputError(_(b"cannot specify a node with --abort"))
4619 4635 return hg.abortmerge(repo.ui, repo)
4620 4636
4621 4637 if opts.get('rev') and node:
4622 4638 raise error.InputError(_(b"please specify just one revision"))
4623 4639 if not node:
4624 4640 node = opts.get('rev')
4625 4641
4626 4642 if node:
4627 4643 ctx = logcmdutil.revsingle(repo, node)
4628 4644 else:
4629 4645 if ui.configbool(b'commands', b'merge.require-rev'):
4630 4646 raise error.InputError(
4631 4647 _(
4632 4648 b'configuration requires specifying revision to merge '
4633 4649 b'with'
4634 4650 )
4635 4651 )
4636 4652 ctx = repo[destutil.destmerge(repo)]
4637 4653
4638 4654 if ctx.node() is None:
4639 4655 raise error.InputError(
4640 4656 _(b'merging with the working copy has no effect')
4641 4657 )
4642 4658
4643 4659 if opts.get('preview'):
4644 4660 # find nodes that are ancestors of p2 but not of p1
4645 4661 p1 = repo[b'.'].node()
4646 4662 p2 = ctx.node()
4647 4663 nodes = repo.changelog.findmissing(common=[p1], heads=[p2])
4648 4664
4649 4665 displayer = logcmdutil.changesetdisplayer(
4650 4666 ui, repo, pycompat.byteskwargs(opts)
4651 4667 )
4652 4668 for node in nodes:
4653 4669 displayer.show(repo[node])
4654 4670 displayer.close()
4655 4671 return 0
4656 4672
4657 4673 # ui.forcemerge is an internal variable, do not document
4658 4674 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
4659 4675 with ui.configoverride(overrides, b'merge'):
4660 4676 force = opts.get('force')
4661 4677 labels = [b'working copy', b'merge rev', b'common ancestor']
4662 4678 return hg.merge(ctx, force=force, labels=labels)
4663 4679
4664 4680
4665 4681 statemod.addunfinished(
4666 4682 b'merge',
4667 4683 fname=None,
4668 4684 clearable=True,
4669 4685 allowcommit=True,
4670 4686 cmdmsg=_(b'outstanding uncommitted merge'),
4671 4687 abortfunc=hg.abortmerge,
4672 4688 statushint=_(
4673 4689 b'To continue: hg commit\nTo abort: hg merge --abort'
4674 4690 ),
4675 4691 cmdhint=_(b"use 'hg commit' or 'hg merge --abort'"),
4676 4692 )
4677 4693
4678 4694
4679 4695 @command(
4680 4696 b'outgoing|out',
4681 4697 [
4682 4698 (
4683 4699 b'f',
4684 4700 b'force',
4685 4701 None,
4686 4702 _(b'run even when the destination is unrelated'),
4687 4703 ),
4688 4704 (
4689 4705 b'r',
4690 4706 b'rev',
4691 4707 [],
4692 4708 _(b'a changeset intended to be included in the destination'),
4693 4709 _(b'REV'),
4694 4710 ),
4695 4711 (b'n', b'newest-first', None, _(b'show newest record first')),
4696 4712 (b'B', b'bookmarks', False, _(b'compare bookmarks')),
4697 4713 (
4698 4714 b'b',
4699 4715 b'branch',
4700 4716 [],
4701 4717 _(b'a specific branch you would like to push'),
4702 4718 _(b'BRANCH'),
4703 4719 ),
4704 4720 ]
4705 4721 + logopts
4706 4722 + remoteopts
4707 4723 + subrepoopts,
4708 4724 _(b'[-M] [-p] [-n] [-f] [-r REV]... [DEST]...'),
4709 4725 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4710 4726 )
4711 4727 def outgoing(ui, repo, *dests, **opts):
4712 4728 """show changesets not found in the destination
4713 4729
4714 4730 Show changesets not found in the specified destination repository
4715 4731 or the default push location. These are the changesets that would
4716 4732 be pushed if a push was requested.
4717 4733
4718 4734 See pull for details of valid destination formats.
4719 4735
4720 4736 .. container:: verbose
4721 4737
4722 4738 With -B/--bookmarks, the result of bookmark comparison between
4723 4739 local and remote repositories is displayed. With -v/--verbose,
4724 4740 status is also displayed for each bookmark like below::
4725 4741
4726 4742 BM1 01234567890a added
4727 4743 BM2 deleted
4728 4744 BM3 234567890abc advanced
4729 4745 BM4 34567890abcd diverged
4730 4746 BM5 4567890abcde changed
4731 4747
4732 4748 The action taken when pushing depends on the
4733 4749 status of each bookmark:
4734 4750
4735 4751 :``added``: push with ``-B`` will create it
4736 4752 :``deleted``: push with ``-B`` will delete it
4737 4753 :``advanced``: push will update it
4738 4754 :``diverged``: push with ``-B`` will update it
4739 4755 :``changed``: push with ``-B`` will update it
4740 4756
4741 4757 From the point of view of pushing behavior, bookmarks
4742 4758 existing only in the remote repository are treated as
4743 4759 ``deleted``, even if it is in fact added remotely.
4744 4760
4745 4761 Returns 0 if there are outgoing changes, 1 otherwise.
4746 4762 """
4747 4763 opts = pycompat.byteskwargs(opts)
4748 4764 if opts.get(b'bookmarks'):
4749 4765 for path in urlutil.get_push_paths(repo, ui, dests):
4750 4766 other = hg.peer(repo, opts, path)
4751 4767 try:
4752 4768 if b'bookmarks' not in other.listkeys(b'namespaces'):
4753 4769 ui.warn(_(b"remote doesn't support bookmarks\n"))
4754 4770 return 0
4755 4771 ui.status(
4756 4772 _(b'comparing with %s\n') % urlutil.hidepassword(path.loc)
4757 4773 )
4758 4774 ui.pager(b'outgoing')
4759 4775 return bookmarks.outgoing(ui, repo, other)
4760 4776 finally:
4761 4777 other.close()
4762 4778
4763 4779 return hg.outgoing(ui, repo, dests, opts)
4764 4780
4765 4781
4766 4782 @command(
4767 4783 b'parents',
4768 4784 [
4769 4785 (
4770 4786 b'r',
4771 4787 b'rev',
4772 4788 b'',
4773 4789 _(b'show parents of the specified revision'),
4774 4790 _(b'REV'),
4775 4791 ),
4776 4792 ]
4777 4793 + templateopts,
4778 4794 _(b'[-r REV] [FILE]'),
4779 4795 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
4780 4796 inferrepo=True,
4781 4797 )
4782 4798 def parents(ui, repo, file_=None, **opts):
4783 4799 """show the parents of the working directory or revision (DEPRECATED)
4784 4800
4785 4801 Print the working directory's parent revisions. If a revision is
4786 4802 given via -r/--rev, the parent of that revision will be printed.
4787 4803 If a file argument is given, the revision in which the file was
4788 4804 last changed (before the working directory revision or the
4789 4805 argument to --rev if given) is printed.
4790 4806
4791 4807 This command is equivalent to::
4792 4808
4793 4809 hg log -r "p1()+p2()" or
4794 4810 hg log -r "p1(REV)+p2(REV)" or
4795 4811 hg log -r "max(::p1() and file(FILE))+max(::p2() and file(FILE))" or
4796 4812 hg log -r "max(::p1(REV) and file(FILE))+max(::p2(REV) and file(FILE))"
4797 4813
4798 4814 See :hg:`summary` and :hg:`help revsets` for related information.
4799 4815
4800 4816 Returns 0 on success.
4801 4817 """
4802 4818
4803 4819 opts = pycompat.byteskwargs(opts)
4804 4820 rev = opts.get(b'rev')
4805 4821 if rev:
4806 4822 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
4807 4823 ctx = logcmdutil.revsingle(repo, rev, None)
4808 4824
4809 4825 if file_:
4810 4826 m = scmutil.match(ctx, (file_,), opts)
4811 4827 if m.anypats() or len(m.files()) != 1:
4812 4828 raise error.InputError(_(b'can only specify an explicit filename'))
4813 4829 file_ = m.files()[0]
4814 4830 filenodes = []
4815 4831 for cp in ctx.parents():
4816 4832 if not cp:
4817 4833 continue
4818 4834 try:
4819 4835 filenodes.append(cp.filenode(file_))
4820 4836 except error.LookupError:
4821 4837 pass
4822 4838 if not filenodes:
4823 4839 raise error.InputError(_(b"'%s' not found in manifest") % file_)
4824 4840 p = []
4825 4841 for fn in filenodes:
4826 4842 fctx = repo.filectx(file_, fileid=fn)
4827 4843 p.append(fctx.node())
4828 4844 else:
4829 4845 p = [cp.node() for cp in ctx.parents()]
4830 4846
4831 4847 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
4832 4848 for n in p:
4833 4849 if n != repo.nullid:
4834 4850 displayer.show(repo[n])
4835 4851 displayer.close()
4836 4852
4837 4853
4838 4854 @command(
4839 4855 b'paths',
4840 4856 formatteropts,
4841 4857 _(b'[NAME]'),
4842 4858 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
4843 4859 optionalrepo=True,
4844 4860 intents={INTENT_READONLY},
4845 4861 )
4846 4862 def paths(ui, repo, search=None, **opts):
4847 4863 """show aliases for remote repositories
4848 4864
4849 4865 Show definition of symbolic path name NAME. If no name is given,
4850 4866 show definition of all available names.
4851 4867
4852 4868 Option -q/--quiet suppresses all output when searching for NAME
4853 4869 and shows only the path names when listing all definitions.
4854 4870
4855 4871 Path names are defined in the [paths] section of your
4856 4872 configuration file and in ``/etc/mercurial/hgrc``. If run inside a
4857 4873 repository, ``.hg/hgrc`` is used, too.
4858 4874
4859 4875 The path names ``default`` and ``default-push`` have a special
4860 4876 meaning. When performing a push or pull operation, they are used
4861 4877 as fallbacks if no location is specified on the command-line.
4862 4878 When ``default-push`` is set, it will be used for push and
4863 4879 ``default`` will be used for pull; otherwise ``default`` is used
4864 4880 as the fallback for both. When cloning a repository, the clone
4865 4881 source is written as ``default`` in ``.hg/hgrc``.
4866 4882
4867 4883 .. note::
4868 4884
4869 4885 ``default`` and ``default-push`` apply to all inbound (e.g.
4870 4886 :hg:`incoming`) and outbound (e.g. :hg:`outgoing`, :hg:`email`
4871 4887 and :hg:`bundle`) operations.
4872 4888
4873 4889 See :hg:`help urls` for more information.
4874 4890
4875 4891 .. container:: verbose
4876 4892
4877 4893 Template:
4878 4894
4879 4895 The following keywords are supported. See also :hg:`help templates`.
4880 4896
4881 4897 :name: String. Symbolic name of the path alias.
4882 4898 :pushurl: String. URL for push operations.
4883 4899 :url: String. URL or directory path for the other operations.
4884 4900
4885 4901 Returns 0 on success.
4886 4902 """
4887 4903
4888 4904 pathitems = urlutil.list_paths(ui, search)
4889 4905 ui.pager(b'paths')
4890 4906
4891 4907 fm = ui.formatter(b'paths', pycompat.byteskwargs(opts))
4892 4908 if fm.isplain():
4893 4909 hidepassword = urlutil.hidepassword
4894 4910 else:
4895 4911 hidepassword = bytes
4896 4912 if ui.quiet:
4897 4913 namefmt = b'%s\n'
4898 4914 else:
4899 4915 namefmt = b'%s = '
4900 4916 showsubopts = not search and not ui.quiet
4901 4917
4902 4918 for name, path in pathitems:
4903 4919 fm.startitem()
4904 4920 fm.condwrite(not search, b'name', namefmt, name)
4905 4921 fm.condwrite(not ui.quiet, b'url', b'%s\n', hidepassword(path.rawloc))
4906 4922 for subopt, value in sorted(path.suboptions.items()):
4907 4923 assert subopt not in (b'name', b'url')
4908 4924 if showsubopts:
4909 4925 fm.plain(b'%s:%s = ' % (name, subopt))
4910 4926 display = urlutil.path_suboptions_display[subopt]
4911 4927 value = display(value)
4912 4928 fm.condwrite(showsubopts, subopt, b'%s\n', value)
4913 4929
4914 4930 fm.end()
4915 4931
4916 4932 if search and not pathitems:
4917 4933 if not ui.quiet:
4918 4934 ui.warn(_(b"not found!\n"))
4919 4935 return 1
4920 4936 else:
4921 4937 return 0
4922 4938
4923 4939
4924 4940 @command(
4925 4941 b'phase',
4926 4942 [
4927 4943 (b'p', b'public', False, _(b'set changeset phase to public')),
4928 4944 (b'd', b'draft', False, _(b'set changeset phase to draft')),
4929 4945 (b's', b'secret', False, _(b'set changeset phase to secret')),
4930 4946 (b'f', b'force', False, _(b'allow to move boundary backward')),
4931 4947 (b'r', b'rev', [], _(b'target revision'), _(b'REV')),
4932 4948 ],
4933 4949 _(b'[-p|-d|-s] [-f] [-r] [REV...]'),
4934 4950 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
4935 4951 )
4936 4952 def phase(ui, repo, *revs, **opts):
4937 4953 """set or show the current phase name
4938 4954
4939 4955 With no argument, show the phase name of the current revision(s).
4940 4956
4941 4957 With one of -p/--public, -d/--draft or -s/--secret, change the
4942 4958 phase value of the specified revisions.
4943 4959
4944 4960 Unless -f/--force is specified, :hg:`phase` won't move changesets from a
4945 4961 lower phase to a higher phase. Phases are ordered as follows::
4946 4962
4947 4963 public < draft < secret
4948 4964
4949 4965 Returns 0 on success, 1 if some phases could not be changed.
4950 4966
4951 4967 (For more information about the phases concept, see :hg:`help phases`.)
4952 4968 """
4953 4969 opts = pycompat.byteskwargs(opts)
4954 4970 # search for a unique phase argument
4955 4971 targetphase = None
4956 4972 for idx, name in enumerate(phases.cmdphasenames):
4957 4973 if opts[name]:
4958 4974 if targetphase is not None:
4959 4975 raise error.InputError(_(b'only one phase can be specified'))
4960 4976 targetphase = idx
4961 4977
4962 4978 # look for specified revision
4963 4979 revs = list(revs)
4964 4980 revs.extend(opts[b'rev'])
4965 4981 if revs:
4966 4982 revs = logcmdutil.revrange(repo, revs)
4967 4983 else:
4968 4984 # display both parents as the second parent phase can influence
4969 4985 # the phase of a merge commit
4970 4986 revs = [c.rev() for c in repo[None].parents()]
4971 4987
4972 4988 ret = 0
4973 4989 if targetphase is None:
4974 4990 # display
4975 4991 for r in revs:
4976 4992 ctx = repo[r]
4977 4993 ui.write(b'%i: %s\n' % (ctx.rev(), ctx.phasestr()))
4978 4994 else:
4979 4995 with repo.lock(), repo.transaction(b"phase") as tr:
4980 4996 # set phase
4981 4997 if not revs:
4982 4998 raise error.InputError(_(b'empty revision set'))
4983 4999 nodes = [repo[r].node() for r in revs]
4984 5000 # moving revision from public to draft may hide them
4985 5001 # We have to check result on an unfiltered repository
4986 5002 unfi = repo.unfiltered()
4987 5003 getphase = unfi._phasecache.phase
4988 5004 olddata = [getphase(unfi, r) for r in unfi]
4989 5005 phases.advanceboundary(repo, tr, targetphase, nodes)
4990 5006 if opts[b'force']:
4991 5007 phases.retractboundary(repo, tr, targetphase, nodes)
4992 5008 getphase = unfi._phasecache.phase
4993 5009 newdata = [getphase(unfi, r) for r in unfi]
4994 5010 changes = sum(newdata[r] != olddata[r] for r in unfi)
4995 5011 cl = unfi.changelog
4996 5012 rejected = [n for n in nodes if newdata[cl.rev(n)] < targetphase]
4997 5013 if rejected:
4998 5014 ui.warn(
4999 5015 _(
5000 5016 b'cannot move %i changesets to a higher '
5001 5017 b'phase, use --force\n'
5002 5018 )
5003 5019 % len(rejected)
5004 5020 )
5005 5021 ret = 1
5006 5022 if changes:
5007 5023 msg = _(b'phase changed for %i changesets\n') % changes
5008 5024 if ret:
5009 5025 ui.status(msg)
5010 5026 else:
5011 5027 ui.note(msg)
5012 5028 else:
5013 5029 ui.warn(_(b'no phases changed\n'))
5014 5030 return ret
5015 5031
5016 5032
5017 5033 @command(
5018 5034 b'pull',
5019 5035 [
5020 5036 (
5021 5037 b'u',
5022 5038 b'update',
5023 5039 None,
5024 5040 _(b'update to new branch head if new descendants were pulled'),
5025 5041 ),
5026 5042 (
5027 5043 b'f',
5028 5044 b'force',
5029 5045 None,
5030 5046 _(b'run even when remote repository is unrelated'),
5031 5047 ),
5032 5048 (
5033 5049 b'',
5034 5050 b'confirm',
5035 5051 None,
5036 5052 _(b'confirm pull before applying changes'),
5037 5053 ),
5038 5054 (
5039 5055 b'r',
5040 5056 b'rev',
5041 5057 [],
5042 5058 _(b'a remote changeset intended to be added'),
5043 5059 _(b'REV'),
5044 5060 ),
5045 5061 (b'B', b'bookmark', [], _(b"bookmark to pull"), _(b'BOOKMARK')),
5046 5062 (
5047 5063 b'b',
5048 5064 b'branch',
5049 5065 [],
5050 5066 _(b'a specific branch you would like to pull'),
5051 5067 _(b'BRANCH'),
5052 5068 ),
5053 5069 (
5054 5070 b'',
5055 5071 b'remote-hidden',
5056 5072 False,
5057 5073 _(b"include changesets hidden on the remote (EXPERIMENTAL)"),
5058 5074 ),
5059 5075 ]
5060 5076 + remoteopts,
5061 5077 _(b'[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]...'),
5062 5078 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5063 5079 helpbasic=True,
5064 5080 )
5065 5081 def pull(ui, repo, *sources, **opts):
5066 5082 """pull changes from the specified source
5067 5083
5068 5084 Pull changes from a remote repository to a local one.
5069 5085
5070 5086 This finds all changes from the repository at the specified path
5071 5087 or URL and adds them to a local repository (the current one unless
5072 5088 -R is specified). By default, this does not update the copy of the
5073 5089 project in the working directory.
5074 5090
5075 5091 When cloning from servers that support it, Mercurial may fetch
5076 5092 pre-generated data. When this is done, hooks operating on incoming
5077 5093 changesets and changegroups may fire more than once, once for each
5078 5094 pre-generated bundle and as well as for any additional remaining
5079 5095 data. See :hg:`help -e clonebundles` for more.
5080 5096
5081 5097 Use :hg:`incoming` if you want to see what would have been added
5082 5098 by a pull at the time you issued this command. If you then decide
5083 5099 to add those changes to the repository, you should use :hg:`pull
5084 5100 -r X` where ``X`` is the last changeset listed by :hg:`incoming`.
5085 5101
5086 5102 If SOURCE is omitted, the 'default' path will be used.
5087 5103 See :hg:`help urls` for more information.
5088 5104
5089 5105 If multiple sources are specified, they will be pulled sequentially as if
5090 5106 the command was run multiple time. If --update is specify and the command
5091 5107 will stop at the first failed --update.
5092 5108
5093 5109 Specifying bookmark as ``.`` is equivalent to specifying the active
5094 5110 bookmark's name.
5095 5111
5096 5112 .. container:: verbose
5097 5113
5098 5114 One can use the `--remote-hidden` flag to pull changesets
5099 5115 hidden on the remote. This flag is "best effort", and will only
5100 5116 work if the server supports the feature and is configured to
5101 5117 allow the user to access hidden changesets. This option is
5102 5118 experimental and backwards compatibility is not garanteed.
5103 5119
5104 5120 Returns 0 on success, 1 if an update had unresolved files.
5105 5121 """
5106 5122
5107 5123 if ui.configbool(b'commands', b'update.requiredest') and opts.get('update'):
5108 5124 msg = _(b'update destination required by configuration')
5109 5125 hint = _(b'use hg pull followed by hg update DEST')
5110 5126 raise error.InputError(msg, hint=hint)
5111 5127
5112 5128 update_conflict = None
5113 5129
5114 5130 for path in urlutil.get_pull_paths(repo, ui, sources):
5115 5131 ui.status(_(b'pulling from %s\n') % urlutil.hidepassword(path.loc))
5116 5132 ui.flush()
5117 5133 other = hg.peer(
5118 5134 repo,
5119 5135 pycompat.byteskwargs(opts),
5120 5136 path,
5121 5137 remotehidden=opts['remote_hidden'],
5122 5138 )
5123 5139 update_conflict = None
5124 5140 try:
5125 5141 branches = (path.branch, opts.get('branch', []))
5126 5142 revs, checkout = hg.addbranchrevs(
5127 5143 repo,
5128 5144 other,
5129 5145 branches,
5130 5146 opts.get('rev'),
5131 5147 remotehidden=opts['remote_hidden'],
5132 5148 )
5133 5149
5134 5150 pullopargs = {}
5135 5151
5136 5152 nodes = None
5137 5153 if opts.get('bookmark') or revs:
5138 5154 # The list of bookmark used here is the same used to actually update
5139 5155 # the bookmark names, to avoid the race from issue 4689 and we do
5140 5156 # all lookup and bookmark queries in one go so they see the same
5141 5157 # version of the server state (issue 4700).
5142 5158 nodes = []
5143 5159 fnodes = []
5144 5160 revs = revs or []
5145 5161 if revs and not other.capable(b'lookup'):
5146 5162 err = _(
5147 5163 b"other repository doesn't support revision lookup, "
5148 5164 b"so a rev cannot be specified."
5149 5165 )
5150 5166 raise error.Abort(err)
5151 5167 with other.commandexecutor() as e:
5152 5168 fremotebookmarks = e.callcommand(
5153 5169 b'listkeys', {b'namespace': b'bookmarks'}
5154 5170 )
5155 5171 for r in revs:
5156 5172 fnodes.append(e.callcommand(b'lookup', {b'key': r}))
5157 5173 remotebookmarks = fremotebookmarks.result()
5158 5174 remotebookmarks = bookmarks.unhexlifybookmarks(remotebookmarks)
5159 5175 pullopargs[b'remotebookmarks'] = remotebookmarks
5160 5176 for b in opts.get('bookmark', []):
5161 5177 b = repo._bookmarks.expandname(b)
5162 5178 if b not in remotebookmarks:
5163 5179 raise error.InputError(
5164 5180 _(b'remote bookmark %s not found!') % b
5165 5181 )
5166 5182 nodes.append(remotebookmarks[b])
5167 5183 for i, rev in enumerate(revs):
5168 5184 node = fnodes[i].result()
5169 5185 nodes.append(node)
5170 5186 if rev == checkout:
5171 5187 checkout = node
5172 5188
5173 5189 wlock = util.nullcontextmanager()
5174 5190 if opts.get('update'):
5175 5191 wlock = repo.wlock()
5176 5192 with wlock:
5177 5193 pullopargs.update(opts.get('opargs', {}))
5178 5194 modheads = exchange.pull(
5179 5195 repo,
5180 5196 other,
5181 5197 path=path,
5182 5198 heads=nodes,
5183 5199 force=opts.get('force'),
5184 5200 bookmarks=opts.get('bookmark', ()),
5185 5201 opargs=pullopargs,
5186 5202 confirm=opts.get('confirm'),
5187 5203 ).cgresult
5188 5204
5189 5205 # brev is a name, which might be a bookmark to be activated at
5190 5206 # the end of the update. In other words, it is an explicit
5191 5207 # destination of the update
5192 5208 brev = None
5193 5209
5194 5210 if checkout:
5195 5211 checkout = repo.unfiltered().changelog.rev(checkout)
5196 5212
5197 5213 # order below depends on implementation of
5198 5214 # hg.addbranchrevs(). opts['bookmark'] is ignored,
5199 5215 # because 'checkout' is determined without it.
5200 5216 if opts.get('rev'):
5201 5217 brev = opts['rev'][0]
5202 5218 elif opts.get('branch'):
5203 5219 brev = opts['branch'][0]
5204 5220 else:
5205 5221 brev = path.branch
5206 5222
5207 5223 # XXX path: we are losing the `path` object here. Keeping it
5208 5224 # would be valuable. For example as a "variant" as we do
5209 5225 # for pushes.
5210 5226 repo._subtoppath = path.loc
5211 5227 try:
5212 5228 update_conflict = cmdutil.postincoming(
5213 5229 ui, repo, modheads, opts.get('update'), checkout, brev
5214 5230 )
5215 5231 except error.FilteredRepoLookupError as exc:
5216 5232 msg = _(b'cannot update to target: %s') % exc.args[0]
5217 5233 exc.args = (msg,) + exc.args[1:]
5218 5234 raise
5219 5235 finally:
5220 5236 del repo._subtoppath
5221 5237
5222 5238 finally:
5223 5239 other.close()
5224 5240 # skip the remaining pull source if they are some conflict.
5225 5241 if update_conflict:
5226 5242 break
5227 5243 if update_conflict:
5228 5244 return 1
5229 5245 else:
5230 5246 return 0
5231 5247
5232 5248
5233 5249 @command(
5234 5250 b'purge|clean',
5235 5251 [
5236 5252 (b'a', b'abort-on-err', None, _(b'abort if an error occurs')),
5237 5253 (b'', b'all', None, _(b'purge ignored files too')),
5238 5254 (b'i', b'ignored', None, _(b'purge only ignored files')),
5239 5255 (b'', b'dirs', None, _(b'purge empty directories')),
5240 5256 (b'', b'files', None, _(b'purge files')),
5241 5257 (b'p', b'print', None, _(b'print filenames instead of deleting them')),
5242 5258 (
5243 5259 b'0',
5244 5260 b'print0',
5245 5261 None,
5246 5262 _(
5247 5263 b'end filenames with NUL, for use with xargs'
5248 5264 b' (implies -p/--print)'
5249 5265 ),
5250 5266 ),
5251 5267 (b'', b'confirm', None, _(b'ask before permanently deleting files')),
5252 5268 ]
5253 5269 + cmdutil.walkopts,
5254 5270 _(b'hg purge [OPTION]... [DIR]...'),
5255 5271 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5256 5272 )
5257 5273 def purge(ui, repo, *dirs, **opts):
5258 5274 """removes files not tracked by Mercurial
5259 5275
5260 5276 Delete files not known to Mercurial. This is useful to test local
5261 5277 and uncommitted changes in an otherwise-clean source tree.
5262 5278
5263 5279 This means that purge will delete the following by default:
5264 5280
5265 5281 - Unknown files: files marked with "?" by :hg:`status`
5266 5282 - Empty directories: in fact Mercurial ignores directories unless
5267 5283 they contain files under source control management
5268 5284
5269 5285 But it will leave untouched:
5270 5286
5271 5287 - Modified and unmodified tracked files
5272 5288 - Ignored files (unless -i or --all is specified)
5273 5289 - New files added to the repository (with :hg:`add`)
5274 5290
5275 5291 The --files and --dirs options can be used to direct purge to delete
5276 5292 only files, only directories, or both. If neither option is given,
5277 5293 both will be deleted.
5278 5294
5279 5295 If directories are given on the command line, only files in these
5280 5296 directories are considered.
5281 5297
5282 5298 Be careful with purge, as you could irreversibly delete some files
5283 5299 you forgot to add to the repository. If you only want to print the
5284 5300 list of files that this program would delete, use the --print
5285 5301 option.
5286 5302 """
5287 5303 cmdutil.check_at_most_one_arg(opts, 'all', 'ignored')
5288 5304
5289 5305 act = not opts.get('print')
5290 5306 eol = b'\n'
5291 5307 if opts.get('print0'):
5292 5308 eol = b'\0'
5293 5309 act = False # --print0 implies --print
5294 5310 if opts.get('all', False):
5295 5311 ignored = True
5296 5312 unknown = True
5297 5313 else:
5298 5314 ignored = opts.get('ignored', False)
5299 5315 unknown = not ignored
5300 5316
5301 5317 removefiles = opts.get('files')
5302 5318 removedirs = opts.get('dirs')
5303 5319 confirm = opts.get('confirm')
5304 5320 if confirm is None:
5305 5321 try:
5306 5322 extensions.find(b'purge')
5307 5323 confirm = False
5308 5324 except KeyError:
5309 5325 confirm = True
5310 5326
5311 5327 if not removefiles and not removedirs:
5312 5328 removefiles = True
5313 5329 removedirs = True
5314 5330
5315 5331 match = scmutil.match(repo[None], dirs, pycompat.byteskwargs(opts))
5316 5332
5317 5333 paths = mergemod.purge(
5318 5334 repo,
5319 5335 match,
5320 5336 unknown=unknown,
5321 5337 ignored=ignored,
5322 5338 removeemptydirs=removedirs,
5323 5339 removefiles=removefiles,
5324 5340 abortonerror=opts.get('abort_on_err'),
5325 5341 noop=not act,
5326 5342 confirm=confirm,
5327 5343 )
5328 5344
5329 5345 for path in paths:
5330 5346 if not act:
5331 5347 ui.write(b'%s%s' % (path, eol))
5332 5348
5333 5349
5334 5350 @command(
5335 5351 b'push',
5336 5352 [
5337 5353 (b'f', b'force', None, _(b'force push')),
5338 5354 (
5339 5355 b'r',
5340 5356 b'rev',
5341 5357 [],
5342 5358 _(b'a changeset intended to be included in the destination'),
5343 5359 _(b'REV'),
5344 5360 ),
5345 5361 (b'B', b'bookmark', [], _(b"bookmark to push"), _(b'BOOKMARK')),
5346 5362 (b'', b'all-bookmarks', None, _(b"push all bookmarks (EXPERIMENTAL)")),
5347 5363 (
5348 5364 b'b',
5349 5365 b'branch',
5350 5366 [],
5351 5367 _(b'a specific branch you would like to push'),
5352 5368 _(b'BRANCH'),
5353 5369 ),
5354 5370 (b'', b'new-branch', False, _(b'allow pushing a new branch')),
5355 5371 (
5356 5372 b'',
5357 5373 b'pushvars',
5358 5374 [],
5359 5375 _(b'variables that can be sent to server (ADVANCED)'),
5360 5376 ),
5361 5377 (
5362 5378 b'',
5363 5379 b'publish',
5364 5380 False,
5365 5381 _(b'push the changeset as public (EXPERIMENTAL)'),
5366 5382 ),
5367 5383 ]
5368 5384 + remoteopts,
5369 5385 _(b'[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]...'),
5370 5386 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
5371 5387 helpbasic=True,
5372 5388 )
5373 5389 def push(ui, repo, *dests, **opts):
5374 5390 """push changes to the specified destination
5375 5391
5376 5392 Push changesets from the local repository to the specified
5377 5393 destination.
5378 5394
5379 5395 This operation is symmetrical to pull: it is identical to a pull
5380 5396 in the destination repository from the current one.
5381 5397
5382 5398 By default, push will not allow creation of new heads at the
5383 5399 destination, since multiple heads would make it unclear which head
5384 5400 to use. In this situation, it is recommended to pull and merge
5385 5401 before pushing.
5386 5402
5387 5403 Use --new-branch if you want to allow push to create a new named
5388 5404 branch that is not present at the destination. This allows you to
5389 5405 only create a new branch without forcing other changes.
5390 5406
5391 5407 .. note::
5392 5408
5393 5409 Extra care should be taken with the -f/--force option,
5394 5410 which will push all new heads on all branches, an action which will
5395 5411 almost always cause confusion for collaborators.
5396 5412
5397 5413 If -r/--rev is used, the specified revision and all its ancestors
5398 5414 will be pushed to the remote repository.
5399 5415
5400 5416 If -B/--bookmark is used, the specified bookmarked revision, its
5401 5417 ancestors, and the bookmark will be pushed to the remote
5402 5418 repository. Specifying ``.`` is equivalent to specifying the active
5403 5419 bookmark's name. Use the --all-bookmarks option for pushing all
5404 5420 current bookmarks.
5405 5421
5406 5422 Please see :hg:`help urls` for important details about ``ssh://``
5407 5423 URLs. If DESTINATION is omitted, a default path will be used.
5408 5424
5409 5425 When passed multiple destinations, push will process them one after the
5410 5426 other, but stop should an error occur.
5411 5427
5412 5428 .. container:: verbose
5413 5429
5414 5430 The --pushvars option sends strings to the server that become
5415 5431 environment variables prepended with ``HG_USERVAR_``. For example,
5416 5432 ``--pushvars ENABLE_FEATURE=true``, provides the server side hooks with
5417 5433 ``HG_USERVAR_ENABLE_FEATURE=true`` as part of their environment.
5418 5434
5419 5435 pushvars can provide for user-overridable hooks as well as set debug
5420 5436 levels. One example is having a hook that blocks commits containing
5421 5437 conflict markers, but enables the user to override the hook if the file
5422 5438 is using conflict markers for testing purposes or the file format has
5423 5439 strings that look like conflict markers.
5424 5440
5425 5441 By default, servers will ignore `--pushvars`. To enable it add the
5426 5442 following to your configuration file::
5427 5443
5428 5444 [push]
5429 5445 pushvars.server = true
5430 5446
5431 5447 Returns 0 if push was successful, 1 if nothing to push.
5432 5448 """
5433 5449
5434 5450 opts = pycompat.byteskwargs(opts)
5435 5451
5436 5452 if opts.get(b'all_bookmarks'):
5437 5453 cmdutil.check_incompatible_arguments(
5438 5454 opts,
5439 5455 b'all_bookmarks',
5440 5456 [b'bookmark', b'rev'],
5441 5457 )
5442 5458 opts[b'bookmark'] = list(repo._bookmarks)
5443 5459
5444 5460 if opts.get(b'bookmark'):
5445 5461 ui.setconfig(b'bookmarks', b'pushing', opts[b'bookmark'], b'push')
5446 5462 for b in opts[b'bookmark']:
5447 5463 # translate -B options to -r so changesets get pushed
5448 5464 b = repo._bookmarks.expandname(b)
5449 5465 if b in repo._bookmarks:
5450 5466 opts.setdefault(b'rev', []).append(b)
5451 5467 else:
5452 5468 # if we try to push a deleted bookmark, translate it to null
5453 5469 # this lets simultaneous -r, -b options continue working
5454 5470 opts.setdefault(b'rev', []).append(b"null")
5455 5471
5456 5472 some_pushed = False
5457 5473 result = 0
5458 5474 for path in urlutil.get_push_paths(repo, ui, dests):
5459 5475 dest = path.loc
5460 5476 branches = (path.branch, opts.get(b'branch') or [])
5461 5477 ui.status(_(b'pushing to %s\n') % urlutil.hidepassword(dest))
5462 5478 revs, checkout = hg.addbranchrevs(
5463 5479 repo, repo, branches, opts.get(b'rev')
5464 5480 )
5465 5481 other = hg.peer(repo, opts, dest)
5466 5482
5467 5483 try:
5468 5484 if revs:
5469 5485 revs = [repo[r].node() for r in logcmdutil.revrange(repo, revs)]
5470 5486 if not revs:
5471 5487 raise error.InputError(
5472 5488 _(b"specified revisions evaluate to an empty set"),
5473 5489 hint=_(b"use different revision arguments"),
5474 5490 )
5475 5491 elif path.pushrev:
5476 5492 # It doesn't make any sense to specify ancestor revisions. So limit
5477 5493 # to DAG heads to make discovery simpler.
5478 5494 expr = revsetlang.formatspec(b'heads(%r)', path.pushrev)
5479 5495 revs = scmutil.revrange(repo, [expr])
5480 5496 revs = [repo[rev].node() for rev in revs]
5481 5497 if not revs:
5482 5498 raise error.InputError(
5483 5499 _(
5484 5500 b'default push revset for path evaluates to an empty set'
5485 5501 )
5486 5502 )
5487 5503 elif ui.configbool(b'commands', b'push.require-revs'):
5488 5504 raise error.InputError(
5489 5505 _(b'no revisions specified to push'),
5490 5506 hint=_(b'did you mean "hg push -r ."?'),
5491 5507 )
5492 5508
5493 5509 repo._subtoppath = dest
5494 5510 try:
5495 5511 # push subrepos depth-first for coherent ordering
5496 5512 c = repo[b'.']
5497 5513 subs = c.substate # only repos that are committed
5498 5514 for s in sorted(subs):
5499 5515 sub_result = c.sub(s).push(opts)
5500 5516 if sub_result == 0:
5501 5517 return 1
5502 5518 finally:
5503 5519 del repo._subtoppath
5504 5520
5505 5521 opargs = dict(
5506 5522 opts.get(b'opargs', {})
5507 5523 ) # copy opargs since we may mutate it
5508 5524 opargs.setdefault(b'pushvars', []).extend(opts.get(b'pushvars', []))
5509 5525
5510 5526 pushop = exchange.push(
5511 5527 repo,
5512 5528 other,
5513 5529 opts.get(b'force'),
5514 5530 revs=revs,
5515 5531 newbranch=opts.get(b'new_branch'),
5516 5532 bookmarks=opts.get(b'bookmark', ()),
5517 5533 publish=opts.get(b'publish'),
5518 5534 opargs=opargs,
5519 5535 )
5520 5536
5521 5537 if pushop.cgresult == 0:
5522 5538 result = 1
5523 5539 elif pushop.cgresult is not None:
5524 5540 some_pushed = True
5525 5541
5526 5542 if pushop.bkresult is not None:
5527 5543 if pushop.bkresult == 2:
5528 5544 result = 2
5529 5545 elif not result and pushop.bkresult:
5530 5546 result = 2
5531 5547
5532 5548 if result:
5533 5549 break
5534 5550
5535 5551 finally:
5536 5552 other.close()
5537 5553 if result == 0 and not some_pushed:
5538 5554 result = 1
5539 5555 return result
5540 5556
5541 5557
5542 5558 @command(
5543 5559 b'recover',
5544 5560 [
5545 5561 (b'', b'verify', False, b"run `hg verify` after successful recover"),
5546 5562 ],
5547 5563 helpcategory=command.CATEGORY_MAINTENANCE,
5548 5564 )
5549 5565 def recover(ui, repo, **opts):
5550 5566 """roll back an interrupted transaction
5551 5567
5552 5568 Recover from an interrupted commit or pull.
5553 5569
5554 5570 This command tries to fix the repository status after an
5555 5571 interrupted operation. It should only be necessary when Mercurial
5556 5572 suggests it.
5557 5573
5558 5574 Returns 0 if successful, 1 if nothing to recover or verify fails.
5559 5575 """
5560 5576 ret = repo.recover()
5561 5577 if ret:
5562 5578 if opts['verify']:
5563 5579 return hg.verify(repo)
5564 5580 else:
5565 5581 msg = _(
5566 5582 b"(verify step skipped, run `hg verify` to check your "
5567 5583 b"repository content)\n"
5568 5584 )
5569 5585 ui.warn(msg)
5570 5586 return 0
5571 5587 return 1
5572 5588
5573 5589
5574 5590 @command(
5575 5591 b'remove|rm',
5576 5592 [
5577 5593 (b'A', b'after', None, _(b'record delete for missing files')),
5578 5594 (b'f', b'force', None, _(b'forget added files, delete modified files')),
5579 5595 ]
5580 5596 + subrepoopts
5581 5597 + walkopts
5582 5598 + dryrunopts,
5583 5599 _(b'[OPTION]... FILE...'),
5584 5600 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5585 5601 helpbasic=True,
5586 5602 inferrepo=True,
5587 5603 )
5588 5604 def remove(ui, repo, *pats, **opts):
5589 5605 """remove the specified files on the next commit
5590 5606
5591 5607 Schedule the indicated files for removal from the current branch.
5592 5608
5593 5609 This command schedules the files to be removed at the next commit.
5594 5610 To undo a remove before that, see :hg:`revert`. To undo added
5595 5611 files, see :hg:`forget`.
5596 5612
5597 5613 .. container:: verbose
5598 5614
5599 5615 -A/--after can be used to remove only files that have already
5600 5616 been deleted, -f/--force can be used to force deletion, and -Af
5601 5617 can be used to remove files from the next revision without
5602 5618 deleting them from the working directory.
5603 5619
5604 5620 The following table details the behavior of remove for different
5605 5621 file states (columns) and option combinations (rows). The file
5606 5622 states are Added [A], Clean [C], Modified [M] and Missing [!]
5607 5623 (as reported by :hg:`status`). The actions are Warn, Remove
5608 5624 (from branch) and Delete (from disk):
5609 5625
5610 5626 ========= == == == ==
5611 5627 opt/state A C M !
5612 5628 ========= == == == ==
5613 5629 none W RD W R
5614 5630 -f R RD RD R
5615 5631 -A W W W R
5616 5632 -Af R R R R
5617 5633 ========= == == == ==
5618 5634
5619 5635 .. note::
5620 5636
5621 5637 :hg:`remove` never deletes files in Added [A] state from the
5622 5638 working directory, not even if ``--force`` is specified.
5623 5639
5624 5640 Returns 0 on success, 1 if any warnings encountered.
5625 5641 """
5626 5642
5627 5643 after, force = opts.get('after'), opts.get('force')
5628 5644 dryrun = opts.get('dry_run')
5629 5645 if not pats and not after:
5630 5646 raise error.InputError(_(b'no files specified'))
5631 5647
5632 5648 with repo.wlock(), repo.dirstate.changing_files(repo):
5633 5649 m = scmutil.match(repo[None], pats, pycompat.byteskwargs(opts))
5634 5650 subrepos = opts.get('subrepos')
5635 5651 uipathfn = scmutil.getuipathfn(repo, legacyrelativevalue=True)
5636 5652 return cmdutil.remove(
5637 5653 ui, repo, m, b"", uipathfn, after, force, subrepos, dryrun=dryrun
5638 5654 )
5639 5655
5640 5656
5641 5657 @command(
5642 5658 b'rename|move|mv',
5643 5659 [
5644 5660 (b'', b'forget', None, _(b'unmark a destination file as renamed')),
5645 5661 (b'A', b'after', None, _(b'record a rename that has already occurred')),
5646 5662 (
5647 5663 b'',
5648 5664 b'at-rev',
5649 5665 b'',
5650 5666 _(b'(un)mark renames in the given revision (EXPERIMENTAL)'),
5651 5667 _(b'REV'),
5652 5668 ),
5653 5669 (
5654 5670 b'f',
5655 5671 b'force',
5656 5672 None,
5657 5673 _(b'forcibly move over an existing managed file'),
5658 5674 ),
5659 5675 ]
5660 5676 + walkopts
5661 5677 + dryrunopts,
5662 5678 _(b'[OPTION]... SOURCE... DEST'),
5663 5679 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5664 5680 )
5665 5681 def rename(ui, repo, *pats, **opts):
5666 5682 """rename files; equivalent of copy + remove
5667 5683
5668 5684 Mark dest as copies of sources; mark sources for deletion. If dest
5669 5685 is a directory, copies are put in that directory. If dest is a
5670 5686 file, there can only be one source.
5671 5687
5672 5688 By default, this command copies the contents of files as they
5673 5689 exist in the working directory. If invoked with -A/--after, the
5674 5690 operation is recorded, but no copying is performed.
5675 5691
5676 5692 To undo marking a destination file as renamed, use --forget. With that
5677 5693 option, all given (positional) arguments are unmarked as renames. The
5678 5694 destination file(s) will be left in place (still tracked). The source
5679 5695 file(s) will not be restored. Note that :hg:`rename --forget` behaves
5680 5696 the same way as :hg:`copy --forget`.
5681 5697
5682 5698 This command takes effect with the next commit by default.
5683 5699
5684 5700 Returns 0 on success, 1 if errors are encountered.
5685 5701 """
5686 5702 context = lambda repo: repo.dirstate.changing_files(repo)
5687 5703 rev = opts.get('at_rev')
5688 5704
5689 5705 if rev:
5690 5706 ctx = logcmdutil.revsingle(repo, rev)
5691 5707 if ctx.rev() is not None:
5692 5708
5693 5709 def context(repo):
5694 5710 return util.nullcontextmanager()
5695 5711
5696 5712 opts['at_rev'] = ctx.rev()
5697 5713 with repo.wlock(), context(repo):
5698 5714 return cmdutil.copy(
5699 5715 ui, repo, pats, pycompat.byteskwargs(opts), rename=True
5700 5716 )
5701 5717
5702 5718
5703 5719 @command(
5704 5720 b'resolve',
5705 5721 [
5706 5722 (b'a', b'all', None, _(b'select all unresolved files')),
5707 5723 (b'l', b'list', None, _(b'list state of files needing merge')),
5708 5724 (b'm', b'mark', None, _(b'mark files as resolved')),
5709 5725 (b'u', b'unmark', None, _(b'mark files as unresolved')),
5710 5726 (b'n', b'no-status', None, _(b'hide status prefix')),
5711 5727 (b'', b're-merge', None, _(b're-merge files')),
5712 5728 ]
5713 5729 + mergetoolopts
5714 5730 + walkopts
5715 5731 + formatteropts,
5716 5732 _(b'[OPTION]... [FILE]...'),
5717 5733 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
5718 5734 inferrepo=True,
5719 5735 )
5720 5736 def resolve(ui, repo, *pats, **opts):
5721 5737 """redo merges or set/view the merge status of files
5722 5738
5723 5739 Merges with unresolved conflicts are often the result of
5724 5740 non-interactive merging using the ``internal:merge`` configuration
5725 5741 setting, or a command-line merge tool like ``diff3``. The resolve
5726 5742 command is used to manage the files involved in a merge, after
5727 5743 :hg:`merge` has been run, and before :hg:`commit` is run (i.e. the
5728 5744 working directory must have two parents). See :hg:`help
5729 5745 merge-tools` for information on configuring merge tools.
5730 5746
5731 5747 The resolve command can be used in the following ways:
5732 5748
5733 5749 - :hg:`resolve [--re-merge] [--tool TOOL] FILE...`: attempt to re-merge
5734 5750 the specified files, discarding any previous merge attempts. Re-merging
5735 5751 is not performed for files already marked as resolved. Use ``--all/-a``
5736 5752 to select all unresolved files. ``--tool`` can be used to specify
5737 5753 the merge tool used for the given files. It overrides the HGMERGE
5738 5754 environment variable and your configuration files. Previous file
5739 5755 contents are saved with a ``.orig`` suffix.
5740 5756
5741 5757 - :hg:`resolve -m [FILE]`: mark a file as having been resolved
5742 5758 (e.g. after having manually fixed-up the files). The default is
5743 5759 to mark all unresolved files.
5744 5760
5745 5761 - :hg:`resolve -u [FILE]...`: mark a file as unresolved. The
5746 5762 default is to mark all resolved files.
5747 5763
5748 5764 - :hg:`resolve -l`: list files which had or still have conflicts.
5749 5765 In the printed list, ``U`` = unresolved and ``R`` = resolved.
5750 5766 You can use ``set:unresolved()`` or ``set:resolved()`` to filter
5751 5767 the list. See :hg:`help filesets` for details.
5752 5768
5753 5769 .. note::
5754 5770
5755 5771 Mercurial will not let you commit files with unresolved merge
5756 5772 conflicts. You must use :hg:`resolve -m ...` before you can
5757 5773 commit after a conflicting merge.
5758 5774
5759 5775 .. container:: verbose
5760 5776
5761 5777 Template:
5762 5778
5763 5779 The following keywords are supported in addition to the common template
5764 5780 keywords and functions. See also :hg:`help templates`.
5765 5781
5766 5782 :mergestatus: String. Character denoting merge conflicts, ``U`` or ``R``.
5767 5783 :path: String. Repository-absolute path of the file.
5768 5784
5769 5785 Returns 0 on success, 1 if any files fail a resolve attempt.
5770 5786 """
5771 5787
5772 5788 opts = pycompat.byteskwargs(opts)
5773 5789 confirm = ui.configbool(b'commands', b'resolve.confirm')
5774 5790 flaglist = b'all mark unmark list no_status re_merge'.split()
5775 5791 all, mark, unmark, show, nostatus, remerge = [opts.get(o) for o in flaglist]
5776 5792
5777 5793 actioncount = len(list(filter(None, [show, mark, unmark, remerge])))
5778 5794 if actioncount > 1:
5779 5795 raise error.InputError(_(b"too many actions specified"))
5780 5796 elif actioncount == 0 and ui.configbool(
5781 5797 b'commands', b'resolve.explicit-re-merge'
5782 5798 ):
5783 5799 hint = _(b'use --mark, --unmark, --list or --re-merge')
5784 5800 raise error.InputError(_(b'no action specified'), hint=hint)
5785 5801 if pats and all:
5786 5802 raise error.InputError(_(b"can't specify --all and patterns"))
5787 5803 if not (all or pats or show or mark or unmark):
5788 5804 raise error.InputError(
5789 5805 _(b'no files or directories specified'),
5790 5806 hint=b'use --all to re-merge all unresolved files',
5791 5807 )
5792 5808
5793 5809 if confirm:
5794 5810 if all:
5795 5811 if ui.promptchoice(
5796 5812 _(b're-merge all unresolved files (yn)?$$ &Yes $$ &No')
5797 5813 ):
5798 5814 raise error.CanceledError(_(b'user quit'))
5799 5815 if mark and not pats:
5800 5816 if ui.promptchoice(
5801 5817 _(
5802 5818 b'mark all unresolved files as resolved (yn)?'
5803 5819 b'$$ &Yes $$ &No'
5804 5820 )
5805 5821 ):
5806 5822 raise error.CanceledError(_(b'user quit'))
5807 5823 if unmark and not pats:
5808 5824 if ui.promptchoice(
5809 5825 _(
5810 5826 b'mark all resolved files as unresolved (yn)?'
5811 5827 b'$$ &Yes $$ &No'
5812 5828 )
5813 5829 ):
5814 5830 raise error.CanceledError(_(b'user quit'))
5815 5831
5816 5832 uipathfn = scmutil.getuipathfn(repo)
5817 5833
5818 5834 if show:
5819 5835 ui.pager(b'resolve')
5820 5836 fm = ui.formatter(b'resolve', opts)
5821 5837 ms = mergestatemod.mergestate.read(repo)
5822 5838 wctx = repo[None]
5823 5839 m = scmutil.match(wctx, pats, opts)
5824 5840
5825 5841 # Labels and keys based on merge state. Unresolved path conflicts show
5826 5842 # as 'P'. Resolved path conflicts show as 'R', the same as normal
5827 5843 # resolved conflicts.
5828 5844 mergestateinfo = {
5829 5845 mergestatemod.MERGE_RECORD_UNRESOLVED: (
5830 5846 b'resolve.unresolved',
5831 5847 b'U',
5832 5848 ),
5833 5849 mergestatemod.MERGE_RECORD_RESOLVED: (b'resolve.resolved', b'R'),
5834 5850 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH: (
5835 5851 b'resolve.unresolved',
5836 5852 b'P',
5837 5853 ),
5838 5854 mergestatemod.MERGE_RECORD_RESOLVED_PATH: (
5839 5855 b'resolve.resolved',
5840 5856 b'R',
5841 5857 ),
5842 5858 }
5843 5859
5844 5860 for f in ms:
5845 5861 if not m(f):
5846 5862 continue
5847 5863
5848 5864 label, key = mergestateinfo[ms[f]]
5849 5865 fm.startitem()
5850 5866 fm.context(ctx=wctx)
5851 5867 fm.condwrite(not nostatus, b'mergestatus', b'%s ', key, label=label)
5852 5868 fm.data(path=f)
5853 5869 fm.plain(b'%s\n' % uipathfn(f), label=label)
5854 5870 fm.end()
5855 5871 return 0
5856 5872
5857 5873 with repo.wlock():
5858 5874 ms = mergestatemod.mergestate.read(repo)
5859 5875
5860 5876 if not (ms.active() or repo.dirstate.p2() != repo.nullid):
5861 5877 raise error.StateError(
5862 5878 _(b'resolve command not applicable when not merging')
5863 5879 )
5864 5880
5865 5881 wctx = repo[None]
5866 5882 m = scmutil.match(wctx, pats, opts)
5867 5883 ret = 0
5868 5884 didwork = False
5869 5885
5870 5886 hasconflictmarkers = []
5871 5887 if mark:
5872 5888 markcheck = ui.config(b'commands', b'resolve.mark-check')
5873 5889 if markcheck not in [b'warn', b'abort']:
5874 5890 # Treat all invalid / unrecognized values as 'none'.
5875 5891 markcheck = False
5876 5892 for f in ms:
5877 5893 if not m(f):
5878 5894 continue
5879 5895
5880 5896 didwork = True
5881 5897
5882 5898 # path conflicts must be resolved manually
5883 5899 if ms[f] in (
5884 5900 mergestatemod.MERGE_RECORD_UNRESOLVED_PATH,
5885 5901 mergestatemod.MERGE_RECORD_RESOLVED_PATH,
5886 5902 ):
5887 5903 if mark:
5888 5904 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED_PATH)
5889 5905 elif unmark:
5890 5906 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED_PATH)
5891 5907 elif ms[f] == mergestatemod.MERGE_RECORD_UNRESOLVED_PATH:
5892 5908 ui.warn(
5893 5909 _(b'%s: path conflict must be resolved manually\n')
5894 5910 % uipathfn(f)
5895 5911 )
5896 5912 continue
5897 5913
5898 5914 if mark:
5899 5915 if markcheck:
5900 5916 fdata = repo.wvfs.tryread(f)
5901 5917 if (
5902 5918 filemerge.hasconflictmarkers(fdata)
5903 5919 and ms[f] != mergestatemod.MERGE_RECORD_RESOLVED
5904 5920 ):
5905 5921 hasconflictmarkers.append(f)
5906 5922 ms.mark(f, mergestatemod.MERGE_RECORD_RESOLVED)
5907 5923 elif unmark:
5908 5924 ms.mark(f, mergestatemod.MERGE_RECORD_UNRESOLVED)
5909 5925 else:
5910 5926 # backup pre-resolve (merge uses .orig for its own purposes)
5911 5927 a = repo.wjoin(f)
5912 5928 try:
5913 5929 util.copyfile(a, a + b".resolve")
5914 5930 except FileNotFoundError:
5915 5931 pass
5916 5932
5917 5933 try:
5918 5934 # preresolve file
5919 5935 overrides = {(b'ui', b'forcemerge'): opts.get(b'tool', b'')}
5920 5936 with ui.configoverride(overrides, b'resolve'):
5921 5937 r = ms.resolve(f, wctx)
5922 5938 if r:
5923 5939 ret = 1
5924 5940 finally:
5925 5941 ms.commit()
5926 5942
5927 5943 # replace filemerge's .orig file with our resolve file
5928 5944 try:
5929 5945 util.rename(
5930 5946 a + b".resolve", scmutil.backuppath(ui, repo, f)
5931 5947 )
5932 5948 except FileNotFoundError:
5933 5949 pass
5934 5950
5935 5951 if hasconflictmarkers:
5936 5952 ui.warn(
5937 5953 _(
5938 5954 b'warning: the following files still have conflict '
5939 5955 b'markers:\n'
5940 5956 )
5941 5957 + b''.join(
5942 5958 b' ' + uipathfn(f) + b'\n' for f in hasconflictmarkers
5943 5959 )
5944 5960 )
5945 5961 if markcheck == b'abort' and not all and not pats:
5946 5962 raise error.StateError(
5947 5963 _(b'conflict markers detected'),
5948 5964 hint=_(b'use --all to mark anyway'),
5949 5965 )
5950 5966
5951 5967 ms.commit()
5952 5968 branchmerge = repo.dirstate.p2() != repo.nullid
5953 5969 # resolve is not doing a parent change here, however, `record updates`
5954 5970 # will call some dirstate API that at intended for parent changes call.
5955 5971 # Ideally we would not need this and could implement a lighter version
5956 5972 # of the recordupdateslogic that will not have to deal with the part
5957 5973 # related to parent changes. However this would requires that:
5958 5974 # - we are sure we passed around enough information at update/merge
5959 5975 # time to no longer needs it at `hg resolve time`
5960 5976 # - we are sure we store that information well enough to be able to reuse it
5961 5977 # - we are the necessary logic to reuse it right.
5962 5978 #
5963 5979 # All this should eventually happens, but in the mean time, we use this
5964 5980 # context manager slightly out of the context it should be.
5965 5981 with repo.dirstate.changing_parents(repo):
5966 5982 mergestatemod.recordupdates(repo, ms.actions(), branchmerge, None)
5967 5983
5968 5984 if not didwork and pats:
5969 5985 hint = None
5970 5986 if not any([p for p in pats if p.find(b':') >= 0]):
5971 5987 pats = [b'path:%s' % p for p in pats]
5972 5988 m = scmutil.match(wctx, pats, opts)
5973 5989 for f in ms:
5974 5990 if not m(f):
5975 5991 continue
5976 5992
5977 5993 def flag(o):
5978 5994 if o == b're_merge':
5979 5995 return b'--re-merge '
5980 5996 return b'-%s ' % o[0:1]
5981 5997
5982 5998 flags = b''.join([flag(o) for o in flaglist if opts.get(o)])
5983 5999 hint = _(b"(try: hg resolve %s%s)\n") % (
5984 6000 flags,
5985 6001 b' '.join(pats),
5986 6002 )
5987 6003 break
5988 6004 ui.warn(_(b"arguments do not match paths that need resolving\n"))
5989 6005 if hint:
5990 6006 ui.warn(hint)
5991 6007
5992 6008 unresolvedf = ms.unresolvedcount()
5993 6009 if not unresolvedf:
5994 6010 ui.status(_(b'(no more unresolved files)\n'))
5995 6011 cmdutil.checkafterresolved(repo)
5996 6012
5997 6013 return ret
5998 6014
5999 6015
6000 6016 @command(
6001 6017 b'revert',
6002 6018 [
6003 6019 (b'a', b'all', None, _(b'revert all changes when no arguments given')),
6004 6020 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
6005 6021 (b'r', b'rev', b'', _(b'revert to the specified revision'), _(b'REV')),
6006 6022 (b'C', b'no-backup', None, _(b'do not save backup copies of files')),
6007 6023 (b'i', b'interactive', None, _(b'interactively select the changes')),
6008 6024 ]
6009 6025 + walkopts
6010 6026 + dryrunopts,
6011 6027 _(b'[OPTION]... [-r REV] [NAME]...'),
6012 6028 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6013 6029 )
6014 6030 def revert(ui, repo, *pats, **opts):
6015 6031 """restore files to their checkout state
6016 6032
6017 6033 .. note::
6018 6034
6019 6035 To check out earlier revisions, you should use :hg:`update REV`.
6020 6036 To cancel an uncommitted merge (and lose your changes),
6021 6037 use :hg:`merge --abort`.
6022 6038
6023 6039 With no revision specified, revert the specified files or directories
6024 6040 to the contents they had in the parent of the working directory.
6025 6041 This restores the contents of files to an unmodified
6026 6042 state and unschedules adds, removes, copies, and renames. If the
6027 6043 working directory has two parents, you must explicitly specify a
6028 6044 revision.
6029 6045
6030 6046 Using the -r/--rev or -d/--date options, revert the given files or
6031 6047 directories to their states as of a specific revision. Because
6032 6048 revert does not change the working directory parents, this will
6033 6049 cause these files to appear modified. This can be helpful to "back
6034 6050 out" some or all of an earlier change. See :hg:`backout` for a
6035 6051 related method.
6036 6052
6037 6053 Modified files are saved with a .orig suffix before reverting.
6038 6054 To disable these backups, use --no-backup. It is possible to store
6039 6055 the backup files in a custom directory relative to the root of the
6040 6056 repository by setting the ``ui.origbackuppath`` configuration
6041 6057 option.
6042 6058
6043 6059 See :hg:`help dates` for a list of formats valid for -d/--date.
6044 6060
6045 6061 See :hg:`help backout` for a way to reverse the effect of an
6046 6062 earlier changeset.
6047 6063
6048 6064 Returns 0 on success.
6049 6065 """
6050 6066
6051 6067 if opts.get("date"):
6052 6068 cmdutil.check_incompatible_arguments(opts, 'date', ['rev'])
6053 6069 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
6054 6070
6055 6071 parent, p2 = repo.dirstate.parents()
6056 6072 if not opts.get('rev') and p2 != repo.nullid:
6057 6073 # revert after merge is a trap for new users (issue2915)
6058 6074 raise error.InputError(
6059 6075 _(b'uncommitted merge with no revision specified'),
6060 6076 hint=_(b"use 'hg update' or see 'hg help revert'"),
6061 6077 )
6062 6078
6063 6079 rev = opts.get('rev')
6064 6080 if rev:
6065 6081 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
6066 6082 ctx = logcmdutil.revsingle(repo, rev)
6067 6083
6068 6084 if not (
6069 6085 pats
6070 6086 or opts.get('include')
6071 6087 or opts.get('exclude')
6072 6088 or opts.get('all')
6073 6089 or opts.get('interactive')
6074 6090 ):
6075 6091 msg = _(b"no files or directories specified")
6076 6092 if p2 != repo.nullid:
6077 6093 hint = _(
6078 6094 b"uncommitted merge, use --all to discard all changes,"
6079 6095 b" or 'hg update -C .' to abort the merge"
6080 6096 )
6081 6097 raise error.InputError(msg, hint=hint)
6082 6098 dirty = any(repo.status())
6083 6099 node = ctx.node()
6084 6100 if node != parent:
6085 6101 if dirty:
6086 6102 hint = (
6087 6103 _(
6088 6104 b"uncommitted changes, use --all to discard all"
6089 6105 b" changes, or 'hg update %d' to update"
6090 6106 )
6091 6107 % ctx.rev()
6092 6108 )
6093 6109 else:
6094 6110 hint = (
6095 6111 _(
6096 6112 b"use --all to revert all files,"
6097 6113 b" or 'hg update %d' to update"
6098 6114 )
6099 6115 % ctx.rev()
6100 6116 )
6101 6117 elif dirty:
6102 6118 hint = _(b"uncommitted changes, use --all to discard all changes")
6103 6119 else:
6104 6120 hint = _(b"use --all to revert all files")
6105 6121 raise error.InputError(msg, hint=hint)
6106 6122
6107 6123 return cmdutil.revert(ui, repo, ctx, *pats, **opts)
6108 6124
6109 6125
6110 6126 @command(
6111 6127 b'rollback',
6112 6128 dryrunopts + [(b'f', b'force', False, _(b'ignore safety measures'))],
6113 6129 helpcategory=command.CATEGORY_MAINTENANCE,
6114 6130 )
6115 6131 def rollback(ui, repo, **opts):
6116 6132 """roll back the last transaction (DANGEROUS) (DEPRECATED)
6117 6133
6118 6134 Please use :hg:`commit --amend` instead of rollback to correct
6119 6135 mistakes in the last commit.
6120 6136
6121 6137 This command should be used with care. There is only one level of
6122 6138 rollback, and there is no way to undo a rollback. It will also
6123 6139 restore the dirstate at the time of the last transaction, losing
6124 6140 any dirstate changes since that time. This command does not alter
6125 6141 the working directory.
6126 6142
6127 6143 Transactions are used to encapsulate the effects of all commands
6128 6144 that create new changesets or propagate existing changesets into a
6129 6145 repository.
6130 6146
6131 6147 .. container:: verbose
6132 6148
6133 6149 For example, the following commands are transactional, and their
6134 6150 effects can be rolled back:
6135 6151
6136 6152 - commit
6137 6153 - import
6138 6154 - pull
6139 6155 - push (with this repository as the destination)
6140 6156 - unbundle
6141 6157
6142 6158 To avoid permanent data loss, rollback will refuse to rollback a
6143 6159 commit transaction if it isn't checked out. Use --force to
6144 6160 override this protection.
6145 6161
6146 6162 The rollback command can be entirely disabled by setting the
6147 6163 ``ui.rollback`` configuration setting to false. If you're here
6148 6164 because you want to use rollback and it's disabled, you can
6149 6165 re-enable the command by setting ``ui.rollback`` to true.
6150 6166
6151 6167 This command is not intended for use on public repositories. Once
6152 6168 changes are visible for pull by other users, rolling a transaction
6153 6169 back locally is ineffective (someone else may already have pulled
6154 6170 the changes). Furthermore, a race is possible with readers of the
6155 6171 repository; for example an in-progress pull from the repository
6156 6172 may fail if a rollback is performed.
6157 6173
6158 6174 Returns 0 on success, 1 if no rollback data is available.
6159 6175 """
6160 6176 if not ui.configbool(b'ui', b'rollback'):
6161 6177 raise error.Abort(
6162 6178 _(b'rollback is disabled because it is unsafe'),
6163 6179 hint=b'see `hg help -v rollback` for information',
6164 6180 )
6165 6181 return repo.rollback(dryrun=opts.get('dry_run'), force=opts.get('force'))
6166 6182
6167 6183
6168 6184 @command(
6169 6185 b'root',
6170 6186 [] + formatteropts,
6171 6187 intents={INTENT_READONLY},
6172 6188 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6173 6189 )
6174 6190 def root(ui, repo, **opts):
6175 6191 """print the root (top) of the current working directory
6176 6192
6177 6193 Print the root directory of the current repository.
6178 6194
6179 6195 .. container:: verbose
6180 6196
6181 6197 Template:
6182 6198
6183 6199 The following keywords are supported in addition to the common template
6184 6200 keywords and functions. See also :hg:`help templates`.
6185 6201
6186 6202 :hgpath: String. Path to the .hg directory.
6187 6203 :storepath: String. Path to the directory holding versioned data.
6188 6204
6189 6205 Returns 0 on success.
6190 6206 """
6191 6207 opts = pycompat.byteskwargs(opts)
6192 6208 with ui.formatter(b'root', opts) as fm:
6193 6209 fm.startitem()
6194 6210 fm.write(b'reporoot', b'%s\n', repo.root)
6195 6211 fm.data(hgpath=repo.path, storepath=repo.spath)
6196 6212
6197 6213
6198 6214 @command(
6199 6215 b'serve',
6200 6216 [
6201 6217 (
6202 6218 b'A',
6203 6219 b'accesslog',
6204 6220 b'',
6205 6221 _(b'name of access log file to write to'),
6206 6222 _(b'FILE'),
6207 6223 ),
6208 6224 (b'd', b'daemon', None, _(b'run server in background')),
6209 6225 (b'', b'daemon-postexec', [], _(b'used internally by daemon mode')),
6210 6226 (
6211 6227 b'E',
6212 6228 b'errorlog',
6213 6229 b'',
6214 6230 _(b'name of error log file to write to'),
6215 6231 _(b'FILE'),
6216 6232 ),
6217 6233 # use string type, then we can check if something was passed
6218 6234 (
6219 6235 b'p',
6220 6236 b'port',
6221 6237 b'',
6222 6238 _(b'port to listen on (default: 8000)'),
6223 6239 _(b'PORT'),
6224 6240 ),
6225 6241 (
6226 6242 b'a',
6227 6243 b'address',
6228 6244 b'',
6229 6245 _(b'address to listen on (default: all interfaces)'),
6230 6246 _(b'ADDR'),
6231 6247 ),
6232 6248 (
6233 6249 b'',
6234 6250 b'prefix',
6235 6251 b'',
6236 6252 _(b'prefix path to serve from (default: server root)'),
6237 6253 _(b'PREFIX'),
6238 6254 ),
6239 6255 (
6240 6256 b'n',
6241 6257 b'name',
6242 6258 b'',
6243 6259 _(b'name to show in web pages (default: working directory)'),
6244 6260 _(b'NAME'),
6245 6261 ),
6246 6262 (
6247 6263 b'',
6248 6264 b'web-conf',
6249 6265 b'',
6250 6266 _(b"name of the hgweb config file (see 'hg help hgweb')"),
6251 6267 _(b'FILE'),
6252 6268 ),
6253 6269 (
6254 6270 b'',
6255 6271 b'webdir-conf',
6256 6272 b'',
6257 6273 _(b'name of the hgweb config file (DEPRECATED)'),
6258 6274 _(b'FILE'),
6259 6275 ),
6260 6276 (
6261 6277 b'',
6262 6278 b'pid-file',
6263 6279 b'',
6264 6280 _(b'name of file to write process ID to'),
6265 6281 _(b'FILE'),
6266 6282 ),
6267 6283 (b'', b'stdio', None, _(b'for remote clients (ADVANCED)')),
6268 6284 (
6269 6285 b'',
6270 6286 b'cmdserver',
6271 6287 b'',
6272 6288 _(b'for remote clients (ADVANCED)'),
6273 6289 _(b'MODE'),
6274 6290 ),
6275 6291 (b't', b'templates', b'', _(b'web templates to use'), _(b'TEMPLATE')),
6276 6292 (b'', b'style', b'', _(b'template style to use'), _(b'STYLE')),
6277 6293 (b'6', b'ipv6', None, _(b'use IPv6 instead of IPv4')),
6278 6294 (b'', b'certificate', b'', _(b'SSL certificate file'), _(b'FILE')),
6279 6295 (b'', b'print-url', None, _(b'start and print only the URL')),
6280 6296 ]
6281 6297 + subrepoopts,
6282 6298 _(b'[OPTION]...'),
6283 6299 helpcategory=command.CATEGORY_REMOTE_REPO_MANAGEMENT,
6284 6300 helpbasic=True,
6285 6301 optionalrepo=True,
6286 6302 )
6287 6303 def serve(ui, repo, **opts):
6288 6304 """start stand-alone webserver
6289 6305
6290 6306 Start a local HTTP repository browser and pull server. You can use
6291 6307 this for ad-hoc sharing and browsing of repositories. It is
6292 6308 recommended to use a real web server to serve a repository for
6293 6309 longer periods of time.
6294 6310
6295 6311 Please note that the server does not implement access control.
6296 6312 This means that, by default, anybody can read from the server and
6297 6313 nobody can write to it by default. Set the ``web.allow-push``
6298 6314 option to ``*`` to allow everybody to push to the server. You
6299 6315 should use a real web server if you need to authenticate users.
6300 6316
6301 6317 By default, the server logs accesses to stdout and errors to
6302 6318 stderr. Use the -A/--accesslog and -E/--errorlog options to log to
6303 6319 files.
6304 6320
6305 6321 To have the server choose a free port number to listen on, specify
6306 6322 a port number of 0; in this case, the server will print the port
6307 6323 number it uses.
6308 6324
6309 6325 Returns 0 on success.
6310 6326 """
6311 6327
6312 6328 cmdutil.check_incompatible_arguments(opts, 'stdio', ['cmdserver'])
6313 6329 opts = pycompat.byteskwargs(opts)
6314 6330 if opts[b"print_url"] and ui.verbose:
6315 6331 raise error.InputError(_(b"cannot use --print-url with --verbose"))
6316 6332
6317 6333 if opts[b"stdio"]:
6318 6334 if repo is None:
6319 6335 raise error.RepoError(
6320 6336 _(b"there is no Mercurial repository here (.hg not found)")
6321 6337 )
6322 6338 accesshidden = False
6323 6339 if repo.filtername is None:
6324 6340 allow = ui.configlist(
6325 6341 b'experimental', b'server.allow-hidden-access'
6326 6342 )
6327 6343 user = procutil.getuser()
6328 6344 if allow and scmutil.ismember(ui, user, allow):
6329 6345 accesshidden = True
6330 6346 else:
6331 6347 msg = (
6332 6348 _(
6333 6349 b'ignoring request to access hidden changeset by '
6334 6350 b'unauthorized user: %s\n'
6335 6351 )
6336 6352 % user
6337 6353 )
6338 6354 ui.warn(msg)
6339 6355
6340 6356 s = wireprotoserver.sshserver(ui, repo, accesshidden=accesshidden)
6341 6357 s.serve_forever()
6342 6358 return
6343 6359
6344 6360 service = server.createservice(ui, repo, opts)
6345 6361 return server.runservice(opts, initfn=service.init, runfn=service.run)
6346 6362
6347 6363
6348 6364 @command(
6349 6365 b'shelve',
6350 6366 [
6351 6367 (
6352 6368 b'A',
6353 6369 b'addremove',
6354 6370 None,
6355 6371 _(b'mark new/missing files as added/removed before shelving'),
6356 6372 ),
6357 6373 (b'u', b'unknown', None, _(b'store unknown files in the shelve')),
6358 6374 (b'', b'cleanup', None, _(b'delete all shelved changes')),
6359 6375 (
6360 6376 b'',
6361 6377 b'date',
6362 6378 b'',
6363 6379 _(b'shelve with the specified commit date'),
6364 6380 _(b'DATE'),
6365 6381 ),
6366 6382 (b'd', b'delete', None, _(b'delete the named shelved change(s)')),
6367 6383 (b'e', b'edit', False, _(b'invoke editor on commit messages')),
6368 6384 (
6369 6385 b'k',
6370 6386 b'keep',
6371 6387 False,
6372 6388 _(b'shelve, but keep changes in the working directory'),
6373 6389 ),
6374 6390 (b'l', b'list', None, _(b'list current shelves')),
6375 6391 (b'm', b'message', b'', _(b'use text as shelve message'), _(b'TEXT')),
6376 6392 (
6377 6393 b'n',
6378 6394 b'name',
6379 6395 b'',
6380 6396 _(b'use the given name for the shelved commit'),
6381 6397 _(b'NAME'),
6382 6398 ),
6383 6399 (
6384 6400 b'p',
6385 6401 b'patch',
6386 6402 None,
6387 6403 _(
6388 6404 b'output patches for changes (provide the names of the shelved '
6389 6405 b'changes as positional arguments)'
6390 6406 ),
6391 6407 ),
6392 6408 (b'i', b'interactive', None, _(b'interactive mode')),
6393 6409 (
6394 6410 b'',
6395 6411 b'stat',
6396 6412 None,
6397 6413 _(
6398 6414 b'output diffstat-style summary of changes (provide the names of '
6399 6415 b'the shelved changes as positional arguments)'
6400 6416 ),
6401 6417 ),
6402 6418 ]
6403 6419 + cmdutil.walkopts,
6404 6420 _(b'hg shelve [OPTION]... [FILE]...'),
6405 6421 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6406 6422 )
6407 6423 def shelve(ui, repo, *pats, **opts):
6408 6424 """save and set aside changes from the working directory
6409 6425
6410 6426 Shelving takes files that "hg status" reports as not clean, saves
6411 6427 the modifications to a bundle (a shelved change), and reverts the
6412 6428 files so that their state in the working directory becomes clean.
6413 6429
6414 6430 To restore these changes to the working directory, using "hg
6415 6431 unshelve"; this will work even if you switch to a different
6416 6432 commit.
6417 6433
6418 6434 When no files are specified, "hg shelve" saves all not-clean
6419 6435 files. If specific files or directories are named, only changes to
6420 6436 those files are shelved.
6421 6437
6422 6438 In bare shelve (when no files are specified, without interactive,
6423 6439 include and exclude option), shelving remembers information if the
6424 6440 working directory was on newly created branch, in other words working
6425 6441 directory was on different branch than its first parent. In this
6426 6442 situation unshelving restores branch information to the working directory.
6427 6443
6428 6444 Each shelved change has a name that makes it easier to find later.
6429 6445 The name of a shelved change defaults to being based on the active
6430 6446 bookmark, or if there is no active bookmark, the current named
6431 6447 branch. To specify a different name, use ``--name``.
6432 6448
6433 6449 To see a list of existing shelved changes, use the ``--list``
6434 6450 option. For each shelved change, this will print its name, age,
6435 6451 and description; use ``--patch`` or ``--stat`` for more details.
6436 6452
6437 6453 To delete specific shelved changes, use ``--delete``. To delete
6438 6454 all shelved changes, use ``--cleanup``.
6439 6455 """
6440 6456 opts = pycompat.byteskwargs(opts)
6441 6457 allowables = [
6442 6458 (b'addremove', {b'create'}), # 'create' is pseudo action
6443 6459 (b'unknown', {b'create'}),
6444 6460 (b'cleanup', {b'cleanup'}),
6445 6461 # ('date', {'create'}), # ignored for passing '--date "0 0"' in tests
6446 6462 (b'delete', {b'delete'}),
6447 6463 (b'edit', {b'create'}),
6448 6464 (b'keep', {b'create'}),
6449 6465 (b'list', {b'list'}),
6450 6466 (b'message', {b'create'}),
6451 6467 (b'name', {b'create'}),
6452 6468 (b'patch', {b'patch', b'list'}),
6453 6469 (b'stat', {b'stat', b'list'}),
6454 6470 ]
6455 6471
6456 6472 def checkopt(opt):
6457 6473 if opts.get(opt):
6458 6474 for i, allowable in allowables:
6459 6475 if opts[i] and opt not in allowable:
6460 6476 raise error.InputError(
6461 6477 _(
6462 6478 b"options '--%s' and '--%s' may not be "
6463 6479 b"used together"
6464 6480 )
6465 6481 % (opt, i)
6466 6482 )
6467 6483 return True
6468 6484
6469 6485 if checkopt(b'cleanup'):
6470 6486 if pats:
6471 6487 raise error.InputError(
6472 6488 _(b"cannot specify names when using '--cleanup'")
6473 6489 )
6474 6490 return shelvemod.cleanupcmd(ui, repo)
6475 6491 elif checkopt(b'delete'):
6476 6492 return shelvemod.deletecmd(ui, repo, pats)
6477 6493 elif checkopt(b'list'):
6478 6494 return shelvemod.listcmd(ui, repo, pats, opts)
6479 6495 elif checkopt(b'patch') or checkopt(b'stat'):
6480 6496 return shelvemod.patchcmds(ui, repo, pats, opts)
6481 6497 else:
6482 6498 return shelvemod.createcmd(ui, repo, pats, opts)
6483 6499
6484 6500
6485 6501 _NOTTERSE = b'nothing'
6486 6502
6487 6503
6488 6504 @command(
6489 6505 b'status|st',
6490 6506 [
6491 6507 (b'A', b'all', None, _(b'show status of all files')),
6492 6508 (b'm', b'modified', None, _(b'show only modified files')),
6493 6509 (b'a', b'added', None, _(b'show only added files')),
6494 6510 (b'r', b'removed', None, _(b'show only removed files')),
6495 6511 (b'd', b'deleted', None, _(b'show only missing files')),
6496 6512 (b'c', b'clean', None, _(b'show only files without changes')),
6497 6513 (b'u', b'unknown', None, _(b'show only unknown (not tracked) files')),
6498 6514 (b'i', b'ignored', None, _(b'show only ignored files')),
6499 6515 (b'n', b'no-status', None, _(b'hide status prefix')),
6500 6516 (b't', b'terse', _NOTTERSE, _(b'show the terse output (EXPERIMENTAL)')),
6501 6517 (
6502 6518 b'C',
6503 6519 b'copies',
6504 6520 None,
6505 6521 _(b'show source of copied files (DEFAULT: ui.statuscopies)'),
6506 6522 ),
6507 6523 (
6508 6524 b'0',
6509 6525 b'print0',
6510 6526 None,
6511 6527 _(b'end filenames with NUL, for use with xargs'),
6512 6528 ),
6513 6529 (b'', b'rev', [], _(b'show difference from revision'), _(b'REV')),
6514 6530 (
6515 6531 b'',
6516 6532 b'change',
6517 6533 b'',
6518 6534 _(b'list the changed files of a revision'),
6519 6535 _(b'REV'),
6520 6536 ),
6521 6537 ]
6522 6538 + walkopts
6523 6539 + subrepoopts
6524 6540 + formatteropts,
6525 6541 _(b'[OPTION]... [FILE]...'),
6526 6542 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6527 6543 helpbasic=True,
6528 6544 inferrepo=True,
6529 6545 intents={INTENT_READONLY},
6530 6546 )
6531 6547 def status(ui, repo, *pats, **opts):
6532 6548 """show changed files in the working directory
6533 6549
6534 6550 Show status of files in the repository. If names are given, only
6535 6551 files that match are shown. Files that are clean or ignored or
6536 6552 the source of a copy/move operation, are not listed unless
6537 6553 -c/--clean, -i/--ignored, -C/--copies or -A/--all are given.
6538 6554 Unless options described with "show only ..." are given, the
6539 6555 options -mardu are used.
6540 6556
6541 6557 Option -q/--quiet hides untracked (unknown and ignored) files
6542 6558 unless explicitly requested with -u/--unknown or -i/--ignored.
6543 6559
6544 6560 .. note::
6545 6561
6546 6562 :hg:`status` may appear to disagree with diff if permissions have
6547 6563 changed or a merge has occurred. The standard diff format does
6548 6564 not report permission changes and diff only reports changes
6549 6565 relative to one merge parent.
6550 6566
6551 6567 If one revision is given, it is used as the base revision.
6552 6568 If two revisions are given, the differences between them are
6553 6569 shown. The --change option can also be used as a shortcut to list
6554 6570 the changed files of a revision from its first parent.
6555 6571
6556 6572 The codes used to show the status of files are::
6557 6573
6558 6574 M = modified
6559 6575 A = added
6560 6576 R = removed
6561 6577 C = clean
6562 6578 ! = missing (deleted by non-hg command, but still tracked)
6563 6579 ? = not tracked
6564 6580 I = ignored
6565 6581 = origin of the previous file (with --copies)
6566 6582
6567 6583 .. container:: verbose
6568 6584
6569 6585 The -t/--terse option abbreviates the output by showing only the directory
6570 6586 name if all the files in it share the same status. The option takes an
6571 6587 argument indicating the statuses to abbreviate: 'm' for 'modified', 'a'
6572 6588 for 'added', 'r' for 'removed', 'd' for 'deleted', 'u' for 'unknown', 'i'
6573 6589 for 'ignored' and 'c' for clean.
6574 6590
6575 6591 It abbreviates only those statuses which are passed. Note that clean and
6576 6592 ignored files are not displayed with '--terse ic' unless the -c/--clean
6577 6593 and -i/--ignored options are also used.
6578 6594
6579 6595 The -v/--verbose option shows information when the repository is in an
6580 6596 unfinished merge, shelve, rebase state etc. You can have this behavior
6581 6597 turned on by default by enabling the ``commands.status.verbose`` option.
6582 6598
6583 6599 You can skip displaying some of these states by setting
6584 6600 ``commands.status.skipstates`` to one or more of: 'bisect', 'graft',
6585 6601 'histedit', 'merge', 'rebase', or 'unshelve'.
6586 6602
6587 6603 Template:
6588 6604
6589 6605 The following keywords are supported in addition to the common template
6590 6606 keywords and functions. See also :hg:`help templates`.
6591 6607
6592 6608 :path: String. Repository-absolute path of the file.
6593 6609 :source: String. Repository-absolute path of the file originated from.
6594 6610 Available if ``--copies`` is specified.
6595 6611 :status: String. Character denoting file's status.
6596 6612
6597 6613 Examples:
6598 6614
6599 6615 - show changes in the working directory relative to a
6600 6616 changeset::
6601 6617
6602 6618 hg status --rev 9353
6603 6619
6604 6620 - show changes in the working directory relative to the
6605 6621 current directory (see :hg:`help patterns` for more information)::
6606 6622
6607 6623 hg status re:
6608 6624
6609 6625 - show all changes including copies in an existing changeset::
6610 6626
6611 6627 hg status --copies --change 9353
6612 6628
6613 6629 - get a NUL separated list of added files, suitable for xargs::
6614 6630
6615 6631 hg status -an0
6616 6632
6617 6633 - show more information about the repository status, abbreviating
6618 6634 added, removed, modified, deleted, and untracked paths::
6619 6635
6620 6636 hg status -v -t mardu
6621 6637
6622 6638 Returns 0 on success.
6623 6639
6624 6640 """
6625 6641
6626 6642 cmdutil.check_at_most_one_arg(opts, 'rev', 'change')
6627 6643 opts = pycompat.byteskwargs(opts)
6628 6644 revs = opts.get(b'rev', [])
6629 6645 change = opts.get(b'change', b'')
6630 6646 terse = opts.get(b'terse', _NOTTERSE)
6631 6647 if terse is _NOTTERSE:
6632 6648 if revs:
6633 6649 terse = b''
6634 6650 else:
6635 6651 terse = ui.config(b'commands', b'status.terse')
6636 6652
6637 6653 if revs and terse:
6638 6654 msg = _(b'cannot use --terse with --rev')
6639 6655 raise error.InputError(msg)
6640 6656 elif change:
6641 6657 repo = scmutil.unhidehashlikerevs(repo, [change], b'nowarn')
6642 6658 ctx2 = logcmdutil.revsingle(repo, change, None)
6643 6659 ctx1 = ctx2.p1()
6644 6660 else:
6645 6661 repo = scmutil.unhidehashlikerevs(repo, revs, b'nowarn')
6646 6662 ctx1, ctx2 = logcmdutil.revpair(repo, revs)
6647 6663
6648 6664 forcerelativevalue = None
6649 6665 if ui.hasconfig(b'commands', b'status.relative'):
6650 6666 forcerelativevalue = ui.configbool(b'commands', b'status.relative')
6651 6667 uipathfn = scmutil.getuipathfn(
6652 6668 repo,
6653 6669 legacyrelativevalue=bool(pats),
6654 6670 forcerelativevalue=forcerelativevalue,
6655 6671 )
6656 6672
6657 6673 if opts.get(b'print0'):
6658 6674 end = b'\0'
6659 6675 else:
6660 6676 end = b'\n'
6661 6677 states = b'modified added removed deleted unknown ignored clean'.split()
6662 6678 show = [k for k in states if opts.get(k)]
6663 6679 if opts.get(b'all'):
6664 6680 show += ui.quiet and (states[:4] + [b'clean']) or states
6665 6681
6666 6682 if not show:
6667 6683 if ui.quiet:
6668 6684 show = states[:4]
6669 6685 else:
6670 6686 show = states[:5]
6671 6687
6672 6688 m = scmutil.match(ctx2, pats, opts)
6673 6689 if terse:
6674 6690 # we need to compute clean and unknown to terse
6675 6691 stat = repo.status(
6676 6692 ctx1.node(),
6677 6693 ctx2.node(),
6678 6694 m,
6679 6695 b'ignored' in show or b'i' in terse,
6680 6696 clean=True,
6681 6697 unknown=True,
6682 6698 listsubrepos=opts.get(b'subrepos'),
6683 6699 )
6684 6700
6685 6701 stat = cmdutil.tersedir(stat, terse)
6686 6702 else:
6687 6703 stat = repo.status(
6688 6704 ctx1.node(),
6689 6705 ctx2.node(),
6690 6706 m,
6691 6707 b'ignored' in show,
6692 6708 b'clean' in show,
6693 6709 b'unknown' in show,
6694 6710 opts.get(b'subrepos'),
6695 6711 )
6696 6712
6697 6713 changestates = zip(
6698 6714 states,
6699 6715 pycompat.iterbytestr(b'MAR!?IC'),
6700 6716 [getattr(stat, s.decode('utf8')) for s in states],
6701 6717 )
6702 6718
6703 6719 copy = {}
6704 6720 show_copies = ui.configbool(b'ui', b'statuscopies')
6705 6721 if opts.get(b'copies') is not None:
6706 6722 show_copies = opts.get(b'copies')
6707 6723 show_copies = (show_copies or opts.get(b'all')) and not opts.get(
6708 6724 b'no_status'
6709 6725 )
6710 6726 if show_copies:
6711 6727 copy = copies.pathcopies(ctx1, ctx2, m)
6712 6728
6713 6729 morestatus = None
6714 6730 if (
6715 6731 (ui.verbose or ui.configbool(b'commands', b'status.verbose'))
6716 6732 and not ui.plain()
6717 6733 and not opts.get(b'print0')
6718 6734 ):
6719 6735 morestatus = cmdutil.readmorestatus(repo)
6720 6736
6721 6737 ui.pager(b'status')
6722 6738 fm = ui.formatter(b'status', opts)
6723 6739 fmt = b'%s' + end
6724 6740 showchar = not opts.get(b'no_status')
6725 6741
6726 6742 for state, char, files in changestates:
6727 6743 if state in show:
6728 6744 label = b'status.' + state
6729 6745 for f in files:
6730 6746 fm.startitem()
6731 6747 fm.context(ctx=ctx2)
6732 6748 fm.data(itemtype=b'file', path=f)
6733 6749 fm.condwrite(showchar, b'status', b'%s ', char, label=label)
6734 6750 fm.plain(fmt % uipathfn(f), label=label)
6735 6751 if f in copy:
6736 6752 fm.data(source=copy[f])
6737 6753 fm.plain(
6738 6754 (b' %s' + end) % uipathfn(copy[f]),
6739 6755 label=b'status.copied',
6740 6756 )
6741 6757 if morestatus:
6742 6758 morestatus.formatfile(f, fm)
6743 6759
6744 6760 if morestatus:
6745 6761 morestatus.formatfooter(fm)
6746 6762 fm.end()
6747 6763
6748 6764
6749 6765 @command(
6750 6766 b'summary|sum',
6751 6767 [(b'', b'remote', None, _(b'check for push and pull'))],
6752 6768 b'[--remote]',
6753 6769 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
6754 6770 helpbasic=True,
6755 6771 intents={INTENT_READONLY},
6756 6772 )
6757 6773 def summary(ui, repo, **opts):
6758 6774 """summarize working directory state
6759 6775
6760 6776 This generates a brief summary of the working directory state,
6761 6777 including parents, branch, commit status, phase and available updates.
6762 6778
6763 6779 With the --remote option, this will check the default paths for
6764 6780 incoming and outgoing changes. This can be time-consuming.
6765 6781
6766 6782 Returns 0 on success.
6767 6783 """
6768 6784
6769 6785 ui.pager(b'summary')
6770 6786 ctx = repo[None]
6771 6787 parents = ctx.parents()
6772 6788 pnode = parents[0].node()
6773 6789 marks = []
6774 6790
6775 6791 try:
6776 6792 ms = mergestatemod.mergestate.read(repo)
6777 6793 except error.UnsupportedMergeRecords as e:
6778 6794 s = b' '.join(e.recordtypes)
6779 6795 ui.warn(
6780 6796 _(b'warning: merge state has unsupported record types: %s\n') % s
6781 6797 )
6782 6798 unresolved = []
6783 6799 else:
6784 6800 unresolved = list(ms.unresolved())
6785 6801
6786 6802 for p in parents:
6787 6803 # label with log.changeset (instead of log.parent) since this
6788 6804 # shows a working directory parent *changeset*:
6789 6805 # i18n: column positioning for "hg summary"
6790 6806 ui.write(
6791 6807 _(b'parent: %d:%s ') % (p.rev(), p),
6792 6808 label=logcmdutil.changesetlabels(p),
6793 6809 )
6794 6810 ui.write(b' '.join(p.tags()), label=b'log.tag')
6795 6811 if p.bookmarks():
6796 6812 marks.extend(p.bookmarks())
6797 6813 if p.rev() == -1:
6798 6814 if not len(repo):
6799 6815 ui.write(_(b' (empty repository)'))
6800 6816 else:
6801 6817 ui.write(_(b' (no revision checked out)'))
6802 6818 if p.obsolete():
6803 6819 ui.write(_(b' (obsolete)'))
6804 6820 if p.isunstable():
6805 6821 instabilities = (
6806 6822 ui.label(instability, b'trouble.%s' % instability)
6807 6823 for instability in p.instabilities()
6808 6824 )
6809 6825 ui.write(b' (' + b', '.join(instabilities) + b')')
6810 6826 ui.write(b'\n')
6811 6827 if p.description():
6812 6828 ui.status(
6813 6829 b' ' + p.description().splitlines()[0].strip() + b'\n',
6814 6830 label=b'log.summary',
6815 6831 )
6816 6832
6817 6833 branch = ctx.branch()
6818 6834 bheads = repo.branchheads(branch)
6819 6835 # i18n: column positioning for "hg summary"
6820 6836 m = _(b'branch: %s\n') % branch
6821 6837 if branch != b'default':
6822 6838 ui.write(m, label=b'log.branch')
6823 6839 else:
6824 6840 ui.status(m, label=b'log.branch')
6825 6841
6826 6842 if marks:
6827 6843 active = repo._activebookmark
6828 6844 # i18n: column positioning for "hg summary"
6829 6845 ui.write(_(b'bookmarks:'), label=b'log.bookmark')
6830 6846 if active is not None:
6831 6847 if active in marks:
6832 6848 ui.write(b' *' + active, label=bookmarks.activebookmarklabel)
6833 6849 marks.remove(active)
6834 6850 else:
6835 6851 ui.write(b' [%s]' % active, label=bookmarks.activebookmarklabel)
6836 6852 for m in marks:
6837 6853 ui.write(b' ' + m, label=b'log.bookmark')
6838 6854 ui.write(b'\n', label=b'log.bookmark')
6839 6855
6840 6856 status = repo.status(unknown=True)
6841 6857
6842 6858 c = repo.dirstate.copies()
6843 6859 copied, renamed = [], []
6844 6860 for d, s in c.items():
6845 6861 if s in status.removed:
6846 6862 status.removed.remove(s)
6847 6863 renamed.append(d)
6848 6864 else:
6849 6865 copied.append(d)
6850 6866 if d in status.added:
6851 6867 status.added.remove(d)
6852 6868
6853 6869 subs = [s for s in ctx.substate if ctx.sub(s).dirty()]
6854 6870
6855 6871 labels = [
6856 6872 (ui.label(_(b'%d modified'), b'status.modified'), status.modified),
6857 6873 (ui.label(_(b'%d added'), b'status.added'), status.added),
6858 6874 (ui.label(_(b'%d removed'), b'status.removed'), status.removed),
6859 6875 (ui.label(_(b'%d renamed'), b'status.copied'), renamed),
6860 6876 (ui.label(_(b'%d copied'), b'status.copied'), copied),
6861 6877 (ui.label(_(b'%d deleted'), b'status.deleted'), status.deleted),
6862 6878 (ui.label(_(b'%d unknown'), b'status.unknown'), status.unknown),
6863 6879 (ui.label(_(b'%d unresolved'), b'resolve.unresolved'), unresolved),
6864 6880 (ui.label(_(b'%d subrepos'), b'status.modified'), subs),
6865 6881 ]
6866 6882 t = []
6867 6883 for l, s in labels:
6868 6884 if s:
6869 6885 t.append(l % len(s))
6870 6886
6871 6887 t = b', '.join(t)
6872 6888 cleanworkdir = False
6873 6889
6874 6890 if repo.vfs.exists(b'graftstate'):
6875 6891 t += _(b' (graft in progress)')
6876 6892 if repo.vfs.exists(b'updatestate'):
6877 6893 t += _(b' (interrupted update)')
6878 6894 elif len(parents) > 1:
6879 6895 t += _(b' (merge)')
6880 6896 elif branch != parents[0].branch():
6881 6897 t += _(b' (new branch)')
6882 6898 elif parents[0].closesbranch() and pnode in repo.branchheads(
6883 6899 branch, closed=True
6884 6900 ):
6885 6901 t += _(b' (head closed)')
6886 6902 elif not (
6887 6903 status.modified
6888 6904 or status.added
6889 6905 or status.removed
6890 6906 or renamed
6891 6907 or copied
6892 6908 or subs
6893 6909 ):
6894 6910 t += _(b' (clean)')
6895 6911 cleanworkdir = True
6896 6912 elif pnode not in bheads:
6897 6913 t += _(b' (new branch head)')
6898 6914
6899 6915 if parents:
6900 6916 pendingphase = max(p.phase() for p in parents)
6901 6917 else:
6902 6918 pendingphase = phases.public
6903 6919
6904 6920 if pendingphase > phases.newcommitphase(ui):
6905 6921 t += b' (%s)' % phases.phasenames[pendingphase]
6906 6922
6907 6923 if cleanworkdir:
6908 6924 # i18n: column positioning for "hg summary"
6909 6925 ui.status(_(b'commit: %s\n') % t.strip())
6910 6926 else:
6911 6927 # i18n: column positioning for "hg summary"
6912 6928 ui.write(_(b'commit: %s\n') % t.strip())
6913 6929
6914 6930 # all ancestors of branch heads - all ancestors of parent = new csets
6915 6931 new = len(
6916 6932 repo.changelog.findmissing([pctx.node() for pctx in parents], bheads)
6917 6933 )
6918 6934
6919 6935 if new == 0:
6920 6936 # i18n: column positioning for "hg summary"
6921 6937 ui.status(_(b'update: (current)\n'))
6922 6938 elif pnode not in bheads:
6923 6939 # i18n: column positioning for "hg summary"
6924 6940 ui.write(_(b'update: %d new changesets (update)\n') % new)
6925 6941 else:
6926 6942 # i18n: column positioning for "hg summary"
6927 6943 ui.write(
6928 6944 _(b'update: %d new changesets, %d branch heads (merge)\n')
6929 6945 % (new, len(bheads))
6930 6946 )
6931 6947
6932 6948 t = []
6933 6949 draft = len(repo.revs(b'draft()'))
6934 6950 if draft:
6935 6951 t.append(_(b'%d draft') % draft)
6936 6952 secret = len(repo.revs(b'secret()'))
6937 6953 if secret:
6938 6954 t.append(_(b'%d secret') % secret)
6939 6955
6940 6956 if draft or secret:
6941 6957 ui.status(_(b'phases: %s\n') % b', '.join(t))
6942 6958
6943 6959 if obsolete.isenabled(repo, obsolete.createmarkersopt):
6944 6960 for trouble in (b"orphan", b"contentdivergent", b"phasedivergent"):
6945 6961 numtrouble = len(repo.revs(trouble + b"()"))
6946 6962 # We write all the possibilities to ease translation
6947 6963 troublemsg = {
6948 6964 b"orphan": _(b"orphan: %d changesets"),
6949 6965 b"contentdivergent": _(b"content-divergent: %d changesets"),
6950 6966 b"phasedivergent": _(b"phase-divergent: %d changesets"),
6951 6967 }
6952 6968 if numtrouble > 0:
6953 6969 ui.status(troublemsg[trouble] % numtrouble + b"\n")
6954 6970
6955 6971 cmdutil.summaryhooks(ui, repo)
6956 6972
6957 6973 if opts.get('remote'):
6958 6974 needsincoming, needsoutgoing = True, True
6959 6975 else:
6960 6976 needsincoming, needsoutgoing = False, False
6961 6977 for i, o in cmdutil.summaryremotehooks(
6962 6978 ui, repo, pycompat.byteskwargs(opts), None
6963 6979 ):
6964 6980 if i:
6965 6981 needsincoming = True
6966 6982 if o:
6967 6983 needsoutgoing = True
6968 6984 if not needsincoming and not needsoutgoing:
6969 6985 return
6970 6986
6971 6987 def getincoming():
6972 6988 # XXX We should actually skip this if no default is specified, instead
6973 6989 # of passing "default" which will resolve as "./default/" if no default
6974 6990 # path is defined.
6975 6991 path = urlutil.get_unique_pull_path_obj(b'summary', ui, b'default')
6976 6992 sbranch = path.branch
6977 6993 try:
6978 6994 other = hg.peer(repo, {}, path)
6979 6995 except error.RepoError:
6980 6996 if opts.get('remote'):
6981 6997 raise
6982 6998 return path.loc, sbranch, None, None, None
6983 6999 branches = (path.branch, [])
6984 7000 revs, checkout = hg.addbranchrevs(repo, other, branches, None)
6985 7001 if revs:
6986 7002 revs = [other.lookup(rev) for rev in revs]
6987 7003 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(path.loc))
6988 7004 with repo.ui.silent():
6989 7005 commoninc = discovery.findcommonincoming(repo, other, heads=revs)
6990 7006 return path.loc, sbranch, other, commoninc, commoninc[1]
6991 7007
6992 7008 if needsincoming:
6993 7009 source, sbranch, sother, commoninc, incoming = getincoming()
6994 7010 else:
6995 7011 source = sbranch = sother = commoninc = incoming = None
6996 7012
6997 7013 def getoutgoing():
6998 7014 # XXX We should actually skip this if no default is specified, instead
6999 7015 # of passing "default" which will resolve as "./default/" if no default
7000 7016 # path is defined.
7001 7017 d = None
7002 7018 if b'default-push' in ui.paths:
7003 7019 d = b'default-push'
7004 7020 elif b'default' in ui.paths:
7005 7021 d = b'default'
7006 7022 path = None
7007 7023 if d is not None:
7008 7024 path = urlutil.get_unique_push_path(b'summary', repo, ui, d)
7009 7025 dest = path.loc
7010 7026 dbranch = path.branch
7011 7027 else:
7012 7028 dest = b'default'
7013 7029 dbranch = None
7014 7030 revs, checkout = hg.addbranchrevs(repo, repo, (dbranch, []), None)
7015 7031 if source != dest:
7016 7032 try:
7017 7033 dother = hg.peer(repo, {}, path if path is not None else dest)
7018 7034 except error.RepoError:
7019 7035 if opts.get('remote'):
7020 7036 raise
7021 7037 return dest, dbranch, None, None
7022 7038 ui.debug(b'comparing with %s\n' % urlutil.hidepassword(dest))
7023 7039 elif sother is None:
7024 7040 # there is no explicit destination peer, but source one is invalid
7025 7041 return dest, dbranch, None, None
7026 7042 else:
7027 7043 dother = sother
7028 7044 if source != dest or (sbranch is not None and sbranch != dbranch):
7029 7045 common = None
7030 7046 else:
7031 7047 common = commoninc
7032 7048 if revs:
7033 7049 revs = [repo.lookup(rev) for rev in revs]
7034 7050 with repo.ui.silent():
7035 7051 outgoing = discovery.findcommonoutgoing(
7036 7052 repo, dother, onlyheads=revs, commoninc=common
7037 7053 )
7038 7054 return dest, dbranch, dother, outgoing
7039 7055
7040 7056 if needsoutgoing:
7041 7057 dest, dbranch, dother, outgoing = getoutgoing()
7042 7058 else:
7043 7059 dest = dbranch = dother = outgoing = None
7044 7060
7045 7061 if opts.get('remote'):
7046 7062 # Help pytype. --remote sets both `needsincoming` and `needsoutgoing`.
7047 7063 # The former always sets `sother` (or raises an exception if it can't);
7048 7064 # the latter always sets `outgoing`.
7049 7065 assert sother is not None
7050 7066 assert outgoing is not None
7051 7067
7052 7068 t = []
7053 7069 if incoming:
7054 7070 t.append(_(b'1 or more incoming'))
7055 7071 o = outgoing.missing
7056 7072 if o:
7057 7073 t.append(_(b'%d outgoing') % len(o))
7058 7074 other = dother or sother
7059 7075 if b'bookmarks' in other.listkeys(b'namespaces'):
7060 7076 counts = bookmarks.summary(repo, other)
7061 7077 if counts[0] > 0:
7062 7078 t.append(_(b'%d incoming bookmarks') % counts[0])
7063 7079 if counts[1] > 0:
7064 7080 t.append(_(b'%d outgoing bookmarks') % counts[1])
7065 7081
7066 7082 if t:
7067 7083 # i18n: column positioning for "hg summary"
7068 7084 ui.write(_(b'remote: %s\n') % (b', '.join(t)))
7069 7085 else:
7070 7086 # i18n: column positioning for "hg summary"
7071 7087 ui.status(_(b'remote: (synced)\n'))
7072 7088
7073 7089 cmdutil.summaryremotehooks(
7074 7090 ui,
7075 7091 repo,
7076 7092 pycompat.byteskwargs(opts),
7077 7093 (
7078 7094 (source, sbranch, sother, commoninc),
7079 7095 (dest, dbranch, dother, outgoing),
7080 7096 ),
7081 7097 )
7082 7098
7083 7099
7084 7100 @command(
7085 7101 b'tag',
7086 7102 [
7087 7103 (b'f', b'force', None, _(b'force tag')),
7088 7104 (b'l', b'local', None, _(b'make the tag local')),
7089 7105 (b'r', b'rev', b'', _(b'revision to tag'), _(b'REV')),
7090 7106 (b'', b'remove', None, _(b'remove a tag')),
7091 7107 # -l/--local is already there, commitopts cannot be used
7092 7108 (b'e', b'edit', None, _(b'invoke editor on commit messages')),
7093 7109 (b'm', b'message', b'', _(b'use text as commit message'), _(b'TEXT')),
7094 7110 ]
7095 7111 + commitopts2,
7096 7112 _(b'[-f] [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...'),
7097 7113 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7098 7114 )
7099 7115 def tag(ui, repo, name1, *names, **opts):
7100 7116 """add one or more tags for the current or given revision
7101 7117
7102 7118 Name a particular revision using <name>.
7103 7119
7104 7120 Tags are used to name particular revisions of the repository and are
7105 7121 very useful to compare different revisions, to go back to significant
7106 7122 earlier versions or to mark branch points as releases, etc. Changing
7107 7123 an existing tag is normally disallowed; use -f/--force to override.
7108 7124
7109 7125 If no revision is given, the parent of the working directory is
7110 7126 used.
7111 7127
7112 7128 To facilitate version control, distribution, and merging of tags,
7113 7129 they are stored as a file named ".hgtags" which is managed similarly
7114 7130 to other project files and can be hand-edited if necessary. This
7115 7131 also means that tagging creates a new commit. The file
7116 7132 ".hg/localtags" is used for local tags (not shared among
7117 7133 repositories).
7118 7134
7119 7135 Tag commits are usually made at the head of a branch. If the parent
7120 7136 of the working directory is not a branch head, :hg:`tag` aborts; use
7121 7137 -f/--force to force the tag commit to be based on a non-head
7122 7138 changeset.
7123 7139
7124 7140 See :hg:`help dates` for a list of formats valid for -d/--date.
7125 7141
7126 7142 Since tag names have priority over branch names during revision
7127 7143 lookup, using an existing branch name as a tag name is discouraged.
7128 7144
7129 7145 Returns 0 on success.
7130 7146 """
7131 7147 cmdutil.check_incompatible_arguments(opts, 'remove', ['rev'])
7132 7148
7133 7149 with repo.wlock(), repo.lock():
7134 7150 rev_ = b"."
7135 7151 names = [t.strip() for t in (name1,) + names]
7136 7152 if len(names) != len(set(names)):
7137 7153 raise error.InputError(_(b'tag names must be unique'))
7138 7154 for n in names:
7139 7155 scmutil.checknewlabel(repo, n, b'tag')
7140 7156 if not n:
7141 7157 raise error.InputError(
7142 7158 _(b'tag names cannot consist entirely of whitespace')
7143 7159 )
7144 7160 if opts.get('rev'):
7145 7161 rev_ = opts['rev']
7146 7162 message = opts.get('message')
7147 7163 if opts.get('remove'):
7148 7164 if opts.get('local'):
7149 7165 expectedtype = b'local'
7150 7166 else:
7151 7167 expectedtype = b'global'
7152 7168
7153 7169 for n in names:
7154 7170 if repo.tagtype(n) == b'global':
7155 7171 alltags = tagsmod.findglobaltags(ui, repo)
7156 7172 if alltags[n][0] == repo.nullid:
7157 7173 raise error.InputError(
7158 7174 _(b"tag '%s' is already removed") % n
7159 7175 )
7160 7176 if not repo.tagtype(n):
7161 7177 raise error.InputError(_(b"tag '%s' does not exist") % n)
7162 7178 if repo.tagtype(n) != expectedtype:
7163 7179 if expectedtype == b'global':
7164 7180 raise error.InputError(
7165 7181 _(b"tag '%s' is not a global tag") % n
7166 7182 )
7167 7183 else:
7168 7184 raise error.InputError(
7169 7185 _(b"tag '%s' is not a local tag") % n
7170 7186 )
7171 7187 rev_ = b'null'
7172 7188 if not message:
7173 7189 # we don't translate commit messages
7174 7190 message = b'Removed tag %s' % b', '.join(names)
7175 7191 elif not opts.get('force'):
7176 7192 for n in names:
7177 7193 if n in repo.tags():
7178 7194 raise error.InputError(
7179 7195 _(b"tag '%s' already exists (use -f to force)") % n
7180 7196 )
7181 7197 if not opts.get('local'):
7182 7198 p1, p2 = repo.dirstate.parents()
7183 7199 if p2 != repo.nullid:
7184 7200 raise error.StateError(_(b'uncommitted merge'))
7185 7201 bheads = repo.branchheads()
7186 7202 if not opts.get('force') and bheads and p1 not in bheads:
7187 7203 raise error.InputError(
7188 7204 _(
7189 7205 b'working directory is not at a branch head '
7190 7206 b'(use -f to force)'
7191 7207 )
7192 7208 )
7193 7209 node = logcmdutil.revsingle(repo, rev_).node()
7194 7210
7195 7211 # don't allow tagging the null rev or the working directory
7196 7212 if node is None:
7197 7213 raise error.InputError(_(b"cannot tag working directory"))
7198 7214 elif not opts.get('remove') and node == nullid:
7199 7215 raise error.InputError(_(b"cannot tag null revision"))
7200 7216
7201 7217 if not message:
7202 7218 # we don't translate commit messages
7203 7219 message = b'Added tag %s for changeset %s' % (
7204 7220 b', '.join(names),
7205 7221 short(node),
7206 7222 )
7207 7223
7208 7224 date = opts.get('date')
7209 7225 if date:
7210 7226 date = dateutil.parsedate(date)
7211 7227
7212 7228 if opts.get('remove'):
7213 7229 editform = b'tag.remove'
7214 7230 else:
7215 7231 editform = b'tag.add'
7216 7232 editor = cmdutil.getcommiteditor(editform=editform, **opts)
7217 7233
7218 7234 tagsmod.tag(
7219 7235 repo,
7220 7236 names,
7221 7237 node,
7222 7238 message,
7223 7239 opts.get('local'),
7224 7240 opts.get('user'),
7225 7241 date,
7226 7242 editor=editor,
7227 7243 )
7228 7244
7229 7245
7230 7246 @command(
7231 7247 b'tags',
7232 7248 formatteropts,
7233 7249 b'',
7234 7250 helpcategory=command.CATEGORY_CHANGE_ORGANIZATION,
7235 7251 intents={INTENT_READONLY},
7236 7252 )
7237 7253 def tags(ui, repo, **opts):
7238 7254 """list repository tags
7239 7255
7240 7256 This lists both regular and local tags. When the -v/--verbose
7241 7257 switch is used, a third column "local" is printed for local tags.
7242 7258 When the -q/--quiet switch is used, only the tag name is printed.
7243 7259
7244 7260 .. container:: verbose
7245 7261
7246 7262 Template:
7247 7263
7248 7264 The following keywords are supported in addition to the common template
7249 7265 keywords and functions such as ``{tag}``. See also
7250 7266 :hg:`help templates`.
7251 7267
7252 7268 :type: String. ``local`` for local tags.
7253 7269
7254 7270 Returns 0 on success.
7255 7271 """
7256 7272
7257 7273 ui.pager(b'tags')
7258 7274 fm = ui.formatter(b'tags', pycompat.byteskwargs(opts))
7259 7275 hexfunc = fm.hexfunc
7260 7276
7261 7277 for t, n in reversed(repo.tagslist()):
7262 7278 hn = hexfunc(n)
7263 7279 label = b'tags.normal'
7264 7280 tagtype = repo.tagtype(t)
7265 7281 if not tagtype or tagtype == b'global':
7266 7282 tagtype = b''
7267 7283 else:
7268 7284 label = b'tags.' + tagtype
7269 7285
7270 7286 fm.startitem()
7271 7287 fm.context(repo=repo)
7272 7288 fm.write(b'tag', b'%s', t, label=label)
7273 7289 fmt = b" " * (30 - encoding.colwidth(t)) + b' %5d:%s'
7274 7290 fm.condwrite(
7275 7291 not ui.quiet,
7276 7292 b'rev node',
7277 7293 fmt,
7278 7294 repo.changelog.rev(n),
7279 7295 hn,
7280 7296 label=label,
7281 7297 )
7282 7298 fm.condwrite(
7283 7299 ui.verbose and tagtype, b'type', b' %s', tagtype, label=label
7284 7300 )
7285 7301 fm.plain(b'\n')
7286 7302 fm.end()
7287 7303
7288 7304
7289 7305 @command(
7290 7306 b'tip',
7291 7307 [
7292 7308 (b'p', b'patch', None, _(b'show patch')),
7293 7309 (b'g', b'git', None, _(b'use git extended diff format')),
7294 7310 ]
7295 7311 + templateopts,
7296 7312 _(b'[-p] [-g]'),
7297 7313 helpcategory=command.CATEGORY_CHANGE_NAVIGATION,
7298 7314 )
7299 7315 def tip(ui, repo, **opts):
7300 7316 """show the tip revision (DEPRECATED)
7301 7317
7302 7318 The tip revision (usually just called the tip) is the changeset
7303 7319 most recently added to the repository (and therefore the most
7304 7320 recently changed head).
7305 7321
7306 7322 If you have just made a commit, that commit will be the tip. If
7307 7323 you have just pulled changes from another repository, the tip of
7308 7324 that repository becomes the current tip. The "tip" tag is special
7309 7325 and cannot be renamed or assigned to a different changeset.
7310 7326
7311 7327 This command is deprecated, please use :hg:`heads` instead.
7312 7328
7313 7329 Returns 0 on success.
7314 7330 """
7315 7331 opts = pycompat.byteskwargs(opts)
7316 7332 displayer = logcmdutil.changesetdisplayer(ui, repo, opts)
7317 7333 displayer.show(repo[b'tip'])
7318 7334 displayer.close()
7319 7335
7320 7336
7321 7337 @command(
7322 7338 b'unbundle',
7323 7339 [
7324 7340 (
7325 7341 b'u',
7326 7342 b'update',
7327 7343 None,
7328 7344 _(b'update to new branch head if changesets were unbundled'),
7329 7345 )
7330 7346 ],
7331 7347 _(b'[-u] FILE...'),
7332 7348 helpcategory=command.CATEGORY_IMPORT_EXPORT,
7333 7349 )
7334 7350 def unbundle(ui, repo, fname1, *fnames, **opts):
7335 7351 """apply one or more bundle files
7336 7352
7337 7353 Apply one or more bundle files generated by :hg:`bundle`.
7338 7354
7339 7355 Returns 0 on success, 1 if an update has unresolved files.
7340 7356 """
7341 7357 fnames = (fname1,) + fnames
7342 7358 modheads = cmdutil.unbundle_files(ui, repo, fnames)
7343 7359
7344 7360 if cmdutil.postincoming(ui, repo, modheads, opts.get('update'), None, None):
7345 7361 return 1
7346 7362 else:
7347 7363 return 0
7348 7364
7349 7365
7350 7366 @command(
7351 7367 b'unshelve',
7352 7368 [
7353 7369 (b'a', b'abort', None, _(b'abort an incomplete unshelve operation')),
7354 7370 (
7355 7371 b'c',
7356 7372 b'continue',
7357 7373 None,
7358 7374 _(b'continue an incomplete unshelve operation'),
7359 7375 ),
7360 7376 (b'i', b'interactive', None, _(b'use interactive mode (EXPERIMENTAL)')),
7361 7377 (b'k', b'keep', None, _(b'keep shelve after unshelving')),
7362 7378 (
7363 7379 b'n',
7364 7380 b'name',
7365 7381 b'',
7366 7382 _(b'restore shelved change with given name'),
7367 7383 _(b'NAME'),
7368 7384 ),
7369 7385 (b't', b'tool', b'', _(b'specify merge tool')),
7370 7386 (
7371 7387 b'',
7372 7388 b'date',
7373 7389 b'',
7374 7390 _(b'set date for temporary commits (DEPRECATED)'),
7375 7391 _(b'DATE'),
7376 7392 ),
7377 7393 ],
7378 7394 _(b'hg unshelve [OPTION]... [[-n] SHELVED]'),
7379 7395 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7380 7396 )
7381 7397 def unshelve(ui, repo, *shelved, **opts):
7382 7398 """restore a shelved change to the working directory
7383 7399
7384 7400 This command accepts an optional name of a shelved change to
7385 7401 restore. If none is given, the most recent shelved change is used.
7386 7402
7387 7403 If a shelved change is applied successfully, the bundle that
7388 7404 contains the shelved changes is moved to a backup location
7389 7405 (.hg/shelve-backup).
7390 7406
7391 7407 Since you can restore a shelved change on top of an arbitrary
7392 7408 commit, it is possible that unshelving will result in a conflict
7393 7409 between your changes and the commits you are unshelving onto. If
7394 7410 this occurs, you must resolve the conflict, then use
7395 7411 ``--continue`` to complete the unshelve operation. (The bundle
7396 7412 will not be moved until you successfully complete the unshelve.)
7397 7413
7398 7414 (Alternatively, you can use ``--abort`` to abandon an unshelve
7399 7415 that causes a conflict. This reverts the unshelved changes, and
7400 7416 leaves the bundle in place.)
7401 7417
7402 7418 If bare shelved change (without interactive, include and exclude
7403 7419 option) was done on newly created branch it would restore branch
7404 7420 information to the working directory.
7405 7421
7406 7422 After a successful unshelve, the shelved changes are stored in a
7407 7423 backup directory. Only the N most recent backups are kept. N
7408 7424 defaults to 10 but can be overridden using the ``shelve.maxbackups``
7409 7425 configuration option.
7410 7426
7411 7427 .. container:: verbose
7412 7428
7413 7429 Timestamp in seconds is used to decide order of backups. More
7414 7430 than ``maxbackups`` backups are kept, if same timestamp
7415 7431 prevents from deciding exact order of them, for safety.
7416 7432
7417 7433 Selected changes can be unshelved with ``--interactive`` flag.
7418 7434 The working directory is updated with the selected changes, and
7419 7435 only the unselected changes remain shelved.
7420 7436 Note: The whole shelve is applied to working directory first before
7421 7437 running interactively. So, this will bring up all the conflicts between
7422 7438 working directory and the shelve, irrespective of which changes will be
7423 7439 unshelved.
7424 7440 """
7425 7441 with repo.wlock():
7426 7442 return shelvemod.unshelvecmd(ui, repo, *shelved, **opts)
7427 7443
7428 7444
7429 7445 statemod.addunfinished(
7430 7446 b'unshelve',
7431 7447 fname=b'shelvedstate',
7432 7448 continueflag=True,
7433 7449 abortfunc=shelvemod.hgabortunshelve,
7434 7450 continuefunc=shelvemod.hgcontinueunshelve,
7435 7451 cmdmsg=_(b'unshelve already in progress'),
7436 7452 )
7437 7453
7438 7454
7439 7455 @command(
7440 7456 b'update|up|checkout|co',
7441 7457 [
7442 7458 (b'C', b'clean', None, _(b'discard uncommitted changes (no backup)')),
7443 7459 (b'c', b'check', None, _(b'require clean working directory')),
7444 7460 (b'm', b'merge', None, _(b'merge uncommitted changes')),
7445 7461 (b'd', b'date', b'', _(b'tipmost revision matching date'), _(b'DATE')),
7446 7462 (b'r', b'rev', b'', _(b'revision'), _(b'REV')),
7447 7463 ]
7448 7464 + mergetoolopts,
7449 7465 _(b'[-C|-c|-m] [-d DATE] [[-r] REV]'),
7450 7466 helpcategory=command.CATEGORY_WORKING_DIRECTORY,
7451 7467 helpbasic=True,
7452 7468 )
7453 7469 def update(ui, repo, node=None, **opts):
7454 7470 """update working directory (or switch revisions)
7455 7471
7456 7472 Update the repository's working directory to the specified
7457 7473 changeset. If no changeset is specified, update to the tip of the
7458 7474 current named branch and move the active bookmark (see :hg:`help
7459 7475 bookmarks`).
7460 7476
7461 7477 Update sets the working directory's parent revision to the specified
7462 7478 changeset (see :hg:`help parents`).
7463 7479
7464 7480 If the changeset is not a descendant or ancestor of the working
7465 7481 directory's parent and there are uncommitted changes, the update is
7466 7482 aborted. With the -c/--check option, the working directory is checked
7467 7483 for uncommitted changes; if none are found, the working directory is
7468 7484 updated to the specified changeset.
7469 7485
7470 7486 .. container:: verbose
7471 7487
7472 7488 The -C/--clean, -c/--check, and -m/--merge options control what
7473 7489 happens if the working directory contains uncommitted changes.
7474 7490 At most of one of them can be specified.
7475 7491
7476 7492 1. If no option is specified, and if
7477 7493 the requested changeset is an ancestor or descendant of
7478 7494 the working directory's parent, the uncommitted changes
7479 7495 are merged into the requested changeset and the merged
7480 7496 result is left uncommitted. If the requested changeset is
7481 7497 not an ancestor or descendant (that is, it is on another
7482 7498 branch), the update is aborted and the uncommitted changes
7483 7499 are preserved.
7484 7500
7485 7501 2. With the -m/--merge option, the update is allowed even if the
7486 7502 requested changeset is not an ancestor or descendant of
7487 7503 the working directory's parent.
7488 7504
7489 7505 3. With the -c/--check option, the update is aborted and the
7490 7506 uncommitted changes are preserved.
7491 7507
7492 7508 4. With the -C/--clean option, uncommitted changes are discarded and
7493 7509 the working directory is updated to the requested changeset.
7494 7510
7495 7511 To cancel an uncommitted merge (and lose your changes), use
7496 7512 :hg:`merge --abort`.
7497 7513
7498 7514 Use null as the changeset to remove the working directory (like
7499 7515 :hg:`clone -U`).
7500 7516
7501 7517 If you want to revert just one file to an older revision, use
7502 7518 :hg:`revert [-r REV] NAME`.
7503 7519
7504 7520 See :hg:`help dates` for a list of formats valid for -d/--date.
7505 7521
7506 7522 Returns 0 on success, 1 if there are unresolved files.
7507 7523 """
7508 7524 cmdutil.check_at_most_one_arg(opts, 'clean', 'check', 'merge')
7509 7525 rev = opts.get('rev')
7510 7526 date = opts.get('date')
7511 7527 clean = opts.get('clean')
7512 7528 check = opts.get('check')
7513 7529 merge = opts.get('merge')
7514 7530 if rev and node:
7515 7531 raise error.InputError(_(b"please specify just one revision"))
7516 7532
7517 7533 if ui.configbool(b'commands', b'update.requiredest'):
7518 7534 if not node and not rev and not date:
7519 7535 raise error.InputError(
7520 7536 _(b'you must specify a destination'),
7521 7537 hint=_(b'for example: hg update ".::"'),
7522 7538 )
7523 7539
7524 7540 if rev is None or rev == b'':
7525 7541 rev = node
7526 7542
7527 7543 if date and rev is not None:
7528 7544 raise error.InputError(_(b"you can't specify a revision and a date"))
7529 7545
7530 7546 updatecheck = None
7531 7547 if check or merge is not None and not merge:
7532 7548 updatecheck = b'abort'
7533 7549 elif merge or check is not None and not check:
7534 7550 updatecheck = b'none'
7535 7551
7536 7552 with repo.wlock():
7537 7553 cmdutil.clearunfinished(repo)
7538 7554 if date:
7539 7555 rev = cmdutil.finddate(ui, repo, date)
7540 7556
7541 7557 # if we defined a bookmark, we have to remember the original name
7542 7558 brev = rev
7543 7559 if rev:
7544 7560 repo = scmutil.unhidehashlikerevs(repo, [rev], b'nowarn')
7545 7561 ctx = logcmdutil.revsingle(repo, rev, default=None)
7546 7562 rev = ctx.rev()
7547 7563 hidden = ctx.hidden()
7548 7564 overrides = {(b'ui', b'forcemerge'): opts.get('tool', b'')}
7549 7565 with ui.configoverride(overrides, b'update'):
7550 7566 ret = hg.updatetotally(
7551 7567 ui, repo, rev, brev, clean=clean, updatecheck=updatecheck
7552 7568 )
7553 7569 if hidden:
7554 7570 ctxstr = ctx.hex()[:12]
7555 7571 ui.warn(_(b"updated to hidden changeset %s\n") % ctxstr)
7556 7572
7557 7573 if ctx.obsolete():
7558 7574 obsfatemsg = obsutil._getfilteredreason(repo, ctxstr, ctx)
7559 7575 ui.warn(b"(%s)\n" % obsfatemsg)
7560 7576 return ret
7561 7577
7562 7578
7563 7579 @command(
7564 7580 b'verify',
7565 7581 [(b'', b'full', False, b'perform more checks (EXPERIMENTAL)')],
7566 7582 helpcategory=command.CATEGORY_MAINTENANCE,
7567 7583 )
7568 7584 def verify(ui, repo, **opts):
7569 7585 """verify the integrity of the repository
7570 7586
7571 7587 Verify the integrity of the current repository.
7572 7588
7573 7589 This will perform an extensive check of the repository's
7574 7590 integrity, validating the hashes and checksums of each entry in
7575 7591 the changelog, manifest, and tracked files, as well as the
7576 7592 integrity of their crosslinks and indices.
7577 7593
7578 7594 Please see https://mercurial-scm.org/wiki/RepositoryCorruption
7579 7595 for more information about recovery from corruption of the
7580 7596 repository.
7581 7597
7582 7598 For an alternative UI with a lot more control over the verification
7583 7599 process and better error reporting, try `hg help admin::verify`.
7584 7600
7585 7601 Returns 0 on success, 1 if errors are encountered.
7586 7602 """
7587 7603 level = None
7588 7604 if opts['full']:
7589 7605 level = verifymod.VERIFY_FULL
7590 7606 return hg.verify(repo, level)
7591 7607
7592 7608
7593 7609 @command(
7594 7610 b'version',
7595 7611 [] + formatteropts,
7596 7612 helpcategory=command.CATEGORY_HELP,
7597 7613 norepo=True,
7598 7614 intents={INTENT_READONLY},
7599 7615 )
7600 7616 def version_(ui, **opts):
7601 7617 """output version and copyright information
7602 7618
7603 7619 .. container:: verbose
7604 7620
7605 7621 Template:
7606 7622
7607 7623 The following keywords are supported. See also :hg:`help templates`.
7608 7624
7609 7625 :extensions: List of extensions.
7610 7626 :ver: String. Version number.
7611 7627
7612 7628 And each entry of ``{extensions}`` provides the following sub-keywords
7613 7629 in addition to ``{ver}``.
7614 7630
7615 7631 :bundled: Boolean. True if included in the release.
7616 7632 :name: String. Extension name.
7617 7633 """
7618 7634 if ui.verbose:
7619 7635 ui.pager(b'version')
7620 7636 fm = ui.formatter(b"version", pycompat.byteskwargs(opts))
7621 7637 fm.startitem()
7622 7638 fm.write(
7623 7639 b"ver", _(b"Mercurial Distributed SCM (version %s)\n"), util.version()
7624 7640 )
7625 7641 license = _(
7626 7642 b"(see https://mercurial-scm.org for more information)\n"
7627 7643 b"\nCopyright (C) 2005-2024 Olivia Mackall and others\n"
7628 7644 b"This is free software; see the source for copying conditions. "
7629 7645 b"There is NO\nwarranty; "
7630 7646 b"not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
7631 7647 )
7632 7648 if not ui.quiet:
7633 7649 fm.plain(license)
7634 7650
7635 7651 if ui.verbose:
7636 7652 fm.plain(_(b"\nEnabled extensions:\n\n"))
7637 7653 # format names and versions into columns
7638 7654 names = []
7639 7655 vers = []
7640 7656 isinternals = []
7641 7657 for name, module in sorted(extensions.extensions()):
7642 7658 names.append(name)
7643 7659 vers.append(extensions.moduleversion(module) or None)
7644 7660 isinternals.append(extensions.ismoduleinternal(module))
7645 7661 fn = fm.nested(b"extensions", tmpl=b'{name}\n')
7646 7662 if names:
7647 7663 namefmt = b" %%-%ds " % max(len(n) for n in names)
7648 7664 places = [_(b"external"), _(b"internal")]
7649 7665 for n, v, p in zip(names, vers, isinternals):
7650 7666 fn.startitem()
7651 7667 fn.condwrite(ui.verbose, b"name", namefmt, n)
7652 7668 if ui.verbose:
7653 7669 fn.plain(b"%s " % places[p])
7654 7670 fn.data(bundled=p)
7655 7671 fn.condwrite(ui.verbose and v, b"ver", b"%s", v)
7656 7672 if ui.verbose:
7657 7673 fn.plain(b"\n")
7658 7674 fn.end()
7659 7675 fm.end()
7660 7676
7661 7677
7662 7678 def loadcmdtable(ui, name, cmdtable):
7663 7679 """Load command functions from specified cmdtable"""
7664 7680 overrides = [cmd for cmd in cmdtable if cmd in table]
7665 7681 if overrides:
7666 7682 ui.warn(
7667 7683 _(b"extension '%s' overrides commands: %s\n")
7668 7684 % (name, b" ".join(overrides))
7669 7685 )
7670 7686 table.update(cmdtable)
@@ -1,463 +1,463
1 1 Show all commands except debug commands
2 2 $ hg debugcomplete
3 3 abort
4 4 add
5 5 addremove
6 6 admin::chainsaw-update
7 7 admin::verify
8 8 annotate
9 9 archive
10 10 backout
11 11 bisect
12 12 bookmarks
13 13 branch
14 14 branches
15 15 bundle
16 16 cat
17 17 clone
18 18 commit
19 19 config
20 20 continue
21 21 copy
22 22 diff
23 23 export
24 24 files
25 25 forget
26 26 graft
27 27 grep
28 28 heads
29 29 help
30 30 identify
31 31 import
32 32 incoming
33 33 init
34 34 locate
35 35 log
36 36 manifest
37 37 merge
38 38 outgoing
39 39 parents
40 40 paths
41 41 phase
42 42 pull
43 43 purge
44 44 push
45 45 recover
46 46 remove
47 47 rename
48 48 resolve
49 49 revert
50 50 rollback
51 51 root
52 52 serve
53 53 shelve
54 54 status
55 55 summary
56 56 tag
57 57 tags
58 58 tip
59 59 unbundle
60 60 unshelve
61 61 update
62 62 verify
63 63 version
64 64
65 65 Show all commands that start with "a"
66 66 $ hg debugcomplete a
67 67 abort
68 68 add
69 69 addremove
70 70 admin::chainsaw-update
71 71 admin::verify
72 72 annotate
73 73 archive
74 74
75 75 Do not show debug commands if there are other candidates
76 76 $ hg debugcomplete d
77 77 diff
78 78
79 79 Show debug commands if there are no other candidates
80 80 $ hg debugcomplete debug
81 81 debug-delta-find
82 82 debug-repair-issue6528
83 83 debug-revlog-index
84 84 debug-revlog-stats
85 85 debug::stable-tail-sort
86 86 debug::stable-tail-sort-leaps
87 87 debug::unbundle
88 88 debugancestor
89 89 debugantivirusrunning
90 90 debugapplystreamclonebundle
91 91 debugbackupbundle
92 92 debugbuilddag
93 93 debugbundle
94 94 debugcapabilities
95 95 debugchangedfiles
96 96 debugcheckstate
97 97 debugcolor
98 98 debugcommands
99 99 debugcomplete
100 100 debugconfig
101 101 debugcreatestreamclonebundle
102 102 debugdag
103 103 debugdata
104 104 debugdate
105 105 debugdeltachain
106 106 debugdirstate
107 107 debugdirstateignorepatternshash
108 108 debugdiscovery
109 109 debugdownload
110 110 debugextensions
111 111 debugfileset
112 112 debugformat
113 113 debugfsinfo
114 114 debuggetbundle
115 115 debugignore
116 116 debugindexdot
117 117 debugindexstats
118 118 debuginstall
119 119 debugknown
120 120 debuglabelcomplete
121 121 debuglocks
122 122 debugmanifestfulltextcache
123 123 debugmergestate
124 124 debugnamecomplete
125 125 debugnodemap
126 126 debugobsolete
127 127 debugp1copies
128 128 debugp2copies
129 129 debugpathcomplete
130 130 debugpathcopies
131 131 debugpeer
132 132 debugpickmergetool
133 133 debugpushkey
134 134 debugpvec
135 135 debugrebuilddirstate
136 136 debugrebuildfncache
137 137 debugrename
138 138 debugrequires
139 139 debugrevlog
140 140 debugrevlogindex
141 141 debugrevspec
142 142 debugserve
143 143 debugsetparents
144 144 debugshell
145 145 debugsidedata
146 146 debugssl
147 147 debugstrip
148 148 debugsub
149 149 debugsuccessorssets
150 150 debugtagscache
151 151 debugtemplate
152 152 debuguigetpass
153 153 debuguiprompt
154 154 debugupdatecaches
155 155 debugupgraderepo
156 156 debugwalk
157 157 debugwhyunstable
158 158 debugwireargs
159 159 debugwireproto
160 160
161 161 Do not show the alias of a debug command if there are other candidates
162 162 (this should hide rawcommit)
163 163 $ hg debugcomplete r
164 164 recover
165 165 remove
166 166 rename
167 167 resolve
168 168 revert
169 169 rollback
170 170 root
171 171 Show the alias of a debug command if there are no other candidates
172 172 $ hg debugcomplete rawc
173 173
174 174
175 175 Show the global options
176 176 $ hg debugcomplete --options | sort
177 177 --color
178 178 --config
179 179 --cwd
180 180 --debug
181 181 --debugger
182 182 --encoding
183 183 --encodingmode
184 184 --help
185 185 --hidden
186 186 --noninteractive
187 187 --pager
188 188 --profile
189 189 --quiet
190 190 --repository
191 191 --time
192 192 --traceback
193 193 --verbose
194 194 --version
195 195 -R
196 196 -h
197 197 -q
198 198 -v
199 199 -y
200 200
201 201 Show the options for the "serve" command
202 202 $ hg debugcomplete --options serve | sort
203 203 --accesslog
204 204 --address
205 205 --certificate
206 206 --cmdserver
207 207 --color
208 208 --config
209 209 --cwd
210 210 --daemon
211 211 --daemon-postexec
212 212 --debug
213 213 --debugger
214 214 --encoding
215 215 --encodingmode
216 216 --errorlog
217 217 --help
218 218 --hidden
219 219 --ipv6
220 220 --name
221 221 --noninteractive
222 222 --pager
223 223 --pid-file
224 224 --port
225 225 --prefix
226 226 --print-url
227 227 --profile
228 228 --quiet
229 229 --repository
230 230 --stdio
231 231 --style
232 232 --subrepos
233 233 --templates
234 234 --time
235 235 --traceback
236 236 --verbose
237 237 --version
238 238 --web-conf
239 239 -6
240 240 -A
241 241 -E
242 242 -R
243 243 -S
244 244 -a
245 245 -d
246 246 -h
247 247 -n
248 248 -p
249 249 -q
250 250 -t
251 251 -v
252 252 -y
253 253
254 254 Show an error if we use --options with an ambiguous abbreviation
255 255 $ hg debugcomplete --options s
256 256 hg: command 's' is ambiguous:
257 257 serve shelve showconfig status summary
258 258 [10]
259 259
260 260 Show all commands + options
261 261 $ hg debugcommands
262 262 abort: dry-run
263 263 add: include, exclude, subrepos, dry-run
264 264 addremove: similarity, subrepos, include, exclude, dry-run
265 265 admin::chainsaw-update: purge-unknown, purge-ignored, rev, source, dest, initial-clone-minimal
266 266 admin::verify: check, option
267 267 annotate: rev, follow, no-follow, text, user, file, date, number, changeset, line-number, skip, line-range, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, include, exclude, template
268 268 archive: no-decode, prefix, rev, type, subrepos, include, exclude
269 269 backout: merge, commit, no-commit, parent, rev, edit, tool, include, exclude, message, logfile, date, user
270 270 bisect: reset, good, bad, skip, extend, command, noupdate
271 271 bookmarks: force, rev, delete, rename, inactive, list, template
272 272 branch: force, clean, rev
273 273 branches: active, closed, rev, template
274 274 bundle: exact, force, rev, branch, base, all, type, ssh, remotecmd, insecure
275 275 cat: output, rev, decode, include, exclude, template
276 276 clone: noupdate, updaterev, rev, branch, pull, uncompressed, stream, ssh, remotecmd, insecure
277 277 commit: addremove, close-branch, amend, secret, draft, edit, force-close-branch, interactive, include, exclude, message, logfile, date, user, subrepos
278 278 config: untrusted, exp-all-known, edit, local, source, shared, non-shared, global, template
279 279 continue: dry-run
280 280 copy: forget, after, at-rev, force, include, exclude, dry-run
281 281 debug-delta-find: changelog, manifest, dir, template, source
282 282 debug-repair-issue6528: to-report, from-report, paranoid, dry-run
283 283 debug-revlog-index: changelog, manifest, dir, template
284 284 debug-revlog-stats: changelog, manifest, filelogs, template
285 285 debug::stable-tail-sort: template
286 286 debug::stable-tail-sort-leaps: template, specific
287 287 debug::unbundle:
288 288 debugancestor:
289 289 debugantivirusrunning:
290 290 debugapplystreamclonebundle:
291 291 debugbackupbundle: recover, patch, git, limit, no-merges, stat, graph, style, template
292 292 debugbuilddag: mergeable-file, overwritten-file, new-file, from-existing
293 293 debugbundle: all, part-type, spec
294 294 debugcapabilities:
295 295 debugchangedfiles: compute
296 296 debugcheckstate:
297 297 debugcolor: style
298 298 debugcommands:
299 299 debugcomplete: options
300 300 debugcreatestreamclonebundle:
301 301 debugdag: tags, branches, dots, spaces
302 302 debugdata: changelog, manifest, dir
303 303 debugdate: extended
304 304 debugdeltachain: rev, all-info, size-info, dist-info, sparse-info, changelog, manifest, dir, template
305 305 debugdirstateignorepatternshash:
306 306 debugdirstate: nodates, dates, datesort, docket, all
307 307 debugdiscovery: old, nonheads, rev, seed, local-as-revs, remote-as-revs, ssh, remotecmd, insecure, template
308 308 debugdownload: output
309 309 debugextensions: template
310 310 debugfileset: rev, all-files, show-matcher, show-stage
311 311 debugformat: template
312 312 debugfsinfo:
313 313 debuggetbundle: head, common, type
314 314 debugignore:
315 315 debugindexdot: changelog, manifest, dir
316 316 debugindexstats:
317 317 debuginstall: template
318 318 debugknown:
319 319 debuglabelcomplete:
320 320 debuglocks: force-free-lock, force-free-wlock, set-lock, set-wlock
321 321 debugmanifestfulltextcache: clear, add
322 322 debugmergestate: style, template
323 323 debugnamecomplete:
324 324 debugnodemap: changelog, manifest, dir, dump-new, dump-disk, check, metadata
325 325 debugobsolete: flags, record-parents, rev, exclusive, index, delete, date, user, template
326 326 debugp1copies: rev
327 327 debugp2copies: rev
328 328 debugpathcomplete: full, normal, added, removed
329 329 debugpathcopies: include, exclude
330 330 debugpeer:
331 331 debugpickmergetool: rev, changedelete, include, exclude, tool
332 332 debugpushkey:
333 333 debugpvec:
334 334 debugrebuilddirstate: rev, minimal
335 335 debugrebuildfncache: only-data
336 336 debugrename: rev
337 337 debugrequires:
338 338 debugrevlog: changelog, manifest, dir, dump
339 339 debugrevlogindex: changelog, manifest, dir, format
340 340 debugrevspec: optimize, show-revs, show-set, show-stage, no-optimized, verify-optimized
341 341 debugserve: sshstdio, logiofd, logiofile
342 342 debugsetparents:
343 343 debugshell: command
344 344 debugsidedata: changelog, manifest, dir
345 345 debugssl:
346 346 debugstrip: rev, force, no-backup, nobackup, , keep, bookmark, soft
347 347 debugsub: rev
348 348 debugsuccessorssets: closest
349 349 debugtagscache:
350 350 debugtemplate: rev, define
351 351 debuguigetpass: prompt
352 352 debuguiprompt: prompt
353 353 debugupdatecaches:
354 354 debugupgraderepo: optimize, run, backup, changelog, manifest, filelogs
355 355 debugwalk: include, exclude
356 356 debugwhyunstable:
357 357 debugwireargs: three, four, five, ssh, remotecmd, insecure
358 358 debugwireproto: localssh, peer, noreadstderr, nologhandshake, ssh, remotecmd, insecure
359 359 diff: rev, from, to, change, text, git, binary, nodates, noprefix, show-function, reverse, ignore-all-space, ignore-space-change, ignore-blank-lines, ignore-space-at-eol, unified, stat, root, include, exclude, subrepos
360 360 export: bookmark, output, switch-parent, rev, text, git, binary, nodates, template
361 361 files: rev, print0, include, exclude, template, subrepos
362 362 forget: interactive, include, exclude, dry-run
363 graft: rev, base, continue, stop, abort, edit, log, no-commit, force, currentdate, currentuser, date, user, tool, dry-run
363 graft: rev, base, to, continue, stop, abort, edit, log, no-commit, force, currentdate, currentuser, date, user, tool, dry-run
364 364 grep: print0, all, diff, text, follow, ignore-case, files-with-matches, line-number, rev, all-files, user, date, template, include, exclude
365 365 heads: rev, topo, active, closed, style, template
366 366 help: extension, command, keyword, system
367 367 identify: rev, num, id, branch, tags, bookmarks, ssh, remotecmd, insecure, template
368 368 import: strip, base, secret, edit, force, no-commit, bypass, partial, exact, prefix, import-branch, message, logfile, date, user, similarity
369 369 incoming: force, newest-first, bundle, rev, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
370 370 init: ssh, remotecmd, insecure
371 371 locate: rev, print0, fullpath, include, exclude
372 372 log: follow, follow-first, date, copies, keyword, rev, line-range, removed, only-merges, user, only-branch, branch, bookmark, prune, patch, git, limit, no-merges, stat, graph, style, template, include, exclude
373 373 manifest: rev, all, template
374 374 merge: force, rev, preview, abort, tool
375 375 outgoing: force, rev, newest-first, bookmarks, branch, patch, git, limit, no-merges, stat, graph, style, template, ssh, remotecmd, insecure, subrepos
376 376 parents: rev, style, template
377 377 paths: template
378 378 phase: public, draft, secret, force, rev
379 379 pull: update, force, confirm, rev, bookmark, branch, remote-hidden, ssh, remotecmd, insecure
380 380 purge: abort-on-err, all, ignored, dirs, files, print, print0, confirm, include, exclude
381 381 push: force, rev, bookmark, all-bookmarks, branch, new-branch, pushvars, publish, ssh, remotecmd, insecure
382 382 recover: verify
383 383 remove: after, force, subrepos, include, exclude, dry-run
384 384 rename: forget, after, at-rev, force, include, exclude, dry-run
385 385 resolve: all, list, mark, unmark, no-status, re-merge, tool, include, exclude, template
386 386 revert: all, date, rev, no-backup, interactive, include, exclude, dry-run
387 387 rollback: dry-run, force
388 388 root: template
389 389 serve: accesslog, daemon, daemon-postexec, errorlog, port, address, prefix, name, web-conf, webdir-conf, pid-file, stdio, cmdserver, templates, style, ipv6, certificate, print-url, subrepos
390 390 shelve: addremove, unknown, cleanup, date, delete, edit, keep, list, message, name, patch, interactive, stat, include, exclude
391 391 status: all, modified, added, removed, deleted, clean, unknown, ignored, no-status, terse, copies, print0, rev, change, include, exclude, subrepos, template
392 392 summary: remote
393 393 tag: force, local, rev, remove, edit, message, date, user
394 394 tags: template
395 395 tip: patch, git, style, template
396 396 unbundle: update
397 397 unshelve: abort, continue, interactive, keep, name, tool, date
398 398 update: clean, check, merge, date, rev, tool
399 399 verify: full
400 400 version: template
401 401
402 402 $ hg init a
403 403 $ cd a
404 404 $ echo fee > fee
405 405 $ hg ci -q -Amfee
406 406 $ hg tag fee
407 407 $ mkdir fie
408 408 $ echo dead > fie/dead
409 409 $ echo live > fie/live
410 410 $ hg bookmark fo
411 411 $ hg branch -q fie
412 412 $ hg ci -q -Amfie
413 413 $ echo fo > fo
414 414 $ hg branch -qf default
415 415 $ hg ci -q -Amfo
416 416 $ echo Fum > Fum
417 417 $ hg ci -q -AmFum
418 418 $ hg bookmark Fum
419 419
420 420 Test debugpathcomplete
421 421
422 422 $ hg debugpathcomplete f
423 423 fee
424 424 fie
425 425 fo
426 426 $ hg debugpathcomplete -f f
427 427 fee
428 428 fie/dead
429 429 fie/live
430 430 fo
431 431
432 432 $ hg rm Fum
433 433 $ hg debugpathcomplete -r F
434 434 Fum
435 435
436 436 Test debugnamecomplete
437 437
438 438 $ hg debugnamecomplete
439 439 Fum
440 440 default
441 441 fee
442 442 fie
443 443 fo
444 444 tip
445 445 $ hg debugnamecomplete f
446 446 fee
447 447 fie
448 448 fo
449 449
450 450 Test debuglabelcomplete, a deprecated name for debugnamecomplete that is still
451 451 used for completions in some shells.
452 452
453 453 $ hg debuglabelcomplete
454 454 Fum
455 455 default
456 456 fee
457 457 fie
458 458 fo
459 459 tip
460 460 $ hg debuglabelcomplete f
461 461 fee
462 462 fie
463 463 fo
@@ -1,923 +1,1251
1 1 $ cat >> $HGRCPATH <<EOF
2 2 > [extdiff]
3 3 > # for portability:
4 4 > pdiff = sh "$RUNTESTDIR/pdiff"
5 5 > EOF
6 6
7 7 Create a repo with some stuff in it:
8 8
9 9 $ hg init a
10 10 $ cd a
11 11 $ echo a > a
12 12 $ echo a > d
13 13 $ echo a > e
14 14 $ hg ci -qAm0
15 15 $ echo b > a
16 16 $ hg ci -m1 -u bar
17 17 $ hg mv a b
18 18 $ hg ci -m2
19 19 $ hg cp b c
20 20 $ hg ci -m3 -u baz
21 21 $ echo b > d
22 22 $ echo f > e
23 23 $ hg ci -m4
24 24 $ hg up -q 3
25 25 $ echo b > e
26 26 $ hg branch -q stable
27 27 $ hg ci -m5
28 28 $ hg merge -q default --tool internal:local # for conflicts in e, choose 5 and ignore 4
29 29 $ hg branch -q default
30 30 $ hg ci -m6
31 31 $ hg phase --public 3
32 32 $ hg phase --force --secret 6
33 33
34 34 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
35 35 @ test@6.secret: 6
36 36 |\
37 37 | o test@5.draft: 5
38 38 | |
39 39 o | test@4.draft: 4
40 40 |/
41 41 o baz@3.public: 3
42 42 |
43 43 o test@2.public: 2
44 44 |
45 45 o bar@1.public: 1
46 46 |
47 47 o test@0.public: 0
48 48
49 49 Test --base for grafting the merge of 4 from the perspective of 5, thus only getting the change to d
50 50
51 51 $ hg up -cqr 3
52 52 $ hg graft -r 6 --base 5
53 53 grafting 6:25a2b029d3ae "6" (tip)
54 54 merging e
55 55 $ hg st --change .
56 56 M d
57 57
58 58 $ hg -q strip . --config extensions.strip=
59 59
60 60 Test --base for collapsing changesets 2 and 3, thus getting both b and c
61 61
62 62 $ hg up -cqr 0
63 63 $ hg graft -r 3 --base 1
64 64 grafting 3:4c60f11aa304 "3"
65 65 merging a and b to b
66 66 merging a and c to c
67 67 $ hg st --change .
68 68 A b
69 69 A c
70 70 R a
71 71
72 72 $ hg -q strip . --config extensions.strip=
73 73
74 74 Specifying child as --base revision fails safely (perhaps slightly confusing, but consistent)
75 75
76 76 $ hg graft -r 2 --base 3
77 77 grafting 2:5c095ad7e90f "2"
78 78 note: possible conflict - c was deleted and renamed to:
79 79 a
80 80 note: graft of 2:5c095ad7e90f created no changes to commit
81 81
82 82 Can't continue without starting:
83 83
84 84 $ hg -q up -cr tip
85 85 $ hg rm -q e
86 86 $ hg graft --continue
87 87 abort: no graft in progress
88 88 [20]
89 89 $ hg revert -r . -q e
90 90
91 91 Need to specify a rev:
92 92
93 93 $ hg graft
94 94 abort: no revisions specified
95 95 [10]
96 96
97 97 Can't graft ancestor:
98 98
99 99 $ hg graft 1 2
100 100 skipping ancestor revision 1:5d205f8b35b6
101 101 skipping ancestor revision 2:5c095ad7e90f
102 102 [255]
103 103
104 104 Specify revisions with -r:
105 105
106 106 $ hg graft -r 1 -r 2
107 107 skipping ancestor revision 1:5d205f8b35b6
108 108 skipping ancestor revision 2:5c095ad7e90f
109 109 [255]
110 110
111 111 $ hg graft -r 1 2
112 112 warning: inconsistent use of --rev might give unexpected revision ordering!
113 113 skipping ancestor revision 2:5c095ad7e90f
114 114 skipping ancestor revision 1:5d205f8b35b6
115 115 [255]
116 116
117 117 Conflicting date/user options:
118 118
119 119 $ hg up -q 0
120 120 $ hg graft -U --user foo 2
121 121 abort: cannot specify both --user and --currentuser
122 122 [10]
123 123 $ hg graft -D --date '0 0' 2
124 124 abort: cannot specify both --date and --currentdate
125 125 [10]
126 126
127 127 Can't graft with dirty wd:
128 128
129 129 $ hg up -q 0
130 130 $ echo foo > a
131 131 $ hg graft 1
132 132 abort: uncommitted changes
133 133 [20]
134 134 $ hg revert a
135 135
136 136 Graft a rename:
137 137 (this also tests that editor is invoked if '--edit' is specified)
138 138
139 139 $ hg status --rev "2^1" --rev 2
140 140 A b
141 141 R a
142 142 $ HGEDITOR=cat hg graft 2 -u foo --edit
143 143 grafting 2:5c095ad7e90f "2"
144 144 merging a and b to b
145 145 2
146 146
147 147
148 148 HG: Enter commit message. Lines beginning with 'HG:' are removed.
149 149 HG: Leave message empty to abort commit.
150 150 HG: --
151 151 HG: user: foo
152 152 HG: branch 'default'
153 153 HG: added b
154 154 HG: removed a
155 155 $ hg export tip --git
156 156 # HG changeset patch
157 157 # User foo
158 158 # Date 0 0
159 159 # Thu Jan 01 00:00:00 1970 +0000
160 160 # Node ID ef0ef43d49e79e81ddafdc7997401ba0041efc82
161 161 # Parent 68795b066622ca79a25816a662041d8f78f3cd9e
162 162 2
163 163
164 164 diff --git a/a b/b
165 165 rename from a
166 166 rename to b
167 167
168 168 Look for extra:source
169 169
170 170 $ hg log --debug -r tip
171 171 changeset: 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
172 172 tag: tip
173 173 phase: draft
174 174 parent: 0:68795b066622ca79a25816a662041d8f78f3cd9e
175 175 parent: -1:0000000000000000000000000000000000000000
176 176 manifest: 7:e59b6b228f9cbf9903d5e9abf996e083a1f533eb
177 177 user: foo
178 178 date: Thu Jan 01 00:00:00 1970 +0000
179 179 files+: b
180 180 files-: a
181 181 extra: branch=default
182 182 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
183 183 description:
184 184 2
185 185
186 186
187 187
188 188 Graft out of order, skipping a merge and a duplicate
189 189 (this also tests that editor is not invoked if '--edit' is not specified)
190 190
191 191 $ hg graft 1 5 4 3 'merge()' 2 -n
192 192 skipping ungraftable merge revision 6
193 193 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
194 194 grafting 1:5d205f8b35b6 "1"
195 195 grafting 5:97f8bfe72746 "5"
196 196 grafting 4:9c233e8e184d "4"
197 197 grafting 3:4c60f11aa304 "3"
198 198
199 199 $ HGEDITOR=cat hg graft 1 5 'merge()' 2 --debug
200 200 skipping ungraftable merge revision 6
201 201 scanning for duplicate grafts
202 202 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
203 203 grafting 1:5d205f8b35b6 "1"
204 204 unmatched files in local:
205 205 b
206 206 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
207 207 on local side:
208 208 src: 'a' -> dst: 'b' *
209 209 checking for directory renames
210 210 resolving manifests
211 211 branchmerge: True, force: True, partial: False
212 212 ancestor: 68795b066622, local: ef0ef43d49e7+, remote: 5d205f8b35b6
213 213 starting 4 threads for background file closing (?)
214 214 preserving b for resolve of b
215 215 b: local copied/moved from a -> m
216 216 picked tool ':merge' for b (binary False symlink False changedelete False)
217 217 merging b and a to b
218 218 my b@ef0ef43d49e7+ other a@5d205f8b35b6 ancestor a@68795b066622
219 219 premerge successful
220 220 committing files:
221 221 b
222 222 committing manifest
223 223 committing changelog
224 224 updating the branch cache
225 225 grafting 5:97f8bfe72746 "5"
226 226 resolving manifests
227 227 branchmerge: True, force: True, partial: False
228 228 ancestor: 4c60f11aa304, local: 6b9e5368ca4e+, remote: 97f8bfe72746
229 229 e: remote is newer -> g
230 230 getting e
231 231 committing files:
232 232 e
233 233 committing manifest
234 234 committing changelog
235 235 updating the branch cache
236 236 $ HGEDITOR=cat hg graft 4 3 --log --debug
237 237 scanning for duplicate grafts
238 238 grafting 4:9c233e8e184d "4"
239 239 resolving manifests
240 240 branchmerge: True, force: True, partial: False
241 241 ancestor: 4c60f11aa304, local: 1905859650ec+, remote: 9c233e8e184d
242 242 d: remote is newer -> g
243 243 getting d
244 244 preserving e for resolve of e
245 245 e: versions differ -> m
246 246 picked tool ':merge' for e (binary False symlink False changedelete False)
247 247 merging e
248 248 my e@1905859650ec+ other e@9c233e8e184d ancestor e@4c60f11aa304
249 249 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
250 250 abort: unresolved conflicts, can't continue
251 251 (use 'hg resolve' and 'hg graft --continue')
252 252 [1]
253 253
254 254 Summary should mention graft:
255 255
256 256 $ hg summary |grep graft
257 257 commit: 2 modified, 2 unknown, 1 unresolved (graft in progress)
258 258
259 259 Using status to get more context
260 260
261 261 $ hg status --verbose
262 262 M d
263 263 M e
264 264 ? a.orig
265 265 ? e.orig
266 266 # The repository is in an unfinished *graft* state.
267 267
268 268 # Unresolved merge conflicts:
269 269 #
270 270 # e
271 271 #
272 272 # To mark files as resolved: hg resolve --mark FILE
273 273
274 274 # To continue: hg graft --continue
275 275 # To abort: hg graft --abort
276 276 # To stop: hg graft --stop
277 277
278 278
279 279 Commit while interrupted should fail:
280 280
281 281 $ hg ci -m 'commit interrupted graft'
282 282 abort: graft in progress
283 283 (use 'hg graft --continue' or 'hg graft --stop' to stop)
284 284 [20]
285 285
286 286 Abort the graft and try committing:
287 287
288 288 $ hg up -C .
289 289 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
290 290 $ echo c >> e
291 291 $ hg ci -mtest
292 292
293 293 $ hg strip . --config extensions.strip=
294 294 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
295 295 saved backup bundle to $TESTTMP/a/.hg/strip-backup/*-backup.hg (glob)
296 296
297 297 Graft again:
298 298
299 299 $ hg graft 1 5 4 3 'merge()' 2
300 300 skipping ungraftable merge revision 6
301 301 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
302 302 skipping revision 1:5d205f8b35b6 (already grafted to 8:6b9e5368ca4e)
303 303 skipping revision 5:97f8bfe72746 (already grafted to 9:1905859650ec)
304 304 grafting 4:9c233e8e184d "4"
305 305 merging e
306 306 warning: conflicts while merging e! (edit, then use 'hg resolve --mark')
307 307 abort: unresolved conflicts, can't continue
308 308 (use 'hg resolve' and 'hg graft --continue')
309 309 [1]
310 310
311 311 Continue without resolve should fail:
312 312
313 313 $ hg graft -c
314 314 grafting 4:9c233e8e184d "4"
315 315 abort: unresolved merge conflicts (see 'hg help resolve')
316 316 [20]
317 317
318 318 Fix up:
319 319
320 320 $ echo b > e
321 321 $ hg resolve -m e
322 322 (no more unresolved files)
323 323 continue: hg graft --continue
324 324
325 325 Continue with a revision should fail:
326 326
327 327 $ hg graft -c 6
328 328 abort: can't specify --continue and revisions
329 329 [10]
330 330
331 331 $ hg graft -c -r 6
332 332 abort: can't specify --continue and revisions
333 333 [10]
334 334
335 335 Continue for real, clobber usernames
336 336
337 337 $ hg graft -c -U
338 338 grafting 4:9c233e8e184d "4"
339 339 grafting 3:4c60f11aa304 "3"
340 340
341 341 Compare with original:
342 342
343 343 $ hg diff -r 6
344 344 $ hg status --rev 0:. -C
345 345 M d
346 346 M e
347 347 A b
348 348 a
349 349 A c
350 350 a
351 351 R a
352 352
353 353 View graph:
354 354
355 355 $ hg log -G --template '{author}@{rev}.{phase}: {desc}\n'
356 356 @ test@11.draft: 3
357 357 |
358 358 o test@10.draft: 4
359 359 |
360 360 o test@9.draft: 5
361 361 |
362 362 o bar@8.draft: 1
363 363 |
364 364 o foo@7.draft: 2
365 365 |
366 366 | o test@6.secret: 6
367 367 | |\
368 368 | | o test@5.draft: 5
369 369 | | |
370 370 | o | test@4.draft: 4
371 371 | |/
372 372 | o baz@3.public: 3
373 373 | |
374 374 | o test@2.public: 2
375 375 | |
376 376 | o bar@1.public: 1
377 377 |/
378 378 o test@0.public: 0
379 379
380 380 Graft again onto another branch should preserve the original source
381 381 $ hg up -q 0
382 382 $ echo 'g'>g
383 383 $ hg add g
384 384 $ hg ci -m 7
385 385 created new head
386 386 $ hg graft 7
387 387 grafting 7:ef0ef43d49e7 "2"
388 388
389 389 $ hg log -r 7 --template '{rev}:{node}\n'
390 390 7:ef0ef43d49e79e81ddafdc7997401ba0041efc82
391 391 $ hg log -r 2 --template '{rev}:{node}\n'
392 392 2:5c095ad7e90f871700f02dd1fa5012cb4498a2d4
393 393
394 394 $ hg log --debug -r tip
395 395 changeset: 13:7a4785234d87ec1aa420ed6b11afe40fa73e12a9
396 396 tag: tip
397 397 phase: draft
398 398 parent: 12:b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
399 399 parent: -1:0000000000000000000000000000000000000000
400 400 manifest: 13:dc313617b8c32457c0d589e0dbbedfe71f3cd637
401 401 user: foo
402 402 date: Thu Jan 01 00:00:00 1970 +0000
403 403 files+: b
404 404 files-: a
405 405 extra: branch=default
406 406 extra: intermediate-source=ef0ef43d49e79e81ddafdc7997401ba0041efc82
407 407 extra: source=5c095ad7e90f871700f02dd1fa5012cb4498a2d4
408 408 description:
409 409 2
410 410
411 411
412 412 Disallow grafting an already grafted cset onto its original branch
413 413 $ hg up -q 6
414 414 $ hg graft 7
415 415 skipping already grafted revision 7:ef0ef43d49e7 (was grafted from 2:5c095ad7e90f)
416 416 [255]
417 417
418 418 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13
419 419 --- */hg-5c095ad7e90f.patch * (glob)
420 420 +++ */hg-7a4785234d87.patch * (glob)
421 421 @@ -1,18 +1,18 @@
422 422 # HG changeset patch
423 423 -# User test
424 424 +# User foo
425 425 # Date 0 0
426 426 # Thu Jan 01 00:00:00 1970 +0000
427 427 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
428 428 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
429 429 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
430 430 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
431 431 2
432 432
433 433 -diff -r 5d205f8b35b6 -r 5c095ad7e90f a
434 434 +diff -r b592ea63bb0c -r 7a4785234d87 a
435 435 --- a/a Thu Jan 01 00:00:00 1970 +0000
436 436 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
437 437 @@ -1,1 +0,0 @@
438 438 --b
439 439 -diff -r 5d205f8b35b6 -r 5c095ad7e90f b
440 440 +-a
441 441 +diff -r b592ea63bb0c -r 7a4785234d87 b
442 442 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
443 443 +++ b/b Thu Jan 01 00:00:00 1970 +0000
444 444 @@ -0,0 +1,1 @@
445 445 -+b
446 446 ++a
447 447 [1]
448 448
449 449 $ hg pdiff --config extensions.extdiff= --patch -r 2 -r 13 -X .
450 450 --- */hg-5c095ad7e90f.patch * (glob)
451 451 +++ */hg-7a4785234d87.patch * (glob)
452 452 @@ -1,8 +1,8 @@
453 453 # HG changeset patch
454 454 -# User test
455 455 +# User foo
456 456 # Date 0 0
457 457 # Thu Jan 01 00:00:00 1970 +0000
458 458 -# Node ID 5c095ad7e90f871700f02dd1fa5012cb4498a2d4
459 459 -# Parent 5d205f8b35b66bc36375c9534ffd3237730e8f04
460 460 +# Node ID 7a4785234d87ec1aa420ed6b11afe40fa73e12a9
461 461 +# Parent b592ea63bb0c19a6c5c44685ee29a2284f9f1b8f
462 462 2
463 463
464 464 [1]
465 465
466 466 Disallow grafting already grafted csets with the same origin onto each other
467 467 $ hg up -q 13
468 468 $ hg graft 2
469 469 skipping revision 2:5c095ad7e90f (already grafted to 13:7a4785234d87)
470 470 [255]
471 471 $ hg graft 7
472 472 skipping already grafted revision 7:ef0ef43d49e7 (13:7a4785234d87 also has origin 2:5c095ad7e90f)
473 473 [255]
474 474
475 475 $ hg up -q 7
476 476 $ hg graft 2
477 477 skipping revision 2:5c095ad7e90f (already grafted to 7:ef0ef43d49e7)
478 478 [255]
479 479 $ hg graft tip
480 480 skipping already grafted revision 13:7a4785234d87 (7:ef0ef43d49e7 also has origin 2:5c095ad7e90f)
481 481 [255]
482 482
483 483 Graft with --log
484 484
485 485 $ hg up -Cq 1
486 486 $ hg graft 3 --log -u foo
487 487 grafting 3:4c60f11aa304 "3"
488 488 $ hg log --template '{rev}:{node|short} {parents} {desc}\n' -r tip
489 489 14:0c921c65ef1e 1:5d205f8b35b6 3
490 490 (grafted from 4c60f11aa304a54ae1c199feb94e7fc771e51ed8)
491 491
492 492 Resolve conflicted graft
493 493 $ hg up -q 0
494 494 $ echo b > a
495 495 $ hg ci -m 8
496 496 created new head
497 497 $ echo c > a
498 498 $ hg ci -m 9
499 499 $ hg graft 1 --tool internal:fail
500 500 grafting 1:5d205f8b35b6 "1"
501 501 abort: unresolved conflicts, can't continue
502 502 (use 'hg resolve' and 'hg graft --continue')
503 503 [1]
504 504 $ hg resolve --all
505 505 merging a
506 506 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
507 507 [1]
508 508 $ cat a
509 509 <<<<<<< local: aaa4406d4f0a - test: 9
510 510 c
511 511 =======
512 512 b
513 513 >>>>>>> graft: 5d205f8b35b6 - bar: 1
514 514 $ echo b > a
515 515 $ hg resolve -m a
516 516 (no more unresolved files)
517 517 continue: hg graft --continue
518 518 $ hg graft -c
519 519 grafting 1:5d205f8b35b6 "1"
520 520 $ hg export tip --git
521 521 # HG changeset patch
522 522 # User bar
523 523 # Date 0 0
524 524 # Thu Jan 01 00:00:00 1970 +0000
525 525 # Node ID f67661df0c4804d301f064f332b57e7d5ddaf2be
526 526 # Parent aaa4406d4f0ae9befd6e58c82ec63706460cbca6
527 527 1
528 528
529 529 diff --git a/a b/a
530 530 --- a/a
531 531 +++ b/a
532 532 @@ -1,1 +1,1 @@
533 533 -c
534 534 +b
535 535
536 536 Resolve conflicted graft with rename
537 537 $ echo c > a
538 538 $ hg ci -m 10
539 539 $ hg graft 2 --tool internal:fail
540 540 grafting 2:5c095ad7e90f "2"
541 541 abort: unresolved conflicts, can't continue
542 542 (use 'hg resolve' and 'hg graft --continue')
543 543 [1]
544 544 $ hg resolve --all
545 545 merging a and b to b
546 546 (no more unresolved files)
547 547 continue: hg graft --continue
548 548 $ hg graft -c
549 549 grafting 2:5c095ad7e90f "2"
550 550 $ hg export tip --git
551 551 # HG changeset patch
552 552 # User test
553 553 # Date 0 0
554 554 # Thu Jan 01 00:00:00 1970 +0000
555 555 # Node ID 9627f653b421c61fc1ea4c4e366745070fa3d2bc
556 556 # Parent ee295f490a40b97f3d18dd4c4f1c8936c233b612
557 557 2
558 558
559 559 diff --git a/a b/b
560 560 rename from a
561 561 rename to b
562 562
563 563 Test simple origin(), with and without args
564 564 $ hg log -r 'origin()'
565 565 changeset: 1:5d205f8b35b6
566 566 user: bar
567 567 date: Thu Jan 01 00:00:00 1970 +0000
568 568 summary: 1
569 569
570 570 changeset: 2:5c095ad7e90f
571 571 user: test
572 572 date: Thu Jan 01 00:00:00 1970 +0000
573 573 summary: 2
574 574
575 575 changeset: 3:4c60f11aa304
576 576 user: baz
577 577 date: Thu Jan 01 00:00:00 1970 +0000
578 578 summary: 3
579 579
580 580 changeset: 4:9c233e8e184d
581 581 user: test
582 582 date: Thu Jan 01 00:00:00 1970 +0000
583 583 summary: 4
584 584
585 585 changeset: 5:97f8bfe72746
586 586 branch: stable
587 587 parent: 3:4c60f11aa304
588 588 user: test
589 589 date: Thu Jan 01 00:00:00 1970 +0000
590 590 summary: 5
591 591
592 592 $ hg log -r 'origin(7)'
593 593 changeset: 2:5c095ad7e90f
594 594 user: test
595 595 date: Thu Jan 01 00:00:00 1970 +0000
596 596 summary: 2
597 597
598 598 Now transplant a graft to test following through copies
599 599 $ hg up -q 0
600 600 $ hg branch -q dev
601 601 $ hg ci -qm "dev branch"
602 602 $ hg --config extensions.transplant= transplant -q 7
603 603 $ hg log -r 'origin(.)'
604 604 changeset: 2:5c095ad7e90f
605 605 user: test
606 606 date: Thu Jan 01 00:00:00 1970 +0000
607 607 summary: 2
608 608
609 609 Test that the graft and transplant markers in extra are converted, allowing
610 610 origin() to still work. Note that these recheck the immediately preceeding two
611 611 tests.
612 612 $ hg --quiet --config extensions.convert= --config convert.hg.saverev=True convert . ../converted
613 613
614 614 The graft case
615 615 $ hg -R ../converted log -r 7 --template "{rev}: {node}\n{join(extras, '\n')}\n"
616 616 7: 7ae846e9111fc8f57745634250c7b9ac0a60689b
617 617 branch=default
618 618 convert_revision=ef0ef43d49e79e81ddafdc7997401ba0041efc82
619 619 source=e0213322b2c1a5d5d236c74e79666441bee67a7d
620 620 $ hg -R ../converted log -r 'origin(7)'
621 621 changeset: 2:e0213322b2c1
622 622 user: test
623 623 date: Thu Jan 01 00:00:00 1970 +0000
624 624 summary: 2
625 625
626 626 Test that template correctly expands more than one 'extra' (issue4362), and that
627 627 'intermediate-source' is converted.
628 628 $ hg -R ../converted log -r 13 --template "{extras % ' Extra: {extra}\n'}"
629 629 Extra: branch=default
630 630 Extra: convert_revision=7a4785234d87ec1aa420ed6b11afe40fa73e12a9
631 631 Extra: intermediate-source=7ae846e9111fc8f57745634250c7b9ac0a60689b
632 632 Extra: source=e0213322b2c1a5d5d236c74e79666441bee67a7d
633 633
634 634 The transplant case
635 635 $ hg -R ../converted log -r tip --template "{rev}: {node}\n{join(extras, '\n')}\n"
636 636 21: fbb6c5cc81002f2b4b49c9d731404688bcae5ade
637 637 branch=dev
638 638 convert_revision=7e61b508e709a11d28194a5359bc3532d910af21
639 639 transplant_source=z\xe8F\xe9\x11\x1f\xc8\xf5wEcBP\xc7\xb9\xac\n`h\x9b
640 640 $ hg -R ../converted log -r 'origin(tip)'
641 641 changeset: 2:e0213322b2c1
642 642 user: test
643 643 date: Thu Jan 01 00:00:00 1970 +0000
644 644 summary: 2
645 645
646 646
647 647 Test simple destination
648 648 $ hg log -r 'destination()'
649 649 changeset: 7:ef0ef43d49e7
650 650 parent: 0:68795b066622
651 651 user: foo
652 652 date: Thu Jan 01 00:00:00 1970 +0000
653 653 summary: 2
654 654
655 655 changeset: 8:6b9e5368ca4e
656 656 user: bar
657 657 date: Thu Jan 01 00:00:00 1970 +0000
658 658 summary: 1
659 659
660 660 changeset: 9:1905859650ec
661 661 user: test
662 662 date: Thu Jan 01 00:00:00 1970 +0000
663 663 summary: 5
664 664
665 665 changeset: 10:52dc0b4c6907
666 666 user: test
667 667 date: Thu Jan 01 00:00:00 1970 +0000
668 668 summary: 4
669 669
670 670 changeset: 11:882b35362a6b
671 671 user: test
672 672 date: Thu Jan 01 00:00:00 1970 +0000
673 673 summary: 3
674 674
675 675 changeset: 13:7a4785234d87
676 676 user: foo
677 677 date: Thu Jan 01 00:00:00 1970 +0000
678 678 summary: 2
679 679
680 680 changeset: 14:0c921c65ef1e
681 681 parent: 1:5d205f8b35b6
682 682 user: foo
683 683 date: Thu Jan 01 00:00:00 1970 +0000
684 684 summary: 3
685 685
686 686 changeset: 17:f67661df0c48
687 687 user: bar
688 688 date: Thu Jan 01 00:00:00 1970 +0000
689 689 summary: 1
690 690
691 691 changeset: 19:9627f653b421
692 692 user: test
693 693 date: Thu Jan 01 00:00:00 1970 +0000
694 694 summary: 2
695 695
696 696 changeset: 21:7e61b508e709
697 697 branch: dev
698 698 tag: tip
699 699 user: foo
700 700 date: Thu Jan 01 00:00:00 1970 +0000
701 701 summary: 2
702 702
703 703 $ hg log -r 'destination(2)'
704 704 changeset: 7:ef0ef43d49e7
705 705 parent: 0:68795b066622
706 706 user: foo
707 707 date: Thu Jan 01 00:00:00 1970 +0000
708 708 summary: 2
709 709
710 710 changeset: 13:7a4785234d87
711 711 user: foo
712 712 date: Thu Jan 01 00:00:00 1970 +0000
713 713 summary: 2
714 714
715 715 changeset: 19:9627f653b421
716 716 user: test
717 717 date: Thu Jan 01 00:00:00 1970 +0000
718 718 summary: 2
719 719
720 720 changeset: 21:7e61b508e709
721 721 branch: dev
722 722 tag: tip
723 723 user: foo
724 724 date: Thu Jan 01 00:00:00 1970 +0000
725 725 summary: 2
726 726
727 727 Transplants of grafts can find a destination...
728 728 $ hg log -r 'destination(7)'
729 729 changeset: 21:7e61b508e709
730 730 branch: dev
731 731 tag: tip
732 732 user: foo
733 733 date: Thu Jan 01 00:00:00 1970 +0000
734 734 summary: 2
735 735
736 736 ... grafts of grafts unfortunately can't
737 737 $ hg graft -q 13 --debug
738 738 scanning for duplicate grafts
739 739 grafting 13:7a4785234d87 "2"
740 740 all copies found (* = to merge, ! = divergent, % = renamed and deleted):
741 741 on local side:
742 742 src: 'a' -> dst: 'b' *
743 743 on remote side:
744 744 src: 'a' -> dst: 'b' *
745 745 checking for directory renames
746 746 resolving manifests
747 747 branchmerge: True, force: True, partial: False
748 748 ancestor: b592ea63bb0c, local: 7e61b508e709+, remote: 7a4785234d87
749 749 starting 4 threads for background file closing (?)
750 750 nothing to commit, clearing merge state
751 751 note: graft of 13:7a4785234d87 created no changes to commit
752 752 $ hg log -r 'destination(13)'
753 753 All copies of a cset
754 754 $ hg log -r 'origin(13) or destination(origin(13))'
755 755 changeset: 2:5c095ad7e90f
756 756 user: test
757 757 date: Thu Jan 01 00:00:00 1970 +0000
758 758 summary: 2
759 759
760 760 changeset: 7:ef0ef43d49e7
761 761 parent: 0:68795b066622
762 762 user: foo
763 763 date: Thu Jan 01 00:00:00 1970 +0000
764 764 summary: 2
765 765
766 766 changeset: 13:7a4785234d87
767 767 user: foo
768 768 date: Thu Jan 01 00:00:00 1970 +0000
769 769 summary: 2
770 770
771 771 changeset: 19:9627f653b421
772 772 user: test
773 773 date: Thu Jan 01 00:00:00 1970 +0000
774 774 summary: 2
775 775
776 776 changeset: 21:7e61b508e709
777 777 branch: dev
778 778 tag: tip
779 779 user: foo
780 780 date: Thu Jan 01 00:00:00 1970 +0000
781 781 summary: 2
782 782
783 783
784 784 graft skips ancestors
785 785
786 786 $ hg graft 21 3
787 787 skipping ancestor revision 21:7e61b508e709
788 788 grafting 3:4c60f11aa304 "3"
789 789 merging b and c to c
790 790
791 791 graft with --force (still doesn't graft merges)
792 792
793 793 $ hg graft 19 0 6
794 794 skipping ungraftable merge revision 6
795 795 skipping ancestor revision 0:68795b066622
796 796 grafting 19:9627f653b421 "2"
797 797 merging b
798 798 note: graft of 19:9627f653b421 created no changes to commit
799 799 $ hg graft 19 0 6 --force
800 800 skipping ungraftable merge revision 6
801 801 grafting 19:9627f653b421 "2"
802 802 merging b
803 803 note: graft of 19:9627f653b421 created no changes to commit
804 804 grafting 0:68795b066622 "0"
805 805
806 806 graft --force after backout. Do the backout with graft too, to make
807 807 sure we support issue6248.
808 808
809 809 $ echo abc > a
810 810 $ hg ci -m 24
811 811 $ hg graft --base . -r ".^" --no-commit
812 812 grafting 23:b1cac6de36a9 "0"
813 813 $ hg commit -m 'Backed out changeset 2e7ea477be26'
814 814 $ hg graft 24
815 815 skipping ancestor revision 24:2e7ea477be26
816 816 [255]
817 817 $ hg graft 24 --force
818 818 grafting 24:2e7ea477be26 "24"
819 819 merging a
820 820 $ cat a
821 821 abc
822 822
823 823 graft --continue after --force
824 824
825 825 $ echo def > a
826 826 $ hg ci -m 27
827 827 $ hg graft 24 --force --tool internal:fail
828 828 grafting 24:2e7ea477be26 "24"
829 829 abort: unresolved conflicts, can't continue
830 830 (use 'hg resolve' and 'hg graft --continue')
831 831 [1]
832 832 $ hg resolve --all
833 833 merging a
834 834 warning: conflicts while merging a! (edit, then use 'hg resolve --mark')
835 835 [1]
836 836 $ echo abc > a
837 837 $ hg resolve -m a
838 838 (no more unresolved files)
839 839 continue: hg graft --continue
840 840 $ hg graft -c
841 841 grafting 24:2e7ea477be26 "24"
842 842 $ cat a
843 843 abc
844 844
845 845 graft --continue after --base with conflits
846 846
847 847 $ echo base > d
848 848 $ hg ci -m _
849 849 $ hg graft -r 6
850 850 skipping ungraftable merge revision 6
851 851 [255]
852 852 $ hg graft -r 6 --base 5
853 853 grafting 6:25a2b029d3ae "6"
854 854 merging d
855 855 warning: conflicts while merging d! (edit, then use 'hg resolve --mark')
856 856 merging e
857 857 abort: unresolved conflicts, can't continue
858 858 (use 'hg resolve' and 'hg graft --continue')
859 859 [1]
860 860 $ echo a > d && hg resolve -qm
861 861 continue: hg graft --continue
862 862 $ hg graft --continue
863 863 grafting 6:25a2b029d3ae "6"
864 864
865 865 Continue testing same origin policy, using revision numbers from test above
866 866 but do some destructive editing of the repo:
867 867
868 868 $ hg up -qC 7
869 869 $ hg tag -l -r 13 tmp
870 870 $ hg --config extensions.strip= strip 2
871 871 saved backup bundle to $TESTTMP/a/.hg/strip-backup/5c095ad7e90f-d323a1e4-backup.hg
872 872 $ hg graft tmp
873 873 skipping already grafted revision 8:7a4785234d87 (2:ef0ef43d49e7 also has unknown origin 5c095ad7e90f)
874 874 [255]
875 875
876 876 Empty graft
877 877
878 878 $ hg up -qr 22
879 879 $ hg tag -f something
880 880 $ hg graft -qr 23
881 881 $ hg graft -f 23
882 882 grafting 23:72d9c7c75bcc "24"
883 883 note: graft of 23:72d9c7c75bcc created no changes to commit
884 884
885 $ pwd
886 $TESTTMP/a
885 887 $ cd ..
886 888
887 889 Graft to duplicate a commit
888 890
889 891 $ hg init graftsibling
890 892 $ cd graftsibling
891 893 $ touch a
892 894 $ hg commit -qAm a
893 895 $ touch b
894 896 $ hg commit -qAm b
895 897 $ hg log -G -T '{rev}\n'
896 898 @ 1
897 899 |
898 900 o 0
899 901
900 902 $ hg up -q 0
901 903 $ hg graft -r 1
902 904 grafting 1:0e067c57feba "b" (tip)
903 905 $ hg log -G -T '{rev}\n'
904 906 @ 2
905 907 |
906 908 | o 1
907 909 |/
908 910 o 0
909 911
910 912 Graft to duplicate a commit twice
911 913
912 914 $ hg up -q 0
913 915 $ hg graft -r 2
914 916 grafting 2:044ec77f6389 "b" (tip)
915 917 $ hg log -G -T '{rev}\n'
916 918 @ 3
917 919 |
918 920 | o 2
919 921 |/
920 922 | o 1
921 923 |/
922 924 o 0
923 925
926 $ cd ../
927
928 In memory graft with --to
929 =========================
930
931
932 setup a repository
933
934 $ hg init base-to
935 $ cd base-to
936 $ hg debugbuilddag -m ".:base..:dst*base.:src*base..:wc"
937 $ hg up "wc"
938 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
939 $ hg log -G -T '{rev}:{desc} {tags}\n'
940 @ 7:r7 tip wc
941 |
942 o 6:r6
943 |
944 o 5:r5
945 |
946 | o 4:r4 src
947 | |
948 | o 3:r3
949 |/
950 | o 2:r2 dst
951 | |
952 | o 1:r1
953 |/
954 o 0:r0 base
955
956
957 $ cd ..
958
959 Simple test
960 -----------
961
962 As few special case as possible
963
964 $ cp -R base-to test-to-simple
965 $ cd test-to-simple
966 $ hg graft --rev src --to dst
967 grafting 4:4178b3134f52 "r4" (src)
968 merging mf
969 $ hg log -G -T '{rev}:{desc} {tags}\n'
970 o 8:r4 tip
971 |
972 | @ 7:r7 wc
973 | |
974 | o 6:r6
975 | |
976 | o 5:r5
977 | |
978 | | o 4:r4 src
979 | | |
980 | | o 3:r3
981 | |/
982 o | 2:r2 dst
983 | |
984 o | 1:r1
985 |/
986 o 0:r0 base
987
988 $ cd ..
989
990 Single changeset, local changes
991 -------------------------------
992
993 Run "graft --to" with local changes
994
995 $ cp -R base-to test-to-local-change
996 $ cd test-to-local-change
997 $ hg st --all
998 C mf
999 $ echo foo >> mf
1000 $ hg status
1001 M mf
1002 $ hg graft --rev src --to dst
1003 grafting 4:4178b3134f52 "r4" (src)
1004 merging mf
1005
1006 local file should not have been touched.
1007
1008 $ hg status
1009 M mf
1010 $ hg log -G -T '{rev}:{desc} {tags}\n'
1011 o 8:r4 tip
1012 |
1013 | @ 7:r7 wc
1014 | |
1015 | o 6:r6
1016 | |
1017 | o 5:r5
1018 | |
1019 | | o 4:r4 src
1020 | | |
1021 | | o 3:r3
1022 | |/
1023 o | 2:r2 dst
1024 | |
1025 o | 1:r1
1026 |/
1027 o 0:r0 base
1028
1029 $ cd ..
1030
1031 Multiple linear changesets
1032 --------------------------
1033
1034 grafting multiple linear changesets
1035
1036 $ cp -R base-to test-to-multiple-linear
1037 $ cd test-to-multiple-linear
1038 $ hg graft --rev 'src~1::src' --to dst
1039 grafting 3:181578a106da "r3"
1040 merging mf
1041 grafting 4:4178b3134f52 "r4" (src)
1042 merging mf
1043 $ hg log -G -T '{rev}:{desc} {tags}\n'
1044 o 9:r4 tip
1045 |
1046 o 8:r3
1047 |
1048 | @ 7:r7 wc
1049 | |
1050 | o 6:r6
1051 | |
1052 | o 5:r5
1053 | |
1054 | | o 4:r4 src
1055 | | |
1056 | | o 3:r3
1057 | |/
1058 o | 2:r2 dst
1059 | |
1060 o | 1:r1
1061 |/
1062 o 0:r0 base
1063
1064 $ cd ..
1065
1066 Multiple unrelated changesets
1067 --------------------------
1068
1069 Grafting multiple changesets on different branch
1070
1071 The order specified on the command line should be preserved.
1072 The result should be linear.
1073
1074 $ cp -R base-to test-to-multiple-unrelated
1075 $ cd test-to-multiple-unrelated
1076 $ hg graft 'src' 'wc~1' 'src~1' --to dst
1077 grafting 4:4178b3134f52 "r4" (src)
1078 merging mf
1079 grafting 6:735f0f7a080b "r6"
1080 merging mf
1081 grafting 3:181578a106da "r3"
1082 merging mf
1083 $ hg log -G -T '{rev}:{desc} {tags}\n'
1084 o 10:r3 tip
1085 |
1086 o 9:r6
1087 |
1088 o 8:r4
1089 |
1090 | @ 7:r7 wc
1091 | |
1092 | o 6:r6
1093 | |
1094 | o 5:r5
1095 | |
1096 | | o 4:r4 src
1097 | | |
1098 | | o 3:r3
1099 | |/
1100 o | 2:r2 dst
1101 | |
1102 o | 1:r1
1103 |/
1104 o 0:r0 base
1105
1106 $ cd ..
1107
1108 with base
1109 ---------
1110
1111 $ cp -R base-to test-to-base
1112 $ cd test-to-base
1113 $ hg graft --base base src --to dst
1114 grafting 4:4178b3134f52 "r4" (src)
1115 merging mf
1116 $ hg log -G -T '{rev}:{desc} {tags}\n'
1117 o 8:r4 tip
1118 |
1119 | @ 7:r7 wc
1120 | |
1121 | o 6:r6
1122 | |
1123 | o 5:r5
1124 | |
1125 | | o 4:r4 src
1126 | | |
1127 | | o 3:r3
1128 | |/
1129 o | 2:r2 dst
1130 | |
1131 o | 1:r1
1132 |/
1133 o 0:r0 base
1134
1135 $ hg diff --from base --to src
1136 diff -r 93cbaf5e6529 -r 4178b3134f52 mf
1137 --- a/mf Thu Jan 01 00:00:00 1970 +0000
1138 +++ b/mf Thu Jan 01 00:00:04 1970 +0000
1139 @@ -4,9 +4,9 @@
1140 3
1141 4
1142 5
1143 -6
1144 +6 r3
1145 7
1146 -8
1147 +8 r4
1148 9
1149 10
1150 11
1151 $ hg export src
1152 # HG changeset patch
1153 # User debugbuilddag
1154 # Date 4 0
1155 # Thu Jan 01 00:00:04 1970 +0000
1156 # Node ID 4178b3134f5224d297d3b9e0e98b983f42e53d55
1157 # Parent 181578a106daabea66d4465f4883f7f8552bbc9d
1158 r4
1159
1160 diff -r 181578a106da -r 4178b3134f52 mf
1161 --- a/mf Thu Jan 01 00:00:03 1970 +0000
1162 +++ b/mf Thu Jan 01 00:00:04 1970 +0000
1163 @@ -6,7 +6,7 @@
1164 5
1165 6 r3
1166 7
1167 -8
1168 +8 r4
1169 9
1170 10
1171 11
1172 $ hg export tip
1173 # HG changeset patch
1174 # User debugbuilddag
1175 # Date 4 0
1176 # Thu Jan 01 00:00:04 1970 +0000
1177 # Node ID 40112ab60ecb01882916c1a4439c798746e34165
1178 # Parent 37d4c1cec295ddfa401f4a365e15a82a1974b056
1179 r4
1180
1181 diff -r 37d4c1cec295 -r 40112ab60ecb mf
1182 --- a/mf Thu Jan 01 00:00:02 1970 +0000
1183 +++ b/mf Thu Jan 01 00:00:04 1970 +0000
1184 @@ -4,9 +4,9 @@
1185 3
1186 4 r2
1187 5
1188 -6
1189 +6 r3
1190 7
1191 -8
1192 +8 r4
1193 9
1194 10
1195 11
1196 $ cd ..
1197
1198 with conflict
1199 -------------
1200
1201 We should abort in case of conflict and rollback any grafted procress
1202
1203 $ cp -R base-to test-to-conflict
1204 $ cd test-to-conflict
1205 $ hg up src
1206 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1207 $ echo this-will-conflict >> mf
1208 $ hg ci -m 'this-will-conflict'
1209 $ hg up dst
1210 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1211 $ echo conflict-this-will-conflict >> mf
1212 $ hg ci -m 'conflict-this-will'
1213 $ hg up wc
1214 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
1215 $ hg graft --to 'max(dst::)' src:: --dry-run
1216 grafting 4:4178b3134f52 "r4" (src)
1217 grafting 8:9fa2d3fe2323 "this-will-conflict"
1218 $ hg graft --to 'max(dst::)' src::
1219 grafting 4:4178b3134f52 "r4" (src)
1220 merging mf
1221 grafting 8:9fa2d3fe2323 "this-will-conflict"
1222 merging mf
1223 transaction abort!
1224 rollback completed
1225 abort: cannot graft in memory: merge conflicts
1226 (in-memory merge does not support merge conflicts)
1227 [255]
1228 $ hg log -G -T '{rev}:{desc} {tags}\n'
1229 o 9:conflict-this-will tip
1230 |
1231 | o 8:this-will-conflict
1232 | |
1233 | | @ 7:r7 wc
1234 | | |
1235 | | o 6:r6
1236 | | |
1237 | | o 5:r5
1238 | | |
1239 | o | 4:r4 src
1240 | | |
1241 | o | 3:r3
1242 | |/
1243 o | 2:r2 dst
1244 | |
1245 o | 1:r1
1246 |/
1247 o 0:r0 base
1248
1249 $ cd ..
1250
1251
General Comments 0
You need to be logged in to leave comments. Login now