##// END OF EJS Templates
merge with crew-stable
Dirkjan Ochtman -
r9219:3f650f6a merge default
parent child Browse files
Show More
@@ -1,15 +1,16 b''
1 1 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0 iD8DBQBEYmO2ywK+sNU5EO8RAnaYAKCO7x15xUn5mnhqWNXqk/ehlhRt2QCfRDfY0LrUq2q4oK/KypuJYPHgq1A=
2 2 2be3001847cb18a23c403439d9e7d0ace30804e9 0 iD8DBQBExUbjywK+sNU5EO8RAhzxAKCtyHAQUzcTSZTqlfJ0by6vhREwWQCghaQFHfkfN0l9/40EowNhuMOKnJk=
3 3 36a957364b1b89c150f2d0e60a99befe0ee08bd3 0 iD8DBQBFfL2QywK+sNU5EO8RAjYFAKCoGlaWRTeMsjdmxAjUYx6diZxOBwCfY6IpBYsKvPTwB3oktnPt5Rmrlys=
4 4 27230c29bfec36d5540fbe1c976810aefecfd1d2 0 iD8DBQBFheweywK+sNU5EO8RAt7VAKCrqJQWT2/uo2RWf0ZI4bLp6v82jACgjrMdsaTbxRsypcmEsdPhlG6/8F4=
5 5 fb4b6d5fe100b0886f8bc3d6731ec0e5ed5c4694 0 iD8DBQBGgHicywK+sNU5EO8RAgNxAJ0VG8ixAaeudx4sZbhngI1syu49HQCeNUJQfWBgA8bkJ2pvsFpNxwYaX3I=
6 6 23889160905a1b09fffe1c07378e9fc1827606eb 0 iD8DBQBHGTzoywK+sNU5EO8RAr/UAJ0Y8s4jQtzgS+G9vM8z6CWBThZ8fwCcCT5XDj2XwxKkz/0s6UELwjsO3LU=
7 7 bae2e9c838e90a393bae3973a7850280413e091a 0 iD8DBQBH6DO5ywK+sNU5EO8RAsfrAJ0e4r9c9GF/MJsM7Xjd3NesLRC3+ACffj6+6HXdZf8cswAoFPO+DY00oD0=
8 8 d5cbbe2c49cee22a9fbeb9ea41daa0ac4e26b846 0 iD8DBQBINdwsywK+sNU5EO8RAjIUAKCPmlFJSpsPAAUKF+iNHAwVnwmzeQCdEXrL27CWclXuUKdbQC8De7LICtE=
9 9 d2375bbee6d47e62ba8e415c86e83a465dc4dce9 0 iD8DBQBIo1wpywK+sNU5EO8RAmRNAJ94x3OFt6blbqu/yBoypm/AJ44fuACfUaldXcV5z9tht97hSp22DVTEPGc=
10 10 2a67430f92f15ea5159c26b09ec4839a0c549a26 0 iEYEABECAAYFAkk1hykACgkQywK+sNU5EO85QACeNJNUanjc2tl4wUoPHNuv+lSj0ZMAoIm93wSTc/feyYnO2YCaQ1iyd9Nu
11 11 3773e510d433969e277b1863c317b674cbee2065 0 iEYEABECAAYFAklNbbAACgkQywK+sNU5EO8o+gCfeb2/lfIJZMvyDA1m+G1CsBAxfFsAoIa6iAMG8SBY7hW1Q85Yf/LXEvaE
12 12 11a4eb81fb4f4742451591489e2797dc47903277 0 iEYEABECAAYFAklcAnsACgkQywK+sNU5EO+uXwCbBVHNNsLy1g7BlAyQJwadYVyHOXoAoKvtAVO71+bv7EbVoukwTzT+P4Sx
13 13 11efa41037e280d08cfb07c09ad485df30fb0ea8 0 iEYEABECAAYFAkmvJRQACgkQywK+sNU5EO9XZwCeLMgDgPSMWMm6vgjL4lDs2pEc5+0AnRxfiFbpbBfuEFTqKz9nbzeyoBlx
14 14 02981000012e3adf40c4849bd7b3d5618f9ce82d 0 iEYEABECAAYFAknEH3wACgkQywK+sNU5EO+uXwCeI+LbLMmhjU1lKSfU3UWJHjjUC7oAoIZLvYDGOL/tNZFUuatc3RnZ2eje
15 15 196d40e7c885fa6e95f89134809b3ec7bdbca34b 0 iEYEABECAAYFAkpL2X4ACgkQywK+sNU5EO9FOwCfXJycjyKJXsvQqKkHrglwOQhEKS4An36GfKzptfN8b1qNc3+ya/5c2WOM
16 3ef6c14a1e8e83a31226f5881b7fe6095bbfa6f6 0 iEYEABECAAYFAkpopLIACgkQywK+sNU5EO8QSgCfZ0ztsd071rOa2lhmp9Fyue/WoI0AoLTei80/xrhRlB8L/rZEf2KBl8dA
@@ -1,27 +1,28 b''
1 1 d40cc5aacc31ed673d9b5b24f98bee78c283062c 0.4f
2 2 1c590d34bf61e2ea12c71738e5a746cd74586157 0.4e
3 3 7eca4cfa8aad5fce9a04f7d8acadcd0452e2f34e 0.4d
4 4 b4d0c3786ad3e47beacf8412157326a32b6d25a4 0.4c
5 5 f40273b0ad7b3a6d3012fd37736d0611f41ecf54 0.5
6 6 0a28dfe59f8fab54a5118c5be4f40da34a53cdb7 0.5b
7 7 12e0fdbc57a0be78f0e817fd1d170a3615cd35da 0.6
8 8 4ccf3de52989b14c3d84e1097f59e39a992e00bd 0.6b
9 9 eac9c8efcd9bd8244e72fb6821f769f450457a32 0.6c
10 10 979c049974485125e1f9357f6bbe9c1b548a64c3 0.7
11 11 3a56574f329a368d645853e0f9e09472aee62349 0.8
12 12 6a03cff2b0f5d30281e6addefe96b993582f2eac 0.8.1
13 13 35fb62a3a673d5322f6274a44ba6456e5e4b3b37 0.9
14 14 2be3001847cb18a23c403439d9e7d0ace30804e9 0.9.1
15 15 36a957364b1b89c150f2d0e60a99befe0ee08bd3 0.9.2
16 16 27230c29bfec36d5540fbe1c976810aefecfd1d2 0.9.3
17 17 fb4b6d5fe100b0886f8bc3d6731ec0e5ed5c4694 0.9.4
18 18 23889160905a1b09fffe1c07378e9fc1827606eb 0.9.5
19 19 bae2e9c838e90a393bae3973a7850280413e091a 1.0
20 20 d5cbbe2c49cee22a9fbeb9ea41daa0ac4e26b846 1.0.1
21 21 d2375bbee6d47e62ba8e415c86e83a465dc4dce9 1.0.2
22 22 2a67430f92f15ea5159c26b09ec4839a0c549a26 1.1
23 23 3773e510d433969e277b1863c317b674cbee2065 1.1.1
24 24 11a4eb81fb4f4742451591489e2797dc47903277 1.1.2
25 25 11efa41037e280d08cfb07c09ad485df30fb0ea8 1.2
26 26 02981000012e3adf40c4849bd7b3d5618f9ce82d 1.2.1
27 27 196d40e7c885fa6e95f89134809b3ec7bdbca34b 1.3
28 3ef6c14a1e8e83a31226f5881b7fe6095bbfa6f6 1.3.1
@@ -1,148 +1,147 b''
1 1 # fetch.py - pull and merge remote changes
2 2 #
3 3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2, incorporated herein by reference.
7 7
8 8 '''pull, update and merge in one command'''
9 9
10 10 from mercurial.i18n import _
11 11 from mercurial.node import nullid, short
12 12 from mercurial import commands, cmdutil, hg, util, url, error
13 13 from mercurial.lock import release
14 14
15 15 def fetch(ui, repo, source='default', **opts):
16 16 '''pull changes from a remote repository, merge new changes if needed.
17 17
18 18 This finds all changes from the repository at the specified path or URL
19 19 and adds them to the local repository.
20 20
21 21 If the pulled changes add a new branch head, the head is automatically
22 22 merged, and the result of the merge is committed. Otherwise, the working
23 23 directory is updated to include the new changes.
24 24
25 25 When a merge occurs, the newly pulled changes are assumed to be
26 26 "authoritative". The head of the new changes is used as the first parent,
27 27 with local changes as the second. To switch the merge order, use
28 28 --switch-parent.
29 29
30 30 See 'hg help dates' for a list of formats valid for -d/--date.
31 31 '''
32 32
33 33 date = opts.get('date')
34 34 if date:
35 35 opts['date'] = util.parsedate(date)
36 36
37 37 parent, p2 = repo.dirstate.parents()
38 38 branch = repo.dirstate.branch()
39 39 branchnode = repo.branchtags().get(branch)
40 40 if parent != branchnode:
41 41 raise util.Abort(_('working dir not at branch tip '
42 42 '(use "hg update" to check out branch tip)'))
43 43
44 44 if p2 != nullid:
45 45 raise util.Abort(_('outstanding uncommitted merge'))
46 46
47 47 wlock = lock = None
48 48 try:
49 49 wlock = repo.wlock()
50 50 lock = repo.lock()
51 51 mod, add, rem, del_ = repo.status()[:4]
52 52
53 53 if mod or add or rem:
54 54 raise util.Abort(_('outstanding uncommitted changes'))
55 55 if del_:
56 56 raise util.Abort(_('working directory is missing some files'))
57 57 bheads = repo.branchheads(branch)
58 58 bheads = [head for head in bheads if len(repo[head].children()) == 0]
59 59 if len(bheads) > 1:
60 60 raise util.Abort(_('multiple heads in this branch '
61 61 '(use "hg heads ." and "hg merge" to merge)'))
62 62
63 63 other = hg.repository(cmdutil.remoteui(repo, opts),
64 64 ui.expandpath(source))
65 65 ui.status(_('pulling from %s\n') %
66 66 url.hidepassword(ui.expandpath(source)))
67 67 revs = None
68 68 if opts['rev']:
69 69 try:
70 70 revs = [other.lookup(rev) for rev in opts['rev']]
71 71 except error.CapabilityError:
72 72 err = _("Other repository doesn't support revision lookup, "
73 73 "so a rev cannot be specified.")
74 74 raise util.Abort(err)
75 75
76 76 # Are there any changes at all?
77 77 modheads = repo.pull(other, heads=revs)
78 78 if modheads == 0:
79 79 return 0
80 80
81 81 # Is this a simple fast-forward along the current branch?
82 82 newheads = repo.branchheads(branch)
83 83 newheads = [head for head in newheads if len(repo[head].children()) == 0]
84 84 newchildren = repo.changelog.nodesbetween([parent], newheads)[2]
85 85 if len(newheads) == 1:
86 86 if newchildren[0] != parent:
87 87 return hg.clean(repo, newchildren[0])
88 88 else:
89 89 return
90 90
91 91 # Are there more than one additional branch heads?
92 92 newchildren = [n for n in newchildren if n != parent]
93 93 newparent = parent
94 94 if newchildren:
95 95 newparent = newchildren[0]
96 96 hg.clean(repo, newparent)
97 97 newheads = [n for n in newheads if n != newparent]
98 98 if len(newheads) > 1:
99 99 ui.status(_('not merging with %d other new branch heads '
100 100 '(use "hg heads ." and "hg merge" to merge them)\n') %
101 101 (len(newheads) - 1))
102 102 return
103 103
104 104 # Otherwise, let's merge.
105 105 err = False
106 106 if newheads:
107 107 # By default, we consider the repository we're pulling
108 108 # *from* as authoritative, so we merge our changes into
109 109 # theirs.
110 110 if opts['switch_parent']:
111 111 firstparent, secondparent = newparent, newheads[0]
112 112 else:
113 113 firstparent, secondparent = newheads[0], newparent
114 114 ui.status(_('updating to %d:%s\n') %
115 115 (repo.changelog.rev(firstparent),
116 116 short(firstparent)))
117 117 hg.clean(repo, firstparent)
118 118 ui.status(_('merging with %d:%s\n') %
119 119 (repo.changelog.rev(secondparent), short(secondparent)))
120 120 err = hg.merge(repo, secondparent, remind=False)
121 121
122 122 if not err:
123 123 # we don't translate commit messages
124 124 message = (cmdutil.logmessage(opts) or
125 125 ('Automated merge with %s' %
126 126 url.removeauth(other.url())))
127 127 editor = cmdutil.commiteditor
128 128 if opts.get('force_editor') or opts.get('edit'):
129 129 editor = cmdutil.commitforceeditor
130 n = repo.commit(message, opts['user'], opts['date'],
131 force=True, editor=editor)
130 n = repo.commit(message, opts['user'], opts['date'], editor=editor)
132 131 ui.status(_('new changeset %d:%s merges remote changes '
133 132 'with local\n') % (repo.changelog.rev(n),
134 133 short(n)))
135 134
136 135 finally:
137 136 release(lock, wlock)
138 137
139 138 cmdtable = {
140 139 'fetch':
141 140 (fetch,
142 141 [('r', 'rev', [], _('a specific revision you would like to pull')),
143 142 ('e', 'edit', None, _('edit commit message')),
144 143 ('', 'force-editor', None, _('edit commit message (DEPRECATED)')),
145 144 ('', 'switch-parent', None, _('switch parents when merging')),
146 145 ] + commands.commitopts + commands.commitopts2 + commands.remoteopts,
147 146 _('hg fetch [SOURCE]')),
148 147 }
@@ -1,544 +1,544 b''
1 1 # keyword.py - $Keyword$ expansion for Mercurial
2 2 #
3 3 # Copyright 2007, 2008 Christian Ebert <blacktrash@gmx.net>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2, incorporated herein by reference.
7 7 #
8 8 # $Id$
9 9 #
10 10 # Keyword expansion hack against the grain of a DSCM
11 11 #
12 12 # There are many good reasons why this is not needed in a distributed
13 13 # SCM, still it may be useful in very small projects based on single
14 14 # files (like LaTeX packages), that are mostly addressed to an
15 15 # audience not running a version control system.
16 16 #
17 17 # For in-depth discussion refer to
18 18 # <http://mercurial.selenic.com/wiki/KeywordPlan>.
19 19 #
20 20 # Keyword expansion is based on Mercurial's changeset template mappings.
21 21 #
22 22 # Binary files are not touched.
23 23 #
24 24 # Files to act upon/ignore are specified in the [keyword] section.
25 25 # Customized keyword template mappings in the [keywordmaps] section.
26 26 #
27 27 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
28 28
29 29 '''expand keywords in tracked files
30 30
31 31 This extension expands RCS/CVS-like or self-customized $Keywords$ in tracked
32 32 text files selected by your configuration.
33 33
34 34 Keywords are only expanded in local repositories and not stored in the change
35 35 history. The mechanism can be regarded as a convenience for the current user
36 36 or for archive distribution.
37 37
38 38 Configuration is done in the [keyword] and [keywordmaps] sections of hgrc
39 39 files.
40 40
41 41 Example::
42 42
43 43 [keyword]
44 44 # expand keywords in every python file except those matching "x*"
45 45 **.py =
46 46 x* = ignore
47 47
48 48 NOTE: the more specific you are in your filename patterns the less you lose
49 49 speed in huge repositories.
50 50
51 51 For [keywordmaps] template mapping and expansion demonstration and control run
52 52 "hg kwdemo".
53 53
54 54 An additional date template filter {date|utcdate} is provided.
55 55
56 56 The default template mappings (view with "hg kwdemo -d") can be replaced with
57 57 customized keywords and templates. Again, run "hg kwdemo" to control the
58 58 results of your config changes.
59 59
60 60 Before changing/disabling active keywords, run "hg kwshrink" to avoid the risk
61 61 of inadvertently storing expanded keywords in the change history.
62 62
63 63 To force expansion after enabling it, or a configuration change, run "hg
64 64 kwexpand".
65 65
66 66 Also, when committing with the record extension or using mq's qrecord, be
67 67 aware that keywords cannot be updated. Again, run "hg kwexpand" on the files
68 68 in question to update keyword expansions after all changes have been checked
69 69 in.
70 70
71 71 Expansions spanning more than one line and incremental expansions, like CVS'
72 72 $Log$, are not supported. A keyword template map "Log = {desc}" expands to the
73 73 first line of the changeset description.
74 74 '''
75 75
76 76 from mercurial import commands, cmdutil, dispatch, filelog, revlog, extensions
77 77 from mercurial import patch, localrepo, templater, templatefilters, util, match
78 78 from mercurial.hgweb import webcommands
79 79 from mercurial.lock import release
80 80 from mercurial.node import nullid
81 81 from mercurial.i18n import _
82 82 import re, shutil, tempfile, time
83 83
84 84 commands.optionalrepo += ' kwdemo'
85 85
86 86 # hg commands that do not act on keywords
87 87 nokwcommands = ('add addremove annotate bundle copy export grep incoming init'
88 88 ' log outgoing push rename rollback tip verify'
89 89 ' convert email glog')
90 90
91 91 # hg commands that trigger expansion only when writing to working dir,
92 92 # not when reading filelog, and unexpand when reading from working dir
93 93 restricted = 'merge record resolve qfold qimport qnew qpush qrefresh qrecord'
94 94
95 95 def utcdate(date):
96 96 '''Returns hgdate in cvs-like UTC format.'''
97 97 return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
98 98
99 99 # make keyword tools accessible
100 100 kwtools = {'templater': None, 'hgcmd': '', 'inc': [], 'exc': ['.hg*']}
101 101
102 102
103 103 class kwtemplater(object):
104 104 '''
105 105 Sets up keyword templates, corresponding keyword regex, and
106 106 provides keyword substitution functions.
107 107 '''
108 108 templates = {
109 109 'Revision': '{node|short}',
110 110 'Author': '{author|user}',
111 111 'Date': '{date|utcdate}',
112 112 'RCSFile': '{file|basename},v',
113 113 'Source': '{root}/{file},v',
114 114 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
115 115 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
116 116 }
117 117
118 118 def __init__(self, ui, repo):
119 119 self.ui = ui
120 120 self.repo = repo
121 121 self.match = match.match(repo.root, '', [],
122 122 kwtools['inc'], kwtools['exc'])
123 123 self.restrict = kwtools['hgcmd'] in restricted.split()
124 124
125 125 kwmaps = self.ui.configitems('keywordmaps')
126 126 if kwmaps: # override default templates
127 127 self.templates = dict((k, templater.parsestring(v, False))
128 128 for k, v in kwmaps)
129 129 escaped = map(re.escape, self.templates.keys())
130 130 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
131 131 self.re_kw = re.compile(kwpat)
132 132
133 133 templatefilters.filters['utcdate'] = utcdate
134 134 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
135 135 False, None, '', False)
136 136
137 137 def substitute(self, data, path, ctx, subfunc):
138 138 '''Replaces keywords in data with expanded template.'''
139 139 def kwsub(mobj):
140 140 kw = mobj.group(1)
141 141 self.ct.use_template(self.templates[kw])
142 142 self.ui.pushbuffer()
143 143 self.ct.show(ctx, root=self.repo.root, file=path)
144 144 ekw = templatefilters.firstline(self.ui.popbuffer())
145 145 return '$%s: %s $' % (kw, ekw)
146 146 return subfunc(kwsub, data)
147 147
148 148 def expand(self, path, node, data):
149 149 '''Returns data with keywords expanded.'''
150 150 if not self.restrict and self.match(path) and not util.binary(data):
151 151 ctx = self.repo.filectx(path, fileid=node).changectx()
152 152 return self.substitute(data, path, ctx, self.re_kw.sub)
153 153 return data
154 154
155 155 def iskwfile(self, path, flagfunc):
156 156 '''Returns true if path matches [keyword] pattern
157 157 and is not a symbolic link.
158 158 Caveat: localrepository._link fails on Windows.'''
159 159 return self.match(path) and not 'l' in flagfunc(path)
160 160
161 161 def overwrite(self, node, expand, files):
162 162 '''Overwrites selected files expanding/shrinking keywords.'''
163 163 ctx = self.repo[node]
164 164 mf = ctx.manifest()
165 165 if node is not None: # commit
166 166 files = [f for f in ctx.files() if f in mf]
167 167 notify = self.ui.debug
168 168 else: # kwexpand/kwshrink
169 169 notify = self.ui.note
170 170 candidates = [f for f in files if self.iskwfile(f, ctx.flags)]
171 171 if candidates:
172 172 self.restrict = True # do not expand when reading
173 173 msg = (expand and _('overwriting %s expanding keywords\n')
174 174 or _('overwriting %s shrinking keywords\n'))
175 175 for f in candidates:
176 176 fp = self.repo.file(f)
177 177 data = fp.read(mf[f])
178 178 if util.binary(data):
179 179 continue
180 180 if expand:
181 181 if node is None:
182 182 ctx = self.repo.filectx(f, fileid=mf[f]).changectx()
183 183 data, found = self.substitute(data, f, ctx,
184 184 self.re_kw.subn)
185 185 else:
186 186 found = self.re_kw.search(data)
187 187 if found:
188 188 notify(msg % f)
189 189 self.repo.wwrite(f, data, mf.flags(f))
190 190 if node is None:
191 191 self.repo.dirstate.normal(f)
192 192 self.restrict = False
193 193
194 194 def shrinktext(self, text):
195 195 '''Unconditionally removes all keyword substitutions from text.'''
196 196 return self.re_kw.sub(r'$\1$', text)
197 197
198 198 def shrink(self, fname, text):
199 199 '''Returns text with all keyword substitutions removed.'''
200 200 if self.match(fname) and not util.binary(text):
201 201 return self.shrinktext(text)
202 202 return text
203 203
204 204 def shrinklines(self, fname, lines):
205 205 '''Returns lines with keyword substitutions removed.'''
206 206 if self.match(fname):
207 207 text = ''.join(lines)
208 208 if not util.binary(text):
209 209 return self.shrinktext(text).splitlines(True)
210 210 return lines
211 211
212 212 def wread(self, fname, data):
213 213 '''If in restricted mode returns data read from wdir with
214 214 keyword substitutions removed.'''
215 215 return self.restrict and self.shrink(fname, data) or data
216 216
217 217 class kwfilelog(filelog.filelog):
218 218 '''
219 219 Subclass of filelog to hook into its read, add, cmp methods.
220 220 Keywords are "stored" unexpanded, and processed on reading.
221 221 '''
222 222 def __init__(self, opener, kwt, path):
223 223 super(kwfilelog, self).__init__(opener, path)
224 224 self.kwt = kwt
225 225 self.path = path
226 226
227 227 def read(self, node):
228 228 '''Expands keywords when reading filelog.'''
229 229 data = super(kwfilelog, self).read(node)
230 230 return self.kwt.expand(self.path, node, data)
231 231
232 232 def add(self, text, meta, tr, link, p1=None, p2=None):
233 233 '''Removes keyword substitutions when adding to filelog.'''
234 234 text = self.kwt.shrink(self.path, text)
235 235 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
236 236
237 237 def cmp(self, node, text):
238 238 '''Removes keyword substitutions for comparison.'''
239 239 text = self.kwt.shrink(self.path, text)
240 240 if self.renamed(node):
241 241 t2 = super(kwfilelog, self).read(node)
242 242 return t2 != text
243 243 return revlog.revlog.cmp(self, node, text)
244 244
245 245 def _status(ui, repo, kwt, unknown, *pats, **opts):
246 246 '''Bails out if [keyword] configuration is not active.
247 247 Returns status of working directory.'''
248 248 if kwt:
249 249 match = cmdutil.match(repo, pats, opts)
250 250 return repo.status(match=match, unknown=unknown, clean=True)
251 251 if ui.configitems('keyword'):
252 252 raise util.Abort(_('[keyword] patterns cannot match'))
253 253 raise util.Abort(_('no [keyword] patterns configured'))
254 254
255 255 def _kwfwrite(ui, repo, expand, *pats, **opts):
256 256 '''Selects files and passes them to kwtemplater.overwrite.'''
257 257 if repo.dirstate.parents()[1] != nullid:
258 258 raise util.Abort(_('outstanding uncommitted merge'))
259 259 kwt = kwtools['templater']
260 260 status = _status(ui, repo, kwt, False, *pats, **opts)
261 261 modified, added, removed, deleted = status[:4]
262 262 if modified or added or removed or deleted:
263 263 raise util.Abort(_('outstanding uncommitted changes'))
264 264 wlock = lock = None
265 265 try:
266 266 wlock = repo.wlock()
267 267 lock = repo.lock()
268 268 kwt.overwrite(None, expand, status[6])
269 269 finally:
270 270 release(lock, wlock)
271 271
272 272 def demo(ui, repo, *args, **opts):
273 273 '''print [keywordmaps] configuration and an expansion example
274 274
275 275 Show current, custom, or default keyword template maps and their
276 276 expansions.
277 277
278 278 Extend current configuration by specifying maps as arguments and
279 279 optionally by reading from an additional hgrc file.
280 280
281 281 Override current keyword template maps with "default" option.
282 282 '''
283 283 def demoitems(section, items):
284 284 ui.write('[%s]\n' % section)
285 285 for k, v in items:
286 286 ui.write('%s = %s\n' % (k, v))
287 287
288 288 msg = 'hg keyword config and expansion example'
289 289 kwstatus = 'current'
290 290 fn = 'demo.txt'
291 291 branchname = 'demobranch'
292 292 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
293 293 ui.note(_('creating temporary repository at %s\n') % tmpdir)
294 294 repo = localrepo.localrepository(ui, tmpdir, True)
295 295 ui.setconfig('keyword', fn, '')
296 296 if args or opts.get('rcfile'):
297 297 kwstatus = 'custom'
298 298 if opts.get('rcfile'):
299 299 ui.readconfig(opts.get('rcfile'))
300 300 if opts.get('default'):
301 301 kwstatus = 'default'
302 302 kwmaps = kwtemplater.templates
303 303 if ui.configitems('keywordmaps'):
304 304 # override maps from optional rcfile
305 305 for k, v in kwmaps.iteritems():
306 306 ui.setconfig('keywordmaps', k, v)
307 307 elif args:
308 308 # simulate hgrc parsing
309 309 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
310 310 fp = repo.opener('hgrc', 'w')
311 311 fp.writelines(rcmaps)
312 312 fp.close()
313 313 ui.readconfig(repo.join('hgrc'))
314 314 if not opts.get('default'):
315 315 kwmaps = dict(ui.configitems('keywordmaps')) or kwtemplater.templates
316 316 uisetup(ui)
317 317 reposetup(ui, repo)
318 318 for k, v in ui.configitems('extensions'):
319 319 if k.endswith('keyword'):
320 320 extension = '%s = %s' % (k, v)
321 321 break
322 322 ui.status(_('\n\tconfig using %s keyword template maps\n') % kwstatus)
323 323 ui.write('[extensions]\n%s\n' % extension)
324 324 demoitems('keyword', ui.configitems('keyword'))
325 325 demoitems('keywordmaps', kwmaps.iteritems())
326 326 keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n'
327 327 repo.wopener(fn, 'w').write(keywords)
328 328 repo.add([fn])
329 329 path = repo.wjoin(fn)
330 330 ui.note(_('\n%s keywords written to %s:\n') % (kwstatus, path))
331 331 ui.note(keywords)
332 332 ui.note('\nhg -R "%s" branch "%s"\n' % (tmpdir, branchname))
333 333 # silence branch command if not verbose
334 334 quiet = ui.quiet
335 335 ui.quiet = not ui.verbose
336 336 commands.branch(ui, repo, branchname)
337 337 ui.quiet = quiet
338 338 for name, cmd in ui.configitems('hooks'):
339 339 if name.split('.', 1)[0].find('commit') > -1:
340 340 repo.ui.setconfig('hooks', name, '')
341 341 ui.note(_('unhooked all commit hooks\n'))
342 342 ui.note('hg -R "%s" ci -m "%s"\n' % (tmpdir, msg))
343 343 repo.commit(text=msg)
344 344 fmt = ui.verbose and ' in %s' % path or ''
345 345 ui.status(_('\n\t%s keywords expanded%s\n') % (kwstatus, fmt))
346 346 ui.write(repo.wread(fn))
347 347 ui.debug(_('\nremoving temporary repository %s\n') % tmpdir)
348 348 shutil.rmtree(tmpdir, ignore_errors=True)
349 349
350 350 def expand(ui, repo, *pats, **opts):
351 351 '''expand keywords in the working directory
352 352
353 353 Run after (re)enabling keyword expansion.
354 354
355 355 kwexpand refuses to run if given files contain local changes.
356 356 '''
357 357 # 3rd argument sets expansion to True
358 358 _kwfwrite(ui, repo, True, *pats, **opts)
359 359
360 360 def files(ui, repo, *pats, **opts):
361 361 '''show files configured for keyword expansion
362 362
363 363 List which files in the working directory are matched by the [keyword]
364 364 configuration patterns.
365 365
366 366 Useful to prevent inadvertent keyword expansion and to speed up execution
367 367 by including only files that are actual candidates for expansion.
368 368
369 369 See "hg help keyword" on how to construct patterns both for inclusion and
370 370 exclusion of files.
371 371
372 372 Use -u/--untracked to list untracked files as well.
373 373
374 374 With -a/--all and -v/--verbose the codes used to show the status of files
375 375 are::
376 376
377 377 K = keyword expansion candidate
378 378 k = keyword expansion candidate (untracked)
379 379 I = ignored
380 380 i = ignored (untracked)
381 381 '''
382 382 kwt = kwtools['templater']
383 383 status = _status(ui, repo, kwt, opts.get('untracked'), *pats, **opts)
384 384 modified, added, removed, deleted, unknown, ignored, clean = status
385 385 files = sorted(modified + added + clean)
386 386 wctx = repo[None]
387 387 kwfiles = [f for f in files if kwt.iskwfile(f, wctx.flags)]
388 388 kwuntracked = [f for f in unknown if kwt.iskwfile(f, wctx.flags)]
389 389 cwd = pats and repo.getcwd() or ''
390 390 kwfstats = (not opts.get('ignore') and
391 391 (('K', kwfiles), ('k', kwuntracked),) or ())
392 392 if opts.get('all') or opts.get('ignore'):
393 393 kwfstats += (('I', [f for f in files if f not in kwfiles]),
394 394 ('i', [f for f in unknown if f not in kwuntracked]),)
395 395 for char, filenames in kwfstats:
396 396 fmt = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
397 397 for f in filenames:
398 398 ui.write(fmt % repo.pathto(f, cwd))
399 399
400 400 def shrink(ui, repo, *pats, **opts):
401 401 '''revert expanded keywords in the working directory
402 402
403 403 Run before changing/disabling active keywords or if you experience
404 404 problems with "hg import" or "hg merge".
405 405
406 406 kwshrink refuses to run if given files contain local changes.
407 407 '''
408 408 # 3rd argument sets expansion to False
409 409 _kwfwrite(ui, repo, False, *pats, **opts)
410 410
411 411
412 412 def uisetup(ui):
413 413 '''Collects [keyword] config in kwtools.
414 414 Monkeypatches dispatch._parse if needed.'''
415 415
416 416 for pat, opt in ui.configitems('keyword'):
417 417 if opt != 'ignore':
418 418 kwtools['inc'].append(pat)
419 419 else:
420 420 kwtools['exc'].append(pat)
421 421
422 422 if kwtools['inc']:
423 423 def kwdispatch_parse(orig, ui, args):
424 424 '''Monkeypatch dispatch._parse to obtain running hg command.'''
425 425 cmd, func, args, options, cmdoptions = orig(ui, args)
426 426 kwtools['hgcmd'] = cmd
427 427 return cmd, func, args, options, cmdoptions
428 428
429 429 extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse)
430 430
431 431 def reposetup(ui, repo):
432 432 '''Sets up repo as kwrepo for keyword substitution.
433 433 Overrides file method to return kwfilelog instead of filelog
434 434 if file matches user configuration.
435 435 Wraps commit to overwrite configured files with updated
436 436 keyword substitutions.
437 437 Monkeypatches patch and webcommands.'''
438 438
439 439 try:
440 440 if (not repo.local() or not kwtools['inc']
441 441 or kwtools['hgcmd'] in nokwcommands.split()
442 442 or '.hg' in util.splitpath(repo.root)
443 443 or repo._url.startswith('bundle:')):
444 444 return
445 445 except AttributeError:
446 446 pass
447 447
448 448 kwtools['templater'] = kwt = kwtemplater(ui, repo)
449 449
450 450 class kwrepo(repo.__class__):
451 451 def file(self, f):
452 452 if f[0] == '/':
453 453 f = f[1:]
454 454 return kwfilelog(self.sopener, kwt, f)
455 455
456 456 def wread(self, filename):
457 457 data = super(kwrepo, self).wread(filename)
458 458 return kwt.wread(filename, data)
459 459
460 460 def commit(self, *args, **opts):
461 461 # use custom commitctx for user commands
462 462 # other extensions can still wrap repo.commitctx directly
463 463 self.commitctx = self.kwcommitctx
464 464 try:
465 465 return super(kwrepo, self).commit(*args, **opts)
466 466 finally:
467 467 del self.commitctx
468 468
469 469 def kwcommitctx(self, ctx, error=False):
470 470 wlock = lock = None
471 471 try:
472 472 wlock = self.wlock()
473 473 lock = self.lock()
474 474 # store and postpone commit hooks
475 475 commithooks = {}
476 476 for name, cmd in ui.configitems('hooks'):
477 477 if name.split('.', 1)[0] == 'commit':
478 478 commithooks[name] = cmd
479 479 ui.setconfig('hooks', name, None)
480 480 if commithooks:
481 481 # store parents for commit hooks
482 482 p1, p2 = ctx.p1(), ctx.p2()
483 483 xp1, xp2 = p1.hex(), p2 and p2.hex() or ''
484 484
485 485 n = super(kwrepo, self).commitctx(ctx, error)
486 486
487 487 kwt.overwrite(n, True, None)
488 488 if commithooks:
489 489 for name, cmd in commithooks.iteritems():
490 490 ui.setconfig('hooks', name, cmd)
491 491 self.hook('commit', node=n, parent1=xp1, parent2=xp2)
492 492 return n
493 493 finally:
494 494 release(lock, wlock)
495 495
496 496 # monkeypatches
497 497 def kwpatchfile_init(orig, self, ui, fname, opener,
498 498 missing=False, eol=None):
499 499 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
500 500 rejects or conflicts due to expanded keywords in working dir.'''
501 501 orig(self, ui, fname, opener, missing, eol)
502 502 # shrink keywords read from working dir
503 503 self.lines = kwt.shrinklines(self.fname, self.lines)
504 504
505 505 def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None,
506 506 opts=None):
507 507 '''Monkeypatch patch.diff to avoid expansion except when
508 508 comparing against working dir.'''
509 509 if node2 is not None:
510 510 kwt.match = util.never
511 511 elif node1 is not None and node1 != repo['.'].node():
512 512 kwt.restrict = True
513 513 return orig(repo, node1, node2, match, changes, opts)
514 514
515 515 def kwweb_skip(orig, web, req, tmpl):
516 516 '''Wraps webcommands.x turning off keyword expansion.'''
517 517 kwt.match = util.never
518 518 return orig(web, req, tmpl)
519 519
520 520 repo.__class__ = kwrepo
521 521
522 522 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
523 523 extensions.wrapfunction(patch, 'diff', kw_diff)
524 524 for c in 'annotate changeset rev filediff diff'.split():
525 525 extensions.wrapfunction(webcommands, c, kwweb_skip)
526 526
527 527 cmdtable = {
528 528 'kwdemo':
529 529 (demo,
530 530 [('d', 'default', None, _('show default keyword template maps')),
531 ('f', 'rcfile', [], _('read maps from rcfile'))],
531 ('f', 'rcfile', '', _('read maps from rcfile'))],
532 532 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
533 533 'kwexpand': (expand, commands.walkopts,
534 534 _('hg kwexpand [OPTION]... [FILE]...')),
535 535 'kwfiles':
536 536 (files,
537 537 [('a', 'all', None, _('show keyword status flags of all files')),
538 538 ('i', 'ignore', None, _('show files excluded from expansion')),
539 539 ('u', 'untracked', None, _('additionally show untracked files')),
540 540 ] + commands.walkopts,
541 541 _('hg kwfiles [OPTION]... [FILE]...')),
542 542 'kwshrink': (shrink, commands.walkopts,
543 543 _('hg kwshrink [OPTION]... [FILE]...')),
544 544 }
@@ -1,3518 +1,3520 b''
1 1 # commands.py - command processing for mercurial
2 2 #
3 3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2, incorporated herein by reference.
7 7
8 8 from node import hex, nullid, nullrev, short
9 9 from lock import release
10 10 from i18n import _, gettext
11 11 import os, re, sys, subprocess, difflib, time, tempfile
12 12 import hg, util, revlog, bundlerepo, extensions, copies, context, error
13 13 import patch, help, mdiff, url, encoding
14 14 import archival, changegroup, cmdutil, sshserver, hbisect
15 15 from hgweb import server
16 16 import merge as merge_
17 17 import minirst
18 18
19 19 # Commands start here, listed alphabetically
20 20
21 21 def add(ui, repo, *pats, **opts):
22 22 """add the specified files on the next commit
23 23
24 24 Schedule files to be version controlled and added to the repository.
25 25
26 26 The files will be added to the repository at the next commit. To undo an
27 27 add before that, see hg forget.
28 28
29 29 If no names are given, add all files to the repository.
30 30 """
31 31
32 32 bad = []
33 33 exacts = {}
34 34 names = []
35 35 m = cmdutil.match(repo, pats, opts)
36 36 oldbad = m.bad
37 37 m.bad = lambda x,y: bad.append(x) or oldbad(x,y)
38 38
39 39 for f in repo.walk(m):
40 40 exact = m.exact(f)
41 41 if exact or f not in repo.dirstate:
42 42 names.append(f)
43 43 if ui.verbose or not exact:
44 44 ui.status(_('adding %s\n') % m.rel(f))
45 45 if not opts.get('dry_run'):
46 46 bad += [f for f in repo.add(names) if f in m.files()]
47 47 return bad and 1 or 0
48 48
49 49 def addremove(ui, repo, *pats, **opts):
50 50 """add all new files, delete all missing files
51 51
52 52 Add all new files and remove all missing files from the repository.
53 53
54 54 New files are ignored if they match any of the patterns in .hgignore. As
55 55 with add, these changes take effect at the next commit.
56 56
57 57 Use the -s/--similarity option to detect renamed files. With a parameter
58 58 greater than 0, this compares every removed file with every added file and
59 59 records those similar enough as renames. This option takes a percentage
60 60 between 0 (disabled) and 100 (files must be identical) as its parameter.
61 61 Detecting renamed files this way can be expensive.
62 62 """
63 63 try:
64 64 sim = float(opts.get('similarity') or 0)
65 65 except ValueError:
66 66 raise util.Abort(_('similarity must be a number'))
67 67 if sim < 0 or sim > 100:
68 68 raise util.Abort(_('similarity must be between 0 and 100'))
69 69 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
70 70
71 71 def annotate(ui, repo, *pats, **opts):
72 72 """show changeset information by line for each file
73 73
74 74 List changes in files, showing the revision id responsible for each line
75 75
76 76 This command is useful for discovering when a change was made and by whom.
77 77
78 78 Without the -a/--text option, annotate will avoid processing files it
79 79 detects as binary. With -a, annotate will annotate the file anyway,
80 80 although the results will probably be neither useful nor desirable.
81 81 """
82 82 datefunc = ui.quiet and util.shortdate or util.datestr
83 83 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
84 84
85 85 if not pats:
86 86 raise util.Abort(_('at least one filename or pattern is required'))
87 87
88 88 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
89 89 ('number', lambda x: str(x[0].rev())),
90 90 ('changeset', lambda x: short(x[0].node())),
91 91 ('date', getdate),
92 92 ('follow', lambda x: x[0].path()),
93 93 ]
94 94
95 95 if (not opts.get('user') and not opts.get('changeset') and not opts.get('date')
96 96 and not opts.get('follow')):
97 97 opts['number'] = 1
98 98
99 99 linenumber = opts.get('line_number') is not None
100 100 if (linenumber and (not opts.get('changeset')) and (not opts.get('number'))):
101 101 raise util.Abort(_('at least one of -n/-c is required for -l'))
102 102
103 103 funcmap = [func for op, func in opmap if opts.get(op)]
104 104 if linenumber:
105 105 lastfunc = funcmap[-1]
106 106 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
107 107
108 108 ctx = repo[opts.get('rev')]
109 109
110 110 m = cmdutil.match(repo, pats, opts)
111 111 for abs in ctx.walk(m):
112 112 fctx = ctx[abs]
113 113 if not opts.get('text') and util.binary(fctx.data()):
114 114 ui.write(_("%s: binary file\n") % ((pats and m.rel(abs)) or abs))
115 115 continue
116 116
117 117 lines = fctx.annotate(follow=opts.get('follow'),
118 118 linenumber=linenumber)
119 119 pieces = []
120 120
121 121 for f in funcmap:
122 122 l = [f(n) for n, dummy in lines]
123 123 if l:
124 124 ml = max(map(len, l))
125 125 pieces.append(["%*s" % (ml, x) for x in l])
126 126
127 127 if pieces:
128 128 for p, l in zip(zip(*pieces), lines):
129 129 ui.write("%s: %s" % (" ".join(p), l[1]))
130 130
131 131 def archive(ui, repo, dest, **opts):
132 132 '''create an unversioned archive of a repository revision
133 133
134 134 By default, the revision used is the parent of the working directory; use
135 135 -r/--rev to specify a different revision.
136 136
137 137 To specify the type of archive to create, use -t/--type. Valid types are::
138 138
139 139 "files" (default): a directory full of files
140 140 "tar": tar archive, uncompressed
141 141 "tbz2": tar archive, compressed using bzip2
142 142 "tgz": tar archive, compressed using gzip
143 143 "uzip": zip archive, uncompressed
144 144 "zip": zip archive, compressed using deflate
145 145
146 146 The exact name of the destination archive or directory is given using a
147 147 format string; see 'hg help export' for details.
148 148
149 149 Each member added to an archive file has a directory prefix prepended. Use
150 150 -p/--prefix to specify a format string for the prefix. The default is the
151 151 basename of the archive, with suffixes removed.
152 152 '''
153 153
154 154 ctx = repo[opts.get('rev')]
155 155 if not ctx:
156 156 raise util.Abort(_('no working directory: please specify a revision'))
157 157 node = ctx.node()
158 158 dest = cmdutil.make_filename(repo, dest, node)
159 159 if os.path.realpath(dest) == repo.root:
160 160 raise util.Abort(_('repository root cannot be destination'))
161 161 matchfn = cmdutil.match(repo, [], opts)
162 162 kind = opts.get('type') or 'files'
163 163 prefix = opts.get('prefix')
164 164 if dest == '-':
165 165 if kind == 'files':
166 166 raise util.Abort(_('cannot archive plain files to stdout'))
167 167 dest = sys.stdout
168 168 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
169 169 prefix = cmdutil.make_filename(repo, prefix, node)
170 170 archival.archive(repo, dest, node, kind, not opts.get('no_decode'),
171 171 matchfn, prefix)
172 172
173 173 def backout(ui, repo, node=None, rev=None, **opts):
174 174 '''reverse effect of earlier changeset
175 175
176 176 Commit the backed out changes as a new changeset. The new changeset is a
177 177 child of the backed out changeset.
178 178
179 179 If you backout a changeset other than the tip, a new head is created. This
180 180 head will be the new tip and you should merge this backout changeset with
181 181 another head.
182 182
183 183 The --merge option remembers the parent of the working directory before
184 184 starting the backout, then merges the new head with that changeset
185 185 afterwards. This saves you from doing the merge by hand. The result of
186 186 this merge is not committed, as with a normal merge.
187 187
188 188 See 'hg help dates' for a list of formats valid for -d/--date.
189 189 '''
190 190 if rev and node:
191 191 raise util.Abort(_("please specify just one revision"))
192 192
193 193 if not rev:
194 194 rev = node
195 195
196 196 if not rev:
197 197 raise util.Abort(_("please specify a revision to backout"))
198 198
199 199 date = opts.get('date')
200 200 if date:
201 201 opts['date'] = util.parsedate(date)
202 202
203 203 cmdutil.bail_if_changed(repo)
204 204 node = repo.lookup(rev)
205 205
206 206 op1, op2 = repo.dirstate.parents()
207 207 a = repo.changelog.ancestor(op1, node)
208 208 if a != node:
209 209 raise util.Abort(_('cannot backout change on a different branch'))
210 210
211 211 p1, p2 = repo.changelog.parents(node)
212 212 if p1 == nullid:
213 213 raise util.Abort(_('cannot backout a change with no parents'))
214 214 if p2 != nullid:
215 215 if not opts.get('parent'):
216 216 raise util.Abort(_('cannot backout a merge changeset without '
217 217 '--parent'))
218 218 p = repo.lookup(opts['parent'])
219 219 if p not in (p1, p2):
220 220 raise util.Abort(_('%s is not a parent of %s') %
221 221 (short(p), short(node)))
222 222 parent = p
223 223 else:
224 224 if opts.get('parent'):
225 225 raise util.Abort(_('cannot use --parent on non-merge changeset'))
226 226 parent = p1
227 227
228 228 # the backout should appear on the same branch
229 229 branch = repo.dirstate.branch()
230 230 hg.clean(repo, node, show_stats=False)
231 231 repo.dirstate.setbranch(branch)
232 232 revert_opts = opts.copy()
233 233 revert_opts['date'] = None
234 234 revert_opts['all'] = True
235 235 revert_opts['rev'] = hex(parent)
236 236 revert_opts['no_backup'] = None
237 237 revert(ui, repo, **revert_opts)
238 238 commit_opts = opts.copy()
239 239 commit_opts['addremove'] = False
240 240 if not commit_opts['message'] and not commit_opts['logfile']:
241 241 # we don't translate commit messages
242 242 commit_opts['message'] = "Backed out changeset %s" % short(node)
243 243 commit_opts['force_editor'] = True
244 244 commit(ui, repo, **commit_opts)
245 245 def nice(node):
246 246 return '%d:%s' % (repo.changelog.rev(node), short(node))
247 247 ui.status(_('changeset %s backs out changeset %s\n') %
248 248 (nice(repo.changelog.tip()), nice(node)))
249 249 if op1 != node:
250 250 hg.clean(repo, op1, show_stats=False)
251 251 if opts.get('merge'):
252 252 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
253 253 hg.merge(repo, hex(repo.changelog.tip()))
254 254 else:
255 255 ui.status(_('the backout changeset is a new head - '
256 256 'do not forget to merge\n'))
257 257 ui.status(_('(use "backout --merge" '
258 258 'if you want to auto-merge)\n'))
259 259
260 260 def bisect(ui, repo, rev=None, extra=None, command=None,
261 261 reset=None, good=None, bad=None, skip=None, noupdate=None):
262 262 """subdivision search of changesets
263 263
264 264 This command helps to find changesets which introduce problems. To use,
265 265 mark the earliest changeset you know exhibits the problem as bad, then
266 266 mark the latest changeset which is free from the problem as good. Bisect
267 267 will update your working directory to a revision for testing (unless the
268 268 -U/--noupdate option is specified). Once you have performed tests, mark
269 269 the working directory as good or bad, and bisect will either update to
270 270 another candidate changeset or announce that it has found the bad
271 271 revision.
272 272
273 273 As a shortcut, you can also use the revision argument to mark a revision
274 274 as good or bad without checking it out first.
275 275
276 276 If you supply a command, it will be used for automatic bisection. Its exit
277 277 status will be used to mark revisions as good or bad: status 0 means good,
278 278 125 means to skip the revision, 127 (command not found) will abort the
279 279 bisection, and any other non-zero exit status means the revision is bad.
280 280 """
281 281 def print_result(nodes, good):
282 282 displayer = cmdutil.show_changeset(ui, repo, {})
283 283 if len(nodes) == 1:
284 284 # narrowed it down to a single revision
285 285 if good:
286 286 ui.write(_("The first good revision is:\n"))
287 287 else:
288 288 ui.write(_("The first bad revision is:\n"))
289 289 displayer.show(repo[nodes[0]])
290 290 else:
291 291 # multiple possible revisions
292 292 if good:
293 293 ui.write(_("Due to skipped revisions, the first "
294 294 "good revision could be any of:\n"))
295 295 else:
296 296 ui.write(_("Due to skipped revisions, the first "
297 297 "bad revision could be any of:\n"))
298 298 for n in nodes:
299 299 displayer.show(repo[n])
300 300
301 301 def check_state(state, interactive=True):
302 302 if not state['good'] or not state['bad']:
303 303 if (good or bad or skip or reset) and interactive:
304 304 return
305 305 if not state['good']:
306 306 raise util.Abort(_('cannot bisect (no known good revisions)'))
307 307 else:
308 308 raise util.Abort(_('cannot bisect (no known bad revisions)'))
309 309 return True
310 310
311 311 # backward compatibility
312 312 if rev in "good bad reset init".split():
313 313 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
314 314 cmd, rev, extra = rev, extra, None
315 315 if cmd == "good":
316 316 good = True
317 317 elif cmd == "bad":
318 318 bad = True
319 319 else:
320 320 reset = True
321 321 elif extra or good + bad + skip + reset + bool(command) > 1:
322 322 raise util.Abort(_('incompatible arguments'))
323 323
324 324 if reset:
325 325 p = repo.join("bisect.state")
326 326 if os.path.exists(p):
327 327 os.unlink(p)
328 328 return
329 329
330 330 state = hbisect.load_state(repo)
331 331
332 332 if command:
333 333 commandpath = util.find_exe(command)
334 334 if commandpath is None:
335 335 raise util.Abort(_("cannot find executable: %s") % command)
336 336 changesets = 1
337 337 try:
338 338 while changesets:
339 339 # update state
340 340 status = subprocess.call([commandpath])
341 341 if status == 125:
342 342 transition = "skip"
343 343 elif status == 0:
344 344 transition = "good"
345 345 # status < 0 means process was killed
346 346 elif status == 127:
347 347 raise util.Abort(_("failed to execute %s") % command)
348 348 elif status < 0:
349 349 raise util.Abort(_("%s killed") % command)
350 350 else:
351 351 transition = "bad"
352 352 ctx = repo[rev or '.']
353 353 state[transition].append(ctx.node())
354 354 ui.status(_('Changeset %d:%s: %s\n') % (ctx, ctx, transition))
355 355 check_state(state, interactive=False)
356 356 # bisect
357 357 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
358 358 # update to next check
359 359 cmdutil.bail_if_changed(repo)
360 360 hg.clean(repo, nodes[0], show_stats=False)
361 361 finally:
362 362 hbisect.save_state(repo, state)
363 363 return print_result(nodes, not status)
364 364
365 365 # update state
366 366 node = repo.lookup(rev or '.')
367 367 if good:
368 368 state['good'].append(node)
369 369 elif bad:
370 370 state['bad'].append(node)
371 371 elif skip:
372 372 state['skip'].append(node)
373 373
374 374 hbisect.save_state(repo, state)
375 375
376 376 if not check_state(state):
377 377 return
378 378
379 379 # actually bisect
380 380 nodes, changesets, good = hbisect.bisect(repo.changelog, state)
381 381 if changesets == 0:
382 382 print_result(nodes, good)
383 383 else:
384 384 assert len(nodes) == 1 # only a single node can be tested next
385 385 node = nodes[0]
386 386 # compute the approximate number of remaining tests
387 387 tests, size = 0, 2
388 388 while size <= changesets:
389 389 tests, size = tests + 1, size * 2
390 390 rev = repo.changelog.rev(node)
391 391 ui.write(_("Testing changeset %d:%s "
392 392 "(%d changesets remaining, ~%d tests)\n")
393 393 % (rev, short(node), changesets, tests))
394 394 if not noupdate:
395 395 cmdutil.bail_if_changed(repo)
396 396 return hg.clean(repo, node)
397 397
398 398 def branch(ui, repo, label=None, **opts):
399 399 """set or show the current branch name
400 400
401 401 With no argument, show the current branch name. With one argument, set the
402 402 working directory branch name (the branch will not exist in the repository
403 403 until the next commit). Standard practice recommends that primary
404 404 development take place on the 'default' branch.
405 405
406 406 Unless -f/--force is specified, branch will not let you set a branch name
407 407 that already exists, even if it's inactive.
408 408
409 409 Use -C/--clean to reset the working directory branch to that of the parent
410 410 of the working directory, negating a previous branch change.
411 411
412 412 Use the command 'hg update' to switch to an existing branch. Use 'hg
413 413 commit --close-branch' to mark this branch as closed.
414 414 """
415 415
416 416 if opts.get('clean'):
417 417 label = repo[None].parents()[0].branch()
418 418 repo.dirstate.setbranch(label)
419 419 ui.status(_('reset working directory to branch %s\n') % label)
420 420 elif label:
421 421 if not opts.get('force') and label in repo.branchtags():
422 422 if label not in [p.branch() for p in repo.parents()]:
423 423 raise util.Abort(_('a branch of the same name already exists'
424 424 ' (use --force to override)'))
425 425 repo.dirstate.setbranch(encoding.fromlocal(label))
426 426 ui.status(_('marked working directory as branch %s\n') % label)
427 427 else:
428 428 ui.write("%s\n" % encoding.tolocal(repo.dirstate.branch()))
429 429
430 430 def branches(ui, repo, active=False, closed=False):
431 431 """list repository named branches
432 432
433 433 List the repository's named branches, indicating which ones are inactive.
434 434 If -c/--closed is specified, also list branches which have been marked
435 435 closed (see hg commit --close-branch).
436 436
437 437 If -a/--active is specified, only show active branches. A branch is
438 438 considered active if it contains repository heads.
439 439
440 440 Use the command 'hg update' to switch to an existing branch.
441 441 """
442 442
443 443 hexfunc = ui.debugflag and hex or short
444 444 activebranches = [encoding.tolocal(repo[n].branch())
445 445 for n in repo.heads()]
446 446 def testactive(tag, node):
447 447 realhead = tag in activebranches
448 448 open = node in repo.branchheads(tag, closed=False)
449 449 return realhead and open
450 450 branches = sorted([(testactive(tag, node), repo.changelog.rev(node), tag)
451 451 for tag, node in repo.branchtags().items()],
452 452 reverse=True)
453 453
454 454 for isactive, node, tag in branches:
455 455 if (not active) or isactive:
456 456 if ui.quiet:
457 457 ui.write("%s\n" % tag)
458 458 else:
459 459 hn = repo.lookup(node)
460 460 if isactive:
461 461 notice = ''
462 462 elif hn not in repo.branchheads(tag, closed=False):
463 463 if not closed:
464 464 continue
465 465 notice = ' (closed)'
466 466 else:
467 467 notice = ' (inactive)'
468 468 rev = str(node).rjust(31 - encoding.colwidth(tag))
469 469 data = tag, rev, hexfunc(hn), notice
470 470 ui.write("%s %s:%s%s\n" % data)
471 471
472 472 def bundle(ui, repo, fname, dest=None, **opts):
473 473 """create a changegroup file
474 474
475 475 Generate a compressed changegroup file collecting changesets not known to
476 476 be in another repository.
477 477
478 478 If no destination repository is specified the destination is assumed to
479 479 have all the nodes specified by one or more --base parameters. To create a
480 480 bundle containing all changesets, use -a/--all (or --base null).
481 481
482 482 You can change compression method with the -t/--type option. The available
483 483 compression methods are: none, bzip2, and gzip (by default, bundles are
484 484 compressed using bzip2).
485 485
486 486 The bundle file can then be transferred using conventional means and
487 487 applied to another repository with the unbundle or pull command. This is
488 488 useful when direct push and pull are not available or when exporting an
489 489 entire repository is undesirable.
490 490
491 491 Applying bundles preserves all changeset contents including permissions,
492 492 copy/rename information, and revision history.
493 493 """
494 494 revs = opts.get('rev') or None
495 495 if revs:
496 496 revs = [repo.lookup(rev) for rev in revs]
497 497 if opts.get('all'):
498 498 base = ['null']
499 499 else:
500 500 base = opts.get('base')
501 501 if base:
502 502 if dest:
503 503 raise util.Abort(_("--base is incompatible with specifying "
504 504 "a destination"))
505 505 base = [repo.lookup(rev) for rev in base]
506 506 # create the right base
507 507 # XXX: nodesbetween / changegroup* should be "fixed" instead
508 508 o = []
509 509 has = set((nullid,))
510 510 for n in base:
511 511 has.update(repo.changelog.reachable(n))
512 512 if revs:
513 513 visit = list(revs)
514 514 else:
515 515 visit = repo.changelog.heads()
516 516 seen = {}
517 517 while visit:
518 518 n = visit.pop(0)
519 519 parents = [p for p in repo.changelog.parents(n) if p not in has]
520 520 if len(parents) == 0:
521 521 o.insert(0, n)
522 522 else:
523 523 for p in parents:
524 524 if p not in seen:
525 525 seen[p] = 1
526 526 visit.append(p)
527 527 else:
528 528 dest, revs, checkout = hg.parseurl(
529 529 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
530 530 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
531 531 o = repo.findoutgoing(other, force=opts.get('force'))
532 532
533 533 if revs:
534 534 cg = repo.changegroupsubset(o, revs, 'bundle')
535 535 else:
536 536 cg = repo.changegroup(o, 'bundle')
537 537
538 538 bundletype = opts.get('type', 'bzip2').lower()
539 539 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
540 540 bundletype = btypes.get(bundletype)
541 541 if bundletype not in changegroup.bundletypes:
542 542 raise util.Abort(_('unknown bundle type specified with --type'))
543 543
544 544 changegroup.writebundle(cg, fname, bundletype)
545 545
546 546 def cat(ui, repo, file1, *pats, **opts):
547 547 """output the current or given revision of files
548 548
549 549 Print the specified files as they were at the given revision. If no
550 550 revision is given, the parent of the working directory is used, or tip if
551 551 no revision is checked out.
552 552
553 553 Output may be to a file, in which case the name of the file is given using
554 554 a format string. The formatting rules are the same as for the export
555 555 command, with the following additions::
556 556
557 557 %s basename of file being printed
558 558 %d dirname of file being printed, or '.' if in repository root
559 559 %p root-relative path name of file being printed
560 560 """
561 561 ctx = repo[opts.get('rev')]
562 562 err = 1
563 563 m = cmdutil.match(repo, (file1,) + pats, opts)
564 564 for abs in ctx.walk(m):
565 565 fp = cmdutil.make_file(repo, opts.get('output'), ctx.node(), pathname=abs)
566 566 data = ctx[abs].data()
567 567 if opts.get('decode'):
568 568 data = repo.wwritedata(abs, data)
569 569 fp.write(data)
570 570 err = 0
571 571 return err
572 572
573 573 def clone(ui, source, dest=None, **opts):
574 574 """make a copy of an existing repository
575 575
576 576 Create a copy of an existing repository in a new directory.
577 577
578 578 If no destination directory name is specified, it defaults to the basename
579 579 of the source.
580 580
581 581 The location of the source is added to the new repository's .hg/hgrc file,
582 582 as the default to be used for future pulls.
583 583
584 584 If you use the -r/--rev option to clone up to a specific revision, no
585 585 subsequent revisions (including subsequent tags) will be present in the
586 586 cloned repository. This option implies --pull, even on local repositories.
587 587
588 588 By default, clone will check out the head of the 'default' branch. If the
589 589 -U/--noupdate option is used, the new clone will contain only a repository
590 590 (.hg) and no working copy (the working copy parent is the null revision).
591 591
592 592 See 'hg help urls' for valid source format details.
593 593
594 594 It is possible to specify an ssh:// URL as the destination, but no
595 595 .hg/hgrc and working directory will be created on the remote side. Please
596 596 see 'hg help urls' for important details about ssh:// URLs.
597 597
598 598 For efficiency, hardlinks are used for cloning whenever the source and
599 599 destination are on the same filesystem (note this applies only to the
600 600 repository data, not to the checked out files). Some filesystems, such as
601 601 AFS, implement hardlinking incorrectly, but do not report errors. In these
602 602 cases, use the --pull option to avoid hardlinking.
603 603
604 604 In some cases, you can clone repositories and checked out files using full
605 605 hardlinks with ::
606 606
607 607 $ cp -al REPO REPOCLONE
608 608
609 609 This is the fastest way to clone, but it is not always safe. The operation
610 610 is not atomic (making sure REPO is not modified during the operation is up
611 611 to you) and you have to make sure your editor breaks hardlinks (Emacs and
612 612 most Linux Kernel tools do so). Also, this is not compatible with certain
613 613 extensions that place their metadata under the .hg directory, such as mq.
614 614 """
615 615 hg.clone(cmdutil.remoteui(ui, opts), source, dest,
616 616 pull=opts.get('pull'),
617 617 stream=opts.get('uncompressed'),
618 618 rev=opts.get('rev'),
619 619 update=not opts.get('noupdate'))
620 620
621 621 def commit(ui, repo, *pats, **opts):
622 622 """commit the specified files or all outstanding changes
623 623
624 624 Commit changes to the given files into the repository. Unlike a
625 625 centralized RCS, this operation is a local operation. See hg push for a
626 626 way to actively distribute your changes.
627 627
628 628 If a list of files is omitted, all changes reported by "hg status" will be
629 629 committed.
630 630
631 631 If you are committing the result of a merge, do not provide any filenames
632 632 or -I/-X filters.
633 633
634 634 If no commit message is specified, the configured editor is started to
635 635 prompt you for a message.
636 636
637 637 See 'hg help dates' for a list of formats valid for -d/--date.
638 638 """
639 639 extra = {}
640 640 if opts.get('close_branch'):
641 641 extra['close'] = 1
642 642 e = cmdutil.commiteditor
643 643 if opts.get('force_editor'):
644 644 e = cmdutil.commitforceeditor
645 645
646 646 def commitfunc(ui, repo, message, match, opts):
647 647 return repo.commit(message, opts.get('user'), opts.get('date'), match,
648 648 editor=e, extra=extra)
649 649
650 650 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
651 651 if not node:
652 652 ui.status(_("nothing changed\n"))
653 653 return
654 654 cl = repo.changelog
655 655 rev = cl.rev(node)
656 656 parents = cl.parentrevs(rev)
657 657 if rev - 1 in parents:
658 658 # one of the parents was the old tip
659 659 pass
660 660 elif (parents == (nullrev, nullrev) or
661 661 len(cl.heads(cl.node(parents[0]))) > 1 and
662 662 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
663 663 ui.status(_('created new head\n'))
664 664
665 665 if ui.debugflag:
666 666 ui.write(_('committed changeset %d:%s\n') % (rev, hex(node)))
667 667 elif ui.verbose:
668 668 ui.write(_('committed changeset %d:%s\n') % (rev, short(node)))
669 669
670 670 def copy(ui, repo, *pats, **opts):
671 671 """mark files as copied for the next commit
672 672
673 673 Mark dest as having copies of source files. If dest is a directory, copies
674 674 are put in that directory. If dest is a file, the source must be a single
675 675 file.
676 676
677 677 By default, this command copies the contents of files as they exist in the
678 678 working directory. If invoked with -A/--after, the operation is recorded,
679 679 but no copying is performed.
680 680
681 681 This command takes effect with the next commit. To undo a copy before
682 682 that, see hg revert.
683 683 """
684 684 wlock = repo.wlock(False)
685 685 try:
686 686 return cmdutil.copy(ui, repo, pats, opts)
687 687 finally:
688 688 wlock.release()
689 689
690 690 def debugancestor(ui, repo, *args):
691 691 """find the ancestor revision of two revisions in a given index"""
692 692 if len(args) == 3:
693 693 index, rev1, rev2 = args
694 694 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
695 695 lookup = r.lookup
696 696 elif len(args) == 2:
697 697 if not repo:
698 698 raise util.Abort(_("There is no Mercurial repository here "
699 699 "(.hg not found)"))
700 700 rev1, rev2 = args
701 701 r = repo.changelog
702 702 lookup = repo.lookup
703 703 else:
704 704 raise util.Abort(_('either two or three arguments required'))
705 705 a = r.ancestor(lookup(rev1), lookup(rev2))
706 706 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
707 707
708 708 def debugcommands(ui, cmd='', *args):
709 709 for cmd, vals in sorted(table.iteritems()):
710 710 cmd = cmd.split('|')[0].strip('^')
711 711 opts = ', '.join([i[1] for i in vals[1]])
712 712 ui.write('%s: %s\n' % (cmd, opts))
713 713
714 714 def debugcomplete(ui, cmd='', **opts):
715 715 """returns the completion list associated with the given command"""
716 716
717 717 if opts.get('options'):
718 718 options = []
719 719 otables = [globalopts]
720 720 if cmd:
721 721 aliases, entry = cmdutil.findcmd(cmd, table, False)
722 722 otables.append(entry[1])
723 723 for t in otables:
724 724 for o in t:
725 725 if o[0]:
726 726 options.append('-%s' % o[0])
727 727 options.append('--%s' % o[1])
728 728 ui.write("%s\n" % "\n".join(options))
729 729 return
730 730
731 731 cmdlist = cmdutil.findpossible(cmd, table)
732 732 if ui.verbose:
733 733 cmdlist = [' '.join(c[0]) for c in cmdlist.values()]
734 734 ui.write("%s\n" % "\n".join(sorted(cmdlist)))
735 735
736 736 def debugfsinfo(ui, path = "."):
737 737 open('.debugfsinfo', 'w').write('')
738 738 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
739 739 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
740 740 ui.write('case-sensitive: %s\n' % (util.checkcase('.debugfsinfo')
741 741 and 'yes' or 'no'))
742 742 os.unlink('.debugfsinfo')
743 743
744 744 def debugrebuildstate(ui, repo, rev="tip"):
745 745 """rebuild the dirstate as it would look like for the given revision"""
746 746 ctx = repo[rev]
747 747 wlock = repo.wlock()
748 748 try:
749 749 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
750 750 finally:
751 751 wlock.release()
752 752
753 753 def debugcheckstate(ui, repo):
754 754 """validate the correctness of the current dirstate"""
755 755 parent1, parent2 = repo.dirstate.parents()
756 756 m1 = repo[parent1].manifest()
757 757 m2 = repo[parent2].manifest()
758 758 errors = 0
759 759 for f in repo.dirstate:
760 760 state = repo.dirstate[f]
761 761 if state in "nr" and f not in m1:
762 762 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
763 763 errors += 1
764 764 if state in "a" and f in m1:
765 765 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
766 766 errors += 1
767 767 if state in "m" and f not in m1 and f not in m2:
768 768 ui.warn(_("%s in state %s, but not in either manifest\n") %
769 769 (f, state))
770 770 errors += 1
771 771 for f in m1:
772 772 state = repo.dirstate[f]
773 773 if state not in "nrm":
774 774 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
775 775 errors += 1
776 776 if errors:
777 777 error = _(".hg/dirstate inconsistent with current parent's manifest")
778 778 raise util.Abort(error)
779 779
780 780 def showconfig(ui, repo, *values, **opts):
781 781 """show combined config settings from all hgrc files
782 782
783 783 With no arguments, print names and values of all config items.
784 784
785 785 With one argument of the form section.name, print just the value of that
786 786 config item.
787 787
788 788 With multiple arguments, print names and values of all config items with
789 789 matching section names.
790 790
791 791 With --debug, the source (filename and line number) is printed for each
792 792 config item.
793 793 """
794 794
795 795 untrusted = bool(opts.get('untrusted'))
796 796 if values:
797 797 if len([v for v in values if '.' in v]) > 1:
798 798 raise util.Abort(_('only one config item permitted'))
799 799 for section, name, value in ui.walkconfig(untrusted=untrusted):
800 800 sectname = section + '.' + name
801 801 if values:
802 802 for v in values:
803 803 if v == section:
804 804 ui.debug('%s: ' %
805 805 ui.configsource(section, name, untrusted))
806 806 ui.write('%s=%s\n' % (sectname, value))
807 807 elif v == sectname:
808 808 ui.debug('%s: ' %
809 809 ui.configsource(section, name, untrusted))
810 810 ui.write(value, '\n')
811 811 else:
812 812 ui.debug('%s: ' %
813 813 ui.configsource(section, name, untrusted))
814 814 ui.write('%s=%s\n' % (sectname, value))
815 815
816 816 def debugsetparents(ui, repo, rev1, rev2=None):
817 817 """manually set the parents of the current working directory
818 818
819 819 This is useful for writing repository conversion tools, but should be used
820 820 with care.
821 821 """
822 822
823 823 if not rev2:
824 824 rev2 = hex(nullid)
825 825
826 826 wlock = repo.wlock()
827 827 try:
828 828 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
829 829 finally:
830 830 wlock.release()
831 831
832 832 def debugstate(ui, repo, nodates=None):
833 833 """show the contents of the current dirstate"""
834 834 timestr = ""
835 835 showdate = not nodates
836 836 for file_, ent in sorted(repo.dirstate._map.iteritems()):
837 837 if showdate:
838 838 if ent[3] == -1:
839 839 # Pad or slice to locale representation
840 840 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
841 841 timestr = 'unset'
842 842 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
843 843 else:
844 844 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
845 845 if ent[1] & 020000:
846 846 mode = 'lnk'
847 847 else:
848 848 mode = '%3o' % (ent[1] & 0777)
849 849 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
850 850 for f in repo.dirstate.copies():
851 851 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
852 852
853 853 def debugsub(ui, repo, rev=None):
854 854 if rev == '':
855 855 rev = None
856 856 for k,v in sorted(repo[rev].substate.items()):
857 857 ui.write('path %s\n' % k)
858 858 ui.write(' source %s\n' % v[0])
859 859 ui.write(' revision %s\n' % v[1])
860 860
861 861 def debugdata(ui, file_, rev):
862 862 """dump the contents of a data file revision"""
863 863 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
864 864 try:
865 865 ui.write(r.revision(r.lookup(rev)))
866 866 except KeyError:
867 867 raise util.Abort(_('invalid revision identifier %s') % rev)
868 868
869 869 def debugdate(ui, date, range=None, **opts):
870 870 """parse and display a date"""
871 871 if opts["extended"]:
872 872 d = util.parsedate(date, util.extendeddateformats)
873 873 else:
874 874 d = util.parsedate(date)
875 875 ui.write("internal: %s %s\n" % d)
876 876 ui.write("standard: %s\n" % util.datestr(d))
877 877 if range:
878 878 m = util.matchdate(range)
879 879 ui.write("match: %s\n" % m(d[0]))
880 880
881 881 def debugindex(ui, file_):
882 882 """dump the contents of an index file"""
883 883 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
884 884 ui.write(" rev offset length base linkrev"
885 885 " nodeid p1 p2\n")
886 886 for i in r:
887 887 node = r.node(i)
888 888 try:
889 889 pp = r.parents(node)
890 890 except:
891 891 pp = [nullid, nullid]
892 892 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
893 893 i, r.start(i), r.length(i), r.base(i), r.linkrev(i),
894 894 short(node), short(pp[0]), short(pp[1])))
895 895
896 896 def debugindexdot(ui, file_):
897 897 """dump an index DAG as a graphviz dot file"""
898 898 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
899 899 ui.write("digraph G {\n")
900 900 for i in r:
901 901 node = r.node(i)
902 902 pp = r.parents(node)
903 903 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
904 904 if pp[1] != nullid:
905 905 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
906 906 ui.write("}\n")
907 907
908 908 def debuginstall(ui):
909 909 '''test Mercurial installation'''
910 910
911 911 def writetemp(contents):
912 912 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
913 913 f = os.fdopen(fd, "wb")
914 914 f.write(contents)
915 915 f.close()
916 916 return name
917 917
918 918 problems = 0
919 919
920 920 # encoding
921 921 ui.status(_("Checking encoding (%s)...\n") % encoding.encoding)
922 922 try:
923 923 encoding.fromlocal("test")
924 924 except util.Abort, inst:
925 925 ui.write(" %s\n" % inst)
926 926 ui.write(_(" (check that your locale is properly set)\n"))
927 927 problems += 1
928 928
929 929 # compiled modules
930 930 ui.status(_("Checking extensions...\n"))
931 931 try:
932 932 import bdiff, mpatch, base85
933 933 except Exception, inst:
934 934 ui.write(" %s\n" % inst)
935 935 ui.write(_(" One or more extensions could not be found"))
936 936 ui.write(_(" (check that you compiled the extensions)\n"))
937 937 problems += 1
938 938
939 939 # templates
940 940 ui.status(_("Checking templates...\n"))
941 941 try:
942 942 import templater
943 943 templater.templater(templater.templatepath("map-cmdline.default"))
944 944 except Exception, inst:
945 945 ui.write(" %s\n" % inst)
946 946 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
947 947 problems += 1
948 948
949 949 # patch
950 950 ui.status(_("Checking patch...\n"))
951 951 patchproblems = 0
952 952 a = "1\n2\n3\n4\n"
953 953 b = "1\n2\n3\ninsert\n4\n"
954 954 fa = writetemp(a)
955 955 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
956 956 os.path.basename(fa))
957 957 fd = writetemp(d)
958 958
959 959 files = {}
960 960 try:
961 961 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
962 962 except util.Abort, e:
963 963 ui.write(_(" patch call failed:\n"))
964 964 ui.write(" " + str(e) + "\n")
965 965 patchproblems += 1
966 966 else:
967 967 if list(files) != [os.path.basename(fa)]:
968 968 ui.write(_(" unexpected patch output!\n"))
969 969 patchproblems += 1
970 970 a = open(fa).read()
971 971 if a != b:
972 972 ui.write(_(" patch test failed!\n"))
973 973 patchproblems += 1
974 974
975 975 if patchproblems:
976 976 if ui.config('ui', 'patch'):
977 977 ui.write(_(" (Current patch tool may be incompatible with patch,"
978 978 " or misconfigured. Please check your .hgrc file)\n"))
979 979 else:
980 980 ui.write(_(" Internal patcher failure, please report this error"
981 981 " to http://mercurial.selenic.com/bts/\n"))
982 982 problems += patchproblems
983 983
984 984 os.unlink(fa)
985 985 os.unlink(fd)
986 986
987 987 # editor
988 988 ui.status(_("Checking commit editor...\n"))
989 989 editor = ui.geteditor()
990 990 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
991 991 if not cmdpath:
992 992 if editor == 'vi':
993 993 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
994 994 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
995 995 else:
996 996 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
997 997 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
998 998 problems += 1
999 999
1000 1000 # check username
1001 1001 ui.status(_("Checking username...\n"))
1002 1002 user = os.environ.get("HGUSER")
1003 1003 if user is None:
1004 1004 user = ui.config("ui", "username")
1005 1005 if user is None:
1006 1006 user = os.environ.get("EMAIL")
1007 1007 if not user:
1008 1008 ui.warn(" ")
1009 1009 ui.username()
1010 1010 ui.write(_(" (specify a username in your .hgrc file)\n"))
1011 1011
1012 1012 if not problems:
1013 1013 ui.status(_("No problems detected\n"))
1014 1014 else:
1015 1015 ui.write(_("%s problems detected,"
1016 1016 " please check your install!\n") % problems)
1017 1017
1018 1018 return problems
1019 1019
1020 1020 def debugrename(ui, repo, file1, *pats, **opts):
1021 1021 """dump rename information"""
1022 1022
1023 1023 ctx = repo[opts.get('rev')]
1024 1024 m = cmdutil.match(repo, (file1,) + pats, opts)
1025 1025 for abs in ctx.walk(m):
1026 1026 fctx = ctx[abs]
1027 1027 o = fctx.filelog().renamed(fctx.filenode())
1028 1028 rel = m.rel(abs)
1029 1029 if o:
1030 1030 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
1031 1031 else:
1032 1032 ui.write(_("%s not renamed\n") % rel)
1033 1033
1034 1034 def debugwalk(ui, repo, *pats, **opts):
1035 1035 """show how files match on given patterns"""
1036 1036 m = cmdutil.match(repo, pats, opts)
1037 1037 items = list(repo.walk(m))
1038 1038 if not items:
1039 1039 return
1040 1040 fmt = 'f %%-%ds %%-%ds %%s' % (
1041 1041 max([len(abs) for abs in items]),
1042 1042 max([len(m.rel(abs)) for abs in items]))
1043 1043 for abs in items:
1044 1044 line = fmt % (abs, m.rel(abs), m.exact(abs) and 'exact' or '')
1045 1045 ui.write("%s\n" % line.rstrip())
1046 1046
1047 1047 def diff(ui, repo, *pats, **opts):
1048 1048 """diff repository (or selected files)
1049 1049
1050 1050 Show differences between revisions for the specified files.
1051 1051
1052 1052 Differences between files are shown using the unified diff format.
1053 1053
1054 1054 NOTE: diff may generate unexpected results for merges, as it will default
1055 1055 to comparing against the working directory's first parent changeset if no
1056 1056 revisions are specified.
1057 1057
1058 1058 When two revision arguments are given, then changes are shown between
1059 1059 those revisions. If only one revision is specified then that revision is
1060 1060 compared to the working directory, and, when no revisions are specified,
1061 1061 the working directory files are compared to its parent.
1062 1062
1063 1063 Without the -a/--text option, diff will avoid generating diffs of files it
1064 1064 detects as binary. With -a, diff will generate a diff anyway, probably
1065 1065 with undesirable results.
1066 1066
1067 1067 Use the -g/--git option to generate diffs in the git extended diff format.
1068 1068 For more information, read 'hg help diffs'.
1069 1069 """
1070 1070
1071 1071 revs = opts.get('rev')
1072 1072 change = opts.get('change')
1073 1073
1074 1074 if revs and change:
1075 1075 msg = _('cannot specify --rev and --change at the same time')
1076 1076 raise util.Abort(msg)
1077 1077 elif change:
1078 1078 node2 = repo.lookup(change)
1079 1079 node1 = repo[node2].parents()[0].node()
1080 1080 else:
1081 1081 node1, node2 = cmdutil.revpair(repo, revs)
1082 1082
1083 1083 m = cmdutil.match(repo, pats, opts)
1084 1084 it = patch.diff(repo, node1, node2, match=m, opts=patch.diffopts(ui, opts))
1085 1085 for chunk in it:
1086 1086 ui.write(chunk)
1087 1087
1088 1088 def export(ui, repo, *changesets, **opts):
1089 1089 """dump the header and diffs for one or more changesets
1090 1090
1091 1091 Print the changeset header and diffs for one or more revisions.
1092 1092
1093 1093 The information shown in the changeset header is: author, changeset hash,
1094 1094 parent(s) and commit comment.
1095 1095
1096 1096 NOTE: export may generate unexpected diff output for merge changesets, as
1097 1097 it will compare the merge changeset against its first parent only.
1098 1098
1099 1099 Output may be to a file, in which case the name of the file is given using
1100 1100 a format string. The formatting rules are as follows::
1101 1101
1102 1102 %% literal "%" character
1103 1103 %H changeset hash (40 bytes of hexadecimal)
1104 1104 %N number of patches being generated
1105 1105 %R changeset revision number
1106 1106 %b basename of the exporting repository
1107 1107 %h short-form changeset hash (12 bytes of hexadecimal)
1108 1108 %n zero-padded sequence number, starting at 1
1109 1109 %r zero-padded changeset revision number
1110 1110
1111 1111 Without the -a/--text option, export will avoid generating diffs of files
1112 1112 it detects as binary. With -a, export will generate a diff anyway,
1113 1113 probably with undesirable results.
1114 1114
1115 1115 Use the -g/--git option to generate diffs in the git extended diff format.
1116 1116 See 'hg help diffs' for more information.
1117 1117
1118 1118 With the --switch-parent option, the diff will be against the second
1119 1119 parent. It can be useful to review a merge.
1120 1120 """
1121 1121 if not changesets:
1122 1122 raise util.Abort(_("export requires at least one changeset"))
1123 1123 revs = cmdutil.revrange(repo, changesets)
1124 1124 if len(revs) > 1:
1125 1125 ui.note(_('exporting patches:\n'))
1126 1126 else:
1127 1127 ui.note(_('exporting patch:\n'))
1128 1128 patch.export(repo, revs, template=opts.get('output'),
1129 1129 switch_parent=opts.get('switch_parent'),
1130 1130 opts=patch.diffopts(ui, opts))
1131 1131
1132 1132 def forget(ui, repo, *pats, **opts):
1133 1133 """forget the specified files on the next commit
1134 1134
1135 1135 Mark the specified files so they will no longer be tracked after the next
1136 1136 commit.
1137 1137
1138 1138 This only removes files from the current branch, not from the entire
1139 1139 project history, and it does not delete them from the working directory.
1140 1140
1141 1141 To undo a forget before the next commit, see hg add.
1142 1142 """
1143 1143
1144 1144 if not pats:
1145 1145 raise util.Abort(_('no files specified'))
1146 1146
1147 1147 m = cmdutil.match(repo, pats, opts)
1148 1148 s = repo.status(match=m, clean=True)
1149 1149 forget = sorted(s[0] + s[1] + s[3] + s[6])
1150 1150
1151 1151 for f in m.files():
1152 1152 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
1153 1153 ui.warn(_('not removing %s: file is already untracked\n')
1154 1154 % m.rel(f))
1155 1155
1156 1156 for f in forget:
1157 1157 if ui.verbose or not m.exact(f):
1158 1158 ui.status(_('removing %s\n') % m.rel(f))
1159 1159
1160 1160 repo.remove(forget, unlink=False)
1161 1161
1162 1162 def grep(ui, repo, pattern, *pats, **opts):
1163 1163 """search for a pattern in specified files and revisions
1164 1164
1165 1165 Search revisions of files for a regular expression.
1166 1166
1167 1167 This command behaves differently than Unix grep. It only accepts
1168 1168 Python/Perl regexps. It searches repository history, not the working
1169 1169 directory. It always prints the revision number in which a match appears.
1170 1170
1171 1171 By default, grep only prints output for the first revision of a file in
1172 1172 which it finds a match. To get it to print every revision that contains a
1173 1173 change in match status ("-" for a match that becomes a non-match, or "+"
1174 1174 for a non-match that becomes a match), use the --all flag.
1175 1175 """
1176 1176 reflags = 0
1177 1177 if opts.get('ignore_case'):
1178 1178 reflags |= re.I
1179 1179 try:
1180 1180 regexp = re.compile(pattern, reflags)
1181 1181 except Exception, inst:
1182 1182 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1183 1183 return None
1184 1184 sep, eol = ':', '\n'
1185 1185 if opts.get('print0'):
1186 1186 sep = eol = '\0'
1187 1187
1188 1188 getfile = util.lrucachefunc(repo.file)
1189 1189
1190 1190 def matchlines(body):
1191 1191 begin = 0
1192 1192 linenum = 0
1193 1193 while True:
1194 1194 match = regexp.search(body, begin)
1195 1195 if not match:
1196 1196 break
1197 1197 mstart, mend = match.span()
1198 1198 linenum += body.count('\n', begin, mstart) + 1
1199 1199 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1200 1200 begin = body.find('\n', mend) + 1 or len(body)
1201 1201 lend = begin - 1
1202 1202 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1203 1203
1204 1204 class linestate(object):
1205 1205 def __init__(self, line, linenum, colstart, colend):
1206 1206 self.line = line
1207 1207 self.linenum = linenum
1208 1208 self.colstart = colstart
1209 1209 self.colend = colend
1210 1210
1211 1211 def __hash__(self):
1212 1212 return hash((self.linenum, self.line))
1213 1213
1214 1214 def __eq__(self, other):
1215 1215 return self.line == other.line
1216 1216
1217 1217 matches = {}
1218 1218 copies = {}
1219 1219 def grepbody(fn, rev, body):
1220 1220 matches[rev].setdefault(fn, [])
1221 1221 m = matches[rev][fn]
1222 1222 for lnum, cstart, cend, line in matchlines(body):
1223 1223 s = linestate(line, lnum, cstart, cend)
1224 1224 m.append(s)
1225 1225
1226 1226 def difflinestates(a, b):
1227 1227 sm = difflib.SequenceMatcher(None, a, b)
1228 1228 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1229 1229 if tag == 'insert':
1230 1230 for i in xrange(blo, bhi):
1231 1231 yield ('+', b[i])
1232 1232 elif tag == 'delete':
1233 1233 for i in xrange(alo, ahi):
1234 1234 yield ('-', a[i])
1235 1235 elif tag == 'replace':
1236 1236 for i in xrange(alo, ahi):
1237 1237 yield ('-', a[i])
1238 1238 for i in xrange(blo, bhi):
1239 1239 yield ('+', b[i])
1240 1240
1241 1241 def display(fn, r, pstates, states):
1242 1242 datefunc = ui.quiet and util.shortdate or util.datestr
1243 1243 found = False
1244 1244 filerevmatches = {}
1245 1245 if opts.get('all'):
1246 1246 iter = difflinestates(pstates, states)
1247 1247 else:
1248 1248 iter = [('', l) for l in states]
1249 1249 for change, l in iter:
1250 1250 cols = [fn, str(r)]
1251 1251 if opts.get('line_number'):
1252 1252 cols.append(str(l.linenum))
1253 1253 if opts.get('all'):
1254 1254 cols.append(change)
1255 1255 if opts.get('user'):
1256 1256 cols.append(ui.shortuser(get(r)[1]))
1257 1257 if opts.get('date'):
1258 1258 cols.append(datefunc(get(r)[2]))
1259 1259 if opts.get('files_with_matches'):
1260 1260 c = (fn, r)
1261 1261 if c in filerevmatches:
1262 1262 continue
1263 1263 filerevmatches[c] = 1
1264 1264 else:
1265 1265 cols.append(l.line)
1266 1266 ui.write(sep.join(cols), eol)
1267 1267 found = True
1268 1268 return found
1269 1269
1270 1270 skip = {}
1271 1271 revfiles = {}
1272 1272 get = util.cachefunc(lambda r: repo[r].changeset())
1273 1273 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1274 1274 found = False
1275 1275 follow = opts.get('follow')
1276 1276 for st, rev, fns in changeiter:
1277 1277 if st == 'window':
1278 1278 matches.clear()
1279 1279 revfiles.clear()
1280 1280 elif st == 'add':
1281 1281 ctx = repo[rev]
1282 1282 pctx = ctx.parents()[0]
1283 1283 parent = pctx.rev()
1284 1284 matches.setdefault(rev, {})
1285 1285 matches.setdefault(parent, {})
1286 1286 files = revfiles.setdefault(rev, [])
1287 1287 for fn in fns:
1288 1288 flog = getfile(fn)
1289 1289 try:
1290 1290 fnode = ctx.filenode(fn)
1291 1291 except error.LookupError:
1292 1292 continue
1293 1293
1294 1294 copied = flog.renamed(fnode)
1295 1295 copy = follow and copied and copied[0]
1296 1296 if copy:
1297 1297 copies.setdefault(rev, {})[fn] = copy
1298 1298 if fn in skip:
1299 1299 if copy:
1300 1300 skip[copy] = True
1301 1301 continue
1302 1302 files.append(fn)
1303 1303
1304 1304 if not matches[rev].has_key(fn):
1305 1305 grepbody(fn, rev, flog.read(fnode))
1306 1306
1307 1307 pfn = copy or fn
1308 1308 if not matches[parent].has_key(pfn):
1309 1309 try:
1310 1310 fnode = pctx.filenode(pfn)
1311 1311 grepbody(pfn, parent, flog.read(fnode))
1312 1312 except error.LookupError:
1313 1313 pass
1314 1314 elif st == 'iter':
1315 1315 parent = repo[rev].parents()[0].rev()
1316 1316 for fn in sorted(revfiles.get(rev, [])):
1317 1317 states = matches[rev][fn]
1318 1318 copy = copies.get(rev, {}).get(fn)
1319 1319 if fn in skip:
1320 1320 if copy:
1321 1321 skip[copy] = True
1322 1322 continue
1323 1323 pstates = matches.get(parent, {}).get(copy or fn, [])
1324 1324 if pstates or states:
1325 1325 r = display(fn, rev, pstates, states)
1326 1326 found = found or r
1327 1327 if r and not opts.get('all'):
1328 1328 skip[fn] = True
1329 1329 if copy:
1330 1330 skip[copy] = True
1331 1331
1332 1332 def heads(ui, repo, *branchrevs, **opts):
1333 1333 """show current repository heads or show branch heads
1334 1334
1335 1335 With no arguments, show all repository head changesets.
1336 1336
1337 1337 Repository "heads" are changesets that don't have child changesets. They
1338 1338 are where development generally takes place and are the usual targets for
1339 1339 update and merge operations.
1340 1340
1341 1341 If one or more REV is given, the "branch heads" will be shown for the
1342 1342 named branch associated with that revision. The name of the branch is
1343 1343 called the revision's branch tag.
1344 1344
1345 1345 Branch heads are revisions on a given named branch that do not have any
1346 1346 descendants on the same branch. A branch head could be a true head or it
1347 1347 could be the last changeset on a branch before a new branch was created.
1348 1348 If none of the branch heads are true heads, the branch is considered
1349 1349 inactive. If -c/--closed is specified, also show branch heads marked
1350 1350 closed (see hg commit --close-branch).
1351 1351
1352 1352 If STARTREV is specified only those heads (or branch heads) that are
1353 1353 descendants of STARTREV will be displayed.
1354 1354 """
1355 1355 if opts.get('rev'):
1356 1356 start = repo.lookup(opts['rev'])
1357 1357 else:
1358 1358 start = None
1359 1359 closed = opts.get('closed')
1360 1360 hideinactive, _heads = opts.get('active'), None
1361 1361 if not branchrevs:
1362 if closed:
1363 raise error.Abort(_('you must specify a branch to use --closed'))
1362 1364 # Assume we're looking repo-wide heads if no revs were specified.
1363 1365 heads = repo.heads(start)
1364 1366 else:
1365 1367 if hideinactive:
1366 1368 _heads = repo.heads(start)
1367 1369 heads = []
1368 1370 visitedset = set()
1369 1371 for branchrev in branchrevs:
1370 1372 branch = repo[branchrev].branch()
1371 1373 if branch in visitedset:
1372 1374 continue
1373 1375 visitedset.add(branch)
1374 1376 bheads = repo.branchheads(branch, start, closed=closed)
1375 1377 if not bheads:
1376 1378 if not opts.get('rev'):
1377 1379 ui.warn(_("no open branch heads on branch %s\n") % branch)
1378 1380 elif branch != branchrev:
1379 1381 ui.warn(_("no changes on branch %s containing %s are "
1380 1382 "reachable from %s\n")
1381 1383 % (branch, branchrev, opts.get('rev')))
1382 1384 else:
1383 1385 ui.warn(_("no changes on branch %s are reachable from %s\n")
1384 1386 % (branch, opts.get('rev')))
1385 1387 if hideinactive:
1386 1388 bheads = [bhead for bhead in bheads if bhead in _heads]
1387 1389 heads.extend(bheads)
1388 1390 if not heads:
1389 1391 return 1
1390 1392 displayer = cmdutil.show_changeset(ui, repo, opts)
1391 1393 for n in heads:
1392 1394 displayer.show(repo[n])
1393 1395
1394 1396 def help_(ui, name=None, with_version=False):
1395 1397 """show help for a given topic or a help overview
1396 1398
1397 1399 With no arguments, print a list of commands with short help messages.
1398 1400
1399 1401 Given a topic, extension, or command name, print help for that topic.
1400 1402 """
1401 1403 option_lists = []
1402 1404 textwidth = util.termwidth() - 2
1403 1405
1404 1406 def addglobalopts(aliases):
1405 1407 if ui.verbose:
1406 1408 option_lists.append((_("global options:"), globalopts))
1407 1409 if name == 'shortlist':
1408 1410 option_lists.append((_('use "hg help" for the full list '
1409 1411 'of commands'), ()))
1410 1412 else:
1411 1413 if name == 'shortlist':
1412 1414 msg = _('use "hg help" for the full list of commands '
1413 1415 'or "hg -v" for details')
1414 1416 elif aliases:
1415 1417 msg = _('use "hg -v help%s" to show aliases and '
1416 1418 'global options') % (name and " " + name or "")
1417 1419 else:
1418 1420 msg = _('use "hg -v help %s" to show global options') % name
1419 1421 option_lists.append((msg, ()))
1420 1422
1421 1423 def helpcmd(name):
1422 1424 if with_version:
1423 1425 version_(ui)
1424 1426 ui.write('\n')
1425 1427
1426 1428 try:
1427 1429 aliases, i = cmdutil.findcmd(name, table, False)
1428 1430 except error.AmbiguousCommand, inst:
1429 1431 # py3k fix: except vars can't be used outside the scope of the
1430 1432 # except block, nor can be used inside a lambda. python issue4617
1431 1433 prefix = inst.args[0]
1432 1434 select = lambda c: c.lstrip('^').startswith(prefix)
1433 1435 helplist(_('list of commands:\n\n'), select)
1434 1436 return
1435 1437
1436 1438 # synopsis
1437 1439 if len(i) > 2:
1438 1440 if i[2].startswith('hg'):
1439 1441 ui.write("%s\n" % i[2])
1440 1442 else:
1441 1443 ui.write('hg %s %s\n' % (aliases[0], i[2]))
1442 1444 else:
1443 1445 ui.write('hg %s\n' % aliases[0])
1444 1446
1445 1447 # aliases
1446 1448 if not ui.quiet and len(aliases) > 1:
1447 1449 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1448 1450
1449 1451 # description
1450 1452 doc = gettext(i[0].__doc__)
1451 1453 if not doc:
1452 1454 doc = _("(no help text available)")
1453 1455 if ui.quiet:
1454 1456 doc = doc.splitlines()[0]
1455 1457 ui.write("\n%s\n" % minirst.format(doc, textwidth))
1456 1458
1457 1459 if not ui.quiet:
1458 1460 # options
1459 1461 if i[1]:
1460 1462 option_lists.append((_("options:\n"), i[1]))
1461 1463
1462 1464 addglobalopts(False)
1463 1465
1464 1466 def helplist(header, select=None):
1465 1467 h = {}
1466 1468 cmds = {}
1467 1469 for c, e in table.iteritems():
1468 1470 f = c.split("|", 1)[0]
1469 1471 if select and not select(f):
1470 1472 continue
1471 1473 if (not select and name != 'shortlist' and
1472 1474 e[0].__module__ != __name__):
1473 1475 continue
1474 1476 if name == "shortlist" and not f.startswith("^"):
1475 1477 continue
1476 1478 f = f.lstrip("^")
1477 1479 if not ui.debugflag and f.startswith("debug"):
1478 1480 continue
1479 1481 doc = e[0].__doc__
1480 1482 if doc and 'DEPRECATED' in doc and not ui.verbose:
1481 1483 continue
1482 1484 doc = gettext(doc)
1483 1485 if not doc:
1484 1486 doc = _("(no help text available)")
1485 1487 h[f] = doc.splitlines()[0].rstrip()
1486 1488 cmds[f] = c.lstrip("^")
1487 1489
1488 1490 if not h:
1489 1491 ui.status(_('no commands defined\n'))
1490 1492 return
1491 1493
1492 1494 ui.status(header)
1493 1495 fns = sorted(h)
1494 1496 m = max(map(len, fns))
1495 1497 for f in fns:
1496 1498 if ui.verbose:
1497 1499 commands = cmds[f].replace("|",", ")
1498 1500 ui.write(" %s:\n %s\n"%(commands, h[f]))
1499 1501 else:
1500 1502 ui.write(' %-*s %s\n' % (m, f, util.wrap(h[f], m + 4)))
1501 1503
1502 1504 if name != 'shortlist':
1503 1505 exts, maxlength = extensions.enabled()
1504 1506 text = help.listexts(_('enabled extensions:'), exts, maxlength)
1505 1507 if text:
1506 1508 ui.write("\n%s\n" % minirst.format(text, textwidth))
1507 1509
1508 1510 if not ui.quiet:
1509 1511 addglobalopts(True)
1510 1512
1511 1513 def helptopic(name):
1512 1514 for names, header, doc in help.helptable:
1513 1515 if name in names:
1514 1516 break
1515 1517 else:
1516 1518 raise error.UnknownCommand(name)
1517 1519
1518 1520 # description
1519 1521 if not doc:
1520 1522 doc = _("(no help text available)")
1521 1523 if hasattr(doc, '__call__'):
1522 1524 doc = doc()
1523 1525
1524 1526 ui.write("%s\n\n" % header)
1525 1527 ui.write("%s\n" % minirst.format(doc, textwidth))
1526 1528
1527 1529 def helpext(name):
1528 1530 try:
1529 1531 mod = extensions.find(name)
1530 1532 except KeyError:
1531 1533 raise error.UnknownCommand(name)
1532 1534
1533 1535 doc = gettext(mod.__doc__) or _('no help text available')
1534 1536 head, tail = doc.split('\n', 1)
1535 1537 ui.write(_('%s extension - %s\n\n') % (name.split('.')[-1], head))
1536 1538 if tail:
1537 1539 ui.write(minirst.format(tail, textwidth))
1538 1540 ui.status('\n\n')
1539 1541
1540 1542 try:
1541 1543 ct = mod.cmdtable
1542 1544 except AttributeError:
1543 1545 ct = {}
1544 1546
1545 1547 modcmds = set([c.split('|', 1)[0] for c in ct])
1546 1548 helplist(_('list of commands:\n\n'), modcmds.__contains__)
1547 1549
1548 1550 if name and name != 'shortlist':
1549 1551 i = None
1550 1552 for f in (helptopic, helpcmd, helpext):
1551 1553 try:
1552 1554 f(name)
1553 1555 i = None
1554 1556 break
1555 1557 except error.UnknownCommand, inst:
1556 1558 i = inst
1557 1559 if i:
1558 1560 raise i
1559 1561
1560 1562 else:
1561 1563 # program name
1562 1564 if ui.verbose or with_version:
1563 1565 version_(ui)
1564 1566 else:
1565 1567 ui.status(_("Mercurial Distributed SCM\n"))
1566 1568 ui.status('\n')
1567 1569
1568 1570 # list of commands
1569 1571 if name == "shortlist":
1570 1572 header = _('basic commands:\n\n')
1571 1573 else:
1572 1574 header = _('list of commands:\n\n')
1573 1575
1574 1576 helplist(header)
1575 1577
1576 1578 # list all option lists
1577 1579 opt_output = []
1578 1580 for title, options in option_lists:
1579 1581 opt_output.append(("\n%s" % title, None))
1580 1582 for shortopt, longopt, default, desc in options:
1581 1583 if "DEPRECATED" in desc and not ui.verbose: continue
1582 1584 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1583 1585 longopt and " --%s" % longopt),
1584 1586 "%s%s" % (desc,
1585 1587 default
1586 1588 and _(" (default: %s)") % default
1587 1589 or "")))
1588 1590
1589 1591 if not name:
1590 1592 ui.write(_("\nadditional help topics:\n\n"))
1591 1593 topics = []
1592 1594 for names, header, doc in help.helptable:
1593 1595 names = [(-len(name), name) for name in names]
1594 1596 names.sort()
1595 1597 topics.append((names[0][1], header))
1596 1598 topics_len = max([len(s[0]) for s in topics])
1597 1599 for t, desc in topics:
1598 1600 ui.write(" %-*s %s\n" % (topics_len, t, desc))
1599 1601
1600 1602 if opt_output:
1601 1603 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1602 1604 for first, second in opt_output:
1603 1605 if second:
1604 1606 second = util.wrap(second, opts_len + 3)
1605 1607 ui.write(" %-*s %s\n" % (opts_len, first, second))
1606 1608 else:
1607 1609 ui.write("%s\n" % first)
1608 1610
1609 1611 def identify(ui, repo, source=None,
1610 1612 rev=None, num=None, id=None, branch=None, tags=None):
1611 1613 """identify the working copy or specified revision
1612 1614
1613 1615 With no revision, print a summary of the current state of the repository.
1614 1616
1615 1617 Specifying a path to a repository root or Mercurial bundle will cause
1616 1618 lookup to operate on that repository/bundle.
1617 1619
1618 1620 This summary identifies the repository state using one or two parent hash
1619 1621 identifiers, followed by a "+" if there are uncommitted changes in the
1620 1622 working directory, a list of tags for this revision and a branch name for
1621 1623 non-default branches.
1622 1624 """
1623 1625
1624 1626 if not repo and not source:
1625 1627 raise util.Abort(_("There is no Mercurial repository here "
1626 1628 "(.hg not found)"))
1627 1629
1628 1630 hexfunc = ui.debugflag and hex or short
1629 1631 default = not (num or id or branch or tags)
1630 1632 output = []
1631 1633
1632 1634 revs = []
1633 1635 if source:
1634 1636 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1635 1637 repo = hg.repository(ui, source)
1636 1638
1637 1639 if not repo.local():
1638 1640 if not rev and revs:
1639 1641 rev = revs[0]
1640 1642 if not rev:
1641 1643 rev = "tip"
1642 1644 if num or branch or tags:
1643 1645 raise util.Abort(
1644 1646 "can't query remote revision number, branch, or tags")
1645 1647 output = [hexfunc(repo.lookup(rev))]
1646 1648 elif not rev:
1647 1649 ctx = repo[None]
1648 1650 parents = ctx.parents()
1649 1651 changed = False
1650 1652 if default or id or num:
1651 1653 changed = ctx.files() + ctx.deleted()
1652 1654 if default or id:
1653 1655 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1654 1656 (changed) and "+" or "")]
1655 1657 if num:
1656 1658 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1657 1659 (changed) and "+" or ""))
1658 1660 else:
1659 1661 ctx = repo[rev]
1660 1662 if default or id:
1661 1663 output = [hexfunc(ctx.node())]
1662 1664 if num:
1663 1665 output.append(str(ctx.rev()))
1664 1666
1665 1667 if repo.local() and default and not ui.quiet:
1666 1668 b = encoding.tolocal(ctx.branch())
1667 1669 if b != 'default':
1668 1670 output.append("(%s)" % b)
1669 1671
1670 1672 # multiple tags for a single parent separated by '/'
1671 1673 t = "/".join(ctx.tags())
1672 1674 if t:
1673 1675 output.append(t)
1674 1676
1675 1677 if branch:
1676 1678 output.append(encoding.tolocal(ctx.branch()))
1677 1679
1678 1680 if tags:
1679 1681 output.extend(ctx.tags())
1680 1682
1681 1683 ui.write("%s\n" % ' '.join(output))
1682 1684
1683 1685 def import_(ui, repo, patch1, *patches, **opts):
1684 1686 """import an ordered set of patches
1685 1687
1686 1688 Import a list of patches and commit them individually.
1687 1689
1688 1690 If there are outstanding changes in the working directory, import will
1689 1691 abort unless given the -f/--force flag.
1690 1692
1691 1693 You can import a patch straight from a mail message. Even patches as
1692 1694 attachments work (to use the body part, it must have type text/plain or
1693 1695 text/x-patch). From and Subject headers of email message are used as
1694 1696 default committer and commit message. All text/plain body parts before
1695 1697 first diff are added to commit message.
1696 1698
1697 1699 If the imported patch was generated by hg export, user and description
1698 1700 from patch override values from message headers and body. Values given on
1699 1701 command line with -m/--message and -u/--user override these.
1700 1702
1701 1703 If --exact is specified, import will set the working directory to the
1702 1704 parent of each patch before applying it, and will abort if the resulting
1703 1705 changeset has a different ID than the one recorded in the patch. This may
1704 1706 happen due to character set problems or other deficiencies in the text
1705 1707 patch format.
1706 1708
1707 1709 With -s/--similarity, hg will attempt to discover renames and copies in
1708 1710 the patch in the same way as 'addremove'.
1709 1711
1710 1712 To read a patch from standard input, use "-" as the patch name. If a URL
1711 1713 is specified, the patch will be downloaded from it. See 'hg help dates'
1712 1714 for a list of formats valid for -d/--date.
1713 1715 """
1714 1716 patches = (patch1,) + patches
1715 1717
1716 1718 date = opts.get('date')
1717 1719 if date:
1718 1720 opts['date'] = util.parsedate(date)
1719 1721
1720 1722 try:
1721 1723 sim = float(opts.get('similarity') or 0)
1722 1724 except ValueError:
1723 1725 raise util.Abort(_('similarity must be a number'))
1724 1726 if sim < 0 or sim > 100:
1725 1727 raise util.Abort(_('similarity must be between 0 and 100'))
1726 1728
1727 1729 if opts.get('exact') or not opts.get('force'):
1728 1730 cmdutil.bail_if_changed(repo)
1729 1731
1730 1732 d = opts["base"]
1731 1733 strip = opts["strip"]
1732 1734 wlock = lock = None
1733 1735 try:
1734 1736 wlock = repo.wlock()
1735 1737 lock = repo.lock()
1736 1738 for p in patches:
1737 1739 pf = os.path.join(d, p)
1738 1740
1739 1741 if pf == '-':
1740 1742 ui.status(_("applying patch from stdin\n"))
1741 1743 pf = sys.stdin
1742 1744 else:
1743 1745 ui.status(_("applying %s\n") % p)
1744 1746 pf = url.open(ui, pf)
1745 1747 data = patch.extract(ui, pf)
1746 1748 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1747 1749
1748 1750 if tmpname is None:
1749 1751 raise util.Abort(_('no diffs found'))
1750 1752
1751 1753 try:
1752 1754 cmdline_message = cmdutil.logmessage(opts)
1753 1755 if cmdline_message:
1754 1756 # pickup the cmdline msg
1755 1757 message = cmdline_message
1756 1758 elif message:
1757 1759 # pickup the patch msg
1758 1760 message = message.strip()
1759 1761 else:
1760 1762 # launch the editor
1761 1763 message = None
1762 1764 ui.debug(_('message:\n%s\n') % message)
1763 1765
1764 1766 wp = repo.parents()
1765 1767 if opts.get('exact'):
1766 1768 if not nodeid or not p1:
1767 1769 raise util.Abort(_('not a Mercurial patch'))
1768 1770 p1 = repo.lookup(p1)
1769 1771 p2 = repo.lookup(p2 or hex(nullid))
1770 1772
1771 1773 if p1 != wp[0].node():
1772 1774 hg.clean(repo, p1)
1773 1775 repo.dirstate.setparents(p1, p2)
1774 1776 elif p2:
1775 1777 try:
1776 1778 p1 = repo.lookup(p1)
1777 1779 p2 = repo.lookup(p2)
1778 1780 if p1 == wp[0].node():
1779 1781 repo.dirstate.setparents(p1, p2)
1780 1782 except error.RepoError:
1781 1783 pass
1782 1784 if opts.get('exact') or opts.get('import_branch'):
1783 1785 repo.dirstate.setbranch(branch or 'default')
1784 1786
1785 1787 files = {}
1786 1788 try:
1787 1789 patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1788 1790 files=files, eolmode=None)
1789 1791 finally:
1790 1792 files = patch.updatedir(ui, repo, files, similarity=sim/100.)
1791 1793 if not opts.get('no_commit'):
1792 1794 m = cmdutil.matchfiles(repo, files or [])
1793 1795 n = repo.commit(message, opts.get('user') or user,
1794 1796 opts.get('date') or date, match=m,
1795 1797 editor=cmdutil.commiteditor)
1796 1798 if opts.get('exact'):
1797 1799 if hex(n) != nodeid:
1798 1800 repo.rollback()
1799 1801 raise util.Abort(_('patch is damaged'
1800 1802 ' or loses information'))
1801 1803 # Force a dirstate write so that the next transaction
1802 1804 # backups an up-do-date file.
1803 1805 repo.dirstate.write()
1804 1806 finally:
1805 1807 os.unlink(tmpname)
1806 1808 finally:
1807 1809 release(lock, wlock)
1808 1810
1809 1811 def incoming(ui, repo, source="default", **opts):
1810 1812 """show new changesets found in source
1811 1813
1812 1814 Show new changesets found in the specified path/URL or the default pull
1813 1815 location. These are the changesets that would have been pulled if a pull
1814 1816 at the time you issued this command.
1815 1817
1816 1818 For remote repository, using --bundle avoids downloading the changesets
1817 1819 twice if the incoming is followed by a pull.
1818 1820
1819 1821 See pull for valid source format details.
1820 1822 """
1821 1823 limit = cmdutil.loglimit(opts)
1822 1824 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
1823 1825 other = hg.repository(cmdutil.remoteui(repo, opts), source)
1824 1826 ui.status(_('comparing with %s\n') % url.hidepassword(source))
1825 1827 if revs:
1826 1828 revs = [other.lookup(rev) for rev in revs]
1827 1829 common, incoming, rheads = repo.findcommonincoming(other, heads=revs,
1828 1830 force=opts["force"])
1829 1831 if not incoming:
1830 1832 try:
1831 1833 os.unlink(opts["bundle"])
1832 1834 except:
1833 1835 pass
1834 1836 ui.status(_("no changes found\n"))
1835 1837 return 1
1836 1838
1837 1839 cleanup = None
1838 1840 try:
1839 1841 fname = opts["bundle"]
1840 1842 if fname or not other.local():
1841 1843 # create a bundle (uncompressed if other repo is not local)
1842 1844
1843 1845 if revs is None and other.capable('changegroupsubset'):
1844 1846 revs = rheads
1845 1847
1846 1848 if revs is None:
1847 1849 cg = other.changegroup(incoming, "incoming")
1848 1850 else:
1849 1851 cg = other.changegroupsubset(incoming, revs, 'incoming')
1850 1852 bundletype = other.local() and "HG10BZ" or "HG10UN"
1851 1853 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1852 1854 # keep written bundle?
1853 1855 if opts["bundle"]:
1854 1856 cleanup = None
1855 1857 if not other.local():
1856 1858 # use the created uncompressed bundlerepo
1857 1859 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1858 1860
1859 1861 o = other.changelog.nodesbetween(incoming, revs)[0]
1860 1862 if opts.get('newest_first'):
1861 1863 o.reverse()
1862 1864 displayer = cmdutil.show_changeset(ui, other, opts)
1863 1865 count = 0
1864 1866 for n in o:
1865 1867 if count >= limit:
1866 1868 break
1867 1869 parents = [p for p in other.changelog.parents(n) if p != nullid]
1868 1870 if opts.get('no_merges') and len(parents) == 2:
1869 1871 continue
1870 1872 count += 1
1871 1873 displayer.show(other[n])
1872 1874 finally:
1873 1875 if hasattr(other, 'close'):
1874 1876 other.close()
1875 1877 if cleanup:
1876 1878 os.unlink(cleanup)
1877 1879
1878 1880 def init(ui, dest=".", **opts):
1879 1881 """create a new repository in the given directory
1880 1882
1881 1883 Initialize a new repository in the given directory. If the given directory
1882 1884 does not exist, it will be created.
1883 1885
1884 1886 If no directory is given, the current directory is used.
1885 1887
1886 1888 It is possible to specify an ssh:// URL as the destination. See 'hg help
1887 1889 urls' for more information.
1888 1890 """
1889 1891 hg.repository(cmdutil.remoteui(ui, opts), dest, create=1)
1890 1892
1891 1893 def locate(ui, repo, *pats, **opts):
1892 1894 """locate files matching specific patterns
1893 1895
1894 1896 Print files under Mercurial control in the working directory whose names
1895 1897 match the given patterns.
1896 1898
1897 1899 By default, this command searches all directories in the working
1898 1900 directory. To search just the current directory and its subdirectories,
1899 1901 use "--include .".
1900 1902
1901 1903 If no patterns are given to match, this command prints the names of all
1902 1904 files under Mercurial control in the working directory.
1903 1905
1904 1906 If you want to feed the output of this command into the "xargs" command,
1905 1907 use the -0 option to both this command and "xargs". This will avoid the
1906 1908 problem of "xargs" treating single filenames that contain whitespace as
1907 1909 multiple filenames.
1908 1910 """
1909 1911 end = opts.get('print0') and '\0' or '\n'
1910 1912 rev = opts.get('rev') or None
1911 1913
1912 1914 ret = 1
1913 1915 m = cmdutil.match(repo, pats, opts, default='relglob')
1914 1916 m.bad = lambda x,y: False
1915 1917 for abs in repo[rev].walk(m):
1916 1918 if not rev and abs not in repo.dirstate:
1917 1919 continue
1918 1920 if opts.get('fullpath'):
1919 1921 ui.write(repo.wjoin(abs), end)
1920 1922 else:
1921 1923 ui.write(((pats and m.rel(abs)) or abs), end)
1922 1924 ret = 0
1923 1925
1924 1926 return ret
1925 1927
1926 1928 def log(ui, repo, *pats, **opts):
1927 1929 """show revision history of entire repository or files
1928 1930
1929 1931 Print the revision history of the specified files or the entire project.
1930 1932
1931 1933 File history is shown without following rename or copy history of files.
1932 1934 Use -f/--follow with a filename to follow history across renames and
1933 1935 copies. --follow without a filename will only show ancestors or
1934 1936 descendants of the starting revision. --follow-first only follows the
1935 1937 first parent of merge revisions.
1936 1938
1937 1939 If no revision range is specified, the default is tip:0 unless --follow is
1938 1940 set, in which case the working directory parent is used as the starting
1939 1941 revision.
1940 1942
1941 1943 See 'hg help dates' for a list of formats valid for -d/--date.
1942 1944
1943 1945 By default this command prints revision number and changeset id, tags,
1944 1946 non-trivial parents, user, date and time, and a summary for each commit.
1945 1947 When the -v/--verbose switch is used, the list of changed files and full
1946 1948 commit message are shown.
1947 1949
1948 1950 NOTE: log -p/--patch may generate unexpected diff output for merge
1949 1951 changesets, as it will only compare the merge changeset against its first
1950 1952 parent. Also, only files different from BOTH parents will appear in
1951 1953 files:.
1952 1954 """
1953 1955
1954 1956 get = util.cachefunc(lambda r: repo[r].changeset())
1955 1957 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1956 1958
1957 1959 limit = cmdutil.loglimit(opts)
1958 1960 count = 0
1959 1961
1960 1962 if opts.get('copies') and opts.get('rev'):
1961 1963 endrev = max(cmdutil.revrange(repo, opts.get('rev'))) + 1
1962 1964 else:
1963 1965 endrev = len(repo)
1964 1966 rcache = {}
1965 1967 ncache = {}
1966 1968 def getrenamed(fn, rev):
1967 1969 '''looks up all renames for a file (up to endrev) the first
1968 1970 time the file is given. It indexes on the changerev and only
1969 1971 parses the manifest if linkrev != changerev.
1970 1972 Returns rename info for fn at changerev rev.'''
1971 1973 if fn not in rcache:
1972 1974 rcache[fn] = {}
1973 1975 ncache[fn] = {}
1974 1976 fl = repo.file(fn)
1975 1977 for i in fl:
1976 1978 node = fl.node(i)
1977 1979 lr = fl.linkrev(i)
1978 1980 renamed = fl.renamed(node)
1979 1981 rcache[fn][lr] = renamed
1980 1982 if renamed:
1981 1983 ncache[fn][node] = renamed
1982 1984 if lr >= endrev:
1983 1985 break
1984 1986 if rev in rcache[fn]:
1985 1987 return rcache[fn][rev]
1986 1988
1987 1989 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1988 1990 # filectx logic.
1989 1991
1990 1992 try:
1991 1993 return repo[rev][fn].renamed()
1992 1994 except error.LookupError:
1993 1995 pass
1994 1996 return None
1995 1997
1996 1998 df = False
1997 1999 if opts["date"]:
1998 2000 df = util.matchdate(opts["date"])
1999 2001
2000 2002 only_branches = opts.get('only_branch')
2001 2003
2002 2004 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
2003 2005 for st, rev, fns in changeiter:
2004 2006 if st == 'add':
2005 2007 parents = [p for p in repo.changelog.parentrevs(rev)
2006 2008 if p != nullrev]
2007 2009 if opts.get('no_merges') and len(parents) == 2:
2008 2010 continue
2009 2011 if opts.get('only_merges') and len(parents) != 2:
2010 2012 continue
2011 2013
2012 2014 if only_branches:
2013 2015 revbranch = get(rev)[5]['branch']
2014 2016 if revbranch not in only_branches:
2015 2017 continue
2016 2018
2017 2019 if df:
2018 2020 changes = get(rev)
2019 2021 if not df(changes[2][0]):
2020 2022 continue
2021 2023
2022 2024 if opts.get('keyword'):
2023 2025 changes = get(rev)
2024 2026 miss = 0
2025 2027 for k in [kw.lower() for kw in opts['keyword']]:
2026 2028 if not (k in changes[1].lower() or
2027 2029 k in changes[4].lower() or
2028 2030 k in " ".join(changes[3]).lower()):
2029 2031 miss = 1
2030 2032 break
2031 2033 if miss:
2032 2034 continue
2033 2035
2034 2036 if opts['user']:
2035 2037 changes = get(rev)
2036 2038 if not [k for k in opts['user'] if k in changes[1]]:
2037 2039 continue
2038 2040
2039 2041 copies = []
2040 2042 if opts.get('copies') and rev:
2041 2043 for fn in get(rev)[3]:
2042 2044 rename = getrenamed(fn, rev)
2043 2045 if rename:
2044 2046 copies.append((fn, rename[0]))
2045 2047 displayer.show(context.changectx(repo, rev), copies=copies)
2046 2048 elif st == 'iter':
2047 2049 if count == limit: break
2048 2050 if displayer.flush(rev):
2049 2051 count += 1
2050 2052
2051 2053 def manifest(ui, repo, node=None, rev=None):
2052 2054 """output the current or given revision of the project manifest
2053 2055
2054 2056 Print a list of version controlled files for the given revision. If no
2055 2057 revision is given, the first parent of the working directory is used, or
2056 2058 the null revision if no revision is checked out.
2057 2059
2058 2060 With -v, print file permissions, symlink and executable bits.
2059 2061 With --debug, print file revision hashes.
2060 2062 """
2061 2063
2062 2064 if rev and node:
2063 2065 raise util.Abort(_("please specify just one revision"))
2064 2066
2065 2067 if not node:
2066 2068 node = rev
2067 2069
2068 2070 decor = {'l':'644 @ ', 'x':'755 * ', '':'644 '}
2069 2071 ctx = repo[node]
2070 2072 for f in ctx:
2071 2073 if ui.debugflag:
2072 2074 ui.write("%40s " % hex(ctx.manifest()[f]))
2073 2075 if ui.verbose:
2074 2076 ui.write(decor[ctx.flags(f)])
2075 2077 ui.write("%s\n" % f)
2076 2078
2077 2079 def merge(ui, repo, node=None, **opts):
2078 2080 """merge working directory with another revision
2079 2081
2080 2082 The current working directory is updated with all changes made in the
2081 2083 requested revision since the last common predecessor revision.
2082 2084
2083 2085 Files that changed between either parent are marked as changed for the
2084 2086 next commit and a commit must be performed before any further updates to
2085 2087 the repository are allowed. The next commit will have two parents.
2086 2088
2087 2089 If no revision is specified, the working directory's parent is a head
2088 2090 revision, and the current branch contains exactly one other head, the
2089 2091 other head is merged with by default. Otherwise, an explicit revision with
2090 2092 which to merge with must be provided.
2091 2093 """
2092 2094
2093 2095 if opts.get('rev') and node:
2094 2096 raise util.Abort(_("please specify just one revision"))
2095 2097 if not node:
2096 2098 node = opts.get('rev')
2097 2099
2098 2100 if not node:
2099 2101 branch = repo.changectx(None).branch()
2100 2102 bheads = repo.branchheads(branch)
2101 2103 if len(bheads) > 2:
2102 2104 raise util.Abort(_("branch '%s' has %d heads - "
2103 2105 "please merge with an explicit rev") %
2104 2106 (branch, len(bheads)))
2105 2107
2106 2108 parent = repo.dirstate.parents()[0]
2107 2109 if len(bheads) == 1:
2108 2110 if len(repo.heads()) > 1:
2109 2111 raise util.Abort(_("branch '%s' has one head - "
2110 2112 "please merge with an explicit rev") %
2111 2113 branch)
2112 2114 msg = _('there is nothing to merge')
2113 2115 if parent != repo.lookup(repo[None].branch()):
2114 2116 msg = _('%s - use "hg update" instead') % msg
2115 2117 raise util.Abort(msg)
2116 2118
2117 2119 if parent not in bheads:
2118 2120 raise util.Abort(_('working dir not at a head rev - '
2119 2121 'use "hg update" or merge with an explicit rev'))
2120 2122 node = parent == bheads[0] and bheads[-1] or bheads[0]
2121 2123
2122 2124 if opts.get('preview'):
2123 2125 p1 = repo['.']
2124 2126 p2 = repo[node]
2125 2127 common = p1.ancestor(p2)
2126 2128 roots, heads = [common.node()], [p2.node()]
2127 2129 displayer = cmdutil.show_changeset(ui, repo, opts)
2128 2130 for node in repo.changelog.nodesbetween(roots=roots, heads=heads)[0]:
2129 2131 displayer.show(repo[node])
2130 2132 return 0
2131 2133
2132 2134 return hg.merge(repo, node, force=opts.get('force'))
2133 2135
2134 2136 def outgoing(ui, repo, dest=None, **opts):
2135 2137 """show changesets not found in destination
2136 2138
2137 2139 Show changesets not found in the specified destination repository or the
2138 2140 default push location. These are the changesets that would be pushed if a
2139 2141 push was requested.
2140 2142
2141 2143 See pull for valid destination format details.
2142 2144 """
2143 2145 limit = cmdutil.loglimit(opts)
2144 2146 dest, revs, checkout = hg.parseurl(
2145 2147 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2146 2148 if revs:
2147 2149 revs = [repo.lookup(rev) for rev in revs]
2148 2150
2149 2151 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2150 2152 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
2151 2153 o = repo.findoutgoing(other, force=opts.get('force'))
2152 2154 if not o:
2153 2155 ui.status(_("no changes found\n"))
2154 2156 return 1
2155 2157 o = repo.changelog.nodesbetween(o, revs)[0]
2156 2158 if opts.get('newest_first'):
2157 2159 o.reverse()
2158 2160 displayer = cmdutil.show_changeset(ui, repo, opts)
2159 2161 count = 0
2160 2162 for n in o:
2161 2163 if count >= limit:
2162 2164 break
2163 2165 parents = [p for p in repo.changelog.parents(n) if p != nullid]
2164 2166 if opts.get('no_merges') and len(parents) == 2:
2165 2167 continue
2166 2168 count += 1
2167 2169 displayer.show(repo[n])
2168 2170
2169 2171 def parents(ui, repo, file_=None, **opts):
2170 2172 """show the parents of the working directory or revision
2171 2173
2172 2174 Print the working directory's parent revisions. If a revision is given via
2173 2175 -r/--rev, the parent of that revision will be printed. If a file argument
2174 2176 is given, the revision in which the file was last changed (before the
2175 2177 working directory revision or the argument to --rev if given) is printed.
2176 2178 """
2177 2179 rev = opts.get('rev')
2178 2180 if rev:
2179 2181 ctx = repo[rev]
2180 2182 else:
2181 2183 ctx = repo[None]
2182 2184
2183 2185 if file_:
2184 2186 m = cmdutil.match(repo, (file_,), opts)
2185 2187 if m.anypats() or len(m.files()) != 1:
2186 2188 raise util.Abort(_('can only specify an explicit filename'))
2187 2189 file_ = m.files()[0]
2188 2190 filenodes = []
2189 2191 for cp in ctx.parents():
2190 2192 if not cp:
2191 2193 continue
2192 2194 try:
2193 2195 filenodes.append(cp.filenode(file_))
2194 2196 except error.LookupError:
2195 2197 pass
2196 2198 if not filenodes:
2197 2199 raise util.Abort(_("'%s' not found in manifest!") % file_)
2198 2200 fl = repo.file(file_)
2199 2201 p = [repo.lookup(fl.linkrev(fl.rev(fn))) for fn in filenodes]
2200 2202 else:
2201 2203 p = [cp.node() for cp in ctx.parents()]
2202 2204
2203 2205 displayer = cmdutil.show_changeset(ui, repo, opts)
2204 2206 for n in p:
2205 2207 if n != nullid:
2206 2208 displayer.show(repo[n])
2207 2209
2208 2210 def paths(ui, repo, search=None):
2209 2211 """show aliases for remote repositories
2210 2212
2211 2213 Show definition of symbolic path name NAME. If no name is given, show
2212 2214 definition of all available names.
2213 2215
2214 2216 Path names are defined in the [paths] section of /etc/mercurial/hgrc and
2215 2217 $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
2216 2218
2217 2219 See 'hg help urls' for more information.
2218 2220 """
2219 2221 if search:
2220 2222 for name, path in ui.configitems("paths"):
2221 2223 if name == search:
2222 2224 ui.write("%s\n" % url.hidepassword(path))
2223 2225 return
2224 2226 ui.warn(_("not found!\n"))
2225 2227 return 1
2226 2228 else:
2227 2229 for name, path in ui.configitems("paths"):
2228 2230 ui.write("%s = %s\n" % (name, url.hidepassword(path)))
2229 2231
2230 2232 def postincoming(ui, repo, modheads, optupdate, checkout):
2231 2233 if modheads == 0:
2232 2234 return
2233 2235 if optupdate:
2234 2236 if (modheads <= 1 or len(repo.branchheads()) == 1) or checkout:
2235 2237 return hg.update(repo, checkout)
2236 2238 else:
2237 2239 ui.status(_("not updating, since new heads added\n"))
2238 2240 if modheads > 1:
2239 2241 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2240 2242 else:
2241 2243 ui.status(_("(run 'hg update' to get a working copy)\n"))
2242 2244
2243 2245 def pull(ui, repo, source="default", **opts):
2244 2246 """pull changes from the specified source
2245 2247
2246 2248 Pull changes from a remote repository to a local one.
2247 2249
2248 2250 This finds all changes from the repository at the specified path or URL
2249 2251 and adds them to a local repository (the current one unless -R is
2250 2252 specified). By default, this does not update the copy of the project in
2251 2253 the working directory.
2252 2254
2253 2255 Use hg incoming if you want to see what would have been added by a pull at
2254 2256 the time you issued this command. If you then decide to added those
2255 2257 changes to the repository, you should use pull -r X where X is the last
2256 2258 changeset listed by hg incoming.
2257 2259
2258 2260 If SOURCE is omitted, the 'default' path will be used. See 'hg help urls'
2259 2261 for more information.
2260 2262 """
2261 2263 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
2262 2264 other = hg.repository(cmdutil.remoteui(repo, opts), source)
2263 2265 ui.status(_('pulling from %s\n') % url.hidepassword(source))
2264 2266 if revs:
2265 2267 try:
2266 2268 revs = [other.lookup(rev) for rev in revs]
2267 2269 except error.CapabilityError:
2268 2270 err = _("Other repository doesn't support revision lookup, "
2269 2271 "so a rev cannot be specified.")
2270 2272 raise util.Abort(err)
2271 2273
2272 2274 modheads = repo.pull(other, heads=revs, force=opts.get('force'))
2273 2275 return postincoming(ui, repo, modheads, opts.get('update'), checkout)
2274 2276
2275 2277 def push(ui, repo, dest=None, **opts):
2276 2278 """push changes to the specified destination
2277 2279
2278 2280 Push changes from the local repository to the given destination.
2279 2281
2280 2282 This is the symmetrical operation for pull. It moves changes from the
2281 2283 current repository to a different one. If the destination is local this is
2282 2284 identical to a pull in that directory from the current one.
2283 2285
2284 2286 By default, push will refuse to run if it detects the result would
2285 2287 increase the number of remote heads. This generally indicates the user
2286 2288 forgot to pull and merge before pushing.
2287 2289
2288 2290 If -r/--rev is used, the named revision and all its ancestors will be
2289 2291 pushed to the remote repository.
2290 2292
2291 2293 Please see 'hg help urls' for important details about ssh:// URLs. If
2292 2294 DESTINATION is omitted, a default path will be used.
2293 2295 """
2294 2296 dest, revs, checkout = hg.parseurl(
2295 2297 ui.expandpath(dest or 'default-push', dest or 'default'), opts.get('rev'))
2296 2298 other = hg.repository(cmdutil.remoteui(repo, opts), dest)
2297 2299 ui.status(_('pushing to %s\n') % url.hidepassword(dest))
2298 2300 if revs:
2299 2301 revs = [repo.lookup(rev) for rev in revs]
2300 2302
2301 2303 # push subrepos depth-first for coherent ordering
2302 2304 c = repo['']
2303 2305 subs = c.substate # only repos that are committed
2304 2306 for s in sorted(subs):
2305 2307 c.sub(s).push(opts.get('force'))
2306 2308
2307 2309 r = repo.push(other, opts.get('force'), revs=revs)
2308 2310 return r == 0
2309 2311
2310 2312 def recover(ui, repo):
2311 2313 """roll back an interrupted transaction
2312 2314
2313 2315 Recover from an interrupted commit or pull.
2314 2316
2315 2317 This command tries to fix the repository status after an interrupted
2316 2318 operation. It should only be necessary when Mercurial suggests it.
2317 2319 """
2318 2320 if repo.recover():
2319 2321 return hg.verify(repo)
2320 2322 return 1
2321 2323
2322 2324 def remove(ui, repo, *pats, **opts):
2323 2325 """remove the specified files on the next commit
2324 2326
2325 2327 Schedule the indicated files for removal from the repository.
2326 2328
2327 2329 This only removes files from the current branch, not from the entire
2328 2330 project history. -A/--after can be used to remove only files that have
2329 2331 already been deleted, -f/--force can be used to force deletion, and -Af
2330 2332 can be used to remove files from the next revision without deleting them
2331 2333 from the working directory.
2332 2334
2333 2335 The following table details the behavior of remove for different file
2334 2336 states (columns) and option combinations (rows). The file states are Added
2335 2337 [A], Clean [C], Modified [M] and Missing [!] (as reported by hg status).
2336 2338 The actions are Warn, Remove (from branch) and Delete (from disk)::
2337 2339
2338 2340 A C M !
2339 2341 none W RD W R
2340 2342 -f R RD RD R
2341 2343 -A W W W R
2342 2344 -Af R R R R
2343 2345
2344 2346 This command schedules the files to be removed at the next commit. To undo
2345 2347 a remove before that, see hg revert.
2346 2348 """
2347 2349
2348 2350 after, force = opts.get('after'), opts.get('force')
2349 2351 if not pats and not after:
2350 2352 raise util.Abort(_('no files specified'))
2351 2353
2352 2354 m = cmdutil.match(repo, pats, opts)
2353 2355 s = repo.status(match=m, clean=True)
2354 2356 modified, added, deleted, clean = s[0], s[1], s[3], s[6]
2355 2357
2356 2358 for f in m.files():
2357 2359 if f not in repo.dirstate and not os.path.isdir(m.rel(f)):
2358 2360 ui.warn(_('not removing %s: file is untracked\n') % m.rel(f))
2359 2361
2360 2362 def warn(files, reason):
2361 2363 for f in files:
2362 2364 ui.warn(_('not removing %s: file %s (use -f to force removal)\n')
2363 2365 % (m.rel(f), reason))
2364 2366
2365 2367 if force:
2366 2368 remove, forget = modified + deleted + clean, added
2367 2369 elif after:
2368 2370 remove, forget = deleted, []
2369 2371 warn(modified + added + clean, _('still exists'))
2370 2372 else:
2371 2373 remove, forget = deleted + clean, []
2372 2374 warn(modified, _('is modified'))
2373 2375 warn(added, _('has been marked for add'))
2374 2376
2375 2377 for f in sorted(remove + forget):
2376 2378 if ui.verbose or not m.exact(f):
2377 2379 ui.status(_('removing %s\n') % m.rel(f))
2378 2380
2379 2381 repo.forget(forget)
2380 2382 repo.remove(remove, unlink=not after)
2381 2383
2382 2384 def rename(ui, repo, *pats, **opts):
2383 2385 """rename files; equivalent of copy + remove
2384 2386
2385 2387 Mark dest as copies of sources; mark sources for deletion. If dest is a
2386 2388 directory, copies are put in that directory. If dest is a file, there can
2387 2389 only be one source.
2388 2390
2389 2391 By default, this command copies the contents of files as they exist in the
2390 2392 working directory. If invoked with -A/--after, the operation is recorded,
2391 2393 but no copying is performed.
2392 2394
2393 2395 This command takes effect at the next commit. To undo a rename before
2394 2396 that, see hg revert.
2395 2397 """
2396 2398 wlock = repo.wlock(False)
2397 2399 try:
2398 2400 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2399 2401 finally:
2400 2402 wlock.release()
2401 2403
2402 2404 def resolve(ui, repo, *pats, **opts):
2403 2405 """retry file merges from a merge or update
2404 2406
2405 2407 This command will cleanly retry unresolved file merges using file
2406 2408 revisions preserved from the last update or merge. To attempt to resolve
2407 2409 all unresolved files, use the -a/--all switch.
2408 2410
2409 2411 If a conflict is resolved manually, please note that the changes will be
2410 2412 overwritten if the merge is retried with resolve. The -m/--mark switch
2411 2413 should be used to mark the file as resolved.
2412 2414
2413 2415 This command also allows listing resolved files and manually indicating
2414 2416 whether or not files are resolved. All files must be marked as resolved
2415 2417 before a commit is permitted.
2416 2418
2417 2419 The codes used to show the status of files are::
2418 2420
2419 2421 U = unresolved
2420 2422 R = resolved
2421 2423 """
2422 2424
2423 2425 all, mark, unmark, show = [opts.get(o) for o in 'all mark unmark list'.split()]
2424 2426
2425 2427 if (show and (mark or unmark)) or (mark and unmark):
2426 2428 raise util.Abort(_("too many options specified"))
2427 2429 if pats and all:
2428 2430 raise util.Abort(_("can't specify --all and patterns"))
2429 2431 if not (all or pats or show or mark or unmark):
2430 2432 raise util.Abort(_('no files or directories specified; '
2431 2433 'use --all to remerge all files'))
2432 2434
2433 2435 ms = merge_.mergestate(repo)
2434 2436 m = cmdutil.match(repo, pats, opts)
2435 2437
2436 2438 for f in ms:
2437 2439 if m(f):
2438 2440 if show:
2439 2441 ui.write("%s %s\n" % (ms[f].upper(), f))
2440 2442 elif mark:
2441 2443 ms.mark(f, "r")
2442 2444 elif unmark:
2443 2445 ms.mark(f, "u")
2444 2446 else:
2445 2447 wctx = repo[None]
2446 2448 mctx = wctx.parents()[-1]
2447 2449
2448 2450 # backup pre-resolve (merge uses .orig for its own purposes)
2449 2451 a = repo.wjoin(f)
2450 2452 util.copyfile(a, a + ".resolve")
2451 2453
2452 2454 # resolve file
2453 2455 ms.resolve(f, wctx, mctx)
2454 2456
2455 2457 # replace filemerge's .orig file with our resolve file
2456 2458 util.rename(a + ".resolve", a + ".orig")
2457 2459
2458 2460 def revert(ui, repo, *pats, **opts):
2459 2461 """restore individual files or directories to an earlier state
2460 2462
2461 2463 (Use update -r to check out earlier revisions, revert does not change the
2462 2464 working directory parents.)
2463 2465
2464 2466 With no revision specified, revert the named files or directories to the
2465 2467 contents they had in the parent of the working directory. This restores
2466 2468 the contents of the affected files to an unmodified state and unschedules
2467 2469 adds, removes, copies, and renames. If the working directory has two
2468 2470 parents, you must explicitly specify the revision to revert to.
2469 2471
2470 2472 Using the -r/--rev option, revert the given files or directories to their
2471 2473 contents as of a specific revision. This can be helpful to "roll back"
2472 2474 some or all of an earlier change. See 'hg help dates' for a list of
2473 2475 formats valid for -d/--date.
2474 2476
2475 2477 Revert modifies the working directory. It does not commit any changes, or
2476 2478 change the parent of the working directory. If you revert to a revision
2477 2479 other than the parent of the working directory, the reverted files will
2478 2480 thus appear modified afterwards.
2479 2481
2480 2482 If a file has been deleted, it is restored. If the executable mode of a
2481 2483 file was changed, it is reset.
2482 2484
2483 2485 If names are given, all files matching the names are reverted. If no
2484 2486 arguments are given, no files are reverted.
2485 2487
2486 2488 Modified files are saved with a .orig suffix before reverting. To disable
2487 2489 these backups, use --no-backup.
2488 2490 """
2489 2491
2490 2492 if opts["date"]:
2491 2493 if opts["rev"]:
2492 2494 raise util.Abort(_("you can't specify a revision and a date"))
2493 2495 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2494 2496
2495 2497 if not pats and not opts.get('all'):
2496 2498 raise util.Abort(_('no files or directories specified; '
2497 2499 'use --all to revert the whole repo'))
2498 2500
2499 2501 parent, p2 = repo.dirstate.parents()
2500 2502 if not opts.get('rev') and p2 != nullid:
2501 2503 raise util.Abort(_('uncommitted merge - please provide a '
2502 2504 'specific revision'))
2503 2505 ctx = repo[opts.get('rev')]
2504 2506 node = ctx.node()
2505 2507 mf = ctx.manifest()
2506 2508 if node == parent:
2507 2509 pmf = mf
2508 2510 else:
2509 2511 pmf = None
2510 2512
2511 2513 # need all matching names in dirstate and manifest of target rev,
2512 2514 # so have to walk both. do not print errors if files exist in one
2513 2515 # but not other.
2514 2516
2515 2517 names = {}
2516 2518
2517 2519 wlock = repo.wlock()
2518 2520 try:
2519 2521 # walk dirstate.
2520 2522
2521 2523 m = cmdutil.match(repo, pats, opts)
2522 2524 m.bad = lambda x,y: False
2523 2525 for abs in repo.walk(m):
2524 2526 names[abs] = m.rel(abs), m.exact(abs)
2525 2527
2526 2528 # walk target manifest.
2527 2529
2528 2530 def badfn(path, msg):
2529 2531 if path in names:
2530 2532 return
2531 2533 path_ = path + '/'
2532 2534 for f in names:
2533 2535 if f.startswith(path_):
2534 2536 return
2535 2537 ui.warn("%s: %s\n" % (m.rel(path), msg))
2536 2538
2537 2539 m = cmdutil.match(repo, pats, opts)
2538 2540 m.bad = badfn
2539 2541 for abs in repo[node].walk(m):
2540 2542 if abs not in names:
2541 2543 names[abs] = m.rel(abs), m.exact(abs)
2542 2544
2543 2545 m = cmdutil.matchfiles(repo, names)
2544 2546 changes = repo.status(match=m)[:4]
2545 2547 modified, added, removed, deleted = map(set, changes)
2546 2548
2547 2549 # if f is a rename, also revert the source
2548 2550 cwd = repo.getcwd()
2549 2551 for f in added:
2550 2552 src = repo.dirstate.copied(f)
2551 2553 if src and src not in names and repo.dirstate[src] == 'r':
2552 2554 removed.add(src)
2553 2555 names[src] = (repo.pathto(src, cwd), True)
2554 2556
2555 2557 def removeforget(abs):
2556 2558 if repo.dirstate[abs] == 'a':
2557 2559 return _('forgetting %s\n')
2558 2560 return _('removing %s\n')
2559 2561
2560 2562 revert = ([], _('reverting %s\n'))
2561 2563 add = ([], _('adding %s\n'))
2562 2564 remove = ([], removeforget)
2563 2565 undelete = ([], _('undeleting %s\n'))
2564 2566
2565 2567 disptable = (
2566 2568 # dispatch table:
2567 2569 # file state
2568 2570 # action if in target manifest
2569 2571 # action if not in target manifest
2570 2572 # make backup if in target manifest
2571 2573 # make backup if not in target manifest
2572 2574 (modified, revert, remove, True, True),
2573 2575 (added, revert, remove, True, False),
2574 2576 (removed, undelete, None, False, False),
2575 2577 (deleted, revert, remove, False, False),
2576 2578 )
2577 2579
2578 2580 for abs, (rel, exact) in sorted(names.items()):
2579 2581 mfentry = mf.get(abs)
2580 2582 target = repo.wjoin(abs)
2581 2583 def handle(xlist, dobackup):
2582 2584 xlist[0].append(abs)
2583 2585 if dobackup and not opts.get('no_backup') and util.lexists(target):
2584 2586 bakname = "%s.orig" % rel
2585 2587 ui.note(_('saving current version of %s as %s\n') %
2586 2588 (rel, bakname))
2587 2589 if not opts.get('dry_run'):
2588 2590 util.copyfile(target, bakname)
2589 2591 if ui.verbose or not exact:
2590 2592 msg = xlist[1]
2591 2593 if not isinstance(msg, basestring):
2592 2594 msg = msg(abs)
2593 2595 ui.status(msg % rel)
2594 2596 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2595 2597 if abs not in table: continue
2596 2598 # file has changed in dirstate
2597 2599 if mfentry:
2598 2600 handle(hitlist, backuphit)
2599 2601 elif misslist is not None:
2600 2602 handle(misslist, backupmiss)
2601 2603 break
2602 2604 else:
2603 2605 if abs not in repo.dirstate:
2604 2606 if mfentry:
2605 2607 handle(add, True)
2606 2608 elif exact:
2607 2609 ui.warn(_('file not managed: %s\n') % rel)
2608 2610 continue
2609 2611 # file has not changed in dirstate
2610 2612 if node == parent:
2611 2613 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2612 2614 continue
2613 2615 if pmf is None:
2614 2616 # only need parent manifest in this unlikely case,
2615 2617 # so do not read by default
2616 2618 pmf = repo[parent].manifest()
2617 2619 if abs in pmf:
2618 2620 if mfentry:
2619 2621 # if version of file is same in parent and target
2620 2622 # manifests, do nothing
2621 2623 if (pmf[abs] != mfentry or
2622 2624 pmf.flags(abs) != mf.flags(abs)):
2623 2625 handle(revert, False)
2624 2626 else:
2625 2627 handle(remove, False)
2626 2628
2627 2629 if not opts.get('dry_run'):
2628 2630 def checkout(f):
2629 2631 fc = ctx[f]
2630 2632 repo.wwrite(f, fc.data(), fc.flags())
2631 2633
2632 2634 audit_path = util.path_auditor(repo.root)
2633 2635 for f in remove[0]:
2634 2636 if repo.dirstate[f] == 'a':
2635 2637 repo.dirstate.forget(f)
2636 2638 continue
2637 2639 audit_path(f)
2638 2640 try:
2639 2641 util.unlink(repo.wjoin(f))
2640 2642 except OSError:
2641 2643 pass
2642 2644 repo.dirstate.remove(f)
2643 2645
2644 2646 normal = None
2645 2647 if node == parent:
2646 2648 # We're reverting to our parent. If possible, we'd like status
2647 2649 # to report the file as clean. We have to use normallookup for
2648 2650 # merges to avoid losing information about merged/dirty files.
2649 2651 if p2 != nullid:
2650 2652 normal = repo.dirstate.normallookup
2651 2653 else:
2652 2654 normal = repo.dirstate.normal
2653 2655 for f in revert[0]:
2654 2656 checkout(f)
2655 2657 if normal:
2656 2658 normal(f)
2657 2659
2658 2660 for f in add[0]:
2659 2661 checkout(f)
2660 2662 repo.dirstate.add(f)
2661 2663
2662 2664 normal = repo.dirstate.normallookup
2663 2665 if node == parent and p2 == nullid:
2664 2666 normal = repo.dirstate.normal
2665 2667 for f in undelete[0]:
2666 2668 checkout(f)
2667 2669 normal(f)
2668 2670
2669 2671 finally:
2670 2672 wlock.release()
2671 2673
2672 2674 def rollback(ui, repo):
2673 2675 """roll back the last transaction
2674 2676
2675 2677 This command should be used with care. There is only one level of
2676 2678 rollback, and there is no way to undo a rollback. It will also restore the
2677 2679 dirstate at the time of the last transaction, losing any dirstate changes
2678 2680 since that time. This command does not alter the working directory.
2679 2681
2680 2682 Transactions are used to encapsulate the effects of all commands that
2681 2683 create new changesets or propagate existing changesets into a repository.
2682 2684 For example, the following commands are transactional, and their effects
2683 2685 can be rolled back::
2684 2686
2685 2687 commit
2686 2688 import
2687 2689 pull
2688 2690 push (with this repository as destination)
2689 2691 unbundle
2690 2692
2691 2693 This command is not intended for use on public repositories. Once changes
2692 2694 are visible for pull by other users, rolling a transaction back locally is
2693 2695 ineffective (someone else may already have pulled the changes).
2694 2696 Furthermore, a race is possible with readers of the repository; for
2695 2697 example an in-progress pull from the repository may fail if a rollback is
2696 2698 performed.
2697 2699 """
2698 2700 repo.rollback()
2699 2701
2700 2702 def root(ui, repo):
2701 2703 """print the root (top) of the current working directory
2702 2704
2703 2705 Print the root directory of the current repository.
2704 2706 """
2705 2707 ui.write(repo.root + "\n")
2706 2708
2707 2709 def serve(ui, repo, **opts):
2708 2710 """export the repository via HTTP
2709 2711
2710 2712 Start a local HTTP repository browser and pull server.
2711 2713
2712 2714 By default, the server logs accesses to stdout and errors to stderr. Use
2713 2715 the -A/--accesslog and -E/--errorlog options to log to files.
2714 2716 """
2715 2717
2716 2718 if opts["stdio"]:
2717 2719 if repo is None:
2718 2720 raise error.RepoError(_("There is no Mercurial repository here"
2719 2721 " (.hg not found)"))
2720 2722 s = sshserver.sshserver(ui, repo)
2721 2723 s.serve_forever()
2722 2724
2723 2725 baseui = repo and repo.baseui or ui
2724 2726 optlist = ("name templates style address port prefix ipv6"
2725 2727 " accesslog errorlog webdir_conf certificate encoding")
2726 2728 for o in optlist.split():
2727 2729 if opts.get(o, None):
2728 2730 baseui.setconfig("web", o, str(opts[o]))
2729 2731 if (repo is not None) and (repo.ui != baseui):
2730 2732 repo.ui.setconfig("web", o, str(opts[o]))
2731 2733
2732 2734 if repo is None and not ui.config("web", "webdir_conf"):
2733 2735 raise error.RepoError(_("There is no Mercurial repository here"
2734 2736 " (.hg not found)"))
2735 2737
2736 2738 class service(object):
2737 2739 def init(self):
2738 2740 util.set_signal_handler()
2739 2741 self.httpd = server.create_server(baseui, repo)
2740 2742
2741 2743 if not ui.verbose: return
2742 2744
2743 2745 if self.httpd.prefix:
2744 2746 prefix = self.httpd.prefix.strip('/') + '/'
2745 2747 else:
2746 2748 prefix = ''
2747 2749
2748 2750 port = ':%d' % self.httpd.port
2749 2751 if port == ':80':
2750 2752 port = ''
2751 2753
2752 2754 bindaddr = self.httpd.addr
2753 2755 if bindaddr == '0.0.0.0':
2754 2756 bindaddr = '*'
2755 2757 elif ':' in bindaddr: # IPv6
2756 2758 bindaddr = '[%s]' % bindaddr
2757 2759
2758 2760 fqaddr = self.httpd.fqaddr
2759 2761 if ':' in fqaddr:
2760 2762 fqaddr = '[%s]' % fqaddr
2761 2763 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2762 2764 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2763 2765
2764 2766 def run(self):
2765 2767 self.httpd.serve_forever()
2766 2768
2767 2769 service = service()
2768 2770
2769 2771 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2770 2772
2771 2773 def status(ui, repo, *pats, **opts):
2772 2774 """show changed files in the working directory
2773 2775
2774 2776 Show status of files in the repository. If names are given, only files
2775 2777 that match are shown. Files that are clean or ignored or the source of a
2776 2778 copy/move operation, are not listed unless -c/--clean, -i/--ignored,
2777 2779 -C/--copies or -A/--all are given. Unless options described with "show
2778 2780 only ..." are given, the options -mardu are used.
2779 2781
2780 2782 Option -q/--quiet hides untracked (unknown and ignored) files unless
2781 2783 explicitly requested with -u/--unknown or -i/--ignored.
2782 2784
2783 2785 NOTE: status may appear to disagree with diff if permissions have changed
2784 2786 or a merge has occurred. The standard diff format does not report
2785 2787 permission changes and diff only reports changes relative to one merge
2786 2788 parent.
2787 2789
2788 2790 If one revision is given, it is used as the base revision. If two
2789 2791 revisions are given, the differences between them are shown.
2790 2792
2791 2793 The codes used to show the status of files are::
2792 2794
2793 2795 M = modified
2794 2796 A = added
2795 2797 R = removed
2796 2798 C = clean
2797 2799 ! = missing (deleted by non-hg command, but still tracked)
2798 2800 ? = not tracked
2799 2801 I = ignored
2800 2802 = origin of the previous file listed as A (added)
2801 2803 """
2802 2804
2803 2805 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2804 2806 cwd = (pats and repo.getcwd()) or ''
2805 2807 end = opts.get('print0') and '\0' or '\n'
2806 2808 copy = {}
2807 2809 states = 'modified added removed deleted unknown ignored clean'.split()
2808 2810 show = [k for k in states if opts.get(k)]
2809 2811 if opts.get('all'):
2810 2812 show += ui.quiet and (states[:4] + ['clean']) or states
2811 2813 if not show:
2812 2814 show = ui.quiet and states[:4] or states[:5]
2813 2815
2814 2816 stat = repo.status(node1, node2, cmdutil.match(repo, pats, opts),
2815 2817 'ignored' in show, 'clean' in show, 'unknown' in show)
2816 2818 changestates = zip(states, 'MAR!?IC', stat)
2817 2819
2818 2820 if (opts.get('all') or opts.get('copies')) and not opts.get('no_status'):
2819 2821 ctxn = repo[nullid]
2820 2822 ctx1 = repo[node1]
2821 2823 ctx2 = repo[node2]
2822 2824 added = stat[1]
2823 2825 if node2 is None:
2824 2826 added = stat[0] + stat[1] # merged?
2825 2827
2826 2828 for k, v in copies.copies(repo, ctx1, ctx2, ctxn)[0].iteritems():
2827 2829 if k in added:
2828 2830 copy[k] = v
2829 2831 elif v in added:
2830 2832 copy[v] = k
2831 2833
2832 2834 for state, char, files in changestates:
2833 2835 if state in show:
2834 2836 format = "%s %%s%s" % (char, end)
2835 2837 if opts.get('no_status'):
2836 2838 format = "%%s%s" % end
2837 2839
2838 2840 for f in files:
2839 2841 ui.write(format % repo.pathto(f, cwd))
2840 2842 if f in copy:
2841 2843 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2842 2844
2843 2845 def tag(ui, repo, name1, *names, **opts):
2844 2846 """add one or more tags for the current or given revision
2845 2847
2846 2848 Name a particular revision using <name>.
2847 2849
2848 2850 Tags are used to name particular revisions of the repository and are very
2849 2851 useful to compare different revisions, to go back to significant earlier
2850 2852 versions or to mark branch points as releases, etc.
2851 2853
2852 2854 If no revision is given, the parent of the working directory is used, or
2853 2855 tip if no revision is checked out.
2854 2856
2855 2857 To facilitate version control, distribution, and merging of tags, they are
2856 2858 stored as a file named ".hgtags" which is managed similarly to other
2857 2859 project files and can be hand-edited if necessary. The file
2858 2860 '.hg/localtags' is used for local tags (not shared among repositories).
2859 2861
2860 2862 See 'hg help dates' for a list of formats valid for -d/--date.
2861 2863 """
2862 2864
2863 2865 rev_ = "."
2864 2866 names = (name1,) + names
2865 2867 if len(names) != len(set(names)):
2866 2868 raise util.Abort(_('tag names must be unique'))
2867 2869 for n in names:
2868 2870 if n in ['tip', '.', 'null']:
2869 2871 raise util.Abort(_('the name \'%s\' is reserved') % n)
2870 2872 if opts.get('rev') and opts.get('remove'):
2871 2873 raise util.Abort(_("--rev and --remove are incompatible"))
2872 2874 if opts.get('rev'):
2873 2875 rev_ = opts['rev']
2874 2876 message = opts.get('message')
2875 2877 if opts.get('remove'):
2876 2878 expectedtype = opts.get('local') and 'local' or 'global'
2877 2879 for n in names:
2878 2880 if not repo.tagtype(n):
2879 2881 raise util.Abort(_('tag \'%s\' does not exist') % n)
2880 2882 if repo.tagtype(n) != expectedtype:
2881 2883 if expectedtype == 'global':
2882 2884 raise util.Abort(_('tag \'%s\' is not a global tag') % n)
2883 2885 else:
2884 2886 raise util.Abort(_('tag \'%s\' is not a local tag') % n)
2885 2887 rev_ = nullid
2886 2888 if not message:
2887 2889 # we don't translate commit messages
2888 2890 message = 'Removed tag %s' % ', '.join(names)
2889 2891 elif not opts.get('force'):
2890 2892 for n in names:
2891 2893 if n in repo.tags():
2892 2894 raise util.Abort(_('tag \'%s\' already exists '
2893 2895 '(use -f to force)') % n)
2894 2896 if not rev_ and repo.dirstate.parents()[1] != nullid:
2895 2897 raise util.Abort(_('uncommitted merge - please provide a '
2896 2898 'specific revision'))
2897 2899 r = repo[rev_].node()
2898 2900
2899 2901 if not message:
2900 2902 # we don't translate commit messages
2901 2903 message = ('Added tag %s for changeset %s' %
2902 2904 (', '.join(names), short(r)))
2903 2905
2904 2906 date = opts.get('date')
2905 2907 if date:
2906 2908 date = util.parsedate(date)
2907 2909
2908 2910 repo.tag(names, r, message, opts.get('local'), opts.get('user'), date)
2909 2911
2910 2912 def tags(ui, repo):
2911 2913 """list repository tags
2912 2914
2913 2915 This lists both regular and local tags. When the -v/--verbose switch is
2914 2916 used, a third column "local" is printed for local tags.
2915 2917 """
2916 2918
2917 2919 hexfunc = ui.debugflag and hex or short
2918 2920 tagtype = ""
2919 2921
2920 2922 for t, n in reversed(repo.tagslist()):
2921 2923 if ui.quiet:
2922 2924 ui.write("%s\n" % t)
2923 2925 continue
2924 2926
2925 2927 try:
2926 2928 hn = hexfunc(n)
2927 2929 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2928 2930 except error.LookupError:
2929 2931 r = " ?:%s" % hn
2930 2932 else:
2931 2933 spaces = " " * (30 - encoding.colwidth(t))
2932 2934 if ui.verbose:
2933 2935 if repo.tagtype(t) == 'local':
2934 2936 tagtype = " local"
2935 2937 else:
2936 2938 tagtype = ""
2937 2939 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2938 2940
2939 2941 def tip(ui, repo, **opts):
2940 2942 """show the tip revision
2941 2943
2942 2944 The tip revision (usually just called the tip) is the changeset most
2943 2945 recently added to the repository (and therefore the most recently changed
2944 2946 head).
2945 2947
2946 2948 If you have just made a commit, that commit will be the tip. If you have
2947 2949 just pulled changes from another repository, the tip of that repository
2948 2950 becomes the current tip. The "tip" tag is special and cannot be renamed or
2949 2951 assigned to a different changeset.
2950 2952 """
2951 2953 cmdutil.show_changeset(ui, repo, opts).show(repo[len(repo) - 1])
2952 2954
2953 2955 def unbundle(ui, repo, fname1, *fnames, **opts):
2954 2956 """apply one or more changegroup files
2955 2957
2956 2958 Apply one or more compressed changegroup files generated by the bundle
2957 2959 command.
2958 2960 """
2959 2961 fnames = (fname1,) + fnames
2960 2962
2961 2963 lock = repo.lock()
2962 2964 try:
2963 2965 for fname in fnames:
2964 2966 f = url.open(ui, fname)
2965 2967 gen = changegroup.readbundle(f, fname)
2966 2968 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2967 2969 finally:
2968 2970 lock.release()
2969 2971
2970 2972 return postincoming(ui, repo, modheads, opts.get('update'), None)
2971 2973
2972 2974 def update(ui, repo, node=None, rev=None, clean=False, date=None, check=False):
2973 2975 """update working directory
2974 2976
2975 2977 Update the repository's working directory to the specified revision, or
2976 2978 the tip of the current branch if none is specified. Use null as the
2977 2979 revision to remove the working copy (like 'hg clone -U').
2978 2980
2979 2981 When the working directory contains no uncommitted changes, it will be
2980 2982 replaced by the state of the requested revision from the repository. When
2981 2983 the requested revision is on a different branch, the working directory
2982 2984 will additionally be switched to that branch.
2983 2985
2984 2986 When there are uncommitted changes, use option -C/--clean to discard them,
2985 2987 forcibly replacing the state of the working directory with the requested
2986 2988 revision. Alternately, use -c/--check to abort.
2987 2989
2988 2990 When there are uncommitted changes and option -C/--clean is not used, and
2989 2991 the parent revision and requested revision are on the same branch, and one
2990 2992 of them is an ancestor of the other, then the new working directory will
2991 2993 contain the requested revision merged with the uncommitted changes.
2992 2994 Otherwise, the update will fail with a suggestion to use 'merge' or
2993 2995 'update -C' instead.
2994 2996
2995 2997 If you want to update just one file to an older revision, use revert.
2996 2998
2997 2999 See 'hg help dates' for a list of formats valid for -d/--date.
2998 3000 """
2999 3001 if rev and node:
3000 3002 raise util.Abort(_("please specify just one revision"))
3001 3003
3002 3004 if not rev:
3003 3005 rev = node
3004 3006
3005 3007 if not clean and check:
3006 3008 # we could use dirty() but we can ignore merge and branch trivia
3007 3009 c = repo[None]
3008 3010 if c.modified() or c.added() or c.removed():
3009 3011 raise util.Abort(_("uncommitted local changes"))
3010 3012
3011 3013 if date:
3012 3014 if rev:
3013 3015 raise util.Abort(_("you can't specify a revision and a date"))
3014 3016 rev = cmdutil.finddate(ui, repo, date)
3015 3017
3016 if clean:
3018 if clean or check:
3017 3019 return hg.clean(repo, rev)
3018 3020 else:
3019 3021 return hg.update(repo, rev)
3020 3022
3021 3023 def verify(ui, repo):
3022 3024 """verify the integrity of the repository
3023 3025
3024 3026 Verify the integrity of the current repository.
3025 3027
3026 3028 This will perform an extensive check of the repository's integrity,
3027 3029 validating the hashes and checksums of each entry in the changelog,
3028 3030 manifest, and tracked files, as well as the integrity of their crosslinks
3029 3031 and indices.
3030 3032 """
3031 3033 return hg.verify(repo)
3032 3034
3033 3035 def version_(ui):
3034 3036 """output version and copyright information"""
3035 3037 ui.write(_("Mercurial Distributed SCM (version %s)\n")
3036 3038 % util.version())
3037 3039 ui.status(_(
3038 3040 "\nCopyright (C) 2005-2009 Matt Mackall <mpm@selenic.com> and others\n"
3039 3041 "This is free software; see the source for copying conditions. "
3040 3042 "There is NO\nwarranty; "
3041 3043 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
3042 3044 ))
3043 3045
3044 3046 # Command options and aliases are listed here, alphabetically
3045 3047
3046 3048 globalopts = [
3047 3049 ('R', 'repository', '',
3048 3050 _('repository root directory or symbolic path name')),
3049 3051 ('', 'cwd', '', _('change working directory')),
3050 3052 ('y', 'noninteractive', None,
3051 3053 _('do not prompt, assume \'yes\' for any required answers')),
3052 3054 ('q', 'quiet', None, _('suppress output')),
3053 3055 ('v', 'verbose', None, _('enable additional output')),
3054 3056 ('', 'config', [], _('set/override config option')),
3055 3057 ('', 'debug', None, _('enable debugging output')),
3056 3058 ('', 'debugger', None, _('start debugger')),
3057 3059 ('', 'encoding', encoding.encoding, _('set the charset encoding')),
3058 3060 ('', 'encodingmode', encoding.encodingmode,
3059 3061 _('set the charset encoding mode')),
3060 3062 ('', 'traceback', None, _('print traceback on exception')),
3061 3063 ('', 'time', None, _('time how long the command takes')),
3062 3064 ('', 'profile', None, _('print command execution profile')),
3063 3065 ('', 'version', None, _('output version information and exit')),
3064 3066 ('h', 'help', None, _('display help and exit')),
3065 3067 ]
3066 3068
3067 3069 dryrunopts = [('n', 'dry-run', None,
3068 3070 _('do not perform actions, just print output'))]
3069 3071
3070 3072 remoteopts = [
3071 3073 ('e', 'ssh', '', _('specify ssh command to use')),
3072 3074 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
3073 3075 ]
3074 3076
3075 3077 walkopts = [
3076 3078 ('I', 'include', [], _('include names matching the given patterns')),
3077 3079 ('X', 'exclude', [], _('exclude names matching the given patterns')),
3078 3080 ]
3079 3081
3080 3082 commitopts = [
3081 3083 ('m', 'message', '', _('use <text> as commit message')),
3082 3084 ('l', 'logfile', '', _('read commit message from <file>')),
3083 3085 ]
3084 3086
3085 3087 commitopts2 = [
3086 3088 ('d', 'date', '', _('record datecode as commit date')),
3087 3089 ('u', 'user', '', _('record the specified user as committer')),
3088 3090 ]
3089 3091
3090 3092 templateopts = [
3091 3093 ('', 'style', '', _('display using template map file')),
3092 3094 ('', 'template', '', _('display with template')),
3093 3095 ]
3094 3096
3095 3097 logopts = [
3096 3098 ('p', 'patch', None, _('show patch')),
3097 3099 ('g', 'git', None, _('use git extended diff format')),
3098 3100 ('l', 'limit', '', _('limit number of changes displayed')),
3099 3101 ('M', 'no-merges', None, _('do not show merges')),
3100 3102 ] + templateopts
3101 3103
3102 3104 diffopts = [
3103 3105 ('a', 'text', None, _('treat all files as text')),
3104 3106 ('g', 'git', None, _('use git extended diff format')),
3105 3107 ('', 'nodates', None, _("don't include dates in diff headers"))
3106 3108 ]
3107 3109
3108 3110 diffopts2 = [
3109 3111 ('p', 'show-function', None, _('show which function each change is in')),
3110 3112 ('w', 'ignore-all-space', None,
3111 3113 _('ignore white space when comparing lines')),
3112 3114 ('b', 'ignore-space-change', None,
3113 3115 _('ignore changes in the amount of white space')),
3114 3116 ('B', 'ignore-blank-lines', None,
3115 3117 _('ignore changes whose lines are all blank')),
3116 3118 ('U', 'unified', '', _('number of lines of context to show'))
3117 3119 ]
3118 3120
3119 3121 similarityopts = [
3120 3122 ('s', 'similarity', '',
3121 3123 _('guess renamed files by similarity (0<=s<=100)'))
3122 3124 ]
3123 3125
3124 3126 table = {
3125 3127 "^add": (add, walkopts + dryrunopts, _('[OPTION]... [FILE]...')),
3126 3128 "addremove":
3127 3129 (addremove, similarityopts + walkopts + dryrunopts,
3128 3130 _('[OPTION]... [FILE]...')),
3129 3131 "^annotate|blame":
3130 3132 (annotate,
3131 3133 [('r', 'rev', '', _('annotate the specified revision')),
3132 3134 ('f', 'follow', None, _('follow file copies and renames')),
3133 3135 ('a', 'text', None, _('treat all files as text')),
3134 3136 ('u', 'user', None, _('list the author (long with -v)')),
3135 3137 ('d', 'date', None, _('list the date (short with -q)')),
3136 3138 ('n', 'number', None, _('list the revision number (default)')),
3137 3139 ('c', 'changeset', None, _('list the changeset')),
3138 3140 ('l', 'line-number', None,
3139 3141 _('show line number at the first appearance'))
3140 3142 ] + walkopts,
3141 3143 _('[-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
3142 3144 "archive":
3143 3145 (archive,
3144 3146 [('', 'no-decode', None, _('do not pass files through decoders')),
3145 3147 ('p', 'prefix', '', _('directory prefix for files in archive')),
3146 3148 ('r', 'rev', '', _('revision to distribute')),
3147 3149 ('t', 'type', '', _('type of distribution to create')),
3148 3150 ] + walkopts,
3149 3151 _('[OPTION]... DEST')),
3150 3152 "backout":
3151 3153 (backout,
3152 3154 [('', 'merge', None,
3153 3155 _('merge with old dirstate parent after backout')),
3154 3156 ('', 'parent', '', _('parent to choose when backing out merge')),
3155 3157 ('r', 'rev', '', _('revision to backout')),
3156 3158 ] + walkopts + commitopts + commitopts2,
3157 3159 _('[OPTION]... [-r] REV')),
3158 3160 "bisect":
3159 3161 (bisect,
3160 3162 [('r', 'reset', False, _('reset bisect state')),
3161 3163 ('g', 'good', False, _('mark changeset good')),
3162 3164 ('b', 'bad', False, _('mark changeset bad')),
3163 3165 ('s', 'skip', False, _('skip testing changeset')),
3164 3166 ('c', 'command', '', _('use command to check changeset state')),
3165 3167 ('U', 'noupdate', False, _('do not update to target'))],
3166 3168 _("[-gbsr] [-c CMD] [REV]")),
3167 3169 "branch":
3168 3170 (branch,
3169 3171 [('f', 'force', None,
3170 3172 _('set branch name even if it shadows an existing branch')),
3171 3173 ('C', 'clean', None, _('reset branch name to parent branch name'))],
3172 3174 _('[-fC] [NAME]')),
3173 3175 "branches":
3174 3176 (branches,
3175 3177 [('a', 'active', False,
3176 3178 _('show only branches that have unmerged heads')),
3177 3179 ('c', 'closed', False,
3178 _('show normal and closed heads'))],
3180 _('show normal and closed branches'))],
3179 3181 _('[-a]')),
3180 3182 "bundle":
3181 3183 (bundle,
3182 3184 [('f', 'force', None,
3183 3185 _('run even when remote repository is unrelated')),
3184 3186 ('r', 'rev', [],
3185 3187 _('a changeset up to which you would like to bundle')),
3186 3188 ('', 'base', [],
3187 3189 _('a base changeset to specify instead of a destination')),
3188 3190 ('a', 'all', None, _('bundle all changesets in the repository')),
3189 3191 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3190 3192 ] + remoteopts,
3191 3193 _('[-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3192 3194 "cat":
3193 3195 (cat,
3194 3196 [('o', 'output', '', _('print output to file with formatted name')),
3195 3197 ('r', 'rev', '', _('print the given revision')),
3196 3198 ('', 'decode', None, _('apply any matching decode filter')),
3197 3199 ] + walkopts,
3198 3200 _('[OPTION]... FILE...')),
3199 3201 "^clone":
3200 3202 (clone,
3201 3203 [('U', 'noupdate', None,
3202 3204 _('the clone will only contain a repository (no working copy)')),
3203 3205 ('r', 'rev', [],
3204 3206 _('a changeset you would like to have after cloning')),
3205 3207 ('', 'pull', None, _('use pull protocol to copy metadata')),
3206 3208 ('', 'uncompressed', None,
3207 3209 _('use uncompressed transfer (fast over LAN)')),
3208 3210 ] + remoteopts,
3209 3211 _('[OPTION]... SOURCE [DEST]')),
3210 3212 "^commit|ci":
3211 3213 (commit,
3212 3214 [('A', 'addremove', None,
3213 3215 _('mark new/missing files as added/removed before committing')),
3214 3216 ('', 'close-branch', None,
3215 3217 _('mark a branch as closed, hiding it from the branch list')),
3216 3218 ] + walkopts + commitopts + commitopts2,
3217 3219 _('[OPTION]... [FILE]...')),
3218 3220 "copy|cp":
3219 3221 (copy,
3220 3222 [('A', 'after', None, _('record a copy that has already occurred')),
3221 3223 ('f', 'force', None,
3222 3224 _('forcibly copy over an existing managed file')),
3223 3225 ] + walkopts + dryrunopts,
3224 3226 _('[OPTION]... [SOURCE]... DEST')),
3225 3227 "debugancestor": (debugancestor, [], _('[INDEX] REV1 REV2')),
3226 3228 "debugcheckstate": (debugcheckstate, []),
3227 3229 "debugcommands": (debugcommands, [], _('[COMMAND]')),
3228 3230 "debugcomplete":
3229 3231 (debugcomplete,
3230 3232 [('o', 'options', None, _('show the command options'))],
3231 3233 _('[-o] CMD')),
3232 3234 "debugdate":
3233 3235 (debugdate,
3234 3236 [('e', 'extended', None, _('try extended date formats'))],
3235 3237 _('[-e] DATE [RANGE]')),
3236 3238 "debugdata": (debugdata, [], _('FILE REV')),
3237 3239 "debugfsinfo": (debugfsinfo, [], _('[PATH]')),
3238 3240 "debugindex": (debugindex, [], _('FILE')),
3239 3241 "debugindexdot": (debugindexdot, [], _('FILE')),
3240 3242 "debuginstall": (debuginstall, []),
3241 3243 "debugrebuildstate":
3242 3244 (debugrebuildstate,
3243 3245 [('r', 'rev', '', _('revision to rebuild to'))],
3244 3246 _('[-r REV] [REV]')),
3245 3247 "debugrename":
3246 3248 (debugrename,
3247 3249 [('r', 'rev', '', _('revision to debug'))],
3248 3250 _('[-r REV] FILE')),
3249 3251 "debugsetparents":
3250 3252 (debugsetparents, [], _('REV1 [REV2]')),
3251 3253 "debugstate":
3252 3254 (debugstate,
3253 3255 [('', 'nodates', None, _('do not display the saved mtime'))],
3254 3256 _('[OPTION]...')),
3255 3257 "debugsub":
3256 3258 (debugsub,
3257 3259 [('r', 'rev', '', _('revision to check'))],
3258 3260 _('[-r REV] [REV]')),
3259 3261 "debugwalk": (debugwalk, walkopts, _('[OPTION]... [FILE]...')),
3260 3262 "^diff":
3261 3263 (diff,
3262 3264 [('r', 'rev', [], _('revision')),
3263 3265 ('c', 'change', '', _('change made by revision'))
3264 3266 ] + diffopts + diffopts2 + walkopts,
3265 3267 _('[OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3266 3268 "^export":
3267 3269 (export,
3268 3270 [('o', 'output', '', _('print output to file with formatted name')),
3269 3271 ('', 'switch-parent', None, _('diff against the second parent'))
3270 3272 ] + diffopts,
3271 3273 _('[OPTION]... [-o OUTFILESPEC] REV...')),
3272 3274 "^forget":
3273 3275 (forget,
3274 3276 [] + walkopts,
3275 3277 _('[OPTION]... FILE...')),
3276 3278 "grep":
3277 3279 (grep,
3278 3280 [('0', 'print0', None, _('end fields with NUL')),
3279 3281 ('', 'all', None, _('print all revisions that match')),
3280 3282 ('f', 'follow', None,
3281 3283 _('follow changeset history, or file history across copies and renames')),
3282 3284 ('i', 'ignore-case', None, _('ignore case when matching')),
3283 3285 ('l', 'files-with-matches', None,
3284 3286 _('print only filenames and revisions that match')),
3285 3287 ('n', 'line-number', None, _('print matching line numbers')),
3286 3288 ('r', 'rev', [], _('search in given revision range')),
3287 3289 ('u', 'user', None, _('list the author (long with -v)')),
3288 3290 ('d', 'date', None, _('list the date (short with -q)')),
3289 3291 ] + walkopts,
3290 3292 _('[OPTION]... PATTERN [FILE]...')),
3291 3293 "heads":
3292 3294 (heads,
3293 3295 [('r', 'rev', '', _('show only heads which are descendants of REV')),
3294 3296 ('a', 'active', False,
3295 _('show only the active heads from open branches')),
3297 _('show only the active branch heads from open branches')),
3296 3298 ('c', 'closed', False,
3297 _('show normal and closed heads')),
3299 _('show normal and closed branch heads')),
3298 3300 ] + templateopts,
3299 3301 _('[-r STARTREV] [REV]...')),
3300 3302 "help": (help_, [], _('[TOPIC]')),
3301 3303 "identify|id":
3302 3304 (identify,
3303 3305 [('r', 'rev', '', _('identify the specified revision')),
3304 3306 ('n', 'num', None, _('show local revision number')),
3305 3307 ('i', 'id', None, _('show global revision id')),
3306 3308 ('b', 'branch', None, _('show branch')),
3307 3309 ('t', 'tags', None, _('show tags'))],
3308 3310 _('[-nibt] [-r REV] [SOURCE]')),
3309 3311 "import|patch":
3310 3312 (import_,
3311 3313 [('p', 'strip', 1,
3312 3314 _('directory strip option for patch. This has the same '
3313 3315 'meaning as the corresponding patch option')),
3314 3316 ('b', 'base', '', _('base path')),
3315 3317 ('f', 'force', None,
3316 3318 _('skip check for outstanding uncommitted changes')),
3317 3319 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3318 3320 ('', 'exact', None,
3319 3321 _('apply patch to the nodes from which it was generated')),
3320 3322 ('', 'import-branch', None,
3321 3323 _('use any branch information in patch (implied by --exact)'))] +
3322 3324 commitopts + commitopts2 + similarityopts,
3323 3325 _('[OPTION]... PATCH...')),
3324 3326 "incoming|in":
3325 3327 (incoming,
3326 3328 [('f', 'force', None,
3327 3329 _('run even when remote repository is unrelated')),
3328 3330 ('n', 'newest-first', None, _('show newest record first')),
3329 3331 ('', 'bundle', '', _('file to store the bundles into')),
3330 3332 ('r', 'rev', [],
3331 3333 _('a specific revision up to which you would like to pull')),
3332 3334 ] + logopts + remoteopts,
3333 3335 _('[-p] [-n] [-M] [-f] [-r REV]...'
3334 3336 ' [--bundle FILENAME] [SOURCE]')),
3335 3337 "^init":
3336 3338 (init,
3337 3339 remoteopts,
3338 3340 _('[-e CMD] [--remotecmd CMD] [DEST]')),
3339 3341 "locate":
3340 3342 (locate,
3341 3343 [('r', 'rev', '', _('search the repository as it stood at REV')),
3342 3344 ('0', 'print0', None,
3343 3345 _('end filenames with NUL, for use with xargs')),
3344 3346 ('f', 'fullpath', None,
3345 3347 _('print complete paths from the filesystem root')),
3346 3348 ] + walkopts,
3347 3349 _('[OPTION]... [PATTERN]...')),
3348 3350 "^log|history":
3349 3351 (log,
3350 3352 [('f', 'follow', None,
3351 3353 _('follow changeset history, or file history across copies and renames')),
3352 3354 ('', 'follow-first', None,
3353 3355 _('only follow the first parent of merge changesets')),
3354 3356 ('d', 'date', '', _('show revisions matching date spec')),
3355 3357 ('C', 'copies', None, _('show copied files')),
3356 3358 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3357 3359 ('r', 'rev', [], _('show the specified revision or range')),
3358 3360 ('', 'removed', None, _('include revisions where files were removed')),
3359 3361 ('m', 'only-merges', None, _('show only merges')),
3360 3362 ('u', 'user', [], _('revisions committed by user')),
3361 3363 ('b', 'only-branch', [],
3362 3364 _('show only changesets within the given named branch')),
3363 3365 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3364 3366 ] + logopts + walkopts,
3365 3367 _('[OPTION]... [FILE]')),
3366 3368 "manifest":
3367 3369 (manifest,
3368 3370 [('r', 'rev', '', _('revision to display'))],
3369 3371 _('[-r REV]')),
3370 3372 "^merge":
3371 3373 (merge,
3372 3374 [('f', 'force', None, _('force a merge with outstanding changes')),
3373 3375 ('r', 'rev', '', _('revision to merge')),
3374 3376 ('P', 'preview', None,
3375 3377 _('review revisions to merge (no merge is performed)'))],
3376 3378 _('[-f] [[-r] REV]')),
3377 3379 "outgoing|out":
3378 3380 (outgoing,
3379 3381 [('f', 'force', None,
3380 3382 _('run even when remote repository is unrelated')),
3381 3383 ('r', 'rev', [],
3382 3384 _('a specific revision up to which you would like to push')),
3383 3385 ('n', 'newest-first', None, _('show newest record first')),
3384 3386 ] + logopts + remoteopts,
3385 3387 _('[-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3386 3388 "^parents":
3387 3389 (parents,
3388 3390 [('r', 'rev', '', _('show parents from the specified revision')),
3389 3391 ] + templateopts,
3390 3392 _('[-r REV] [FILE]')),
3391 3393 "paths": (paths, [], _('[NAME]')),
3392 3394 "^pull":
3393 3395 (pull,
3394 3396 [('u', 'update', None,
3395 3397 _('update to new tip if changesets were pulled')),
3396 3398 ('f', 'force', None,
3397 3399 _('run even when remote repository is unrelated')),
3398 3400 ('r', 'rev', [],
3399 3401 _('a specific revision up to which you would like to pull')),
3400 3402 ] + remoteopts,
3401 3403 _('[-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3402 3404 "^push":
3403 3405 (push,
3404 3406 [('f', 'force', None, _('force push')),
3405 3407 ('r', 'rev', [],
3406 3408 _('a specific revision up to which you would like to push')),
3407 3409 ] + remoteopts,
3408 3410 _('[-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3409 3411 "recover": (recover, []),
3410 3412 "^remove|rm":
3411 3413 (remove,
3412 3414 [('A', 'after', None, _('record delete for missing files')),
3413 3415 ('f', 'force', None,
3414 3416 _('remove (and delete) file even if added or modified')),
3415 3417 ] + walkopts,
3416 3418 _('[OPTION]... FILE...')),
3417 3419 "rename|mv":
3418 3420 (rename,
3419 3421 [('A', 'after', None, _('record a rename that has already occurred')),
3420 3422 ('f', 'force', None,
3421 3423 _('forcibly copy over an existing managed file')),
3422 3424 ] + walkopts + dryrunopts,
3423 3425 _('[OPTION]... SOURCE... DEST')),
3424 3426 "resolve":
3425 3427 (resolve,
3426 3428 [('a', 'all', None, _('remerge all unresolved files')),
3427 3429 ('l', 'list', None, _('list state of files needing merge')),
3428 3430 ('m', 'mark', None, _('mark files as resolved')),
3429 3431 ('u', 'unmark', None, _('unmark files as resolved'))]
3430 3432 + walkopts,
3431 3433 _('[OPTION]... [FILE]...')),
3432 3434 "revert":
3433 3435 (revert,
3434 3436 [('a', 'all', None, _('revert all changes when no arguments given')),
3435 3437 ('d', 'date', '', _('tipmost revision matching date')),
3436 3438 ('r', 'rev', '', _('revision to revert to')),
3437 3439 ('', 'no-backup', None, _('do not save backup copies of files')),
3438 3440 ] + walkopts + dryrunopts,
3439 3441 _('[OPTION]... [-r REV] [NAME]...')),
3440 3442 "rollback": (rollback, []),
3441 3443 "root": (root, []),
3442 3444 "^serve":
3443 3445 (serve,
3444 3446 [('A', 'accesslog', '', _('name of access log file to write to')),
3445 3447 ('d', 'daemon', None, _('run server in background')),
3446 3448 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3447 3449 ('E', 'errorlog', '', _('name of error log file to write to')),
3448 3450 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3449 3451 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3450 3452 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3451 3453 ('n', 'name', '',
3452 3454 _('name to show in web pages (default: working directory)')),
3453 3455 ('', 'webdir-conf', '', _('name of the webdir config file'
3454 3456 ' (serve more than one repository)')),
3455 3457 ('', 'pid-file', '', _('name of file to write process ID to')),
3456 3458 ('', 'stdio', None, _('for remote clients')),
3457 3459 ('t', 'templates', '', _('web templates to use')),
3458 3460 ('', 'style', '', _('template style to use')),
3459 3461 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3460 3462 ('', 'certificate', '', _('SSL certificate file'))],
3461 3463 _('[OPTION]...')),
3462 3464 "showconfig|debugconfig":
3463 3465 (showconfig,
3464 3466 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3465 3467 _('[-u] [NAME]...')),
3466 3468 "^status|st":
3467 3469 (status,
3468 3470 [('A', 'all', None, _('show status of all files')),
3469 3471 ('m', 'modified', None, _('show only modified files')),
3470 3472 ('a', 'added', None, _('show only added files')),
3471 3473 ('r', 'removed', None, _('show only removed files')),
3472 3474 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3473 3475 ('c', 'clean', None, _('show only files without changes')),
3474 3476 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3475 3477 ('i', 'ignored', None, _('show only ignored files')),
3476 3478 ('n', 'no-status', None, _('hide status prefix')),
3477 3479 ('C', 'copies', None, _('show source of copied files')),
3478 3480 ('0', 'print0', None,
3479 3481 _('end filenames with NUL, for use with xargs')),
3480 3482 ('', 'rev', [], _('show difference from revision')),
3481 3483 ] + walkopts,
3482 3484 _('[OPTION]... [FILE]...')),
3483 3485 "tag":
3484 3486 (tag,
3485 3487 [('f', 'force', None, _('replace existing tag')),
3486 3488 ('l', 'local', None, _('make the tag local')),
3487 3489 ('r', 'rev', '', _('revision to tag')),
3488 3490 ('', 'remove', None, _('remove a tag')),
3489 3491 # -l/--local is already there, commitopts cannot be used
3490 3492 ('m', 'message', '', _('use <text> as commit message')),
3491 3493 ] + commitopts2,
3492 3494 _('[-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3493 3495 "tags": (tags, []),
3494 3496 "tip":
3495 3497 (tip,
3496 3498 [('p', 'patch', None, _('show patch')),
3497 3499 ('g', 'git', None, _('use git extended diff format')),
3498 3500 ] + templateopts,
3499 3501 _('[-p]')),
3500 3502 "unbundle":
3501 3503 (unbundle,
3502 3504 [('u', 'update', None,
3503 3505 _('update to new tip if changesets were unbundled'))],
3504 3506 _('[-u] FILE...')),
3505 3507 "^update|up|checkout|co":
3506 3508 (update,
3507 3509 [('C', 'clean', None, _('overwrite locally modified files (no backup)')),
3508 3510 ('c', 'check', None, _('check for uncommitted changes')),
3509 3511 ('d', 'date', '', _('tipmost revision matching date')),
3510 3512 ('r', 'rev', '', _('revision'))],
3511 3513 _('[-C] [-d DATE] [[-r] REV]')),
3512 3514 "verify": (verify, []),
3513 3515 "version": (version_, []),
3514 3516 }
3515 3517
3516 3518 norepo = ("clone init version help debugcommands debugcomplete debugdata"
3517 3519 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3518 3520 optionalrepo = ("identify paths serve showconfig debugancestor")
@@ -1,192 +1,197 b''
1 1 # subrepo.py - sub-repository handling for Mercurial
2 2 #
3 3 # Copyright 2006, 2007 Matt Mackall <mpm@selenic.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2, incorporated herein by reference.
7 7
8 8 import errno, os
9 9 from i18n import _
10 10 import config, util, node, error
11 11 hg = None
12 12
13 13 nullstate = ('', '')
14 14
15 15 def state(ctx):
16 16 p = config.config()
17 17 def read(f, sections=None, remap=None):
18 18 if f in ctx:
19 19 try:
20 20 p.parse(f, ctx[f].data(), sections, remap)
21 21 except IOError, err:
22 22 if err.errno != errno.ENOENT:
23 23 raise
24 24 read('.hgsub')
25 25
26 26 rev = {}
27 27 if '.hgsubstate' in ctx:
28 28 try:
29 29 for l in ctx['.hgsubstate'].data().splitlines():
30 30 revision, path = l.split()
31 31 rev[path] = revision
32 32 except IOError, err:
33 33 if err.errno != errno.ENOENT:
34 34 raise
35 35
36 36 state = {}
37 37 for path, src in p[''].items():
38 38 state[path] = (src, rev.get(path, ''))
39 39
40 40 return state
41 41
42 42 def writestate(repo, state):
43 43 repo.wwrite('.hgsubstate',
44 44 ''.join(['%s %s\n' % (state[s][1], s)
45 45 for s in sorted(state)]), '')
46 46
47 47 def submerge(repo, wctx, mctx, actx):
48 48 if mctx == actx: # backwards?
49 49 actx = wctx.p1()
50 50 s1 = wctx.substate
51 51 s2 = mctx.substate
52 52 sa = actx.substate
53 53 sm = {}
54 54
55 55 for s, l in s1.items():
56 56 a = sa.get(s, nullstate)
57 57 if s in s2:
58 58 r = s2[s]
59 59 if l == r or r == a: # no change or local is newer
60 60 sm[s] = l
61 61 continue
62 62 elif l == a: # other side changed
63 63 wctx.sub(s).get(r)
64 64 sm[s] = r
65 65 elif l[0] != r[0]: # sources differ
66 66 if repo.ui.promptchoice(
67 67 _(' subrepository sources for %s differ\n'
68 68 'use (l)ocal source (%s) or (r)emote source (%s)?')
69 69 % (s, l[0], r[0]),
70 70 (_('&Local'), _('&Remote')), 0):
71 71 wctx.sub(s).get(r)
72 72 sm[s] = r
73 73 elif l[1] == a[1]: # local side is unchanged
74 74 wctx.sub(s).get(r)
75 75 sm[s] = r
76 76 else:
77 77 wctx.sub(s).merge(r)
78 78 sm[s] = l
79 79 elif l == a: # remote removed, local unchanged
80 80 wctx.sub(s).remove()
81 81 else:
82 82 if repo.ui.promptchoice(
83 83 _(' local changed subrepository %s which remote removed\n'
84 84 'use (c)hanged version or (d)elete?') % s,
85 85 (_('&Changed'), _('&Delete')), 0):
86 86 wctx.sub(s).remove()
87 87
88 88 for s, r in s2.items():
89 89 if s in s1:
90 90 continue
91 91 elif s not in sa:
92 92 wctx.sub(s).get(r)
93 93 sm[s] = r
94 94 elif r != sa[s]:
95 95 if repo.ui.promptchoice(
96 96 _(' remote changed subrepository %s which local removed\n'
97 97 'use (c)hanged version or (d)elete?') % s,
98 98 (_('&Changed'), _('&Delete')), 0) == 0:
99 99 wctx.sub(s).get(r)
100 100 sm[s] = r
101 101
102 102 # record merged .hgsubstate
103 103 writestate(repo, sm)
104 104
105 105 def _abssource(repo, push=False):
106 106 if hasattr(repo, '_subparent'):
107 107 source = repo._subsource
108 108 if source.startswith('/') or '://' in source:
109 109 return source
110 return os.path.join(_abssource(repo._subparent), repo._subsource)
110 parent = _abssource(repo._subparent)
111 if '://' in parent:
112 if parent[-1] == '/':
113 parent = parent[:-1]
114 return parent + '/' + source
115 return os.path.join(parent, repo._subsource)
111 116 if push and repo.ui.config('paths', 'default-push'):
112 117 return repo.ui.config('paths', 'default-push', repo.root)
113 118 return repo.ui.config('paths', 'default', repo.root)
114 119
115 120 def subrepo(ctx, path):
116 121 # subrepo inherently violates our import layering rules
117 122 # because it wants to make repo objects from deep inside the stack
118 123 # so we manually delay the circular imports to not break
119 124 # scripts that don't use our demand-loading
120 125 global hg
121 126 import hg as h
122 127 hg = h
123 128
124 129 util.path_auditor(ctx._repo.root)(path)
125 130 state = ctx.substate.get(path, nullstate)
126 131 if state[0].startswith('['): # future expansion
127 132 raise error.Abort('unknown subrepo source %s' % state[0])
128 133 return hgsubrepo(ctx, path, state)
129 134
130 135 class hgsubrepo(object):
131 136 def __init__(self, ctx, path, state):
132 137 self._path = path
133 138 self._state = state
134 139 r = ctx._repo
135 140 root = r.wjoin(path)
136 141 if os.path.exists(os.path.join(root, '.hg')):
137 142 self._repo = hg.repository(r.ui, root)
138 143 else:
139 144 util.makedirs(root)
140 145 self._repo = hg.repository(r.ui, root, create=True)
141 146 self._repo._subparent = r
142 147 self._repo._subsource = state[0]
143 148
144 149 def dirty(self):
145 150 r = self._state[1]
146 151 if r == '':
147 152 return True
148 153 w = self._repo[None]
149 154 if w.p1() != self._repo[r]: # version checked out changed
150 155 return True
151 156 return w.dirty() # working directory changed
152 157
153 158 def commit(self, text, user, date):
154 159 n = self._repo.commit(text, user, date)
155 160 if not n:
156 161 return self._repo['.'].hex() # different version checked out
157 162 return node.hex(n)
158 163
159 164 def remove(self):
160 165 # we can't fully delete the repository as it may contain
161 166 # local-only history
162 167 self._repo.ui.note(_('removing subrepo %s\n') % self._path)
163 168 hg.clean(self._repo, node.nullid, False)
164 169
165 170 def get(self, state):
166 171 source, revision = state
167 172 try:
168 173 self._repo.lookup(revision)
169 174 except error.RepoError:
170 175 self._repo._subsource = source
171 176 self._repo.ui.status(_('pulling subrepo %s\n') % self._path)
172 177 srcurl = _abssource(self._repo)
173 178 other = hg.repository(self._repo.ui, srcurl)
174 179 self._repo.pull(other)
175 180
176 181 hg.clean(self._repo, revision, False)
177 182
178 183 def merge(self, state):
179 184 hg.merge(self._repo, state[1], remind=False)
180 185
181 186 def push(self, force):
182 187 # push subrepos depth-first for coherent ordering
183 188 c = self._repo['']
184 189 subs = c.substate # only repos that are committed
185 190 for s in sorted(subs):
186 191 c.sub(s).push(force)
187 192
188 193 self._repo.ui.status(_('pushing subrepo %s\n') % self._path)
189 194 dsturl = _abssource(self._repo, True)
190 195 other = hg.repository(self._repo.ui, dsturl)
191 196 self._repo.push(other, force)
192 197
@@ -1,192 +1,173 b''
1 1 marked working directory as branch a
2 2 marked working directory as branch q
3 3 reset working directory to branch a
4 4 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
5 5 marked working directory as branch b
6 6 created new head
7 7 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
8 8 marked working directory as branch c
9 9 marked working directory as branch a branch name much longer than the default justification used by branches
10 10 a branch name much longer than the default justification used by branches 7:10ff5895aa57
11 11 b 4:aee39cd168d0
12 12 c 6:589736a22561 (inactive)
13 13 a 5:d8cbc61dbaa6 (inactive)
14 14 default 0:19709c5a4e75 (inactive)
15 15 -------
16 16 a branch name much longer than the default justification used by branches 7:10ff5895aa57
17 17 b 4:aee39cd168d0
18 18 --- Branch a
19 19 changeset: 5:d8cbc61dbaa6
20 20 branch: a
21 21 parent: 2:881fe2b92ad0
22 22 user: test
23 23 date: Thu Jan 01 00:00:04 1970 +0000
24 24 summary: Adding b branch head 2
25 25
26 26 changeset: 2:881fe2b92ad0
27 27 branch: a
28 28 user: test
29 29 date: Thu Jan 01 00:00:02 1970 +0000
30 30 summary: Adding to a branch
31 31
32 32 changeset: 1:dd6b440dd85a
33 33 branch: a
34 34 user: test
35 35 date: Thu Jan 01 00:00:01 1970 +0000
36 36 summary: Adding a branch
37 37
38 38 ---- Branch b
39 39 changeset: 4:aee39cd168d0
40 40 branch: b
41 41 user: test
42 42 date: Thu Jan 01 00:00:03 1970 +0000
43 43 summary: Adding b branch head 1
44 44
45 45 changeset: 3:ac22033332d1
46 46 branch: b
47 47 parent: 0:19709c5a4e75
48 48 user: test
49 49 date: Thu Jan 01 00:00:02 1970 +0000
50 50 summary: Adding b branch
51 51
52 52 ---- going to test branch closing
53 53 a branch name much longer than the default justification used by branches 7:10ff5895aa57
54 54 b 4:aee39cd168d0
55 55 c 6:589736a22561 (inactive)
56 56 a 5:d8cbc61dbaa6 (inactive)
57 57 default 0:19709c5a4e75 (inactive)
58 58 2 files updated, 0 files merged, 4 files removed, 0 files unresolved
59 59 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
60 60 created new head
61 61 b 10:bfbe841b666e
62 62 a branch name much longer than the default justification used by branches 7:10ff5895aa57
63 63 c 6:589736a22561 (inactive)
64 64 a 5:d8cbc61dbaa6 (inactive)
65 65 default 0:19709c5a4e75 (inactive)
66 changeset: 10:bfbe841b666e
67 branch: b
68 tag: tip
69 user: test
70 date: Thu Jan 01 00:00:09 1970 +0000
71 summary: adding another cset to branch b
72
73 changeset: 8:eebb944467c9
74 branch: b
75 parent: 4:aee39cd168d0
76 user: test
77 date: Thu Jan 01 00:00:07 1970 +0000
78 summary: adding cset to branch b
79
80 changeset: 7:10ff5895aa57
81 branch: a branch name much longer than the default justification used by branches
82 user: test
83 date: Thu Jan 01 00:00:06 1970 +0000
84 summary: Adding d branch
85
66 abort: you must specify a branch to use --closed
86 67 changeset: 10:bfbe841b666e
87 68 branch: b
88 69 tag: tip
89 70 user: test
90 71 date: Thu Jan 01 00:00:09 1970 +0000
91 72 summary: adding another cset to branch b
92 73
93 74 changeset: 8:eebb944467c9
94 75 branch: b
95 76 parent: 4:aee39cd168d0
96 77 user: test
97 78 date: Thu Jan 01 00:00:07 1970 +0000
98 79 summary: adding cset to branch b
99 80
100 81 changeset: 7:10ff5895aa57
101 82 branch: a branch name much longer than the default justification used by branches
102 83 user: test
103 84 date: Thu Jan 01 00:00:06 1970 +0000
104 85 summary: Adding d branch
105 86
106 87 b 8:eebb944467c9
107 88 a branch name much longer than the default justification used by branches 7:10ff5895aa57
108 89 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
109 90 --- b branch should be inactive
110 91 a branch name much longer than the default justification used by branches 7:10ff5895aa57
111 92 c 6:589736a22561 (inactive)
112 93 a 5:d8cbc61dbaa6 (inactive)
113 94 default 0:19709c5a4e75 (inactive)
114 95 a branch name much longer than the default justification used by branches 7:10ff5895aa57
115 96 b 12:2da6583810df (closed)
116 97 c 6:589736a22561 (inactive)
117 98 a 5:d8cbc61dbaa6 (inactive)
118 99 default 0:19709c5a4e75 (inactive)
119 100 a branch name much longer than the default justification used by branches 7:10ff5895aa57
120 101 no open branch heads on branch b
121 102 changeset: 12:2da6583810df
122 103 branch: b
123 104 tag: tip
124 105 parent: 8:eebb944467c9
125 106 user: test
126 107 date: Thu Jan 01 00:00:09 1970 +0000
127 108 summary: close this part branch too
128 109
129 110 changeset: 11:c84627f3c15d
130 111 branch: b
131 112 user: test
132 113 date: Thu Jan 01 00:00:09 1970 +0000
133 114 summary: prune bad branch
134 115
135 116 --- branch b is back in action
136 117 b 13:6ac12926b8c3
137 118 a branch name much longer than the default justification used by branches 7:10ff5895aa57
138 119 ---- test heads listings
139 120 changeset: 13:6ac12926b8c3
140 121 branch: b
141 122 tag: tip
142 123 user: test
143 124 date: Thu Jan 01 00:00:09 1970 +0000
144 125 summary: reopen branch with a change
145 126
146 127 changeset: 11:c84627f3c15d
147 128 branch: b
148 129 user: test
149 130 date: Thu Jan 01 00:00:09 1970 +0000
150 131 summary: prune bad branch
151 132
152 133 changeset: 7:10ff5895aa57
153 134 branch: a branch name much longer than the default justification used by branches
154 135 user: test
155 136 date: Thu Jan 01 00:00:06 1970 +0000
156 137 summary: Adding d branch
157 138
158 139 % branch default
159 140 changeset: 0:19709c5a4e75
160 141 user: test
161 142 date: Thu Jan 01 00:00:00 1970 +0000
162 143 summary: Adding root node
163 144
164 145 % branch a
165 146 changeset: 5:d8cbc61dbaa6
166 147 branch: a
167 148 parent: 2:881fe2b92ad0
168 149 user: test
169 150 date: Thu Jan 01 00:00:04 1970 +0000
170 151 summary: Adding b branch head 2
171 152
172 153 % branch b
173 154 changeset: 13:6ac12926b8c3
174 155 branch: b
175 156 tag: tip
176 157 user: test
177 158 date: Thu Jan 01 00:00:09 1970 +0000
178 159 summary: reopen branch with a change
179 160
180 161 changeset: 13:6ac12926b8c3
181 162 branch: b
182 163 tag: tip
183 164 user: test
184 165 date: Thu Jan 01 00:00:09 1970 +0000
185 166 summary: reopen branch with a change
186 167
187 168 changeset: 11:c84627f3c15d
188 169 branch: b
189 170 user: test
190 171 date: Thu Jan 01 00:00:09 1970 +0000
191 172 summary: prune bad branch
192 173
General Comments 0
You need to be logged in to leave comments. Login now