##// END OF EJS Templates
commit: when committing the results of a merge, it's all or nothing...
Bryan O'Sullivan -
r6385:0d4e068e default
parent child Browse files
Show More
@@ -1,127 +1,127 b''
1 # fetch.py - pull and merge remote changes
1 # fetch.py - pull and merge remote changes
2 #
2 #
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from mercurial.i18n import _
8 from mercurial.i18n import _
9 from mercurial.node import nullid, short
9 from mercurial.node import nullid, short
10 from mercurial import commands, cmdutil, hg, util
10 from mercurial import commands, cmdutil, hg, util
11
11
12 def fetch(ui, repo, source='default', **opts):
12 def fetch(ui, repo, source='default', **opts):
13 '''Pull changes from a remote repository, merge new changes if needed.
13 '''Pull changes from a remote repository, merge new changes if needed.
14
14
15 This finds all changes from the repository at the specified path
15 This finds all changes from the repository at the specified path
16 or URL and adds them to the local repository.
16 or URL and adds them to the local repository.
17
17
18 If the pulled changes add a new head, the head is automatically
18 If the pulled changes add a new head, the head is automatically
19 merged, and the result of the merge is committed. Otherwise, the
19 merged, and the result of the merge is committed. Otherwise, the
20 working directory is updated to include the new changes.
20 working directory is updated to include the new changes.
21
21
22 When a merge occurs, the newly pulled changes are assumed to be
22 When a merge occurs, the newly pulled changes are assumed to be
23 "authoritative". The head of the new changes is used as the first
23 "authoritative". The head of the new changes is used as the first
24 parent, with local changes as the second. To switch the merge
24 parent, with local changes as the second. To switch the merge
25 order, use --switch-parent.
25 order, use --switch-parent.
26
26
27 See 'hg help dates' for a list of formats valid for -d/--date.
27 See 'hg help dates' for a list of formats valid for -d/--date.
28 '''
28 '''
29
29
30 def postincoming(other, modheads):
30 def postincoming(other, modheads):
31 if modheads == 0:
31 if modheads == 0:
32 return 0
32 return 0
33 if modheads == 1:
33 if modheads == 1:
34 return hg.clean(repo, repo.changelog.tip())
34 return hg.clean(repo, repo.changelog.tip())
35 newheads = repo.heads(parent)
35 newheads = repo.heads(parent)
36 newchildren = [n for n in repo.heads(parent) if n != parent]
36 newchildren = [n for n in repo.heads(parent) if n != parent]
37 newparent = parent
37 newparent = parent
38 if newchildren:
38 if newchildren:
39 newparent = newchildren[0]
39 newparent = newchildren[0]
40 hg.clean(repo, newparent)
40 hg.clean(repo, newparent)
41 newheads = [n for n in repo.heads() if n != newparent]
41 newheads = [n for n in repo.heads() if n != newparent]
42 if len(newheads) > 1:
42 if len(newheads) > 1:
43 ui.status(_('not merging with %d other new heads '
43 ui.status(_('not merging with %d other new heads '
44 '(use "hg heads" and "hg merge" to merge them)') %
44 '(use "hg heads" and "hg merge" to merge them)') %
45 (len(newheads) - 1))
45 (len(newheads) - 1))
46 return
46 return
47 err = False
47 err = False
48 if newheads:
48 if newheads:
49 # By default, we consider the repository we're pulling
49 # By default, we consider the repository we're pulling
50 # *from* as authoritative, so we merge our changes into
50 # *from* as authoritative, so we merge our changes into
51 # theirs.
51 # theirs.
52 if opts['switch_parent']:
52 if opts['switch_parent']:
53 firstparent, secondparent = newparent, newheads[0]
53 firstparent, secondparent = newparent, newheads[0]
54 else:
54 else:
55 firstparent, secondparent = newheads[0], newparent
55 firstparent, secondparent = newheads[0], newparent
56 ui.status(_('updating to %d:%s\n') %
56 ui.status(_('updating to %d:%s\n') %
57 (repo.changelog.rev(firstparent),
57 (repo.changelog.rev(firstparent),
58 short(firstparent)))
58 short(firstparent)))
59 hg.clean(repo, firstparent)
59 hg.clean(repo, firstparent)
60 ui.status(_('merging with %d:%s\n') %
60 ui.status(_('merging with %d:%s\n') %
61 (repo.changelog.rev(secondparent), short(secondparent)))
61 (repo.changelog.rev(secondparent), short(secondparent)))
62 err = hg.merge(repo, secondparent, remind=False)
62 err = hg.merge(repo, secondparent, remind=False)
63 if not err:
63 if not err:
64 mod, add, rem = repo.status()[:3]
64 mod, add, rem = repo.status()[:3]
65 message = (cmdutil.logmessage(opts) or
65 message = (cmdutil.logmessage(opts) or
66 (_('Automated merge with %s') %
66 (_('Automated merge with %s') %
67 util.removeauth(other.url())))
67 util.removeauth(other.url())))
68 force_editor = opts.get('force_editor') or opts.get('edit')
68 force_editor = opts.get('force_editor') or opts.get('edit')
69 n = repo.commit(mod + add + rem, message,
69 n = repo.commit(mod + add + rem, message,
70 opts['user'], opts['date'],
70 opts['user'], opts['date'], force=True,
71 force_editor=force_editor)
71 force_editor=force_editor)
72 ui.status(_('new changeset %d:%s merges remote changes '
72 ui.status(_('new changeset %d:%s merges remote changes '
73 'with local\n') % (repo.changelog.rev(n),
73 'with local\n') % (repo.changelog.rev(n),
74 short(n)))
74 short(n)))
75
75
76 def pull():
76 def pull():
77 cmdutil.setremoteconfig(ui, opts)
77 cmdutil.setremoteconfig(ui, opts)
78
78
79 other = hg.repository(ui, ui.expandpath(source))
79 other = hg.repository(ui, ui.expandpath(source))
80 ui.status(_('pulling from %s\n') %
80 ui.status(_('pulling from %s\n') %
81 util.hidepassword(ui.expandpath(source)))
81 util.hidepassword(ui.expandpath(source)))
82 revs = None
82 revs = None
83 if opts['rev']:
83 if opts['rev']:
84 if not other.local():
84 if not other.local():
85 raise util.Abort(_("fetch -r doesn't work for remote "
85 raise util.Abort(_("fetch -r doesn't work for remote "
86 "repositories yet"))
86 "repositories yet"))
87 else:
87 else:
88 revs = [other.lookup(rev) for rev in opts['rev']]
88 revs = [other.lookup(rev) for rev in opts['rev']]
89 modheads = repo.pull(other, heads=revs)
89 modheads = repo.pull(other, heads=revs)
90 return postincoming(other, modheads)
90 return postincoming(other, modheads)
91
91
92 date = opts.get('date')
92 date = opts.get('date')
93 if date:
93 if date:
94 opts['date'] = util.parsedate(date)
94 opts['date'] = util.parsedate(date)
95
95
96 parent, p2 = repo.dirstate.parents()
96 parent, p2 = repo.dirstate.parents()
97 if parent != repo.changelog.tip():
97 if parent != repo.changelog.tip():
98 raise util.Abort(_('working dir not at tip '
98 raise util.Abort(_('working dir not at tip '
99 '(use "hg update" to check out tip)'))
99 '(use "hg update" to check out tip)'))
100 if p2 != nullid:
100 if p2 != nullid:
101 raise util.Abort(_('outstanding uncommitted merge'))
101 raise util.Abort(_('outstanding uncommitted merge'))
102 wlock = lock = None
102 wlock = lock = None
103 try:
103 try:
104 wlock = repo.wlock()
104 wlock = repo.wlock()
105 lock = repo.lock()
105 lock = repo.lock()
106 mod, add, rem, del_ = repo.status()[:4]
106 mod, add, rem, del_ = repo.status()[:4]
107 if mod or add or rem:
107 if mod or add or rem:
108 raise util.Abort(_('outstanding uncommitted changes'))
108 raise util.Abort(_('outstanding uncommitted changes'))
109 if del_:
109 if del_:
110 raise util.Abort(_('working directory is missing some files'))
110 raise util.Abort(_('working directory is missing some files'))
111 if len(repo.heads()) > 1:
111 if len(repo.heads()) > 1:
112 raise util.Abort(_('multiple heads in this repository '
112 raise util.Abort(_('multiple heads in this repository '
113 '(use "hg heads" and "hg merge" to merge)'))
113 '(use "hg heads" and "hg merge" to merge)'))
114 return pull()
114 return pull()
115 finally:
115 finally:
116 del lock, wlock
116 del lock, wlock
117
117
118 cmdtable = {
118 cmdtable = {
119 'fetch':
119 'fetch':
120 (fetch,
120 (fetch,
121 [('r', 'rev', [], _('a specific revision you would like to pull')),
121 [('r', 'rev', [], _('a specific revision you would like to pull')),
122 ('e', 'edit', None, _('edit commit message')),
122 ('e', 'edit', None, _('edit commit message')),
123 ('', 'force-editor', None, _('edit commit message (DEPRECATED)')),
123 ('', 'force-editor', None, _('edit commit message (DEPRECATED)')),
124 ('', 'switch-parent', None, _('switch parents when merging')),
124 ('', 'switch-parent', None, _('switch parents when merging')),
125 ] + commands.commitopts + commands.commitopts2 + commands.remoteopts,
125 ] + commands.commitopts + commands.commitopts2 + commands.remoteopts,
126 _('hg fetch [SOURCE]')),
126 _('hg fetch [SOURCE]')),
127 }
127 }
@@ -1,3263 +1,3266 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from repo import RepoError
9 from repo import RepoError
10 from i18n import _
10 from i18n import _
11 import os, re, sys, urllib
11 import os, re, sys, urllib
12 import hg, util, revlog, bundlerepo, extensions, copies
12 import hg, util, revlog, bundlerepo, extensions, copies
13 import difflib, patch, time, help, mdiff, tempfile
13 import difflib, patch, time, help, mdiff, tempfile
14 import version, socket
14 import version, socket
15 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
15 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
16
16
17 # Commands start here, listed alphabetically
17 # Commands start here, listed alphabetically
18
18
19 def add(ui, repo, *pats, **opts):
19 def add(ui, repo, *pats, **opts):
20 """add the specified files on the next commit
20 """add the specified files on the next commit
21
21
22 Schedule files to be version controlled and added to the repository.
22 Schedule files to be version controlled and added to the repository.
23
23
24 The files will be added to the repository at the next commit. To
24 The files will be added to the repository at the next commit. To
25 undo an add before that, see hg revert.
25 undo an add before that, see hg revert.
26
26
27 If no names are given, add all files in the repository.
27 If no names are given, add all files in the repository.
28 """
28 """
29
29
30 rejected = None
30 rejected = None
31 exacts = {}
31 exacts = {}
32 names = []
32 names = []
33 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
33 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
34 badmatch=util.always):
34 badmatch=util.always):
35 if exact:
35 if exact:
36 if ui.verbose:
36 if ui.verbose:
37 ui.status(_('adding %s\n') % rel)
37 ui.status(_('adding %s\n') % rel)
38 names.append(abs)
38 names.append(abs)
39 exacts[abs] = 1
39 exacts[abs] = 1
40 elif abs not in repo.dirstate:
40 elif abs not in repo.dirstate:
41 ui.status(_('adding %s\n') % rel)
41 ui.status(_('adding %s\n') % rel)
42 names.append(abs)
42 names.append(abs)
43 if not opts.get('dry_run'):
43 if not opts.get('dry_run'):
44 rejected = repo.add(names)
44 rejected = repo.add(names)
45 rejected = [p for p in rejected if p in exacts]
45 rejected = [p for p in rejected if p in exacts]
46 return rejected and 1 or 0
46 return rejected and 1 or 0
47
47
48 def addremove(ui, repo, *pats, **opts):
48 def addremove(ui, repo, *pats, **opts):
49 """add all new files, delete all missing files
49 """add all new files, delete all missing files
50
50
51 Add all new files and remove all missing files from the repository.
51 Add all new files and remove all missing files from the repository.
52
52
53 New files are ignored if they match any of the patterns in .hgignore. As
53 New files are ignored if they match any of the patterns in .hgignore. As
54 with add, these changes take effect at the next commit.
54 with add, these changes take effect at the next commit.
55
55
56 Use the -s option to detect renamed files. With a parameter > 0,
56 Use the -s option to detect renamed files. With a parameter > 0,
57 this compares every removed file with every added file and records
57 this compares every removed file with every added file and records
58 those similar enough as renames. This option takes a percentage
58 those similar enough as renames. This option takes a percentage
59 between 0 (disabled) and 100 (files must be identical) as its
59 between 0 (disabled) and 100 (files must be identical) as its
60 parameter. Detecting renamed files this way can be expensive.
60 parameter. Detecting renamed files this way can be expensive.
61 """
61 """
62 try:
62 try:
63 sim = float(opts.get('similarity') or 0)
63 sim = float(opts.get('similarity') or 0)
64 except ValueError:
64 except ValueError:
65 raise util.Abort(_('similarity must be a number'))
65 raise util.Abort(_('similarity must be a number'))
66 if sim < 0 or sim > 100:
66 if sim < 0 or sim > 100:
67 raise util.Abort(_('similarity must be between 0 and 100'))
67 raise util.Abort(_('similarity must be between 0 and 100'))
68 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
68 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
69
69
70 def annotate(ui, repo, *pats, **opts):
70 def annotate(ui, repo, *pats, **opts):
71 """show changeset information per file line
71 """show changeset information per file line
72
72
73 List changes in files, showing the revision id responsible for each line
73 List changes in files, showing the revision id responsible for each line
74
74
75 This command is useful to discover who did a change or when a change took
75 This command is useful to discover who did a change or when a change took
76 place.
76 place.
77
77
78 Without the -a option, annotate will avoid processing files it
78 Without the -a option, annotate will avoid processing files it
79 detects as binary. With -a, annotate will generate an annotation
79 detects as binary. With -a, annotate will generate an annotation
80 anyway, probably with undesirable results.
80 anyway, probably with undesirable results.
81 """
81 """
82 datefunc = ui.quiet and util.shortdate or util.datestr
82 datefunc = ui.quiet and util.shortdate or util.datestr
83 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
83 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
84
84
85 if not pats:
85 if not pats:
86 raise util.Abort(_('at least one file name or pattern required'))
86 raise util.Abort(_('at least one file name or pattern required'))
87
87
88 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
88 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
89 ('number', lambda x: str(x[0].rev())),
89 ('number', lambda x: str(x[0].rev())),
90 ('changeset', lambda x: short(x[0].node())),
90 ('changeset', lambda x: short(x[0].node())),
91 ('date', getdate),
91 ('date', getdate),
92 ('follow', lambda x: x[0].path()),
92 ('follow', lambda x: x[0].path()),
93 ]
93 ]
94
94
95 if (not opts['user'] and not opts['changeset'] and not opts['date']
95 if (not opts['user'] and not opts['changeset'] and not opts['date']
96 and not opts['follow']):
96 and not opts['follow']):
97 opts['number'] = 1
97 opts['number'] = 1
98
98
99 linenumber = opts.get('line_number') is not None
99 linenumber = opts.get('line_number') is not None
100 if (linenumber and (not opts['changeset']) and (not opts['number'])):
100 if (linenumber and (not opts['changeset']) and (not opts['number'])):
101 raise util.Abort(_('at least one of -n/-c is required for -l'))
101 raise util.Abort(_('at least one of -n/-c is required for -l'))
102
102
103 funcmap = [func for op, func in opmap if opts.get(op)]
103 funcmap = [func for op, func in opmap if opts.get(op)]
104 if linenumber:
104 if linenumber:
105 lastfunc = funcmap[-1]
105 lastfunc = funcmap[-1]
106 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
106 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
107
107
108 ctx = repo.changectx(opts['rev'])
108 ctx = repo.changectx(opts['rev'])
109
109
110 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
110 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
111 node=ctx.node()):
111 node=ctx.node()):
112 fctx = ctx.filectx(abs)
112 fctx = ctx.filectx(abs)
113 if not opts['text'] and util.binary(fctx.data()):
113 if not opts['text'] and util.binary(fctx.data()):
114 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
114 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
115 continue
115 continue
116
116
117 lines = fctx.annotate(follow=opts.get('follow'),
117 lines = fctx.annotate(follow=opts.get('follow'),
118 linenumber=linenumber)
118 linenumber=linenumber)
119 pieces = []
119 pieces = []
120
120
121 for f in funcmap:
121 for f in funcmap:
122 l = [f(n) for n, dummy in lines]
122 l = [f(n) for n, dummy in lines]
123 if l:
123 if l:
124 m = max(map(len, l))
124 m = max(map(len, l))
125 pieces.append(["%*s" % (m, x) for x in l])
125 pieces.append(["%*s" % (m, x) for x in l])
126
126
127 if pieces:
127 if pieces:
128 for p, l in zip(zip(*pieces), lines):
128 for p, l in zip(zip(*pieces), lines):
129 ui.write("%s: %s" % (" ".join(p), l[1]))
129 ui.write("%s: %s" % (" ".join(p), l[1]))
130
130
131 def archive(ui, repo, dest, **opts):
131 def archive(ui, repo, dest, **opts):
132 '''create unversioned archive of a repository revision
132 '''create unversioned archive of a repository revision
133
133
134 By default, the revision used is the parent of the working
134 By default, the revision used is the parent of the working
135 directory; use "-r" to specify a different revision.
135 directory; use "-r" to specify a different revision.
136
136
137 To specify the type of archive to create, use "-t". Valid
137 To specify the type of archive to create, use "-t". Valid
138 types are:
138 types are:
139
139
140 "files" (default): a directory full of files
140 "files" (default): a directory full of files
141 "tar": tar archive, uncompressed
141 "tar": tar archive, uncompressed
142 "tbz2": tar archive, compressed using bzip2
142 "tbz2": tar archive, compressed using bzip2
143 "tgz": tar archive, compressed using gzip
143 "tgz": tar archive, compressed using gzip
144 "uzip": zip archive, uncompressed
144 "uzip": zip archive, uncompressed
145 "zip": zip archive, compressed using deflate
145 "zip": zip archive, compressed using deflate
146
146
147 The exact name of the destination archive or directory is given
147 The exact name of the destination archive or directory is given
148 using a format string; see "hg help export" for details.
148 using a format string; see "hg help export" for details.
149
149
150 Each member added to an archive file has a directory prefix
150 Each member added to an archive file has a directory prefix
151 prepended. Use "-p" to specify a format string for the prefix.
151 prepended. Use "-p" to specify a format string for the prefix.
152 The default is the basename of the archive, with suffixes removed.
152 The default is the basename of the archive, with suffixes removed.
153 '''
153 '''
154
154
155 ctx = repo.changectx(opts['rev'])
155 ctx = repo.changectx(opts['rev'])
156 if not ctx:
156 if not ctx:
157 raise util.Abort(_('repository has no revisions'))
157 raise util.Abort(_('repository has no revisions'))
158 node = ctx.node()
158 node = ctx.node()
159 dest = cmdutil.make_filename(repo, dest, node)
159 dest = cmdutil.make_filename(repo, dest, node)
160 if os.path.realpath(dest) == repo.root:
160 if os.path.realpath(dest) == repo.root:
161 raise util.Abort(_('repository root cannot be destination'))
161 raise util.Abort(_('repository root cannot be destination'))
162 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
162 dummy, matchfn, dummy = cmdutil.matchpats(repo, [], opts)
163 kind = opts.get('type') or 'files'
163 kind = opts.get('type') or 'files'
164 prefix = opts['prefix']
164 prefix = opts['prefix']
165 if dest == '-':
165 if dest == '-':
166 if kind == 'files':
166 if kind == 'files':
167 raise util.Abort(_('cannot archive plain files to stdout'))
167 raise util.Abort(_('cannot archive plain files to stdout'))
168 dest = sys.stdout
168 dest = sys.stdout
169 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
169 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
170 prefix = cmdutil.make_filename(repo, prefix, node)
170 prefix = cmdutil.make_filename(repo, prefix, node)
171 archival.archive(repo, dest, node, kind, not opts['no_decode'],
171 archival.archive(repo, dest, node, kind, not opts['no_decode'],
172 matchfn, prefix)
172 matchfn, prefix)
173
173
174 def backout(ui, repo, node=None, rev=None, **opts):
174 def backout(ui, repo, node=None, rev=None, **opts):
175 '''reverse effect of earlier changeset
175 '''reverse effect of earlier changeset
176
176
177 Commit the backed out changes as a new changeset. The new
177 Commit the backed out changes as a new changeset. The new
178 changeset is a child of the backed out changeset.
178 changeset is a child of the backed out changeset.
179
179
180 If you back out a changeset other than the tip, a new head is
180 If you back out a changeset other than the tip, a new head is
181 created. This head will be the new tip and you should merge this
181 created. This head will be the new tip and you should merge this
182 backout changeset with another head (current one by default).
182 backout changeset with another head (current one by default).
183
183
184 The --merge option remembers the parent of the working directory
184 The --merge option remembers the parent of the working directory
185 before starting the backout, then merges the new head with that
185 before starting the backout, then merges the new head with that
186 changeset afterwards. This saves you from doing the merge by
186 changeset afterwards. This saves you from doing the merge by
187 hand. The result of this merge is not committed, as for a normal
187 hand. The result of this merge is not committed, as for a normal
188 merge.
188 merge.
189
189
190 See 'hg help dates' for a list of formats valid for -d/--date.
190 See 'hg help dates' for a list of formats valid for -d/--date.
191 '''
191 '''
192 if rev and node:
192 if rev and node:
193 raise util.Abort(_("please specify just one revision"))
193 raise util.Abort(_("please specify just one revision"))
194
194
195 if not rev:
195 if not rev:
196 rev = node
196 rev = node
197
197
198 if not rev:
198 if not rev:
199 raise util.Abort(_("please specify a revision to backout"))
199 raise util.Abort(_("please specify a revision to backout"))
200
200
201 date = opts.get('date')
201 date = opts.get('date')
202 if date:
202 if date:
203 opts['date'] = util.parsedate(date)
203 opts['date'] = util.parsedate(date)
204
204
205 cmdutil.bail_if_changed(repo)
205 cmdutil.bail_if_changed(repo)
206 node = repo.lookup(rev)
206 node = repo.lookup(rev)
207
207
208 op1, op2 = repo.dirstate.parents()
208 op1, op2 = repo.dirstate.parents()
209 a = repo.changelog.ancestor(op1, node)
209 a = repo.changelog.ancestor(op1, node)
210 if a != node:
210 if a != node:
211 raise util.Abort(_('cannot back out change on a different branch'))
211 raise util.Abort(_('cannot back out change on a different branch'))
212
212
213 p1, p2 = repo.changelog.parents(node)
213 p1, p2 = repo.changelog.parents(node)
214 if p1 == nullid:
214 if p1 == nullid:
215 raise util.Abort(_('cannot back out a change with no parents'))
215 raise util.Abort(_('cannot back out a change with no parents'))
216 if p2 != nullid:
216 if p2 != nullid:
217 if not opts['parent']:
217 if not opts['parent']:
218 raise util.Abort(_('cannot back out a merge changeset without '
218 raise util.Abort(_('cannot back out a merge changeset without '
219 '--parent'))
219 '--parent'))
220 p = repo.lookup(opts['parent'])
220 p = repo.lookup(opts['parent'])
221 if p not in (p1, p2):
221 if p not in (p1, p2):
222 raise util.Abort(_('%s is not a parent of %s') %
222 raise util.Abort(_('%s is not a parent of %s') %
223 (short(p), short(node)))
223 (short(p), short(node)))
224 parent = p
224 parent = p
225 else:
225 else:
226 if opts['parent']:
226 if opts['parent']:
227 raise util.Abort(_('cannot use --parent on non-merge changeset'))
227 raise util.Abort(_('cannot use --parent on non-merge changeset'))
228 parent = p1
228 parent = p1
229
229
230 hg.clean(repo, node, show_stats=False)
230 hg.clean(repo, node, show_stats=False)
231 revert_opts = opts.copy()
231 revert_opts = opts.copy()
232 revert_opts['date'] = None
232 revert_opts['date'] = None
233 revert_opts['all'] = True
233 revert_opts['all'] = True
234 revert_opts['rev'] = hex(parent)
234 revert_opts['rev'] = hex(parent)
235 revert_opts['no_backup'] = None
235 revert_opts['no_backup'] = None
236 revert(ui, repo, **revert_opts)
236 revert(ui, repo, **revert_opts)
237 commit_opts = opts.copy()
237 commit_opts = opts.copy()
238 commit_opts['addremove'] = False
238 commit_opts['addremove'] = False
239 if not commit_opts['message'] and not commit_opts['logfile']:
239 if not commit_opts['message'] and not commit_opts['logfile']:
240 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
240 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
241 commit_opts['force_editor'] = True
241 commit_opts['force_editor'] = True
242 commit(ui, repo, **commit_opts)
242 commit(ui, repo, **commit_opts)
243 def nice(node):
243 def nice(node):
244 return '%d:%s' % (repo.changelog.rev(node), short(node))
244 return '%d:%s' % (repo.changelog.rev(node), short(node))
245 ui.status(_('changeset %s backs out changeset %s\n') %
245 ui.status(_('changeset %s backs out changeset %s\n') %
246 (nice(repo.changelog.tip()), nice(node)))
246 (nice(repo.changelog.tip()), nice(node)))
247 if op1 != node:
247 if op1 != node:
248 hg.clean(repo, op1, show_stats=False)
248 hg.clean(repo, op1, show_stats=False)
249 if opts['merge']:
249 if opts['merge']:
250 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
250 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
251 hg.merge(repo, hex(repo.changelog.tip()))
251 hg.merge(repo, hex(repo.changelog.tip()))
252 else:
252 else:
253 ui.status(_('the backout changeset is a new head - '
253 ui.status(_('the backout changeset is a new head - '
254 'do not forget to merge\n'))
254 'do not forget to merge\n'))
255 ui.status(_('(use "backout --merge" '
255 ui.status(_('(use "backout --merge" '
256 'if you want to auto-merge)\n'))
256 'if you want to auto-merge)\n'))
257
257
258 def bisect(ui, repo, rev=None, extra=None,
258 def bisect(ui, repo, rev=None, extra=None,
259 reset=None, good=None, bad=None, skip=None, noupdate=None):
259 reset=None, good=None, bad=None, skip=None, noupdate=None):
260 """subdivision search of changesets
260 """subdivision search of changesets
261
261
262 This command helps to find changesets which introduce problems.
262 This command helps to find changesets which introduce problems.
263 To use, mark the earliest changeset you know exhibits the problem
263 To use, mark the earliest changeset you know exhibits the problem
264 as bad, then mark the latest changeset which is free from the
264 as bad, then mark the latest changeset which is free from the
265 problem as good. Bisect will update your working directory to a
265 problem as good. Bisect will update your working directory to a
266 revision for testing. Once you have performed tests, mark the
266 revision for testing. Once you have performed tests, mark the
267 working directory as bad or good and bisect will either update to
267 working directory as bad or good and bisect will either update to
268 another candidate changeset or announce that it has found the bad
268 another candidate changeset or announce that it has found the bad
269 revision.
269 revision.
270 """
270 """
271 # backward compatibility
271 # backward compatibility
272 if rev in "good bad reset init".split():
272 if rev in "good bad reset init".split():
273 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
273 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
274 cmd, rev, extra = rev, extra, None
274 cmd, rev, extra = rev, extra, None
275 if cmd == "good":
275 if cmd == "good":
276 good = True
276 good = True
277 elif cmd == "bad":
277 elif cmd == "bad":
278 bad = True
278 bad = True
279 else:
279 else:
280 reset = True
280 reset = True
281 elif extra or good + bad + skip + reset > 1:
281 elif extra or good + bad + skip + reset > 1:
282 raise util.Abort("Incompatible arguments")
282 raise util.Abort("Incompatible arguments")
283
283
284 if reset:
284 if reset:
285 p = repo.join("bisect.state")
285 p = repo.join("bisect.state")
286 if os.path.exists(p):
286 if os.path.exists(p):
287 os.unlink(p)
287 os.unlink(p)
288 return
288 return
289
289
290 # load state
290 # load state
291 state = {'good': [], 'bad': [], 'skip': []}
291 state = {'good': [], 'bad': [], 'skip': []}
292 if os.path.exists(repo.join("bisect.state")):
292 if os.path.exists(repo.join("bisect.state")):
293 for l in repo.opener("bisect.state"):
293 for l in repo.opener("bisect.state"):
294 kind, node = l[:-1].split()
294 kind, node = l[:-1].split()
295 node = repo.lookup(node)
295 node = repo.lookup(node)
296 if kind not in state:
296 if kind not in state:
297 raise util.Abort(_("unknown bisect kind %s") % kind)
297 raise util.Abort(_("unknown bisect kind %s") % kind)
298 state[kind].append(node)
298 state[kind].append(node)
299
299
300 # update state
300 # update state
301 node = repo.lookup(rev or '.')
301 node = repo.lookup(rev or '.')
302 if good:
302 if good:
303 state['good'].append(node)
303 state['good'].append(node)
304 elif bad:
304 elif bad:
305 state['bad'].append(node)
305 state['bad'].append(node)
306 elif skip:
306 elif skip:
307 state['skip'].append(node)
307 state['skip'].append(node)
308
308
309 # save state
309 # save state
310 f = repo.opener("bisect.state", "w", atomictemp=True)
310 f = repo.opener("bisect.state", "w", atomictemp=True)
311 wlock = repo.wlock()
311 wlock = repo.wlock()
312 try:
312 try:
313 for kind in state:
313 for kind in state:
314 for node in state[kind]:
314 for node in state[kind]:
315 f.write("%s %s\n" % (kind, hex(node)))
315 f.write("%s %s\n" % (kind, hex(node)))
316 f.rename()
316 f.rename()
317 finally:
317 finally:
318 del wlock
318 del wlock
319
319
320 if not state['good'] or not state['bad']:
320 if not state['good'] or not state['bad']:
321 return
321 return
322
322
323 # actually bisect
323 # actually bisect
324 node, changesets, good = hbisect.bisect(repo.changelog, state)
324 node, changesets, good = hbisect.bisect(repo.changelog, state)
325 if changesets == 0:
325 if changesets == 0:
326 ui.write(_("The first %s revision is:\n") % (good and "good" or "bad"))
326 ui.write(_("The first %s revision is:\n") % (good and "good" or "bad"))
327 displayer = cmdutil.show_changeset(ui, repo, {})
327 displayer = cmdutil.show_changeset(ui, repo, {})
328 displayer.show(changenode=node)
328 displayer.show(changenode=node)
329 elif node is not None:
329 elif node is not None:
330 # compute the approximate number of remaining tests
330 # compute the approximate number of remaining tests
331 tests, size = 0, 2
331 tests, size = 0, 2
332 while size <= changesets:
332 while size <= changesets:
333 tests, size = tests + 1, size * 2
333 tests, size = tests + 1, size * 2
334 rev = repo.changelog.rev(node)
334 rev = repo.changelog.rev(node)
335 ui.write(_("Testing changeset %s:%s "
335 ui.write(_("Testing changeset %s:%s "
336 "(%s changesets remaining, ~%s tests)\n")
336 "(%s changesets remaining, ~%s tests)\n")
337 % (rev, short(node), changesets, tests))
337 % (rev, short(node), changesets, tests))
338 if not noupdate:
338 if not noupdate:
339 cmdutil.bail_if_changed(repo)
339 cmdutil.bail_if_changed(repo)
340 return hg.clean(repo, node)
340 return hg.clean(repo, node)
341
341
342 def branch(ui, repo, label=None, **opts):
342 def branch(ui, repo, label=None, **opts):
343 """set or show the current branch name
343 """set or show the current branch name
344
344
345 With no argument, show the current branch name. With one argument,
345 With no argument, show the current branch name. With one argument,
346 set the working directory branch name (the branch does not exist in
346 set the working directory branch name (the branch does not exist in
347 the repository until the next commit).
347 the repository until the next commit).
348
348
349 Unless --force is specified, branch will not let you set a
349 Unless --force is specified, branch will not let you set a
350 branch name that shadows an existing branch.
350 branch name that shadows an existing branch.
351
351
352 Use the command 'hg update' to switch to an existing branch.
352 Use the command 'hg update' to switch to an existing branch.
353 """
353 """
354
354
355 if label:
355 if label:
356 if not opts.get('force') and label in repo.branchtags():
356 if not opts.get('force') and label in repo.branchtags():
357 if label not in [p.branch() for p in repo.workingctx().parents()]:
357 if label not in [p.branch() for p in repo.workingctx().parents()]:
358 raise util.Abort(_('a branch of the same name already exists'
358 raise util.Abort(_('a branch of the same name already exists'
359 ' (use --force to override)'))
359 ' (use --force to override)'))
360 repo.dirstate.setbranch(util.fromlocal(label))
360 repo.dirstate.setbranch(util.fromlocal(label))
361 ui.status(_('marked working directory as branch %s\n') % label)
361 ui.status(_('marked working directory as branch %s\n') % label)
362 else:
362 else:
363 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
363 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
364
364
365 def branches(ui, repo, active=False):
365 def branches(ui, repo, active=False):
366 """list repository named branches
366 """list repository named branches
367
367
368 List the repository's named branches, indicating which ones are
368 List the repository's named branches, indicating which ones are
369 inactive. If active is specified, only show active branches.
369 inactive. If active is specified, only show active branches.
370
370
371 A branch is considered active if it contains unmerged heads.
371 A branch is considered active if it contains unmerged heads.
372
372
373 Use the command 'hg update' to switch to an existing branch.
373 Use the command 'hg update' to switch to an existing branch.
374 """
374 """
375 b = repo.branchtags()
375 b = repo.branchtags()
376 heads = dict.fromkeys(repo.heads(), 1)
376 heads = dict.fromkeys(repo.heads(), 1)
377 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
377 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
378 l.sort()
378 l.sort()
379 l.reverse()
379 l.reverse()
380 for ishead, r, n, t in l:
380 for ishead, r, n, t in l:
381 if active and not ishead:
381 if active and not ishead:
382 # If we're only displaying active branches, abort the loop on
382 # If we're only displaying active branches, abort the loop on
383 # encountering the first inactive head
383 # encountering the first inactive head
384 break
384 break
385 else:
385 else:
386 hexfunc = ui.debugflag and hex or short
386 hexfunc = ui.debugflag and hex or short
387 if ui.quiet:
387 if ui.quiet:
388 ui.write("%s\n" % t)
388 ui.write("%s\n" % t)
389 else:
389 else:
390 spaces = " " * (30 - util.locallen(t))
390 spaces = " " * (30 - util.locallen(t))
391 # The code only gets here if inactive branches are being
391 # The code only gets here if inactive branches are being
392 # displayed or the branch is active.
392 # displayed or the branch is active.
393 isinactive = ((not ishead) and " (inactive)") or ''
393 isinactive = ((not ishead) and " (inactive)") or ''
394 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
394 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
395
395
396 def bundle(ui, repo, fname, dest=None, **opts):
396 def bundle(ui, repo, fname, dest=None, **opts):
397 """create a changegroup file
397 """create a changegroup file
398
398
399 Generate a compressed changegroup file collecting changesets not
399 Generate a compressed changegroup file collecting changesets not
400 found in the other repository.
400 found in the other repository.
401
401
402 If no destination repository is specified the destination is
402 If no destination repository is specified the destination is
403 assumed to have all the nodes specified by one or more --base
403 assumed to have all the nodes specified by one or more --base
404 parameters. To create a bundle containing all changesets, use
404 parameters. To create a bundle containing all changesets, use
405 --all (or --base null).
405 --all (or --base null).
406
406
407 The bundle file can then be transferred using conventional means and
407 The bundle file can then be transferred using conventional means and
408 applied to another repository with the unbundle or pull command.
408 applied to another repository with the unbundle or pull command.
409 This is useful when direct push and pull are not available or when
409 This is useful when direct push and pull are not available or when
410 exporting an entire repository is undesirable.
410 exporting an entire repository is undesirable.
411
411
412 Applying bundles preserves all changeset contents including
412 Applying bundles preserves all changeset contents including
413 permissions, copy/rename information, and revision history.
413 permissions, copy/rename information, and revision history.
414 """
414 """
415 revs = opts.get('rev') or None
415 revs = opts.get('rev') or None
416 if revs:
416 if revs:
417 revs = [repo.lookup(rev) for rev in revs]
417 revs = [repo.lookup(rev) for rev in revs]
418 if opts.get('all'):
418 if opts.get('all'):
419 base = ['null']
419 base = ['null']
420 else:
420 else:
421 base = opts.get('base')
421 base = opts.get('base')
422 if base:
422 if base:
423 if dest:
423 if dest:
424 raise util.Abort(_("--base is incompatible with specifiying "
424 raise util.Abort(_("--base is incompatible with specifiying "
425 "a destination"))
425 "a destination"))
426 base = [repo.lookup(rev) for rev in base]
426 base = [repo.lookup(rev) for rev in base]
427 # create the right base
427 # create the right base
428 # XXX: nodesbetween / changegroup* should be "fixed" instead
428 # XXX: nodesbetween / changegroup* should be "fixed" instead
429 o = []
429 o = []
430 has = {nullid: None}
430 has = {nullid: None}
431 for n in base:
431 for n in base:
432 has.update(repo.changelog.reachable(n))
432 has.update(repo.changelog.reachable(n))
433 if revs:
433 if revs:
434 visit = list(revs)
434 visit = list(revs)
435 else:
435 else:
436 visit = repo.changelog.heads()
436 visit = repo.changelog.heads()
437 seen = {}
437 seen = {}
438 while visit:
438 while visit:
439 n = visit.pop(0)
439 n = visit.pop(0)
440 parents = [p for p in repo.changelog.parents(n) if p not in has]
440 parents = [p for p in repo.changelog.parents(n) if p not in has]
441 if len(parents) == 0:
441 if len(parents) == 0:
442 o.insert(0, n)
442 o.insert(0, n)
443 else:
443 else:
444 for p in parents:
444 for p in parents:
445 if p not in seen:
445 if p not in seen:
446 seen[p] = 1
446 seen[p] = 1
447 visit.append(p)
447 visit.append(p)
448 else:
448 else:
449 cmdutil.setremoteconfig(ui, opts)
449 cmdutil.setremoteconfig(ui, opts)
450 dest, revs, checkout = hg.parseurl(
450 dest, revs, checkout = hg.parseurl(
451 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
451 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
452 other = hg.repository(ui, dest)
452 other = hg.repository(ui, dest)
453 o = repo.findoutgoing(other, force=opts['force'])
453 o = repo.findoutgoing(other, force=opts['force'])
454
454
455 if revs:
455 if revs:
456 cg = repo.changegroupsubset(o, revs, 'bundle')
456 cg = repo.changegroupsubset(o, revs, 'bundle')
457 else:
457 else:
458 cg = repo.changegroup(o, 'bundle')
458 cg = repo.changegroup(o, 'bundle')
459 changegroup.writebundle(cg, fname, "HG10BZ")
459 changegroup.writebundle(cg, fname, "HG10BZ")
460
460
461 def cat(ui, repo, file1, *pats, **opts):
461 def cat(ui, repo, file1, *pats, **opts):
462 """output the current or given revision of files
462 """output the current or given revision of files
463
463
464 Print the specified files as they were at the given revision.
464 Print the specified files as they were at the given revision.
465 If no revision is given, the parent of the working directory is used,
465 If no revision is given, the parent of the working directory is used,
466 or tip if no revision is checked out.
466 or tip if no revision is checked out.
467
467
468 Output may be to a file, in which case the name of the file is
468 Output may be to a file, in which case the name of the file is
469 given using a format string. The formatting rules are the same as
469 given using a format string. The formatting rules are the same as
470 for the export command, with the following additions:
470 for the export command, with the following additions:
471
471
472 %s basename of file being printed
472 %s basename of file being printed
473 %d dirname of file being printed, or '.' if in repo root
473 %d dirname of file being printed, or '.' if in repo root
474 %p root-relative path name of file being printed
474 %p root-relative path name of file being printed
475 """
475 """
476 ctx = repo.changectx(opts['rev'])
476 ctx = repo.changectx(opts['rev'])
477 err = 1
477 err = 1
478 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
478 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
479 ctx.node()):
479 ctx.node()):
480 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
480 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
481 data = ctx.filectx(abs).data()
481 data = ctx.filectx(abs).data()
482 if opts.get('decode'):
482 if opts.get('decode'):
483 data = repo.wwritedata(abs, data)
483 data = repo.wwritedata(abs, data)
484 fp.write(data)
484 fp.write(data)
485 err = 0
485 err = 0
486 return err
486 return err
487
487
488 def clone(ui, source, dest=None, **opts):
488 def clone(ui, source, dest=None, **opts):
489 """make a copy of an existing repository
489 """make a copy of an existing repository
490
490
491 Create a copy of an existing repository in a new directory.
491 Create a copy of an existing repository in a new directory.
492
492
493 If no destination directory name is specified, it defaults to the
493 If no destination directory name is specified, it defaults to the
494 basename of the source.
494 basename of the source.
495
495
496 The location of the source is added to the new repository's
496 The location of the source is added to the new repository's
497 .hg/hgrc file, as the default to be used for future pulls.
497 .hg/hgrc file, as the default to be used for future pulls.
498
498
499 For efficiency, hardlinks are used for cloning whenever the source
499 For efficiency, hardlinks are used for cloning whenever the source
500 and destination are on the same filesystem (note this applies only
500 and destination are on the same filesystem (note this applies only
501 to the repository data, not to the checked out files). Some
501 to the repository data, not to the checked out files). Some
502 filesystems, such as AFS, implement hardlinking incorrectly, but
502 filesystems, such as AFS, implement hardlinking incorrectly, but
503 do not report errors. In these cases, use the --pull option to
503 do not report errors. In these cases, use the --pull option to
504 avoid hardlinking.
504 avoid hardlinking.
505
505
506 You can safely clone repositories and checked out files using full
506 You can safely clone repositories and checked out files using full
507 hardlinks with
507 hardlinks with
508
508
509 $ cp -al REPO REPOCLONE
509 $ cp -al REPO REPOCLONE
510
510
511 which is the fastest way to clone. However, the operation is not
511 which is the fastest way to clone. However, the operation is not
512 atomic (making sure REPO is not modified during the operation is
512 atomic (making sure REPO is not modified during the operation is
513 up to you) and you have to make sure your editor breaks hardlinks
513 up to you) and you have to make sure your editor breaks hardlinks
514 (Emacs and most Linux Kernel tools do so).
514 (Emacs and most Linux Kernel tools do so).
515
515
516 If you use the -r option to clone up to a specific revision, no
516 If you use the -r option to clone up to a specific revision, no
517 subsequent revisions will be present in the cloned repository.
517 subsequent revisions will be present in the cloned repository.
518 This option implies --pull, even on local repositories.
518 This option implies --pull, even on local repositories.
519
519
520 See pull for valid source format details.
520 See pull for valid source format details.
521
521
522 It is possible to specify an ssh:// URL as the destination, but no
522 It is possible to specify an ssh:// URL as the destination, but no
523 .hg/hgrc and working directory will be created on the remote side.
523 .hg/hgrc and working directory will be created on the remote side.
524 Look at the help text for the pull command for important details
524 Look at the help text for the pull command for important details
525 about ssh:// URLs.
525 about ssh:// URLs.
526 """
526 """
527 cmdutil.setremoteconfig(ui, opts)
527 cmdutil.setremoteconfig(ui, opts)
528 hg.clone(ui, source, dest,
528 hg.clone(ui, source, dest,
529 pull=opts['pull'],
529 pull=opts['pull'],
530 stream=opts['uncompressed'],
530 stream=opts['uncompressed'],
531 rev=opts['rev'],
531 rev=opts['rev'],
532 update=not opts['noupdate'])
532 update=not opts['noupdate'])
533
533
534 def commit(ui, repo, *pats, **opts):
534 def commit(ui, repo, *pats, **opts):
535 """commit the specified files or all outstanding changes
535 """commit the specified files or all outstanding changes
536
536
537 Commit changes to the given files into the repository.
537 Commit changes to the given files into the repository.
538
538
539 If a list of files is omitted, all changes reported by "hg status"
539 If a list of files is omitted, all changes reported by "hg status"
540 will be committed.
540 will be committed.
541
541
542 If you are committing the result of a merge, do not provide any
543 file names or -I/-X filters.
544
542 If no commit message is specified, the configured editor is started to
545 If no commit message is specified, the configured editor is started to
543 enter a message.
546 enter a message.
544
547
545 See 'hg help dates' for a list of formats valid for -d/--date.
548 See 'hg help dates' for a list of formats valid for -d/--date.
546 """
549 """
547 def commitfunc(ui, repo, files, message, match, opts):
550 def commitfunc(ui, repo, files, message, match, opts):
548 return repo.commit(files, message, opts['user'], opts['date'], match,
551 return repo.commit(files, message, opts['user'], opts['date'], match,
549 force_editor=opts.get('force_editor'))
552 force_editor=opts.get('force_editor'))
550
553
551 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
554 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
552 if not node:
555 if not node:
553 return
556 return
554 cl = repo.changelog
557 cl = repo.changelog
555 rev = cl.rev(node)
558 rev = cl.rev(node)
556 parents = cl.parentrevs(rev)
559 parents = cl.parentrevs(rev)
557 if rev - 1 in parents:
560 if rev - 1 in parents:
558 # one of the parents was the old tip
561 # one of the parents was the old tip
559 return
562 return
560 if (parents == (nullrev, nullrev) or
563 if (parents == (nullrev, nullrev) or
561 len(cl.heads(cl.node(parents[0]))) > 1 and
564 len(cl.heads(cl.node(parents[0]))) > 1 and
562 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
565 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
563 ui.status(_('created new head\n'))
566 ui.status(_('created new head\n'))
564
567
565 def copy(ui, repo, *pats, **opts):
568 def copy(ui, repo, *pats, **opts):
566 """mark files as copied for the next commit
569 """mark files as copied for the next commit
567
570
568 Mark dest as having copies of source files. If dest is a
571 Mark dest as having copies of source files. If dest is a
569 directory, copies are put in that directory. If dest is a file,
572 directory, copies are put in that directory. If dest is a file,
570 there can only be one source.
573 there can only be one source.
571
574
572 By default, this command copies the contents of files as they
575 By default, this command copies the contents of files as they
573 stand in the working directory. If invoked with --after, the
576 stand in the working directory. If invoked with --after, the
574 operation is recorded, but no copying is performed.
577 operation is recorded, but no copying is performed.
575
578
576 This command takes effect in the next commit. To undo a copy
579 This command takes effect in the next commit. To undo a copy
577 before that, see hg revert.
580 before that, see hg revert.
578 """
581 """
579 wlock = repo.wlock(False)
582 wlock = repo.wlock(False)
580 try:
583 try:
581 return cmdutil.copy(ui, repo, pats, opts)
584 return cmdutil.copy(ui, repo, pats, opts)
582 finally:
585 finally:
583 del wlock
586 del wlock
584
587
585 def debugancestor(ui, repo, *args):
588 def debugancestor(ui, repo, *args):
586 """find the ancestor revision of two revisions in a given index"""
589 """find the ancestor revision of two revisions in a given index"""
587 if len(args) == 3:
590 if len(args) == 3:
588 index, rev1, rev2 = args
591 index, rev1, rev2 = args
589 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
592 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
590 lookup = r.lookup
593 lookup = r.lookup
591 elif len(args) == 2:
594 elif len(args) == 2:
592 if not repo:
595 if not repo:
593 raise util.Abort(_("There is no Mercurial repository here "
596 raise util.Abort(_("There is no Mercurial repository here "
594 "(.hg not found)"))
597 "(.hg not found)"))
595 rev1, rev2 = args
598 rev1, rev2 = args
596 r = repo.changelog
599 r = repo.changelog
597 lookup = repo.lookup
600 lookup = repo.lookup
598 else:
601 else:
599 raise util.Abort(_('either two or three arguments required'))
602 raise util.Abort(_('either two or three arguments required'))
600 a = r.ancestor(lookup(rev1), lookup(rev2))
603 a = r.ancestor(lookup(rev1), lookup(rev2))
601 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
604 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
602
605
603 def debugcomplete(ui, cmd='', **opts):
606 def debugcomplete(ui, cmd='', **opts):
604 """returns the completion list associated with the given command"""
607 """returns the completion list associated with the given command"""
605
608
606 if opts['options']:
609 if opts['options']:
607 options = []
610 options = []
608 otables = [globalopts]
611 otables = [globalopts]
609 if cmd:
612 if cmd:
610 aliases, entry = cmdutil.findcmd(ui, cmd, table)
613 aliases, entry = cmdutil.findcmd(ui, cmd, table)
611 otables.append(entry[1])
614 otables.append(entry[1])
612 for t in otables:
615 for t in otables:
613 for o in t:
616 for o in t:
614 if o[0]:
617 if o[0]:
615 options.append('-%s' % o[0])
618 options.append('-%s' % o[0])
616 options.append('--%s' % o[1])
619 options.append('--%s' % o[1])
617 ui.write("%s\n" % "\n".join(options))
620 ui.write("%s\n" % "\n".join(options))
618 return
621 return
619
622
620 clist = cmdutil.findpossible(ui, cmd, table).keys()
623 clist = cmdutil.findpossible(ui, cmd, table).keys()
621 clist.sort()
624 clist.sort()
622 ui.write("%s\n" % "\n".join(clist))
625 ui.write("%s\n" % "\n".join(clist))
623
626
624 def debugfsinfo(ui, path = "."):
627 def debugfsinfo(ui, path = "."):
625 file('.debugfsinfo', 'w').write('')
628 file('.debugfsinfo', 'w').write('')
626 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
629 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
627 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
630 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
628 ui.write('case-sensitive: %s\n' % (util.checkfolding('.debugfsinfo')
631 ui.write('case-sensitive: %s\n' % (util.checkfolding('.debugfsinfo')
629 and 'yes' or 'no'))
632 and 'yes' or 'no'))
630 os.unlink('.debugfsinfo')
633 os.unlink('.debugfsinfo')
631
634
632 def debugrebuildstate(ui, repo, rev=""):
635 def debugrebuildstate(ui, repo, rev=""):
633 """rebuild the dirstate as it would look like for the given revision"""
636 """rebuild the dirstate as it would look like for the given revision"""
634 if rev == "":
637 if rev == "":
635 rev = repo.changelog.tip()
638 rev = repo.changelog.tip()
636 ctx = repo.changectx(rev)
639 ctx = repo.changectx(rev)
637 files = ctx.manifest()
640 files = ctx.manifest()
638 wlock = repo.wlock()
641 wlock = repo.wlock()
639 try:
642 try:
640 repo.dirstate.rebuild(rev, files)
643 repo.dirstate.rebuild(rev, files)
641 finally:
644 finally:
642 del wlock
645 del wlock
643
646
644 def debugcheckstate(ui, repo):
647 def debugcheckstate(ui, repo):
645 """validate the correctness of the current dirstate"""
648 """validate the correctness of the current dirstate"""
646 parent1, parent2 = repo.dirstate.parents()
649 parent1, parent2 = repo.dirstate.parents()
647 m1 = repo.changectx(parent1).manifest()
650 m1 = repo.changectx(parent1).manifest()
648 m2 = repo.changectx(parent2).manifest()
651 m2 = repo.changectx(parent2).manifest()
649 errors = 0
652 errors = 0
650 for f in repo.dirstate:
653 for f in repo.dirstate:
651 state = repo.dirstate[f]
654 state = repo.dirstate[f]
652 if state in "nr" and f not in m1:
655 if state in "nr" and f not in m1:
653 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
656 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
654 errors += 1
657 errors += 1
655 if state in "a" and f in m1:
658 if state in "a" and f in m1:
656 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
659 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
657 errors += 1
660 errors += 1
658 if state in "m" and f not in m1 and f not in m2:
661 if state in "m" and f not in m1 and f not in m2:
659 ui.warn(_("%s in state %s, but not in either manifest\n") %
662 ui.warn(_("%s in state %s, but not in either manifest\n") %
660 (f, state))
663 (f, state))
661 errors += 1
664 errors += 1
662 for f in m1:
665 for f in m1:
663 state = repo.dirstate[f]
666 state = repo.dirstate[f]
664 if state not in "nrm":
667 if state not in "nrm":
665 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
668 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
666 errors += 1
669 errors += 1
667 if errors:
670 if errors:
668 error = _(".hg/dirstate inconsistent with current parent's manifest")
671 error = _(".hg/dirstate inconsistent with current parent's manifest")
669 raise util.Abort(error)
672 raise util.Abort(error)
670
673
671 def showconfig(ui, repo, *values, **opts):
674 def showconfig(ui, repo, *values, **opts):
672 """show combined config settings from all hgrc files
675 """show combined config settings from all hgrc files
673
676
674 With no args, print names and values of all config items.
677 With no args, print names and values of all config items.
675
678
676 With one arg of the form section.name, print just the value of
679 With one arg of the form section.name, print just the value of
677 that config item.
680 that config item.
678
681
679 With multiple args, print names and values of all config items
682 With multiple args, print names and values of all config items
680 with matching section names."""
683 with matching section names."""
681
684
682 untrusted = bool(opts.get('untrusted'))
685 untrusted = bool(opts.get('untrusted'))
683 if values:
686 if values:
684 if len([v for v in values if '.' in v]) > 1:
687 if len([v for v in values if '.' in v]) > 1:
685 raise util.Abort(_('only one config item permitted'))
688 raise util.Abort(_('only one config item permitted'))
686 for section, name, value in ui.walkconfig(untrusted=untrusted):
689 for section, name, value in ui.walkconfig(untrusted=untrusted):
687 sectname = section + '.' + name
690 sectname = section + '.' + name
688 if values:
691 if values:
689 for v in values:
692 for v in values:
690 if v == section:
693 if v == section:
691 ui.write('%s=%s\n' % (sectname, value))
694 ui.write('%s=%s\n' % (sectname, value))
692 elif v == sectname:
695 elif v == sectname:
693 ui.write(value, '\n')
696 ui.write(value, '\n')
694 else:
697 else:
695 ui.write('%s=%s\n' % (sectname, value))
698 ui.write('%s=%s\n' % (sectname, value))
696
699
697 def debugsetparents(ui, repo, rev1, rev2=None):
700 def debugsetparents(ui, repo, rev1, rev2=None):
698 """manually set the parents of the current working directory
701 """manually set the parents of the current working directory
699
702
700 This is useful for writing repository conversion tools, but should
703 This is useful for writing repository conversion tools, but should
701 be used with care.
704 be used with care.
702 """
705 """
703
706
704 if not rev2:
707 if not rev2:
705 rev2 = hex(nullid)
708 rev2 = hex(nullid)
706
709
707 wlock = repo.wlock()
710 wlock = repo.wlock()
708 try:
711 try:
709 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
712 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
710 finally:
713 finally:
711 del wlock
714 del wlock
712
715
713 def debugstate(ui, repo, nodates=None):
716 def debugstate(ui, repo, nodates=None):
714 """show the contents of the current dirstate"""
717 """show the contents of the current dirstate"""
715 k = repo.dirstate._map.items()
718 k = repo.dirstate._map.items()
716 k.sort()
719 k.sort()
717 timestr = ""
720 timestr = ""
718 showdate = not nodates
721 showdate = not nodates
719 for file_, ent in k:
722 for file_, ent in k:
720 if showdate:
723 if showdate:
721 if ent[3] == -1:
724 if ent[3] == -1:
722 # Pad or slice to locale representation
725 # Pad or slice to locale representation
723 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
726 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
724 timestr = 'unset'
727 timestr = 'unset'
725 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
728 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
726 else:
729 else:
727 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
730 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
728 if ent[1] & 020000:
731 if ent[1] & 020000:
729 mode = 'lnk'
732 mode = 'lnk'
730 else:
733 else:
731 mode = '%3o' % (ent[1] & 0777)
734 mode = '%3o' % (ent[1] & 0777)
732 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
735 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
733 for f in repo.dirstate.copies():
736 for f in repo.dirstate.copies():
734 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
737 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
735
738
736 def debugdata(ui, file_, rev):
739 def debugdata(ui, file_, rev):
737 """dump the contents of a data file revision"""
740 """dump the contents of a data file revision"""
738 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
741 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
739 try:
742 try:
740 ui.write(r.revision(r.lookup(rev)))
743 ui.write(r.revision(r.lookup(rev)))
741 except KeyError:
744 except KeyError:
742 raise util.Abort(_('invalid revision identifier %s') % rev)
745 raise util.Abort(_('invalid revision identifier %s') % rev)
743
746
744 def debugdate(ui, date, range=None, **opts):
747 def debugdate(ui, date, range=None, **opts):
745 """parse and display a date"""
748 """parse and display a date"""
746 if opts["extended"]:
749 if opts["extended"]:
747 d = util.parsedate(date, util.extendeddateformats)
750 d = util.parsedate(date, util.extendeddateformats)
748 else:
751 else:
749 d = util.parsedate(date)
752 d = util.parsedate(date)
750 ui.write("internal: %s %s\n" % d)
753 ui.write("internal: %s %s\n" % d)
751 ui.write("standard: %s\n" % util.datestr(d))
754 ui.write("standard: %s\n" % util.datestr(d))
752 if range:
755 if range:
753 m = util.matchdate(range)
756 m = util.matchdate(range)
754 ui.write("match: %s\n" % m(d[0]))
757 ui.write("match: %s\n" % m(d[0]))
755
758
756 def debugindex(ui, file_):
759 def debugindex(ui, file_):
757 """dump the contents of an index file"""
760 """dump the contents of an index file"""
758 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
761 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
759 ui.write(" rev offset length base linkrev" +
762 ui.write(" rev offset length base linkrev" +
760 " nodeid p1 p2\n")
763 " nodeid p1 p2\n")
761 for i in xrange(r.count()):
764 for i in xrange(r.count()):
762 node = r.node(i)
765 node = r.node(i)
763 try:
766 try:
764 pp = r.parents(node)
767 pp = r.parents(node)
765 except:
768 except:
766 pp = [nullid, nullid]
769 pp = [nullid, nullid]
767 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
770 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
768 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
771 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
769 short(node), short(pp[0]), short(pp[1])))
772 short(node), short(pp[0]), short(pp[1])))
770
773
771 def debugindexdot(ui, file_):
774 def debugindexdot(ui, file_):
772 """dump an index DAG as a .dot file"""
775 """dump an index DAG as a .dot file"""
773 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
776 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
774 ui.write("digraph G {\n")
777 ui.write("digraph G {\n")
775 for i in xrange(r.count()):
778 for i in xrange(r.count()):
776 node = r.node(i)
779 node = r.node(i)
777 pp = r.parents(node)
780 pp = r.parents(node)
778 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
781 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
779 if pp[1] != nullid:
782 if pp[1] != nullid:
780 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
783 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
781 ui.write("}\n")
784 ui.write("}\n")
782
785
783 def debuginstall(ui):
786 def debuginstall(ui):
784 '''test Mercurial installation'''
787 '''test Mercurial installation'''
785
788
786 def writetemp(contents):
789 def writetemp(contents):
787 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
790 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
788 f = os.fdopen(fd, "wb")
791 f = os.fdopen(fd, "wb")
789 f.write(contents)
792 f.write(contents)
790 f.close()
793 f.close()
791 return name
794 return name
792
795
793 problems = 0
796 problems = 0
794
797
795 # encoding
798 # encoding
796 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
799 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
797 try:
800 try:
798 util.fromlocal("test")
801 util.fromlocal("test")
799 except util.Abort, inst:
802 except util.Abort, inst:
800 ui.write(" %s\n" % inst)
803 ui.write(" %s\n" % inst)
801 ui.write(_(" (check that your locale is properly set)\n"))
804 ui.write(_(" (check that your locale is properly set)\n"))
802 problems += 1
805 problems += 1
803
806
804 # compiled modules
807 # compiled modules
805 ui.status(_("Checking extensions...\n"))
808 ui.status(_("Checking extensions...\n"))
806 try:
809 try:
807 import bdiff, mpatch, base85
810 import bdiff, mpatch, base85
808 except Exception, inst:
811 except Exception, inst:
809 ui.write(" %s\n" % inst)
812 ui.write(" %s\n" % inst)
810 ui.write(_(" One or more extensions could not be found"))
813 ui.write(_(" One or more extensions could not be found"))
811 ui.write(_(" (check that you compiled the extensions)\n"))
814 ui.write(_(" (check that you compiled the extensions)\n"))
812 problems += 1
815 problems += 1
813
816
814 # templates
817 # templates
815 ui.status(_("Checking templates...\n"))
818 ui.status(_("Checking templates...\n"))
816 try:
819 try:
817 import templater
820 import templater
818 t = templater.templater(templater.templatepath("map-cmdline.default"))
821 t = templater.templater(templater.templatepath("map-cmdline.default"))
819 except Exception, inst:
822 except Exception, inst:
820 ui.write(" %s\n" % inst)
823 ui.write(" %s\n" % inst)
821 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
824 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
822 problems += 1
825 problems += 1
823
826
824 # patch
827 # patch
825 ui.status(_("Checking patch...\n"))
828 ui.status(_("Checking patch...\n"))
826 patchproblems = 0
829 patchproblems = 0
827 a = "1\n2\n3\n4\n"
830 a = "1\n2\n3\n4\n"
828 b = "1\n2\n3\ninsert\n4\n"
831 b = "1\n2\n3\ninsert\n4\n"
829 fa = writetemp(a)
832 fa = writetemp(a)
830 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
833 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
831 os.path.basename(fa))
834 os.path.basename(fa))
832 fd = writetemp(d)
835 fd = writetemp(d)
833
836
834 files = {}
837 files = {}
835 try:
838 try:
836 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
839 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
837 except util.Abort, e:
840 except util.Abort, e:
838 ui.write(_(" patch call failed:\n"))
841 ui.write(_(" patch call failed:\n"))
839 ui.write(" " + str(e) + "\n")
842 ui.write(" " + str(e) + "\n")
840 patchproblems += 1
843 patchproblems += 1
841 else:
844 else:
842 if list(files) != [os.path.basename(fa)]:
845 if list(files) != [os.path.basename(fa)]:
843 ui.write(_(" unexpected patch output!\n"))
846 ui.write(_(" unexpected patch output!\n"))
844 patchproblems += 1
847 patchproblems += 1
845 a = file(fa).read()
848 a = file(fa).read()
846 if a != b:
849 if a != b:
847 ui.write(_(" patch test failed!\n"))
850 ui.write(_(" patch test failed!\n"))
848 patchproblems += 1
851 patchproblems += 1
849
852
850 if patchproblems:
853 if patchproblems:
851 if ui.config('ui', 'patch'):
854 if ui.config('ui', 'patch'):
852 ui.write(_(" (Current patch tool may be incompatible with patch,"
855 ui.write(_(" (Current patch tool may be incompatible with patch,"
853 " or misconfigured. Please check your .hgrc file)\n"))
856 " or misconfigured. Please check your .hgrc file)\n"))
854 else:
857 else:
855 ui.write(_(" Internal patcher failure, please report this error"
858 ui.write(_(" Internal patcher failure, please report this error"
856 " to http://www.selenic.com/mercurial/bts\n"))
859 " to http://www.selenic.com/mercurial/bts\n"))
857 problems += patchproblems
860 problems += patchproblems
858
861
859 os.unlink(fa)
862 os.unlink(fa)
860 os.unlink(fd)
863 os.unlink(fd)
861
864
862 # editor
865 # editor
863 ui.status(_("Checking commit editor...\n"))
866 ui.status(_("Checking commit editor...\n"))
864 editor = ui.geteditor()
867 editor = ui.geteditor()
865 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
868 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
866 if not cmdpath:
869 if not cmdpath:
867 if editor == 'vi':
870 if editor == 'vi':
868 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
871 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
869 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
872 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
870 else:
873 else:
871 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
874 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
872 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
875 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
873 problems += 1
876 problems += 1
874
877
875 # check username
878 # check username
876 ui.status(_("Checking username...\n"))
879 ui.status(_("Checking username...\n"))
877 user = os.environ.get("HGUSER")
880 user = os.environ.get("HGUSER")
878 if user is None:
881 if user is None:
879 user = ui.config("ui", "username")
882 user = ui.config("ui", "username")
880 if user is None:
883 if user is None:
881 user = os.environ.get("EMAIL")
884 user = os.environ.get("EMAIL")
882 if not user:
885 if not user:
883 ui.warn(" ")
886 ui.warn(" ")
884 ui.username()
887 ui.username()
885 ui.write(_(" (specify a username in your .hgrc file)\n"))
888 ui.write(_(" (specify a username in your .hgrc file)\n"))
886
889
887 if not problems:
890 if not problems:
888 ui.status(_("No problems detected\n"))
891 ui.status(_("No problems detected\n"))
889 else:
892 else:
890 ui.write(_("%s problems detected,"
893 ui.write(_("%s problems detected,"
891 " please check your install!\n") % problems)
894 " please check your install!\n") % problems)
892
895
893 return problems
896 return problems
894
897
895 def debugrename(ui, repo, file1, *pats, **opts):
898 def debugrename(ui, repo, file1, *pats, **opts):
896 """dump rename information"""
899 """dump rename information"""
897
900
898 ctx = repo.changectx(opts.get('rev', 'tip'))
901 ctx = repo.changectx(opts.get('rev', 'tip'))
899 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
902 for src, abs, rel, exact in cmdutil.walk(repo, (file1,) + pats, opts,
900 ctx.node()):
903 ctx.node()):
901 fctx = ctx.filectx(abs)
904 fctx = ctx.filectx(abs)
902 m = fctx.filelog().renamed(fctx.filenode())
905 m = fctx.filelog().renamed(fctx.filenode())
903 if m:
906 if m:
904 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
907 ui.write(_("%s renamed from %s:%s\n") % (rel, m[0], hex(m[1])))
905 else:
908 else:
906 ui.write(_("%s not renamed\n") % rel)
909 ui.write(_("%s not renamed\n") % rel)
907
910
908 def debugwalk(ui, repo, *pats, **opts):
911 def debugwalk(ui, repo, *pats, **opts):
909 """show how files match on given patterns"""
912 """show how files match on given patterns"""
910 items = list(cmdutil.walk(repo, pats, opts))
913 items = list(cmdutil.walk(repo, pats, opts))
911 if not items:
914 if not items:
912 return
915 return
913 fmt = '%%s %%-%ds %%-%ds %%s' % (
916 fmt = '%%s %%-%ds %%-%ds %%s' % (
914 max([len(abs) for (src, abs, rel, exact) in items]),
917 max([len(abs) for (src, abs, rel, exact) in items]),
915 max([len(rel) for (src, abs, rel, exact) in items]))
918 max([len(rel) for (src, abs, rel, exact) in items]))
916 for src, abs, rel, exact in items:
919 for src, abs, rel, exact in items:
917 line = fmt % (src, abs, rel, exact and 'exact' or '')
920 line = fmt % (src, abs, rel, exact and 'exact' or '')
918 ui.write("%s\n" % line.rstrip())
921 ui.write("%s\n" % line.rstrip())
919
922
920 def diff(ui, repo, *pats, **opts):
923 def diff(ui, repo, *pats, **opts):
921 """diff repository (or selected files)
924 """diff repository (or selected files)
922
925
923 Show differences between revisions for the specified files.
926 Show differences between revisions for the specified files.
924
927
925 Differences between files are shown using the unified diff format.
928 Differences between files are shown using the unified diff format.
926
929
927 NOTE: diff may generate unexpected results for merges, as it will
930 NOTE: diff may generate unexpected results for merges, as it will
928 default to comparing against the working directory's first parent
931 default to comparing against the working directory's first parent
929 changeset if no revisions are specified.
932 changeset if no revisions are specified.
930
933
931 When two revision arguments are given, then changes are shown
934 When two revision arguments are given, then changes are shown
932 between those revisions. If only one revision is specified then
935 between those revisions. If only one revision is specified then
933 that revision is compared to the working directory, and, when no
936 that revision is compared to the working directory, and, when no
934 revisions are specified, the working directory files are compared
937 revisions are specified, the working directory files are compared
935 to its parent.
938 to its parent.
936
939
937 Without the -a option, diff will avoid generating diffs of files
940 Without the -a option, diff will avoid generating diffs of files
938 it detects as binary. With -a, diff will generate a diff anyway,
941 it detects as binary. With -a, diff will generate a diff anyway,
939 probably with undesirable results.
942 probably with undesirable results.
940 """
943 """
941 node1, node2 = cmdutil.revpair(repo, opts['rev'])
944 node1, node2 = cmdutil.revpair(repo, opts['rev'])
942
945
943 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
946 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
944
947
945 patch.diff(repo, node1, node2, fns, match=matchfn,
948 patch.diff(repo, node1, node2, fns, match=matchfn,
946 opts=patch.diffopts(ui, opts))
949 opts=patch.diffopts(ui, opts))
947
950
948 def export(ui, repo, *changesets, **opts):
951 def export(ui, repo, *changesets, **opts):
949 """dump the header and diffs for one or more changesets
952 """dump the header and diffs for one or more changesets
950
953
951 Print the changeset header and diffs for one or more revisions.
954 Print the changeset header and diffs for one or more revisions.
952
955
953 The information shown in the changeset header is: author,
956 The information shown in the changeset header is: author,
954 changeset hash, parent(s) and commit comment.
957 changeset hash, parent(s) and commit comment.
955
958
956 NOTE: export may generate unexpected diff output for merge changesets,
959 NOTE: export may generate unexpected diff output for merge changesets,
957 as it will compare the merge changeset against its first parent only.
960 as it will compare the merge changeset against its first parent only.
958
961
959 Output may be to a file, in which case the name of the file is
962 Output may be to a file, in which case the name of the file is
960 given using a format string. The formatting rules are as follows:
963 given using a format string. The formatting rules are as follows:
961
964
962 %% literal "%" character
965 %% literal "%" character
963 %H changeset hash (40 bytes of hexadecimal)
966 %H changeset hash (40 bytes of hexadecimal)
964 %N number of patches being generated
967 %N number of patches being generated
965 %R changeset revision number
968 %R changeset revision number
966 %b basename of the exporting repository
969 %b basename of the exporting repository
967 %h short-form changeset hash (12 bytes of hexadecimal)
970 %h short-form changeset hash (12 bytes of hexadecimal)
968 %n zero-padded sequence number, starting at 1
971 %n zero-padded sequence number, starting at 1
969 %r zero-padded changeset revision number
972 %r zero-padded changeset revision number
970
973
971 Without the -a option, export will avoid generating diffs of files
974 Without the -a option, export will avoid generating diffs of files
972 it detects as binary. With -a, export will generate a diff anyway,
975 it detects as binary. With -a, export will generate a diff anyway,
973 probably with undesirable results.
976 probably with undesirable results.
974
977
975 With the --switch-parent option, the diff will be against the second
978 With the --switch-parent option, the diff will be against the second
976 parent. It can be useful to review a merge.
979 parent. It can be useful to review a merge.
977 """
980 """
978 if not changesets:
981 if not changesets:
979 raise util.Abort(_("export requires at least one changeset"))
982 raise util.Abort(_("export requires at least one changeset"))
980 revs = cmdutil.revrange(repo, changesets)
983 revs = cmdutil.revrange(repo, changesets)
981 if len(revs) > 1:
984 if len(revs) > 1:
982 ui.note(_('exporting patches:\n'))
985 ui.note(_('exporting patches:\n'))
983 else:
986 else:
984 ui.note(_('exporting patch:\n'))
987 ui.note(_('exporting patch:\n'))
985 patch.export(repo, revs, template=opts['output'],
988 patch.export(repo, revs, template=opts['output'],
986 switch_parent=opts['switch_parent'],
989 switch_parent=opts['switch_parent'],
987 opts=patch.diffopts(ui, opts))
990 opts=patch.diffopts(ui, opts))
988
991
989 def grep(ui, repo, pattern, *pats, **opts):
992 def grep(ui, repo, pattern, *pats, **opts):
990 """search for a pattern in specified files and revisions
993 """search for a pattern in specified files and revisions
991
994
992 Search revisions of files for a regular expression.
995 Search revisions of files for a regular expression.
993
996
994 This command behaves differently than Unix grep. It only accepts
997 This command behaves differently than Unix grep. It only accepts
995 Python/Perl regexps. It searches repository history, not the
998 Python/Perl regexps. It searches repository history, not the
996 working directory. It always prints the revision number in which
999 working directory. It always prints the revision number in which
997 a match appears.
1000 a match appears.
998
1001
999 By default, grep only prints output for the first revision of a
1002 By default, grep only prints output for the first revision of a
1000 file in which it finds a match. To get it to print every revision
1003 file in which it finds a match. To get it to print every revision
1001 that contains a change in match status ("-" for a match that
1004 that contains a change in match status ("-" for a match that
1002 becomes a non-match, or "+" for a non-match that becomes a match),
1005 becomes a non-match, or "+" for a non-match that becomes a match),
1003 use the --all flag.
1006 use the --all flag.
1004 """
1007 """
1005 reflags = 0
1008 reflags = 0
1006 if opts['ignore_case']:
1009 if opts['ignore_case']:
1007 reflags |= re.I
1010 reflags |= re.I
1008 try:
1011 try:
1009 regexp = re.compile(pattern, reflags)
1012 regexp = re.compile(pattern, reflags)
1010 except Exception, inst:
1013 except Exception, inst:
1011 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1014 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1012 return None
1015 return None
1013 sep, eol = ':', '\n'
1016 sep, eol = ':', '\n'
1014 if opts['print0']:
1017 if opts['print0']:
1015 sep = eol = '\0'
1018 sep = eol = '\0'
1016
1019
1017 fcache = {}
1020 fcache = {}
1018 def getfile(fn):
1021 def getfile(fn):
1019 if fn not in fcache:
1022 if fn not in fcache:
1020 fcache[fn] = repo.file(fn)
1023 fcache[fn] = repo.file(fn)
1021 return fcache[fn]
1024 return fcache[fn]
1022
1025
1023 def matchlines(body):
1026 def matchlines(body):
1024 begin = 0
1027 begin = 0
1025 linenum = 0
1028 linenum = 0
1026 while True:
1029 while True:
1027 match = regexp.search(body, begin)
1030 match = regexp.search(body, begin)
1028 if not match:
1031 if not match:
1029 break
1032 break
1030 mstart, mend = match.span()
1033 mstart, mend = match.span()
1031 linenum += body.count('\n', begin, mstart) + 1
1034 linenum += body.count('\n', begin, mstart) + 1
1032 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1035 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1033 lend = body.find('\n', mend)
1036 lend = body.find('\n', mend)
1034 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1037 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1035 begin = lend + 1
1038 begin = lend + 1
1036
1039
1037 class linestate(object):
1040 class linestate(object):
1038 def __init__(self, line, linenum, colstart, colend):
1041 def __init__(self, line, linenum, colstart, colend):
1039 self.line = line
1042 self.line = line
1040 self.linenum = linenum
1043 self.linenum = linenum
1041 self.colstart = colstart
1044 self.colstart = colstart
1042 self.colend = colend
1045 self.colend = colend
1043
1046
1044 def __eq__(self, other):
1047 def __eq__(self, other):
1045 return self.line == other.line
1048 return self.line == other.line
1046
1049
1047 matches = {}
1050 matches = {}
1048 copies = {}
1051 copies = {}
1049 def grepbody(fn, rev, body):
1052 def grepbody(fn, rev, body):
1050 matches[rev].setdefault(fn, [])
1053 matches[rev].setdefault(fn, [])
1051 m = matches[rev][fn]
1054 m = matches[rev][fn]
1052 for lnum, cstart, cend, line in matchlines(body):
1055 for lnum, cstart, cend, line in matchlines(body):
1053 s = linestate(line, lnum, cstart, cend)
1056 s = linestate(line, lnum, cstart, cend)
1054 m.append(s)
1057 m.append(s)
1055
1058
1056 def difflinestates(a, b):
1059 def difflinestates(a, b):
1057 sm = difflib.SequenceMatcher(None, a, b)
1060 sm = difflib.SequenceMatcher(None, a, b)
1058 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1061 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1059 if tag == 'insert':
1062 if tag == 'insert':
1060 for i in xrange(blo, bhi):
1063 for i in xrange(blo, bhi):
1061 yield ('+', b[i])
1064 yield ('+', b[i])
1062 elif tag == 'delete':
1065 elif tag == 'delete':
1063 for i in xrange(alo, ahi):
1066 for i in xrange(alo, ahi):
1064 yield ('-', a[i])
1067 yield ('-', a[i])
1065 elif tag == 'replace':
1068 elif tag == 'replace':
1066 for i in xrange(alo, ahi):
1069 for i in xrange(alo, ahi):
1067 yield ('-', a[i])
1070 yield ('-', a[i])
1068 for i in xrange(blo, bhi):
1071 for i in xrange(blo, bhi):
1069 yield ('+', b[i])
1072 yield ('+', b[i])
1070
1073
1071 prev = {}
1074 prev = {}
1072 def display(fn, rev, states, prevstates):
1075 def display(fn, rev, states, prevstates):
1073 datefunc = ui.quiet and util.shortdate or util.datestr
1076 datefunc = ui.quiet and util.shortdate or util.datestr
1074 found = False
1077 found = False
1075 filerevmatches = {}
1078 filerevmatches = {}
1076 r = prev.get(fn, -1)
1079 r = prev.get(fn, -1)
1077 if opts['all']:
1080 if opts['all']:
1078 iter = difflinestates(states, prevstates)
1081 iter = difflinestates(states, prevstates)
1079 else:
1082 else:
1080 iter = [('', l) for l in prevstates]
1083 iter = [('', l) for l in prevstates]
1081 for change, l in iter:
1084 for change, l in iter:
1082 cols = [fn, str(r)]
1085 cols = [fn, str(r)]
1083 if opts['line_number']:
1086 if opts['line_number']:
1084 cols.append(str(l.linenum))
1087 cols.append(str(l.linenum))
1085 if opts['all']:
1088 if opts['all']:
1086 cols.append(change)
1089 cols.append(change)
1087 if opts['user']:
1090 if opts['user']:
1088 cols.append(ui.shortuser(get(r)[1]))
1091 cols.append(ui.shortuser(get(r)[1]))
1089 if opts.get('date'):
1092 if opts.get('date'):
1090 cols.append(datefunc(get(r)[2]))
1093 cols.append(datefunc(get(r)[2]))
1091 if opts['files_with_matches']:
1094 if opts['files_with_matches']:
1092 c = (fn, r)
1095 c = (fn, r)
1093 if c in filerevmatches:
1096 if c in filerevmatches:
1094 continue
1097 continue
1095 filerevmatches[c] = 1
1098 filerevmatches[c] = 1
1096 else:
1099 else:
1097 cols.append(l.line)
1100 cols.append(l.line)
1098 ui.write(sep.join(cols), eol)
1101 ui.write(sep.join(cols), eol)
1099 found = True
1102 found = True
1100 return found
1103 return found
1101
1104
1102 fstate = {}
1105 fstate = {}
1103 skip = {}
1106 skip = {}
1104 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1107 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1105 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1108 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1106 found = False
1109 found = False
1107 follow = opts.get('follow')
1110 follow = opts.get('follow')
1108 for st, rev, fns in changeiter:
1111 for st, rev, fns in changeiter:
1109 if st == 'window':
1112 if st == 'window':
1110 matches.clear()
1113 matches.clear()
1111 elif st == 'add':
1114 elif st == 'add':
1112 ctx = repo.changectx(rev)
1115 ctx = repo.changectx(rev)
1113 matches[rev] = {}
1116 matches[rev] = {}
1114 for fn in fns:
1117 for fn in fns:
1115 if fn in skip:
1118 if fn in skip:
1116 continue
1119 continue
1117 try:
1120 try:
1118 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1121 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1119 fstate.setdefault(fn, [])
1122 fstate.setdefault(fn, [])
1120 if follow:
1123 if follow:
1121 copied = getfile(fn).renamed(ctx.filenode(fn))
1124 copied = getfile(fn).renamed(ctx.filenode(fn))
1122 if copied:
1125 if copied:
1123 copies.setdefault(rev, {})[fn] = copied[0]
1126 copies.setdefault(rev, {})[fn] = copied[0]
1124 except revlog.LookupError:
1127 except revlog.LookupError:
1125 pass
1128 pass
1126 elif st == 'iter':
1129 elif st == 'iter':
1127 states = matches[rev].items()
1130 states = matches[rev].items()
1128 states.sort()
1131 states.sort()
1129 for fn, m in states:
1132 for fn, m in states:
1130 copy = copies.get(rev, {}).get(fn)
1133 copy = copies.get(rev, {}).get(fn)
1131 if fn in skip:
1134 if fn in skip:
1132 if copy:
1135 if copy:
1133 skip[copy] = True
1136 skip[copy] = True
1134 continue
1137 continue
1135 if fn in prev or fstate[fn]:
1138 if fn in prev or fstate[fn]:
1136 r = display(fn, rev, m, fstate[fn])
1139 r = display(fn, rev, m, fstate[fn])
1137 found = found or r
1140 found = found or r
1138 if r and not opts['all']:
1141 if r and not opts['all']:
1139 skip[fn] = True
1142 skip[fn] = True
1140 if copy:
1143 if copy:
1141 skip[copy] = True
1144 skip[copy] = True
1142 fstate[fn] = m
1145 fstate[fn] = m
1143 if copy:
1146 if copy:
1144 fstate[copy] = m
1147 fstate[copy] = m
1145 prev[fn] = rev
1148 prev[fn] = rev
1146
1149
1147 fstate = fstate.items()
1150 fstate = fstate.items()
1148 fstate.sort()
1151 fstate.sort()
1149 for fn, state in fstate:
1152 for fn, state in fstate:
1150 if fn in skip:
1153 if fn in skip:
1151 continue
1154 continue
1152 if fn not in copies.get(prev[fn], {}):
1155 if fn not in copies.get(prev[fn], {}):
1153 found = display(fn, rev, {}, state) or found
1156 found = display(fn, rev, {}, state) or found
1154 return (not found and 1) or 0
1157 return (not found and 1) or 0
1155
1158
1156 def heads(ui, repo, *branchrevs, **opts):
1159 def heads(ui, repo, *branchrevs, **opts):
1157 """show current repository heads or show branch heads
1160 """show current repository heads or show branch heads
1158
1161
1159 With no arguments, show all repository head changesets.
1162 With no arguments, show all repository head changesets.
1160
1163
1161 If branch or revisions names are given this will show the heads of
1164 If branch or revisions names are given this will show the heads of
1162 the specified branches or the branches those revisions are tagged
1165 the specified branches or the branches those revisions are tagged
1163 with.
1166 with.
1164
1167
1165 Repository "heads" are changesets that don't have child
1168 Repository "heads" are changesets that don't have child
1166 changesets. They are where development generally takes place and
1169 changesets. They are where development generally takes place and
1167 are the usual targets for update and merge operations.
1170 are the usual targets for update and merge operations.
1168
1171
1169 Branch heads are changesets that have a given branch tag, but have
1172 Branch heads are changesets that have a given branch tag, but have
1170 no child changesets with that tag. They are usually where
1173 no child changesets with that tag. They are usually where
1171 development on the given branch takes place.
1174 development on the given branch takes place.
1172 """
1175 """
1173 if opts['rev']:
1176 if opts['rev']:
1174 start = repo.lookup(opts['rev'])
1177 start = repo.lookup(opts['rev'])
1175 else:
1178 else:
1176 start = None
1179 start = None
1177 if not branchrevs:
1180 if not branchrevs:
1178 # Assume we're looking repo-wide heads if no revs were specified.
1181 # Assume we're looking repo-wide heads if no revs were specified.
1179 heads = repo.heads(start)
1182 heads = repo.heads(start)
1180 else:
1183 else:
1181 heads = []
1184 heads = []
1182 visitedset = util.set()
1185 visitedset = util.set()
1183 for branchrev in branchrevs:
1186 for branchrev in branchrevs:
1184 branch = repo.changectx(branchrev).branch()
1187 branch = repo.changectx(branchrev).branch()
1185 if branch in visitedset:
1188 if branch in visitedset:
1186 continue
1189 continue
1187 visitedset.add(branch)
1190 visitedset.add(branch)
1188 bheads = repo.branchheads(branch, start)
1191 bheads = repo.branchheads(branch, start)
1189 if not bheads:
1192 if not bheads:
1190 if branch != branchrev:
1193 if branch != branchrev:
1191 ui.warn(_("no changes on branch %s containing %s are "
1194 ui.warn(_("no changes on branch %s containing %s are "
1192 "reachable from %s\n")
1195 "reachable from %s\n")
1193 % (branch, branchrev, opts['rev']))
1196 % (branch, branchrev, opts['rev']))
1194 else:
1197 else:
1195 ui.warn(_("no changes on branch %s are reachable from %s\n")
1198 ui.warn(_("no changes on branch %s are reachable from %s\n")
1196 % (branch, opts['rev']))
1199 % (branch, opts['rev']))
1197 heads.extend(bheads)
1200 heads.extend(bheads)
1198 if not heads:
1201 if not heads:
1199 return 1
1202 return 1
1200 displayer = cmdutil.show_changeset(ui, repo, opts)
1203 displayer = cmdutil.show_changeset(ui, repo, opts)
1201 for n in heads:
1204 for n in heads:
1202 displayer.show(changenode=n)
1205 displayer.show(changenode=n)
1203
1206
1204 def help_(ui, name=None, with_version=False):
1207 def help_(ui, name=None, with_version=False):
1205 """show help for a command, extension, or list of commands
1208 """show help for a command, extension, or list of commands
1206
1209
1207 With no arguments, print a list of commands and short help.
1210 With no arguments, print a list of commands and short help.
1208
1211
1209 Given a command name, print help for that command.
1212 Given a command name, print help for that command.
1210
1213
1211 Given an extension name, print help for that extension, and the
1214 Given an extension name, print help for that extension, and the
1212 commands it provides."""
1215 commands it provides."""
1213 option_lists = []
1216 option_lists = []
1214
1217
1215 def addglobalopts(aliases):
1218 def addglobalopts(aliases):
1216 if ui.verbose:
1219 if ui.verbose:
1217 option_lists.append((_("global options:"), globalopts))
1220 option_lists.append((_("global options:"), globalopts))
1218 if name == 'shortlist':
1221 if name == 'shortlist':
1219 option_lists.append((_('use "hg help" for the full list '
1222 option_lists.append((_('use "hg help" for the full list '
1220 'of commands'), ()))
1223 'of commands'), ()))
1221 else:
1224 else:
1222 if name == 'shortlist':
1225 if name == 'shortlist':
1223 msg = _('use "hg help" for the full list of commands '
1226 msg = _('use "hg help" for the full list of commands '
1224 'or "hg -v" for details')
1227 'or "hg -v" for details')
1225 elif aliases:
1228 elif aliases:
1226 msg = _('use "hg -v help%s" to show aliases and '
1229 msg = _('use "hg -v help%s" to show aliases and '
1227 'global options') % (name and " " + name or "")
1230 'global options') % (name and " " + name or "")
1228 else:
1231 else:
1229 msg = _('use "hg -v help %s" to show global options') % name
1232 msg = _('use "hg -v help %s" to show global options') % name
1230 option_lists.append((msg, ()))
1233 option_lists.append((msg, ()))
1231
1234
1232 def helpcmd(name):
1235 def helpcmd(name):
1233 if with_version:
1236 if with_version:
1234 version_(ui)
1237 version_(ui)
1235 ui.write('\n')
1238 ui.write('\n')
1236 aliases, i = cmdutil.findcmd(ui, name, table)
1239 aliases, i = cmdutil.findcmd(ui, name, table)
1237 # synopsis
1240 # synopsis
1238 ui.write("%s\n" % i[2])
1241 ui.write("%s\n" % i[2])
1239
1242
1240 # aliases
1243 # aliases
1241 if not ui.quiet and len(aliases) > 1:
1244 if not ui.quiet and len(aliases) > 1:
1242 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1245 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1243
1246
1244 # description
1247 # description
1245 doc = i[0].__doc__
1248 doc = i[0].__doc__
1246 if not doc:
1249 if not doc:
1247 doc = _("(No help text available)")
1250 doc = _("(No help text available)")
1248 if ui.quiet:
1251 if ui.quiet:
1249 doc = doc.splitlines(0)[0]
1252 doc = doc.splitlines(0)[0]
1250 ui.write("\n%s\n" % doc.rstrip())
1253 ui.write("\n%s\n" % doc.rstrip())
1251
1254
1252 if not ui.quiet:
1255 if not ui.quiet:
1253 # options
1256 # options
1254 if i[1]:
1257 if i[1]:
1255 option_lists.append((_("options:\n"), i[1]))
1258 option_lists.append((_("options:\n"), i[1]))
1256
1259
1257 addglobalopts(False)
1260 addglobalopts(False)
1258
1261
1259 def helplist(header, select=None):
1262 def helplist(header, select=None):
1260 h = {}
1263 h = {}
1261 cmds = {}
1264 cmds = {}
1262 for c, e in table.items():
1265 for c, e in table.items():
1263 f = c.split("|", 1)[0]
1266 f = c.split("|", 1)[0]
1264 if select and not select(f):
1267 if select and not select(f):
1265 continue
1268 continue
1266 if name == "shortlist" and not f.startswith("^"):
1269 if name == "shortlist" and not f.startswith("^"):
1267 continue
1270 continue
1268 f = f.lstrip("^")
1271 f = f.lstrip("^")
1269 if not ui.debugflag and f.startswith("debug"):
1272 if not ui.debugflag and f.startswith("debug"):
1270 continue
1273 continue
1271 doc = e[0].__doc__
1274 doc = e[0].__doc__
1272 if not doc:
1275 if not doc:
1273 doc = _("(No help text available)")
1276 doc = _("(No help text available)")
1274 h[f] = doc.splitlines(0)[0].rstrip()
1277 h[f] = doc.splitlines(0)[0].rstrip()
1275 cmds[f] = c.lstrip("^")
1278 cmds[f] = c.lstrip("^")
1276
1279
1277 if not h:
1280 if not h:
1278 ui.status(_('no commands defined\n'))
1281 ui.status(_('no commands defined\n'))
1279 return
1282 return
1280
1283
1281 ui.status(header)
1284 ui.status(header)
1282 fns = h.keys()
1285 fns = h.keys()
1283 fns.sort()
1286 fns.sort()
1284 m = max(map(len, fns))
1287 m = max(map(len, fns))
1285 for f in fns:
1288 for f in fns:
1286 if ui.verbose:
1289 if ui.verbose:
1287 commands = cmds[f].replace("|",", ")
1290 commands = cmds[f].replace("|",", ")
1288 ui.write(" %s:\n %s\n"%(commands, h[f]))
1291 ui.write(" %s:\n %s\n"%(commands, h[f]))
1289 else:
1292 else:
1290 ui.write(' %-*s %s\n' % (m, f, h[f]))
1293 ui.write(' %-*s %s\n' % (m, f, h[f]))
1291
1294
1292 if not ui.quiet:
1295 if not ui.quiet:
1293 addglobalopts(True)
1296 addglobalopts(True)
1294
1297
1295 def helptopic(name):
1298 def helptopic(name):
1296 v = None
1299 v = None
1297 for i in help.helptable:
1300 for i in help.helptable:
1298 l = i.split('|')
1301 l = i.split('|')
1299 if name in l:
1302 if name in l:
1300 v = i
1303 v = i
1301 header = l[-1]
1304 header = l[-1]
1302 if not v:
1305 if not v:
1303 raise cmdutil.UnknownCommand(name)
1306 raise cmdutil.UnknownCommand(name)
1304
1307
1305 # description
1308 # description
1306 doc = help.helptable[v]
1309 doc = help.helptable[v]
1307 if not doc:
1310 if not doc:
1308 doc = _("(No help text available)")
1311 doc = _("(No help text available)")
1309 if callable(doc):
1312 if callable(doc):
1310 doc = doc()
1313 doc = doc()
1311
1314
1312 ui.write("%s\n" % header)
1315 ui.write("%s\n" % header)
1313 ui.write("%s\n" % doc.rstrip())
1316 ui.write("%s\n" % doc.rstrip())
1314
1317
1315 def helpext(name):
1318 def helpext(name):
1316 try:
1319 try:
1317 mod = extensions.find(name)
1320 mod = extensions.find(name)
1318 except KeyError:
1321 except KeyError:
1319 raise cmdutil.UnknownCommand(name)
1322 raise cmdutil.UnknownCommand(name)
1320
1323
1321 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1324 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1322 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1325 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1323 for d in doc[1:]:
1326 for d in doc[1:]:
1324 ui.write(d, '\n')
1327 ui.write(d, '\n')
1325
1328
1326 ui.status('\n')
1329 ui.status('\n')
1327
1330
1328 try:
1331 try:
1329 ct = mod.cmdtable
1332 ct = mod.cmdtable
1330 except AttributeError:
1333 except AttributeError:
1331 ct = {}
1334 ct = {}
1332
1335
1333 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1336 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1334 helplist(_('list of commands:\n\n'), modcmds.has_key)
1337 helplist(_('list of commands:\n\n'), modcmds.has_key)
1335
1338
1336 if name and name != 'shortlist':
1339 if name and name != 'shortlist':
1337 i = None
1340 i = None
1338 for f in (helpcmd, helptopic, helpext):
1341 for f in (helpcmd, helptopic, helpext):
1339 try:
1342 try:
1340 f(name)
1343 f(name)
1341 i = None
1344 i = None
1342 break
1345 break
1343 except cmdutil.UnknownCommand, inst:
1346 except cmdutil.UnknownCommand, inst:
1344 i = inst
1347 i = inst
1345 if i:
1348 if i:
1346 raise i
1349 raise i
1347
1350
1348 else:
1351 else:
1349 # program name
1352 # program name
1350 if ui.verbose or with_version:
1353 if ui.verbose or with_version:
1351 version_(ui)
1354 version_(ui)
1352 else:
1355 else:
1353 ui.status(_("Mercurial Distributed SCM\n"))
1356 ui.status(_("Mercurial Distributed SCM\n"))
1354 ui.status('\n')
1357 ui.status('\n')
1355
1358
1356 # list of commands
1359 # list of commands
1357 if name == "shortlist":
1360 if name == "shortlist":
1358 header = _('basic commands:\n\n')
1361 header = _('basic commands:\n\n')
1359 else:
1362 else:
1360 header = _('list of commands:\n\n')
1363 header = _('list of commands:\n\n')
1361
1364
1362 helplist(header)
1365 helplist(header)
1363
1366
1364 # list all option lists
1367 # list all option lists
1365 opt_output = []
1368 opt_output = []
1366 for title, options in option_lists:
1369 for title, options in option_lists:
1367 opt_output.append(("\n%s" % title, None))
1370 opt_output.append(("\n%s" % title, None))
1368 for shortopt, longopt, default, desc in options:
1371 for shortopt, longopt, default, desc in options:
1369 if "DEPRECATED" in desc and not ui.verbose: continue
1372 if "DEPRECATED" in desc and not ui.verbose: continue
1370 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1373 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1371 longopt and " --%s" % longopt),
1374 longopt and " --%s" % longopt),
1372 "%s%s" % (desc,
1375 "%s%s" % (desc,
1373 default
1376 default
1374 and _(" (default: %s)") % default
1377 and _(" (default: %s)") % default
1375 or "")))
1378 or "")))
1376
1379
1377 if opt_output:
1380 if opt_output:
1378 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1381 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1379 for first, second in opt_output:
1382 for first, second in opt_output:
1380 if second:
1383 if second:
1381 ui.write(" %-*s %s\n" % (opts_len, first, second))
1384 ui.write(" %-*s %s\n" % (opts_len, first, second))
1382 else:
1385 else:
1383 ui.write("%s\n" % first)
1386 ui.write("%s\n" % first)
1384
1387
1385 def identify(ui, repo, source=None,
1388 def identify(ui, repo, source=None,
1386 rev=None, num=None, id=None, branch=None, tags=None):
1389 rev=None, num=None, id=None, branch=None, tags=None):
1387 """identify the working copy or specified revision
1390 """identify the working copy or specified revision
1388
1391
1389 With no revision, print a summary of the current state of the repo.
1392 With no revision, print a summary of the current state of the repo.
1390
1393
1391 With a path, do a lookup in another repository.
1394 With a path, do a lookup in another repository.
1392
1395
1393 This summary identifies the repository state using one or two parent
1396 This summary identifies the repository state using one or two parent
1394 hash identifiers, followed by a "+" if there are uncommitted changes
1397 hash identifiers, followed by a "+" if there are uncommitted changes
1395 in the working directory, a list of tags for this revision and a branch
1398 in the working directory, a list of tags for this revision and a branch
1396 name for non-default branches.
1399 name for non-default branches.
1397 """
1400 """
1398
1401
1399 if not repo and not source:
1402 if not repo and not source:
1400 raise util.Abort(_("There is no Mercurial repository here "
1403 raise util.Abort(_("There is no Mercurial repository here "
1401 "(.hg not found)"))
1404 "(.hg not found)"))
1402
1405
1403 hexfunc = ui.debugflag and hex or short
1406 hexfunc = ui.debugflag and hex or short
1404 default = not (num or id or branch or tags)
1407 default = not (num or id or branch or tags)
1405 output = []
1408 output = []
1406
1409
1407 if source:
1410 if source:
1408 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1411 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1409 srepo = hg.repository(ui, source)
1412 srepo = hg.repository(ui, source)
1410 if not rev and revs:
1413 if not rev and revs:
1411 rev = revs[0]
1414 rev = revs[0]
1412 if not rev:
1415 if not rev:
1413 rev = "tip"
1416 rev = "tip"
1414 if num or branch or tags:
1417 if num or branch or tags:
1415 raise util.Abort(
1418 raise util.Abort(
1416 "can't query remote revision number, branch, or tags")
1419 "can't query remote revision number, branch, or tags")
1417 output = [hexfunc(srepo.lookup(rev))]
1420 output = [hexfunc(srepo.lookup(rev))]
1418 elif not rev:
1421 elif not rev:
1419 ctx = repo.workingctx()
1422 ctx = repo.workingctx()
1420 parents = ctx.parents()
1423 parents = ctx.parents()
1421 changed = False
1424 changed = False
1422 if default or id or num:
1425 if default or id or num:
1423 changed = ctx.files() + ctx.deleted()
1426 changed = ctx.files() + ctx.deleted()
1424 if default or id:
1427 if default or id:
1425 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1428 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1426 (changed) and "+" or "")]
1429 (changed) and "+" or "")]
1427 if num:
1430 if num:
1428 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1431 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1429 (changed) and "+" or ""))
1432 (changed) and "+" or ""))
1430 else:
1433 else:
1431 ctx = repo.changectx(rev)
1434 ctx = repo.changectx(rev)
1432 if default or id:
1435 if default or id:
1433 output = [hexfunc(ctx.node())]
1436 output = [hexfunc(ctx.node())]
1434 if num:
1437 if num:
1435 output.append(str(ctx.rev()))
1438 output.append(str(ctx.rev()))
1436
1439
1437 if not source and default and not ui.quiet:
1440 if not source and default and not ui.quiet:
1438 b = util.tolocal(ctx.branch())
1441 b = util.tolocal(ctx.branch())
1439 if b != 'default':
1442 if b != 'default':
1440 output.append("(%s)" % b)
1443 output.append("(%s)" % b)
1441
1444
1442 # multiple tags for a single parent separated by '/'
1445 # multiple tags for a single parent separated by '/'
1443 t = "/".join(ctx.tags())
1446 t = "/".join(ctx.tags())
1444 if t:
1447 if t:
1445 output.append(t)
1448 output.append(t)
1446
1449
1447 if branch:
1450 if branch:
1448 output.append(util.tolocal(ctx.branch()))
1451 output.append(util.tolocal(ctx.branch()))
1449
1452
1450 if tags:
1453 if tags:
1451 output.extend(ctx.tags())
1454 output.extend(ctx.tags())
1452
1455
1453 ui.write("%s\n" % ' '.join(output))
1456 ui.write("%s\n" % ' '.join(output))
1454
1457
1455 def import_(ui, repo, patch1, *patches, **opts):
1458 def import_(ui, repo, patch1, *patches, **opts):
1456 """import an ordered set of patches
1459 """import an ordered set of patches
1457
1460
1458 Import a list of patches and commit them individually.
1461 Import a list of patches and commit them individually.
1459
1462
1460 If there are outstanding changes in the working directory, import
1463 If there are outstanding changes in the working directory, import
1461 will abort unless given the -f flag.
1464 will abort unless given the -f flag.
1462
1465
1463 You can import a patch straight from a mail message. Even patches
1466 You can import a patch straight from a mail message. Even patches
1464 as attachments work (body part must be type text/plain or
1467 as attachments work (body part must be type text/plain or
1465 text/x-patch to be used). From and Subject headers of email
1468 text/x-patch to be used). From and Subject headers of email
1466 message are used as default committer and commit message. All
1469 message are used as default committer and commit message. All
1467 text/plain body parts before first diff are added to commit
1470 text/plain body parts before first diff are added to commit
1468 message.
1471 message.
1469
1472
1470 If the imported patch was generated by hg export, user and description
1473 If the imported patch was generated by hg export, user and description
1471 from patch override values from message headers and body. Values
1474 from patch override values from message headers and body. Values
1472 given on command line with -m and -u override these.
1475 given on command line with -m and -u override these.
1473
1476
1474 If --exact is specified, import will set the working directory
1477 If --exact is specified, import will set the working directory
1475 to the parent of each patch before applying it, and will abort
1478 to the parent of each patch before applying it, and will abort
1476 if the resulting changeset has a different ID than the one
1479 if the resulting changeset has a different ID than the one
1477 recorded in the patch. This may happen due to character set
1480 recorded in the patch. This may happen due to character set
1478 problems or other deficiencies in the text patch format.
1481 problems or other deficiencies in the text patch format.
1479
1482
1480 To read a patch from standard input, use patch name "-".
1483 To read a patch from standard input, use patch name "-".
1481 See 'hg help dates' for a list of formats valid for -d/--date.
1484 See 'hg help dates' for a list of formats valid for -d/--date.
1482 """
1485 """
1483 patches = (patch1,) + patches
1486 patches = (patch1,) + patches
1484
1487
1485 date = opts.get('date')
1488 date = opts.get('date')
1486 if date:
1489 if date:
1487 opts['date'] = util.parsedate(date)
1490 opts['date'] = util.parsedate(date)
1488
1491
1489 if opts.get('exact') or not opts['force']:
1492 if opts.get('exact') or not opts['force']:
1490 cmdutil.bail_if_changed(repo)
1493 cmdutil.bail_if_changed(repo)
1491
1494
1492 d = opts["base"]
1495 d = opts["base"]
1493 strip = opts["strip"]
1496 strip = opts["strip"]
1494 wlock = lock = None
1497 wlock = lock = None
1495 try:
1498 try:
1496 wlock = repo.wlock()
1499 wlock = repo.wlock()
1497 lock = repo.lock()
1500 lock = repo.lock()
1498 for p in patches:
1501 for p in patches:
1499 pf = os.path.join(d, p)
1502 pf = os.path.join(d, p)
1500
1503
1501 if pf == '-':
1504 if pf == '-':
1502 ui.status(_("applying patch from stdin\n"))
1505 ui.status(_("applying patch from stdin\n"))
1503 data = patch.extract(ui, sys.stdin)
1506 data = patch.extract(ui, sys.stdin)
1504 else:
1507 else:
1505 ui.status(_("applying %s\n") % p)
1508 ui.status(_("applying %s\n") % p)
1506 if os.path.exists(pf):
1509 if os.path.exists(pf):
1507 data = patch.extract(ui, file(pf, 'rb'))
1510 data = patch.extract(ui, file(pf, 'rb'))
1508 else:
1511 else:
1509 data = patch.extract(ui, urllib.urlopen(pf))
1512 data = patch.extract(ui, urllib.urlopen(pf))
1510 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1513 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1511
1514
1512 if tmpname is None:
1515 if tmpname is None:
1513 raise util.Abort(_('no diffs found'))
1516 raise util.Abort(_('no diffs found'))
1514
1517
1515 try:
1518 try:
1516 cmdline_message = cmdutil.logmessage(opts)
1519 cmdline_message = cmdutil.logmessage(opts)
1517 if cmdline_message:
1520 if cmdline_message:
1518 # pickup the cmdline msg
1521 # pickup the cmdline msg
1519 message = cmdline_message
1522 message = cmdline_message
1520 elif message:
1523 elif message:
1521 # pickup the patch msg
1524 # pickup the patch msg
1522 message = message.strip()
1525 message = message.strip()
1523 else:
1526 else:
1524 # launch the editor
1527 # launch the editor
1525 message = None
1528 message = None
1526 ui.debug(_('message:\n%s\n') % message)
1529 ui.debug(_('message:\n%s\n') % message)
1527
1530
1528 wp = repo.workingctx().parents()
1531 wp = repo.workingctx().parents()
1529 if opts.get('exact'):
1532 if opts.get('exact'):
1530 if not nodeid or not p1:
1533 if not nodeid or not p1:
1531 raise util.Abort(_('not a mercurial patch'))
1534 raise util.Abort(_('not a mercurial patch'))
1532 p1 = repo.lookup(p1)
1535 p1 = repo.lookup(p1)
1533 p2 = repo.lookup(p2 or hex(nullid))
1536 p2 = repo.lookup(p2 or hex(nullid))
1534
1537
1535 if p1 != wp[0].node():
1538 if p1 != wp[0].node():
1536 hg.clean(repo, p1)
1539 hg.clean(repo, p1)
1537 repo.dirstate.setparents(p1, p2)
1540 repo.dirstate.setparents(p1, p2)
1538 elif p2:
1541 elif p2:
1539 try:
1542 try:
1540 p1 = repo.lookup(p1)
1543 p1 = repo.lookup(p1)
1541 p2 = repo.lookup(p2)
1544 p2 = repo.lookup(p2)
1542 if p1 == wp[0].node():
1545 if p1 == wp[0].node():
1543 repo.dirstate.setparents(p1, p2)
1546 repo.dirstate.setparents(p1, p2)
1544 except RepoError:
1547 except RepoError:
1545 pass
1548 pass
1546 if opts.get('exact') or opts.get('import_branch'):
1549 if opts.get('exact') or opts.get('import_branch'):
1547 repo.dirstate.setbranch(branch or 'default')
1550 repo.dirstate.setbranch(branch or 'default')
1548
1551
1549 files = {}
1552 files = {}
1550 try:
1553 try:
1551 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1554 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1552 files=files)
1555 files=files)
1553 finally:
1556 finally:
1554 files = patch.updatedir(ui, repo, files)
1557 files = patch.updatedir(ui, repo, files)
1555 if not opts.get('no_commit'):
1558 if not opts.get('no_commit'):
1556 n = repo.commit(files, message, opts.get('user') or user,
1559 n = repo.commit(files, message, opts.get('user') or user,
1557 opts.get('date') or date)
1560 opts.get('date') or date)
1558 if opts.get('exact'):
1561 if opts.get('exact'):
1559 if hex(n) != nodeid:
1562 if hex(n) != nodeid:
1560 repo.rollback()
1563 repo.rollback()
1561 raise util.Abort(_('patch is damaged'
1564 raise util.Abort(_('patch is damaged'
1562 ' or loses information'))
1565 ' or loses information'))
1563 # Force a dirstate write so that the next transaction
1566 # Force a dirstate write so that the next transaction
1564 # backups an up-do-date file.
1567 # backups an up-do-date file.
1565 repo.dirstate.write()
1568 repo.dirstate.write()
1566 finally:
1569 finally:
1567 os.unlink(tmpname)
1570 os.unlink(tmpname)
1568 finally:
1571 finally:
1569 del lock, wlock
1572 del lock, wlock
1570
1573
1571 def incoming(ui, repo, source="default", **opts):
1574 def incoming(ui, repo, source="default", **opts):
1572 """show new changesets found in source
1575 """show new changesets found in source
1573
1576
1574 Show new changesets found in the specified path/URL or the default
1577 Show new changesets found in the specified path/URL or the default
1575 pull location. These are the changesets that would be pulled if a pull
1578 pull location. These are the changesets that would be pulled if a pull
1576 was requested.
1579 was requested.
1577
1580
1578 For remote repository, using --bundle avoids downloading the changesets
1581 For remote repository, using --bundle avoids downloading the changesets
1579 twice if the incoming is followed by a pull.
1582 twice if the incoming is followed by a pull.
1580
1583
1581 See pull for valid source format details.
1584 See pull for valid source format details.
1582 """
1585 """
1583 limit = cmdutil.loglimit(opts)
1586 limit = cmdutil.loglimit(opts)
1584 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
1587 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
1585 cmdutil.setremoteconfig(ui, opts)
1588 cmdutil.setremoteconfig(ui, opts)
1586
1589
1587 other = hg.repository(ui, source)
1590 other = hg.repository(ui, source)
1588 ui.status(_('comparing with %s\n') % util.hidepassword(source))
1591 ui.status(_('comparing with %s\n') % util.hidepassword(source))
1589 if revs:
1592 if revs:
1590 revs = [other.lookup(rev) for rev in revs]
1593 revs = [other.lookup(rev) for rev in revs]
1591 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1594 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1592 if not incoming:
1595 if not incoming:
1593 try:
1596 try:
1594 os.unlink(opts["bundle"])
1597 os.unlink(opts["bundle"])
1595 except:
1598 except:
1596 pass
1599 pass
1597 ui.status(_("no changes found\n"))
1600 ui.status(_("no changes found\n"))
1598 return 1
1601 return 1
1599
1602
1600 cleanup = None
1603 cleanup = None
1601 try:
1604 try:
1602 fname = opts["bundle"]
1605 fname = opts["bundle"]
1603 if fname or not other.local():
1606 if fname or not other.local():
1604 # create a bundle (uncompressed if other repo is not local)
1607 # create a bundle (uncompressed if other repo is not local)
1605 if revs is None:
1608 if revs is None:
1606 cg = other.changegroup(incoming, "incoming")
1609 cg = other.changegroup(incoming, "incoming")
1607 else:
1610 else:
1608 cg = other.changegroupsubset(incoming, revs, 'incoming')
1611 cg = other.changegroupsubset(incoming, revs, 'incoming')
1609 bundletype = other.local() and "HG10BZ" or "HG10UN"
1612 bundletype = other.local() and "HG10BZ" or "HG10UN"
1610 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1613 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1611 # keep written bundle?
1614 # keep written bundle?
1612 if opts["bundle"]:
1615 if opts["bundle"]:
1613 cleanup = None
1616 cleanup = None
1614 if not other.local():
1617 if not other.local():
1615 # use the created uncompressed bundlerepo
1618 # use the created uncompressed bundlerepo
1616 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1619 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1617
1620
1618 o = other.changelog.nodesbetween(incoming, revs)[0]
1621 o = other.changelog.nodesbetween(incoming, revs)[0]
1619 if opts['newest_first']:
1622 if opts['newest_first']:
1620 o.reverse()
1623 o.reverse()
1621 displayer = cmdutil.show_changeset(ui, other, opts)
1624 displayer = cmdutil.show_changeset(ui, other, opts)
1622 count = 0
1625 count = 0
1623 for n in o:
1626 for n in o:
1624 if count >= limit:
1627 if count >= limit:
1625 break
1628 break
1626 parents = [p for p in other.changelog.parents(n) if p != nullid]
1629 parents = [p for p in other.changelog.parents(n) if p != nullid]
1627 if opts['no_merges'] and len(parents) == 2:
1630 if opts['no_merges'] and len(parents) == 2:
1628 continue
1631 continue
1629 count += 1
1632 count += 1
1630 displayer.show(changenode=n)
1633 displayer.show(changenode=n)
1631 finally:
1634 finally:
1632 if hasattr(other, 'close'):
1635 if hasattr(other, 'close'):
1633 other.close()
1636 other.close()
1634 if cleanup:
1637 if cleanup:
1635 os.unlink(cleanup)
1638 os.unlink(cleanup)
1636
1639
1637 def init(ui, dest=".", **opts):
1640 def init(ui, dest=".", **opts):
1638 """create a new repository in the given directory
1641 """create a new repository in the given directory
1639
1642
1640 Initialize a new repository in the given directory. If the given
1643 Initialize a new repository in the given directory. If the given
1641 directory does not exist, it is created.
1644 directory does not exist, it is created.
1642
1645
1643 If no directory is given, the current directory is used.
1646 If no directory is given, the current directory is used.
1644
1647
1645 It is possible to specify an ssh:// URL as the destination.
1648 It is possible to specify an ssh:// URL as the destination.
1646 Look at the help text for the pull command for important details
1649 Look at the help text for the pull command for important details
1647 about ssh:// URLs.
1650 about ssh:// URLs.
1648 """
1651 """
1649 cmdutil.setremoteconfig(ui, opts)
1652 cmdutil.setremoteconfig(ui, opts)
1650 hg.repository(ui, dest, create=1)
1653 hg.repository(ui, dest, create=1)
1651
1654
1652 def locate(ui, repo, *pats, **opts):
1655 def locate(ui, repo, *pats, **opts):
1653 """locate files matching specific patterns
1656 """locate files matching specific patterns
1654
1657
1655 Print all files under Mercurial control whose names match the
1658 Print all files under Mercurial control whose names match the
1656 given patterns.
1659 given patterns.
1657
1660
1658 This command searches the entire repository by default. To search
1661 This command searches the entire repository by default. To search
1659 just the current directory and its subdirectories, use
1662 just the current directory and its subdirectories, use
1660 "--include .".
1663 "--include .".
1661
1664
1662 If no patterns are given to match, this command prints all file
1665 If no patterns are given to match, this command prints all file
1663 names.
1666 names.
1664
1667
1665 If you want to feed the output of this command into the "xargs"
1668 If you want to feed the output of this command into the "xargs"
1666 command, use the "-0" option to both this command and "xargs".
1669 command, use the "-0" option to both this command and "xargs".
1667 This will avoid the problem of "xargs" treating single filenames
1670 This will avoid the problem of "xargs" treating single filenames
1668 that contain white space as multiple filenames.
1671 that contain white space as multiple filenames.
1669 """
1672 """
1670 end = opts['print0'] and '\0' or '\n'
1673 end = opts['print0'] and '\0' or '\n'
1671 rev = opts['rev']
1674 rev = opts['rev']
1672 if rev:
1675 if rev:
1673 node = repo.lookup(rev)
1676 node = repo.lookup(rev)
1674 else:
1677 else:
1675 node = None
1678 node = None
1676
1679
1677 ret = 1
1680 ret = 1
1678 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1681 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
1679 badmatch=util.always,
1682 badmatch=util.always,
1680 default='relglob'):
1683 default='relglob'):
1681 if src == 'b':
1684 if src == 'b':
1682 continue
1685 continue
1683 if not node and abs not in repo.dirstate:
1686 if not node and abs not in repo.dirstate:
1684 continue
1687 continue
1685 if opts['fullpath']:
1688 if opts['fullpath']:
1686 ui.write(os.path.join(repo.root, abs), end)
1689 ui.write(os.path.join(repo.root, abs), end)
1687 else:
1690 else:
1688 ui.write(((pats and rel) or abs), end)
1691 ui.write(((pats and rel) or abs), end)
1689 ret = 0
1692 ret = 0
1690
1693
1691 return ret
1694 return ret
1692
1695
1693 def log(ui, repo, *pats, **opts):
1696 def log(ui, repo, *pats, **opts):
1694 """show revision history of entire repository or files
1697 """show revision history of entire repository or files
1695
1698
1696 Print the revision history of the specified files or the entire
1699 Print the revision history of the specified files or the entire
1697 project.
1700 project.
1698
1701
1699 File history is shown without following rename or copy history of
1702 File history is shown without following rename or copy history of
1700 files. Use -f/--follow with a file name to follow history across
1703 files. Use -f/--follow with a file name to follow history across
1701 renames and copies. --follow without a file name will only show
1704 renames and copies. --follow without a file name will only show
1702 ancestors or descendants of the starting revision. --follow-first
1705 ancestors or descendants of the starting revision. --follow-first
1703 only follows the first parent of merge revisions.
1706 only follows the first parent of merge revisions.
1704
1707
1705 If no revision range is specified, the default is tip:0 unless
1708 If no revision range is specified, the default is tip:0 unless
1706 --follow is set, in which case the working directory parent is
1709 --follow is set, in which case the working directory parent is
1707 used as the starting revision.
1710 used as the starting revision.
1708
1711
1709 See 'hg help dates' for a list of formats valid for -d/--date.
1712 See 'hg help dates' for a list of formats valid for -d/--date.
1710
1713
1711 By default this command outputs: changeset id and hash, tags,
1714 By default this command outputs: changeset id and hash, tags,
1712 non-trivial parents, user, date and time, and a summary for each
1715 non-trivial parents, user, date and time, and a summary for each
1713 commit. When the -v/--verbose switch is used, the list of changed
1716 commit. When the -v/--verbose switch is used, the list of changed
1714 files and full commit message is shown.
1717 files and full commit message is shown.
1715
1718
1716 NOTE: log -p may generate unexpected diff output for merge
1719 NOTE: log -p may generate unexpected diff output for merge
1717 changesets, as it will compare the merge changeset against its
1720 changesets, as it will compare the merge changeset against its
1718 first parent only. Also, the files: list will only reflect files
1721 first parent only. Also, the files: list will only reflect files
1719 that are different from BOTH parents.
1722 that are different from BOTH parents.
1720
1723
1721 """
1724 """
1722
1725
1723 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1726 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1724 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1727 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1725
1728
1726 limit = cmdutil.loglimit(opts)
1729 limit = cmdutil.loglimit(opts)
1727 count = 0
1730 count = 0
1728
1731
1729 if opts['copies'] and opts['rev']:
1732 if opts['copies'] and opts['rev']:
1730 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1733 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1731 else:
1734 else:
1732 endrev = repo.changelog.count()
1735 endrev = repo.changelog.count()
1733 rcache = {}
1736 rcache = {}
1734 ncache = {}
1737 ncache = {}
1735 def getrenamed(fn, rev):
1738 def getrenamed(fn, rev):
1736 '''looks up all renames for a file (up to endrev) the first
1739 '''looks up all renames for a file (up to endrev) the first
1737 time the file is given. It indexes on the changerev and only
1740 time the file is given. It indexes on the changerev and only
1738 parses the manifest if linkrev != changerev.
1741 parses the manifest if linkrev != changerev.
1739 Returns rename info for fn at changerev rev.'''
1742 Returns rename info for fn at changerev rev.'''
1740 if fn not in rcache:
1743 if fn not in rcache:
1741 rcache[fn] = {}
1744 rcache[fn] = {}
1742 ncache[fn] = {}
1745 ncache[fn] = {}
1743 fl = repo.file(fn)
1746 fl = repo.file(fn)
1744 for i in xrange(fl.count()):
1747 for i in xrange(fl.count()):
1745 node = fl.node(i)
1748 node = fl.node(i)
1746 lr = fl.linkrev(node)
1749 lr = fl.linkrev(node)
1747 renamed = fl.renamed(node)
1750 renamed = fl.renamed(node)
1748 rcache[fn][lr] = renamed
1751 rcache[fn][lr] = renamed
1749 if renamed:
1752 if renamed:
1750 ncache[fn][node] = renamed
1753 ncache[fn][node] = renamed
1751 if lr >= endrev:
1754 if lr >= endrev:
1752 break
1755 break
1753 if rev in rcache[fn]:
1756 if rev in rcache[fn]:
1754 return rcache[fn][rev]
1757 return rcache[fn][rev]
1755
1758
1756 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1759 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1757 # filectx logic.
1760 # filectx logic.
1758
1761
1759 try:
1762 try:
1760 return repo.changectx(rev).filectx(fn).renamed()
1763 return repo.changectx(rev).filectx(fn).renamed()
1761 except revlog.LookupError:
1764 except revlog.LookupError:
1762 pass
1765 pass
1763 return None
1766 return None
1764
1767
1765 df = False
1768 df = False
1766 if opts["date"]:
1769 if opts["date"]:
1767 df = util.matchdate(opts["date"])
1770 df = util.matchdate(opts["date"])
1768
1771
1769 only_branches = opts['only_branch']
1772 only_branches = opts['only_branch']
1770
1773
1771 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1774 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1772 for st, rev, fns in changeiter:
1775 for st, rev, fns in changeiter:
1773 if st == 'add':
1776 if st == 'add':
1774 changenode = repo.changelog.node(rev)
1777 changenode = repo.changelog.node(rev)
1775 parents = [p for p in repo.changelog.parentrevs(rev)
1778 parents = [p for p in repo.changelog.parentrevs(rev)
1776 if p != nullrev]
1779 if p != nullrev]
1777 if opts['no_merges'] and len(parents) == 2:
1780 if opts['no_merges'] and len(parents) == 2:
1778 continue
1781 continue
1779 if opts['only_merges'] and len(parents) != 2:
1782 if opts['only_merges'] and len(parents) != 2:
1780 continue
1783 continue
1781
1784
1782 if only_branches:
1785 if only_branches:
1783 revbranch = get(rev)[5]['branch']
1786 revbranch = get(rev)[5]['branch']
1784 if revbranch not in only_branches:
1787 if revbranch not in only_branches:
1785 continue
1788 continue
1786
1789
1787 if df:
1790 if df:
1788 changes = get(rev)
1791 changes = get(rev)
1789 if not df(changes[2][0]):
1792 if not df(changes[2][0]):
1790 continue
1793 continue
1791
1794
1792 if opts['keyword']:
1795 if opts['keyword']:
1793 changes = get(rev)
1796 changes = get(rev)
1794 miss = 0
1797 miss = 0
1795 for k in [kw.lower() for kw in opts['keyword']]:
1798 for k in [kw.lower() for kw in opts['keyword']]:
1796 if not (k in changes[1].lower() or
1799 if not (k in changes[1].lower() or
1797 k in changes[4].lower() or
1800 k in changes[4].lower() or
1798 k in " ".join(changes[3]).lower()):
1801 k in " ".join(changes[3]).lower()):
1799 miss = 1
1802 miss = 1
1800 break
1803 break
1801 if miss:
1804 if miss:
1802 continue
1805 continue
1803
1806
1804 copies = []
1807 copies = []
1805 if opts.get('copies') and rev:
1808 if opts.get('copies') and rev:
1806 for fn in get(rev)[3]:
1809 for fn in get(rev)[3]:
1807 rename = getrenamed(fn, rev)
1810 rename = getrenamed(fn, rev)
1808 if rename:
1811 if rename:
1809 copies.append((fn, rename[0]))
1812 copies.append((fn, rename[0]))
1810 displayer.show(rev, changenode, copies=copies)
1813 displayer.show(rev, changenode, copies=copies)
1811 elif st == 'iter':
1814 elif st == 'iter':
1812 if count == limit: break
1815 if count == limit: break
1813 if displayer.flush(rev):
1816 if displayer.flush(rev):
1814 count += 1
1817 count += 1
1815
1818
1816 def manifest(ui, repo, node=None, rev=None):
1819 def manifest(ui, repo, node=None, rev=None):
1817 """output the current or given revision of the project manifest
1820 """output the current or given revision of the project manifest
1818
1821
1819 Print a list of version controlled files for the given revision.
1822 Print a list of version controlled files for the given revision.
1820 If no revision is given, the parent of the working directory is used,
1823 If no revision is given, the parent of the working directory is used,
1821 or tip if no revision is checked out.
1824 or tip if no revision is checked out.
1822
1825
1823 The manifest is the list of files being version controlled. If no revision
1826 The manifest is the list of files being version controlled. If no revision
1824 is given then the first parent of the working directory is used.
1827 is given then the first parent of the working directory is used.
1825
1828
1826 With -v flag, print file permissions, symlink and executable bits. With
1829 With -v flag, print file permissions, symlink and executable bits. With
1827 --debug flag, print file revision hashes.
1830 --debug flag, print file revision hashes.
1828 """
1831 """
1829
1832
1830 if rev and node:
1833 if rev and node:
1831 raise util.Abort(_("please specify just one revision"))
1834 raise util.Abort(_("please specify just one revision"))
1832
1835
1833 if not node:
1836 if not node:
1834 node = rev
1837 node = rev
1835
1838
1836 m = repo.changectx(node).manifest()
1839 m = repo.changectx(node).manifest()
1837 files = m.keys()
1840 files = m.keys()
1838 files.sort()
1841 files.sort()
1839
1842
1840 for f in files:
1843 for f in files:
1841 if ui.debugflag:
1844 if ui.debugflag:
1842 ui.write("%40s " % hex(m[f]))
1845 ui.write("%40s " % hex(m[f]))
1843 if ui.verbose:
1846 if ui.verbose:
1844 type = m.execf(f) and "*" or m.linkf(f) and "@" or " "
1847 type = m.execf(f) and "*" or m.linkf(f) and "@" or " "
1845 perm = m.execf(f) and "755" or "644"
1848 perm = m.execf(f) and "755" or "644"
1846 ui.write("%3s %1s " % (perm, type))
1849 ui.write("%3s %1s " % (perm, type))
1847 ui.write("%s\n" % f)
1850 ui.write("%s\n" % f)
1848
1851
1849 def merge(ui, repo, node=None, force=None, rev=None):
1852 def merge(ui, repo, node=None, force=None, rev=None):
1850 """merge working directory with another revision
1853 """merge working directory with another revision
1851
1854
1852 Merge the contents of the current working directory and the
1855 Merge the contents of the current working directory and the
1853 requested revision. Files that changed between either parent are
1856 requested revision. Files that changed between either parent are
1854 marked as changed for the next commit and a commit must be
1857 marked as changed for the next commit and a commit must be
1855 performed before any further updates are allowed.
1858 performed before any further updates are allowed.
1856
1859
1857 If no revision is specified, the working directory's parent is a
1860 If no revision is specified, the working directory's parent is a
1858 head revision, and the repository contains exactly one other head,
1861 head revision, and the repository contains exactly one other head,
1859 the other head is merged with by default. Otherwise, an explicit
1862 the other head is merged with by default. Otherwise, an explicit
1860 revision to merge with must be provided.
1863 revision to merge with must be provided.
1861 """
1864 """
1862
1865
1863 if rev and node:
1866 if rev and node:
1864 raise util.Abort(_("please specify just one revision"))
1867 raise util.Abort(_("please specify just one revision"))
1865 if not node:
1868 if not node:
1866 node = rev
1869 node = rev
1867
1870
1868 if not node:
1871 if not node:
1869 heads = repo.heads()
1872 heads = repo.heads()
1870 if len(heads) > 2:
1873 if len(heads) > 2:
1871 raise util.Abort(_('repo has %d heads - '
1874 raise util.Abort(_('repo has %d heads - '
1872 'please merge with an explicit rev') %
1875 'please merge with an explicit rev') %
1873 len(heads))
1876 len(heads))
1874 parent = repo.dirstate.parents()[0]
1877 parent = repo.dirstate.parents()[0]
1875 if len(heads) == 1:
1878 if len(heads) == 1:
1876 msg = _('there is nothing to merge')
1879 msg = _('there is nothing to merge')
1877 if parent != repo.lookup(repo.workingctx().branch()):
1880 if parent != repo.lookup(repo.workingctx().branch()):
1878 msg = _('%s - use "hg update" instead') % msg
1881 msg = _('%s - use "hg update" instead') % msg
1879 raise util.Abort(msg)
1882 raise util.Abort(msg)
1880
1883
1881 if parent not in heads:
1884 if parent not in heads:
1882 raise util.Abort(_('working dir not at a head rev - '
1885 raise util.Abort(_('working dir not at a head rev - '
1883 'use "hg update" or merge with an explicit rev'))
1886 'use "hg update" or merge with an explicit rev'))
1884 node = parent == heads[0] and heads[-1] or heads[0]
1887 node = parent == heads[0] and heads[-1] or heads[0]
1885 return hg.merge(repo, node, force=force)
1888 return hg.merge(repo, node, force=force)
1886
1889
1887 def outgoing(ui, repo, dest=None, **opts):
1890 def outgoing(ui, repo, dest=None, **opts):
1888 """show changesets not found in destination
1891 """show changesets not found in destination
1889
1892
1890 Show changesets not found in the specified destination repository or
1893 Show changesets not found in the specified destination repository or
1891 the default push location. These are the changesets that would be pushed
1894 the default push location. These are the changesets that would be pushed
1892 if a push was requested.
1895 if a push was requested.
1893
1896
1894 See pull for valid destination format details.
1897 See pull for valid destination format details.
1895 """
1898 """
1896 limit = cmdutil.loglimit(opts)
1899 limit = cmdutil.loglimit(opts)
1897 dest, revs, checkout = hg.parseurl(
1900 dest, revs, checkout = hg.parseurl(
1898 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1901 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1899 cmdutil.setremoteconfig(ui, opts)
1902 cmdutil.setremoteconfig(ui, opts)
1900 if revs:
1903 if revs:
1901 revs = [repo.lookup(rev) for rev in revs]
1904 revs = [repo.lookup(rev) for rev in revs]
1902
1905
1903 other = hg.repository(ui, dest)
1906 other = hg.repository(ui, dest)
1904 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
1907 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
1905 o = repo.findoutgoing(other, force=opts['force'])
1908 o = repo.findoutgoing(other, force=opts['force'])
1906 if not o:
1909 if not o:
1907 ui.status(_("no changes found\n"))
1910 ui.status(_("no changes found\n"))
1908 return 1
1911 return 1
1909 o = repo.changelog.nodesbetween(o, revs)[0]
1912 o = repo.changelog.nodesbetween(o, revs)[0]
1910 if opts['newest_first']:
1913 if opts['newest_first']:
1911 o.reverse()
1914 o.reverse()
1912 displayer = cmdutil.show_changeset(ui, repo, opts)
1915 displayer = cmdutil.show_changeset(ui, repo, opts)
1913 count = 0
1916 count = 0
1914 for n in o:
1917 for n in o:
1915 if count >= limit:
1918 if count >= limit:
1916 break
1919 break
1917 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1920 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1918 if opts['no_merges'] and len(parents) == 2:
1921 if opts['no_merges'] and len(parents) == 2:
1919 continue
1922 continue
1920 count += 1
1923 count += 1
1921 displayer.show(changenode=n)
1924 displayer.show(changenode=n)
1922
1925
1923 def parents(ui, repo, file_=None, **opts):
1926 def parents(ui, repo, file_=None, **opts):
1924 """show the parents of the working dir or revision
1927 """show the parents of the working dir or revision
1925
1928
1926 Print the working directory's parent revisions. If a
1929 Print the working directory's parent revisions. If a
1927 revision is given via --rev, the parent of that revision
1930 revision is given via --rev, the parent of that revision
1928 will be printed. If a file argument is given, revision in
1931 will be printed. If a file argument is given, revision in
1929 which the file was last changed (before the working directory
1932 which the file was last changed (before the working directory
1930 revision or the argument to --rev if given) is printed.
1933 revision or the argument to --rev if given) is printed.
1931 """
1934 """
1932 rev = opts.get('rev')
1935 rev = opts.get('rev')
1933 if rev:
1936 if rev:
1934 ctx = repo.changectx(rev)
1937 ctx = repo.changectx(rev)
1935 else:
1938 else:
1936 ctx = repo.workingctx()
1939 ctx = repo.workingctx()
1937
1940
1938 if file_:
1941 if file_:
1939 files, match, anypats = cmdutil.matchpats(repo, (file_,), opts)
1942 files, match, anypats = cmdutil.matchpats(repo, (file_,), opts)
1940 if anypats or len(files) != 1:
1943 if anypats or len(files) != 1:
1941 raise util.Abort(_('can only specify an explicit file name'))
1944 raise util.Abort(_('can only specify an explicit file name'))
1942 file_ = files[0]
1945 file_ = files[0]
1943 filenodes = []
1946 filenodes = []
1944 for cp in ctx.parents():
1947 for cp in ctx.parents():
1945 if not cp:
1948 if not cp:
1946 continue
1949 continue
1947 try:
1950 try:
1948 filenodes.append(cp.filenode(file_))
1951 filenodes.append(cp.filenode(file_))
1949 except revlog.LookupError:
1952 except revlog.LookupError:
1950 pass
1953 pass
1951 if not filenodes:
1954 if not filenodes:
1952 raise util.Abort(_("'%s' not found in manifest!") % file_)
1955 raise util.Abort(_("'%s' not found in manifest!") % file_)
1953 fl = repo.file(file_)
1956 fl = repo.file(file_)
1954 p = [repo.lookup(fl.linkrev(fn)) for fn in filenodes]
1957 p = [repo.lookup(fl.linkrev(fn)) for fn in filenodes]
1955 else:
1958 else:
1956 p = [cp.node() for cp in ctx.parents()]
1959 p = [cp.node() for cp in ctx.parents()]
1957
1960
1958 displayer = cmdutil.show_changeset(ui, repo, opts)
1961 displayer = cmdutil.show_changeset(ui, repo, opts)
1959 for n in p:
1962 for n in p:
1960 if n != nullid:
1963 if n != nullid:
1961 displayer.show(changenode=n)
1964 displayer.show(changenode=n)
1962
1965
1963 def paths(ui, repo, search=None):
1966 def paths(ui, repo, search=None):
1964 """show definition of symbolic path names
1967 """show definition of symbolic path names
1965
1968
1966 Show definition of symbolic path name NAME. If no name is given, show
1969 Show definition of symbolic path name NAME. If no name is given, show
1967 definition of available names.
1970 definition of available names.
1968
1971
1969 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1972 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1970 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1973 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1971 """
1974 """
1972 if search:
1975 if search:
1973 for name, path in ui.configitems("paths"):
1976 for name, path in ui.configitems("paths"):
1974 if name == search:
1977 if name == search:
1975 ui.write("%s\n" % util.hidepassword(path))
1978 ui.write("%s\n" % util.hidepassword(path))
1976 return
1979 return
1977 ui.warn(_("not found!\n"))
1980 ui.warn(_("not found!\n"))
1978 return 1
1981 return 1
1979 else:
1982 else:
1980 for name, path in ui.configitems("paths"):
1983 for name, path in ui.configitems("paths"):
1981 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
1984 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
1982
1985
1983 def postincoming(ui, repo, modheads, optupdate, checkout):
1986 def postincoming(ui, repo, modheads, optupdate, checkout):
1984 if modheads == 0:
1987 if modheads == 0:
1985 return
1988 return
1986 if optupdate:
1989 if optupdate:
1987 if modheads <= 1 or checkout:
1990 if modheads <= 1 or checkout:
1988 return hg.update(repo, checkout)
1991 return hg.update(repo, checkout)
1989 else:
1992 else:
1990 ui.status(_("not updating, since new heads added\n"))
1993 ui.status(_("not updating, since new heads added\n"))
1991 if modheads > 1:
1994 if modheads > 1:
1992 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1995 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
1993 else:
1996 else:
1994 ui.status(_("(run 'hg update' to get a working copy)\n"))
1997 ui.status(_("(run 'hg update' to get a working copy)\n"))
1995
1998
1996 def pull(ui, repo, source="default", **opts):
1999 def pull(ui, repo, source="default", **opts):
1997 """pull changes from the specified source
2000 """pull changes from the specified source
1998
2001
1999 Pull changes from a remote repository to a local one.
2002 Pull changes from a remote repository to a local one.
2000
2003
2001 This finds all changes from the repository at the specified path
2004 This finds all changes from the repository at the specified path
2002 or URL and adds them to the local repository. By default, this
2005 or URL and adds them to the local repository. By default, this
2003 does not update the copy of the project in the working directory.
2006 does not update the copy of the project in the working directory.
2004
2007
2005 Valid URLs are of the form:
2008 Valid URLs are of the form:
2006
2009
2007 local/filesystem/path (or file://local/filesystem/path)
2010 local/filesystem/path (or file://local/filesystem/path)
2008 http://[user@]host[:port]/[path]
2011 http://[user@]host[:port]/[path]
2009 https://[user@]host[:port]/[path]
2012 https://[user@]host[:port]/[path]
2010 ssh://[user@]host[:port]/[path]
2013 ssh://[user@]host[:port]/[path]
2011 static-http://host[:port]/[path]
2014 static-http://host[:port]/[path]
2012
2015
2013 Paths in the local filesystem can either point to Mercurial
2016 Paths in the local filesystem can either point to Mercurial
2014 repositories or to bundle files (as created by 'hg bundle' or
2017 repositories or to bundle files (as created by 'hg bundle' or
2015 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2018 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2016 allows access to a Mercurial repository where you simply use a web
2019 allows access to a Mercurial repository where you simply use a web
2017 server to publish the .hg directory as static content.
2020 server to publish the .hg directory as static content.
2018
2021
2019 An optional identifier after # indicates a particular branch, tag,
2022 An optional identifier after # indicates a particular branch, tag,
2020 or changeset to pull.
2023 or changeset to pull.
2021
2024
2022 Some notes about using SSH with Mercurial:
2025 Some notes about using SSH with Mercurial:
2023 - SSH requires an accessible shell account on the destination machine
2026 - SSH requires an accessible shell account on the destination machine
2024 and a copy of hg in the remote path or specified with as remotecmd.
2027 and a copy of hg in the remote path or specified with as remotecmd.
2025 - path is relative to the remote user's home directory by default.
2028 - path is relative to the remote user's home directory by default.
2026 Use an extra slash at the start of a path to specify an absolute path:
2029 Use an extra slash at the start of a path to specify an absolute path:
2027 ssh://example.com//tmp/repository
2030 ssh://example.com//tmp/repository
2028 - Mercurial doesn't use its own compression via SSH; the right thing
2031 - Mercurial doesn't use its own compression via SSH; the right thing
2029 to do is to configure it in your ~/.ssh/config, e.g.:
2032 to do is to configure it in your ~/.ssh/config, e.g.:
2030 Host *.mylocalnetwork.example.com
2033 Host *.mylocalnetwork.example.com
2031 Compression no
2034 Compression no
2032 Host *
2035 Host *
2033 Compression yes
2036 Compression yes
2034 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2037 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2035 with the --ssh command line option.
2038 with the --ssh command line option.
2036 """
2039 """
2037 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
2040 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
2038 cmdutil.setremoteconfig(ui, opts)
2041 cmdutil.setremoteconfig(ui, opts)
2039
2042
2040 other = hg.repository(ui, source)
2043 other = hg.repository(ui, source)
2041 ui.status(_('pulling from %s\n') % util.hidepassword(source))
2044 ui.status(_('pulling from %s\n') % util.hidepassword(source))
2042 if revs:
2045 if revs:
2043 try:
2046 try:
2044 revs = [other.lookup(rev) for rev in revs]
2047 revs = [other.lookup(rev) for rev in revs]
2045 except repo.NoCapability:
2048 except repo.NoCapability:
2046 error = _("Other repository doesn't support revision lookup, "
2049 error = _("Other repository doesn't support revision lookup, "
2047 "so a rev cannot be specified.")
2050 "so a rev cannot be specified.")
2048 raise util.Abort(error)
2051 raise util.Abort(error)
2049
2052
2050 modheads = repo.pull(other, heads=revs, force=opts['force'])
2053 modheads = repo.pull(other, heads=revs, force=opts['force'])
2051 return postincoming(ui, repo, modheads, opts['update'], checkout)
2054 return postincoming(ui, repo, modheads, opts['update'], checkout)
2052
2055
2053 def push(ui, repo, dest=None, **opts):
2056 def push(ui, repo, dest=None, **opts):
2054 """push changes to the specified destination
2057 """push changes to the specified destination
2055
2058
2056 Push changes from the local repository to the given destination.
2059 Push changes from the local repository to the given destination.
2057
2060
2058 This is the symmetrical operation for pull. It helps to move
2061 This is the symmetrical operation for pull. It helps to move
2059 changes from the current repository to a different one. If the
2062 changes from the current repository to a different one. If the
2060 destination is local this is identical to a pull in that directory
2063 destination is local this is identical to a pull in that directory
2061 from the current one.
2064 from the current one.
2062
2065
2063 By default, push will refuse to run if it detects the result would
2066 By default, push will refuse to run if it detects the result would
2064 increase the number of remote heads. This generally indicates the
2067 increase the number of remote heads. This generally indicates the
2065 the client has forgotten to sync and merge before pushing.
2068 the client has forgotten to sync and merge before pushing.
2066
2069
2067 Valid URLs are of the form:
2070 Valid URLs are of the form:
2068
2071
2069 local/filesystem/path (or file://local/filesystem/path)
2072 local/filesystem/path (or file://local/filesystem/path)
2070 ssh://[user@]host[:port]/[path]
2073 ssh://[user@]host[:port]/[path]
2071 http://[user@]host[:port]/[path]
2074 http://[user@]host[:port]/[path]
2072 https://[user@]host[:port]/[path]
2075 https://[user@]host[:port]/[path]
2073
2076
2074 An optional identifier after # indicates a particular branch, tag,
2077 An optional identifier after # indicates a particular branch, tag,
2075 or changeset to push.
2078 or changeset to push.
2076
2079
2077 Look at the help text for the pull command for important details
2080 Look at the help text for the pull command for important details
2078 about ssh:// URLs.
2081 about ssh:// URLs.
2079
2082
2080 Pushing to http:// and https:// URLs is only possible, if this
2083 Pushing to http:// and https:// URLs is only possible, if this
2081 feature is explicitly enabled on the remote Mercurial server.
2084 feature is explicitly enabled on the remote Mercurial server.
2082 """
2085 """
2083 dest, revs, checkout = hg.parseurl(
2086 dest, revs, checkout = hg.parseurl(
2084 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2087 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2085 cmdutil.setremoteconfig(ui, opts)
2088 cmdutil.setremoteconfig(ui, opts)
2086
2089
2087 other = hg.repository(ui, dest)
2090 other = hg.repository(ui, dest)
2088 ui.status('pushing to %s\n' % util.hidepassword(dest))
2091 ui.status('pushing to %s\n' % util.hidepassword(dest))
2089 if revs:
2092 if revs:
2090 revs = [repo.lookup(rev) for rev in revs]
2093 revs = [repo.lookup(rev) for rev in revs]
2091 r = repo.push(other, opts['force'], revs=revs)
2094 r = repo.push(other, opts['force'], revs=revs)
2092 return r == 0
2095 return r == 0
2093
2096
2094 def rawcommit(ui, repo, *pats, **opts):
2097 def rawcommit(ui, repo, *pats, **opts):
2095 """raw commit interface (DEPRECATED)
2098 """raw commit interface (DEPRECATED)
2096
2099
2097 (DEPRECATED)
2100 (DEPRECATED)
2098 Lowlevel commit, for use in helper scripts.
2101 Lowlevel commit, for use in helper scripts.
2099
2102
2100 This command is not intended to be used by normal users, as it is
2103 This command is not intended to be used by normal users, as it is
2101 primarily useful for importing from other SCMs.
2104 primarily useful for importing from other SCMs.
2102
2105
2103 This command is now deprecated and will be removed in a future
2106 This command is now deprecated and will be removed in a future
2104 release, please use debugsetparents and commit instead.
2107 release, please use debugsetparents and commit instead.
2105 """
2108 """
2106
2109
2107 ui.warn(_("(the rawcommit command is deprecated)\n"))
2110 ui.warn(_("(the rawcommit command is deprecated)\n"))
2108
2111
2109 message = cmdutil.logmessage(opts)
2112 message = cmdutil.logmessage(opts)
2110
2113
2111 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2114 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2112 if opts['files']:
2115 if opts['files']:
2113 files += open(opts['files']).read().splitlines()
2116 files += open(opts['files']).read().splitlines()
2114
2117
2115 parents = [repo.lookup(p) for p in opts['parent']]
2118 parents = [repo.lookup(p) for p in opts['parent']]
2116
2119
2117 try:
2120 try:
2118 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2121 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2119 except ValueError, inst:
2122 except ValueError, inst:
2120 raise util.Abort(str(inst))
2123 raise util.Abort(str(inst))
2121
2124
2122 def recover(ui, repo):
2125 def recover(ui, repo):
2123 """roll back an interrupted transaction
2126 """roll back an interrupted transaction
2124
2127
2125 Recover from an interrupted commit or pull.
2128 Recover from an interrupted commit or pull.
2126
2129
2127 This command tries to fix the repository status after an interrupted
2130 This command tries to fix the repository status after an interrupted
2128 operation. It should only be necessary when Mercurial suggests it.
2131 operation. It should only be necessary when Mercurial suggests it.
2129 """
2132 """
2130 if repo.recover():
2133 if repo.recover():
2131 return hg.verify(repo)
2134 return hg.verify(repo)
2132 return 1
2135 return 1
2133
2136
2134 def remove(ui, repo, *pats, **opts):
2137 def remove(ui, repo, *pats, **opts):
2135 """remove the specified files on the next commit
2138 """remove the specified files on the next commit
2136
2139
2137 Schedule the indicated files for removal from the repository.
2140 Schedule the indicated files for removal from the repository.
2138
2141
2139 This only removes files from the current branch, not from the entire
2142 This only removes files from the current branch, not from the entire
2140 project history. -A can be used to remove only files that have already
2143 project history. -A can be used to remove only files that have already
2141 been deleted, -f can be used to force deletion, and -Af can be used
2144 been deleted, -f can be used to force deletion, and -Af can be used
2142 to remove files from the next revision without deleting them.
2145 to remove files from the next revision without deleting them.
2143
2146
2144 The following table details the behavior of remove for different file
2147 The following table details the behavior of remove for different file
2145 states (columns) and option combinations (rows). The file states are
2148 states (columns) and option combinations (rows). The file states are
2146 Added, Clean, Modified and Missing (as reported by hg status). The
2149 Added, Clean, Modified and Missing (as reported by hg status). The
2147 actions are Warn, Remove (from branch) and Delete (from disk).
2150 actions are Warn, Remove (from branch) and Delete (from disk).
2148
2151
2149 A C M !
2152 A C M !
2150 none W RD W R
2153 none W RD W R
2151 -f R RD RD R
2154 -f R RD RD R
2152 -A W W W R
2155 -A W W W R
2153 -Af R R R R
2156 -Af R R R R
2154
2157
2155 This command schedules the files to be removed at the next commit.
2158 This command schedules the files to be removed at the next commit.
2156 To undo a remove before that, see hg revert.
2159 To undo a remove before that, see hg revert.
2157 """
2160 """
2158
2161
2159 after, force = opts.get('after'), opts.get('force')
2162 after, force = opts.get('after'), opts.get('force')
2160 if not pats and not after:
2163 if not pats and not after:
2161 raise util.Abort(_('no files specified'))
2164 raise util.Abort(_('no files specified'))
2162
2165
2163 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2166 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2164 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2167 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2165 modified, added, removed, deleted, unknown = mardu
2168 modified, added, removed, deleted, unknown = mardu
2166
2169
2167 remove, forget = [], []
2170 remove, forget = [], []
2168 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2171 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts):
2169
2172
2170 reason = None
2173 reason = None
2171 if abs in removed or abs in unknown:
2174 if abs in removed or abs in unknown:
2172 continue
2175 continue
2173
2176
2174 # last column
2177 # last column
2175 elif abs in deleted:
2178 elif abs in deleted:
2176 remove.append(abs)
2179 remove.append(abs)
2177
2180
2178 # rest of the third row
2181 # rest of the third row
2179 elif after and not force:
2182 elif after and not force:
2180 reason = _('still exists (use -f to force removal)')
2183 reason = _('still exists (use -f to force removal)')
2181
2184
2182 # rest of the first column
2185 # rest of the first column
2183 elif abs in added:
2186 elif abs in added:
2184 if not force:
2187 if not force:
2185 reason = _('has been marked for add (use -f to force removal)')
2188 reason = _('has been marked for add (use -f to force removal)')
2186 else:
2189 else:
2187 forget.append(abs)
2190 forget.append(abs)
2188
2191
2189 # rest of the third column
2192 # rest of the third column
2190 elif abs in modified:
2193 elif abs in modified:
2191 if not force:
2194 if not force:
2192 reason = _('is modified (use -f to force removal)')
2195 reason = _('is modified (use -f to force removal)')
2193 else:
2196 else:
2194 remove.append(abs)
2197 remove.append(abs)
2195
2198
2196 # rest of the second column
2199 # rest of the second column
2197 elif not reason:
2200 elif not reason:
2198 remove.append(abs)
2201 remove.append(abs)
2199
2202
2200 if reason:
2203 if reason:
2201 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2204 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2202 elif ui.verbose or not exact:
2205 elif ui.verbose or not exact:
2203 ui.status(_('removing %s\n') % rel)
2206 ui.status(_('removing %s\n') % rel)
2204
2207
2205 repo.forget(forget)
2208 repo.forget(forget)
2206 repo.remove(remove, unlink=not after)
2209 repo.remove(remove, unlink=not after)
2207
2210
2208 def rename(ui, repo, *pats, **opts):
2211 def rename(ui, repo, *pats, **opts):
2209 """rename files; equivalent of copy + remove
2212 """rename files; equivalent of copy + remove
2210
2213
2211 Mark dest as copies of sources; mark sources for deletion. If
2214 Mark dest as copies of sources; mark sources for deletion. If
2212 dest is a directory, copies are put in that directory. If dest is
2215 dest is a directory, copies are put in that directory. If dest is
2213 a file, there can only be one source.
2216 a file, there can only be one source.
2214
2217
2215 By default, this command copies the contents of files as they
2218 By default, this command copies the contents of files as they
2216 stand in the working directory. If invoked with --after, the
2219 stand in the working directory. If invoked with --after, the
2217 operation is recorded, but no copying is performed.
2220 operation is recorded, but no copying is performed.
2218
2221
2219 This command takes effect in the next commit. To undo a rename
2222 This command takes effect in the next commit. To undo a rename
2220 before that, see hg revert.
2223 before that, see hg revert.
2221 """
2224 """
2222 wlock = repo.wlock(False)
2225 wlock = repo.wlock(False)
2223 try:
2226 try:
2224 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2227 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2225 finally:
2228 finally:
2226 del wlock
2229 del wlock
2227
2230
2228 def revert(ui, repo, *pats, **opts):
2231 def revert(ui, repo, *pats, **opts):
2229 """restore individual files or dirs to an earlier state
2232 """restore individual files or dirs to an earlier state
2230
2233
2231 (use update -r to check out earlier revisions, revert does not
2234 (use update -r to check out earlier revisions, revert does not
2232 change the working dir parents)
2235 change the working dir parents)
2233
2236
2234 With no revision specified, revert the named files or directories
2237 With no revision specified, revert the named files or directories
2235 to the contents they had in the parent of the working directory.
2238 to the contents they had in the parent of the working directory.
2236 This restores the contents of the affected files to an unmodified
2239 This restores the contents of the affected files to an unmodified
2237 state and unschedules adds, removes, copies, and renames. If the
2240 state and unschedules adds, removes, copies, and renames. If the
2238 working directory has two parents, you must explicitly specify the
2241 working directory has two parents, you must explicitly specify the
2239 revision to revert to.
2242 revision to revert to.
2240
2243
2241 Using the -r option, revert the given files or directories to their
2244 Using the -r option, revert the given files or directories to their
2242 contents as of a specific revision. This can be helpful to "roll
2245 contents as of a specific revision. This can be helpful to "roll
2243 back" some or all of an earlier change.
2246 back" some or all of an earlier change.
2244 See 'hg help dates' for a list of formats valid for -d/--date.
2247 See 'hg help dates' for a list of formats valid for -d/--date.
2245
2248
2246 Revert modifies the working directory. It does not commit any
2249 Revert modifies the working directory. It does not commit any
2247 changes, or change the parent of the working directory. If you
2250 changes, or change the parent of the working directory. If you
2248 revert to a revision other than the parent of the working
2251 revert to a revision other than the parent of the working
2249 directory, the reverted files will thus appear modified
2252 directory, the reverted files will thus appear modified
2250 afterwards.
2253 afterwards.
2251
2254
2252 If a file has been deleted, it is restored. If the executable
2255 If a file has been deleted, it is restored. If the executable
2253 mode of a file was changed, it is reset.
2256 mode of a file was changed, it is reset.
2254
2257
2255 If names are given, all files matching the names are reverted.
2258 If names are given, all files matching the names are reverted.
2256 If no arguments are given, no files are reverted.
2259 If no arguments are given, no files are reverted.
2257
2260
2258 Modified files are saved with a .orig suffix before reverting.
2261 Modified files are saved with a .orig suffix before reverting.
2259 To disable these backups, use --no-backup.
2262 To disable these backups, use --no-backup.
2260 """
2263 """
2261
2264
2262 if opts["date"]:
2265 if opts["date"]:
2263 if opts["rev"]:
2266 if opts["rev"]:
2264 raise util.Abort(_("you can't specify a revision and a date"))
2267 raise util.Abort(_("you can't specify a revision and a date"))
2265 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2268 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2266
2269
2267 if not pats and not opts['all']:
2270 if not pats and not opts['all']:
2268 raise util.Abort(_('no files or directories specified; '
2271 raise util.Abort(_('no files or directories specified; '
2269 'use --all to revert the whole repo'))
2272 'use --all to revert the whole repo'))
2270
2273
2271 parent, p2 = repo.dirstate.parents()
2274 parent, p2 = repo.dirstate.parents()
2272 if not opts['rev'] and p2 != nullid:
2275 if not opts['rev'] and p2 != nullid:
2273 raise util.Abort(_('uncommitted merge - please provide a '
2276 raise util.Abort(_('uncommitted merge - please provide a '
2274 'specific revision'))
2277 'specific revision'))
2275 ctx = repo.changectx(opts['rev'])
2278 ctx = repo.changectx(opts['rev'])
2276 node = ctx.node()
2279 node = ctx.node()
2277 mf = ctx.manifest()
2280 mf = ctx.manifest()
2278 if node == parent:
2281 if node == parent:
2279 pmf = mf
2282 pmf = mf
2280 else:
2283 else:
2281 pmf = None
2284 pmf = None
2282
2285
2283 # need all matching names in dirstate and manifest of target rev,
2286 # need all matching names in dirstate and manifest of target rev,
2284 # so have to walk both. do not print errors if files exist in one
2287 # so have to walk both. do not print errors if files exist in one
2285 # but not other.
2288 # but not other.
2286
2289
2287 names = {}
2290 names = {}
2288
2291
2289 wlock = repo.wlock()
2292 wlock = repo.wlock()
2290 try:
2293 try:
2291 # walk dirstate.
2294 # walk dirstate.
2292 files = []
2295 files = []
2293 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2296 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts,
2294 badmatch=mf.has_key):
2297 badmatch=mf.has_key):
2295 names[abs] = (rel, exact)
2298 names[abs] = (rel, exact)
2296 if src != 'b':
2299 if src != 'b':
2297 files.append(abs)
2300 files.append(abs)
2298
2301
2299 # walk target manifest.
2302 # walk target manifest.
2300
2303
2301 def badmatch(path):
2304 def badmatch(path):
2302 if path in names:
2305 if path in names:
2303 return True
2306 return True
2304 path_ = path + '/'
2307 path_ = path + '/'
2305 for f in names:
2308 for f in names:
2306 if f.startswith(path_):
2309 if f.startswith(path_):
2307 return True
2310 return True
2308 return False
2311 return False
2309
2312
2310 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2313 for src, abs, rel, exact in cmdutil.walk(repo, pats, opts, node=node,
2311 badmatch=badmatch):
2314 badmatch=badmatch):
2312 if abs in names or src == 'b':
2315 if abs in names or src == 'b':
2313 continue
2316 continue
2314 names[abs] = (rel, exact)
2317 names[abs] = (rel, exact)
2315
2318
2316 changes = repo.status(files=files, match=names.has_key)[:4]
2319 changes = repo.status(files=files, match=names.has_key)[:4]
2317 modified, added, removed, deleted = map(dict.fromkeys, changes)
2320 modified, added, removed, deleted = map(dict.fromkeys, changes)
2318
2321
2319 # if f is a rename, also revert the source
2322 # if f is a rename, also revert the source
2320 cwd = repo.getcwd()
2323 cwd = repo.getcwd()
2321 for f in added:
2324 for f in added:
2322 src = repo.dirstate.copied(f)
2325 src = repo.dirstate.copied(f)
2323 if src and src not in names and repo.dirstate[src] == 'r':
2326 if src and src not in names and repo.dirstate[src] == 'r':
2324 removed[src] = None
2327 removed[src] = None
2325 names[src] = (repo.pathto(src, cwd), True)
2328 names[src] = (repo.pathto(src, cwd), True)
2326
2329
2327 def removeforget(abs):
2330 def removeforget(abs):
2328 if repo.dirstate[abs] == 'a':
2331 if repo.dirstate[abs] == 'a':
2329 return _('forgetting %s\n')
2332 return _('forgetting %s\n')
2330 return _('removing %s\n')
2333 return _('removing %s\n')
2331
2334
2332 revert = ([], _('reverting %s\n'))
2335 revert = ([], _('reverting %s\n'))
2333 add = ([], _('adding %s\n'))
2336 add = ([], _('adding %s\n'))
2334 remove = ([], removeforget)
2337 remove = ([], removeforget)
2335 undelete = ([], _('undeleting %s\n'))
2338 undelete = ([], _('undeleting %s\n'))
2336
2339
2337 disptable = (
2340 disptable = (
2338 # dispatch table:
2341 # dispatch table:
2339 # file state
2342 # file state
2340 # action if in target manifest
2343 # action if in target manifest
2341 # action if not in target manifest
2344 # action if not in target manifest
2342 # make backup if in target manifest
2345 # make backup if in target manifest
2343 # make backup if not in target manifest
2346 # make backup if not in target manifest
2344 (modified, revert, remove, True, True),
2347 (modified, revert, remove, True, True),
2345 (added, revert, remove, True, False),
2348 (added, revert, remove, True, False),
2346 (removed, undelete, None, False, False),
2349 (removed, undelete, None, False, False),
2347 (deleted, revert, remove, False, False),
2350 (deleted, revert, remove, False, False),
2348 )
2351 )
2349
2352
2350 entries = names.items()
2353 entries = names.items()
2351 entries.sort()
2354 entries.sort()
2352
2355
2353 for abs, (rel, exact) in entries:
2356 for abs, (rel, exact) in entries:
2354 mfentry = mf.get(abs)
2357 mfentry = mf.get(abs)
2355 target = repo.wjoin(abs)
2358 target = repo.wjoin(abs)
2356 def handle(xlist, dobackup):
2359 def handle(xlist, dobackup):
2357 xlist[0].append(abs)
2360 xlist[0].append(abs)
2358 if dobackup and not opts['no_backup'] and util.lexists(target):
2361 if dobackup and not opts['no_backup'] and util.lexists(target):
2359 bakname = "%s.orig" % rel
2362 bakname = "%s.orig" % rel
2360 ui.note(_('saving current version of %s as %s\n') %
2363 ui.note(_('saving current version of %s as %s\n') %
2361 (rel, bakname))
2364 (rel, bakname))
2362 if not opts.get('dry_run'):
2365 if not opts.get('dry_run'):
2363 util.copyfile(target, bakname)
2366 util.copyfile(target, bakname)
2364 if ui.verbose or not exact:
2367 if ui.verbose or not exact:
2365 msg = xlist[1]
2368 msg = xlist[1]
2366 if not isinstance(msg, basestring):
2369 if not isinstance(msg, basestring):
2367 msg = msg(abs)
2370 msg = msg(abs)
2368 ui.status(msg % rel)
2371 ui.status(msg % rel)
2369 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2372 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2370 if abs not in table: continue
2373 if abs not in table: continue
2371 # file has changed in dirstate
2374 # file has changed in dirstate
2372 if mfentry:
2375 if mfentry:
2373 handle(hitlist, backuphit)
2376 handle(hitlist, backuphit)
2374 elif misslist is not None:
2377 elif misslist is not None:
2375 handle(misslist, backupmiss)
2378 handle(misslist, backupmiss)
2376 break
2379 break
2377 else:
2380 else:
2378 if abs not in repo.dirstate:
2381 if abs not in repo.dirstate:
2379 if mfentry:
2382 if mfentry:
2380 handle(add, True)
2383 handle(add, True)
2381 elif exact:
2384 elif exact:
2382 ui.warn(_('file not managed: %s\n') % rel)
2385 ui.warn(_('file not managed: %s\n') % rel)
2383 continue
2386 continue
2384 # file has not changed in dirstate
2387 # file has not changed in dirstate
2385 if node == parent:
2388 if node == parent:
2386 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2389 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2387 continue
2390 continue
2388 if pmf is None:
2391 if pmf is None:
2389 # only need parent manifest in this unlikely case,
2392 # only need parent manifest in this unlikely case,
2390 # so do not read by default
2393 # so do not read by default
2391 pmf = repo.changectx(parent).manifest()
2394 pmf = repo.changectx(parent).manifest()
2392 if abs in pmf:
2395 if abs in pmf:
2393 if mfentry:
2396 if mfentry:
2394 # if version of file is same in parent and target
2397 # if version of file is same in parent and target
2395 # manifests, do nothing
2398 # manifests, do nothing
2396 if (pmf[abs] != mfentry or
2399 if (pmf[abs] != mfentry or
2397 pmf.flags(abs) != mf.flags(abs)):
2400 pmf.flags(abs) != mf.flags(abs)):
2398 handle(revert, False)
2401 handle(revert, False)
2399 else:
2402 else:
2400 handle(remove, False)
2403 handle(remove, False)
2401
2404
2402 if not opts.get('dry_run'):
2405 if not opts.get('dry_run'):
2403 def checkout(f):
2406 def checkout(f):
2404 fc = ctx[f]
2407 fc = ctx[f]
2405 repo.wwrite(f, fc.data(), fc.fileflags())
2408 repo.wwrite(f, fc.data(), fc.fileflags())
2406
2409
2407 audit_path = util.path_auditor(repo.root)
2410 audit_path = util.path_auditor(repo.root)
2408 for f in remove[0]:
2411 for f in remove[0]:
2409 if repo.dirstate[f] == 'a':
2412 if repo.dirstate[f] == 'a':
2410 repo.dirstate.forget(f)
2413 repo.dirstate.forget(f)
2411 continue
2414 continue
2412 audit_path(f)
2415 audit_path(f)
2413 try:
2416 try:
2414 util.unlink(repo.wjoin(f))
2417 util.unlink(repo.wjoin(f))
2415 except OSError:
2418 except OSError:
2416 pass
2419 pass
2417 repo.dirstate.remove(f)
2420 repo.dirstate.remove(f)
2418
2421
2419 normal = None
2422 normal = None
2420 if node == parent:
2423 if node == parent:
2421 # We're reverting to our parent. If possible, we'd like status
2424 # We're reverting to our parent. If possible, we'd like status
2422 # to report the file as clean. We have to use normallookup for
2425 # to report the file as clean. We have to use normallookup for
2423 # merges to avoid losing information about merged/dirty files.
2426 # merges to avoid losing information about merged/dirty files.
2424 if p2 != nullid:
2427 if p2 != nullid:
2425 normal = repo.dirstate.normallookup
2428 normal = repo.dirstate.normallookup
2426 else:
2429 else:
2427 normal = repo.dirstate.normal
2430 normal = repo.dirstate.normal
2428 for f in revert[0]:
2431 for f in revert[0]:
2429 checkout(f)
2432 checkout(f)
2430 if normal:
2433 if normal:
2431 normal(f)
2434 normal(f)
2432
2435
2433 for f in add[0]:
2436 for f in add[0]:
2434 checkout(f)
2437 checkout(f)
2435 repo.dirstate.add(f)
2438 repo.dirstate.add(f)
2436
2439
2437 normal = repo.dirstate.normallookup
2440 normal = repo.dirstate.normallookup
2438 if node == parent and p2 == nullid:
2441 if node == parent and p2 == nullid:
2439 normal = repo.dirstate.normal
2442 normal = repo.dirstate.normal
2440 for f in undelete[0]:
2443 for f in undelete[0]:
2441 checkout(f)
2444 checkout(f)
2442 normal(f)
2445 normal(f)
2443
2446
2444 finally:
2447 finally:
2445 del wlock
2448 del wlock
2446
2449
2447 def rollback(ui, repo):
2450 def rollback(ui, repo):
2448 """roll back the last transaction
2451 """roll back the last transaction
2449
2452
2450 This command should be used with care. There is only one level of
2453 This command should be used with care. There is only one level of
2451 rollback, and there is no way to undo a rollback. It will also
2454 rollback, and there is no way to undo a rollback. It will also
2452 restore the dirstate at the time of the last transaction, losing
2455 restore the dirstate at the time of the last transaction, losing
2453 any dirstate changes since that time.
2456 any dirstate changes since that time.
2454
2457
2455 Transactions are used to encapsulate the effects of all commands
2458 Transactions are used to encapsulate the effects of all commands
2456 that create new changesets or propagate existing changesets into a
2459 that create new changesets or propagate existing changesets into a
2457 repository. For example, the following commands are transactional,
2460 repository. For example, the following commands are transactional,
2458 and their effects can be rolled back:
2461 and their effects can be rolled back:
2459
2462
2460 commit
2463 commit
2461 import
2464 import
2462 pull
2465 pull
2463 push (with this repository as destination)
2466 push (with this repository as destination)
2464 unbundle
2467 unbundle
2465
2468
2466 This command is not intended for use on public repositories. Once
2469 This command is not intended for use on public repositories. Once
2467 changes are visible for pull by other users, rolling a transaction
2470 changes are visible for pull by other users, rolling a transaction
2468 back locally is ineffective (someone else may already have pulled
2471 back locally is ineffective (someone else may already have pulled
2469 the changes). Furthermore, a race is possible with readers of the
2472 the changes). Furthermore, a race is possible with readers of the
2470 repository; for example an in-progress pull from the repository
2473 repository; for example an in-progress pull from the repository
2471 may fail if a rollback is performed.
2474 may fail if a rollback is performed.
2472 """
2475 """
2473 repo.rollback()
2476 repo.rollback()
2474
2477
2475 def root(ui, repo):
2478 def root(ui, repo):
2476 """print the root (top) of the current working dir
2479 """print the root (top) of the current working dir
2477
2480
2478 Print the root directory of the current repository.
2481 Print the root directory of the current repository.
2479 """
2482 """
2480 ui.write(repo.root + "\n")
2483 ui.write(repo.root + "\n")
2481
2484
2482 def serve(ui, repo, **opts):
2485 def serve(ui, repo, **opts):
2483 """export the repository via HTTP
2486 """export the repository via HTTP
2484
2487
2485 Start a local HTTP repository browser and pull server.
2488 Start a local HTTP repository browser and pull server.
2486
2489
2487 By default, the server logs accesses to stdout and errors to
2490 By default, the server logs accesses to stdout and errors to
2488 stderr. Use the "-A" and "-E" options to log to files.
2491 stderr. Use the "-A" and "-E" options to log to files.
2489 """
2492 """
2490
2493
2491 if opts["stdio"]:
2494 if opts["stdio"]:
2492 if repo is None:
2495 if repo is None:
2493 raise RepoError(_("There is no Mercurial repository here"
2496 raise RepoError(_("There is no Mercurial repository here"
2494 " (.hg not found)"))
2497 " (.hg not found)"))
2495 s = sshserver.sshserver(ui, repo)
2498 s = sshserver.sshserver(ui, repo)
2496 s.serve_forever()
2499 s.serve_forever()
2497
2500
2498 parentui = ui.parentui or ui
2501 parentui = ui.parentui or ui
2499 optlist = ("name templates style address port prefix ipv6"
2502 optlist = ("name templates style address port prefix ipv6"
2500 " accesslog errorlog webdir_conf certificate")
2503 " accesslog errorlog webdir_conf certificate")
2501 for o in optlist.split():
2504 for o in optlist.split():
2502 if opts[o]:
2505 if opts[o]:
2503 parentui.setconfig("web", o, str(opts[o]))
2506 parentui.setconfig("web", o, str(opts[o]))
2504 if (repo is not None) and (repo.ui != parentui):
2507 if (repo is not None) and (repo.ui != parentui):
2505 repo.ui.setconfig("web", o, str(opts[o]))
2508 repo.ui.setconfig("web", o, str(opts[o]))
2506
2509
2507 if repo is None and not ui.config("web", "webdir_conf"):
2510 if repo is None and not ui.config("web", "webdir_conf"):
2508 raise RepoError(_("There is no Mercurial repository here"
2511 raise RepoError(_("There is no Mercurial repository here"
2509 " (.hg not found)"))
2512 " (.hg not found)"))
2510
2513
2511 class service:
2514 class service:
2512 def init(self):
2515 def init(self):
2513 util.set_signal_handler()
2516 util.set_signal_handler()
2514 self.httpd = hgweb.server.create_server(parentui, repo)
2517 self.httpd = hgweb.server.create_server(parentui, repo)
2515
2518
2516 if not ui.verbose: return
2519 if not ui.verbose: return
2517
2520
2518 if self.httpd.prefix:
2521 if self.httpd.prefix:
2519 prefix = self.httpd.prefix.strip('/') + '/'
2522 prefix = self.httpd.prefix.strip('/') + '/'
2520 else:
2523 else:
2521 prefix = ''
2524 prefix = ''
2522
2525
2523 port = ':%d' % self.httpd.port
2526 port = ':%d' % self.httpd.port
2524 if port == ':80':
2527 if port == ':80':
2525 port = ''
2528 port = ''
2526
2529
2527 ui.status(_('listening at http://%s%s/%s (%s:%d)\n') %
2530 ui.status(_('listening at http://%s%s/%s (%s:%d)\n') %
2528 (self.httpd.fqaddr, port, prefix, self.httpd.addr, self.httpd.port))
2531 (self.httpd.fqaddr, port, prefix, self.httpd.addr, self.httpd.port))
2529
2532
2530 def run(self):
2533 def run(self):
2531 self.httpd.serve_forever()
2534 self.httpd.serve_forever()
2532
2535
2533 service = service()
2536 service = service()
2534
2537
2535 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2538 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2536
2539
2537 def status(ui, repo, *pats, **opts):
2540 def status(ui, repo, *pats, **opts):
2538 """show changed files in the working directory
2541 """show changed files in the working directory
2539
2542
2540 Show status of files in the repository. If names are given, only
2543 Show status of files in the repository. If names are given, only
2541 files that match are shown. Files that are clean or ignored or
2544 files that match are shown. Files that are clean or ignored or
2542 source of a copy/move operation, are not listed unless -c (clean),
2545 source of a copy/move operation, are not listed unless -c (clean),
2543 -i (ignored), -C (copies) or -A is given. Unless options described
2546 -i (ignored), -C (copies) or -A is given. Unless options described
2544 with "show only ..." are given, the options -mardu are used.
2547 with "show only ..." are given, the options -mardu are used.
2545
2548
2546 Option -q/--quiet hides untracked (unknown and ignored) files
2549 Option -q/--quiet hides untracked (unknown and ignored) files
2547 unless explicitly requested with -u/--unknown or -i/-ignored.
2550 unless explicitly requested with -u/--unknown or -i/-ignored.
2548
2551
2549 NOTE: status may appear to disagree with diff if permissions have
2552 NOTE: status may appear to disagree with diff if permissions have
2550 changed or a merge has occurred. The standard diff format does not
2553 changed or a merge has occurred. The standard diff format does not
2551 report permission changes and diff only reports changes relative
2554 report permission changes and diff only reports changes relative
2552 to one merge parent.
2555 to one merge parent.
2553
2556
2554 If one revision is given, it is used as the base revision.
2557 If one revision is given, it is used as the base revision.
2555 If two revisions are given, the difference between them is shown.
2558 If two revisions are given, the difference between them is shown.
2556
2559
2557 The codes used to show the status of files are:
2560 The codes used to show the status of files are:
2558 M = modified
2561 M = modified
2559 A = added
2562 A = added
2560 R = removed
2563 R = removed
2561 C = clean
2564 C = clean
2562 ! = deleted, but still tracked
2565 ! = deleted, but still tracked
2563 ? = not tracked
2566 ? = not tracked
2564 I = ignored
2567 I = ignored
2565 = the previous added file was copied from here
2568 = the previous added file was copied from here
2566 """
2569 """
2567
2570
2568 all = opts['all']
2571 all = opts['all']
2569 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2572 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2570
2573
2571 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2574 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2572 cwd = (pats and repo.getcwd()) or ''
2575 cwd = (pats and repo.getcwd()) or ''
2573 modified, added, removed, deleted, unknown, ignored, clean = [
2576 modified, added, removed, deleted, unknown, ignored, clean = [
2574 n for n in repo.status(node1=node1, node2=node2, files=files,
2577 n for n in repo.status(node1=node1, node2=node2, files=files,
2575 match=matchfn,
2578 match=matchfn,
2576 list_ignored=opts['ignored']
2579 list_ignored=opts['ignored']
2577 or all and not ui.quiet,
2580 or all and not ui.quiet,
2578 list_clean=opts['clean'] or all,
2581 list_clean=opts['clean'] or all,
2579 list_unknown=opts['unknown']
2582 list_unknown=opts['unknown']
2580 or not (ui.quiet or
2583 or not (ui.quiet or
2581 opts['modified'] or
2584 opts['modified'] or
2582 opts['added'] or
2585 opts['added'] or
2583 opts['removed'] or
2586 opts['removed'] or
2584 opts['deleted'] or
2587 opts['deleted'] or
2585 opts['ignored']))]
2588 opts['ignored']))]
2586
2589
2587 changetypes = (('modified', 'M', modified),
2590 changetypes = (('modified', 'M', modified),
2588 ('added', 'A', added),
2591 ('added', 'A', added),
2589 ('removed', 'R', removed),
2592 ('removed', 'R', removed),
2590 ('deleted', '!', deleted),
2593 ('deleted', '!', deleted),
2591 ('unknown', '?', unknown),
2594 ('unknown', '?', unknown),
2592 ('ignored', 'I', ignored))
2595 ('ignored', 'I', ignored))
2593
2596
2594 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2597 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2595
2598
2596 copy = {}
2599 copy = {}
2597 showcopy = {}
2600 showcopy = {}
2598 if ((all or opts.get('copies')) and not opts.get('no_status')):
2601 if ((all or opts.get('copies')) and not opts.get('no_status')):
2599 if opts.get('rev') == []:
2602 if opts.get('rev') == []:
2600 # fast path, more correct with merge parents
2603 # fast path, more correct with merge parents
2601 showcopy = copy = repo.dirstate.copies().copy()
2604 showcopy = copy = repo.dirstate.copies().copy()
2602 else:
2605 else:
2603 ctxn = repo.changectx(nullid)
2606 ctxn = repo.changectx(nullid)
2604 ctx1 = repo.changectx(node1)
2607 ctx1 = repo.changectx(node1)
2605 ctx2 = repo.changectx(node2)
2608 ctx2 = repo.changectx(node2)
2606 if node2 is None:
2609 if node2 is None:
2607 ctx2 = repo.workingctx()
2610 ctx2 = repo.workingctx()
2608 copy, diverge = copies.copies(repo, ctx1, ctx2, ctxn)
2611 copy, diverge = copies.copies(repo, ctx1, ctx2, ctxn)
2609 for k, v in copy.items():
2612 for k, v in copy.items():
2610 copy[v] = k
2613 copy[v] = k
2611
2614
2612 end = opts['print0'] and '\0' or '\n'
2615 end = opts['print0'] and '\0' or '\n'
2613
2616
2614 for opt, char, changes in ([ct for ct in explicit_changetypes
2617 for opt, char, changes in ([ct for ct in explicit_changetypes
2615 if all or opts[ct[0]]]
2618 if all or opts[ct[0]]]
2616 or changetypes):
2619 or changetypes):
2617
2620
2618 if opts['no_status']:
2621 if opts['no_status']:
2619 format = "%%s%s" % end
2622 format = "%%s%s" % end
2620 else:
2623 else:
2621 format = "%s %%s%s" % (char, end)
2624 format = "%s %%s%s" % (char, end)
2622
2625
2623 for f in changes:
2626 for f in changes:
2624 ui.write(format % repo.pathto(f, cwd))
2627 ui.write(format % repo.pathto(f, cwd))
2625 if f in copy and (f in added or f in showcopy):
2628 if f in copy and (f in added or f in showcopy):
2626 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2629 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2627
2630
2628 def tag(ui, repo, name1, *names, **opts):
2631 def tag(ui, repo, name1, *names, **opts):
2629 """add one or more tags for the current or given revision
2632 """add one or more tags for the current or given revision
2630
2633
2631 Name a particular revision using <name>.
2634 Name a particular revision using <name>.
2632
2635
2633 Tags are used to name particular revisions of the repository and are
2636 Tags are used to name particular revisions of the repository and are
2634 very useful to compare different revisions, to go back to significant
2637 very useful to compare different revisions, to go back to significant
2635 earlier versions or to mark branch points as releases, etc.
2638 earlier versions or to mark branch points as releases, etc.
2636
2639
2637 If no revision is given, the parent of the working directory is used,
2640 If no revision is given, the parent of the working directory is used,
2638 or tip if no revision is checked out.
2641 or tip if no revision is checked out.
2639
2642
2640 To facilitate version control, distribution, and merging of tags,
2643 To facilitate version control, distribution, and merging of tags,
2641 they are stored as a file named ".hgtags" which is managed
2644 they are stored as a file named ".hgtags" which is managed
2642 similarly to other project files and can be hand-edited if
2645 similarly to other project files and can be hand-edited if
2643 necessary. The file '.hg/localtags' is used for local tags (not
2646 necessary. The file '.hg/localtags' is used for local tags (not
2644 shared among repositories).
2647 shared among repositories).
2645
2648
2646 See 'hg help dates' for a list of formats valid for -d/--date.
2649 See 'hg help dates' for a list of formats valid for -d/--date.
2647 """
2650 """
2648
2651
2649 rev_ = None
2652 rev_ = None
2650 names = (name1,) + names
2653 names = (name1,) + names
2651 if len(names) != len(dict.fromkeys(names)):
2654 if len(names) != len(dict.fromkeys(names)):
2652 raise util.Abort(_('tag names must be unique'))
2655 raise util.Abort(_('tag names must be unique'))
2653 for n in names:
2656 for n in names:
2654 if n in ['tip', '.', 'null']:
2657 if n in ['tip', '.', 'null']:
2655 raise util.Abort(_('the name \'%s\' is reserved') % n)
2658 raise util.Abort(_('the name \'%s\' is reserved') % n)
2656 if opts['rev'] and opts['remove']:
2659 if opts['rev'] and opts['remove']:
2657 raise util.Abort(_("--rev and --remove are incompatible"))
2660 raise util.Abort(_("--rev and --remove are incompatible"))
2658 if opts['rev']:
2661 if opts['rev']:
2659 rev_ = opts['rev']
2662 rev_ = opts['rev']
2660 message = opts['message']
2663 message = opts['message']
2661 if opts['remove']:
2664 if opts['remove']:
2662 expectedtype = opts['local'] and 'local' or 'global'
2665 expectedtype = opts['local'] and 'local' or 'global'
2663 for n in names:
2666 for n in names:
2664 if not repo.tagtype(n):
2667 if not repo.tagtype(n):
2665 raise util.Abort(_('tag \'%s\' does not exist') % n)
2668 raise util.Abort(_('tag \'%s\' does not exist') % n)
2666 if repo.tagtype(n) != expectedtype:
2669 if repo.tagtype(n) != expectedtype:
2667 raise util.Abort(_('tag \'%s\' is not a %s tag') %
2670 raise util.Abort(_('tag \'%s\' is not a %s tag') %
2668 (n, expectedtype))
2671 (n, expectedtype))
2669 rev_ = nullid
2672 rev_ = nullid
2670 if not message:
2673 if not message:
2671 message = _('Removed tag %s') % ', '.join(names)
2674 message = _('Removed tag %s') % ', '.join(names)
2672 elif not opts['force']:
2675 elif not opts['force']:
2673 for n in names:
2676 for n in names:
2674 if n in repo.tags():
2677 if n in repo.tags():
2675 raise util.Abort(_('tag \'%s\' already exists '
2678 raise util.Abort(_('tag \'%s\' already exists '
2676 '(use -f to force)') % n)
2679 '(use -f to force)') % n)
2677 if not rev_ and repo.dirstate.parents()[1] != nullid:
2680 if not rev_ and repo.dirstate.parents()[1] != nullid:
2678 raise util.Abort(_('uncommitted merge - please provide a '
2681 raise util.Abort(_('uncommitted merge - please provide a '
2679 'specific revision'))
2682 'specific revision'))
2680 r = repo.changectx(rev_).node()
2683 r = repo.changectx(rev_).node()
2681
2684
2682 if not message:
2685 if not message:
2683 message = (_('Added tag %s for changeset %s') %
2686 message = (_('Added tag %s for changeset %s') %
2684 (', '.join(names), short(r)))
2687 (', '.join(names), short(r)))
2685
2688
2686 date = opts.get('date')
2689 date = opts.get('date')
2687 if date:
2690 if date:
2688 date = util.parsedate(date)
2691 date = util.parsedate(date)
2689
2692
2690 repo.tag(names, r, message, opts['local'], opts['user'], date)
2693 repo.tag(names, r, message, opts['local'], opts['user'], date)
2691
2694
2692 def tags(ui, repo):
2695 def tags(ui, repo):
2693 """list repository tags
2696 """list repository tags
2694
2697
2695 List the repository tags.
2698 List the repository tags.
2696
2699
2697 This lists both regular and local tags. When the -v/--verbose switch
2700 This lists both regular and local tags. When the -v/--verbose switch
2698 is used, a third column "local" is printed for local tags.
2701 is used, a third column "local" is printed for local tags.
2699 """
2702 """
2700
2703
2701 l = repo.tagslist()
2704 l = repo.tagslist()
2702 l.reverse()
2705 l.reverse()
2703 hexfunc = ui.debugflag and hex or short
2706 hexfunc = ui.debugflag and hex or short
2704 tagtype = ""
2707 tagtype = ""
2705
2708
2706 for t, n in l:
2709 for t, n in l:
2707 if ui.quiet:
2710 if ui.quiet:
2708 ui.write("%s\n" % t)
2711 ui.write("%s\n" % t)
2709 continue
2712 continue
2710
2713
2711 try:
2714 try:
2712 hn = hexfunc(n)
2715 hn = hexfunc(n)
2713 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2716 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2714 except revlog.LookupError:
2717 except revlog.LookupError:
2715 r = " ?:%s" % hn
2718 r = " ?:%s" % hn
2716 else:
2719 else:
2717 spaces = " " * (30 - util.locallen(t))
2720 spaces = " " * (30 - util.locallen(t))
2718 if ui.verbose:
2721 if ui.verbose:
2719 if repo.tagtype(t) == 'local':
2722 if repo.tagtype(t) == 'local':
2720 tagtype = " local"
2723 tagtype = " local"
2721 else:
2724 else:
2722 tagtype = ""
2725 tagtype = ""
2723 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2726 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2724
2727
2725 def tip(ui, repo, **opts):
2728 def tip(ui, repo, **opts):
2726 """show the tip revision
2729 """show the tip revision
2727
2730
2728 The tip revision (usually just called the tip) is the most
2731 The tip revision (usually just called the tip) is the most
2729 recently added changeset in the repository, the most recently
2732 recently added changeset in the repository, the most recently
2730 changed head.
2733 changed head.
2731
2734
2732 If you have just made a commit, that commit will be the tip. If
2735 If you have just made a commit, that commit will be the tip. If
2733 you have just pulled changes from another repository, the tip of
2736 you have just pulled changes from another repository, the tip of
2734 that repository becomes the current tip. The "tip" tag is special
2737 that repository becomes the current tip. The "tip" tag is special
2735 and cannot be renamed or assigned to a different changeset.
2738 and cannot be renamed or assigned to a different changeset.
2736 """
2739 """
2737 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2740 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2738
2741
2739 def unbundle(ui, repo, fname1, *fnames, **opts):
2742 def unbundle(ui, repo, fname1, *fnames, **opts):
2740 """apply one or more changegroup files
2743 """apply one or more changegroup files
2741
2744
2742 Apply one or more compressed changegroup files generated by the
2745 Apply one or more compressed changegroup files generated by the
2743 bundle command.
2746 bundle command.
2744 """
2747 """
2745 fnames = (fname1,) + fnames
2748 fnames = (fname1,) + fnames
2746
2749
2747 lock = None
2750 lock = None
2748 try:
2751 try:
2749 lock = repo.lock()
2752 lock = repo.lock()
2750 for fname in fnames:
2753 for fname in fnames:
2751 if os.path.exists(fname):
2754 if os.path.exists(fname):
2752 f = open(fname, "rb")
2755 f = open(fname, "rb")
2753 else:
2756 else:
2754 f = urllib.urlopen(fname)
2757 f = urllib.urlopen(fname)
2755 gen = changegroup.readbundle(f, fname)
2758 gen = changegroup.readbundle(f, fname)
2756 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2759 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2757 finally:
2760 finally:
2758 del lock
2761 del lock
2759
2762
2760 return postincoming(ui, repo, modheads, opts['update'], None)
2763 return postincoming(ui, repo, modheads, opts['update'], None)
2761
2764
2762 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2765 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2763 """update working directory
2766 """update working directory
2764
2767
2765 Update the working directory to the specified revision, or the
2768 Update the working directory to the specified revision, or the
2766 tip of the current branch if none is specified.
2769 tip of the current branch if none is specified.
2767
2770
2768 If the requested revision is a descendant of the working
2771 If the requested revision is a descendant of the working
2769 directory, any outstanding changes in the working directory will
2772 directory, any outstanding changes in the working directory will
2770 be merged into the result. If it is not directly descended but is
2773 be merged into the result. If it is not directly descended but is
2771 on the same named branch, update aborts with a suggestion to use
2774 on the same named branch, update aborts with a suggestion to use
2772 merge or update -C instead.
2775 merge or update -C instead.
2773
2776
2774 If the requested revision is on a different named branch and the
2777 If the requested revision is on a different named branch and the
2775 working directory is clean, update quietly switches branches.
2778 working directory is clean, update quietly switches branches.
2776
2779
2777 See 'hg help dates' for a list of formats valid for --date.
2780 See 'hg help dates' for a list of formats valid for --date.
2778 """
2781 """
2779 if rev and node:
2782 if rev and node:
2780 raise util.Abort(_("please specify just one revision"))
2783 raise util.Abort(_("please specify just one revision"))
2781
2784
2782 if not rev:
2785 if not rev:
2783 rev = node
2786 rev = node
2784
2787
2785 if date:
2788 if date:
2786 if rev:
2789 if rev:
2787 raise util.Abort(_("you can't specify a revision and a date"))
2790 raise util.Abort(_("you can't specify a revision and a date"))
2788 rev = cmdutil.finddate(ui, repo, date)
2791 rev = cmdutil.finddate(ui, repo, date)
2789
2792
2790 if clean:
2793 if clean:
2791 return hg.clean(repo, rev)
2794 return hg.clean(repo, rev)
2792 else:
2795 else:
2793 return hg.update(repo, rev)
2796 return hg.update(repo, rev)
2794
2797
2795 def verify(ui, repo):
2798 def verify(ui, repo):
2796 """verify the integrity of the repository
2799 """verify the integrity of the repository
2797
2800
2798 Verify the integrity of the current repository.
2801 Verify the integrity of the current repository.
2799
2802
2800 This will perform an extensive check of the repository's
2803 This will perform an extensive check of the repository's
2801 integrity, validating the hashes and checksums of each entry in
2804 integrity, validating the hashes and checksums of each entry in
2802 the changelog, manifest, and tracked files, as well as the
2805 the changelog, manifest, and tracked files, as well as the
2803 integrity of their crosslinks and indices.
2806 integrity of their crosslinks and indices.
2804 """
2807 """
2805 return hg.verify(repo)
2808 return hg.verify(repo)
2806
2809
2807 def version_(ui):
2810 def version_(ui):
2808 """output version and copyright information"""
2811 """output version and copyright information"""
2809 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2812 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2810 % version.get_version())
2813 % version.get_version())
2811 ui.status(_(
2814 ui.status(_(
2812 "\nCopyright (C) 2005-2008 Matt Mackall <mpm@selenic.com> and others\n"
2815 "\nCopyright (C) 2005-2008 Matt Mackall <mpm@selenic.com> and others\n"
2813 "This is free software; see the source for copying conditions. "
2816 "This is free software; see the source for copying conditions. "
2814 "There is NO\nwarranty; "
2817 "There is NO\nwarranty; "
2815 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2818 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2816 ))
2819 ))
2817
2820
2818 # Command options and aliases are listed here, alphabetically
2821 # Command options and aliases are listed here, alphabetically
2819
2822
2820 globalopts = [
2823 globalopts = [
2821 ('R', 'repository', '',
2824 ('R', 'repository', '',
2822 _('repository root directory or symbolic path name')),
2825 _('repository root directory or symbolic path name')),
2823 ('', 'cwd', '', _('change working directory')),
2826 ('', 'cwd', '', _('change working directory')),
2824 ('y', 'noninteractive', None,
2827 ('y', 'noninteractive', None,
2825 _('do not prompt, assume \'yes\' for any required answers')),
2828 _('do not prompt, assume \'yes\' for any required answers')),
2826 ('q', 'quiet', None, _('suppress output')),
2829 ('q', 'quiet', None, _('suppress output')),
2827 ('v', 'verbose', None, _('enable additional output')),
2830 ('v', 'verbose', None, _('enable additional output')),
2828 ('', 'config', [], _('set/override config option')),
2831 ('', 'config', [], _('set/override config option')),
2829 ('', 'debug', None, _('enable debugging output')),
2832 ('', 'debug', None, _('enable debugging output')),
2830 ('', 'debugger', None, _('start debugger')),
2833 ('', 'debugger', None, _('start debugger')),
2831 ('', 'encoding', util._encoding, _('set the charset encoding')),
2834 ('', 'encoding', util._encoding, _('set the charset encoding')),
2832 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2835 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2833 ('', 'lsprof', None, _('print improved command execution profile')),
2836 ('', 'lsprof', None, _('print improved command execution profile')),
2834 ('', 'traceback', None, _('print traceback on exception')),
2837 ('', 'traceback', None, _('print traceback on exception')),
2835 ('', 'time', None, _('time how long the command takes')),
2838 ('', 'time', None, _('time how long the command takes')),
2836 ('', 'profile', None, _('print command execution profile')),
2839 ('', 'profile', None, _('print command execution profile')),
2837 ('', 'version', None, _('output version information and exit')),
2840 ('', 'version', None, _('output version information and exit')),
2838 ('h', 'help', None, _('display help and exit')),
2841 ('h', 'help', None, _('display help and exit')),
2839 ]
2842 ]
2840
2843
2841 dryrunopts = [('n', 'dry-run', None,
2844 dryrunopts = [('n', 'dry-run', None,
2842 _('do not perform actions, just print output'))]
2845 _('do not perform actions, just print output'))]
2843
2846
2844 remoteopts = [
2847 remoteopts = [
2845 ('e', 'ssh', '', _('specify ssh command to use')),
2848 ('e', 'ssh', '', _('specify ssh command to use')),
2846 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2849 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2847 ]
2850 ]
2848
2851
2849 walkopts = [
2852 walkopts = [
2850 ('I', 'include', [], _('include names matching the given patterns')),
2853 ('I', 'include', [], _('include names matching the given patterns')),
2851 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2854 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2852 ]
2855 ]
2853
2856
2854 commitopts = [
2857 commitopts = [
2855 ('m', 'message', '', _('use <text> as commit message')),
2858 ('m', 'message', '', _('use <text> as commit message')),
2856 ('l', 'logfile', '', _('read commit message from <file>')),
2859 ('l', 'logfile', '', _('read commit message from <file>')),
2857 ]
2860 ]
2858
2861
2859 commitopts2 = [
2862 commitopts2 = [
2860 ('d', 'date', '', _('record datecode as commit date')),
2863 ('d', 'date', '', _('record datecode as commit date')),
2861 ('u', 'user', '', _('record user as committer')),
2864 ('u', 'user', '', _('record user as committer')),
2862 ]
2865 ]
2863
2866
2864 templateopts = [
2867 templateopts = [
2865 ('', 'style', '', _('display using template map file')),
2868 ('', 'style', '', _('display using template map file')),
2866 ('', 'template', '', _('display with template')),
2869 ('', 'template', '', _('display with template')),
2867 ]
2870 ]
2868
2871
2869 logopts = [
2872 logopts = [
2870 ('p', 'patch', None, _('show patch')),
2873 ('p', 'patch', None, _('show patch')),
2871 ('l', 'limit', '', _('limit number of changes displayed')),
2874 ('l', 'limit', '', _('limit number of changes displayed')),
2872 ('M', 'no-merges', None, _('do not show merges')),
2875 ('M', 'no-merges', None, _('do not show merges')),
2873 ] + templateopts
2876 ] + templateopts
2874
2877
2875 table = {
2878 table = {
2876 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2879 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2877 "addremove":
2880 "addremove":
2878 (addremove,
2881 (addremove,
2879 [('s', 'similarity', '',
2882 [('s', 'similarity', '',
2880 _('guess renamed files by similarity (0<=s<=100)')),
2883 _('guess renamed files by similarity (0<=s<=100)')),
2881 ] + walkopts + dryrunopts,
2884 ] + walkopts + dryrunopts,
2882 _('hg addremove [OPTION]... [FILE]...')),
2885 _('hg addremove [OPTION]... [FILE]...')),
2883 "^annotate|blame":
2886 "^annotate|blame":
2884 (annotate,
2887 (annotate,
2885 [('r', 'rev', '', _('annotate the specified revision')),
2888 [('r', 'rev', '', _('annotate the specified revision')),
2886 ('f', 'follow', None, _('follow file copies and renames')),
2889 ('f', 'follow', None, _('follow file copies and renames')),
2887 ('a', 'text', None, _('treat all files as text')),
2890 ('a', 'text', None, _('treat all files as text')),
2888 ('u', 'user', None, _('list the author (long with -v)')),
2891 ('u', 'user', None, _('list the author (long with -v)')),
2889 ('d', 'date', None, _('list the date (short with -q)')),
2892 ('d', 'date', None, _('list the date (short with -q)')),
2890 ('n', 'number', None, _('list the revision number (default)')),
2893 ('n', 'number', None, _('list the revision number (default)')),
2891 ('c', 'changeset', None, _('list the changeset')),
2894 ('c', 'changeset', None, _('list the changeset')),
2892 ('l', 'line-number', None,
2895 ('l', 'line-number', None,
2893 _('show line number at the first appearance'))
2896 _('show line number at the first appearance'))
2894 ] + walkopts,
2897 ] + walkopts,
2895 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
2898 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
2896 "archive":
2899 "archive":
2897 (archive,
2900 (archive,
2898 [('', 'no-decode', None, _('do not pass files through decoders')),
2901 [('', 'no-decode', None, _('do not pass files through decoders')),
2899 ('p', 'prefix', '', _('directory prefix for files in archive')),
2902 ('p', 'prefix', '', _('directory prefix for files in archive')),
2900 ('r', 'rev', '', _('revision to distribute')),
2903 ('r', 'rev', '', _('revision to distribute')),
2901 ('t', 'type', '', _('type of distribution to create')),
2904 ('t', 'type', '', _('type of distribution to create')),
2902 ] + walkopts,
2905 ] + walkopts,
2903 _('hg archive [OPTION]... DEST')),
2906 _('hg archive [OPTION]... DEST')),
2904 "backout":
2907 "backout":
2905 (backout,
2908 (backout,
2906 [('', 'merge', None,
2909 [('', 'merge', None,
2907 _('merge with old dirstate parent after backout')),
2910 _('merge with old dirstate parent after backout')),
2908 ('', 'parent', '', _('parent to choose when backing out merge')),
2911 ('', 'parent', '', _('parent to choose when backing out merge')),
2909 ('r', 'rev', '', _('revision to backout')),
2912 ('r', 'rev', '', _('revision to backout')),
2910 ] + walkopts + commitopts + commitopts2,
2913 ] + walkopts + commitopts + commitopts2,
2911 _('hg backout [OPTION]... [-r] REV')),
2914 _('hg backout [OPTION]... [-r] REV')),
2912 "bisect":
2915 "bisect":
2913 (bisect,
2916 (bisect,
2914 [('r', 'reset', False, _('reset bisect state')),
2917 [('r', 'reset', False, _('reset bisect state')),
2915 ('g', 'good', False, _('mark changeset good')),
2918 ('g', 'good', False, _('mark changeset good')),
2916 ('b', 'bad', False, _('mark changeset bad')),
2919 ('b', 'bad', False, _('mark changeset bad')),
2917 ('s', 'skip', False, _('skip testing changeset')),
2920 ('s', 'skip', False, _('skip testing changeset')),
2918 ('U', 'noupdate', False, _('do not update to target'))],
2921 ('U', 'noupdate', False, _('do not update to target'))],
2919 _("hg bisect [-gbsr] [REV]")),
2922 _("hg bisect [-gbsr] [REV]")),
2920 "branch":
2923 "branch":
2921 (branch,
2924 (branch,
2922 [('f', 'force', None,
2925 [('f', 'force', None,
2923 _('set branch name even if it shadows an existing branch'))],
2926 _('set branch name even if it shadows an existing branch'))],
2924 _('hg branch [-f] [NAME]')),
2927 _('hg branch [-f] [NAME]')),
2925 "branches":
2928 "branches":
2926 (branches,
2929 (branches,
2927 [('a', 'active', False,
2930 [('a', 'active', False,
2928 _('show only branches that have unmerged heads'))],
2931 _('show only branches that have unmerged heads'))],
2929 _('hg branches [-a]')),
2932 _('hg branches [-a]')),
2930 "bundle":
2933 "bundle":
2931 (bundle,
2934 (bundle,
2932 [('f', 'force', None,
2935 [('f', 'force', None,
2933 _('run even when remote repository is unrelated')),
2936 _('run even when remote repository is unrelated')),
2934 ('r', 'rev', [],
2937 ('r', 'rev', [],
2935 _('a changeset up to which you would like to bundle')),
2938 _('a changeset up to which you would like to bundle')),
2936 ('', 'base', [],
2939 ('', 'base', [],
2937 _('a base changeset to specify instead of a destination')),
2940 _('a base changeset to specify instead of a destination')),
2938 ('a', 'all', None,
2941 ('a', 'all', None,
2939 _('bundle all changesets in the repository')),
2942 _('bundle all changesets in the repository')),
2940 ] + remoteopts,
2943 ] + remoteopts,
2941 _('hg bundle [-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
2944 _('hg bundle [-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
2942 "cat":
2945 "cat":
2943 (cat,
2946 (cat,
2944 [('o', 'output', '', _('print output to file with formatted name')),
2947 [('o', 'output', '', _('print output to file with formatted name')),
2945 ('r', 'rev', '', _('print the given revision')),
2948 ('r', 'rev', '', _('print the given revision')),
2946 ('', 'decode', None, _('apply any matching decode filter')),
2949 ('', 'decode', None, _('apply any matching decode filter')),
2947 ] + walkopts,
2950 ] + walkopts,
2948 _('hg cat [OPTION]... FILE...')),
2951 _('hg cat [OPTION]... FILE...')),
2949 "^clone":
2952 "^clone":
2950 (clone,
2953 (clone,
2951 [('U', 'noupdate', None, _('do not update the new working directory')),
2954 [('U', 'noupdate', None, _('do not update the new working directory')),
2952 ('r', 'rev', [],
2955 ('r', 'rev', [],
2953 _('a changeset you would like to have after cloning')),
2956 _('a changeset you would like to have after cloning')),
2954 ('', 'pull', None, _('use pull protocol to copy metadata')),
2957 ('', 'pull', None, _('use pull protocol to copy metadata')),
2955 ('', 'uncompressed', None,
2958 ('', 'uncompressed', None,
2956 _('use uncompressed transfer (fast over LAN)')),
2959 _('use uncompressed transfer (fast over LAN)')),
2957 ] + remoteopts,
2960 ] + remoteopts,
2958 _('hg clone [OPTION]... SOURCE [DEST]')),
2961 _('hg clone [OPTION]... SOURCE [DEST]')),
2959 "^commit|ci":
2962 "^commit|ci":
2960 (commit,
2963 (commit,
2961 [('A', 'addremove', None,
2964 [('A', 'addremove', None,
2962 _('mark new/missing files as added/removed before committing')),
2965 _('mark new/missing files as added/removed before committing')),
2963 ] + walkopts + commitopts + commitopts2,
2966 ] + walkopts + commitopts + commitopts2,
2964 _('hg commit [OPTION]... [FILE]...')),
2967 _('hg commit [OPTION]... [FILE]...')),
2965 "copy|cp":
2968 "copy|cp":
2966 (copy,
2969 (copy,
2967 [('A', 'after', None, _('record a copy that has already occurred')),
2970 [('A', 'after', None, _('record a copy that has already occurred')),
2968 ('f', 'force', None,
2971 ('f', 'force', None,
2969 _('forcibly copy over an existing managed file')),
2972 _('forcibly copy over an existing managed file')),
2970 ] + walkopts + dryrunopts,
2973 ] + walkopts + dryrunopts,
2971 _('hg copy [OPTION]... [SOURCE]... DEST')),
2974 _('hg copy [OPTION]... [SOURCE]... DEST')),
2972 "debugancestor": (debugancestor, [],
2975 "debugancestor": (debugancestor, [],
2973 _('hg debugancestor [INDEX] REV1 REV2')),
2976 _('hg debugancestor [INDEX] REV1 REV2')),
2974 "debugcheckstate": (debugcheckstate, [], _('hg debugcheckstate')),
2977 "debugcheckstate": (debugcheckstate, [], _('hg debugcheckstate')),
2975 "debugcomplete":
2978 "debugcomplete":
2976 (debugcomplete,
2979 (debugcomplete,
2977 [('o', 'options', None, _('show the command options'))],
2980 [('o', 'options', None, _('show the command options'))],
2978 _('hg debugcomplete [-o] CMD')),
2981 _('hg debugcomplete [-o] CMD')),
2979 "debugdate":
2982 "debugdate":
2980 (debugdate,
2983 (debugdate,
2981 [('e', 'extended', None, _('try extended date formats'))],
2984 [('e', 'extended', None, _('try extended date formats'))],
2982 _('hg debugdate [-e] DATE [RANGE]')),
2985 _('hg debugdate [-e] DATE [RANGE]')),
2983 "debugdata": (debugdata, [], _('hg debugdata FILE REV')),
2986 "debugdata": (debugdata, [], _('hg debugdata FILE REV')),
2984 "debugfsinfo": (debugfsinfo, [], _('hg debugfsinfo [PATH]')),
2987 "debugfsinfo": (debugfsinfo, [], _('hg debugfsinfo [PATH]')),
2985 "debugindex": (debugindex, [], _('hg debugindex FILE')),
2988 "debugindex": (debugindex, [], _('hg debugindex FILE')),
2986 "debugindexdot": (debugindexdot, [], _('hg debugindexdot FILE')),
2989 "debugindexdot": (debugindexdot, [], _('hg debugindexdot FILE')),
2987 "debuginstall": (debuginstall, [], _('hg debuginstall')),
2990 "debuginstall": (debuginstall, [], _('hg debuginstall')),
2988 "debugrawcommit|rawcommit":
2991 "debugrawcommit|rawcommit":
2989 (rawcommit,
2992 (rawcommit,
2990 [('p', 'parent', [], _('parent')),
2993 [('p', 'parent', [], _('parent')),
2991 ('F', 'files', '', _('file list'))
2994 ('F', 'files', '', _('file list'))
2992 ] + commitopts + commitopts2,
2995 ] + commitopts + commitopts2,
2993 _('hg debugrawcommit [OPTION]... [FILE]...')),
2996 _('hg debugrawcommit [OPTION]... [FILE]...')),
2994 "debugrebuildstate":
2997 "debugrebuildstate":
2995 (debugrebuildstate,
2998 (debugrebuildstate,
2996 [('r', 'rev', '', _('revision to rebuild to'))],
2999 [('r', 'rev', '', _('revision to rebuild to'))],
2997 _('hg debugrebuildstate [-r REV] [REV]')),
3000 _('hg debugrebuildstate [-r REV] [REV]')),
2998 "debugrename":
3001 "debugrename":
2999 (debugrename,
3002 (debugrename,
3000 [('r', 'rev', '', _('revision to debug'))],
3003 [('r', 'rev', '', _('revision to debug'))],
3001 _('hg debugrename [-r REV] FILE')),
3004 _('hg debugrename [-r REV] FILE')),
3002 "debugsetparents":
3005 "debugsetparents":
3003 (debugsetparents,
3006 (debugsetparents,
3004 [],
3007 [],
3005 _('hg debugsetparents REV1 [REV2]')),
3008 _('hg debugsetparents REV1 [REV2]')),
3006 "debugstate":
3009 "debugstate":
3007 (debugstate,
3010 (debugstate,
3008 [('', 'nodates', None, _('do not display the saved mtime'))],
3011 [('', 'nodates', None, _('do not display the saved mtime'))],
3009 _('hg debugstate [OPTS]')),
3012 _('hg debugstate [OPTS]')),
3010 "debugwalk": (debugwalk, walkopts, _('hg debugwalk [OPTION]... [FILE]...')),
3013 "debugwalk": (debugwalk, walkopts, _('hg debugwalk [OPTION]... [FILE]...')),
3011 "^diff":
3014 "^diff":
3012 (diff,
3015 (diff,
3013 [('r', 'rev', [], _('revision')),
3016 [('r', 'rev', [], _('revision')),
3014 ('a', 'text', None, _('treat all files as text')),
3017 ('a', 'text', None, _('treat all files as text')),
3015 ('p', 'show-function', None,
3018 ('p', 'show-function', None,
3016 _('show which function each change is in')),
3019 _('show which function each change is in')),
3017 ('g', 'git', None, _('use git extended diff format')),
3020 ('g', 'git', None, _('use git extended diff format')),
3018 ('', 'nodates', None, _("don't include dates in diff headers")),
3021 ('', 'nodates', None, _("don't include dates in diff headers")),
3019 ('w', 'ignore-all-space', None,
3022 ('w', 'ignore-all-space', None,
3020 _('ignore white space when comparing lines')),
3023 _('ignore white space when comparing lines')),
3021 ('b', 'ignore-space-change', None,
3024 ('b', 'ignore-space-change', None,
3022 _('ignore changes in the amount of white space')),
3025 _('ignore changes in the amount of white space')),
3023 ('B', 'ignore-blank-lines', None,
3026 ('B', 'ignore-blank-lines', None,
3024 _('ignore changes whose lines are all blank')),
3027 _('ignore changes whose lines are all blank')),
3025 ('U', 'unified', 3,
3028 ('U', 'unified', 3,
3026 _('number of lines of context to show'))
3029 _('number of lines of context to show'))
3027 ] + walkopts,
3030 ] + walkopts,
3028 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3031 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3029 "^export":
3032 "^export":
3030 (export,
3033 (export,
3031 [('o', 'output', '', _('print output to file with formatted name')),
3034 [('o', 'output', '', _('print output to file with formatted name')),
3032 ('a', 'text', None, _('treat all files as text')),
3035 ('a', 'text', None, _('treat all files as text')),
3033 ('g', 'git', None, _('use git extended diff format')),
3036 ('g', 'git', None, _('use git extended diff format')),
3034 ('', 'nodates', None, _("don't include dates in diff headers")),
3037 ('', 'nodates', None, _("don't include dates in diff headers")),
3035 ('', 'switch-parent', None, _('diff against the second parent'))],
3038 ('', 'switch-parent', None, _('diff against the second parent'))],
3036 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
3039 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
3037 "grep":
3040 "grep":
3038 (grep,
3041 (grep,
3039 [('0', 'print0', None, _('end fields with NUL')),
3042 [('0', 'print0', None, _('end fields with NUL')),
3040 ('', 'all', None, _('print all revisions that match')),
3043 ('', 'all', None, _('print all revisions that match')),
3041 ('f', 'follow', None,
3044 ('f', 'follow', None,
3042 _('follow changeset history, or file history across copies and renames')),
3045 _('follow changeset history, or file history across copies and renames')),
3043 ('i', 'ignore-case', None, _('ignore case when matching')),
3046 ('i', 'ignore-case', None, _('ignore case when matching')),
3044 ('l', 'files-with-matches', None,
3047 ('l', 'files-with-matches', None,
3045 _('print only filenames and revs that match')),
3048 _('print only filenames and revs that match')),
3046 ('n', 'line-number', None, _('print matching line numbers')),
3049 ('n', 'line-number', None, _('print matching line numbers')),
3047 ('r', 'rev', [], _('search in given revision range')),
3050 ('r', 'rev', [], _('search in given revision range')),
3048 ('u', 'user', None, _('list the author (long with -v)')),
3051 ('u', 'user', None, _('list the author (long with -v)')),
3049 ('d', 'date', None, _('list the date (short with -q)')),
3052 ('d', 'date', None, _('list the date (short with -q)')),
3050 ] + walkopts,
3053 ] + walkopts,
3051 _('hg grep [OPTION]... PATTERN [FILE]...')),
3054 _('hg grep [OPTION]... PATTERN [FILE]...')),
3052 "heads":
3055 "heads":
3053 (heads,
3056 (heads,
3054 [('r', 'rev', '', _('show only heads which are descendants of rev')),
3057 [('r', 'rev', '', _('show only heads which are descendants of rev')),
3055 ] + templateopts,
3058 ] + templateopts,
3056 _('hg heads [-r REV] [REV]...')),
3059 _('hg heads [-r REV] [REV]...')),
3057 "help": (help_, [], _('hg help [COMMAND]')),
3060 "help": (help_, [], _('hg help [COMMAND]')),
3058 "identify|id":
3061 "identify|id":
3059 (identify,
3062 (identify,
3060 [('r', 'rev', '', _('identify the specified rev')),
3063 [('r', 'rev', '', _('identify the specified rev')),
3061 ('n', 'num', None, _('show local revision number')),
3064 ('n', 'num', None, _('show local revision number')),
3062 ('i', 'id', None, _('show global revision id')),
3065 ('i', 'id', None, _('show global revision id')),
3063 ('b', 'branch', None, _('show branch')),
3066 ('b', 'branch', None, _('show branch')),
3064 ('t', 'tags', None, _('show tags'))],
3067 ('t', 'tags', None, _('show tags'))],
3065 _('hg identify [-nibt] [-r REV] [SOURCE]')),
3068 _('hg identify [-nibt] [-r REV] [SOURCE]')),
3066 "import|patch":
3069 "import|patch":
3067 (import_,
3070 (import_,
3068 [('p', 'strip', 1,
3071 [('p', 'strip', 1,
3069 _('directory strip option for patch. This has the same\n'
3072 _('directory strip option for patch. This has the same\n'
3070 'meaning as the corresponding patch option')),
3073 'meaning as the corresponding patch option')),
3071 ('b', 'base', '', _('base path')),
3074 ('b', 'base', '', _('base path')),
3072 ('f', 'force', None,
3075 ('f', 'force', None,
3073 _('skip check for outstanding uncommitted changes')),
3076 _('skip check for outstanding uncommitted changes')),
3074 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3077 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3075 ('', 'exact', None,
3078 ('', 'exact', None,
3076 _('apply patch to the nodes from which it was generated')),
3079 _('apply patch to the nodes from which it was generated')),
3077 ('', 'import-branch', None,
3080 ('', 'import-branch', None,
3078 _('Use any branch information in patch (implied by --exact)'))] +
3081 _('Use any branch information in patch (implied by --exact)'))] +
3079 commitopts + commitopts2,
3082 commitopts + commitopts2,
3080 _('hg import [OPTION]... PATCH...')),
3083 _('hg import [OPTION]... PATCH...')),
3081 "incoming|in":
3084 "incoming|in":
3082 (incoming,
3085 (incoming,
3083 [('f', 'force', None,
3086 [('f', 'force', None,
3084 _('run even when remote repository is unrelated')),
3087 _('run even when remote repository is unrelated')),
3085 ('n', 'newest-first', None, _('show newest record first')),
3088 ('n', 'newest-first', None, _('show newest record first')),
3086 ('', 'bundle', '', _('file to store the bundles into')),
3089 ('', 'bundle', '', _('file to store the bundles into')),
3087 ('r', 'rev', [],
3090 ('r', 'rev', [],
3088 _('a specific revision up to which you would like to pull')),
3091 _('a specific revision up to which you would like to pull')),
3089 ] + logopts + remoteopts,
3092 ] + logopts + remoteopts,
3090 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
3093 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
3091 ' [--bundle FILENAME] [SOURCE]')),
3094 ' [--bundle FILENAME] [SOURCE]')),
3092 "^init":
3095 "^init":
3093 (init,
3096 (init,
3094 remoteopts,
3097 remoteopts,
3095 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
3098 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
3096 "locate":
3099 "locate":
3097 (locate,
3100 (locate,
3098 [('r', 'rev', '', _('search the repository as it stood at rev')),
3101 [('r', 'rev', '', _('search the repository as it stood at rev')),
3099 ('0', 'print0', None,
3102 ('0', 'print0', None,
3100 _('end filenames with NUL, for use with xargs')),
3103 _('end filenames with NUL, for use with xargs')),
3101 ('f', 'fullpath', None,
3104 ('f', 'fullpath', None,
3102 _('print complete paths from the filesystem root')),
3105 _('print complete paths from the filesystem root')),
3103 ] + walkopts,
3106 ] + walkopts,
3104 _('hg locate [OPTION]... [PATTERN]...')),
3107 _('hg locate [OPTION]... [PATTERN]...')),
3105 "^log|history":
3108 "^log|history":
3106 (log,
3109 (log,
3107 [('f', 'follow', None,
3110 [('f', 'follow', None,
3108 _('follow changeset history, or file history across copies and renames')),
3111 _('follow changeset history, or file history across copies and renames')),
3109 ('', 'follow-first', None,
3112 ('', 'follow-first', None,
3110 _('only follow the first parent of merge changesets')),
3113 _('only follow the first parent of merge changesets')),
3111 ('d', 'date', '', _('show revs matching date spec')),
3114 ('d', 'date', '', _('show revs matching date spec')),
3112 ('C', 'copies', None, _('show copied files')),
3115 ('C', 'copies', None, _('show copied files')),
3113 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3116 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3114 ('r', 'rev', [], _('show the specified revision or range')),
3117 ('r', 'rev', [], _('show the specified revision or range')),
3115 ('', 'removed', None, _('include revs where files were removed')),
3118 ('', 'removed', None, _('include revs where files were removed')),
3116 ('m', 'only-merges', None, _('show only merges')),
3119 ('m', 'only-merges', None, _('show only merges')),
3117 ('b', 'only-branch', [],
3120 ('b', 'only-branch', [],
3118 _('show only changesets within the given named branch')),
3121 _('show only changesets within the given named branch')),
3119 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3122 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3120 ] + logopts + walkopts,
3123 ] + logopts + walkopts,
3121 _('hg log [OPTION]... [FILE]')),
3124 _('hg log [OPTION]... [FILE]')),
3122 "manifest":
3125 "manifest":
3123 (manifest,
3126 (manifest,
3124 [('r', 'rev', '', _('revision to display'))],
3127 [('r', 'rev', '', _('revision to display'))],
3125 _('hg manifest [-r REV]')),
3128 _('hg manifest [-r REV]')),
3126 "^merge":
3129 "^merge":
3127 (merge,
3130 (merge,
3128 [('f', 'force', None, _('force a merge with outstanding changes')),
3131 [('f', 'force', None, _('force a merge with outstanding changes')),
3129 ('r', 'rev', '', _('revision to merge')),
3132 ('r', 'rev', '', _('revision to merge')),
3130 ],
3133 ],
3131 _('hg merge [-f] [[-r] REV]')),
3134 _('hg merge [-f] [[-r] REV]')),
3132 "outgoing|out":
3135 "outgoing|out":
3133 (outgoing,
3136 (outgoing,
3134 [('f', 'force', None,
3137 [('f', 'force', None,
3135 _('run even when remote repository is unrelated')),
3138 _('run even when remote repository is unrelated')),
3136 ('r', 'rev', [],
3139 ('r', 'rev', [],
3137 _('a specific revision up to which you would like to push')),
3140 _('a specific revision up to which you would like to push')),
3138 ('n', 'newest-first', None, _('show newest record first')),
3141 ('n', 'newest-first', None, _('show newest record first')),
3139 ] + logopts + remoteopts,
3142 ] + logopts + remoteopts,
3140 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3143 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3141 "^parents":
3144 "^parents":
3142 (parents,
3145 (parents,
3143 [('r', 'rev', '', _('show parents from the specified rev')),
3146 [('r', 'rev', '', _('show parents from the specified rev')),
3144 ] + templateopts,
3147 ] + templateopts,
3145 _('hg parents [-r REV] [FILE]')),
3148 _('hg parents [-r REV] [FILE]')),
3146 "paths": (paths, [], _('hg paths [NAME]')),
3149 "paths": (paths, [], _('hg paths [NAME]')),
3147 "^pull":
3150 "^pull":
3148 (pull,
3151 (pull,
3149 [('u', 'update', None,
3152 [('u', 'update', None,
3150 _('update to new tip if changesets were pulled')),
3153 _('update to new tip if changesets were pulled')),
3151 ('f', 'force', None,
3154 ('f', 'force', None,
3152 _('run even when remote repository is unrelated')),
3155 _('run even when remote repository is unrelated')),
3153 ('r', 'rev', [],
3156 ('r', 'rev', [],
3154 _('a specific revision up to which you would like to pull')),
3157 _('a specific revision up to which you would like to pull')),
3155 ] + remoteopts,
3158 ] + remoteopts,
3156 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3159 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3157 "^push":
3160 "^push":
3158 (push,
3161 (push,
3159 [('f', 'force', None, _('force push')),
3162 [('f', 'force', None, _('force push')),
3160 ('r', 'rev', [],
3163 ('r', 'rev', [],
3161 _('a specific revision up to which you would like to push')),
3164 _('a specific revision up to which you would like to push')),
3162 ] + remoteopts,
3165 ] + remoteopts,
3163 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3166 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3164 "recover": (recover, [], _('hg recover')),
3167 "recover": (recover, [], _('hg recover')),
3165 "^remove|rm":
3168 "^remove|rm":
3166 (remove,
3169 (remove,
3167 [('A', 'after', None, _('record delete for missing files')),
3170 [('A', 'after', None, _('record delete for missing files')),
3168 ('f', 'force', None,
3171 ('f', 'force', None,
3169 _('remove (and delete) file even if added or modified')),
3172 _('remove (and delete) file even if added or modified')),
3170 ] + walkopts,
3173 ] + walkopts,
3171 _('hg remove [OPTION]... FILE...')),
3174 _('hg remove [OPTION]... FILE...')),
3172 "rename|mv":
3175 "rename|mv":
3173 (rename,
3176 (rename,
3174 [('A', 'after', None, _('record a rename that has already occurred')),
3177 [('A', 'after', None, _('record a rename that has already occurred')),
3175 ('f', 'force', None,
3178 ('f', 'force', None,
3176 _('forcibly copy over an existing managed file')),
3179 _('forcibly copy over an existing managed file')),
3177 ] + walkopts + dryrunopts,
3180 ] + walkopts + dryrunopts,
3178 _('hg rename [OPTION]... SOURCE... DEST')),
3181 _('hg rename [OPTION]... SOURCE... DEST')),
3179 "revert":
3182 "revert":
3180 (revert,
3183 (revert,
3181 [('a', 'all', None, _('revert all changes when no arguments given')),
3184 [('a', 'all', None, _('revert all changes when no arguments given')),
3182 ('d', 'date', '', _('tipmost revision matching date')),
3185 ('d', 'date', '', _('tipmost revision matching date')),
3183 ('r', 'rev', '', _('revision to revert to')),
3186 ('r', 'rev', '', _('revision to revert to')),
3184 ('', 'no-backup', None, _('do not save backup copies of files')),
3187 ('', 'no-backup', None, _('do not save backup copies of files')),
3185 ] + walkopts + dryrunopts,
3188 ] + walkopts + dryrunopts,
3186 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3189 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3187 "rollback": (rollback, [], _('hg rollback')),
3190 "rollback": (rollback, [], _('hg rollback')),
3188 "root": (root, [], _('hg root')),
3191 "root": (root, [], _('hg root')),
3189 "^serve":
3192 "^serve":
3190 (serve,
3193 (serve,
3191 [('A', 'accesslog', '', _('name of access log file to write to')),
3194 [('A', 'accesslog', '', _('name of access log file to write to')),
3192 ('d', 'daemon', None, _('run server in background')),
3195 ('d', 'daemon', None, _('run server in background')),
3193 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3196 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3194 ('E', 'errorlog', '', _('name of error log file to write to')),
3197 ('E', 'errorlog', '', _('name of error log file to write to')),
3195 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3198 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3196 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3199 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3197 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3200 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3198 ('n', 'name', '',
3201 ('n', 'name', '',
3199 _('name to show in web pages (default: working dir)')),
3202 _('name to show in web pages (default: working dir)')),
3200 ('', 'webdir-conf', '', _('name of the webdir config file'
3203 ('', 'webdir-conf', '', _('name of the webdir config file'
3201 ' (serve more than one repo)')),
3204 ' (serve more than one repo)')),
3202 ('', 'pid-file', '', _('name of file to write process ID to')),
3205 ('', 'pid-file', '', _('name of file to write process ID to')),
3203 ('', 'stdio', None, _('for remote clients')),
3206 ('', 'stdio', None, _('for remote clients')),
3204 ('t', 'templates', '', _('web templates to use')),
3207 ('t', 'templates', '', _('web templates to use')),
3205 ('', 'style', '', _('template style to use')),
3208 ('', 'style', '', _('template style to use')),
3206 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3209 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3207 ('', 'certificate', '', _('SSL certificate file'))],
3210 ('', 'certificate', '', _('SSL certificate file'))],
3208 _('hg serve [OPTION]...')),
3211 _('hg serve [OPTION]...')),
3209 "showconfig|debugconfig":
3212 "showconfig|debugconfig":
3210 (showconfig,
3213 (showconfig,
3211 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3214 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3212 _('hg showconfig [-u] [NAME]...')),
3215 _('hg showconfig [-u] [NAME]...')),
3213 "^status|st":
3216 "^status|st":
3214 (status,
3217 (status,
3215 [('A', 'all', None, _('show status of all files')),
3218 [('A', 'all', None, _('show status of all files')),
3216 ('m', 'modified', None, _('show only modified files')),
3219 ('m', 'modified', None, _('show only modified files')),
3217 ('a', 'added', None, _('show only added files')),
3220 ('a', 'added', None, _('show only added files')),
3218 ('r', 'removed', None, _('show only removed files')),
3221 ('r', 'removed', None, _('show only removed files')),
3219 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3222 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3220 ('c', 'clean', None, _('show only files without changes')),
3223 ('c', 'clean', None, _('show only files without changes')),
3221 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3224 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3222 ('i', 'ignored', None, _('show only ignored files')),
3225 ('i', 'ignored', None, _('show only ignored files')),
3223 ('n', 'no-status', None, _('hide status prefix')),
3226 ('n', 'no-status', None, _('hide status prefix')),
3224 ('C', 'copies', None, _('show source of copied files')),
3227 ('C', 'copies', None, _('show source of copied files')),
3225 ('0', 'print0', None,
3228 ('0', 'print0', None,
3226 _('end filenames with NUL, for use with xargs')),
3229 _('end filenames with NUL, for use with xargs')),
3227 ('', 'rev', [], _('show difference from revision')),
3230 ('', 'rev', [], _('show difference from revision')),
3228 ] + walkopts,
3231 ] + walkopts,
3229 _('hg status [OPTION]... [FILE]...')),
3232 _('hg status [OPTION]... [FILE]...')),
3230 "tag":
3233 "tag":
3231 (tag,
3234 (tag,
3232 [('f', 'force', None, _('replace existing tag')),
3235 [('f', 'force', None, _('replace existing tag')),
3233 ('l', 'local', None, _('make the tag local')),
3236 ('l', 'local', None, _('make the tag local')),
3234 ('r', 'rev', '', _('revision to tag')),
3237 ('r', 'rev', '', _('revision to tag')),
3235 ('', 'remove', None, _('remove a tag')),
3238 ('', 'remove', None, _('remove a tag')),
3236 # -l/--local is already there, commitopts cannot be used
3239 # -l/--local is already there, commitopts cannot be used
3237 ('m', 'message', '', _('use <text> as commit message')),
3240 ('m', 'message', '', _('use <text> as commit message')),
3238 ] + commitopts2,
3241 ] + commitopts2,
3239 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3242 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3240 "tags": (tags, [], _('hg tags')),
3243 "tags": (tags, [], _('hg tags')),
3241 "tip":
3244 "tip":
3242 (tip,
3245 (tip,
3243 [('p', 'patch', None, _('show patch')),
3246 [('p', 'patch', None, _('show patch')),
3244 ] + templateopts,
3247 ] + templateopts,
3245 _('hg tip [-p]')),
3248 _('hg tip [-p]')),
3246 "unbundle":
3249 "unbundle":
3247 (unbundle,
3250 (unbundle,
3248 [('u', 'update', None,
3251 [('u', 'update', None,
3249 _('update to new tip if changesets were unbundled'))],
3252 _('update to new tip if changesets were unbundled'))],
3250 _('hg unbundle [-u] FILE...')),
3253 _('hg unbundle [-u] FILE...')),
3251 "^update|up|checkout|co":
3254 "^update|up|checkout|co":
3252 (update,
3255 (update,
3253 [('C', 'clean', None, _('overwrite locally modified files')),
3256 [('C', 'clean', None, _('overwrite locally modified files')),
3254 ('d', 'date', '', _('tipmost revision matching date')),
3257 ('d', 'date', '', _('tipmost revision matching date')),
3255 ('r', 'rev', '', _('revision'))],
3258 ('r', 'rev', '', _('revision'))],
3256 _('hg update [-C] [-d DATE] [[-r] REV]')),
3259 _('hg update [-C] [-d DATE] [[-r] REV]')),
3257 "verify": (verify, [], _('hg verify')),
3260 "verify": (verify, [], _('hg verify')),
3258 "version": (version_, [], _('hg version')),
3261 "version": (version_, [], _('hg version')),
3259 }
3262 }
3260
3263
3261 norepo = ("clone init version help debugcomplete debugdata"
3264 norepo = ("clone init version help debugcomplete debugdata"
3262 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3265 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3263 optionalrepo = ("identify paths serve showconfig debugancestor")
3266 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,2133 +1,2138 b''
1 # localrepo.py - read/write repository class for mercurial
1 # localrepo.py - read/write repository class for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import bin, hex, nullid, nullrev, short
8 from node import bin, hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import repo, changegroup
10 import repo, changegroup
11 import changelog, dirstate, filelog, manifest, context, weakref
11 import changelog, dirstate, filelog, manifest, context, weakref
12 import lock, transaction, stat, errno, ui
12 import lock, transaction, stat, errno, ui
13 import os, revlog, time, util, extensions, hook, inspect
13 import os, revlog, time, util, extensions, hook, inspect
14
14
15 class localrepository(repo.repository):
15 class localrepository(repo.repository):
16 capabilities = util.set(('lookup', 'changegroupsubset'))
16 capabilities = util.set(('lookup', 'changegroupsubset'))
17 supported = ('revlogv1', 'store')
17 supported = ('revlogv1', 'store')
18
18
19 def __init__(self, parentui, path=None, create=0):
19 def __init__(self, parentui, path=None, create=0):
20 repo.repository.__init__(self)
20 repo.repository.__init__(self)
21 self.root = os.path.realpath(path)
21 self.root = os.path.realpath(path)
22 self.path = os.path.join(self.root, ".hg")
22 self.path = os.path.join(self.root, ".hg")
23 self.origroot = path
23 self.origroot = path
24 self.opener = util.opener(self.path)
24 self.opener = util.opener(self.path)
25 self.wopener = util.opener(self.root)
25 self.wopener = util.opener(self.root)
26
26
27 if not os.path.isdir(self.path):
27 if not os.path.isdir(self.path):
28 if create:
28 if create:
29 if not os.path.exists(path):
29 if not os.path.exists(path):
30 os.mkdir(path)
30 os.mkdir(path)
31 os.mkdir(self.path)
31 os.mkdir(self.path)
32 requirements = ["revlogv1"]
32 requirements = ["revlogv1"]
33 if parentui.configbool('format', 'usestore', True):
33 if parentui.configbool('format', 'usestore', True):
34 os.mkdir(os.path.join(self.path, "store"))
34 os.mkdir(os.path.join(self.path, "store"))
35 requirements.append("store")
35 requirements.append("store")
36 # create an invalid changelog
36 # create an invalid changelog
37 self.opener("00changelog.i", "a").write(
37 self.opener("00changelog.i", "a").write(
38 '\0\0\0\2' # represents revlogv2
38 '\0\0\0\2' # represents revlogv2
39 ' dummy changelog to prevent using the old repo layout'
39 ' dummy changelog to prevent using the old repo layout'
40 )
40 )
41 reqfile = self.opener("requires", "w")
41 reqfile = self.opener("requires", "w")
42 for r in requirements:
42 for r in requirements:
43 reqfile.write("%s\n" % r)
43 reqfile.write("%s\n" % r)
44 reqfile.close()
44 reqfile.close()
45 else:
45 else:
46 raise repo.RepoError(_("repository %s not found") % path)
46 raise repo.RepoError(_("repository %s not found") % path)
47 elif create:
47 elif create:
48 raise repo.RepoError(_("repository %s already exists") % path)
48 raise repo.RepoError(_("repository %s already exists") % path)
49 else:
49 else:
50 # find requirements
50 # find requirements
51 try:
51 try:
52 requirements = self.opener("requires").read().splitlines()
52 requirements = self.opener("requires").read().splitlines()
53 except IOError, inst:
53 except IOError, inst:
54 if inst.errno != errno.ENOENT:
54 if inst.errno != errno.ENOENT:
55 raise
55 raise
56 requirements = []
56 requirements = []
57 # check them
57 # check them
58 for r in requirements:
58 for r in requirements:
59 if r not in self.supported:
59 if r not in self.supported:
60 raise repo.RepoError(_("requirement '%s' not supported") % r)
60 raise repo.RepoError(_("requirement '%s' not supported") % r)
61
61
62 # setup store
62 # setup store
63 if "store" in requirements:
63 if "store" in requirements:
64 self.encodefn = util.encodefilename
64 self.encodefn = util.encodefilename
65 self.decodefn = util.decodefilename
65 self.decodefn = util.decodefilename
66 self.spath = os.path.join(self.path, "store")
66 self.spath = os.path.join(self.path, "store")
67 else:
67 else:
68 self.encodefn = lambda x: x
68 self.encodefn = lambda x: x
69 self.decodefn = lambda x: x
69 self.decodefn = lambda x: x
70 self.spath = self.path
70 self.spath = self.path
71
71
72 try:
72 try:
73 # files in .hg/ will be created using this mode
73 # files in .hg/ will be created using this mode
74 mode = os.stat(self.spath).st_mode
74 mode = os.stat(self.spath).st_mode
75 # avoid some useless chmods
75 # avoid some useless chmods
76 if (0777 & ~util._umask) == (0777 & mode):
76 if (0777 & ~util._umask) == (0777 & mode):
77 mode = None
77 mode = None
78 except OSError:
78 except OSError:
79 mode = None
79 mode = None
80
80
81 self._createmode = mode
81 self._createmode = mode
82 self.opener.createmode = mode
82 self.opener.createmode = mode
83 sopener = util.opener(self.spath)
83 sopener = util.opener(self.spath)
84 sopener.createmode = mode
84 sopener.createmode = mode
85 self.sopener = util.encodedopener(sopener, self.encodefn)
85 self.sopener = util.encodedopener(sopener, self.encodefn)
86
86
87 self.ui = ui.ui(parentui=parentui)
87 self.ui = ui.ui(parentui=parentui)
88 try:
88 try:
89 self.ui.readconfig(self.join("hgrc"), self.root)
89 self.ui.readconfig(self.join("hgrc"), self.root)
90 extensions.loadall(self.ui)
90 extensions.loadall(self.ui)
91 except IOError:
91 except IOError:
92 pass
92 pass
93
93
94 self.tagscache = None
94 self.tagscache = None
95 self._tagstypecache = None
95 self._tagstypecache = None
96 self.branchcache = None
96 self.branchcache = None
97 self._ubranchcache = None # UTF-8 version of branchcache
97 self._ubranchcache = None # UTF-8 version of branchcache
98 self._branchcachetip = None
98 self._branchcachetip = None
99 self.nodetagscache = None
99 self.nodetagscache = None
100 self.filterpats = {}
100 self.filterpats = {}
101 self._datafilters = {}
101 self._datafilters = {}
102 self._transref = self._lockref = self._wlockref = None
102 self._transref = self._lockref = self._wlockref = None
103
103
104 def __getattr__(self, name):
104 def __getattr__(self, name):
105 if name == 'changelog':
105 if name == 'changelog':
106 self.changelog = changelog.changelog(self.sopener)
106 self.changelog = changelog.changelog(self.sopener)
107 self.sopener.defversion = self.changelog.version
107 self.sopener.defversion = self.changelog.version
108 return self.changelog
108 return self.changelog
109 if name == 'manifest':
109 if name == 'manifest':
110 self.changelog
110 self.changelog
111 self.manifest = manifest.manifest(self.sopener)
111 self.manifest = manifest.manifest(self.sopener)
112 return self.manifest
112 return self.manifest
113 if name == 'dirstate':
113 if name == 'dirstate':
114 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
114 self.dirstate = dirstate.dirstate(self.opener, self.ui, self.root)
115 return self.dirstate
115 return self.dirstate
116 else:
116 else:
117 raise AttributeError, name
117 raise AttributeError, name
118
118
119 def url(self):
119 def url(self):
120 return 'file:' + self.root
120 return 'file:' + self.root
121
121
122 def hook(self, name, throw=False, **args):
122 def hook(self, name, throw=False, **args):
123 return hook.hook(self.ui, self, name, throw, **args)
123 return hook.hook(self.ui, self, name, throw, **args)
124
124
125 tag_disallowed = ':\r\n'
125 tag_disallowed = ':\r\n'
126
126
127 def _tag(self, names, node, message, local, user, date, parent=None,
127 def _tag(self, names, node, message, local, user, date, parent=None,
128 extra={}):
128 extra={}):
129 use_dirstate = parent is None
129 use_dirstate = parent is None
130
130
131 if isinstance(names, str):
131 if isinstance(names, str):
132 allchars = names
132 allchars = names
133 names = (names,)
133 names = (names,)
134 else:
134 else:
135 allchars = ''.join(names)
135 allchars = ''.join(names)
136 for c in self.tag_disallowed:
136 for c in self.tag_disallowed:
137 if c in allchars:
137 if c in allchars:
138 raise util.Abort(_('%r cannot be used in a tag name') % c)
138 raise util.Abort(_('%r cannot be used in a tag name') % c)
139
139
140 for name in names:
140 for name in names:
141 self.hook('pretag', throw=True, node=hex(node), tag=name,
141 self.hook('pretag', throw=True, node=hex(node), tag=name,
142 local=local)
142 local=local)
143
143
144 def writetags(fp, names, munge, prevtags):
144 def writetags(fp, names, munge, prevtags):
145 fp.seek(0, 2)
145 fp.seek(0, 2)
146 if prevtags and prevtags[-1] != '\n':
146 if prevtags and prevtags[-1] != '\n':
147 fp.write('\n')
147 fp.write('\n')
148 for name in names:
148 for name in names:
149 fp.write('%s %s\n' % (hex(node), munge and munge(name) or name))
149 fp.write('%s %s\n' % (hex(node), munge and munge(name) or name))
150 fp.close()
150 fp.close()
151
151
152 prevtags = ''
152 prevtags = ''
153 if local:
153 if local:
154 try:
154 try:
155 fp = self.opener('localtags', 'r+')
155 fp = self.opener('localtags', 'r+')
156 except IOError, err:
156 except IOError, err:
157 fp = self.opener('localtags', 'a')
157 fp = self.opener('localtags', 'a')
158 else:
158 else:
159 prevtags = fp.read()
159 prevtags = fp.read()
160
160
161 # local tags are stored in the current charset
161 # local tags are stored in the current charset
162 writetags(fp, names, None, prevtags)
162 writetags(fp, names, None, prevtags)
163 for name in names:
163 for name in names:
164 self.hook('tag', node=hex(node), tag=name, local=local)
164 self.hook('tag', node=hex(node), tag=name, local=local)
165 return
165 return
166
166
167 if use_dirstate:
167 if use_dirstate:
168 try:
168 try:
169 fp = self.wfile('.hgtags', 'rb+')
169 fp = self.wfile('.hgtags', 'rb+')
170 except IOError, err:
170 except IOError, err:
171 fp = self.wfile('.hgtags', 'ab')
171 fp = self.wfile('.hgtags', 'ab')
172 else:
172 else:
173 prevtags = fp.read()
173 prevtags = fp.read()
174 else:
174 else:
175 try:
175 try:
176 prevtags = self.filectx('.hgtags', parent).data()
176 prevtags = self.filectx('.hgtags', parent).data()
177 except revlog.LookupError:
177 except revlog.LookupError:
178 pass
178 pass
179 fp = self.wfile('.hgtags', 'wb')
179 fp = self.wfile('.hgtags', 'wb')
180 if prevtags:
180 if prevtags:
181 fp.write(prevtags)
181 fp.write(prevtags)
182
182
183 # committed tags are stored in UTF-8
183 # committed tags are stored in UTF-8
184 writetags(fp, names, util.fromlocal, prevtags)
184 writetags(fp, names, util.fromlocal, prevtags)
185
185
186 if use_dirstate and '.hgtags' not in self.dirstate:
186 if use_dirstate and '.hgtags' not in self.dirstate:
187 self.add(['.hgtags'])
187 self.add(['.hgtags'])
188
188
189 tagnode = self.commit(['.hgtags'], message, user, date, p1=parent,
189 tagnode = self.commit(['.hgtags'], message, user, date, p1=parent,
190 extra=extra)
190 extra=extra)
191
191
192 for name in names:
192 for name in names:
193 self.hook('tag', node=hex(node), tag=name, local=local)
193 self.hook('tag', node=hex(node), tag=name, local=local)
194
194
195 return tagnode
195 return tagnode
196
196
197 def tag(self, names, node, message, local, user, date):
197 def tag(self, names, node, message, local, user, date):
198 '''tag a revision with one or more symbolic names.
198 '''tag a revision with one or more symbolic names.
199
199
200 names is a list of strings or, when adding a single tag, names may be a
200 names is a list of strings or, when adding a single tag, names may be a
201 string.
201 string.
202
202
203 if local is True, the tags are stored in a per-repository file.
203 if local is True, the tags are stored in a per-repository file.
204 otherwise, they are stored in the .hgtags file, and a new
204 otherwise, they are stored in the .hgtags file, and a new
205 changeset is committed with the change.
205 changeset is committed with the change.
206
206
207 keyword arguments:
207 keyword arguments:
208
208
209 local: whether to store tags in non-version-controlled file
209 local: whether to store tags in non-version-controlled file
210 (default False)
210 (default False)
211
211
212 message: commit message to use if committing
212 message: commit message to use if committing
213
213
214 user: name of user to use if committing
214 user: name of user to use if committing
215
215
216 date: date tuple to use if committing'''
216 date: date tuple to use if committing'''
217
217
218 for x in self.status()[:5]:
218 for x in self.status()[:5]:
219 if '.hgtags' in x:
219 if '.hgtags' in x:
220 raise util.Abort(_('working copy of .hgtags is changed '
220 raise util.Abort(_('working copy of .hgtags is changed '
221 '(please commit .hgtags manually)'))
221 '(please commit .hgtags manually)'))
222
222
223 self._tag(names, node, message, local, user, date)
223 self._tag(names, node, message, local, user, date)
224
224
225 def tags(self):
225 def tags(self):
226 '''return a mapping of tag to node'''
226 '''return a mapping of tag to node'''
227 if self.tagscache:
227 if self.tagscache:
228 return self.tagscache
228 return self.tagscache
229
229
230 globaltags = {}
230 globaltags = {}
231 tagtypes = {}
231 tagtypes = {}
232
232
233 def readtags(lines, fn, tagtype):
233 def readtags(lines, fn, tagtype):
234 filetags = {}
234 filetags = {}
235 count = 0
235 count = 0
236
236
237 def warn(msg):
237 def warn(msg):
238 self.ui.warn(_("%s, line %s: %s\n") % (fn, count, msg))
238 self.ui.warn(_("%s, line %s: %s\n") % (fn, count, msg))
239
239
240 for l in lines:
240 for l in lines:
241 count += 1
241 count += 1
242 if not l:
242 if not l:
243 continue
243 continue
244 s = l.split(" ", 1)
244 s = l.split(" ", 1)
245 if len(s) != 2:
245 if len(s) != 2:
246 warn(_("cannot parse entry"))
246 warn(_("cannot parse entry"))
247 continue
247 continue
248 node, key = s
248 node, key = s
249 key = util.tolocal(key.strip()) # stored in UTF-8
249 key = util.tolocal(key.strip()) # stored in UTF-8
250 try:
250 try:
251 bin_n = bin(node)
251 bin_n = bin(node)
252 except TypeError:
252 except TypeError:
253 warn(_("node '%s' is not well formed") % node)
253 warn(_("node '%s' is not well formed") % node)
254 continue
254 continue
255 if bin_n not in self.changelog.nodemap:
255 if bin_n not in self.changelog.nodemap:
256 warn(_("tag '%s' refers to unknown node") % key)
256 warn(_("tag '%s' refers to unknown node") % key)
257 continue
257 continue
258
258
259 h = []
259 h = []
260 if key in filetags:
260 if key in filetags:
261 n, h = filetags[key]
261 n, h = filetags[key]
262 h.append(n)
262 h.append(n)
263 filetags[key] = (bin_n, h)
263 filetags[key] = (bin_n, h)
264
264
265 for k, nh in filetags.items():
265 for k, nh in filetags.items():
266 if k not in globaltags:
266 if k not in globaltags:
267 globaltags[k] = nh
267 globaltags[k] = nh
268 tagtypes[k] = tagtype
268 tagtypes[k] = tagtype
269 continue
269 continue
270
270
271 # we prefer the global tag if:
271 # we prefer the global tag if:
272 # it supercedes us OR
272 # it supercedes us OR
273 # mutual supercedes and it has a higher rank
273 # mutual supercedes and it has a higher rank
274 # otherwise we win because we're tip-most
274 # otherwise we win because we're tip-most
275 an, ah = nh
275 an, ah = nh
276 bn, bh = globaltags[k]
276 bn, bh = globaltags[k]
277 if (bn != an and an in bh and
277 if (bn != an and an in bh and
278 (bn not in ah or len(bh) > len(ah))):
278 (bn not in ah or len(bh) > len(ah))):
279 an = bn
279 an = bn
280 ah.extend([n for n in bh if n not in ah])
280 ah.extend([n for n in bh if n not in ah])
281 globaltags[k] = an, ah
281 globaltags[k] = an, ah
282 tagtypes[k] = tagtype
282 tagtypes[k] = tagtype
283
283
284 # read the tags file from each head, ending with the tip
284 # read the tags file from each head, ending with the tip
285 f = None
285 f = None
286 for rev, node, fnode in self._hgtagsnodes():
286 for rev, node, fnode in self._hgtagsnodes():
287 f = (f and f.filectx(fnode) or
287 f = (f and f.filectx(fnode) or
288 self.filectx('.hgtags', fileid=fnode))
288 self.filectx('.hgtags', fileid=fnode))
289 readtags(f.data().splitlines(), f, "global")
289 readtags(f.data().splitlines(), f, "global")
290
290
291 try:
291 try:
292 data = util.fromlocal(self.opener("localtags").read())
292 data = util.fromlocal(self.opener("localtags").read())
293 # localtags are stored in the local character set
293 # localtags are stored in the local character set
294 # while the internal tag table is stored in UTF-8
294 # while the internal tag table is stored in UTF-8
295 readtags(data.splitlines(), "localtags", "local")
295 readtags(data.splitlines(), "localtags", "local")
296 except IOError:
296 except IOError:
297 pass
297 pass
298
298
299 self.tagscache = {}
299 self.tagscache = {}
300 self._tagstypecache = {}
300 self._tagstypecache = {}
301 for k,nh in globaltags.items():
301 for k,nh in globaltags.items():
302 n = nh[0]
302 n = nh[0]
303 if n != nullid:
303 if n != nullid:
304 self.tagscache[k] = n
304 self.tagscache[k] = n
305 self._tagstypecache[k] = tagtypes[k]
305 self._tagstypecache[k] = tagtypes[k]
306 self.tagscache['tip'] = self.changelog.tip()
306 self.tagscache['tip'] = self.changelog.tip()
307
307
308 return self.tagscache
308 return self.tagscache
309
309
310 def tagtype(self, tagname):
310 def tagtype(self, tagname):
311 '''
311 '''
312 return the type of the given tag. result can be:
312 return the type of the given tag. result can be:
313
313
314 'local' : a local tag
314 'local' : a local tag
315 'global' : a global tag
315 'global' : a global tag
316 None : tag does not exist
316 None : tag does not exist
317 '''
317 '''
318
318
319 self.tags()
319 self.tags()
320
320
321 return self._tagstypecache.get(tagname)
321 return self._tagstypecache.get(tagname)
322
322
323 def _hgtagsnodes(self):
323 def _hgtagsnodes(self):
324 heads = self.heads()
324 heads = self.heads()
325 heads.reverse()
325 heads.reverse()
326 last = {}
326 last = {}
327 ret = []
327 ret = []
328 for node in heads:
328 for node in heads:
329 c = self.changectx(node)
329 c = self.changectx(node)
330 rev = c.rev()
330 rev = c.rev()
331 try:
331 try:
332 fnode = c.filenode('.hgtags')
332 fnode = c.filenode('.hgtags')
333 except revlog.LookupError:
333 except revlog.LookupError:
334 continue
334 continue
335 ret.append((rev, node, fnode))
335 ret.append((rev, node, fnode))
336 if fnode in last:
336 if fnode in last:
337 ret[last[fnode]] = None
337 ret[last[fnode]] = None
338 last[fnode] = len(ret) - 1
338 last[fnode] = len(ret) - 1
339 return [item for item in ret if item]
339 return [item for item in ret if item]
340
340
341 def tagslist(self):
341 def tagslist(self):
342 '''return a list of tags ordered by revision'''
342 '''return a list of tags ordered by revision'''
343 l = []
343 l = []
344 for t, n in self.tags().items():
344 for t, n in self.tags().items():
345 try:
345 try:
346 r = self.changelog.rev(n)
346 r = self.changelog.rev(n)
347 except:
347 except:
348 r = -2 # sort to the beginning of the list if unknown
348 r = -2 # sort to the beginning of the list if unknown
349 l.append((r, t, n))
349 l.append((r, t, n))
350 l.sort()
350 l.sort()
351 return [(t, n) for r, t, n in l]
351 return [(t, n) for r, t, n in l]
352
352
353 def nodetags(self, node):
353 def nodetags(self, node):
354 '''return the tags associated with a node'''
354 '''return the tags associated with a node'''
355 if not self.nodetagscache:
355 if not self.nodetagscache:
356 self.nodetagscache = {}
356 self.nodetagscache = {}
357 for t, n in self.tags().items():
357 for t, n in self.tags().items():
358 self.nodetagscache.setdefault(n, []).append(t)
358 self.nodetagscache.setdefault(n, []).append(t)
359 return self.nodetagscache.get(node, [])
359 return self.nodetagscache.get(node, [])
360
360
361 def _branchtags(self, partial, lrev):
361 def _branchtags(self, partial, lrev):
362 tiprev = self.changelog.count() - 1
362 tiprev = self.changelog.count() - 1
363 if lrev != tiprev:
363 if lrev != tiprev:
364 self._updatebranchcache(partial, lrev+1, tiprev+1)
364 self._updatebranchcache(partial, lrev+1, tiprev+1)
365 self._writebranchcache(partial, self.changelog.tip(), tiprev)
365 self._writebranchcache(partial, self.changelog.tip(), tiprev)
366
366
367 return partial
367 return partial
368
368
369 def branchtags(self):
369 def branchtags(self):
370 tip = self.changelog.tip()
370 tip = self.changelog.tip()
371 if self.branchcache is not None and self._branchcachetip == tip:
371 if self.branchcache is not None and self._branchcachetip == tip:
372 return self.branchcache
372 return self.branchcache
373
373
374 oldtip = self._branchcachetip
374 oldtip = self._branchcachetip
375 self._branchcachetip = tip
375 self._branchcachetip = tip
376 if self.branchcache is None:
376 if self.branchcache is None:
377 self.branchcache = {} # avoid recursion in changectx
377 self.branchcache = {} # avoid recursion in changectx
378 else:
378 else:
379 self.branchcache.clear() # keep using the same dict
379 self.branchcache.clear() # keep using the same dict
380 if oldtip is None or oldtip not in self.changelog.nodemap:
380 if oldtip is None or oldtip not in self.changelog.nodemap:
381 partial, last, lrev = self._readbranchcache()
381 partial, last, lrev = self._readbranchcache()
382 else:
382 else:
383 lrev = self.changelog.rev(oldtip)
383 lrev = self.changelog.rev(oldtip)
384 partial = self._ubranchcache
384 partial = self._ubranchcache
385
385
386 self._branchtags(partial, lrev)
386 self._branchtags(partial, lrev)
387
387
388 # the branch cache is stored on disk as UTF-8, but in the local
388 # the branch cache is stored on disk as UTF-8, but in the local
389 # charset internally
389 # charset internally
390 for k, v in partial.items():
390 for k, v in partial.items():
391 self.branchcache[util.tolocal(k)] = v
391 self.branchcache[util.tolocal(k)] = v
392 self._ubranchcache = partial
392 self._ubranchcache = partial
393 return self.branchcache
393 return self.branchcache
394
394
395 def _readbranchcache(self):
395 def _readbranchcache(self):
396 partial = {}
396 partial = {}
397 try:
397 try:
398 f = self.opener("branch.cache")
398 f = self.opener("branch.cache")
399 lines = f.read().split('\n')
399 lines = f.read().split('\n')
400 f.close()
400 f.close()
401 except (IOError, OSError):
401 except (IOError, OSError):
402 return {}, nullid, nullrev
402 return {}, nullid, nullrev
403
403
404 try:
404 try:
405 last, lrev = lines.pop(0).split(" ", 1)
405 last, lrev = lines.pop(0).split(" ", 1)
406 last, lrev = bin(last), int(lrev)
406 last, lrev = bin(last), int(lrev)
407 if not (lrev < self.changelog.count() and
407 if not (lrev < self.changelog.count() and
408 self.changelog.node(lrev) == last): # sanity check
408 self.changelog.node(lrev) == last): # sanity check
409 # invalidate the cache
409 # invalidate the cache
410 raise ValueError('invalidating branch cache (tip differs)')
410 raise ValueError('invalidating branch cache (tip differs)')
411 for l in lines:
411 for l in lines:
412 if not l: continue
412 if not l: continue
413 node, label = l.split(" ", 1)
413 node, label = l.split(" ", 1)
414 partial[label.strip()] = bin(node)
414 partial[label.strip()] = bin(node)
415 except (KeyboardInterrupt, util.SignalInterrupt):
415 except (KeyboardInterrupt, util.SignalInterrupt):
416 raise
416 raise
417 except Exception, inst:
417 except Exception, inst:
418 if self.ui.debugflag:
418 if self.ui.debugflag:
419 self.ui.warn(str(inst), '\n')
419 self.ui.warn(str(inst), '\n')
420 partial, last, lrev = {}, nullid, nullrev
420 partial, last, lrev = {}, nullid, nullrev
421 return partial, last, lrev
421 return partial, last, lrev
422
422
423 def _writebranchcache(self, branches, tip, tiprev):
423 def _writebranchcache(self, branches, tip, tiprev):
424 try:
424 try:
425 f = self.opener("branch.cache", "w", atomictemp=True)
425 f = self.opener("branch.cache", "w", atomictemp=True)
426 f.write("%s %s\n" % (hex(tip), tiprev))
426 f.write("%s %s\n" % (hex(tip), tiprev))
427 for label, node in branches.iteritems():
427 for label, node in branches.iteritems():
428 f.write("%s %s\n" % (hex(node), label))
428 f.write("%s %s\n" % (hex(node), label))
429 f.rename()
429 f.rename()
430 except (IOError, OSError):
430 except (IOError, OSError):
431 pass
431 pass
432
432
433 def _updatebranchcache(self, partial, start, end):
433 def _updatebranchcache(self, partial, start, end):
434 for r in xrange(start, end):
434 for r in xrange(start, end):
435 c = self.changectx(r)
435 c = self.changectx(r)
436 b = c.branch()
436 b = c.branch()
437 partial[b] = c.node()
437 partial[b] = c.node()
438
438
439 def lookup(self, key):
439 def lookup(self, key):
440 if key == '.':
440 if key == '.':
441 key, second = self.dirstate.parents()
441 key, second = self.dirstate.parents()
442 if key == nullid:
442 if key == nullid:
443 raise repo.RepoError(_("no revision checked out"))
443 raise repo.RepoError(_("no revision checked out"))
444 if second != nullid:
444 if second != nullid:
445 self.ui.warn(_("warning: working directory has two parents, "
445 self.ui.warn(_("warning: working directory has two parents, "
446 "tag '.' uses the first\n"))
446 "tag '.' uses the first\n"))
447 elif key == 'null':
447 elif key == 'null':
448 return nullid
448 return nullid
449 n = self.changelog._match(key)
449 n = self.changelog._match(key)
450 if n:
450 if n:
451 return n
451 return n
452 if key in self.tags():
452 if key in self.tags():
453 return self.tags()[key]
453 return self.tags()[key]
454 if key in self.branchtags():
454 if key in self.branchtags():
455 return self.branchtags()[key]
455 return self.branchtags()[key]
456 n = self.changelog._partialmatch(key)
456 n = self.changelog._partialmatch(key)
457 if n:
457 if n:
458 return n
458 return n
459 try:
459 try:
460 if len(key) == 20:
460 if len(key) == 20:
461 key = hex(key)
461 key = hex(key)
462 except:
462 except:
463 pass
463 pass
464 raise repo.RepoError(_("unknown revision '%s'") % key)
464 raise repo.RepoError(_("unknown revision '%s'") % key)
465
465
466 def local(self):
466 def local(self):
467 return True
467 return True
468
468
469 def join(self, f):
469 def join(self, f):
470 return os.path.join(self.path, f)
470 return os.path.join(self.path, f)
471
471
472 def sjoin(self, f):
472 def sjoin(self, f):
473 f = self.encodefn(f)
473 f = self.encodefn(f)
474 return os.path.join(self.spath, f)
474 return os.path.join(self.spath, f)
475
475
476 def wjoin(self, f):
476 def wjoin(self, f):
477 return os.path.join(self.root, f)
477 return os.path.join(self.root, f)
478
478
479 def file(self, f):
479 def file(self, f):
480 if f[0] == '/':
480 if f[0] == '/':
481 f = f[1:]
481 f = f[1:]
482 return filelog.filelog(self.sopener, f)
482 return filelog.filelog(self.sopener, f)
483
483
484 def changectx(self, changeid=None):
484 def changectx(self, changeid=None):
485 return context.changectx(self, changeid)
485 return context.changectx(self, changeid)
486
486
487 def workingctx(self):
487 def workingctx(self):
488 return context.workingctx(self)
488 return context.workingctx(self)
489
489
490 def parents(self, changeid=None):
490 def parents(self, changeid=None):
491 '''
491 '''
492 get list of changectxs for parents of changeid or working directory
492 get list of changectxs for parents of changeid or working directory
493 '''
493 '''
494 if changeid is None:
494 if changeid is None:
495 pl = self.dirstate.parents()
495 pl = self.dirstate.parents()
496 else:
496 else:
497 n = self.changelog.lookup(changeid)
497 n = self.changelog.lookup(changeid)
498 pl = self.changelog.parents(n)
498 pl = self.changelog.parents(n)
499 if pl[1] == nullid:
499 if pl[1] == nullid:
500 return [self.changectx(pl[0])]
500 return [self.changectx(pl[0])]
501 return [self.changectx(pl[0]), self.changectx(pl[1])]
501 return [self.changectx(pl[0]), self.changectx(pl[1])]
502
502
503 def filectx(self, path, changeid=None, fileid=None):
503 def filectx(self, path, changeid=None, fileid=None):
504 """changeid can be a changeset revision, node, or tag.
504 """changeid can be a changeset revision, node, or tag.
505 fileid can be a file revision or node."""
505 fileid can be a file revision or node."""
506 return context.filectx(self, path, changeid, fileid)
506 return context.filectx(self, path, changeid, fileid)
507
507
508 def getcwd(self):
508 def getcwd(self):
509 return self.dirstate.getcwd()
509 return self.dirstate.getcwd()
510
510
511 def pathto(self, f, cwd=None):
511 def pathto(self, f, cwd=None):
512 return self.dirstate.pathto(f, cwd)
512 return self.dirstate.pathto(f, cwd)
513
513
514 def wfile(self, f, mode='r'):
514 def wfile(self, f, mode='r'):
515 return self.wopener(f, mode)
515 return self.wopener(f, mode)
516
516
517 def _link(self, f):
517 def _link(self, f):
518 return os.path.islink(self.wjoin(f))
518 return os.path.islink(self.wjoin(f))
519
519
520 def _filter(self, filter, filename, data):
520 def _filter(self, filter, filename, data):
521 if filter not in self.filterpats:
521 if filter not in self.filterpats:
522 l = []
522 l = []
523 for pat, cmd in self.ui.configitems(filter):
523 for pat, cmd in self.ui.configitems(filter):
524 mf = util.matcher(self.root, "", [pat], [], [])[1]
524 mf = util.matcher(self.root, "", [pat], [], [])[1]
525 fn = None
525 fn = None
526 params = cmd
526 params = cmd
527 for name, filterfn in self._datafilters.iteritems():
527 for name, filterfn in self._datafilters.iteritems():
528 if cmd.startswith(name):
528 if cmd.startswith(name):
529 fn = filterfn
529 fn = filterfn
530 params = cmd[len(name):].lstrip()
530 params = cmd[len(name):].lstrip()
531 break
531 break
532 if not fn:
532 if not fn:
533 fn = lambda s, c, **kwargs: util.filter(s, c)
533 fn = lambda s, c, **kwargs: util.filter(s, c)
534 # Wrap old filters not supporting keyword arguments
534 # Wrap old filters not supporting keyword arguments
535 if not inspect.getargspec(fn)[2]:
535 if not inspect.getargspec(fn)[2]:
536 oldfn = fn
536 oldfn = fn
537 fn = lambda s, c, **kwargs: oldfn(s, c)
537 fn = lambda s, c, **kwargs: oldfn(s, c)
538 l.append((mf, fn, params))
538 l.append((mf, fn, params))
539 self.filterpats[filter] = l
539 self.filterpats[filter] = l
540
540
541 for mf, fn, cmd in self.filterpats[filter]:
541 for mf, fn, cmd in self.filterpats[filter]:
542 if mf(filename):
542 if mf(filename):
543 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
543 self.ui.debug(_("filtering %s through %s\n") % (filename, cmd))
544 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
544 data = fn(data, cmd, ui=self.ui, repo=self, filename=filename)
545 break
545 break
546
546
547 return data
547 return data
548
548
549 def adddatafilter(self, name, filter):
549 def adddatafilter(self, name, filter):
550 self._datafilters[name] = filter
550 self._datafilters[name] = filter
551
551
552 def wread(self, filename):
552 def wread(self, filename):
553 if self._link(filename):
553 if self._link(filename):
554 data = os.readlink(self.wjoin(filename))
554 data = os.readlink(self.wjoin(filename))
555 else:
555 else:
556 data = self.wopener(filename, 'r').read()
556 data = self.wopener(filename, 'r').read()
557 return self._filter("encode", filename, data)
557 return self._filter("encode", filename, data)
558
558
559 def wwrite(self, filename, data, flags):
559 def wwrite(self, filename, data, flags):
560 data = self._filter("decode", filename, data)
560 data = self._filter("decode", filename, data)
561 try:
561 try:
562 os.unlink(self.wjoin(filename))
562 os.unlink(self.wjoin(filename))
563 except OSError:
563 except OSError:
564 pass
564 pass
565 self.wopener(filename, 'w').write(data)
565 self.wopener(filename, 'w').write(data)
566 util.set_flags(self.wjoin(filename), flags)
566 util.set_flags(self.wjoin(filename), flags)
567
567
568 def wwritedata(self, filename, data):
568 def wwritedata(self, filename, data):
569 return self._filter("decode", filename, data)
569 return self._filter("decode", filename, data)
570
570
571 def transaction(self):
571 def transaction(self):
572 if self._transref and self._transref():
572 if self._transref and self._transref():
573 return self._transref().nest()
573 return self._transref().nest()
574
574
575 # abort here if the journal already exists
575 # abort here if the journal already exists
576 if os.path.exists(self.sjoin("journal")):
576 if os.path.exists(self.sjoin("journal")):
577 raise repo.RepoError(_("journal already exists - run hg recover"))
577 raise repo.RepoError(_("journal already exists - run hg recover"))
578
578
579 # save dirstate for rollback
579 # save dirstate for rollback
580 try:
580 try:
581 ds = self.opener("dirstate").read()
581 ds = self.opener("dirstate").read()
582 except IOError:
582 except IOError:
583 ds = ""
583 ds = ""
584 self.opener("journal.dirstate", "w").write(ds)
584 self.opener("journal.dirstate", "w").write(ds)
585 self.opener("journal.branch", "w").write(self.dirstate.branch())
585 self.opener("journal.branch", "w").write(self.dirstate.branch())
586
586
587 renames = [(self.sjoin("journal"), self.sjoin("undo")),
587 renames = [(self.sjoin("journal"), self.sjoin("undo")),
588 (self.join("journal.dirstate"), self.join("undo.dirstate")),
588 (self.join("journal.dirstate"), self.join("undo.dirstate")),
589 (self.join("journal.branch"), self.join("undo.branch"))]
589 (self.join("journal.branch"), self.join("undo.branch"))]
590 tr = transaction.transaction(self.ui.warn, self.sopener,
590 tr = transaction.transaction(self.ui.warn, self.sopener,
591 self.sjoin("journal"),
591 self.sjoin("journal"),
592 aftertrans(renames),
592 aftertrans(renames),
593 self._createmode)
593 self._createmode)
594 self._transref = weakref.ref(tr)
594 self._transref = weakref.ref(tr)
595 return tr
595 return tr
596
596
597 def recover(self):
597 def recover(self):
598 l = self.lock()
598 l = self.lock()
599 try:
599 try:
600 if os.path.exists(self.sjoin("journal")):
600 if os.path.exists(self.sjoin("journal")):
601 self.ui.status(_("rolling back interrupted transaction\n"))
601 self.ui.status(_("rolling back interrupted transaction\n"))
602 transaction.rollback(self.sopener, self.sjoin("journal"))
602 transaction.rollback(self.sopener, self.sjoin("journal"))
603 self.invalidate()
603 self.invalidate()
604 return True
604 return True
605 else:
605 else:
606 self.ui.warn(_("no interrupted transaction available\n"))
606 self.ui.warn(_("no interrupted transaction available\n"))
607 return False
607 return False
608 finally:
608 finally:
609 del l
609 del l
610
610
611 def rollback(self):
611 def rollback(self):
612 wlock = lock = None
612 wlock = lock = None
613 try:
613 try:
614 wlock = self.wlock()
614 wlock = self.wlock()
615 lock = self.lock()
615 lock = self.lock()
616 if os.path.exists(self.sjoin("undo")):
616 if os.path.exists(self.sjoin("undo")):
617 self.ui.status(_("rolling back last transaction\n"))
617 self.ui.status(_("rolling back last transaction\n"))
618 transaction.rollback(self.sopener, self.sjoin("undo"))
618 transaction.rollback(self.sopener, self.sjoin("undo"))
619 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
619 util.rename(self.join("undo.dirstate"), self.join("dirstate"))
620 try:
620 try:
621 branch = self.opener("undo.branch").read()
621 branch = self.opener("undo.branch").read()
622 self.dirstate.setbranch(branch)
622 self.dirstate.setbranch(branch)
623 except IOError:
623 except IOError:
624 self.ui.warn(_("Named branch could not be reset, "
624 self.ui.warn(_("Named branch could not be reset, "
625 "current branch still is: %s\n")
625 "current branch still is: %s\n")
626 % util.tolocal(self.dirstate.branch()))
626 % util.tolocal(self.dirstate.branch()))
627 self.invalidate()
627 self.invalidate()
628 self.dirstate.invalidate()
628 self.dirstate.invalidate()
629 else:
629 else:
630 self.ui.warn(_("no rollback information available\n"))
630 self.ui.warn(_("no rollback information available\n"))
631 finally:
631 finally:
632 del lock, wlock
632 del lock, wlock
633
633
634 def invalidate(self):
634 def invalidate(self):
635 for a in "changelog manifest".split():
635 for a in "changelog manifest".split():
636 if a in self.__dict__:
636 if a in self.__dict__:
637 delattr(self, a)
637 delattr(self, a)
638 self.tagscache = None
638 self.tagscache = None
639 self._tagstypecache = None
639 self._tagstypecache = None
640 self.nodetagscache = None
640 self.nodetagscache = None
641 self.branchcache = None
641 self.branchcache = None
642 self._ubranchcache = None
642 self._ubranchcache = None
643 self._branchcachetip = None
643 self._branchcachetip = None
644
644
645 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
645 def _lock(self, lockname, wait, releasefn, acquirefn, desc):
646 try:
646 try:
647 l = lock.lock(lockname, 0, releasefn, desc=desc)
647 l = lock.lock(lockname, 0, releasefn, desc=desc)
648 except lock.LockHeld, inst:
648 except lock.LockHeld, inst:
649 if not wait:
649 if not wait:
650 raise
650 raise
651 self.ui.warn(_("waiting for lock on %s held by %r\n") %
651 self.ui.warn(_("waiting for lock on %s held by %r\n") %
652 (desc, inst.locker))
652 (desc, inst.locker))
653 # default to 600 seconds timeout
653 # default to 600 seconds timeout
654 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
654 l = lock.lock(lockname, int(self.ui.config("ui", "timeout", "600")),
655 releasefn, desc=desc)
655 releasefn, desc=desc)
656 if acquirefn:
656 if acquirefn:
657 acquirefn()
657 acquirefn()
658 return l
658 return l
659
659
660 def lock(self, wait=True):
660 def lock(self, wait=True):
661 if self._lockref and self._lockref():
661 if self._lockref and self._lockref():
662 return self._lockref()
662 return self._lockref()
663
663
664 l = self._lock(self.sjoin("lock"), wait, None, self.invalidate,
664 l = self._lock(self.sjoin("lock"), wait, None, self.invalidate,
665 _('repository %s') % self.origroot)
665 _('repository %s') % self.origroot)
666 self._lockref = weakref.ref(l)
666 self._lockref = weakref.ref(l)
667 return l
667 return l
668
668
669 def wlock(self, wait=True):
669 def wlock(self, wait=True):
670 if self._wlockref and self._wlockref():
670 if self._wlockref and self._wlockref():
671 return self._wlockref()
671 return self._wlockref()
672
672
673 l = self._lock(self.join("wlock"), wait, self.dirstate.write,
673 l = self._lock(self.join("wlock"), wait, self.dirstate.write,
674 self.dirstate.invalidate, _('working directory of %s') %
674 self.dirstate.invalidate, _('working directory of %s') %
675 self.origroot)
675 self.origroot)
676 self._wlockref = weakref.ref(l)
676 self._wlockref = weakref.ref(l)
677 return l
677 return l
678
678
679 def filecommit(self, fn, manifest1, manifest2, linkrev, tr, changelist):
679 def filecommit(self, fn, manifest1, manifest2, linkrev, tr, changelist):
680 """
680 """
681 commit an individual file as part of a larger transaction
681 commit an individual file as part of a larger transaction
682 """
682 """
683
683
684 t = self.wread(fn)
684 t = self.wread(fn)
685 fl = self.file(fn)
685 fl = self.file(fn)
686 fp1 = manifest1.get(fn, nullid)
686 fp1 = manifest1.get(fn, nullid)
687 fp2 = manifest2.get(fn, nullid)
687 fp2 = manifest2.get(fn, nullid)
688
688
689 meta = {}
689 meta = {}
690 cp = self.dirstate.copied(fn)
690 cp = self.dirstate.copied(fn)
691 if cp:
691 if cp:
692 # Mark the new revision of this file as a copy of another
692 # Mark the new revision of this file as a copy of another
693 # file. This copy data will effectively act as a parent
693 # file. This copy data will effectively act as a parent
694 # of this new revision. If this is a merge, the first
694 # of this new revision. If this is a merge, the first
695 # parent will be the nullid (meaning "look up the copy data")
695 # parent will be the nullid (meaning "look up the copy data")
696 # and the second one will be the other parent. For example:
696 # and the second one will be the other parent. For example:
697 #
697 #
698 # 0 --- 1 --- 3 rev1 changes file foo
698 # 0 --- 1 --- 3 rev1 changes file foo
699 # \ / rev2 renames foo to bar and changes it
699 # \ / rev2 renames foo to bar and changes it
700 # \- 2 -/ rev3 should have bar with all changes and
700 # \- 2 -/ rev3 should have bar with all changes and
701 # should record that bar descends from
701 # should record that bar descends from
702 # bar in rev2 and foo in rev1
702 # bar in rev2 and foo in rev1
703 #
703 #
704 # this allows this merge to succeed:
704 # this allows this merge to succeed:
705 #
705 #
706 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
706 # 0 --- 1 --- 3 rev4 reverts the content change from rev2
707 # \ / merging rev3 and rev4 should use bar@rev2
707 # \ / merging rev3 and rev4 should use bar@rev2
708 # \- 2 --- 4 as the merge base
708 # \- 2 --- 4 as the merge base
709 #
709 #
710 meta["copy"] = cp
710 meta["copy"] = cp
711 if not manifest2: # not a branch merge
711 if not manifest2: # not a branch merge
712 meta["copyrev"] = hex(manifest1.get(cp, nullid))
712 meta["copyrev"] = hex(manifest1.get(cp, nullid))
713 fp2 = nullid
713 fp2 = nullid
714 elif fp2 != nullid: # copied on remote side
714 elif fp2 != nullid: # copied on remote side
715 meta["copyrev"] = hex(manifest1.get(cp, nullid))
715 meta["copyrev"] = hex(manifest1.get(cp, nullid))
716 elif fp1 != nullid: # copied on local side, reversed
716 elif fp1 != nullid: # copied on local side, reversed
717 meta["copyrev"] = hex(manifest2.get(cp))
717 meta["copyrev"] = hex(manifest2.get(cp))
718 fp2 = fp1
718 fp2 = fp1
719 elif cp in manifest2: # directory rename on local side
719 elif cp in manifest2: # directory rename on local side
720 meta["copyrev"] = hex(manifest2[cp])
720 meta["copyrev"] = hex(manifest2[cp])
721 else: # directory rename on remote side
721 else: # directory rename on remote side
722 meta["copyrev"] = hex(manifest1.get(cp, nullid))
722 meta["copyrev"] = hex(manifest1.get(cp, nullid))
723 self.ui.debug(_(" %s: copy %s:%s\n") %
723 self.ui.debug(_(" %s: copy %s:%s\n") %
724 (fn, cp, meta["copyrev"]))
724 (fn, cp, meta["copyrev"]))
725 fp1 = nullid
725 fp1 = nullid
726 elif fp2 != nullid:
726 elif fp2 != nullid:
727 # is one parent an ancestor of the other?
727 # is one parent an ancestor of the other?
728 fpa = fl.ancestor(fp1, fp2)
728 fpa = fl.ancestor(fp1, fp2)
729 if fpa == fp1:
729 if fpa == fp1:
730 fp1, fp2 = fp2, nullid
730 fp1, fp2 = fp2, nullid
731 elif fpa == fp2:
731 elif fpa == fp2:
732 fp2 = nullid
732 fp2 = nullid
733
733
734 # is the file unmodified from the parent? report existing entry
734 # is the file unmodified from the parent? report existing entry
735 if fp2 == nullid and not fl.cmp(fp1, t) and not meta:
735 if fp2 == nullid and not fl.cmp(fp1, t) and not meta:
736 return fp1
736 return fp1
737
737
738 changelist.append(fn)
738 changelist.append(fn)
739 return fl.add(t, meta, tr, linkrev, fp1, fp2)
739 return fl.add(t, meta, tr, linkrev, fp1, fp2)
740
740
741 def rawcommit(self, files, text, user, date, p1=None, p2=None, extra={}):
741 def rawcommit(self, files, text, user, date, p1=None, p2=None, extra={}):
742 if p1 is None:
742 if p1 is None:
743 p1, p2 = self.dirstate.parents()
743 p1, p2 = self.dirstate.parents()
744 return self.commit(files=files, text=text, user=user, date=date,
744 return self.commit(files=files, text=text, user=user, date=date,
745 p1=p1, p2=p2, extra=extra, empty_ok=True)
745 p1=p1, p2=p2, extra=extra, empty_ok=True)
746
746
747 def commit(self, files=None, text="", user=None, date=None,
747 def commit(self, files=None, text="", user=None, date=None,
748 match=util.always, force=False, force_editor=False,
748 match=util.always, force=False, force_editor=False,
749 p1=None, p2=None, extra={}, empty_ok=False):
749 p1=None, p2=None, extra={}, empty_ok=False):
750 wlock = lock = tr = None
750 wlock = lock = tr = None
751 valid = 0 # don't save the dirstate if this isn't set
751 valid = 0 # don't save the dirstate if this isn't set
752 if files:
752 if files:
753 files = util.unique(files)
753 files = util.unique(files)
754 try:
754 try:
755 wlock = self.wlock()
755 wlock = self.wlock()
756 lock = self.lock()
756 lock = self.lock()
757 commit = []
757 commit = []
758 remove = []
758 remove = []
759 changed = []
759 changed = []
760 use_dirstate = (p1 is None) # not rawcommit
760 use_dirstate = (p1 is None) # not rawcommit
761 extra = extra.copy()
761 extra = extra.copy()
762
762
763 if use_dirstate:
763 if use_dirstate:
764 if files:
764 if files:
765 for f in files:
765 for f in files:
766 s = self.dirstate[f]
766 s = self.dirstate[f]
767 if s in 'nma':
767 if s in 'nma':
768 commit.append(f)
768 commit.append(f)
769 elif s == 'r':
769 elif s == 'r':
770 remove.append(f)
770 remove.append(f)
771 else:
771 else:
772 self.ui.warn(_("%s not tracked!\n") % f)
772 self.ui.warn(_("%s not tracked!\n") % f)
773 else:
773 else:
774 changes = self.status(match=match)[:5]
774 changes = self.status(match=match)[:5]
775 modified, added, removed, deleted, unknown = changes
775 modified, added, removed, deleted, unknown = changes
776 commit = modified + added
776 commit = modified + added
777 remove = removed
777 remove = removed
778 else:
778 else:
779 commit = files
779 commit = files
780
780
781 if use_dirstate:
781 if use_dirstate:
782 p1, p2 = self.dirstate.parents()
782 p1, p2 = self.dirstate.parents()
783 update_dirstate = True
783 update_dirstate = True
784
785 if (not force and p2 != nullid and
786 (files or match != util.always)):
787 raise util.Abort(_('cannot partially commit a merge '
788 '(do not specify files or patterns)'))
784 else:
789 else:
785 p1, p2 = p1, p2 or nullid
790 p1, p2 = p1, p2 or nullid
786 update_dirstate = (self.dirstate.parents()[0] == p1)
791 update_dirstate = (self.dirstate.parents()[0] == p1)
787
792
788 c1 = self.changelog.read(p1)
793 c1 = self.changelog.read(p1)
789 c2 = self.changelog.read(p2)
794 c2 = self.changelog.read(p2)
790 m1 = self.manifest.read(c1[0]).copy()
795 m1 = self.manifest.read(c1[0]).copy()
791 m2 = self.manifest.read(c2[0])
796 m2 = self.manifest.read(c2[0])
792
797
793 if use_dirstate:
798 if use_dirstate:
794 branchname = self.workingctx().branch()
799 branchname = self.workingctx().branch()
795 try:
800 try:
796 branchname = branchname.decode('UTF-8').encode('UTF-8')
801 branchname = branchname.decode('UTF-8').encode('UTF-8')
797 except UnicodeDecodeError:
802 except UnicodeDecodeError:
798 raise util.Abort(_('branch name not in UTF-8!'))
803 raise util.Abort(_('branch name not in UTF-8!'))
799 else:
804 else:
800 branchname = ""
805 branchname = ""
801
806
802 if use_dirstate:
807 if use_dirstate:
803 oldname = c1[5].get("branch") # stored in UTF-8
808 oldname = c1[5].get("branch") # stored in UTF-8
804 if (not commit and not remove and not force and p2 == nullid
809 if (not commit and not remove and not force and p2 == nullid
805 and branchname == oldname):
810 and branchname == oldname):
806 self.ui.status(_("nothing changed\n"))
811 self.ui.status(_("nothing changed\n"))
807 return None
812 return None
808
813
809 xp1 = hex(p1)
814 xp1 = hex(p1)
810 if p2 == nullid: xp2 = ''
815 if p2 == nullid: xp2 = ''
811 else: xp2 = hex(p2)
816 else: xp2 = hex(p2)
812
817
813 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
818 self.hook("precommit", throw=True, parent1=xp1, parent2=xp2)
814
819
815 tr = self.transaction()
820 tr = self.transaction()
816 trp = weakref.proxy(tr)
821 trp = weakref.proxy(tr)
817
822
818 # check in files
823 # check in files
819 new = {}
824 new = {}
820 linkrev = self.changelog.count()
825 linkrev = self.changelog.count()
821 commit.sort()
826 commit.sort()
822 is_exec = util.execfunc(self.root, m1.execf)
827 is_exec = util.execfunc(self.root, m1.execf)
823 is_link = util.linkfunc(self.root, m1.linkf)
828 is_link = util.linkfunc(self.root, m1.linkf)
824 for f in commit:
829 for f in commit:
825 self.ui.note(f + "\n")
830 self.ui.note(f + "\n")
826 try:
831 try:
827 new[f] = self.filecommit(f, m1, m2, linkrev, trp, changed)
832 new[f] = self.filecommit(f, m1, m2, linkrev, trp, changed)
828 new_exec = is_exec(f)
833 new_exec = is_exec(f)
829 new_link = is_link(f)
834 new_link = is_link(f)
830 if ((not changed or changed[-1] != f) and
835 if ((not changed or changed[-1] != f) and
831 m2.get(f) != new[f]):
836 m2.get(f) != new[f]):
832 # mention the file in the changelog if some
837 # mention the file in the changelog if some
833 # flag changed, even if there was no content
838 # flag changed, even if there was no content
834 # change.
839 # change.
835 old_exec = m1.execf(f)
840 old_exec = m1.execf(f)
836 old_link = m1.linkf(f)
841 old_link = m1.linkf(f)
837 if old_exec != new_exec or old_link != new_link:
842 if old_exec != new_exec or old_link != new_link:
838 changed.append(f)
843 changed.append(f)
839 m1.set(f, new_exec, new_link)
844 m1.set(f, new_exec, new_link)
840 if use_dirstate:
845 if use_dirstate:
841 self.dirstate.normal(f)
846 self.dirstate.normal(f)
842
847
843 except (OSError, IOError):
848 except (OSError, IOError):
844 if use_dirstate:
849 if use_dirstate:
845 self.ui.warn(_("trouble committing %s!\n") % f)
850 self.ui.warn(_("trouble committing %s!\n") % f)
846 raise
851 raise
847 else:
852 else:
848 remove.append(f)
853 remove.append(f)
849
854
850 # update manifest
855 # update manifest
851 m1.update(new)
856 m1.update(new)
852 remove.sort()
857 remove.sort()
853 removed = []
858 removed = []
854
859
855 for f in remove:
860 for f in remove:
856 if f in m1:
861 if f in m1:
857 del m1[f]
862 del m1[f]
858 removed.append(f)
863 removed.append(f)
859 elif f in m2:
864 elif f in m2:
860 removed.append(f)
865 removed.append(f)
861 mn = self.manifest.add(m1, trp, linkrev, c1[0], c2[0],
866 mn = self.manifest.add(m1, trp, linkrev, c1[0], c2[0],
862 (new, removed))
867 (new, removed))
863
868
864 # add changeset
869 # add changeset
865 new = new.keys()
870 new = new.keys()
866 new.sort()
871 new.sort()
867
872
868 user = user or self.ui.username()
873 user = user or self.ui.username()
869 if (not empty_ok and not text) or force_editor:
874 if (not empty_ok and not text) or force_editor:
870 edittext = []
875 edittext = []
871 if text:
876 if text:
872 edittext.append(text)
877 edittext.append(text)
873 edittext.append("")
878 edittext.append("")
874 edittext.append(_("HG: Enter commit message."
879 edittext.append(_("HG: Enter commit message."
875 " Lines beginning with 'HG:' are removed."))
880 " Lines beginning with 'HG:' are removed."))
876 edittext.append("HG: --")
881 edittext.append("HG: --")
877 edittext.append("HG: user: %s" % user)
882 edittext.append("HG: user: %s" % user)
878 if p2 != nullid:
883 if p2 != nullid:
879 edittext.append("HG: branch merge")
884 edittext.append("HG: branch merge")
880 if branchname:
885 if branchname:
881 edittext.append("HG: branch '%s'" % util.tolocal(branchname))
886 edittext.append("HG: branch '%s'" % util.tolocal(branchname))
882 edittext.extend(["HG: changed %s" % f for f in changed])
887 edittext.extend(["HG: changed %s" % f for f in changed])
883 edittext.extend(["HG: removed %s" % f for f in removed])
888 edittext.extend(["HG: removed %s" % f for f in removed])
884 if not changed and not remove:
889 if not changed and not remove:
885 edittext.append("HG: no files changed")
890 edittext.append("HG: no files changed")
886 edittext.append("")
891 edittext.append("")
887 # run editor in the repository root
892 # run editor in the repository root
888 olddir = os.getcwd()
893 olddir = os.getcwd()
889 os.chdir(self.root)
894 os.chdir(self.root)
890 text = self.ui.edit("\n".join(edittext), user)
895 text = self.ui.edit("\n".join(edittext), user)
891 os.chdir(olddir)
896 os.chdir(olddir)
892
897
893 if branchname:
898 if branchname:
894 extra["branch"] = branchname
899 extra["branch"] = branchname
895
900
896 lines = [line.rstrip() for line in text.rstrip().splitlines()]
901 lines = [line.rstrip() for line in text.rstrip().splitlines()]
897 while lines and not lines[0]:
902 while lines and not lines[0]:
898 del lines[0]
903 del lines[0]
899 if not lines and use_dirstate:
904 if not lines and use_dirstate:
900 raise util.Abort(_("empty commit message"))
905 raise util.Abort(_("empty commit message"))
901 text = '\n'.join(lines)
906 text = '\n'.join(lines)
902
907
903 n = self.changelog.add(mn, changed + removed, text, trp, p1, p2,
908 n = self.changelog.add(mn, changed + removed, text, trp, p1, p2,
904 user, date, extra)
909 user, date, extra)
905 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
910 self.hook('pretxncommit', throw=True, node=hex(n), parent1=xp1,
906 parent2=xp2)
911 parent2=xp2)
907 tr.close()
912 tr.close()
908
913
909 if self.branchcache:
914 if self.branchcache:
910 self.branchtags()
915 self.branchtags()
911
916
912 if use_dirstate or update_dirstate:
917 if use_dirstate or update_dirstate:
913 self.dirstate.setparents(n)
918 self.dirstate.setparents(n)
914 if use_dirstate:
919 if use_dirstate:
915 for f in removed:
920 for f in removed:
916 self.dirstate.forget(f)
921 self.dirstate.forget(f)
917 valid = 1 # our dirstate updates are complete
922 valid = 1 # our dirstate updates are complete
918
923
919 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
924 self.hook("commit", node=hex(n), parent1=xp1, parent2=xp2)
920 return n
925 return n
921 finally:
926 finally:
922 if not valid: # don't save our updated dirstate
927 if not valid: # don't save our updated dirstate
923 self.dirstate.invalidate()
928 self.dirstate.invalidate()
924 del tr, lock, wlock
929 del tr, lock, wlock
925
930
926 def walk(self, node=None, files=[], match=util.always, badmatch=None):
931 def walk(self, node=None, files=[], match=util.always, badmatch=None):
927 '''
932 '''
928 walk recursively through the directory tree or a given
933 walk recursively through the directory tree or a given
929 changeset, finding all files matched by the match
934 changeset, finding all files matched by the match
930 function
935 function
931
936
932 results are yielded in a tuple (src, filename), where src
937 results are yielded in a tuple (src, filename), where src
933 is one of:
938 is one of:
934 'f' the file was found in the directory tree
939 'f' the file was found in the directory tree
935 'm' the file was only in the dirstate and not in the tree
940 'm' the file was only in the dirstate and not in the tree
936 'b' file was not found and matched badmatch
941 'b' file was not found and matched badmatch
937 '''
942 '''
938
943
939 if node:
944 if node:
940 fdict = dict.fromkeys(files)
945 fdict = dict.fromkeys(files)
941 # for dirstate.walk, files=['.'] means "walk the whole tree".
946 # for dirstate.walk, files=['.'] means "walk the whole tree".
942 # follow that here, too
947 # follow that here, too
943 fdict.pop('.', None)
948 fdict.pop('.', None)
944 mdict = self.manifest.read(self.changelog.read(node)[0])
949 mdict = self.manifest.read(self.changelog.read(node)[0])
945 mfiles = mdict.keys()
950 mfiles = mdict.keys()
946 mfiles.sort()
951 mfiles.sort()
947 for fn in mfiles:
952 for fn in mfiles:
948 for ffn in fdict:
953 for ffn in fdict:
949 # match if the file is the exact name or a directory
954 # match if the file is the exact name or a directory
950 if ffn == fn or fn.startswith("%s/" % ffn):
955 if ffn == fn or fn.startswith("%s/" % ffn):
951 del fdict[ffn]
956 del fdict[ffn]
952 break
957 break
953 if match(fn):
958 if match(fn):
954 yield 'm', fn
959 yield 'm', fn
955 ffiles = fdict.keys()
960 ffiles = fdict.keys()
956 ffiles.sort()
961 ffiles.sort()
957 for fn in ffiles:
962 for fn in ffiles:
958 if badmatch and badmatch(fn):
963 if badmatch and badmatch(fn):
959 if match(fn):
964 if match(fn):
960 yield 'b', fn
965 yield 'b', fn
961 else:
966 else:
962 self.ui.warn(_('%s: No such file in rev %s\n')
967 self.ui.warn(_('%s: No such file in rev %s\n')
963 % (self.pathto(fn), short(node)))
968 % (self.pathto(fn), short(node)))
964 else:
969 else:
965 for src, fn in self.dirstate.walk(files, match, badmatch=badmatch):
970 for src, fn in self.dirstate.walk(files, match, badmatch=badmatch):
966 yield src, fn
971 yield src, fn
967
972
968 def status(self, node1=None, node2=None, files=[], match=util.always,
973 def status(self, node1=None, node2=None, files=[], match=util.always,
969 list_ignored=False, list_clean=False, list_unknown=True):
974 list_ignored=False, list_clean=False, list_unknown=True):
970 """return status of files between two nodes or node and working directory
975 """return status of files between two nodes or node and working directory
971
976
972 If node1 is None, use the first dirstate parent instead.
977 If node1 is None, use the first dirstate parent instead.
973 If node2 is None, compare node1 with working directory.
978 If node2 is None, compare node1 with working directory.
974 """
979 """
975
980
976 def fcmp(fn, getnode):
981 def fcmp(fn, getnode):
977 t1 = self.wread(fn)
982 t1 = self.wread(fn)
978 return self.file(fn).cmp(getnode(fn), t1)
983 return self.file(fn).cmp(getnode(fn), t1)
979
984
980 def mfmatches(node):
985 def mfmatches(node):
981 change = self.changelog.read(node)
986 change = self.changelog.read(node)
982 mf = self.manifest.read(change[0]).copy()
987 mf = self.manifest.read(change[0]).copy()
983 for fn in mf.keys():
988 for fn in mf.keys():
984 if not match(fn):
989 if not match(fn):
985 del mf[fn]
990 del mf[fn]
986 return mf
991 return mf
987
992
988 modified, added, removed, deleted, unknown = [], [], [], [], []
993 modified, added, removed, deleted, unknown = [], [], [], [], []
989 ignored, clean = [], []
994 ignored, clean = [], []
990
995
991 compareworking = False
996 compareworking = False
992 if not node1 or (not node2 and node1 == self.dirstate.parents()[0]):
997 if not node1 or (not node2 and node1 == self.dirstate.parents()[0]):
993 compareworking = True
998 compareworking = True
994
999
995 if not compareworking:
1000 if not compareworking:
996 # read the manifest from node1 before the manifest from node2,
1001 # read the manifest from node1 before the manifest from node2,
997 # so that we'll hit the manifest cache if we're going through
1002 # so that we'll hit the manifest cache if we're going through
998 # all the revisions in parent->child order.
1003 # all the revisions in parent->child order.
999 mf1 = mfmatches(node1)
1004 mf1 = mfmatches(node1)
1000
1005
1001 # are we comparing the working directory?
1006 # are we comparing the working directory?
1002 if not node2:
1007 if not node2:
1003 (lookup, modified, added, removed, deleted, unknown,
1008 (lookup, modified, added, removed, deleted, unknown,
1004 ignored, clean) = self.dirstate.status(files, match,
1009 ignored, clean) = self.dirstate.status(files, match,
1005 list_ignored, list_clean,
1010 list_ignored, list_clean,
1006 list_unknown)
1011 list_unknown)
1007
1012
1008 # are we comparing working dir against its parent?
1013 # are we comparing working dir against its parent?
1009 if compareworking:
1014 if compareworking:
1010 if lookup:
1015 if lookup:
1011 fixup = []
1016 fixup = []
1012 # do a full compare of any files that might have changed
1017 # do a full compare of any files that might have changed
1013 ctx = self.changectx()
1018 ctx = self.changectx()
1014 mexec = lambda f: 'x' in ctx.fileflags(f)
1019 mexec = lambda f: 'x' in ctx.fileflags(f)
1015 mlink = lambda f: 'l' in ctx.fileflags(f)
1020 mlink = lambda f: 'l' in ctx.fileflags(f)
1016 is_exec = util.execfunc(self.root, mexec)
1021 is_exec = util.execfunc(self.root, mexec)
1017 is_link = util.linkfunc(self.root, mlink)
1022 is_link = util.linkfunc(self.root, mlink)
1018 def flags(f):
1023 def flags(f):
1019 return is_link(f) and 'l' or is_exec(f) and 'x' or ''
1024 return is_link(f) and 'l' or is_exec(f) and 'x' or ''
1020 for f in lookup:
1025 for f in lookup:
1021 if (f not in ctx or flags(f) != ctx.fileflags(f)
1026 if (f not in ctx or flags(f) != ctx.fileflags(f)
1022 or ctx[f].cmp(self.wread(f))):
1027 or ctx[f].cmp(self.wread(f))):
1023 modified.append(f)
1028 modified.append(f)
1024 else:
1029 else:
1025 fixup.append(f)
1030 fixup.append(f)
1026 if list_clean:
1031 if list_clean:
1027 clean.append(f)
1032 clean.append(f)
1028
1033
1029 # update dirstate for files that are actually clean
1034 # update dirstate for files that are actually clean
1030 if fixup:
1035 if fixup:
1031 wlock = None
1036 wlock = None
1032 try:
1037 try:
1033 try:
1038 try:
1034 wlock = self.wlock(False)
1039 wlock = self.wlock(False)
1035 except lock.LockException:
1040 except lock.LockException:
1036 pass
1041 pass
1037 if wlock:
1042 if wlock:
1038 for f in fixup:
1043 for f in fixup:
1039 self.dirstate.normal(f)
1044 self.dirstate.normal(f)
1040 finally:
1045 finally:
1041 del wlock
1046 del wlock
1042 else:
1047 else:
1043 # we are comparing working dir against non-parent
1048 # we are comparing working dir against non-parent
1044 # generate a pseudo-manifest for the working dir
1049 # generate a pseudo-manifest for the working dir
1045 # XXX: create it in dirstate.py ?
1050 # XXX: create it in dirstate.py ?
1046 mf2 = mfmatches(self.dirstate.parents()[0])
1051 mf2 = mfmatches(self.dirstate.parents()[0])
1047 is_exec = util.execfunc(self.root, mf2.execf)
1052 is_exec = util.execfunc(self.root, mf2.execf)
1048 is_link = util.linkfunc(self.root, mf2.linkf)
1053 is_link = util.linkfunc(self.root, mf2.linkf)
1049 for f in lookup + modified + added:
1054 for f in lookup + modified + added:
1050 mf2[f] = ""
1055 mf2[f] = ""
1051 mf2.set(f, is_exec(f), is_link(f))
1056 mf2.set(f, is_exec(f), is_link(f))
1052 for f in removed:
1057 for f in removed:
1053 if f in mf2:
1058 if f in mf2:
1054 del mf2[f]
1059 del mf2[f]
1055
1060
1056 else:
1061 else:
1057 # we are comparing two revisions
1062 # we are comparing two revisions
1058 mf2 = mfmatches(node2)
1063 mf2 = mfmatches(node2)
1059
1064
1060 if not compareworking:
1065 if not compareworking:
1061 # flush lists from dirstate before comparing manifests
1066 # flush lists from dirstate before comparing manifests
1062 modified, added, clean = [], [], []
1067 modified, added, clean = [], [], []
1063
1068
1064 # make sure to sort the files so we talk to the disk in a
1069 # make sure to sort the files so we talk to the disk in a
1065 # reasonable order
1070 # reasonable order
1066 mf2keys = mf2.keys()
1071 mf2keys = mf2.keys()
1067 mf2keys.sort()
1072 mf2keys.sort()
1068 getnode = lambda fn: mf1.get(fn, nullid)
1073 getnode = lambda fn: mf1.get(fn, nullid)
1069 for fn in mf2keys:
1074 for fn in mf2keys:
1070 if fn in mf1:
1075 if fn in mf1:
1071 if (mf1.flags(fn) != mf2.flags(fn) or
1076 if (mf1.flags(fn) != mf2.flags(fn) or
1072 (mf1[fn] != mf2[fn] and
1077 (mf1[fn] != mf2[fn] and
1073 (mf2[fn] != "" or fcmp(fn, getnode)))):
1078 (mf2[fn] != "" or fcmp(fn, getnode)))):
1074 modified.append(fn)
1079 modified.append(fn)
1075 elif list_clean:
1080 elif list_clean:
1076 clean.append(fn)
1081 clean.append(fn)
1077 del mf1[fn]
1082 del mf1[fn]
1078 else:
1083 else:
1079 added.append(fn)
1084 added.append(fn)
1080
1085
1081 removed = mf1.keys()
1086 removed = mf1.keys()
1082
1087
1083 # sort and return results:
1088 # sort and return results:
1084 for l in modified, added, removed, deleted, unknown, ignored, clean:
1089 for l in modified, added, removed, deleted, unknown, ignored, clean:
1085 l.sort()
1090 l.sort()
1086 return (modified, added, removed, deleted, unknown, ignored, clean)
1091 return (modified, added, removed, deleted, unknown, ignored, clean)
1087
1092
1088 def add(self, list):
1093 def add(self, list):
1089 wlock = self.wlock()
1094 wlock = self.wlock()
1090 try:
1095 try:
1091 rejected = []
1096 rejected = []
1092 for f in list:
1097 for f in list:
1093 p = self.wjoin(f)
1098 p = self.wjoin(f)
1094 try:
1099 try:
1095 st = os.lstat(p)
1100 st = os.lstat(p)
1096 except:
1101 except:
1097 self.ui.warn(_("%s does not exist!\n") % f)
1102 self.ui.warn(_("%s does not exist!\n") % f)
1098 rejected.append(f)
1103 rejected.append(f)
1099 continue
1104 continue
1100 if st.st_size > 10000000:
1105 if st.st_size > 10000000:
1101 self.ui.warn(_("%s: files over 10MB may cause memory and"
1106 self.ui.warn(_("%s: files over 10MB may cause memory and"
1102 " performance problems\n"
1107 " performance problems\n"
1103 "(use 'hg revert %s' to unadd the file)\n")
1108 "(use 'hg revert %s' to unadd the file)\n")
1104 % (f, f))
1109 % (f, f))
1105 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1110 if not (stat.S_ISREG(st.st_mode) or stat.S_ISLNK(st.st_mode)):
1106 self.ui.warn(_("%s not added: only files and symlinks "
1111 self.ui.warn(_("%s not added: only files and symlinks "
1107 "supported currently\n") % f)
1112 "supported currently\n") % f)
1108 rejected.append(p)
1113 rejected.append(p)
1109 elif self.dirstate[f] in 'amn':
1114 elif self.dirstate[f] in 'amn':
1110 self.ui.warn(_("%s already tracked!\n") % f)
1115 self.ui.warn(_("%s already tracked!\n") % f)
1111 elif self.dirstate[f] == 'r':
1116 elif self.dirstate[f] == 'r':
1112 self.dirstate.normallookup(f)
1117 self.dirstate.normallookup(f)
1113 else:
1118 else:
1114 self.dirstate.add(f)
1119 self.dirstate.add(f)
1115 return rejected
1120 return rejected
1116 finally:
1121 finally:
1117 del wlock
1122 del wlock
1118
1123
1119 def forget(self, list):
1124 def forget(self, list):
1120 wlock = self.wlock()
1125 wlock = self.wlock()
1121 try:
1126 try:
1122 for f in list:
1127 for f in list:
1123 if self.dirstate[f] != 'a':
1128 if self.dirstate[f] != 'a':
1124 self.ui.warn(_("%s not added!\n") % f)
1129 self.ui.warn(_("%s not added!\n") % f)
1125 else:
1130 else:
1126 self.dirstate.forget(f)
1131 self.dirstate.forget(f)
1127 finally:
1132 finally:
1128 del wlock
1133 del wlock
1129
1134
1130 def remove(self, list, unlink=False):
1135 def remove(self, list, unlink=False):
1131 wlock = None
1136 wlock = None
1132 try:
1137 try:
1133 if unlink:
1138 if unlink:
1134 for f in list:
1139 for f in list:
1135 try:
1140 try:
1136 util.unlink(self.wjoin(f))
1141 util.unlink(self.wjoin(f))
1137 except OSError, inst:
1142 except OSError, inst:
1138 if inst.errno != errno.ENOENT:
1143 if inst.errno != errno.ENOENT:
1139 raise
1144 raise
1140 wlock = self.wlock()
1145 wlock = self.wlock()
1141 for f in list:
1146 for f in list:
1142 if unlink and os.path.exists(self.wjoin(f)):
1147 if unlink and os.path.exists(self.wjoin(f)):
1143 self.ui.warn(_("%s still exists!\n") % f)
1148 self.ui.warn(_("%s still exists!\n") % f)
1144 elif self.dirstate[f] == 'a':
1149 elif self.dirstate[f] == 'a':
1145 self.dirstate.forget(f)
1150 self.dirstate.forget(f)
1146 elif f not in self.dirstate:
1151 elif f not in self.dirstate:
1147 self.ui.warn(_("%s not tracked!\n") % f)
1152 self.ui.warn(_("%s not tracked!\n") % f)
1148 else:
1153 else:
1149 self.dirstate.remove(f)
1154 self.dirstate.remove(f)
1150 finally:
1155 finally:
1151 del wlock
1156 del wlock
1152
1157
1153 def undelete(self, list):
1158 def undelete(self, list):
1154 wlock = None
1159 wlock = None
1155 try:
1160 try:
1156 manifests = [self.manifest.read(self.changelog.read(p)[0])
1161 manifests = [self.manifest.read(self.changelog.read(p)[0])
1157 for p in self.dirstate.parents() if p != nullid]
1162 for p in self.dirstate.parents() if p != nullid]
1158 wlock = self.wlock()
1163 wlock = self.wlock()
1159 for f in list:
1164 for f in list:
1160 if self.dirstate[f] != 'r':
1165 if self.dirstate[f] != 'r':
1161 self.ui.warn("%s not removed!\n" % f)
1166 self.ui.warn("%s not removed!\n" % f)
1162 else:
1167 else:
1163 m = f in manifests[0] and manifests[0] or manifests[1]
1168 m = f in manifests[0] and manifests[0] or manifests[1]
1164 t = self.file(f).read(m[f])
1169 t = self.file(f).read(m[f])
1165 self.wwrite(f, t, m.flags(f))
1170 self.wwrite(f, t, m.flags(f))
1166 self.dirstate.normal(f)
1171 self.dirstate.normal(f)
1167 finally:
1172 finally:
1168 del wlock
1173 del wlock
1169
1174
1170 def copy(self, source, dest):
1175 def copy(self, source, dest):
1171 wlock = None
1176 wlock = None
1172 try:
1177 try:
1173 p = self.wjoin(dest)
1178 p = self.wjoin(dest)
1174 if not (os.path.exists(p) or os.path.islink(p)):
1179 if not (os.path.exists(p) or os.path.islink(p)):
1175 self.ui.warn(_("%s does not exist!\n") % dest)
1180 self.ui.warn(_("%s does not exist!\n") % dest)
1176 elif not (os.path.isfile(p) or os.path.islink(p)):
1181 elif not (os.path.isfile(p) or os.path.islink(p)):
1177 self.ui.warn(_("copy failed: %s is not a file or a "
1182 self.ui.warn(_("copy failed: %s is not a file or a "
1178 "symbolic link\n") % dest)
1183 "symbolic link\n") % dest)
1179 else:
1184 else:
1180 wlock = self.wlock()
1185 wlock = self.wlock()
1181 if dest not in self.dirstate:
1186 if dest not in self.dirstate:
1182 self.dirstate.add(dest)
1187 self.dirstate.add(dest)
1183 self.dirstate.copy(source, dest)
1188 self.dirstate.copy(source, dest)
1184 finally:
1189 finally:
1185 del wlock
1190 del wlock
1186
1191
1187 def heads(self, start=None):
1192 def heads(self, start=None):
1188 heads = self.changelog.heads(start)
1193 heads = self.changelog.heads(start)
1189 # sort the output in rev descending order
1194 # sort the output in rev descending order
1190 heads = [(-self.changelog.rev(h), h) for h in heads]
1195 heads = [(-self.changelog.rev(h), h) for h in heads]
1191 heads.sort()
1196 heads.sort()
1192 return [n for (r, n) in heads]
1197 return [n for (r, n) in heads]
1193
1198
1194 def branchheads(self, branch, start=None):
1199 def branchheads(self, branch, start=None):
1195 branches = self.branchtags()
1200 branches = self.branchtags()
1196 if branch not in branches:
1201 if branch not in branches:
1197 return []
1202 return []
1198 # The basic algorithm is this:
1203 # The basic algorithm is this:
1199 #
1204 #
1200 # Start from the branch tip since there are no later revisions that can
1205 # Start from the branch tip since there are no later revisions that can
1201 # possibly be in this branch, and the tip is a guaranteed head.
1206 # possibly be in this branch, and the tip is a guaranteed head.
1202 #
1207 #
1203 # Remember the tip's parents as the first ancestors, since these by
1208 # Remember the tip's parents as the first ancestors, since these by
1204 # definition are not heads.
1209 # definition are not heads.
1205 #
1210 #
1206 # Step backwards from the brach tip through all the revisions. We are
1211 # Step backwards from the brach tip through all the revisions. We are
1207 # guaranteed by the rules of Mercurial that we will now be visiting the
1212 # guaranteed by the rules of Mercurial that we will now be visiting the
1208 # nodes in reverse topological order (children before parents).
1213 # nodes in reverse topological order (children before parents).
1209 #
1214 #
1210 # If a revision is one of the ancestors of a head then we can toss it
1215 # If a revision is one of the ancestors of a head then we can toss it
1211 # out of the ancestors set (we've already found it and won't be
1216 # out of the ancestors set (we've already found it and won't be
1212 # visiting it again) and put its parents in the ancestors set.
1217 # visiting it again) and put its parents in the ancestors set.
1213 #
1218 #
1214 # Otherwise, if a revision is in the branch it's another head, since it
1219 # Otherwise, if a revision is in the branch it's another head, since it
1215 # wasn't in the ancestor list of an existing head. So add it to the
1220 # wasn't in the ancestor list of an existing head. So add it to the
1216 # head list, and add its parents to the ancestor list.
1221 # head list, and add its parents to the ancestor list.
1217 #
1222 #
1218 # If it is not in the branch ignore it.
1223 # If it is not in the branch ignore it.
1219 #
1224 #
1220 # Once we have a list of heads, use nodesbetween to filter out all the
1225 # Once we have a list of heads, use nodesbetween to filter out all the
1221 # heads that cannot be reached from startrev. There may be a more
1226 # heads that cannot be reached from startrev. There may be a more
1222 # efficient way to do this as part of the previous algorithm.
1227 # efficient way to do this as part of the previous algorithm.
1223
1228
1224 set = util.set
1229 set = util.set
1225 heads = [self.changelog.rev(branches[branch])]
1230 heads = [self.changelog.rev(branches[branch])]
1226 # Don't care if ancestors contains nullrev or not.
1231 # Don't care if ancestors contains nullrev or not.
1227 ancestors = set(self.changelog.parentrevs(heads[0]))
1232 ancestors = set(self.changelog.parentrevs(heads[0]))
1228 for rev in xrange(heads[0] - 1, nullrev, -1):
1233 for rev in xrange(heads[0] - 1, nullrev, -1):
1229 if rev in ancestors:
1234 if rev in ancestors:
1230 ancestors.update(self.changelog.parentrevs(rev))
1235 ancestors.update(self.changelog.parentrevs(rev))
1231 ancestors.remove(rev)
1236 ancestors.remove(rev)
1232 elif self.changectx(rev).branch() == branch:
1237 elif self.changectx(rev).branch() == branch:
1233 heads.append(rev)
1238 heads.append(rev)
1234 ancestors.update(self.changelog.parentrevs(rev))
1239 ancestors.update(self.changelog.parentrevs(rev))
1235 heads = [self.changelog.node(rev) for rev in heads]
1240 heads = [self.changelog.node(rev) for rev in heads]
1236 if start is not None:
1241 if start is not None:
1237 heads = self.changelog.nodesbetween([start], heads)[2]
1242 heads = self.changelog.nodesbetween([start], heads)[2]
1238 return heads
1243 return heads
1239
1244
1240 def branches(self, nodes):
1245 def branches(self, nodes):
1241 if not nodes:
1246 if not nodes:
1242 nodes = [self.changelog.tip()]
1247 nodes = [self.changelog.tip()]
1243 b = []
1248 b = []
1244 for n in nodes:
1249 for n in nodes:
1245 t = n
1250 t = n
1246 while 1:
1251 while 1:
1247 p = self.changelog.parents(n)
1252 p = self.changelog.parents(n)
1248 if p[1] != nullid or p[0] == nullid:
1253 if p[1] != nullid or p[0] == nullid:
1249 b.append((t, n, p[0], p[1]))
1254 b.append((t, n, p[0], p[1]))
1250 break
1255 break
1251 n = p[0]
1256 n = p[0]
1252 return b
1257 return b
1253
1258
1254 def between(self, pairs):
1259 def between(self, pairs):
1255 r = []
1260 r = []
1256
1261
1257 for top, bottom in pairs:
1262 for top, bottom in pairs:
1258 n, l, i = top, [], 0
1263 n, l, i = top, [], 0
1259 f = 1
1264 f = 1
1260
1265
1261 while n != bottom:
1266 while n != bottom:
1262 p = self.changelog.parents(n)[0]
1267 p = self.changelog.parents(n)[0]
1263 if i == f:
1268 if i == f:
1264 l.append(n)
1269 l.append(n)
1265 f = f * 2
1270 f = f * 2
1266 n = p
1271 n = p
1267 i += 1
1272 i += 1
1268
1273
1269 r.append(l)
1274 r.append(l)
1270
1275
1271 return r
1276 return r
1272
1277
1273 def findincoming(self, remote, base=None, heads=None, force=False):
1278 def findincoming(self, remote, base=None, heads=None, force=False):
1274 """Return list of roots of the subsets of missing nodes from remote
1279 """Return list of roots of the subsets of missing nodes from remote
1275
1280
1276 If base dict is specified, assume that these nodes and their parents
1281 If base dict is specified, assume that these nodes and their parents
1277 exist on the remote side and that no child of a node of base exists
1282 exist on the remote side and that no child of a node of base exists
1278 in both remote and self.
1283 in both remote and self.
1279 Furthermore base will be updated to include the nodes that exists
1284 Furthermore base will be updated to include the nodes that exists
1280 in self and remote but no children exists in self and remote.
1285 in self and remote but no children exists in self and remote.
1281 If a list of heads is specified, return only nodes which are heads
1286 If a list of heads is specified, return only nodes which are heads
1282 or ancestors of these heads.
1287 or ancestors of these heads.
1283
1288
1284 All the ancestors of base are in self and in remote.
1289 All the ancestors of base are in self and in remote.
1285 All the descendants of the list returned are missing in self.
1290 All the descendants of the list returned are missing in self.
1286 (and so we know that the rest of the nodes are missing in remote, see
1291 (and so we know that the rest of the nodes are missing in remote, see
1287 outgoing)
1292 outgoing)
1288 """
1293 """
1289 m = self.changelog.nodemap
1294 m = self.changelog.nodemap
1290 search = []
1295 search = []
1291 fetch = {}
1296 fetch = {}
1292 seen = {}
1297 seen = {}
1293 seenbranch = {}
1298 seenbranch = {}
1294 if base == None:
1299 if base == None:
1295 base = {}
1300 base = {}
1296
1301
1297 if not heads:
1302 if not heads:
1298 heads = remote.heads()
1303 heads = remote.heads()
1299
1304
1300 if self.changelog.tip() == nullid:
1305 if self.changelog.tip() == nullid:
1301 base[nullid] = 1
1306 base[nullid] = 1
1302 if heads != [nullid]:
1307 if heads != [nullid]:
1303 return [nullid]
1308 return [nullid]
1304 return []
1309 return []
1305
1310
1306 # assume we're closer to the tip than the root
1311 # assume we're closer to the tip than the root
1307 # and start by examining the heads
1312 # and start by examining the heads
1308 self.ui.status(_("searching for changes\n"))
1313 self.ui.status(_("searching for changes\n"))
1309
1314
1310 unknown = []
1315 unknown = []
1311 for h in heads:
1316 for h in heads:
1312 if h not in m:
1317 if h not in m:
1313 unknown.append(h)
1318 unknown.append(h)
1314 else:
1319 else:
1315 base[h] = 1
1320 base[h] = 1
1316
1321
1317 if not unknown:
1322 if not unknown:
1318 return []
1323 return []
1319
1324
1320 req = dict.fromkeys(unknown)
1325 req = dict.fromkeys(unknown)
1321 reqcnt = 0
1326 reqcnt = 0
1322
1327
1323 # search through remote branches
1328 # search through remote branches
1324 # a 'branch' here is a linear segment of history, with four parts:
1329 # a 'branch' here is a linear segment of history, with four parts:
1325 # head, root, first parent, second parent
1330 # head, root, first parent, second parent
1326 # (a branch always has two parents (or none) by definition)
1331 # (a branch always has two parents (or none) by definition)
1327 unknown = remote.branches(unknown)
1332 unknown = remote.branches(unknown)
1328 while unknown:
1333 while unknown:
1329 r = []
1334 r = []
1330 while unknown:
1335 while unknown:
1331 n = unknown.pop(0)
1336 n = unknown.pop(0)
1332 if n[0] in seen:
1337 if n[0] in seen:
1333 continue
1338 continue
1334
1339
1335 self.ui.debug(_("examining %s:%s\n")
1340 self.ui.debug(_("examining %s:%s\n")
1336 % (short(n[0]), short(n[1])))
1341 % (short(n[0]), short(n[1])))
1337 if n[0] == nullid: # found the end of the branch
1342 if n[0] == nullid: # found the end of the branch
1338 pass
1343 pass
1339 elif n in seenbranch:
1344 elif n in seenbranch:
1340 self.ui.debug(_("branch already found\n"))
1345 self.ui.debug(_("branch already found\n"))
1341 continue
1346 continue
1342 elif n[1] and n[1] in m: # do we know the base?
1347 elif n[1] and n[1] in m: # do we know the base?
1343 self.ui.debug(_("found incomplete branch %s:%s\n")
1348 self.ui.debug(_("found incomplete branch %s:%s\n")
1344 % (short(n[0]), short(n[1])))
1349 % (short(n[0]), short(n[1])))
1345 search.append(n) # schedule branch range for scanning
1350 search.append(n) # schedule branch range for scanning
1346 seenbranch[n] = 1
1351 seenbranch[n] = 1
1347 else:
1352 else:
1348 if n[1] not in seen and n[1] not in fetch:
1353 if n[1] not in seen and n[1] not in fetch:
1349 if n[2] in m and n[3] in m:
1354 if n[2] in m and n[3] in m:
1350 self.ui.debug(_("found new changeset %s\n") %
1355 self.ui.debug(_("found new changeset %s\n") %
1351 short(n[1]))
1356 short(n[1]))
1352 fetch[n[1]] = 1 # earliest unknown
1357 fetch[n[1]] = 1 # earliest unknown
1353 for p in n[2:4]:
1358 for p in n[2:4]:
1354 if p in m:
1359 if p in m:
1355 base[p] = 1 # latest known
1360 base[p] = 1 # latest known
1356
1361
1357 for p in n[2:4]:
1362 for p in n[2:4]:
1358 if p not in req and p not in m:
1363 if p not in req and p not in m:
1359 r.append(p)
1364 r.append(p)
1360 req[p] = 1
1365 req[p] = 1
1361 seen[n[0]] = 1
1366 seen[n[0]] = 1
1362
1367
1363 if r:
1368 if r:
1364 reqcnt += 1
1369 reqcnt += 1
1365 self.ui.debug(_("request %d: %s\n") %
1370 self.ui.debug(_("request %d: %s\n") %
1366 (reqcnt, " ".join(map(short, r))))
1371 (reqcnt, " ".join(map(short, r))))
1367 for p in xrange(0, len(r), 10):
1372 for p in xrange(0, len(r), 10):
1368 for b in remote.branches(r[p:p+10]):
1373 for b in remote.branches(r[p:p+10]):
1369 self.ui.debug(_("received %s:%s\n") %
1374 self.ui.debug(_("received %s:%s\n") %
1370 (short(b[0]), short(b[1])))
1375 (short(b[0]), short(b[1])))
1371 unknown.append(b)
1376 unknown.append(b)
1372
1377
1373 # do binary search on the branches we found
1378 # do binary search on the branches we found
1374 while search:
1379 while search:
1375 n = search.pop(0)
1380 n = search.pop(0)
1376 reqcnt += 1
1381 reqcnt += 1
1377 l = remote.between([(n[0], n[1])])[0]
1382 l = remote.between([(n[0], n[1])])[0]
1378 l.append(n[1])
1383 l.append(n[1])
1379 p = n[0]
1384 p = n[0]
1380 f = 1
1385 f = 1
1381 for i in l:
1386 for i in l:
1382 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
1387 self.ui.debug(_("narrowing %d:%d %s\n") % (f, len(l), short(i)))
1383 if i in m:
1388 if i in m:
1384 if f <= 2:
1389 if f <= 2:
1385 self.ui.debug(_("found new branch changeset %s\n") %
1390 self.ui.debug(_("found new branch changeset %s\n") %
1386 short(p))
1391 short(p))
1387 fetch[p] = 1
1392 fetch[p] = 1
1388 base[i] = 1
1393 base[i] = 1
1389 else:
1394 else:
1390 self.ui.debug(_("narrowed branch search to %s:%s\n")
1395 self.ui.debug(_("narrowed branch search to %s:%s\n")
1391 % (short(p), short(i)))
1396 % (short(p), short(i)))
1392 search.append((p, i))
1397 search.append((p, i))
1393 break
1398 break
1394 p, f = i, f * 2
1399 p, f = i, f * 2
1395
1400
1396 # sanity check our fetch list
1401 # sanity check our fetch list
1397 for f in fetch.keys():
1402 for f in fetch.keys():
1398 if f in m:
1403 if f in m:
1399 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
1404 raise repo.RepoError(_("already have changeset ") + short(f[:4]))
1400
1405
1401 if base.keys() == [nullid]:
1406 if base.keys() == [nullid]:
1402 if force:
1407 if force:
1403 self.ui.warn(_("warning: repository is unrelated\n"))
1408 self.ui.warn(_("warning: repository is unrelated\n"))
1404 else:
1409 else:
1405 raise util.Abort(_("repository is unrelated"))
1410 raise util.Abort(_("repository is unrelated"))
1406
1411
1407 self.ui.debug(_("found new changesets starting at ") +
1412 self.ui.debug(_("found new changesets starting at ") +
1408 " ".join([short(f) for f in fetch]) + "\n")
1413 " ".join([short(f) for f in fetch]) + "\n")
1409
1414
1410 self.ui.debug(_("%d total queries\n") % reqcnt)
1415 self.ui.debug(_("%d total queries\n") % reqcnt)
1411
1416
1412 return fetch.keys()
1417 return fetch.keys()
1413
1418
1414 def findoutgoing(self, remote, base=None, heads=None, force=False):
1419 def findoutgoing(self, remote, base=None, heads=None, force=False):
1415 """Return list of nodes that are roots of subsets not in remote
1420 """Return list of nodes that are roots of subsets not in remote
1416
1421
1417 If base dict is specified, assume that these nodes and their parents
1422 If base dict is specified, assume that these nodes and their parents
1418 exist on the remote side.
1423 exist on the remote side.
1419 If a list of heads is specified, return only nodes which are heads
1424 If a list of heads is specified, return only nodes which are heads
1420 or ancestors of these heads, and return a second element which
1425 or ancestors of these heads, and return a second element which
1421 contains all remote heads which get new children.
1426 contains all remote heads which get new children.
1422 """
1427 """
1423 if base == None:
1428 if base == None:
1424 base = {}
1429 base = {}
1425 self.findincoming(remote, base, heads, force=force)
1430 self.findincoming(remote, base, heads, force=force)
1426
1431
1427 self.ui.debug(_("common changesets up to ")
1432 self.ui.debug(_("common changesets up to ")
1428 + " ".join(map(short, base.keys())) + "\n")
1433 + " ".join(map(short, base.keys())) + "\n")
1429
1434
1430 remain = dict.fromkeys(self.changelog.nodemap)
1435 remain = dict.fromkeys(self.changelog.nodemap)
1431
1436
1432 # prune everything remote has from the tree
1437 # prune everything remote has from the tree
1433 del remain[nullid]
1438 del remain[nullid]
1434 remove = base.keys()
1439 remove = base.keys()
1435 while remove:
1440 while remove:
1436 n = remove.pop(0)
1441 n = remove.pop(0)
1437 if n in remain:
1442 if n in remain:
1438 del remain[n]
1443 del remain[n]
1439 for p in self.changelog.parents(n):
1444 for p in self.changelog.parents(n):
1440 remove.append(p)
1445 remove.append(p)
1441
1446
1442 # find every node whose parents have been pruned
1447 # find every node whose parents have been pruned
1443 subset = []
1448 subset = []
1444 # find every remote head that will get new children
1449 # find every remote head that will get new children
1445 updated_heads = {}
1450 updated_heads = {}
1446 for n in remain:
1451 for n in remain:
1447 p1, p2 = self.changelog.parents(n)
1452 p1, p2 = self.changelog.parents(n)
1448 if p1 not in remain and p2 not in remain:
1453 if p1 not in remain and p2 not in remain:
1449 subset.append(n)
1454 subset.append(n)
1450 if heads:
1455 if heads:
1451 if p1 in heads:
1456 if p1 in heads:
1452 updated_heads[p1] = True
1457 updated_heads[p1] = True
1453 if p2 in heads:
1458 if p2 in heads:
1454 updated_heads[p2] = True
1459 updated_heads[p2] = True
1455
1460
1456 # this is the set of all roots we have to push
1461 # this is the set of all roots we have to push
1457 if heads:
1462 if heads:
1458 return subset, updated_heads.keys()
1463 return subset, updated_heads.keys()
1459 else:
1464 else:
1460 return subset
1465 return subset
1461
1466
1462 def pull(self, remote, heads=None, force=False):
1467 def pull(self, remote, heads=None, force=False):
1463 lock = self.lock()
1468 lock = self.lock()
1464 try:
1469 try:
1465 fetch = self.findincoming(remote, heads=heads, force=force)
1470 fetch = self.findincoming(remote, heads=heads, force=force)
1466 if fetch == [nullid]:
1471 if fetch == [nullid]:
1467 self.ui.status(_("requesting all changes\n"))
1472 self.ui.status(_("requesting all changes\n"))
1468
1473
1469 if not fetch:
1474 if not fetch:
1470 self.ui.status(_("no changes found\n"))
1475 self.ui.status(_("no changes found\n"))
1471 return 0
1476 return 0
1472
1477
1473 if heads is None:
1478 if heads is None:
1474 cg = remote.changegroup(fetch, 'pull')
1479 cg = remote.changegroup(fetch, 'pull')
1475 else:
1480 else:
1476 if 'changegroupsubset' not in remote.capabilities:
1481 if 'changegroupsubset' not in remote.capabilities:
1477 raise util.Abort(_("Partial pull cannot be done because other repository doesn't support changegroupsubset."))
1482 raise util.Abort(_("Partial pull cannot be done because other repository doesn't support changegroupsubset."))
1478 cg = remote.changegroupsubset(fetch, heads, 'pull')
1483 cg = remote.changegroupsubset(fetch, heads, 'pull')
1479 return self.addchangegroup(cg, 'pull', remote.url())
1484 return self.addchangegroup(cg, 'pull', remote.url())
1480 finally:
1485 finally:
1481 del lock
1486 del lock
1482
1487
1483 def push(self, remote, force=False, revs=None):
1488 def push(self, remote, force=False, revs=None):
1484 # there are two ways to push to remote repo:
1489 # there are two ways to push to remote repo:
1485 #
1490 #
1486 # addchangegroup assumes local user can lock remote
1491 # addchangegroup assumes local user can lock remote
1487 # repo (local filesystem, old ssh servers).
1492 # repo (local filesystem, old ssh servers).
1488 #
1493 #
1489 # unbundle assumes local user cannot lock remote repo (new ssh
1494 # unbundle assumes local user cannot lock remote repo (new ssh
1490 # servers, http servers).
1495 # servers, http servers).
1491
1496
1492 if remote.capable('unbundle'):
1497 if remote.capable('unbundle'):
1493 return self.push_unbundle(remote, force, revs)
1498 return self.push_unbundle(remote, force, revs)
1494 return self.push_addchangegroup(remote, force, revs)
1499 return self.push_addchangegroup(remote, force, revs)
1495
1500
1496 def prepush(self, remote, force, revs):
1501 def prepush(self, remote, force, revs):
1497 base = {}
1502 base = {}
1498 remote_heads = remote.heads()
1503 remote_heads = remote.heads()
1499 inc = self.findincoming(remote, base, remote_heads, force=force)
1504 inc = self.findincoming(remote, base, remote_heads, force=force)
1500
1505
1501 update, updated_heads = self.findoutgoing(remote, base, remote_heads)
1506 update, updated_heads = self.findoutgoing(remote, base, remote_heads)
1502 if revs is not None:
1507 if revs is not None:
1503 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
1508 msng_cl, bases, heads = self.changelog.nodesbetween(update, revs)
1504 else:
1509 else:
1505 bases, heads = update, self.changelog.heads()
1510 bases, heads = update, self.changelog.heads()
1506
1511
1507 if not bases:
1512 if not bases:
1508 self.ui.status(_("no changes found\n"))
1513 self.ui.status(_("no changes found\n"))
1509 return None, 1
1514 return None, 1
1510 elif not force:
1515 elif not force:
1511 # check if we're creating new remote heads
1516 # check if we're creating new remote heads
1512 # to be a remote head after push, node must be either
1517 # to be a remote head after push, node must be either
1513 # - unknown locally
1518 # - unknown locally
1514 # - a local outgoing head descended from update
1519 # - a local outgoing head descended from update
1515 # - a remote head that's known locally and not
1520 # - a remote head that's known locally and not
1516 # ancestral to an outgoing head
1521 # ancestral to an outgoing head
1517
1522
1518 warn = 0
1523 warn = 0
1519
1524
1520 if remote_heads == [nullid]:
1525 if remote_heads == [nullid]:
1521 warn = 0
1526 warn = 0
1522 elif not revs and len(heads) > len(remote_heads):
1527 elif not revs and len(heads) > len(remote_heads):
1523 warn = 1
1528 warn = 1
1524 else:
1529 else:
1525 newheads = list(heads)
1530 newheads = list(heads)
1526 for r in remote_heads:
1531 for r in remote_heads:
1527 if r in self.changelog.nodemap:
1532 if r in self.changelog.nodemap:
1528 desc = self.changelog.heads(r, heads)
1533 desc = self.changelog.heads(r, heads)
1529 l = [h for h in heads if h in desc]
1534 l = [h for h in heads if h in desc]
1530 if not l:
1535 if not l:
1531 newheads.append(r)
1536 newheads.append(r)
1532 else:
1537 else:
1533 newheads.append(r)
1538 newheads.append(r)
1534 if len(newheads) > len(remote_heads):
1539 if len(newheads) > len(remote_heads):
1535 warn = 1
1540 warn = 1
1536
1541
1537 if warn:
1542 if warn:
1538 self.ui.warn(_("abort: push creates new remote heads!\n"))
1543 self.ui.warn(_("abort: push creates new remote heads!\n"))
1539 self.ui.status(_("(did you forget to merge?"
1544 self.ui.status(_("(did you forget to merge?"
1540 " use push -f to force)\n"))
1545 " use push -f to force)\n"))
1541 return None, 0
1546 return None, 0
1542 elif inc:
1547 elif inc:
1543 self.ui.warn(_("note: unsynced remote changes!\n"))
1548 self.ui.warn(_("note: unsynced remote changes!\n"))
1544
1549
1545
1550
1546 if revs is None:
1551 if revs is None:
1547 cg = self.changegroup(update, 'push')
1552 cg = self.changegroup(update, 'push')
1548 else:
1553 else:
1549 cg = self.changegroupsubset(update, revs, 'push')
1554 cg = self.changegroupsubset(update, revs, 'push')
1550 return cg, remote_heads
1555 return cg, remote_heads
1551
1556
1552 def push_addchangegroup(self, remote, force, revs):
1557 def push_addchangegroup(self, remote, force, revs):
1553 lock = remote.lock()
1558 lock = remote.lock()
1554 try:
1559 try:
1555 ret = self.prepush(remote, force, revs)
1560 ret = self.prepush(remote, force, revs)
1556 if ret[0] is not None:
1561 if ret[0] is not None:
1557 cg, remote_heads = ret
1562 cg, remote_heads = ret
1558 return remote.addchangegroup(cg, 'push', self.url())
1563 return remote.addchangegroup(cg, 'push', self.url())
1559 return ret[1]
1564 return ret[1]
1560 finally:
1565 finally:
1561 del lock
1566 del lock
1562
1567
1563 def push_unbundle(self, remote, force, revs):
1568 def push_unbundle(self, remote, force, revs):
1564 # local repo finds heads on server, finds out what revs it
1569 # local repo finds heads on server, finds out what revs it
1565 # must push. once revs transferred, if server finds it has
1570 # must push. once revs transferred, if server finds it has
1566 # different heads (someone else won commit/push race), server
1571 # different heads (someone else won commit/push race), server
1567 # aborts.
1572 # aborts.
1568
1573
1569 ret = self.prepush(remote, force, revs)
1574 ret = self.prepush(remote, force, revs)
1570 if ret[0] is not None:
1575 if ret[0] is not None:
1571 cg, remote_heads = ret
1576 cg, remote_heads = ret
1572 if force: remote_heads = ['force']
1577 if force: remote_heads = ['force']
1573 return remote.unbundle(cg, remote_heads, 'push')
1578 return remote.unbundle(cg, remote_heads, 'push')
1574 return ret[1]
1579 return ret[1]
1575
1580
1576 def changegroupinfo(self, nodes, source):
1581 def changegroupinfo(self, nodes, source):
1577 if self.ui.verbose or source == 'bundle':
1582 if self.ui.verbose or source == 'bundle':
1578 self.ui.status(_("%d changesets found\n") % len(nodes))
1583 self.ui.status(_("%d changesets found\n") % len(nodes))
1579 if self.ui.debugflag:
1584 if self.ui.debugflag:
1580 self.ui.debug(_("List of changesets:\n"))
1585 self.ui.debug(_("List of changesets:\n"))
1581 for node in nodes:
1586 for node in nodes:
1582 self.ui.debug("%s\n" % hex(node))
1587 self.ui.debug("%s\n" % hex(node))
1583
1588
1584 def changegroupsubset(self, bases, heads, source, extranodes=None):
1589 def changegroupsubset(self, bases, heads, source, extranodes=None):
1585 """This function generates a changegroup consisting of all the nodes
1590 """This function generates a changegroup consisting of all the nodes
1586 that are descendents of any of the bases, and ancestors of any of
1591 that are descendents of any of the bases, and ancestors of any of
1587 the heads.
1592 the heads.
1588
1593
1589 It is fairly complex as determining which filenodes and which
1594 It is fairly complex as determining which filenodes and which
1590 manifest nodes need to be included for the changeset to be complete
1595 manifest nodes need to be included for the changeset to be complete
1591 is non-trivial.
1596 is non-trivial.
1592
1597
1593 Another wrinkle is doing the reverse, figuring out which changeset in
1598 Another wrinkle is doing the reverse, figuring out which changeset in
1594 the changegroup a particular filenode or manifestnode belongs to.
1599 the changegroup a particular filenode or manifestnode belongs to.
1595
1600
1596 The caller can specify some nodes that must be included in the
1601 The caller can specify some nodes that must be included in the
1597 changegroup using the extranodes argument. It should be a dict
1602 changegroup using the extranodes argument. It should be a dict
1598 where the keys are the filenames (or 1 for the manifest), and the
1603 where the keys are the filenames (or 1 for the manifest), and the
1599 values are lists of (node, linknode) tuples, where node is a wanted
1604 values are lists of (node, linknode) tuples, where node is a wanted
1600 node and linknode is the changelog node that should be transmitted as
1605 node and linknode is the changelog node that should be transmitted as
1601 the linkrev.
1606 the linkrev.
1602 """
1607 """
1603
1608
1604 self.hook('preoutgoing', throw=True, source=source)
1609 self.hook('preoutgoing', throw=True, source=source)
1605
1610
1606 # Set up some initial variables
1611 # Set up some initial variables
1607 # Make it easy to refer to self.changelog
1612 # Make it easy to refer to self.changelog
1608 cl = self.changelog
1613 cl = self.changelog
1609 # msng is short for missing - compute the list of changesets in this
1614 # msng is short for missing - compute the list of changesets in this
1610 # changegroup.
1615 # changegroup.
1611 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1616 msng_cl_lst, bases, heads = cl.nodesbetween(bases, heads)
1612 self.changegroupinfo(msng_cl_lst, source)
1617 self.changegroupinfo(msng_cl_lst, source)
1613 # Some bases may turn out to be superfluous, and some heads may be
1618 # Some bases may turn out to be superfluous, and some heads may be
1614 # too. nodesbetween will return the minimal set of bases and heads
1619 # too. nodesbetween will return the minimal set of bases and heads
1615 # necessary to re-create the changegroup.
1620 # necessary to re-create the changegroup.
1616
1621
1617 # Known heads are the list of heads that it is assumed the recipient
1622 # Known heads are the list of heads that it is assumed the recipient
1618 # of this changegroup will know about.
1623 # of this changegroup will know about.
1619 knownheads = {}
1624 knownheads = {}
1620 # We assume that all parents of bases are known heads.
1625 # We assume that all parents of bases are known heads.
1621 for n in bases:
1626 for n in bases:
1622 for p in cl.parents(n):
1627 for p in cl.parents(n):
1623 if p != nullid:
1628 if p != nullid:
1624 knownheads[p] = 1
1629 knownheads[p] = 1
1625 knownheads = knownheads.keys()
1630 knownheads = knownheads.keys()
1626 if knownheads:
1631 if knownheads:
1627 # Now that we know what heads are known, we can compute which
1632 # Now that we know what heads are known, we can compute which
1628 # changesets are known. The recipient must know about all
1633 # changesets are known. The recipient must know about all
1629 # changesets required to reach the known heads from the null
1634 # changesets required to reach the known heads from the null
1630 # changeset.
1635 # changeset.
1631 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1636 has_cl_set, junk, junk = cl.nodesbetween(None, knownheads)
1632 junk = None
1637 junk = None
1633 # Transform the list into an ersatz set.
1638 # Transform the list into an ersatz set.
1634 has_cl_set = dict.fromkeys(has_cl_set)
1639 has_cl_set = dict.fromkeys(has_cl_set)
1635 else:
1640 else:
1636 # If there were no known heads, the recipient cannot be assumed to
1641 # If there were no known heads, the recipient cannot be assumed to
1637 # know about any changesets.
1642 # know about any changesets.
1638 has_cl_set = {}
1643 has_cl_set = {}
1639
1644
1640 # Make it easy to refer to self.manifest
1645 # Make it easy to refer to self.manifest
1641 mnfst = self.manifest
1646 mnfst = self.manifest
1642 # We don't know which manifests are missing yet
1647 # We don't know which manifests are missing yet
1643 msng_mnfst_set = {}
1648 msng_mnfst_set = {}
1644 # Nor do we know which filenodes are missing.
1649 # Nor do we know which filenodes are missing.
1645 msng_filenode_set = {}
1650 msng_filenode_set = {}
1646
1651
1647 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1652 junk = mnfst.index[mnfst.count() - 1] # Get around a bug in lazyindex
1648 junk = None
1653 junk = None
1649
1654
1650 # A changeset always belongs to itself, so the changenode lookup
1655 # A changeset always belongs to itself, so the changenode lookup
1651 # function for a changenode is identity.
1656 # function for a changenode is identity.
1652 def identity(x):
1657 def identity(x):
1653 return x
1658 return x
1654
1659
1655 # A function generating function. Sets up an environment for the
1660 # A function generating function. Sets up an environment for the
1656 # inner function.
1661 # inner function.
1657 def cmp_by_rev_func(revlog):
1662 def cmp_by_rev_func(revlog):
1658 # Compare two nodes by their revision number in the environment's
1663 # Compare two nodes by their revision number in the environment's
1659 # revision history. Since the revision number both represents the
1664 # revision history. Since the revision number both represents the
1660 # most efficient order to read the nodes in, and represents a
1665 # most efficient order to read the nodes in, and represents a
1661 # topological sorting of the nodes, this function is often useful.
1666 # topological sorting of the nodes, this function is often useful.
1662 def cmp_by_rev(a, b):
1667 def cmp_by_rev(a, b):
1663 return cmp(revlog.rev(a), revlog.rev(b))
1668 return cmp(revlog.rev(a), revlog.rev(b))
1664 return cmp_by_rev
1669 return cmp_by_rev
1665
1670
1666 # If we determine that a particular file or manifest node must be a
1671 # If we determine that a particular file or manifest node must be a
1667 # node that the recipient of the changegroup will already have, we can
1672 # node that the recipient of the changegroup will already have, we can
1668 # also assume the recipient will have all the parents. This function
1673 # also assume the recipient will have all the parents. This function
1669 # prunes them from the set of missing nodes.
1674 # prunes them from the set of missing nodes.
1670 def prune_parents(revlog, hasset, msngset):
1675 def prune_parents(revlog, hasset, msngset):
1671 haslst = hasset.keys()
1676 haslst = hasset.keys()
1672 haslst.sort(cmp_by_rev_func(revlog))
1677 haslst.sort(cmp_by_rev_func(revlog))
1673 for node in haslst:
1678 for node in haslst:
1674 parentlst = [p for p in revlog.parents(node) if p != nullid]
1679 parentlst = [p for p in revlog.parents(node) if p != nullid]
1675 while parentlst:
1680 while parentlst:
1676 n = parentlst.pop()
1681 n = parentlst.pop()
1677 if n not in hasset:
1682 if n not in hasset:
1678 hasset[n] = 1
1683 hasset[n] = 1
1679 p = [p for p in revlog.parents(n) if p != nullid]
1684 p = [p for p in revlog.parents(n) if p != nullid]
1680 parentlst.extend(p)
1685 parentlst.extend(p)
1681 for n in hasset:
1686 for n in hasset:
1682 msngset.pop(n, None)
1687 msngset.pop(n, None)
1683
1688
1684 # This is a function generating function used to set up an environment
1689 # This is a function generating function used to set up an environment
1685 # for the inner function to execute in.
1690 # for the inner function to execute in.
1686 def manifest_and_file_collector(changedfileset):
1691 def manifest_and_file_collector(changedfileset):
1687 # This is an information gathering function that gathers
1692 # This is an information gathering function that gathers
1688 # information from each changeset node that goes out as part of
1693 # information from each changeset node that goes out as part of
1689 # the changegroup. The information gathered is a list of which
1694 # the changegroup. The information gathered is a list of which
1690 # manifest nodes are potentially required (the recipient may
1695 # manifest nodes are potentially required (the recipient may
1691 # already have them) and total list of all files which were
1696 # already have them) and total list of all files which were
1692 # changed in any changeset in the changegroup.
1697 # changed in any changeset in the changegroup.
1693 #
1698 #
1694 # We also remember the first changenode we saw any manifest
1699 # We also remember the first changenode we saw any manifest
1695 # referenced by so we can later determine which changenode 'owns'
1700 # referenced by so we can later determine which changenode 'owns'
1696 # the manifest.
1701 # the manifest.
1697 def collect_manifests_and_files(clnode):
1702 def collect_manifests_and_files(clnode):
1698 c = cl.read(clnode)
1703 c = cl.read(clnode)
1699 for f in c[3]:
1704 for f in c[3]:
1700 # This is to make sure we only have one instance of each
1705 # This is to make sure we only have one instance of each
1701 # filename string for each filename.
1706 # filename string for each filename.
1702 changedfileset.setdefault(f, f)
1707 changedfileset.setdefault(f, f)
1703 msng_mnfst_set.setdefault(c[0], clnode)
1708 msng_mnfst_set.setdefault(c[0], clnode)
1704 return collect_manifests_and_files
1709 return collect_manifests_and_files
1705
1710
1706 # Figure out which manifest nodes (of the ones we think might be part
1711 # Figure out which manifest nodes (of the ones we think might be part
1707 # of the changegroup) the recipient must know about and remove them
1712 # of the changegroup) the recipient must know about and remove them
1708 # from the changegroup.
1713 # from the changegroup.
1709 def prune_manifests():
1714 def prune_manifests():
1710 has_mnfst_set = {}
1715 has_mnfst_set = {}
1711 for n in msng_mnfst_set:
1716 for n in msng_mnfst_set:
1712 # If a 'missing' manifest thinks it belongs to a changenode
1717 # If a 'missing' manifest thinks it belongs to a changenode
1713 # the recipient is assumed to have, obviously the recipient
1718 # the recipient is assumed to have, obviously the recipient
1714 # must have that manifest.
1719 # must have that manifest.
1715 linknode = cl.node(mnfst.linkrev(n))
1720 linknode = cl.node(mnfst.linkrev(n))
1716 if linknode in has_cl_set:
1721 if linknode in has_cl_set:
1717 has_mnfst_set[n] = 1
1722 has_mnfst_set[n] = 1
1718 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1723 prune_parents(mnfst, has_mnfst_set, msng_mnfst_set)
1719
1724
1720 # Use the information collected in collect_manifests_and_files to say
1725 # Use the information collected in collect_manifests_and_files to say
1721 # which changenode any manifestnode belongs to.
1726 # which changenode any manifestnode belongs to.
1722 def lookup_manifest_link(mnfstnode):
1727 def lookup_manifest_link(mnfstnode):
1723 return msng_mnfst_set[mnfstnode]
1728 return msng_mnfst_set[mnfstnode]
1724
1729
1725 # A function generating function that sets up the initial environment
1730 # A function generating function that sets up the initial environment
1726 # the inner function.
1731 # the inner function.
1727 def filenode_collector(changedfiles):
1732 def filenode_collector(changedfiles):
1728 next_rev = [0]
1733 next_rev = [0]
1729 # This gathers information from each manifestnode included in the
1734 # This gathers information from each manifestnode included in the
1730 # changegroup about which filenodes the manifest node references
1735 # changegroup about which filenodes the manifest node references
1731 # so we can include those in the changegroup too.
1736 # so we can include those in the changegroup too.
1732 #
1737 #
1733 # It also remembers which changenode each filenode belongs to. It
1738 # It also remembers which changenode each filenode belongs to. It
1734 # does this by assuming the a filenode belongs to the changenode
1739 # does this by assuming the a filenode belongs to the changenode
1735 # the first manifest that references it belongs to.
1740 # the first manifest that references it belongs to.
1736 def collect_msng_filenodes(mnfstnode):
1741 def collect_msng_filenodes(mnfstnode):
1737 r = mnfst.rev(mnfstnode)
1742 r = mnfst.rev(mnfstnode)
1738 if r == next_rev[0]:
1743 if r == next_rev[0]:
1739 # If the last rev we looked at was the one just previous,
1744 # If the last rev we looked at was the one just previous,
1740 # we only need to see a diff.
1745 # we only need to see a diff.
1741 deltamf = mnfst.readdelta(mnfstnode)
1746 deltamf = mnfst.readdelta(mnfstnode)
1742 # For each line in the delta
1747 # For each line in the delta
1743 for f, fnode in deltamf.items():
1748 for f, fnode in deltamf.items():
1744 f = changedfiles.get(f, None)
1749 f = changedfiles.get(f, None)
1745 # And if the file is in the list of files we care
1750 # And if the file is in the list of files we care
1746 # about.
1751 # about.
1747 if f is not None:
1752 if f is not None:
1748 # Get the changenode this manifest belongs to
1753 # Get the changenode this manifest belongs to
1749 clnode = msng_mnfst_set[mnfstnode]
1754 clnode = msng_mnfst_set[mnfstnode]
1750 # Create the set of filenodes for the file if
1755 # Create the set of filenodes for the file if
1751 # there isn't one already.
1756 # there isn't one already.
1752 ndset = msng_filenode_set.setdefault(f, {})
1757 ndset = msng_filenode_set.setdefault(f, {})
1753 # And set the filenode's changelog node to the
1758 # And set the filenode's changelog node to the
1754 # manifest's if it hasn't been set already.
1759 # manifest's if it hasn't been set already.
1755 ndset.setdefault(fnode, clnode)
1760 ndset.setdefault(fnode, clnode)
1756 else:
1761 else:
1757 # Otherwise we need a full manifest.
1762 # Otherwise we need a full manifest.
1758 m = mnfst.read(mnfstnode)
1763 m = mnfst.read(mnfstnode)
1759 # For every file in we care about.
1764 # For every file in we care about.
1760 for f in changedfiles:
1765 for f in changedfiles:
1761 fnode = m.get(f, None)
1766 fnode = m.get(f, None)
1762 # If it's in the manifest
1767 # If it's in the manifest
1763 if fnode is not None:
1768 if fnode is not None:
1764 # See comments above.
1769 # See comments above.
1765 clnode = msng_mnfst_set[mnfstnode]
1770 clnode = msng_mnfst_set[mnfstnode]
1766 ndset = msng_filenode_set.setdefault(f, {})
1771 ndset = msng_filenode_set.setdefault(f, {})
1767 ndset.setdefault(fnode, clnode)
1772 ndset.setdefault(fnode, clnode)
1768 # Remember the revision we hope to see next.
1773 # Remember the revision we hope to see next.
1769 next_rev[0] = r + 1
1774 next_rev[0] = r + 1
1770 return collect_msng_filenodes
1775 return collect_msng_filenodes
1771
1776
1772 # We have a list of filenodes we think we need for a file, lets remove
1777 # We have a list of filenodes we think we need for a file, lets remove
1773 # all those we now the recipient must have.
1778 # all those we now the recipient must have.
1774 def prune_filenodes(f, filerevlog):
1779 def prune_filenodes(f, filerevlog):
1775 msngset = msng_filenode_set[f]
1780 msngset = msng_filenode_set[f]
1776 hasset = {}
1781 hasset = {}
1777 # If a 'missing' filenode thinks it belongs to a changenode we
1782 # If a 'missing' filenode thinks it belongs to a changenode we
1778 # assume the recipient must have, then the recipient must have
1783 # assume the recipient must have, then the recipient must have
1779 # that filenode.
1784 # that filenode.
1780 for n in msngset:
1785 for n in msngset:
1781 clnode = cl.node(filerevlog.linkrev(n))
1786 clnode = cl.node(filerevlog.linkrev(n))
1782 if clnode in has_cl_set:
1787 if clnode in has_cl_set:
1783 hasset[n] = 1
1788 hasset[n] = 1
1784 prune_parents(filerevlog, hasset, msngset)
1789 prune_parents(filerevlog, hasset, msngset)
1785
1790
1786 # A function generator function that sets up the a context for the
1791 # A function generator function that sets up the a context for the
1787 # inner function.
1792 # inner function.
1788 def lookup_filenode_link_func(fname):
1793 def lookup_filenode_link_func(fname):
1789 msngset = msng_filenode_set[fname]
1794 msngset = msng_filenode_set[fname]
1790 # Lookup the changenode the filenode belongs to.
1795 # Lookup the changenode the filenode belongs to.
1791 def lookup_filenode_link(fnode):
1796 def lookup_filenode_link(fnode):
1792 return msngset[fnode]
1797 return msngset[fnode]
1793 return lookup_filenode_link
1798 return lookup_filenode_link
1794
1799
1795 # Add the nodes that were explicitly requested.
1800 # Add the nodes that were explicitly requested.
1796 def add_extra_nodes(name, nodes):
1801 def add_extra_nodes(name, nodes):
1797 if not extranodes or name not in extranodes:
1802 if not extranodes or name not in extranodes:
1798 return
1803 return
1799
1804
1800 for node, linknode in extranodes[name]:
1805 for node, linknode in extranodes[name]:
1801 if node not in nodes:
1806 if node not in nodes:
1802 nodes[node] = linknode
1807 nodes[node] = linknode
1803
1808
1804 # Now that we have all theses utility functions to help out and
1809 # Now that we have all theses utility functions to help out and
1805 # logically divide up the task, generate the group.
1810 # logically divide up the task, generate the group.
1806 def gengroup():
1811 def gengroup():
1807 # The set of changed files starts empty.
1812 # The set of changed files starts empty.
1808 changedfiles = {}
1813 changedfiles = {}
1809 # Create a changenode group generator that will call our functions
1814 # Create a changenode group generator that will call our functions
1810 # back to lookup the owning changenode and collect information.
1815 # back to lookup the owning changenode and collect information.
1811 group = cl.group(msng_cl_lst, identity,
1816 group = cl.group(msng_cl_lst, identity,
1812 manifest_and_file_collector(changedfiles))
1817 manifest_and_file_collector(changedfiles))
1813 for chnk in group:
1818 for chnk in group:
1814 yield chnk
1819 yield chnk
1815
1820
1816 # The list of manifests has been collected by the generator
1821 # The list of manifests has been collected by the generator
1817 # calling our functions back.
1822 # calling our functions back.
1818 prune_manifests()
1823 prune_manifests()
1819 add_extra_nodes(1, msng_mnfst_set)
1824 add_extra_nodes(1, msng_mnfst_set)
1820 msng_mnfst_lst = msng_mnfst_set.keys()
1825 msng_mnfst_lst = msng_mnfst_set.keys()
1821 # Sort the manifestnodes by revision number.
1826 # Sort the manifestnodes by revision number.
1822 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1827 msng_mnfst_lst.sort(cmp_by_rev_func(mnfst))
1823 # Create a generator for the manifestnodes that calls our lookup
1828 # Create a generator for the manifestnodes that calls our lookup
1824 # and data collection functions back.
1829 # and data collection functions back.
1825 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1830 group = mnfst.group(msng_mnfst_lst, lookup_manifest_link,
1826 filenode_collector(changedfiles))
1831 filenode_collector(changedfiles))
1827 for chnk in group:
1832 for chnk in group:
1828 yield chnk
1833 yield chnk
1829
1834
1830 # These are no longer needed, dereference and toss the memory for
1835 # These are no longer needed, dereference and toss the memory for
1831 # them.
1836 # them.
1832 msng_mnfst_lst = None
1837 msng_mnfst_lst = None
1833 msng_mnfst_set.clear()
1838 msng_mnfst_set.clear()
1834
1839
1835 if extranodes:
1840 if extranodes:
1836 for fname in extranodes:
1841 for fname in extranodes:
1837 if isinstance(fname, int):
1842 if isinstance(fname, int):
1838 continue
1843 continue
1839 add_extra_nodes(fname,
1844 add_extra_nodes(fname,
1840 msng_filenode_set.setdefault(fname, {}))
1845 msng_filenode_set.setdefault(fname, {}))
1841 changedfiles[fname] = 1
1846 changedfiles[fname] = 1
1842 changedfiles = changedfiles.keys()
1847 changedfiles = changedfiles.keys()
1843 changedfiles.sort()
1848 changedfiles.sort()
1844 # Go through all our files in order sorted by name.
1849 # Go through all our files in order sorted by name.
1845 for fname in changedfiles:
1850 for fname in changedfiles:
1846 filerevlog = self.file(fname)
1851 filerevlog = self.file(fname)
1847 if filerevlog.count() == 0:
1852 if filerevlog.count() == 0:
1848 raise util.Abort(_("empty or missing revlog for %s") % fname)
1853 raise util.Abort(_("empty or missing revlog for %s") % fname)
1849 # Toss out the filenodes that the recipient isn't really
1854 # Toss out the filenodes that the recipient isn't really
1850 # missing.
1855 # missing.
1851 if fname in msng_filenode_set:
1856 if fname in msng_filenode_set:
1852 prune_filenodes(fname, filerevlog)
1857 prune_filenodes(fname, filerevlog)
1853 msng_filenode_lst = msng_filenode_set[fname].keys()
1858 msng_filenode_lst = msng_filenode_set[fname].keys()
1854 else:
1859 else:
1855 msng_filenode_lst = []
1860 msng_filenode_lst = []
1856 # If any filenodes are left, generate the group for them,
1861 # If any filenodes are left, generate the group for them,
1857 # otherwise don't bother.
1862 # otherwise don't bother.
1858 if len(msng_filenode_lst) > 0:
1863 if len(msng_filenode_lst) > 0:
1859 yield changegroup.chunkheader(len(fname))
1864 yield changegroup.chunkheader(len(fname))
1860 yield fname
1865 yield fname
1861 # Sort the filenodes by their revision #
1866 # Sort the filenodes by their revision #
1862 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1867 msng_filenode_lst.sort(cmp_by_rev_func(filerevlog))
1863 # Create a group generator and only pass in a changenode
1868 # Create a group generator and only pass in a changenode
1864 # lookup function as we need to collect no information
1869 # lookup function as we need to collect no information
1865 # from filenodes.
1870 # from filenodes.
1866 group = filerevlog.group(msng_filenode_lst,
1871 group = filerevlog.group(msng_filenode_lst,
1867 lookup_filenode_link_func(fname))
1872 lookup_filenode_link_func(fname))
1868 for chnk in group:
1873 for chnk in group:
1869 yield chnk
1874 yield chnk
1870 if fname in msng_filenode_set:
1875 if fname in msng_filenode_set:
1871 # Don't need this anymore, toss it to free memory.
1876 # Don't need this anymore, toss it to free memory.
1872 del msng_filenode_set[fname]
1877 del msng_filenode_set[fname]
1873 # Signal that no more groups are left.
1878 # Signal that no more groups are left.
1874 yield changegroup.closechunk()
1879 yield changegroup.closechunk()
1875
1880
1876 if msng_cl_lst:
1881 if msng_cl_lst:
1877 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1882 self.hook('outgoing', node=hex(msng_cl_lst[0]), source=source)
1878
1883
1879 return util.chunkbuffer(gengroup())
1884 return util.chunkbuffer(gengroup())
1880
1885
1881 def changegroup(self, basenodes, source):
1886 def changegroup(self, basenodes, source):
1882 """Generate a changegroup of all nodes that we have that a recipient
1887 """Generate a changegroup of all nodes that we have that a recipient
1883 doesn't.
1888 doesn't.
1884
1889
1885 This is much easier than the previous function as we can assume that
1890 This is much easier than the previous function as we can assume that
1886 the recipient has any changenode we aren't sending them."""
1891 the recipient has any changenode we aren't sending them."""
1887
1892
1888 self.hook('preoutgoing', throw=True, source=source)
1893 self.hook('preoutgoing', throw=True, source=source)
1889
1894
1890 cl = self.changelog
1895 cl = self.changelog
1891 nodes = cl.nodesbetween(basenodes, None)[0]
1896 nodes = cl.nodesbetween(basenodes, None)[0]
1892 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1897 revset = dict.fromkeys([cl.rev(n) for n in nodes])
1893 self.changegroupinfo(nodes, source)
1898 self.changegroupinfo(nodes, source)
1894
1899
1895 def identity(x):
1900 def identity(x):
1896 return x
1901 return x
1897
1902
1898 def gennodelst(revlog):
1903 def gennodelst(revlog):
1899 for r in xrange(0, revlog.count()):
1904 for r in xrange(0, revlog.count()):
1900 n = revlog.node(r)
1905 n = revlog.node(r)
1901 if revlog.linkrev(n) in revset:
1906 if revlog.linkrev(n) in revset:
1902 yield n
1907 yield n
1903
1908
1904 def changed_file_collector(changedfileset):
1909 def changed_file_collector(changedfileset):
1905 def collect_changed_files(clnode):
1910 def collect_changed_files(clnode):
1906 c = cl.read(clnode)
1911 c = cl.read(clnode)
1907 for fname in c[3]:
1912 for fname in c[3]:
1908 changedfileset[fname] = 1
1913 changedfileset[fname] = 1
1909 return collect_changed_files
1914 return collect_changed_files
1910
1915
1911 def lookuprevlink_func(revlog):
1916 def lookuprevlink_func(revlog):
1912 def lookuprevlink(n):
1917 def lookuprevlink(n):
1913 return cl.node(revlog.linkrev(n))
1918 return cl.node(revlog.linkrev(n))
1914 return lookuprevlink
1919 return lookuprevlink
1915
1920
1916 def gengroup():
1921 def gengroup():
1917 # construct a list of all changed files
1922 # construct a list of all changed files
1918 changedfiles = {}
1923 changedfiles = {}
1919
1924
1920 for chnk in cl.group(nodes, identity,
1925 for chnk in cl.group(nodes, identity,
1921 changed_file_collector(changedfiles)):
1926 changed_file_collector(changedfiles)):
1922 yield chnk
1927 yield chnk
1923 changedfiles = changedfiles.keys()
1928 changedfiles = changedfiles.keys()
1924 changedfiles.sort()
1929 changedfiles.sort()
1925
1930
1926 mnfst = self.manifest
1931 mnfst = self.manifest
1927 nodeiter = gennodelst(mnfst)
1932 nodeiter = gennodelst(mnfst)
1928 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1933 for chnk in mnfst.group(nodeiter, lookuprevlink_func(mnfst)):
1929 yield chnk
1934 yield chnk
1930
1935
1931 for fname in changedfiles:
1936 for fname in changedfiles:
1932 filerevlog = self.file(fname)
1937 filerevlog = self.file(fname)
1933 if filerevlog.count() == 0:
1938 if filerevlog.count() == 0:
1934 raise util.Abort(_("empty or missing revlog for %s") % fname)
1939 raise util.Abort(_("empty or missing revlog for %s") % fname)
1935 nodeiter = gennodelst(filerevlog)
1940 nodeiter = gennodelst(filerevlog)
1936 nodeiter = list(nodeiter)
1941 nodeiter = list(nodeiter)
1937 if nodeiter:
1942 if nodeiter:
1938 yield changegroup.chunkheader(len(fname))
1943 yield changegroup.chunkheader(len(fname))
1939 yield fname
1944 yield fname
1940 lookup = lookuprevlink_func(filerevlog)
1945 lookup = lookuprevlink_func(filerevlog)
1941 for chnk in filerevlog.group(nodeiter, lookup):
1946 for chnk in filerevlog.group(nodeiter, lookup):
1942 yield chnk
1947 yield chnk
1943
1948
1944 yield changegroup.closechunk()
1949 yield changegroup.closechunk()
1945
1950
1946 if nodes:
1951 if nodes:
1947 self.hook('outgoing', node=hex(nodes[0]), source=source)
1952 self.hook('outgoing', node=hex(nodes[0]), source=source)
1948
1953
1949 return util.chunkbuffer(gengroup())
1954 return util.chunkbuffer(gengroup())
1950
1955
1951 def addchangegroup(self, source, srctype, url, emptyok=False):
1956 def addchangegroup(self, source, srctype, url, emptyok=False):
1952 """add changegroup to repo.
1957 """add changegroup to repo.
1953
1958
1954 return values:
1959 return values:
1955 - nothing changed or no source: 0
1960 - nothing changed or no source: 0
1956 - more heads than before: 1+added heads (2..n)
1961 - more heads than before: 1+added heads (2..n)
1957 - less heads than before: -1-removed heads (-2..-n)
1962 - less heads than before: -1-removed heads (-2..-n)
1958 - number of heads stays the same: 1
1963 - number of heads stays the same: 1
1959 """
1964 """
1960 def csmap(x):
1965 def csmap(x):
1961 self.ui.debug(_("add changeset %s\n") % short(x))
1966 self.ui.debug(_("add changeset %s\n") % short(x))
1962 return cl.count()
1967 return cl.count()
1963
1968
1964 def revmap(x):
1969 def revmap(x):
1965 return cl.rev(x)
1970 return cl.rev(x)
1966
1971
1967 if not source:
1972 if not source:
1968 return 0
1973 return 0
1969
1974
1970 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1975 self.hook('prechangegroup', throw=True, source=srctype, url=url)
1971
1976
1972 changesets = files = revisions = 0
1977 changesets = files = revisions = 0
1973
1978
1974 # write changelog data to temp files so concurrent readers will not see
1979 # write changelog data to temp files so concurrent readers will not see
1975 # inconsistent view
1980 # inconsistent view
1976 cl = self.changelog
1981 cl = self.changelog
1977 cl.delayupdate()
1982 cl.delayupdate()
1978 oldheads = len(cl.heads())
1983 oldheads = len(cl.heads())
1979
1984
1980 tr = self.transaction()
1985 tr = self.transaction()
1981 try:
1986 try:
1982 trp = weakref.proxy(tr)
1987 trp = weakref.proxy(tr)
1983 # pull off the changeset group
1988 # pull off the changeset group
1984 self.ui.status(_("adding changesets\n"))
1989 self.ui.status(_("adding changesets\n"))
1985 cor = cl.count() - 1
1990 cor = cl.count() - 1
1986 chunkiter = changegroup.chunkiter(source)
1991 chunkiter = changegroup.chunkiter(source)
1987 if cl.addgroup(chunkiter, csmap, trp, 1) is None and not emptyok:
1992 if cl.addgroup(chunkiter, csmap, trp, 1) is None and not emptyok:
1988 raise util.Abort(_("received changelog group is empty"))
1993 raise util.Abort(_("received changelog group is empty"))
1989 cnr = cl.count() - 1
1994 cnr = cl.count() - 1
1990 changesets = cnr - cor
1995 changesets = cnr - cor
1991
1996
1992 # pull off the manifest group
1997 # pull off the manifest group
1993 self.ui.status(_("adding manifests\n"))
1998 self.ui.status(_("adding manifests\n"))
1994 chunkiter = changegroup.chunkiter(source)
1999 chunkiter = changegroup.chunkiter(source)
1995 # no need to check for empty manifest group here:
2000 # no need to check for empty manifest group here:
1996 # if the result of the merge of 1 and 2 is the same in 3 and 4,
2001 # if the result of the merge of 1 and 2 is the same in 3 and 4,
1997 # no new manifest will be created and the manifest group will
2002 # no new manifest will be created and the manifest group will
1998 # be empty during the pull
2003 # be empty during the pull
1999 self.manifest.addgroup(chunkiter, revmap, trp)
2004 self.manifest.addgroup(chunkiter, revmap, trp)
2000
2005
2001 # process the files
2006 # process the files
2002 self.ui.status(_("adding file changes\n"))
2007 self.ui.status(_("adding file changes\n"))
2003 while 1:
2008 while 1:
2004 f = changegroup.getchunk(source)
2009 f = changegroup.getchunk(source)
2005 if not f:
2010 if not f:
2006 break
2011 break
2007 self.ui.debug(_("adding %s revisions\n") % f)
2012 self.ui.debug(_("adding %s revisions\n") % f)
2008 fl = self.file(f)
2013 fl = self.file(f)
2009 o = fl.count()
2014 o = fl.count()
2010 chunkiter = changegroup.chunkiter(source)
2015 chunkiter = changegroup.chunkiter(source)
2011 if fl.addgroup(chunkiter, revmap, trp) is None:
2016 if fl.addgroup(chunkiter, revmap, trp) is None:
2012 raise util.Abort(_("received file revlog group is empty"))
2017 raise util.Abort(_("received file revlog group is empty"))
2013 revisions += fl.count() - o
2018 revisions += fl.count() - o
2014 files += 1
2019 files += 1
2015
2020
2016 # make changelog see real files again
2021 # make changelog see real files again
2017 cl.finalize(trp)
2022 cl.finalize(trp)
2018
2023
2019 newheads = len(self.changelog.heads())
2024 newheads = len(self.changelog.heads())
2020 heads = ""
2025 heads = ""
2021 if oldheads and newheads != oldheads:
2026 if oldheads and newheads != oldheads:
2022 heads = _(" (%+d heads)") % (newheads - oldheads)
2027 heads = _(" (%+d heads)") % (newheads - oldheads)
2023
2028
2024 self.ui.status(_("added %d changesets"
2029 self.ui.status(_("added %d changesets"
2025 " with %d changes to %d files%s\n")
2030 " with %d changes to %d files%s\n")
2026 % (changesets, revisions, files, heads))
2031 % (changesets, revisions, files, heads))
2027
2032
2028 if changesets > 0:
2033 if changesets > 0:
2029 self.hook('pretxnchangegroup', throw=True,
2034 self.hook('pretxnchangegroup', throw=True,
2030 node=hex(self.changelog.node(cor+1)), source=srctype,
2035 node=hex(self.changelog.node(cor+1)), source=srctype,
2031 url=url)
2036 url=url)
2032
2037
2033 tr.close()
2038 tr.close()
2034 finally:
2039 finally:
2035 del tr
2040 del tr
2036
2041
2037 if changesets > 0:
2042 if changesets > 0:
2038 # forcefully update the on-disk branch cache
2043 # forcefully update the on-disk branch cache
2039 self.ui.debug(_("updating the branch cache\n"))
2044 self.ui.debug(_("updating the branch cache\n"))
2040 self.branchtags()
2045 self.branchtags()
2041 self.hook("changegroup", node=hex(self.changelog.node(cor+1)),
2046 self.hook("changegroup", node=hex(self.changelog.node(cor+1)),
2042 source=srctype, url=url)
2047 source=srctype, url=url)
2043
2048
2044 for i in xrange(cor + 1, cnr + 1):
2049 for i in xrange(cor + 1, cnr + 1):
2045 self.hook("incoming", node=hex(self.changelog.node(i)),
2050 self.hook("incoming", node=hex(self.changelog.node(i)),
2046 source=srctype, url=url)
2051 source=srctype, url=url)
2047
2052
2048 # never return 0 here:
2053 # never return 0 here:
2049 if newheads < oldheads:
2054 if newheads < oldheads:
2050 return newheads - oldheads - 1
2055 return newheads - oldheads - 1
2051 else:
2056 else:
2052 return newheads - oldheads + 1
2057 return newheads - oldheads + 1
2053
2058
2054
2059
2055 def stream_in(self, remote):
2060 def stream_in(self, remote):
2056 fp = remote.stream_out()
2061 fp = remote.stream_out()
2057 l = fp.readline()
2062 l = fp.readline()
2058 try:
2063 try:
2059 resp = int(l)
2064 resp = int(l)
2060 except ValueError:
2065 except ValueError:
2061 raise util.UnexpectedOutput(
2066 raise util.UnexpectedOutput(
2062 _('Unexpected response from remote server:'), l)
2067 _('Unexpected response from remote server:'), l)
2063 if resp == 1:
2068 if resp == 1:
2064 raise util.Abort(_('operation forbidden by server'))
2069 raise util.Abort(_('operation forbidden by server'))
2065 elif resp == 2:
2070 elif resp == 2:
2066 raise util.Abort(_('locking the remote repository failed'))
2071 raise util.Abort(_('locking the remote repository failed'))
2067 elif resp != 0:
2072 elif resp != 0:
2068 raise util.Abort(_('the server sent an unknown error code'))
2073 raise util.Abort(_('the server sent an unknown error code'))
2069 self.ui.status(_('streaming all changes\n'))
2074 self.ui.status(_('streaming all changes\n'))
2070 l = fp.readline()
2075 l = fp.readline()
2071 try:
2076 try:
2072 total_files, total_bytes = map(int, l.split(' ', 1))
2077 total_files, total_bytes = map(int, l.split(' ', 1))
2073 except ValueError, TypeError:
2078 except ValueError, TypeError:
2074 raise util.UnexpectedOutput(
2079 raise util.UnexpectedOutput(
2075 _('Unexpected response from remote server:'), l)
2080 _('Unexpected response from remote server:'), l)
2076 self.ui.status(_('%d files to transfer, %s of data\n') %
2081 self.ui.status(_('%d files to transfer, %s of data\n') %
2077 (total_files, util.bytecount(total_bytes)))
2082 (total_files, util.bytecount(total_bytes)))
2078 start = time.time()
2083 start = time.time()
2079 for i in xrange(total_files):
2084 for i in xrange(total_files):
2080 # XXX doesn't support '\n' or '\r' in filenames
2085 # XXX doesn't support '\n' or '\r' in filenames
2081 l = fp.readline()
2086 l = fp.readline()
2082 try:
2087 try:
2083 name, size = l.split('\0', 1)
2088 name, size = l.split('\0', 1)
2084 size = int(size)
2089 size = int(size)
2085 except ValueError, TypeError:
2090 except ValueError, TypeError:
2086 raise util.UnexpectedOutput(
2091 raise util.UnexpectedOutput(
2087 _('Unexpected response from remote server:'), l)
2092 _('Unexpected response from remote server:'), l)
2088 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
2093 self.ui.debug('adding %s (%s)\n' % (name, util.bytecount(size)))
2089 ofp = self.sopener(name, 'w')
2094 ofp = self.sopener(name, 'w')
2090 for chunk in util.filechunkiter(fp, limit=size):
2095 for chunk in util.filechunkiter(fp, limit=size):
2091 ofp.write(chunk)
2096 ofp.write(chunk)
2092 ofp.close()
2097 ofp.close()
2093 elapsed = time.time() - start
2098 elapsed = time.time() - start
2094 if elapsed <= 0:
2099 if elapsed <= 0:
2095 elapsed = 0.001
2100 elapsed = 0.001
2096 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
2101 self.ui.status(_('transferred %s in %.1f seconds (%s/sec)\n') %
2097 (util.bytecount(total_bytes), elapsed,
2102 (util.bytecount(total_bytes), elapsed,
2098 util.bytecount(total_bytes / elapsed)))
2103 util.bytecount(total_bytes / elapsed)))
2099 self.invalidate()
2104 self.invalidate()
2100 return len(self.heads()) + 1
2105 return len(self.heads()) + 1
2101
2106
2102 def clone(self, remote, heads=[], stream=False):
2107 def clone(self, remote, heads=[], stream=False):
2103 '''clone remote repository.
2108 '''clone remote repository.
2104
2109
2105 keyword arguments:
2110 keyword arguments:
2106 heads: list of revs to clone (forces use of pull)
2111 heads: list of revs to clone (forces use of pull)
2107 stream: use streaming clone if possible'''
2112 stream: use streaming clone if possible'''
2108
2113
2109 # now, all clients that can request uncompressed clones can
2114 # now, all clients that can request uncompressed clones can
2110 # read repo formats supported by all servers that can serve
2115 # read repo formats supported by all servers that can serve
2111 # them.
2116 # them.
2112
2117
2113 # if revlog format changes, client will have to check version
2118 # if revlog format changes, client will have to check version
2114 # and format flags on "stream" capability, and use
2119 # and format flags on "stream" capability, and use
2115 # uncompressed only if compatible.
2120 # uncompressed only if compatible.
2116
2121
2117 if stream and not heads and remote.capable('stream'):
2122 if stream and not heads and remote.capable('stream'):
2118 return self.stream_in(remote)
2123 return self.stream_in(remote)
2119 return self.pull(remote, heads)
2124 return self.pull(remote, heads)
2120
2125
2121 # used to avoid circular references so destructors work
2126 # used to avoid circular references so destructors work
2122 def aftertrans(files):
2127 def aftertrans(files):
2123 renamefiles = [tuple(t) for t in files]
2128 renamefiles = [tuple(t) for t in files]
2124 def a():
2129 def a():
2125 for src, dest in renamefiles:
2130 for src, dest in renamefiles:
2126 util.rename(src, dest)
2131 util.rename(src, dest)
2127 return a
2132 return a
2128
2133
2129 def instance(ui, path, create):
2134 def instance(ui, path, create):
2130 return localrepository(ui, util.drop_scheme('file', path), create)
2135 return localrepository(ui, util.drop_scheme('file', path), create)
2131
2136
2132 def islocal(path):
2137 def islocal(path):
2133 return True
2138 return True
@@ -1,146 +1,146 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 # @ (34) head
3 # @ (34) head
4 # |
4 # |
5 # | o (33) head
5 # | o (33) head
6 # | |
6 # | |
7 # o | (32) expand
7 # o | (32) expand
8 # |\ \
8 # |\ \
9 # | o \ (31) expand
9 # | o \ (31) expand
10 # | |\ \
10 # | |\ \
11 # | | o \ (30) expand
11 # | | o \ (30) expand
12 # | | |\ \
12 # | | |\ \
13 # | | | o | (29) regular commit
13 # | | | o | (29) regular commit
14 # | | | | |
14 # | | | | |
15 # | | o | | (28) merge zero known
15 # | | o | | (28) merge zero known
16 # | | |\ \ \
16 # | | |\ \ \
17 # o | | | | | (27) collapse
17 # o | | | | | (27) collapse
18 # |/ / / / /
18 # |/ / / / /
19 # | | o---+ (26) merge one known; far right
19 # | | o---+ (26) merge one known; far right
20 # | | | | |
20 # | | | | |
21 # +---o | | (25) merge one known; far left
21 # +---o | | (25) merge one known; far left
22 # | | | | |
22 # | | | | |
23 # | | o | | (24) merge one known; immediate right
23 # | | o | | (24) merge one known; immediate right
24 # | | |\| |
24 # | | |\| |
25 # | | o | | (23) merge one known; immediate left
25 # | | o | | (23) merge one known; immediate left
26 # | |/| | |
26 # | |/| | |
27 # +---o---+ (22) merge two known; one far left, one far right
27 # +---o---+ (22) merge two known; one far left, one far right
28 # | | / /
28 # | | / /
29 # o | | | (21) expand
29 # o | | | (21) expand
30 # |\ \ \ \
30 # |\ \ \ \
31 # | o---+-+ (20) merge two known; two far right
31 # | o---+-+ (20) merge two known; two far right
32 # | / / /
32 # | / / /
33 # o | | | (19) expand
33 # o | | | (19) expand
34 # |\ \ \ \
34 # |\ \ \ \
35 # +---+---o (18) merge two known; two far left
35 # +---+---o (18) merge two known; two far left
36 # | | | |
36 # | | | |
37 # | o | | (17) expand
37 # | o | | (17) expand
38 # | |\ \ \
38 # | |\ \ \
39 # | | o---+ (16) merge two known; one immediate right, one near right
39 # | | o---+ (16) merge two known; one immediate right, one near right
40 # | | |/ /
40 # | | |/ /
41 # o | | | (15) expand
41 # o | | | (15) expand
42 # |\ \ \ \
42 # |\ \ \ \
43 # | o-----+ (14) merge two known; one immediate right, one far right
43 # | o-----+ (14) merge two known; one immediate right, one far right
44 # | |/ / /
44 # | |/ / /
45 # o | | | (13) expand
45 # o | | | (13) expand
46 # |\ \ \ \
46 # |\ \ \ \
47 # +---o | | (12) merge two known; one immediate right, one far left
47 # +---o | | (12) merge two known; one immediate right, one far left
48 # | | |/ /
48 # | | |/ /
49 # | o | | (11) expand
49 # | o | | (11) expand
50 # | |\ \ \
50 # | |\ \ \
51 # | | o---+ (10) merge two known; one immediate left, one near right
51 # | | o---+ (10) merge two known; one immediate left, one near right
52 # | |/ / /
52 # | |/ / /
53 # o | | | (9) expand
53 # o | | | (9) expand
54 # |\ \ \ \
54 # |\ \ \ \
55 # | o-----+ (8) merge two known; one immediate left, one far right
55 # | o-----+ (8) merge two known; one immediate left, one far right
56 # |/ / / /
56 # |/ / / /
57 # o | | | (7) expand
57 # o | | | (7) expand
58 # |\ \ \ \
58 # |\ \ \ \
59 # +---o | | (6) merge two known; one immediate left, one far left
59 # +---o | | (6) merge two known; one immediate left, one far left
60 # | |/ / /
60 # | |/ / /
61 # | o | | (5) expand
61 # | o | | (5) expand
62 # | |\ \ \
62 # | |\ \ \
63 # | | o | | (4) merge two known; one immediate left, one immediate right
63 # | | o | | (4) merge two known; one immediate left, one immediate right
64 # | |/|/ /
64 # | |/|/ /
65 # | o / / (3) collapse
65 # | o / / (3) collapse
66 # |/ / /
66 # |/ / /
67 # o / / (2) collapse
67 # o / / (2) collapse
68 # |/ /
68 # |/ /
69 # o / (1) collapse
69 # o / (1) collapse
70 # |/
70 # |/
71 # o (0) root
71 # o (0) root
72
72
73 set -e
73 set -e
74
74
75 commit()
75 commit()
76 {
76 {
77 rev=$1
77 rev=$1
78 msg=$2
78 msg=$2
79 shift 2
79 shift 2
80 if [ "$#" -gt 0 ]; then
80 if [ "$#" -gt 0 ]; then
81 hg debugsetparents "$@"
81 hg debugsetparents "$@"
82 fi
82 fi
83 echo $rev > $rev
83 echo $rev > $rev
84 hg add $rev
84 hg add $rev
85 hg ci -d "$rev 0" -m "($rev) $msg" $rev
85 hg rawcommit -q -d "$rev 0" -m "($rev) $msg" $rev
86 }
86 }
87
87
88 echo "[extensions]" >> $HGRCPATH
88 echo "[extensions]" >> $HGRCPATH
89 echo "graphlog=" >> $HGRCPATH
89 echo "graphlog=" >> $HGRCPATH
90
90
91 echo % init
91 echo % init
92 hg init repo
92 hg init repo
93
93
94 cd repo
94 cd repo
95
95
96 echo % empty repo
96 echo % empty repo
97 hg glog
97 hg glog
98
98
99 echo % building tree
99 echo % building tree
100 commit 0 "root"
100 commit 0 "root"
101 commit 1 "collapse" 0
101 commit 1 "collapse" 0
102 commit 2 "collapse" 1
102 commit 2 "collapse" 1
103 commit 3 "collapse" 2
103 commit 3 "collapse" 2
104 commit 4 "merge two known; one immediate left, one immediate right" 1 3
104 commit 4 "merge two known; one immediate left, one immediate right" 1 3
105 commit 5 "expand" 3 4
105 commit 5 "expand" 3 4
106 commit 6 "merge two known; one immediate left, one far left" 2 5
106 commit 6 "merge two known; one immediate left, one far left" 2 5
107 commit 7 "expand" 2 5
107 commit 7 "expand" 2 5
108 commit 8 "merge two known; one immediate left, one far right" 0 7
108 commit 8 "merge two known; one immediate left, one far right" 0 7
109 commit 9 "expand" 7 8
109 commit 9 "expand" 7 8
110 commit 10 "merge two known; one immediate left, one near right" 0 6
110 commit 10 "merge two known; one immediate left, one near right" 0 6
111 commit 11 "expand" 6 10
111 commit 11 "expand" 6 10
112 commit 12 "merge two known; one immediate right, one far left" 1 9
112 commit 12 "merge two known; one immediate right, one far left" 1 9
113 commit 13 "expand" 9 11
113 commit 13 "expand" 9 11
114 commit 14 "merge two known; one immediate right, one far right" 0 12
114 commit 14 "merge two known; one immediate right, one far right" 0 12
115 commit 15 "expand" 13 14
115 commit 15 "expand" 13 14
116 commit 16 "merge two known; one immediate right, one near right" 0 1
116 commit 16 "merge two known; one immediate right, one near right" 0 1
117 commit 17 "expand" 12 16
117 commit 17 "expand" 12 16
118 commit 18 "merge two known; two far left" 1 15
118 commit 18 "merge two known; two far left" 1 15
119 commit 19 "expand" 15 17
119 commit 19 "expand" 15 17
120 commit 20 "merge two known; two far right" 0 18
120 commit 20 "merge two known; two far right" 0 18
121 commit 21 "expand" 19 20
121 commit 21 "expand" 19 20
122 commit 22 "merge two known; one far left, one far right" 18 21
122 commit 22 "merge two known; one far left, one far right" 18 21
123 commit 23 "merge one known; immediate left" 1 22
123 commit 23 "merge one known; immediate left" 1 22
124 commit 24 "merge one known; immediate right" 0 23
124 commit 24 "merge one known; immediate right" 0 23
125 commit 25 "merge one known; far left" 21 24
125 commit 25 "merge one known; far left" 21 24
126 commit 26 "merge one known; far right" 18 25
126 commit 26 "merge one known; far right" 18 25
127 commit 27 "collapse" 21
127 commit 27 "collapse" 21
128 commit 28 "merge zero known" 1 26
128 commit 28 "merge zero known" 1 26
129 commit 29 "regular commit" 0
129 commit 29 "regular commit" 0
130 commit 30 "expand" 28 29
130 commit 30 "expand" 28 29
131 commit 31 "expand" 21 30
131 commit 31 "expand" 21 30
132 commit 32 "expand" 27 31
132 commit 32 "expand" 27 31
133 commit 33 "head" 18
133 commit 33 "head" 18
134 commit 34 "head" 32
134 commit 34 "head" 32
135
135
136 echo % glog -q
136 echo % glog -q
137 hg glog -q
137 hg glog -q
138
138
139 echo % glog
139 echo % glog
140 hg glog
140 hg glog
141
141
142 echo % file glog
142 echo % file glog
143 hg glog 5
143 hg glog 5
144
144
145 echo % unused arguments
145 echo % unused arguments
146 hg glog -q foo bar || echo failed
146 hg glog -q foo bar || echo failed
@@ -1,328 +1,358 b''
1 % init
1 % init
2 % empty repo
2 % empty repo
3 % building tree
3 % building tree
4 created new head
4 (the rawcommit command is deprecated)
5 created new head
5 (the rawcommit command is deprecated)
6 created new head
6 (the rawcommit command is deprecated)
7 created new head
7 (the rawcommit command is deprecated)
8 created new head
8 (the rawcommit command is deprecated)
9 (the rawcommit command is deprecated)
10 (the rawcommit command is deprecated)
11 (the rawcommit command is deprecated)
12 (the rawcommit command is deprecated)
13 (the rawcommit command is deprecated)
14 (the rawcommit command is deprecated)
15 (the rawcommit command is deprecated)
16 (the rawcommit command is deprecated)
17 (the rawcommit command is deprecated)
18 (the rawcommit command is deprecated)
19 (the rawcommit command is deprecated)
20 (the rawcommit command is deprecated)
21 (the rawcommit command is deprecated)
22 (the rawcommit command is deprecated)
23 (the rawcommit command is deprecated)
24 (the rawcommit command is deprecated)
25 (the rawcommit command is deprecated)
26 (the rawcommit command is deprecated)
27 (the rawcommit command is deprecated)
28 (the rawcommit command is deprecated)
29 (the rawcommit command is deprecated)
30 (the rawcommit command is deprecated)
31 (the rawcommit command is deprecated)
32 (the rawcommit command is deprecated)
33 (the rawcommit command is deprecated)
34 (the rawcommit command is deprecated)
35 (the rawcommit command is deprecated)
36 (the rawcommit command is deprecated)
37 (the rawcommit command is deprecated)
38 (the rawcommit command is deprecated)
9 % glog -q
39 % glog -q
10 @ 34:0eed7cd895e0
40 @ 34:0eed7cd895e0
11 |
41 |
12 | o 33:2e9d1b521374
42 | o 33:2e9d1b521374
13 | |
43 | |
14 o | 32:77f7d8438a3c
44 o | 32:77f7d8438a3c
15 |\ \
45 |\ \
16 | o \ 31:82ee55204a79
46 | o \ 31:82ee55204a79
17 | |\ \
47 | |\ \
18 | | o \ 30:777dfc428649
48 | | o \ 30:777dfc428649
19 | | |\ \
49 | | |\ \
20 | | | o | 29:f8e7fee63353
50 | | | o | 29:f8e7fee63353
21 | | | | |
51 | | | | |
22 | | o | | 28:4b6e9bd48cf9
52 | | o | | 28:4b6e9bd48cf9
23 | | |\ \ \
53 | | |\ \ \
24 o | | | | | 27:e9e08174cd30
54 o | | | | | 27:e9e08174cd30
25 |/ / / / /
55 |/ / / / /
26 | | o---+ 26:720dc079a855
56 | | o---+ 26:720dc079a855
27 | | | | |
57 | | | | |
28 +---o | | 25:9d4ed048d013
58 +---o | | 25:9d4ed048d013
29 | | | | |
59 | | | | |
30 | | o | | 24:4a68967db00d
60 | | o | | 24:4a68967db00d
31 | | |\| |
61 | | |\| |
32 | | o | | 23:bc31393cabdf
62 | | o | | 23:bc31393cabdf
33 | |/| | |
63 | |/| | |
34 +---o---+ 22:a37f2ea6ebc6
64 +---o---+ 22:a37f2ea6ebc6
35 | | / /
65 | | / /
36 o | | | 21:e758e8f4ace9
66 o | | | 21:e758e8f4ace9
37 |\ \ \ \
67 |\ \ \ \
38 | o---+-+ 20:aeccadad74b4
68 | o---+-+ 20:aeccadad74b4
39 | / / /
69 | / / /
40 o | | | 19:138069b5dad7
70 o | | | 19:138069b5dad7
41 |\ \ \ \
71 |\ \ \ \
42 +---+---o 18:5a8c9a29ef81
72 +---+---o 18:5a8c9a29ef81
43 | | | |
73 | | | |
44 | o | | 17:43e52b935494
74 | o | | 17:43e52b935494
45 | |\ \ \
75 | |\ \ \
46 | | o---+ 16:449a2f9562a4
76 | | o---+ 16:449a2f9562a4
47 | | |/ /
77 | | |/ /
48 o | | | 15:c0b4283d4c1d
78 o | | | 15:c0b4283d4c1d
49 |\ \ \ \
79 |\ \ \ \
50 | o-----+ 14:9d533950abf0
80 | o-----+ 14:9d533950abf0
51 | |/ / /
81 | |/ / /
52 o | | | 13:c39d0a2b8165
82 o | | | 13:c39d0a2b8165
53 |\ \ \ \
83 |\ \ \ \
54 +---o | | 12:74dc7aea4494
84 +---o | | 12:74dc7aea4494
55 | | |/ /
85 | | |/ /
56 | o | | 11:c3c395dd8b98
86 | o | | 11:c3c395dd8b98
57 | |\ \ \
87 | |\ \ \
58 | | o---+ 10:8094c50149ef
88 | | o---+ 10:8094c50149ef
59 | |/ / /
89 | |/ / /
60 o | | | 9:79ab1812f961
90 o | | | 9:79ab1812f961
61 |\ \ \ \
91 |\ \ \ \
62 | o-----+ 8:d7aa38594334
92 | o-----+ 8:d7aa38594334
63 |/ / / /
93 |/ / / /
64 o | | | 7:699392d1259e
94 o | | | 7:699392d1259e
65 |\ \ \ \
95 |\ \ \ \
66 +---o | | 6:0ca7c061cf45
96 +---o | | 6:0ca7c061cf45
67 | |/ / /
97 | |/ / /
68 | o | | 5:3589c3c477ab
98 | o | | 5:3589c3c477ab
69 | |\ \ \
99 | |\ \ \
70 | | o | | 4:e2cad8233c77
100 | | o | | 4:e2cad8233c77
71 | |/|/ /
101 | |/|/ /
72 | o / / 3:02173ffbf857
102 | o / / 3:02173ffbf857
73 |/ / /
103 |/ / /
74 o / / 2:e8ea2256f9ec
104 o / / 2:e8ea2256f9ec
75 |/ /
105 |/ /
76 o / 1:3cae7826a707
106 o / 1:3cae7826a707
77 |/
107 |/
78 o 0:7aa22e58e8c1
108 o 0:7aa22e58e8c1
79
109
80 % glog
110 % glog
81 @ changeset: 34:0eed7cd895e0
111 @ changeset: 34:0eed7cd895e0
82 | tag: tip
112 | tag: tip
83 | parent: 32:77f7d8438a3c
113 | parent: 32:77f7d8438a3c
84 | user: test
114 | user: test
85 | date: Thu Jan 01 00:00:34 1970 +0000
115 | date: Thu Jan 01 00:00:34 1970 +0000
86 | summary: (34) head
116 | summary: (34) head
87 |
117 |
88 | o changeset: 33:2e9d1b521374
118 | o changeset: 33:2e9d1b521374
89 | | parent: 18:5a8c9a29ef81
119 | | parent: 18:5a8c9a29ef81
90 | | user: test
120 | | user: test
91 | | date: Thu Jan 01 00:00:33 1970 +0000
121 | | date: Thu Jan 01 00:00:33 1970 +0000
92 | | summary: (33) head
122 | | summary: (33) head
93 | |
123 | |
94 o | changeset: 32:77f7d8438a3c
124 o | changeset: 32:77f7d8438a3c
95 |\ \ parent: 27:e9e08174cd30
125 |\ \ parent: 27:e9e08174cd30
96 | | | parent: 31:82ee55204a79
126 | | | parent: 31:82ee55204a79
97 | | | user: test
127 | | | user: test
98 | | | date: Thu Jan 01 00:00:32 1970 +0000
128 | | | date: Thu Jan 01 00:00:32 1970 +0000
99 | | | summary: (32) expand
129 | | | summary: (32) expand
100 | | |
130 | | |
101 | o | changeset: 31:82ee55204a79
131 | o | changeset: 31:82ee55204a79
102 | |\ \ parent: 21:e758e8f4ace9
132 | |\ \ parent: 21:e758e8f4ace9
103 | | | | parent: 30:777dfc428649
133 | | | | parent: 30:777dfc428649
104 | | | | user: test
134 | | | | user: test
105 | | | | date: Thu Jan 01 00:00:31 1970 +0000
135 | | | | date: Thu Jan 01 00:00:31 1970 +0000
106 | | | | summary: (31) expand
136 | | | | summary: (31) expand
107 | | | |
137 | | | |
108 | | o | changeset: 30:777dfc428649
138 | | o | changeset: 30:777dfc428649
109 | | |\ \ parent: 28:4b6e9bd48cf9
139 | | |\ \ parent: 28:4b6e9bd48cf9
110 | | | | | parent: 29:f8e7fee63353
140 | | | | | parent: 29:f8e7fee63353
111 | | | | | user: test
141 | | | | | user: test
112 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
142 | | | | | date: Thu Jan 01 00:00:30 1970 +0000
113 | | | | | summary: (30) expand
143 | | | | | summary: (30) expand
114 | | | | |
144 | | | | |
115 | | | o | changeset: 29:f8e7fee63353
145 | | | o | changeset: 29:f8e7fee63353
116 | | | | | parent: 0:7aa22e58e8c1
146 | | | | | parent: 0:7aa22e58e8c1
117 | | | | | user: test
147 | | | | | user: test
118 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
148 | | | | | date: Thu Jan 01 00:00:29 1970 +0000
119 | | | | | summary: (29) regular commit
149 | | | | | summary: (29) regular commit
120 | | | | |
150 | | | | |
121 | | o | | changeset: 28:4b6e9bd48cf9
151 | | o | | changeset: 28:4b6e9bd48cf9
122 | | |\ \ \ parent: 1:3cae7826a707
152 | | |\ \ \ parent: 1:3cae7826a707
123 | | | | | | parent: 26:720dc079a855
153 | | | | | | parent: 26:720dc079a855
124 | | | | | | user: test
154 | | | | | | user: test
125 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
155 | | | | | | date: Thu Jan 01 00:00:28 1970 +0000
126 | | | | | | summary: (28) merge zero known
156 | | | | | | summary: (28) merge zero known
127 | | | | | |
157 | | | | | |
128 o | | | | | changeset: 27:e9e08174cd30
158 o | | | | | changeset: 27:e9e08174cd30
129 |/ / / / / parent: 21:e758e8f4ace9
159 |/ / / / / parent: 21:e758e8f4ace9
130 | | | | | user: test
160 | | | | | user: test
131 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
161 | | | | | date: Thu Jan 01 00:00:27 1970 +0000
132 | | | | | summary: (27) collapse
162 | | | | | summary: (27) collapse
133 | | | | |
163 | | | | |
134 | | o---+ changeset: 26:720dc079a855
164 | | o---+ changeset: 26:720dc079a855
135 | | | | | parent: 18:5a8c9a29ef81
165 | | | | | parent: 18:5a8c9a29ef81
136 | | | | | parent: 25:9d4ed048d013
166 | | | | | parent: 25:9d4ed048d013
137 | | | | | user: test
167 | | | | | user: test
138 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
168 | | | | | date: Thu Jan 01 00:00:26 1970 +0000
139 | | | | | summary: (26) merge one known; far right
169 | | | | | summary: (26) merge one known; far right
140 | | | | |
170 | | | | |
141 +---o | | changeset: 25:9d4ed048d013
171 +---o | | changeset: 25:9d4ed048d013
142 | | | | | parent: 21:e758e8f4ace9
172 | | | | | parent: 21:e758e8f4ace9
143 | | | | | parent: 24:4a68967db00d
173 | | | | | parent: 24:4a68967db00d
144 | | | | | user: test
174 | | | | | user: test
145 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
175 | | | | | date: Thu Jan 01 00:00:25 1970 +0000
146 | | | | | summary: (25) merge one known; far left
176 | | | | | summary: (25) merge one known; far left
147 | | | | |
177 | | | | |
148 | | o | | changeset: 24:4a68967db00d
178 | | o | | changeset: 24:4a68967db00d
149 | | |\| | parent: 0:7aa22e58e8c1
179 | | |\| | parent: 0:7aa22e58e8c1
150 | | | | | parent: 23:bc31393cabdf
180 | | | | | parent: 23:bc31393cabdf
151 | | | | | user: test
181 | | | | | user: test
152 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
182 | | | | | date: Thu Jan 01 00:00:24 1970 +0000
153 | | | | | summary: (24) merge one known; immediate right
183 | | | | | summary: (24) merge one known; immediate right
154 | | | | |
184 | | | | |
155 | | o | | changeset: 23:bc31393cabdf
185 | | o | | changeset: 23:bc31393cabdf
156 | |/| | | parent: 1:3cae7826a707
186 | |/| | | parent: 1:3cae7826a707
157 | | | | | parent: 22:a37f2ea6ebc6
187 | | | | | parent: 22:a37f2ea6ebc6
158 | | | | | user: test
188 | | | | | user: test
159 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
189 | | | | | date: Thu Jan 01 00:00:23 1970 +0000
160 | | | | | summary: (23) merge one known; immediate left
190 | | | | | summary: (23) merge one known; immediate left
161 | | | | |
191 | | | | |
162 +---o---+ changeset: 22:a37f2ea6ebc6
192 +---o---+ changeset: 22:a37f2ea6ebc6
163 | | | | parent: 18:5a8c9a29ef81
193 | | | | parent: 18:5a8c9a29ef81
164 | | / / parent: 21:e758e8f4ace9
194 | | / / parent: 21:e758e8f4ace9
165 | | | | user: test
195 | | | | user: test
166 | | | | date: Thu Jan 01 00:00:22 1970 +0000
196 | | | | date: Thu Jan 01 00:00:22 1970 +0000
167 | | | | summary: (22) merge two known; one far left, one far right
197 | | | | summary: (22) merge two known; one far left, one far right
168 | | | |
198 | | | |
169 o | | | changeset: 21:e758e8f4ace9
199 o | | | changeset: 21:e758e8f4ace9
170 |\ \ \ \ parent: 19:138069b5dad7
200 |\ \ \ \ parent: 19:138069b5dad7
171 | | | | | parent: 20:aeccadad74b4
201 | | | | | parent: 20:aeccadad74b4
172 | | | | | user: test
202 | | | | | user: test
173 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
203 | | | | | date: Thu Jan 01 00:00:21 1970 +0000
174 | | | | | summary: (21) expand
204 | | | | | summary: (21) expand
175 | | | | |
205 | | | | |
176 | o---+-+ changeset: 20:aeccadad74b4
206 | o---+-+ changeset: 20:aeccadad74b4
177 | | | | parent: 0:7aa22e58e8c1
207 | | | | parent: 0:7aa22e58e8c1
178 | / / / parent: 18:5a8c9a29ef81
208 | / / / parent: 18:5a8c9a29ef81
179 | | | | user: test
209 | | | | user: test
180 | | | | date: Thu Jan 01 00:00:20 1970 +0000
210 | | | | date: Thu Jan 01 00:00:20 1970 +0000
181 | | | | summary: (20) merge two known; two far right
211 | | | | summary: (20) merge two known; two far right
182 | | | |
212 | | | |
183 o | | | changeset: 19:138069b5dad7
213 o | | | changeset: 19:138069b5dad7
184 |\ \ \ \ parent: 15:c0b4283d4c1d
214 |\ \ \ \ parent: 15:c0b4283d4c1d
185 | | | | | parent: 17:43e52b935494
215 | | | | | parent: 17:43e52b935494
186 | | | | | user: test
216 | | | | | user: test
187 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
217 | | | | | date: Thu Jan 01 00:00:19 1970 +0000
188 | | | | | summary: (19) expand
218 | | | | | summary: (19) expand
189 | | | | |
219 | | | | |
190 +---+---o changeset: 18:5a8c9a29ef81
220 +---+---o changeset: 18:5a8c9a29ef81
191 | | | | parent: 1:3cae7826a707
221 | | | | parent: 1:3cae7826a707
192 | | | | parent: 15:c0b4283d4c1d
222 | | | | parent: 15:c0b4283d4c1d
193 | | | | user: test
223 | | | | user: test
194 | | | | date: Thu Jan 01 00:00:18 1970 +0000
224 | | | | date: Thu Jan 01 00:00:18 1970 +0000
195 | | | | summary: (18) merge two known; two far left
225 | | | | summary: (18) merge two known; two far left
196 | | | |
226 | | | |
197 | o | | changeset: 17:43e52b935494
227 | o | | changeset: 17:43e52b935494
198 | |\ \ \ parent: 12:74dc7aea4494
228 | |\ \ \ parent: 12:74dc7aea4494
199 | | | | | parent: 16:449a2f9562a4
229 | | | | | parent: 16:449a2f9562a4
200 | | | | | user: test
230 | | | | | user: test
201 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
231 | | | | | date: Thu Jan 01 00:00:17 1970 +0000
202 | | | | | summary: (17) expand
232 | | | | | summary: (17) expand
203 | | | | |
233 | | | | |
204 | | o---+ changeset: 16:449a2f9562a4
234 | | o---+ changeset: 16:449a2f9562a4
205 | | | | | parent: 0:7aa22e58e8c1
235 | | | | | parent: 0:7aa22e58e8c1
206 | | |/ / parent: 1:3cae7826a707
236 | | |/ / parent: 1:3cae7826a707
207 | | | | user: test
237 | | | | user: test
208 | | | | date: Thu Jan 01 00:00:16 1970 +0000
238 | | | | date: Thu Jan 01 00:00:16 1970 +0000
209 | | | | summary: (16) merge two known; one immediate right, one near right
239 | | | | summary: (16) merge two known; one immediate right, one near right
210 | | | |
240 | | | |
211 o | | | changeset: 15:c0b4283d4c1d
241 o | | | changeset: 15:c0b4283d4c1d
212 |\ \ \ \ parent: 13:c39d0a2b8165
242 |\ \ \ \ parent: 13:c39d0a2b8165
213 | | | | | parent: 14:9d533950abf0
243 | | | | | parent: 14:9d533950abf0
214 | | | | | user: test
244 | | | | | user: test
215 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
245 | | | | | date: Thu Jan 01 00:00:15 1970 +0000
216 | | | | | summary: (15) expand
246 | | | | | summary: (15) expand
217 | | | | |
247 | | | | |
218 | o-----+ changeset: 14:9d533950abf0
248 | o-----+ changeset: 14:9d533950abf0
219 | | | | | parent: 0:7aa22e58e8c1
249 | | | | | parent: 0:7aa22e58e8c1
220 | |/ / / parent: 12:74dc7aea4494
250 | |/ / / parent: 12:74dc7aea4494
221 | | | | user: test
251 | | | | user: test
222 | | | | date: Thu Jan 01 00:00:14 1970 +0000
252 | | | | date: Thu Jan 01 00:00:14 1970 +0000
223 | | | | summary: (14) merge two known; one immediate right, one far right
253 | | | | summary: (14) merge two known; one immediate right, one far right
224 | | | |
254 | | | |
225 o | | | changeset: 13:c39d0a2b8165
255 o | | | changeset: 13:c39d0a2b8165
226 |\ \ \ \ parent: 9:79ab1812f961
256 |\ \ \ \ parent: 9:79ab1812f961
227 | | | | | parent: 11:c3c395dd8b98
257 | | | | | parent: 11:c3c395dd8b98
228 | | | | | user: test
258 | | | | | user: test
229 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
259 | | | | | date: Thu Jan 01 00:00:13 1970 +0000
230 | | | | | summary: (13) expand
260 | | | | | summary: (13) expand
231 | | | | |
261 | | | | |
232 +---o | | changeset: 12:74dc7aea4494
262 +---o | | changeset: 12:74dc7aea4494
233 | | |/ / parent: 1:3cae7826a707
263 | | |/ / parent: 1:3cae7826a707
234 | | | | parent: 9:79ab1812f961
264 | | | | parent: 9:79ab1812f961
235 | | | | user: test
265 | | | | user: test
236 | | | | date: Thu Jan 01 00:00:12 1970 +0000
266 | | | | date: Thu Jan 01 00:00:12 1970 +0000
237 | | | | summary: (12) merge two known; one immediate right, one far left
267 | | | | summary: (12) merge two known; one immediate right, one far left
238 | | | |
268 | | | |
239 | o | | changeset: 11:c3c395dd8b98
269 | o | | changeset: 11:c3c395dd8b98
240 | |\ \ \ parent: 6:0ca7c061cf45
270 | |\ \ \ parent: 6:0ca7c061cf45
241 | | | | | parent: 10:8094c50149ef
271 | | | | | parent: 10:8094c50149ef
242 | | | | | user: test
272 | | | | | user: test
243 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
273 | | | | | date: Thu Jan 01 00:00:11 1970 +0000
244 | | | | | summary: (11) expand
274 | | | | | summary: (11) expand
245 | | | | |
275 | | | | |
246 | | o---+ changeset: 10:8094c50149ef
276 | | o---+ changeset: 10:8094c50149ef
247 | | | | | parent: 0:7aa22e58e8c1
277 | | | | | parent: 0:7aa22e58e8c1
248 | |/ / / parent: 6:0ca7c061cf45
278 | |/ / / parent: 6:0ca7c061cf45
249 | | | | user: test
279 | | | | user: test
250 | | | | date: Thu Jan 01 00:00:10 1970 +0000
280 | | | | date: Thu Jan 01 00:00:10 1970 +0000
251 | | | | summary: (10) merge two known; one immediate left, one near right
281 | | | | summary: (10) merge two known; one immediate left, one near right
252 | | | |
282 | | | |
253 o | | | changeset: 9:79ab1812f961
283 o | | | changeset: 9:79ab1812f961
254 |\ \ \ \ parent: 7:699392d1259e
284 |\ \ \ \ parent: 7:699392d1259e
255 | | | | | parent: 8:d7aa38594334
285 | | | | | parent: 8:d7aa38594334
256 | | | | | user: test
286 | | | | | user: test
257 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
287 | | | | | date: Thu Jan 01 00:00:09 1970 +0000
258 | | | | | summary: (9) expand
288 | | | | | summary: (9) expand
259 | | | | |
289 | | | | |
260 | o-----+ changeset: 8:d7aa38594334
290 | o-----+ changeset: 8:d7aa38594334
261 | | | | | parent: 0:7aa22e58e8c1
291 | | | | | parent: 0:7aa22e58e8c1
262 |/ / / / parent: 7:699392d1259e
292 |/ / / / parent: 7:699392d1259e
263 | | | | user: test
293 | | | | user: test
264 | | | | date: Thu Jan 01 00:00:08 1970 +0000
294 | | | | date: Thu Jan 01 00:00:08 1970 +0000
265 | | | | summary: (8) merge two known; one immediate left, one far right
295 | | | | summary: (8) merge two known; one immediate left, one far right
266 | | | |
296 | | | |
267 o | | | changeset: 7:699392d1259e
297 o | | | changeset: 7:699392d1259e
268 |\ \ \ \ parent: 2:e8ea2256f9ec
298 |\ \ \ \ parent: 2:e8ea2256f9ec
269 | | | | | parent: 5:3589c3c477ab
299 | | | | | parent: 5:3589c3c477ab
270 | | | | | user: test
300 | | | | | user: test
271 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
301 | | | | | date: Thu Jan 01 00:00:07 1970 +0000
272 | | | | | summary: (7) expand
302 | | | | | summary: (7) expand
273 | | | | |
303 | | | | |
274 +---o | | changeset: 6:0ca7c061cf45
304 +---o | | changeset: 6:0ca7c061cf45
275 | |/ / / parent: 2:e8ea2256f9ec
305 | |/ / / parent: 2:e8ea2256f9ec
276 | | | | parent: 5:3589c3c477ab
306 | | | | parent: 5:3589c3c477ab
277 | | | | user: test
307 | | | | user: test
278 | | | | date: Thu Jan 01 00:00:06 1970 +0000
308 | | | | date: Thu Jan 01 00:00:06 1970 +0000
279 | | | | summary: (6) merge two known; one immediate left, one far left
309 | | | | summary: (6) merge two known; one immediate left, one far left
280 | | | |
310 | | | |
281 | o | | changeset: 5:3589c3c477ab
311 | o | | changeset: 5:3589c3c477ab
282 | |\ \ \ parent: 3:02173ffbf857
312 | |\ \ \ parent: 3:02173ffbf857
283 | | | | | parent: 4:e2cad8233c77
313 | | | | | parent: 4:e2cad8233c77
284 | | | | | user: test
314 | | | | | user: test
285 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
315 | | | | | date: Thu Jan 01 00:00:05 1970 +0000
286 | | | | | summary: (5) expand
316 | | | | | summary: (5) expand
287 | | | | |
317 | | | | |
288 | | o | | changeset: 4:e2cad8233c77
318 | | o | | changeset: 4:e2cad8233c77
289 | |/|/ / parent: 1:3cae7826a707
319 | |/|/ / parent: 1:3cae7826a707
290 | | | | parent: 3:02173ffbf857
320 | | | | parent: 3:02173ffbf857
291 | | | | user: test
321 | | | | user: test
292 | | | | date: Thu Jan 01 00:00:04 1970 +0000
322 | | | | date: Thu Jan 01 00:00:04 1970 +0000
293 | | | | summary: (4) merge two known; one immediate left, one immediate right
323 | | | | summary: (4) merge two known; one immediate left, one immediate right
294 | | | |
324 | | | |
295 | o | | changeset: 3:02173ffbf857
325 | o | | changeset: 3:02173ffbf857
296 |/ / / user: test
326 |/ / / user: test
297 | | | date: Thu Jan 01 00:00:03 1970 +0000
327 | | | date: Thu Jan 01 00:00:03 1970 +0000
298 | | | summary: (3) collapse
328 | | | summary: (3) collapse
299 | | |
329 | | |
300 o | | changeset: 2:e8ea2256f9ec
330 o | | changeset: 2:e8ea2256f9ec
301 |/ / user: test
331 |/ / user: test
302 | | date: Thu Jan 01 00:00:02 1970 +0000
332 | | date: Thu Jan 01 00:00:02 1970 +0000
303 | | summary: (2) collapse
333 | | summary: (2) collapse
304 | |
334 | |
305 o | changeset: 1:3cae7826a707
335 o | changeset: 1:3cae7826a707
306 |/ user: test
336 |/ user: test
307 | date: Thu Jan 01 00:00:01 1970 +0000
337 | date: Thu Jan 01 00:00:01 1970 +0000
308 | summary: (1) collapse
338 | summary: (1) collapse
309 |
339 |
310 o changeset: 0:7aa22e58e8c1
340 o changeset: 0:7aa22e58e8c1
311 user: test
341 user: test
312 date: Thu Jan 01 00:00:00 1970 +0000
342 date: Thu Jan 01 00:00:00 1970 +0000
313 summary: (0) root
343 summary: (0) root
314
344
315 % file glog
345 % file glog
316 o changeset: 5:3589c3c477ab
346 o changeset: 5:3589c3c477ab
317 parent: 3:02173ffbf857
347 parent: 3:02173ffbf857
318 parent: 4:e2cad8233c77
348 parent: 4:e2cad8233c77
319 user: test
349 user: test
320 date: Thu Jan 01 00:00:05 1970 +0000
350 date: Thu Jan 01 00:00:05 1970 +0000
321 summary: (5) expand
351 summary: (5) expand
322
352
323 % unused arguments
353 % unused arguments
324 hg glog: invalid arguments
354 hg glog: invalid arguments
325 hg glog [OPTION]... [FILE]
355 hg glog [OPTION]... [FILE]
326
356
327 show revision history alongside an ASCII revision graph
357 show revision history alongside an ASCII revision graph
328 failed
358 failed
@@ -1,69 +1,69 b''
1 #!/bin/sh
1 #!/bin/sh
2
2
3 commit()
3 commit()
4 {
4 {
5 msg=$1
5 msg=$1
6 p1=$2
6 p1=$2
7 p2=$3
7 p2=$3
8
8
9 if [ "$p1" ]; then
9 if [ "$p1" ]; then
10 hg up -qC $p1
10 hg up -qC $p1
11 fi
11 fi
12
12
13 if [ "$p2" ]; then
13 if [ "$p2" ]; then
14 HGMERGE=true hg merge -q $p2
14 HGMERGE=true hg merge -q $p2
15 fi
15 fi
16
16
17 echo >> foo
17 echo >> foo
18
18
19 hg commit -d '0 0' -qAm "$msg" foo
19 hg commit -d '0 0' -qAm "$msg"
20 }
20 }
21
21
22 hg init repo
22 hg init repo
23 cd repo
23 cd repo
24
24
25 echo '[extensions]' > .hg/hgrc
25 echo '[extensions]' > .hg/hgrc
26 echo 'hgext.parentrevspec =' >> .hg/hgrc
26 echo 'hgext.parentrevspec =' >> .hg/hgrc
27
27
28 commit '0: add foo'
28 commit '0: add foo'
29 commit '1: change foo 1'
29 commit '1: change foo 1'
30 commit '2: change foo 2a'
30 commit '2: change foo 2a'
31 commit '3: change foo 3a'
31 commit '3: change foo 3a'
32 commit '4: change foo 2b' 1
32 commit '4: change foo 2b' 1
33 commit '5: merge' 3 4
33 commit '5: merge' 3 4
34 commit '6: change foo again'
34 commit '6: change foo again'
35
35
36 hg log --template '#rev#:#node|short# #parents#\n'
36 hg log --template '#rev#:#node|short# #parents#\n'
37 echo
37 echo
38
38
39 lookup()
39 lookup()
40 {
40 {
41 for rev in "$@"; do
41 for rev in "$@"; do
42 printf "$rev: "
42 printf "$rev: "
43 hg id -nr $rev
43 hg id -nr $rev
44 done
44 done
45 true
45 true
46 }
46 }
47
47
48 tipnode=`hg id -ir tip`
48 tipnode=`hg id -ir tip`
49
49
50 echo 'should work with tag/branch/node/rev'
50 echo 'should work with tag/branch/node/rev'
51 for r in tip default $tipnode 6; do
51 for r in tip default $tipnode 6; do
52 lookup "$r^"
52 lookup "$r^"
53 done
53 done
54 echo
54 echo
55
55
56 echo 'some random lookups'
56 echo 'some random lookups'
57 lookup "6^^" "6^^^" "6^^^^" "6^^^^^" "6^^^^^^" "6^1" "6^2" "6^^2" "6^1^2" "6^^3"
57 lookup "6^^" "6^^^" "6^^^^" "6^^^^^" "6^^^^^^" "6^1" "6^2" "6^^2" "6^1^2" "6^^3"
58 lookup "6~" "6~1" "6~2" "6~3" "6~4" "6~5" "6~42" "6~1^2" "6~1^2~2"
58 lookup "6~" "6~1" "6~2" "6~3" "6~4" "6~5" "6~42" "6~1^2" "6~1^2~2"
59 echo
59 echo
60
60
61 echo 'with a tag "6^" pointing to rev 1'
61 echo 'with a tag "6^" pointing to rev 1'
62 hg tag -l -r 1 "6^"
62 hg tag -l -r 1 "6^"
63 lookup "6^" "6^1" "6~1" "6^^"
63 lookup "6^" "6^1" "6~1" "6^^"
64 echo
64 echo
65
65
66 echo 'with a tag "foo^bar" pointing to rev 2'
66 echo 'with a tag "foo^bar" pointing to rev 2'
67 hg tag -l -r 2 "foo^bar"
67 hg tag -l -r 2 "foo^bar"
68 lookup "foo^bar" "foo^bar^"
68 lookup "foo^bar" "foo^bar^"
69
69
General Comments 0
You need to be logged in to leave comments. Login now