comparison mercurial/commands.py @ 2029:d436b21b20dc

rewrite revert command. fix issues 93, 123, 147. new version does these things: - saves backup copies of modified files (issue 147) - prints output like other commands, and errors when files not found (issue 123) - marks files added/removed (issue 93)
author Vadim Gelfer <vadim.gelfer@gmail.com>
date Fri, 31 Mar 2006 10:37:25 -0800
parents a59da8cc35e4
children e3280d350792 c9226bcc288d
comparison
equal deleted inserted replaced
2028:1f1fc418a96c 2029:d436b21b20dc
41 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']] 41 opts['exclude'] = [os.path.join(cwd, x) for x in opts['exclude']]
42 cwd = '' 42 cwd = ''
43 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'), 43 return util.cmdmatcher(repo.root, cwd, pats or ['.'], opts.get('include'),
44 opts.get('exclude'), head) 44 opts.get('exclude'), head)
45 45
46 def makewalk(repo, pats, opts, node=None, head=''): 46 def makewalk(repo, pats, opts, node=None, head='', badmatch=None):
47 files, matchfn, anypats = matchpats(repo, pats, opts, head) 47 files, matchfn, anypats = matchpats(repo, pats, opts, head)
48 exact = dict(zip(files, files)) 48 exact = dict(zip(files, files))
49 def walk(): 49 def walk():
50 for src, fn in repo.walk(node=node, files=files, match=matchfn): 50 for src, fn in repo.walk(node=node, files=files, match=matchfn,
51 badmatch=None):
51 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact 52 yield src, fn, util.pathto(repo.getcwd(), fn), fn in exact
52 return files, matchfn, walk() 53 return files, matchfn, walk()
53 54
54 def walk(repo, pats, opts, node=None, head=''): 55 def walk(repo, pats, opts, node=None, head='', badmatch=None):
55 files, matchfn, results = makewalk(repo, pats, opts, node, head) 56 files, matchfn, results = makewalk(repo, pats, opts, node, head, badmatch)
56 for r in results: 57 for r in results:
57 yield r 58 yield r
58 59
59 def walkchangerevs(ui, repo, pats, opts): 60 def walkchangerevs(ui, repo, pats, opts):
60 '''Iterate over files and the revs they changed in. 61 '''Iterate over files and the revs they changed in.
2001 requested revision. Files that changed between either parent are 2002 requested revision. Files that changed between either parent are
2002 marked as changed for the next commit and a commit must be 2003 marked as changed for the next commit and a commit must be
2003 performed before any further updates are allowed. 2004 performed before any further updates are allowed.
2004 """ 2005 """
2005 return update(ui, repo, node=node, merge=True, **opts) 2006 return update(ui, repo, node=node, merge=True, **opts)
2006 2007
2007 def outgoing(ui, repo, dest="default-push", **opts): 2008 def outgoing(ui, repo, dest="default-push", **opts):
2008 """show changesets not found in destination 2009 """show changesets not found in destination
2009 2010
2010 Show changesets not found in the specified destination repository or 2011 Show changesets not found in the specified destination repository or
2011 the default push location. These are the changesets that would be pushed 2012 the default push location. These are the changesets that would be pushed
2086 ui.status(_("not updating, since new heads added\n")) 2087 ui.status(_("not updating, since new heads added\n"))
2087 if modheads > 1: 2088 if modheads > 1:
2088 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n")) 2089 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2089 else: 2090 else:
2090 ui.status(_("(run 'hg update' to get a working copy)\n")) 2091 ui.status(_("(run 'hg update' to get a working copy)\n"))
2091 2092
2092 def pull(ui, repo, source="default", **opts): 2093 def pull(ui, repo, source="default", **opts):
2093 """pull changes from the specified source 2094 """pull changes from the specified source
2094 2095
2095 Pull changes from a remote repository to a local one. 2096 Pull changes from a remote repository to a local one.
2096 2097
2284 2285
2285 In its default mode, it reverts any uncommitted modifications made 2286 In its default mode, it reverts any uncommitted modifications made
2286 to the named files or directories. This restores the contents of 2287 to the named files or directories. This restores the contents of
2287 the affected files to an unmodified state. 2288 the affected files to an unmodified state.
2288 2289
2290 Modified files have backup copies saved before revert. To disable
2291 backups, use --no-backup. To change the name of backup files, use
2292 --backup to give a format string.
2293
2289 Using the -r option, it reverts the given files or directories to 2294 Using the -r option, it reverts the given files or directories to
2290 their state as of an earlier revision. This can be helpful to "roll 2295 their state as of an earlier revision. This can be helpful to "roll
2291 back" some or all of a change that should not have been committed. 2296 back" some or all of a change that should not have been committed.
2292 2297
2293 Revert modifies the working directory. It does not commit any 2298 Revert modifies the working directory. It does not commit any
2298 2303
2299 If names are given, all files matching the names are reverted. 2304 If names are given, all files matching the names are reverted.
2300 2305
2301 If no arguments are given, all files in the repository are reverted. 2306 If no arguments are given, all files in the repository are reverted.
2302 """ 2307 """
2303 node = opts['rev'] and repo.lookup(opts['rev']) or \ 2308 parent = repo.dirstate.parents()[0]
2304 repo.dirstate.parents()[0] 2309 node = opts['rev'] and repo.lookup(opts['rev']) or parent
2305 2310 mf = repo.manifest.read(repo.changelog.read(node)[0])
2306 files, choose, anypats = matchpats(repo, pats, opts) 2311
2307 modified, added, removed, deleted, unknown = repo.changes(match=choose) 2312 def backup(name, exact):
2308 repo.forget(added) 2313 bakname = make_filename(repo, repo.changelog,
2309 repo.undelete(removed) 2314 opts['backup_name'] or '%p.orig',
2310 2315 node=parent, pathname=name)
2311 return repo.update(node, False, True, choose, False) 2316 if os.path.exists(name):
2317 # if backup already exists and is same as backup we want
2318 # to make, do nothing
2319 if os.path.exists(bakname):
2320 if repo.wread(name) == repo.wread(bakname):
2321 return
2322 raise util.Abort(_('cannot save current version of %s - '
2323 '%s exists and differs') %
2324 (name, bakname))
2325 ui.status(('saving current version of %s as %s\n') %
2326 (name, bakname))
2327 shutil.copyfile(name, bakname)
2328 shutil.copymode(name, bakname)
2329
2330 wlock = repo.wlock()
2331
2332 entries = []
2333 names = {}
2334 for src, abs, rel, exact in walk(repo, pats, opts, badmatch=mf.has_key):
2335 names[abs] = True
2336 entries.append((abs, rel, exact))
2337
2338 changes = repo.changes(match=names.has_key, wlock=wlock)
2339 modified, added, removed, deleted, unknown = map(dict.fromkeys, changes)
2340
2341 revert = ([], _('reverting %s\n'))
2342 add = ([], _('adding %s\n'))
2343 remove = ([], _('removing %s\n'))
2344 forget = ([], _('forgetting %s\n'))
2345 undelete = ([], _('undeleting %s\n'))
2346 update = {}
2347
2348 disptable = (
2349 # dispatch table:
2350 # file state
2351 # action if in target manifest
2352 # action if not in target manifest
2353 # make backup if in target manifest
2354 # make backup if not in target manifest
2355 (modified, revert, remove, True, True),
2356 (added, revert, forget, True, True),
2357 (removed, undelete, None, False, False),
2358 (deleted, revert, remove, False, False),
2359 (unknown, add, None, True, False),
2360 )
2361
2362 for abs, rel, exact in entries:
2363 def handle(xlist, dobackup):
2364 xlist[0].append(abs)
2365 if dobackup and not opts['no_backup']:
2366 backup(rel, exact)
2367 if ui.verbose or not exact:
2368 ui.status(xlist[1] % rel)
2369 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2370 if abs not in table: continue
2371 # file has changed in dirstate
2372 if abs in mf:
2373 handle(hitlist, backuphit)
2374 elif misslist is not None:
2375 handle(misslist, backupmiss)
2376 else:
2377 if exact: ui.warn(_('file not managed: %s\n' % rel))
2378 break
2379 else:
2380 # file has not changed in dirstate
2381 if node == parent:
2382 if exact: ui.warn(_('no changes needed to %s\n' % rel))
2383 continue
2384 if abs not in mf:
2385 remove[0].append(abs)
2386 update[abs] = True
2387
2388 repo.dirstate.forget(forget[0])
2389 r = repo.update(node, False, True, update.has_key, False, wlock=wlock)
2390 repo.dirstate.update(add[0], 'a')
2391 repo.dirstate.update(undelete[0], 'n')
2392 repo.dirstate.update(remove[0], 'r')
2393 return r
2312 2394
2313 def root(ui, repo): 2395 def root(ui, repo):
2314 """print the root (top) of the current working dir 2396 """print the root (top) of the current working dir
2315 2397
2316 Print the root directory of the current repository. 2398 Print the root directory of the current repository.
2927 ('X', 'exclude', [], _('exclude names matching the given patterns'))], 3009 ('X', 'exclude', [], _('exclude names matching the given patterns'))],
2928 _('hg rename [OPTION]... SOURCE... DEST')), 3010 _('hg rename [OPTION]... SOURCE... DEST')),
2929 "^revert": 3011 "^revert":
2930 (revert, 3012 (revert,
2931 [('r', 'rev', '', _('revision to revert to')), 3013 [('r', 'rev', '', _('revision to revert to')),
2932 ('I', 'include', [], _('include names matching the given patterns')), 3014 ('', 'backup-name', '', _('save backup with formatted name')),
2933 ('X', 'exclude', [], _('exclude names matching the given patterns'))], 3015 ('', 'no-backup', None, _('do not save backup copies of files')),
3016 ('I', 'include', [], _('include names matching given patterns')),
3017 ('X', 'exclude', [], _('exclude names matching given patterns'))],
2934 _('hg revert [-r REV] [NAME]...')), 3018 _('hg revert [-r REV] [NAME]...')),
2935 "root": (root, [], _('hg root')), 3019 "root": (root, [], _('hg root')),
2936 "^serve": 3020 "^serve":
2937 (serve, 3021 (serve,
2938 [('A', 'accesslog', '', _('name of access log file to write to')), 3022 [('A', 'accesslog', '', _('name of access log file to write to')),