##// END OF EJS Templates
walk: remove remaining users of cmdutils.matchpats
Matt Mackall -
r6582:5acbdd39 default
parent child Browse files
Show More
@@ -1,251 +1,251 b''
1 # extdiff.py - external diff program support for mercurial
1 # extdiff.py - external diff program support for mercurial
2 #
2 #
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
3 # Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 '''
8 '''
9 The `extdiff' Mercurial extension allows you to use external programs
9 The `extdiff' Mercurial extension allows you to use external programs
10 to compare revisions, or revision with working dir. The external diff
10 to compare revisions, or revision with working dir. The external diff
11 programs are called with a configurable set of options and two
11 programs are called with a configurable set of options and two
12 non-option arguments: paths to directories containing snapshots of
12 non-option arguments: paths to directories containing snapshots of
13 files to compare.
13 files to compare.
14
14
15 To enable this extension:
15 To enable this extension:
16
16
17 [extensions]
17 [extensions]
18 hgext.extdiff =
18 hgext.extdiff =
19
19
20 The `extdiff' extension also allows to configure new diff commands, so
20 The `extdiff' extension also allows to configure new diff commands, so
21 you do not need to type "hg extdiff -p kdiff3" always.
21 you do not need to type "hg extdiff -p kdiff3" always.
22
22
23 [extdiff]
23 [extdiff]
24 # add new command that runs GNU diff(1) in 'context diff' mode
24 # add new command that runs GNU diff(1) in 'context diff' mode
25 cdiff = gdiff -Nprc5
25 cdiff = gdiff -Nprc5
26 ## or the old way:
26 ## or the old way:
27 #cmd.cdiff = gdiff
27 #cmd.cdiff = gdiff
28 #opts.cdiff = -Nprc5
28 #opts.cdiff = -Nprc5
29
29
30 # add new command called vdiff, runs kdiff3
30 # add new command called vdiff, runs kdiff3
31 vdiff = kdiff3
31 vdiff = kdiff3
32
32
33 # add new command called meld, runs meld (no need to name twice)
33 # add new command called meld, runs meld (no need to name twice)
34 meld =
34 meld =
35
35
36 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
36 # add new command called vimdiff, runs gvimdiff with DirDiff plugin
37 #(see http://www.vim.org/scripts/script.php?script_id=102)
37 #(see http://www.vim.org/scripts/script.php?script_id=102)
38 # Non english user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
38 # Non english user, be sure to put "let g:DirDiffDynamicDiffText = 1" in
39 # your .vimrc
39 # your .vimrc
40 vimdiff = gvim -f '+next' '+execute "DirDiff" argv(0) argv(1)'
40 vimdiff = gvim -f '+next' '+execute "DirDiff" argv(0) argv(1)'
41
41
42 You can use -I/-X and list of file or directory names like normal
42 You can use -I/-X and list of file or directory names like normal
43 "hg diff" command. The `extdiff' extension makes snapshots of only
43 "hg diff" command. The `extdiff' extension makes snapshots of only
44 needed files, so running the external diff program will actually be
44 needed files, so running the external diff program will actually be
45 pretty fast (at least faster than having to compare the entire tree).
45 pretty fast (at least faster than having to compare the entire tree).
46 '''
46 '''
47
47
48 from mercurial.i18n import _
48 from mercurial.i18n import _
49 from mercurial.node import short
49 from mercurial.node import short
50 from mercurial import cmdutil, util, commands
50 from mercurial import cmdutil, util, commands
51 import os, shlex, shutil, tempfile
51 import os, shlex, shutil, tempfile
52
52
53 def snapshot_node(ui, repo, files, node, tmproot):
53 def snapshot_node(ui, repo, files, node, tmproot):
54 '''snapshot files as of some revision'''
54 '''snapshot files as of some revision'''
55 mf = repo.changectx(node).manifest()
55 mf = repo.changectx(node).manifest()
56 dirname = os.path.basename(repo.root)
56 dirname = os.path.basename(repo.root)
57 if dirname == "":
57 if dirname == "":
58 dirname = "root"
58 dirname = "root"
59 dirname = '%s.%s' % (dirname, short(node))
59 dirname = '%s.%s' % (dirname, short(node))
60 base = os.path.join(tmproot, dirname)
60 base = os.path.join(tmproot, dirname)
61 os.mkdir(base)
61 os.mkdir(base)
62 ui.note(_('making snapshot of %d files from rev %s\n') %
62 ui.note(_('making snapshot of %d files from rev %s\n') %
63 (len(files), short(node)))
63 (len(files), short(node)))
64 for fn in files:
64 for fn in files:
65 if not fn in mf:
65 if not fn in mf:
66 # skipping new file after a merge ?
66 # skipping new file after a merge ?
67 continue
67 continue
68 wfn = util.pconvert(fn)
68 wfn = util.pconvert(fn)
69 ui.note(' %s\n' % wfn)
69 ui.note(' %s\n' % wfn)
70 dest = os.path.join(base, wfn)
70 dest = os.path.join(base, wfn)
71 destdir = os.path.dirname(dest)
71 destdir = os.path.dirname(dest)
72 if not os.path.isdir(destdir):
72 if not os.path.isdir(destdir):
73 os.makedirs(destdir)
73 os.makedirs(destdir)
74 data = repo.wwritedata(wfn, repo.file(wfn).read(mf[wfn]))
74 data = repo.wwritedata(wfn, repo.file(wfn).read(mf[wfn]))
75 open(dest, 'wb').write(data)
75 open(dest, 'wb').write(data)
76 return dirname
76 return dirname
77
77
78
78
79 def snapshot_wdir(ui, repo, files, tmproot):
79 def snapshot_wdir(ui, repo, files, tmproot):
80 '''snapshot files from working directory.
80 '''snapshot files from working directory.
81 if not using snapshot, -I/-X does not work and recursive diff
81 if not using snapshot, -I/-X does not work and recursive diff
82 in tools like kdiff3 and meld displays too many files.'''
82 in tools like kdiff3 and meld displays too many files.'''
83 repo_root = repo.root
83 repo_root = repo.root
84
84
85 dirname = os.path.basename(repo_root)
85 dirname = os.path.basename(repo_root)
86 if dirname == "":
86 if dirname == "":
87 dirname = "root"
87 dirname = "root"
88 base = os.path.join(tmproot, dirname)
88 base = os.path.join(tmproot, dirname)
89 os.mkdir(base)
89 os.mkdir(base)
90 ui.note(_('making snapshot of %d files from working dir\n') %
90 ui.note(_('making snapshot of %d files from working dir\n') %
91 (len(files)))
91 (len(files)))
92
92
93 fns_and_mtime = []
93 fns_and_mtime = []
94
94
95 for fn in files:
95 for fn in files:
96 wfn = util.pconvert(fn)
96 wfn = util.pconvert(fn)
97 ui.note(' %s\n' % wfn)
97 ui.note(' %s\n' % wfn)
98 dest = os.path.join(base, wfn)
98 dest = os.path.join(base, wfn)
99 destdir = os.path.dirname(dest)
99 destdir = os.path.dirname(dest)
100 if not os.path.isdir(destdir):
100 if not os.path.isdir(destdir):
101 os.makedirs(destdir)
101 os.makedirs(destdir)
102
102
103 fp = open(dest, 'wb')
103 fp = open(dest, 'wb')
104 for chunk in util.filechunkiter(repo.wopener(wfn)):
104 for chunk in util.filechunkiter(repo.wopener(wfn)):
105 fp.write(chunk)
105 fp.write(chunk)
106 fp.close()
106 fp.close()
107
107
108 fns_and_mtime.append((dest, os.path.join(repo_root, fn),
108 fns_and_mtime.append((dest, os.path.join(repo_root, fn),
109 os.path.getmtime(dest)))
109 os.path.getmtime(dest)))
110
110
111
111
112 return dirname, fns_and_mtime
112 return dirname, fns_and_mtime
113
113
114
114
115 def dodiff(ui, repo, diffcmd, diffopts, pats, opts):
115 def dodiff(ui, repo, diffcmd, diffopts, pats, opts):
116 '''Do the actuall diff:
116 '''Do the actuall diff:
117
117
118 - copy to a temp structure if diffing 2 internal revisions
118 - copy to a temp structure if diffing 2 internal revisions
119 - copy to a temp structure if diffing working revision with
119 - copy to a temp structure if diffing working revision with
120 another one and more than 1 file is changed
120 another one and more than 1 file is changed
121 - just invoke the diff for a single file in the working dir
121 - just invoke the diff for a single file in the working dir
122 '''
122 '''
123 node1, node2 = cmdutil.revpair(repo, opts['rev'])
123 node1, node2 = cmdutil.revpair(repo, opts['rev'])
124 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
124 matcher = cmdutil.match(repo, pats, opts)
125 modified, added, removed, deleted, unknown = repo.status(
125 modified, added, removed, deleted, unknown = repo.status(
126 node1, node2, files, match=matchfn)[:5]
126 node1, node2, matcher.files(), match=matcher)[:5]
127 if not (modified or added or removed):
127 if not (modified or added or removed):
128 return 0
128 return 0
129
129
130 tmproot = tempfile.mkdtemp(prefix='extdiff.')
130 tmproot = tempfile.mkdtemp(prefix='extdiff.')
131 dir2root = ''
131 dir2root = ''
132 try:
132 try:
133 # Always make a copy of node1
133 # Always make a copy of node1
134 dir1 = snapshot_node(ui, repo, modified + removed, node1, tmproot)
134 dir1 = snapshot_node(ui, repo, modified + removed, node1, tmproot)
135 changes = len(modified) + len(removed) + len(added)
135 changes = len(modified) + len(removed) + len(added)
136
136
137 fns_and_mtime = []
137 fns_and_mtime = []
138
138
139 # If node2 in not the wc or there is >1 change, copy it
139 # If node2 in not the wc or there is >1 change, copy it
140 if node2:
140 if node2:
141 dir2 = snapshot_node(ui, repo, modified + added, node2, tmproot)
141 dir2 = snapshot_node(ui, repo, modified + added, node2, tmproot)
142 elif changes > 1:
142 elif changes > 1:
143 #we only actually need to get the files to copy back to the working
143 #we only actually need to get the files to copy back to the working
144 #dir in this case (because the other cases are: diffing 2 revisions
144 #dir in this case (because the other cases are: diffing 2 revisions
145 #or single file -- in which case the file is already directly passed
145 #or single file -- in which case the file is already directly passed
146 #to the diff tool).
146 #to the diff tool).
147 dir2, fns_and_mtime = snapshot_wdir(ui, repo, modified + added, tmproot)
147 dir2, fns_and_mtime = snapshot_wdir(ui, repo, modified + added, tmproot)
148 else:
148 else:
149 # This lets the diff tool open the changed file directly
149 # This lets the diff tool open the changed file directly
150 dir2 = ''
150 dir2 = ''
151 dir2root = repo.root
151 dir2root = repo.root
152
152
153 # If only one change, diff the files instead of the directories
153 # If only one change, diff the files instead of the directories
154 if changes == 1 :
154 if changes == 1 :
155 if len(modified):
155 if len(modified):
156 dir1 = os.path.join(dir1, util.localpath(modified[0]))
156 dir1 = os.path.join(dir1, util.localpath(modified[0]))
157 dir2 = os.path.join(dir2root, dir2, util.localpath(modified[0]))
157 dir2 = os.path.join(dir2root, dir2, util.localpath(modified[0]))
158 elif len(removed) :
158 elif len(removed) :
159 dir1 = os.path.join(dir1, util.localpath(removed[0]))
159 dir1 = os.path.join(dir1, util.localpath(removed[0]))
160 dir2 = os.devnull
160 dir2 = os.devnull
161 else:
161 else:
162 dir1 = os.devnull
162 dir1 = os.devnull
163 dir2 = os.path.join(dir2root, dir2, util.localpath(added[0]))
163 dir2 = os.path.join(dir2root, dir2, util.localpath(added[0]))
164
164
165 cmdline = ('%s %s %s %s' %
165 cmdline = ('%s %s %s %s' %
166 (util.shellquote(diffcmd), ' '.join(diffopts),
166 (util.shellquote(diffcmd), ' '.join(diffopts),
167 util.shellquote(dir1), util.shellquote(dir2)))
167 util.shellquote(dir1), util.shellquote(dir2)))
168 ui.debug('running %r in %s\n' % (cmdline, tmproot))
168 ui.debug('running %r in %s\n' % (cmdline, tmproot))
169 util.system(cmdline, cwd=tmproot)
169 util.system(cmdline, cwd=tmproot)
170
170
171 for copy_fn, working_fn, mtime in fns_and_mtime:
171 for copy_fn, working_fn, mtime in fns_and_mtime:
172 if os.path.getmtime(copy_fn) != mtime:
172 if os.path.getmtime(copy_fn) != mtime:
173 ui.debug('File changed while diffing. '
173 ui.debug('File changed while diffing. '
174 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn))
174 'Overwriting: %s (src: %s)\n' % (working_fn, copy_fn))
175 util.copyfile(copy_fn, working_fn)
175 util.copyfile(copy_fn, working_fn)
176
176
177 return 1
177 return 1
178 finally:
178 finally:
179 ui.note(_('cleaning up temp directory\n'))
179 ui.note(_('cleaning up temp directory\n'))
180 shutil.rmtree(tmproot)
180 shutil.rmtree(tmproot)
181
181
182 def extdiff(ui, repo, *pats, **opts):
182 def extdiff(ui, repo, *pats, **opts):
183 '''use external program to diff repository (or selected files)
183 '''use external program to diff repository (or selected files)
184
184
185 Show differences between revisions for the specified files, using
185 Show differences between revisions for the specified files, using
186 an external program. The default program used is diff, with
186 an external program. The default program used is diff, with
187 default options "-Npru".
187 default options "-Npru".
188
188
189 To select a different program, use the -p option. The program
189 To select a different program, use the -p option. The program
190 will be passed the names of two directories to compare. To pass
190 will be passed the names of two directories to compare. To pass
191 additional options to the program, use the -o option. These will
191 additional options to the program, use the -o option. These will
192 be passed before the names of the directories to compare.
192 be passed before the names of the directories to compare.
193
193
194 When two revision arguments are given, then changes are
194 When two revision arguments are given, then changes are
195 shown between those revisions. If only one revision is
195 shown between those revisions. If only one revision is
196 specified then that revision is compared to the working
196 specified then that revision is compared to the working
197 directory, and, when no revisions are specified, the
197 directory, and, when no revisions are specified, the
198 working directory files are compared to its parent.'''
198 working directory files are compared to its parent.'''
199 program = opts['program'] or 'diff'
199 program = opts['program'] or 'diff'
200 if opts['program']:
200 if opts['program']:
201 option = opts['option']
201 option = opts['option']
202 else:
202 else:
203 option = opts['option'] or ['-Npru']
203 option = opts['option'] or ['-Npru']
204 return dodiff(ui, repo, program, option, pats, opts)
204 return dodiff(ui, repo, program, option, pats, opts)
205
205
206 cmdtable = {
206 cmdtable = {
207 "extdiff":
207 "extdiff":
208 (extdiff,
208 (extdiff,
209 [('p', 'program', '', _('comparison program to run')),
209 [('p', 'program', '', _('comparison program to run')),
210 ('o', 'option', [], _('pass option to comparison program')),
210 ('o', 'option', [], _('pass option to comparison program')),
211 ('r', 'rev', [], _('revision')),
211 ('r', 'rev', [], _('revision')),
212 ] + commands.walkopts,
212 ] + commands.walkopts,
213 _('hg extdiff [OPT]... [FILE]...')),
213 _('hg extdiff [OPT]... [FILE]...')),
214 }
214 }
215
215
216 def uisetup(ui):
216 def uisetup(ui):
217 for cmd, path in ui.configitems('extdiff'):
217 for cmd, path in ui.configitems('extdiff'):
218 if cmd.startswith('cmd.'):
218 if cmd.startswith('cmd.'):
219 cmd = cmd[4:]
219 cmd = cmd[4:]
220 if not path: path = cmd
220 if not path: path = cmd
221 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
221 diffopts = ui.config('extdiff', 'opts.' + cmd, '')
222 diffopts = diffopts and [diffopts] or []
222 diffopts = diffopts and [diffopts] or []
223 elif cmd.startswith('opts.'):
223 elif cmd.startswith('opts.'):
224 continue
224 continue
225 else:
225 else:
226 # command = path opts
226 # command = path opts
227 if path:
227 if path:
228 diffopts = shlex.split(path)
228 diffopts = shlex.split(path)
229 path = diffopts.pop(0)
229 path = diffopts.pop(0)
230 else:
230 else:
231 path, diffopts = cmd, []
231 path, diffopts = cmd, []
232 def save(cmd, path, diffopts):
232 def save(cmd, path, diffopts):
233 '''use closure to save diff command to use'''
233 '''use closure to save diff command to use'''
234 def mydiff(ui, repo, *pats, **opts):
234 def mydiff(ui, repo, *pats, **opts):
235 return dodiff(ui, repo, path, diffopts, pats, opts)
235 return dodiff(ui, repo, path, diffopts, pats, opts)
236 mydiff.__doc__ = '''use %(path)s to diff repository (or selected files)
236 mydiff.__doc__ = '''use %(path)s to diff repository (or selected files)
237
237
238 Show differences between revisions for the specified
238 Show differences between revisions for the specified
239 files, using the %(path)s program.
239 files, using the %(path)s program.
240
240
241 When two revision arguments are given, then changes are
241 When two revision arguments are given, then changes are
242 shown between those revisions. If only one revision is
242 shown between those revisions. If only one revision is
243 specified then that revision is compared to the working
243 specified then that revision is compared to the working
244 directory, and, when no revisions are specified, the
244 directory, and, when no revisions are specified, the
245 working directory files are compared to its parent.''' % {
245 working directory files are compared to its parent.''' % {
246 'path': util.uirepr(path),
246 'path': util.uirepr(path),
247 }
247 }
248 return mydiff
248 return mydiff
249 cmdtable[cmd] = (save(cmd, path, diffopts),
249 cmdtable[cmd] = (save(cmd, path, diffopts),
250 cmdtable['extdiff'][1][1:],
250 cmdtable['extdiff'][1][1:],
251 _('hg %s [OPTION]... [FILE]...') % cmd)
251 _('hg %s [OPTION]... [FILE]...') % cmd)
@@ -1,556 +1,556 b''
1 # keyword.py - $Keyword$ expansion for Mercurial
1 # keyword.py - $Keyword$ expansion for Mercurial
2 #
2 #
3 # Copyright 2007, 2008 Christian Ebert <blacktrash@gmx.net>
3 # Copyright 2007, 2008 Christian Ebert <blacktrash@gmx.net>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7 #
7 #
8 # $Id$
8 # $Id$
9 #
9 #
10 # Keyword expansion hack against the grain of a DSCM
10 # Keyword expansion hack against the grain of a DSCM
11 #
11 #
12 # There are many good reasons why this is not needed in a distributed
12 # There are many good reasons why this is not needed in a distributed
13 # SCM, still it may be useful in very small projects based on single
13 # SCM, still it may be useful in very small projects based on single
14 # files (like LaTeX packages), that are mostly addressed to an audience
14 # files (like LaTeX packages), that are mostly addressed to an audience
15 # not running a version control system.
15 # not running a version control system.
16 #
16 #
17 # For in-depth discussion refer to
17 # For in-depth discussion refer to
18 # <http://www.selenic.com/mercurial/wiki/index.cgi/KeywordPlan>.
18 # <http://www.selenic.com/mercurial/wiki/index.cgi/KeywordPlan>.
19 #
19 #
20 # Keyword expansion is based on Mercurial's changeset template mappings.
20 # Keyword expansion is based on Mercurial's changeset template mappings.
21 #
21 #
22 # Binary files are not touched.
22 # Binary files are not touched.
23 #
23 #
24 # Setup in hgrc:
24 # Setup in hgrc:
25 #
25 #
26 # [extensions]
26 # [extensions]
27 # # enable extension
27 # # enable extension
28 # hgext.keyword =
28 # hgext.keyword =
29 #
29 #
30 # Files to act upon/ignore are specified in the [keyword] section.
30 # Files to act upon/ignore are specified in the [keyword] section.
31 # Customized keyword template mappings in the [keywordmaps] section.
31 # Customized keyword template mappings in the [keywordmaps] section.
32 #
32 #
33 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
33 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
34
34
35 '''keyword expansion in local repositories
35 '''keyword expansion in local repositories
36
36
37 This extension expands RCS/CVS-like or self-customized $Keywords$
37 This extension expands RCS/CVS-like or self-customized $Keywords$
38 in tracked text files selected by your configuration.
38 in tracked text files selected by your configuration.
39
39
40 Keywords are only expanded in local repositories and not stored in
40 Keywords are only expanded in local repositories and not stored in
41 the change history. The mechanism can be regarded as a convenience
41 the change history. The mechanism can be regarded as a convenience
42 for the current user or for archive distribution.
42 for the current user or for archive distribution.
43
43
44 Configuration is done in the [keyword] and [keywordmaps] sections
44 Configuration is done in the [keyword] and [keywordmaps] sections
45 of hgrc files.
45 of hgrc files.
46
46
47 Example:
47 Example:
48
48
49 [keyword]
49 [keyword]
50 # expand keywords in every python file except those matching "x*"
50 # expand keywords in every python file except those matching "x*"
51 **.py =
51 **.py =
52 x* = ignore
52 x* = ignore
53
53
54 Note: the more specific you are in your filename patterns
54 Note: the more specific you are in your filename patterns
55 the less you lose speed in huge repos.
55 the less you lose speed in huge repos.
56
56
57 For [keywordmaps] template mapping and expansion demonstration and
57 For [keywordmaps] template mapping and expansion demonstration and
58 control run "hg kwdemo".
58 control run "hg kwdemo".
59
59
60 An additional date template filter {date|utcdate} is provided.
60 An additional date template filter {date|utcdate} is provided.
61
61
62 The default template mappings (view with "hg kwdemo -d") can be replaced
62 The default template mappings (view with "hg kwdemo -d") can be replaced
63 with customized keywords and templates.
63 with customized keywords and templates.
64 Again, run "hg kwdemo" to control the results of your config changes.
64 Again, run "hg kwdemo" to control the results of your config changes.
65
65
66 Before changing/disabling active keywords, run "hg kwshrink" to avoid
66 Before changing/disabling active keywords, run "hg kwshrink" to avoid
67 the risk of inadvertedly storing expanded keywords in the change history.
67 the risk of inadvertedly storing expanded keywords in the change history.
68
68
69 To force expansion after enabling it, or a configuration change, run
69 To force expansion after enabling it, or a configuration change, run
70 "hg kwexpand".
70 "hg kwexpand".
71
71
72 Also, when committing with the record extension or using mq's qrecord, be aware
72 Also, when committing with the record extension or using mq's qrecord, be aware
73 that keywords cannot be updated. Again, run "hg kwexpand" on the files in
73 that keywords cannot be updated. Again, run "hg kwexpand" on the files in
74 question to update keyword expansions after all changes have been checked in.
74 question to update keyword expansions after all changes have been checked in.
75
75
76 Expansions spanning more than one line and incremental expansions,
76 Expansions spanning more than one line and incremental expansions,
77 like CVS' $Log$, are not supported. A keyword template map
77 like CVS' $Log$, are not supported. A keyword template map
78 "Log = {desc}" expands to the first line of the changeset description.
78 "Log = {desc}" expands to the first line of the changeset description.
79 '''
79 '''
80
80
81 from mercurial import commands, cmdutil, context, dispatch, filelog, revlog
81 from mercurial import commands, cmdutil, context, dispatch, filelog, revlog
82 from mercurial import patch, localrepo, templater, templatefilters, util
82 from mercurial import patch, localrepo, templater, templatefilters, util
83 from mercurial.hgweb import webcommands
83 from mercurial.hgweb import webcommands
84 from mercurial.node import nullid, hex
84 from mercurial.node import nullid, hex
85 from mercurial.i18n import _
85 from mercurial.i18n import _
86 import re, shutil, tempfile, time
86 import re, shutil, tempfile, time
87
87
88 commands.optionalrepo += ' kwdemo'
88 commands.optionalrepo += ' kwdemo'
89
89
90 # hg commands that do not act on keywords
90 # hg commands that do not act on keywords
91 nokwcommands = ('add addremove bundle copy export grep incoming init'
91 nokwcommands = ('add addremove bundle copy export grep incoming init'
92 ' log outgoing push rename rollback tip'
92 ' log outgoing push rename rollback tip'
93 ' convert email glog')
93 ' convert email glog')
94
94
95 # hg commands that trigger expansion only when writing to working dir,
95 # hg commands that trigger expansion only when writing to working dir,
96 # not when reading filelog, and unexpand when reading from working dir
96 # not when reading filelog, and unexpand when reading from working dir
97 restricted = 'record qfold qimport qnew qpush qrefresh qrecord'
97 restricted = 'record qfold qimport qnew qpush qrefresh qrecord'
98
98
99 def utcdate(date):
99 def utcdate(date):
100 '''Returns hgdate in cvs-like UTC format.'''
100 '''Returns hgdate in cvs-like UTC format.'''
101 return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
101 return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
102
102
103 # make keyword tools accessible
103 # make keyword tools accessible
104 kwtools = {'templater': None, 'hgcmd': '', 'inc': [], 'exc': ['.hg*']}
104 kwtools = {'templater': None, 'hgcmd': '', 'inc': [], 'exc': ['.hg*']}
105
105
106
106
107 class kwtemplater(object):
107 class kwtemplater(object):
108 '''
108 '''
109 Sets up keyword templates, corresponding keyword regex, and
109 Sets up keyword templates, corresponding keyword regex, and
110 provides keyword substitution functions.
110 provides keyword substitution functions.
111 '''
111 '''
112 templates = {
112 templates = {
113 'Revision': '{node|short}',
113 'Revision': '{node|short}',
114 'Author': '{author|user}',
114 'Author': '{author|user}',
115 'Date': '{date|utcdate}',
115 'Date': '{date|utcdate}',
116 'RCSFile': '{file|basename},v',
116 'RCSFile': '{file|basename},v',
117 'Source': '{root}/{file},v',
117 'Source': '{root}/{file},v',
118 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
118 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
119 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
119 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
120 }
120 }
121
121
122 def __init__(self, ui, repo):
122 def __init__(self, ui, repo):
123 self.ui = ui
123 self.ui = ui
124 self.repo = repo
124 self.repo = repo
125 self.matcher = util.matcher(repo.root,
125 self.matcher = util.matcher(repo.root,
126 inc=kwtools['inc'], exc=kwtools['exc'])[1]
126 inc=kwtools['inc'], exc=kwtools['exc'])[1]
127 self.restrict = kwtools['hgcmd'] in restricted.split()
127 self.restrict = kwtools['hgcmd'] in restricted.split()
128
128
129 kwmaps = self.ui.configitems('keywordmaps')
129 kwmaps = self.ui.configitems('keywordmaps')
130 if kwmaps: # override default templates
130 if kwmaps: # override default templates
131 kwmaps = [(k, templater.parsestring(v, False))
131 kwmaps = [(k, templater.parsestring(v, False))
132 for (k, v) in kwmaps]
132 for (k, v) in kwmaps]
133 self.templates = dict(kwmaps)
133 self.templates = dict(kwmaps)
134 escaped = map(re.escape, self.templates.keys())
134 escaped = map(re.escape, self.templates.keys())
135 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
135 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
136 self.re_kw = re.compile(kwpat)
136 self.re_kw = re.compile(kwpat)
137
137
138 templatefilters.filters['utcdate'] = utcdate
138 templatefilters.filters['utcdate'] = utcdate
139 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
139 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
140 False, '', False)
140 False, '', False)
141
141
142 def getnode(self, path, fnode):
142 def getnode(self, path, fnode):
143 '''Derives changenode from file path and filenode.'''
143 '''Derives changenode from file path and filenode.'''
144 # used by kwfilelog.read and kwexpand
144 # used by kwfilelog.read and kwexpand
145 c = context.filectx(self.repo, path, fileid=fnode)
145 c = context.filectx(self.repo, path, fileid=fnode)
146 return c.node()
146 return c.node()
147
147
148 def substitute(self, data, path, node, subfunc):
148 def substitute(self, data, path, node, subfunc):
149 '''Replaces keywords in data with expanded template.'''
149 '''Replaces keywords in data with expanded template.'''
150 def kwsub(mobj):
150 def kwsub(mobj):
151 kw = mobj.group(1)
151 kw = mobj.group(1)
152 self.ct.use_template(self.templates[kw])
152 self.ct.use_template(self.templates[kw])
153 self.ui.pushbuffer()
153 self.ui.pushbuffer()
154 self.ct.show(changenode=node, root=self.repo.root, file=path)
154 self.ct.show(changenode=node, root=self.repo.root, file=path)
155 ekw = templatefilters.firstline(self.ui.popbuffer())
155 ekw = templatefilters.firstline(self.ui.popbuffer())
156 return '$%s: %s $' % (kw, ekw)
156 return '$%s: %s $' % (kw, ekw)
157 return subfunc(kwsub, data)
157 return subfunc(kwsub, data)
158
158
159 def expand(self, path, node, data):
159 def expand(self, path, node, data):
160 '''Returns data with keywords expanded.'''
160 '''Returns data with keywords expanded.'''
161 if not self.restrict and self.matcher(path) and not util.binary(data):
161 if not self.restrict and self.matcher(path) and not util.binary(data):
162 changenode = self.getnode(path, node)
162 changenode = self.getnode(path, node)
163 return self.substitute(data, path, changenode, self.re_kw.sub)
163 return self.substitute(data, path, changenode, self.re_kw.sub)
164 return data
164 return data
165
165
166 def iskwfile(self, path, islink):
166 def iskwfile(self, path, islink):
167 '''Returns true if path matches [keyword] pattern
167 '''Returns true if path matches [keyword] pattern
168 and is not a symbolic link.
168 and is not a symbolic link.
169 Caveat: localrepository._link fails on Windows.'''
169 Caveat: localrepository._link fails on Windows.'''
170 return self.matcher(path) and not islink(path)
170 return self.matcher(path) and not islink(path)
171
171
172 def overwrite(self, node, expand, files):
172 def overwrite(self, node, expand, files):
173 '''Overwrites selected files expanding/shrinking keywords.'''
173 '''Overwrites selected files expanding/shrinking keywords.'''
174 ctx = self.repo.changectx(node)
174 ctx = self.repo.changectx(node)
175 mf = ctx.manifest()
175 mf = ctx.manifest()
176 if node is not None: # commit
176 if node is not None: # commit
177 files = [f for f in ctx.files() if f in mf]
177 files = [f for f in ctx.files() if f in mf]
178 notify = self.ui.debug
178 notify = self.ui.debug
179 else: # kwexpand/kwshrink
179 else: # kwexpand/kwshrink
180 notify = self.ui.note
180 notify = self.ui.note
181 candidates = [f for f in files if self.iskwfile(f, mf.linkf)]
181 candidates = [f for f in files if self.iskwfile(f, mf.linkf)]
182 if candidates:
182 if candidates:
183 self.restrict = True # do not expand when reading
183 self.restrict = True # do not expand when reading
184 candidates.sort()
184 candidates.sort()
185 action = expand and 'expanding' or 'shrinking'
185 action = expand and 'expanding' or 'shrinking'
186 for f in candidates:
186 for f in candidates:
187 fp = self.repo.file(f)
187 fp = self.repo.file(f)
188 data = fp.read(mf[f])
188 data = fp.read(mf[f])
189 if util.binary(data):
189 if util.binary(data):
190 continue
190 continue
191 if expand:
191 if expand:
192 changenode = node or self.getnode(f, mf[f])
192 changenode = node or self.getnode(f, mf[f])
193 data, found = self.substitute(data, f, changenode,
193 data, found = self.substitute(data, f, changenode,
194 self.re_kw.subn)
194 self.re_kw.subn)
195 else:
195 else:
196 found = self.re_kw.search(data)
196 found = self.re_kw.search(data)
197 if found:
197 if found:
198 notify(_('overwriting %s %s keywords\n') % (f, action))
198 notify(_('overwriting %s %s keywords\n') % (f, action))
199 self.repo.wwrite(f, data, mf.flags(f))
199 self.repo.wwrite(f, data, mf.flags(f))
200 self.repo.dirstate.normal(f)
200 self.repo.dirstate.normal(f)
201 self.restrict = False
201 self.restrict = False
202
202
203 def shrinktext(self, text):
203 def shrinktext(self, text):
204 '''Unconditionally removes all keyword substitutions from text.'''
204 '''Unconditionally removes all keyword substitutions from text.'''
205 return self.re_kw.sub(r'$\1$', text)
205 return self.re_kw.sub(r'$\1$', text)
206
206
207 def shrink(self, fname, text):
207 def shrink(self, fname, text):
208 '''Returns text with all keyword substitutions removed.'''
208 '''Returns text with all keyword substitutions removed.'''
209 if self.matcher(fname) and not util.binary(text):
209 if self.matcher(fname) and not util.binary(text):
210 return self.shrinktext(text)
210 return self.shrinktext(text)
211 return text
211 return text
212
212
213 def shrinklines(self, fname, lines):
213 def shrinklines(self, fname, lines):
214 '''Returns lines with keyword substitutions removed.'''
214 '''Returns lines with keyword substitutions removed.'''
215 if self.matcher(fname):
215 if self.matcher(fname):
216 text = ''.join(lines)
216 text = ''.join(lines)
217 if not util.binary(text):
217 if not util.binary(text):
218 return self.shrinktext(text).splitlines(True)
218 return self.shrinktext(text).splitlines(True)
219 return lines
219 return lines
220
220
221 def wread(self, fname, data):
221 def wread(self, fname, data):
222 '''If in restricted mode returns data read from wdir with
222 '''If in restricted mode returns data read from wdir with
223 keyword substitutions removed.'''
223 keyword substitutions removed.'''
224 return self.restrict and self.shrink(fname, data) or data
224 return self.restrict and self.shrink(fname, data) or data
225
225
226 class kwfilelog(filelog.filelog):
226 class kwfilelog(filelog.filelog):
227 '''
227 '''
228 Subclass of filelog to hook into its read, add, cmp methods.
228 Subclass of filelog to hook into its read, add, cmp methods.
229 Keywords are "stored" unexpanded, and processed on reading.
229 Keywords are "stored" unexpanded, and processed on reading.
230 '''
230 '''
231 def __init__(self, opener, kwt, path):
231 def __init__(self, opener, kwt, path):
232 super(kwfilelog, self).__init__(opener, path)
232 super(kwfilelog, self).__init__(opener, path)
233 self.kwt = kwt
233 self.kwt = kwt
234 self.path = path
234 self.path = path
235
235
236 def read(self, node):
236 def read(self, node):
237 '''Expands keywords when reading filelog.'''
237 '''Expands keywords when reading filelog.'''
238 data = super(kwfilelog, self).read(node)
238 data = super(kwfilelog, self).read(node)
239 return self.kwt.expand(self.path, node, data)
239 return self.kwt.expand(self.path, node, data)
240
240
241 def add(self, text, meta, tr, link, p1=None, p2=None):
241 def add(self, text, meta, tr, link, p1=None, p2=None):
242 '''Removes keyword substitutions when adding to filelog.'''
242 '''Removes keyword substitutions when adding to filelog.'''
243 text = self.kwt.shrink(self.path, text)
243 text = self.kwt.shrink(self.path, text)
244 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
244 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
245
245
246 def cmp(self, node, text):
246 def cmp(self, node, text):
247 '''Removes keyword substitutions for comparison.'''
247 '''Removes keyword substitutions for comparison.'''
248 text = self.kwt.shrink(self.path, text)
248 text = self.kwt.shrink(self.path, text)
249 if self.renamed(node):
249 if self.renamed(node):
250 t2 = super(kwfilelog, self).read(node)
250 t2 = super(kwfilelog, self).read(node)
251 return t2 != text
251 return t2 != text
252 return revlog.revlog.cmp(self, node, text)
252 return revlog.revlog.cmp(self, node, text)
253
253
254 def _status(ui, repo, kwt, *pats, **opts):
254 def _status(ui, repo, kwt, *pats, **opts):
255 '''Bails out if [keyword] configuration is not active.
255 '''Bails out if [keyword] configuration is not active.
256 Returns status of working directory.'''
256 Returns status of working directory.'''
257 if kwt:
257 if kwt:
258 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
258 matcher = cmdutil.match(repo, pats, opts)
259 return repo.status(files=files, match=match, list_clean=True)
259 return repo.status(files=matcher.files(), match=matcher, list_clean=True)
260 if ui.configitems('keyword'):
260 if ui.configitems('keyword'):
261 raise util.Abort(_('[keyword] patterns cannot match'))
261 raise util.Abort(_('[keyword] patterns cannot match'))
262 raise util.Abort(_('no [keyword] patterns configured'))
262 raise util.Abort(_('no [keyword] patterns configured'))
263
263
264 def _kwfwrite(ui, repo, expand, *pats, **opts):
264 def _kwfwrite(ui, repo, expand, *pats, **opts):
265 '''Selects files and passes them to kwtemplater.overwrite.'''
265 '''Selects files and passes them to kwtemplater.overwrite.'''
266 kwt = kwtools['templater']
266 kwt = kwtools['templater']
267 status = _status(ui, repo, kwt, *pats, **opts)
267 status = _status(ui, repo, kwt, *pats, **opts)
268 modified, added, removed, deleted, unknown, ignored, clean = status
268 modified, added, removed, deleted, unknown, ignored, clean = status
269 if modified or added or removed or deleted:
269 if modified or added or removed or deleted:
270 raise util.Abort(_('outstanding uncommitted changes in given files'))
270 raise util.Abort(_('outstanding uncommitted changes in given files'))
271 wlock = lock = None
271 wlock = lock = None
272 try:
272 try:
273 wlock = repo.wlock()
273 wlock = repo.wlock()
274 lock = repo.lock()
274 lock = repo.lock()
275 kwt.overwrite(None, expand, clean)
275 kwt.overwrite(None, expand, clean)
276 finally:
276 finally:
277 del wlock, lock
277 del wlock, lock
278
278
279
279
280 def demo(ui, repo, *args, **opts):
280 def demo(ui, repo, *args, **opts):
281 '''print [keywordmaps] configuration and an expansion example
281 '''print [keywordmaps] configuration and an expansion example
282
282
283 Show current, custom, or default keyword template maps
283 Show current, custom, or default keyword template maps
284 and their expansion.
284 and their expansion.
285
285
286 Extend current configuration by specifying maps as arguments
286 Extend current configuration by specifying maps as arguments
287 and optionally by reading from an additional hgrc file.
287 and optionally by reading from an additional hgrc file.
288
288
289 Override current keyword template maps with "default" option.
289 Override current keyword template maps with "default" option.
290 '''
290 '''
291 def demostatus(stat):
291 def demostatus(stat):
292 ui.status(_('\n\t%s\n') % stat)
292 ui.status(_('\n\t%s\n') % stat)
293
293
294 def demoitems(section, items):
294 def demoitems(section, items):
295 ui.write('[%s]\n' % section)
295 ui.write('[%s]\n' % section)
296 for k, v in items:
296 for k, v in items:
297 ui.write('%s = %s\n' % (k, v))
297 ui.write('%s = %s\n' % (k, v))
298
298
299 msg = 'hg keyword config and expansion example'
299 msg = 'hg keyword config and expansion example'
300 kwstatus = 'current'
300 kwstatus = 'current'
301 fn = 'demo.txt'
301 fn = 'demo.txt'
302 branchname = 'demobranch'
302 branchname = 'demobranch'
303 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
303 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
304 ui.note(_('creating temporary repo at %s\n') % tmpdir)
304 ui.note(_('creating temporary repo at %s\n') % tmpdir)
305 repo = localrepo.localrepository(ui, tmpdir, True)
305 repo = localrepo.localrepository(ui, tmpdir, True)
306 ui.setconfig('keyword', fn, '')
306 ui.setconfig('keyword', fn, '')
307 if args or opts.get('rcfile'):
307 if args or opts.get('rcfile'):
308 kwstatus = 'custom'
308 kwstatus = 'custom'
309 if opts.get('rcfile'):
309 if opts.get('rcfile'):
310 ui.readconfig(opts.get('rcfile'))
310 ui.readconfig(opts.get('rcfile'))
311 if opts.get('default'):
311 if opts.get('default'):
312 kwstatus = 'default'
312 kwstatus = 'default'
313 kwmaps = kwtemplater.templates
313 kwmaps = kwtemplater.templates
314 if ui.configitems('keywordmaps'):
314 if ui.configitems('keywordmaps'):
315 # override maps from optional rcfile
315 # override maps from optional rcfile
316 for k, v in kwmaps.iteritems():
316 for k, v in kwmaps.iteritems():
317 ui.setconfig('keywordmaps', k, v)
317 ui.setconfig('keywordmaps', k, v)
318 elif args:
318 elif args:
319 # simulate hgrc parsing
319 # simulate hgrc parsing
320 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
320 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
321 fp = repo.opener('hgrc', 'w')
321 fp = repo.opener('hgrc', 'w')
322 fp.writelines(rcmaps)
322 fp.writelines(rcmaps)
323 fp.close()
323 fp.close()
324 ui.readconfig(repo.join('hgrc'))
324 ui.readconfig(repo.join('hgrc'))
325 if not opts.get('default'):
325 if not opts.get('default'):
326 kwmaps = dict(ui.configitems('keywordmaps')) or kwtemplater.templates
326 kwmaps = dict(ui.configitems('keywordmaps')) or kwtemplater.templates
327 uisetup(ui)
327 uisetup(ui)
328 reposetup(ui, repo)
328 reposetup(ui, repo)
329 for k, v in ui.configitems('extensions'):
329 for k, v in ui.configitems('extensions'):
330 if k.endswith('keyword'):
330 if k.endswith('keyword'):
331 extension = '%s = %s' % (k, v)
331 extension = '%s = %s' % (k, v)
332 break
332 break
333 demostatus('config using %s keyword template maps' % kwstatus)
333 demostatus('config using %s keyword template maps' % kwstatus)
334 ui.write('[extensions]\n%s\n' % extension)
334 ui.write('[extensions]\n%s\n' % extension)
335 demoitems('keyword', ui.configitems('keyword'))
335 demoitems('keyword', ui.configitems('keyword'))
336 demoitems('keywordmaps', kwmaps.iteritems())
336 demoitems('keywordmaps', kwmaps.iteritems())
337 keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n'
337 keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n'
338 repo.wopener(fn, 'w').write(keywords)
338 repo.wopener(fn, 'w').write(keywords)
339 repo.add([fn])
339 repo.add([fn])
340 path = repo.wjoin(fn)
340 path = repo.wjoin(fn)
341 ui.note(_('\n%s keywords written to %s:\n') % (kwstatus, path))
341 ui.note(_('\n%s keywords written to %s:\n') % (kwstatus, path))
342 ui.note(keywords)
342 ui.note(keywords)
343 ui.note('\nhg -R "%s" branch "%s"\n' % (tmpdir, branchname))
343 ui.note('\nhg -R "%s" branch "%s"\n' % (tmpdir, branchname))
344 # silence branch command if not verbose
344 # silence branch command if not verbose
345 quiet = ui.quiet
345 quiet = ui.quiet
346 ui.quiet = not ui.verbose
346 ui.quiet = not ui.verbose
347 commands.branch(ui, repo, branchname)
347 commands.branch(ui, repo, branchname)
348 ui.quiet = quiet
348 ui.quiet = quiet
349 for name, cmd in ui.configitems('hooks'):
349 for name, cmd in ui.configitems('hooks'):
350 if name.split('.', 1)[0].find('commit') > -1:
350 if name.split('.', 1)[0].find('commit') > -1:
351 repo.ui.setconfig('hooks', name, '')
351 repo.ui.setconfig('hooks', name, '')
352 ui.note(_('unhooked all commit hooks\n'))
352 ui.note(_('unhooked all commit hooks\n'))
353 ui.note('hg -R "%s" ci -m "%s"\n' % (tmpdir, msg))
353 ui.note('hg -R "%s" ci -m "%s"\n' % (tmpdir, msg))
354 repo.commit(text=msg)
354 repo.commit(text=msg)
355 format = ui.verbose and ' in %s' % path or ''
355 format = ui.verbose and ' in %s' % path or ''
356 demostatus('%s keywords expanded%s' % (kwstatus, format))
356 demostatus('%s keywords expanded%s' % (kwstatus, format))
357 ui.write(repo.wread(fn))
357 ui.write(repo.wread(fn))
358 ui.debug(_('\nremoving temporary repo %s\n') % tmpdir)
358 ui.debug(_('\nremoving temporary repo %s\n') % tmpdir)
359 shutil.rmtree(tmpdir, ignore_errors=True)
359 shutil.rmtree(tmpdir, ignore_errors=True)
360
360
361 def expand(ui, repo, *pats, **opts):
361 def expand(ui, repo, *pats, **opts):
362 '''expand keywords in working directory
362 '''expand keywords in working directory
363
363
364 Run after (re)enabling keyword expansion.
364 Run after (re)enabling keyword expansion.
365
365
366 kwexpand refuses to run if given files contain local changes.
366 kwexpand refuses to run if given files contain local changes.
367 '''
367 '''
368 # 3rd argument sets expansion to True
368 # 3rd argument sets expansion to True
369 _kwfwrite(ui, repo, True, *pats, **opts)
369 _kwfwrite(ui, repo, True, *pats, **opts)
370
370
371 def files(ui, repo, *pats, **opts):
371 def files(ui, repo, *pats, **opts):
372 '''print files currently configured for keyword expansion
372 '''print files currently configured for keyword expansion
373
373
374 Crosscheck which files in working directory are potential targets for
374 Crosscheck which files in working directory are potential targets for
375 keyword expansion.
375 keyword expansion.
376 That is, files matched by [keyword] config patterns but not symlinks.
376 That is, files matched by [keyword] config patterns but not symlinks.
377 '''
377 '''
378 kwt = kwtools['templater']
378 kwt = kwtools['templater']
379 status = _status(ui, repo, kwt, *pats, **opts)
379 status = _status(ui, repo, kwt, *pats, **opts)
380 modified, added, removed, deleted, unknown, ignored, clean = status
380 modified, added, removed, deleted, unknown, ignored, clean = status
381 files = modified + added + clean
381 files = modified + added + clean
382 if opts.get('untracked'):
382 if opts.get('untracked'):
383 files += unknown
383 files += unknown
384 files.sort()
384 files.sort()
385 wctx = repo.workingctx()
385 wctx = repo.workingctx()
386 islink = lambda p: 'l' in wctx.fileflags(p)
386 islink = lambda p: 'l' in wctx.fileflags(p)
387 kwfiles = [f for f in files if kwt.iskwfile(f, islink)]
387 kwfiles = [f for f in files if kwt.iskwfile(f, islink)]
388 cwd = pats and repo.getcwd() or ''
388 cwd = pats and repo.getcwd() or ''
389 kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
389 kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
390 if opts.get('all') or opts.get('ignore'):
390 if opts.get('all') or opts.get('ignore'):
391 kwfstats += (('I', [f for f in files if f not in kwfiles]),)
391 kwfstats += (('I', [f for f in files if f not in kwfiles]),)
392 for char, filenames in kwfstats:
392 for char, filenames in kwfstats:
393 format = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
393 format = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
394 for f in filenames:
394 for f in filenames:
395 ui.write(format % repo.pathto(f, cwd))
395 ui.write(format % repo.pathto(f, cwd))
396
396
397 def shrink(ui, repo, *pats, **opts):
397 def shrink(ui, repo, *pats, **opts):
398 '''revert expanded keywords in working directory
398 '''revert expanded keywords in working directory
399
399
400 Run before changing/disabling active keywords
400 Run before changing/disabling active keywords
401 or if you experience problems with "hg import" or "hg merge".
401 or if you experience problems with "hg import" or "hg merge".
402
402
403 kwshrink refuses to run if given files contain local changes.
403 kwshrink refuses to run if given files contain local changes.
404 '''
404 '''
405 # 3rd argument sets expansion to False
405 # 3rd argument sets expansion to False
406 _kwfwrite(ui, repo, False, *pats, **opts)
406 _kwfwrite(ui, repo, False, *pats, **opts)
407
407
408
408
409 def uisetup(ui):
409 def uisetup(ui):
410 '''Collects [keyword] config in kwtools.
410 '''Collects [keyword] config in kwtools.
411 Monkeypatches dispatch._parse if needed.'''
411 Monkeypatches dispatch._parse if needed.'''
412
412
413 for pat, opt in ui.configitems('keyword'):
413 for pat, opt in ui.configitems('keyword'):
414 if opt != 'ignore':
414 if opt != 'ignore':
415 kwtools['inc'].append(pat)
415 kwtools['inc'].append(pat)
416 else:
416 else:
417 kwtools['exc'].append(pat)
417 kwtools['exc'].append(pat)
418
418
419 if kwtools['inc']:
419 if kwtools['inc']:
420 def kwdispatch_parse(ui, args):
420 def kwdispatch_parse(ui, args):
421 '''Monkeypatch dispatch._parse to obtain running hg command.'''
421 '''Monkeypatch dispatch._parse to obtain running hg command.'''
422 cmd, func, args, options, cmdoptions = dispatch_parse(ui, args)
422 cmd, func, args, options, cmdoptions = dispatch_parse(ui, args)
423 kwtools['hgcmd'] = cmd
423 kwtools['hgcmd'] = cmd
424 return cmd, func, args, options, cmdoptions
424 return cmd, func, args, options, cmdoptions
425
425
426 dispatch_parse = dispatch._parse
426 dispatch_parse = dispatch._parse
427 dispatch._parse = kwdispatch_parse
427 dispatch._parse = kwdispatch_parse
428
428
429 def reposetup(ui, repo):
429 def reposetup(ui, repo):
430 '''Sets up repo as kwrepo for keyword substitution.
430 '''Sets up repo as kwrepo for keyword substitution.
431 Overrides file method to return kwfilelog instead of filelog
431 Overrides file method to return kwfilelog instead of filelog
432 if file matches user configuration.
432 if file matches user configuration.
433 Wraps commit to overwrite configured files with updated
433 Wraps commit to overwrite configured files with updated
434 keyword substitutions.
434 keyword substitutions.
435 Monkeypatches patch and webcommands.'''
435 Monkeypatches patch and webcommands.'''
436
436
437 try:
437 try:
438 if (not repo.local() or not kwtools['inc']
438 if (not repo.local() or not kwtools['inc']
439 or kwtools['hgcmd'] in nokwcommands.split()
439 or kwtools['hgcmd'] in nokwcommands.split()
440 or '.hg' in util.splitpath(repo.root)
440 or '.hg' in util.splitpath(repo.root)
441 or repo._url.startswith('bundle:')):
441 or repo._url.startswith('bundle:')):
442 return
442 return
443 except AttributeError:
443 except AttributeError:
444 pass
444 pass
445
445
446 kwtools['templater'] = kwt = kwtemplater(ui, repo)
446 kwtools['templater'] = kwt = kwtemplater(ui, repo)
447
447
448 class kwrepo(repo.__class__):
448 class kwrepo(repo.__class__):
449 def file(self, f):
449 def file(self, f):
450 if f[0] == '/':
450 if f[0] == '/':
451 f = f[1:]
451 f = f[1:]
452 return kwfilelog(self.sopener, kwt, f)
452 return kwfilelog(self.sopener, kwt, f)
453
453
454 def wread(self, filename):
454 def wread(self, filename):
455 data = super(kwrepo, self).wread(filename)
455 data = super(kwrepo, self).wread(filename)
456 return kwt.wread(filename, data)
456 return kwt.wread(filename, data)
457
457
458 def commit(self, files=None, text='', user=None, date=None,
458 def commit(self, files=None, text='', user=None, date=None,
459 match=util.always, force=False, force_editor=False,
459 match=util.always, force=False, force_editor=False,
460 p1=None, p2=None, extra={}, empty_ok=False):
460 p1=None, p2=None, extra={}, empty_ok=False):
461 wlock = lock = None
461 wlock = lock = None
462 _p1 = _p2 = None
462 _p1 = _p2 = None
463 try:
463 try:
464 wlock = self.wlock()
464 wlock = self.wlock()
465 lock = self.lock()
465 lock = self.lock()
466 # store and postpone commit hooks
466 # store and postpone commit hooks
467 commithooks = {}
467 commithooks = {}
468 for name, cmd in ui.configitems('hooks'):
468 for name, cmd in ui.configitems('hooks'):
469 if name.split('.', 1)[0] == 'commit':
469 if name.split('.', 1)[0] == 'commit':
470 commithooks[name] = cmd
470 commithooks[name] = cmd
471 ui.setconfig('hooks', name, None)
471 ui.setconfig('hooks', name, None)
472 if commithooks:
472 if commithooks:
473 # store parents for commit hook environment
473 # store parents for commit hook environment
474 if p1 is None:
474 if p1 is None:
475 _p1, _p2 = repo.dirstate.parents()
475 _p1, _p2 = repo.dirstate.parents()
476 else:
476 else:
477 _p1, _p2 = p1, p2 or nullid
477 _p1, _p2 = p1, p2 or nullid
478 _p1 = hex(_p1)
478 _p1 = hex(_p1)
479 if _p2 == nullid:
479 if _p2 == nullid:
480 _p2 = ''
480 _p2 = ''
481 else:
481 else:
482 _p2 = hex(_p2)
482 _p2 = hex(_p2)
483
483
484 n = super(kwrepo, self).commit(files, text, user, date, match,
484 n = super(kwrepo, self).commit(files, text, user, date, match,
485 force, force_editor, p1, p2,
485 force, force_editor, p1, p2,
486 extra, empty_ok)
486 extra, empty_ok)
487
487
488 # restore commit hooks
488 # restore commit hooks
489 for name, cmd in commithooks.iteritems():
489 for name, cmd in commithooks.iteritems():
490 ui.setconfig('hooks', name, cmd)
490 ui.setconfig('hooks', name, cmd)
491 if n is not None:
491 if n is not None:
492 kwt.overwrite(n, True, None)
492 kwt.overwrite(n, True, None)
493 repo.hook('commit', node=n, parent1=_p1, parent2=_p2)
493 repo.hook('commit', node=n, parent1=_p1, parent2=_p2)
494 return n
494 return n
495 finally:
495 finally:
496 del wlock, lock
496 del wlock, lock
497
497
498 # monkeypatches
498 # monkeypatches
499 def kwpatchfile_init(self, ui, fname, missing=False):
499 def kwpatchfile_init(self, ui, fname, missing=False):
500 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
500 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
501 rejects or conflicts due to expanded keywords in working dir.'''
501 rejects or conflicts due to expanded keywords in working dir.'''
502 patchfile_init(self, ui, fname, missing)
502 patchfile_init(self, ui, fname, missing)
503 # shrink keywords read from working dir
503 # shrink keywords read from working dir
504 self.lines = kwt.shrinklines(self.fname, self.lines)
504 self.lines = kwt.shrinklines(self.fname, self.lines)
505
505
506 def kw_diff(repo, node1=None, node2=None, files=None, match=util.always,
506 def kw_diff(repo, node1=None, node2=None, files=None, match=util.always,
507 fp=None, changes=None, opts=None):
507 fp=None, changes=None, opts=None):
508 '''Monkeypatch patch.diff to avoid expansion except when
508 '''Monkeypatch patch.diff to avoid expansion except when
509 comparing against working dir.'''
509 comparing against working dir.'''
510 if node2 is not None:
510 if node2 is not None:
511 kwt.matcher = util.never
511 kwt.matcher = util.never
512 elif node1 is not None and node1 != repo.changectx().node():
512 elif node1 is not None and node1 != repo.changectx().node():
513 kwt.restrict = True
513 kwt.restrict = True
514 patch_diff(repo, node1, node2, files, match, fp, changes, opts)
514 patch_diff(repo, node1, node2, files, match, fp, changes, opts)
515
515
516 def kwweb_changeset(web, req, tmpl):
516 def kwweb_changeset(web, req, tmpl):
517 '''Wraps webcommands.changeset turning off keyword expansion.'''
517 '''Wraps webcommands.changeset turning off keyword expansion.'''
518 kwt.matcher = util.never
518 kwt.matcher = util.never
519 return webcommands_changeset(web, req, tmpl)
519 return webcommands_changeset(web, req, tmpl)
520
520
521 def kwweb_filediff(web, req, tmpl):
521 def kwweb_filediff(web, req, tmpl):
522 '''Wraps webcommands.filediff turning off keyword expansion.'''
522 '''Wraps webcommands.filediff turning off keyword expansion.'''
523 kwt.matcher = util.never
523 kwt.matcher = util.never
524 return webcommands_filediff(web, req, tmpl)
524 return webcommands_filediff(web, req, tmpl)
525
525
526 repo.__class__ = kwrepo
526 repo.__class__ = kwrepo
527
527
528 patchfile_init = patch.patchfile.__init__
528 patchfile_init = patch.patchfile.__init__
529 patch_diff = patch.diff
529 patch_diff = patch.diff
530 webcommands_changeset = webcommands.changeset
530 webcommands_changeset = webcommands.changeset
531 webcommands_filediff = webcommands.filediff
531 webcommands_filediff = webcommands.filediff
532
532
533 patch.patchfile.__init__ = kwpatchfile_init
533 patch.patchfile.__init__ = kwpatchfile_init
534 patch.diff = kw_diff
534 patch.diff = kw_diff
535 webcommands.changeset = webcommands.rev = kwweb_changeset
535 webcommands.changeset = webcommands.rev = kwweb_changeset
536 webcommands.filediff = webcommands.diff = kwweb_filediff
536 webcommands.filediff = webcommands.diff = kwweb_filediff
537
537
538
538
539 cmdtable = {
539 cmdtable = {
540 'kwdemo':
540 'kwdemo':
541 (demo,
541 (demo,
542 [('d', 'default', None, _('show default keyword template maps')),
542 [('d', 'default', None, _('show default keyword template maps')),
543 ('f', 'rcfile', [], _('read maps from rcfile'))],
543 ('f', 'rcfile', [], _('read maps from rcfile'))],
544 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
544 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
545 'kwexpand': (expand, commands.walkopts,
545 'kwexpand': (expand, commands.walkopts,
546 _('hg kwexpand [OPTION]... [FILE]...')),
546 _('hg kwexpand [OPTION]... [FILE]...')),
547 'kwfiles':
547 'kwfiles':
548 (files,
548 (files,
549 [('a', 'all', None, _('show keyword status flags of all files')),
549 [('a', 'all', None, _('show keyword status flags of all files')),
550 ('i', 'ignore', None, _('show files excluded from expansion')),
550 ('i', 'ignore', None, _('show files excluded from expansion')),
551 ('u', 'untracked', None, _('additionally show untracked files')),
551 ('u', 'untracked', None, _('additionally show untracked files')),
552 ] + commands.walkopts,
552 ] + commands.walkopts,
553 _('hg kwfiles [OPTION]... [FILE]...')),
553 _('hg kwfiles [OPTION]... [FILE]...')),
554 'kwshrink': (shrink, commands.walkopts,
554 'kwshrink': (shrink, commands.walkopts,
555 _('hg kwshrink [OPTION]... [FILE]...')),
555 _('hg kwshrink [OPTION]... [FILE]...')),
556 }
556 }
@@ -1,2367 +1,2366 b''
1 # mq.py - patch queues for mercurial
1 # mq.py - patch queues for mercurial
2 #
2 #
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
3 # Copyright 2005, 2006 Chris Mason <mason@suse.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 '''patch management and development
8 '''patch management and development
9
9
10 This extension lets you work with a stack of patches in a Mercurial
10 This extension lets you work with a stack of patches in a Mercurial
11 repository. It manages two stacks of patches - all known patches, and
11 repository. It manages two stacks of patches - all known patches, and
12 applied patches (subset of known patches).
12 applied patches (subset of known patches).
13
13
14 Known patches are represented as patch files in the .hg/patches
14 Known patches are represented as patch files in the .hg/patches
15 directory. Applied patches are both patch files and changesets.
15 directory. Applied patches are both patch files and changesets.
16
16
17 Common tasks (use "hg help command" for more details):
17 Common tasks (use "hg help command" for more details):
18
18
19 prepare repository to work with patches qinit
19 prepare repository to work with patches qinit
20 create new patch qnew
20 create new patch qnew
21 import existing patch qimport
21 import existing patch qimport
22
22
23 print patch series qseries
23 print patch series qseries
24 print applied patches qapplied
24 print applied patches qapplied
25 print name of top applied patch qtop
25 print name of top applied patch qtop
26
26
27 add known patch to applied stack qpush
27 add known patch to applied stack qpush
28 remove patch from applied stack qpop
28 remove patch from applied stack qpop
29 refresh contents of top applied patch qrefresh
29 refresh contents of top applied patch qrefresh
30 '''
30 '''
31
31
32 from mercurial.i18n import _
32 from mercurial.i18n import _
33 from mercurial.node import bin, hex, short
33 from mercurial.node import bin, hex, short
34 from mercurial.repo import RepoError
34 from mercurial.repo import RepoError
35 from mercurial import commands, cmdutil, hg, patch, revlog, util
35 from mercurial import commands, cmdutil, hg, patch, revlog, util
36 from mercurial import repair
36 from mercurial import repair
37 import os, sys, re, errno
37 import os, sys, re, errno
38
38
39 commands.norepo += " qclone"
39 commands.norepo += " qclone"
40
40
41 # Patch names looks like unix-file names.
41 # Patch names looks like unix-file names.
42 # They must be joinable with queue directory and result in the patch path.
42 # They must be joinable with queue directory and result in the patch path.
43 normname = util.normpath
43 normname = util.normpath
44
44
45 class statusentry:
45 class statusentry:
46 def __init__(self, rev, name=None):
46 def __init__(self, rev, name=None):
47 if not name:
47 if not name:
48 fields = rev.split(':', 1)
48 fields = rev.split(':', 1)
49 if len(fields) == 2:
49 if len(fields) == 2:
50 self.rev, self.name = fields
50 self.rev, self.name = fields
51 else:
51 else:
52 self.rev, self.name = None, None
52 self.rev, self.name = None, None
53 else:
53 else:
54 self.rev, self.name = rev, name
54 self.rev, self.name = rev, name
55
55
56 def __str__(self):
56 def __str__(self):
57 return self.rev + ':' + self.name
57 return self.rev + ':' + self.name
58
58
59 class queue:
59 class queue:
60 def __init__(self, ui, path, patchdir=None):
60 def __init__(self, ui, path, patchdir=None):
61 self.basepath = path
61 self.basepath = path
62 self.path = patchdir or os.path.join(path, "patches")
62 self.path = patchdir or os.path.join(path, "patches")
63 self.opener = util.opener(self.path)
63 self.opener = util.opener(self.path)
64 self.ui = ui
64 self.ui = ui
65 self.applied = []
65 self.applied = []
66 self.full_series = []
66 self.full_series = []
67 self.applied_dirty = 0
67 self.applied_dirty = 0
68 self.series_dirty = 0
68 self.series_dirty = 0
69 self.series_path = "series"
69 self.series_path = "series"
70 self.status_path = "status"
70 self.status_path = "status"
71 self.guards_path = "guards"
71 self.guards_path = "guards"
72 self.active_guards = None
72 self.active_guards = None
73 self.guards_dirty = False
73 self.guards_dirty = False
74 self._diffopts = None
74 self._diffopts = None
75
75
76 if os.path.exists(self.join(self.series_path)):
76 if os.path.exists(self.join(self.series_path)):
77 self.full_series = self.opener(self.series_path).read().splitlines()
77 self.full_series = self.opener(self.series_path).read().splitlines()
78 self.parse_series()
78 self.parse_series()
79
79
80 if os.path.exists(self.join(self.status_path)):
80 if os.path.exists(self.join(self.status_path)):
81 lines = self.opener(self.status_path).read().splitlines()
81 lines = self.opener(self.status_path).read().splitlines()
82 self.applied = [statusentry(l) for l in lines]
82 self.applied = [statusentry(l) for l in lines]
83
83
84 def diffopts(self):
84 def diffopts(self):
85 if self._diffopts is None:
85 if self._diffopts is None:
86 self._diffopts = patch.diffopts(self.ui)
86 self._diffopts = patch.diffopts(self.ui)
87 return self._diffopts
87 return self._diffopts
88
88
89 def join(self, *p):
89 def join(self, *p):
90 return os.path.join(self.path, *p)
90 return os.path.join(self.path, *p)
91
91
92 def find_series(self, patch):
92 def find_series(self, patch):
93 pre = re.compile("(\s*)([^#]+)")
93 pre = re.compile("(\s*)([^#]+)")
94 index = 0
94 index = 0
95 for l in self.full_series:
95 for l in self.full_series:
96 m = pre.match(l)
96 m = pre.match(l)
97 if m:
97 if m:
98 s = m.group(2)
98 s = m.group(2)
99 s = s.rstrip()
99 s = s.rstrip()
100 if s == patch:
100 if s == patch:
101 return index
101 return index
102 index += 1
102 index += 1
103 return None
103 return None
104
104
105 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
105 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
106
106
107 def parse_series(self):
107 def parse_series(self):
108 self.series = []
108 self.series = []
109 self.series_guards = []
109 self.series_guards = []
110 for l in self.full_series:
110 for l in self.full_series:
111 h = l.find('#')
111 h = l.find('#')
112 if h == -1:
112 if h == -1:
113 patch = l
113 patch = l
114 comment = ''
114 comment = ''
115 elif h == 0:
115 elif h == 0:
116 continue
116 continue
117 else:
117 else:
118 patch = l[:h]
118 patch = l[:h]
119 comment = l[h:]
119 comment = l[h:]
120 patch = patch.strip()
120 patch = patch.strip()
121 if patch:
121 if patch:
122 if patch in self.series:
122 if patch in self.series:
123 raise util.Abort(_('%s appears more than once in %s') %
123 raise util.Abort(_('%s appears more than once in %s') %
124 (patch, self.join(self.series_path)))
124 (patch, self.join(self.series_path)))
125 self.series.append(patch)
125 self.series.append(patch)
126 self.series_guards.append(self.guard_re.findall(comment))
126 self.series_guards.append(self.guard_re.findall(comment))
127
127
128 def check_guard(self, guard):
128 def check_guard(self, guard):
129 bad_chars = '# \t\r\n\f'
129 bad_chars = '# \t\r\n\f'
130 first = guard[0]
130 first = guard[0]
131 for c in '-+':
131 for c in '-+':
132 if first == c:
132 if first == c:
133 return (_('guard %r starts with invalid character: %r') %
133 return (_('guard %r starts with invalid character: %r') %
134 (guard, c))
134 (guard, c))
135 for c in bad_chars:
135 for c in bad_chars:
136 if c in guard:
136 if c in guard:
137 return _('invalid character in guard %r: %r') % (guard, c)
137 return _('invalid character in guard %r: %r') % (guard, c)
138
138
139 def set_active(self, guards):
139 def set_active(self, guards):
140 for guard in guards:
140 for guard in guards:
141 bad = self.check_guard(guard)
141 bad = self.check_guard(guard)
142 if bad:
142 if bad:
143 raise util.Abort(bad)
143 raise util.Abort(bad)
144 guards = dict.fromkeys(guards).keys()
144 guards = dict.fromkeys(guards).keys()
145 guards.sort()
145 guards.sort()
146 self.ui.debug('active guards: %s\n' % ' '.join(guards))
146 self.ui.debug('active guards: %s\n' % ' '.join(guards))
147 self.active_guards = guards
147 self.active_guards = guards
148 self.guards_dirty = True
148 self.guards_dirty = True
149
149
150 def active(self):
150 def active(self):
151 if self.active_guards is None:
151 if self.active_guards is None:
152 self.active_guards = []
152 self.active_guards = []
153 try:
153 try:
154 guards = self.opener(self.guards_path).read().split()
154 guards = self.opener(self.guards_path).read().split()
155 except IOError, err:
155 except IOError, err:
156 if err.errno != errno.ENOENT: raise
156 if err.errno != errno.ENOENT: raise
157 guards = []
157 guards = []
158 for i, guard in enumerate(guards):
158 for i, guard in enumerate(guards):
159 bad = self.check_guard(guard)
159 bad = self.check_guard(guard)
160 if bad:
160 if bad:
161 self.ui.warn('%s:%d: %s\n' %
161 self.ui.warn('%s:%d: %s\n' %
162 (self.join(self.guards_path), i + 1, bad))
162 (self.join(self.guards_path), i + 1, bad))
163 else:
163 else:
164 self.active_guards.append(guard)
164 self.active_guards.append(guard)
165 return self.active_guards
165 return self.active_guards
166
166
167 def set_guards(self, idx, guards):
167 def set_guards(self, idx, guards):
168 for g in guards:
168 for g in guards:
169 if len(g) < 2:
169 if len(g) < 2:
170 raise util.Abort(_('guard %r too short') % g)
170 raise util.Abort(_('guard %r too short') % g)
171 if g[0] not in '-+':
171 if g[0] not in '-+':
172 raise util.Abort(_('guard %r starts with invalid char') % g)
172 raise util.Abort(_('guard %r starts with invalid char') % g)
173 bad = self.check_guard(g[1:])
173 bad = self.check_guard(g[1:])
174 if bad:
174 if bad:
175 raise util.Abort(bad)
175 raise util.Abort(bad)
176 drop = self.guard_re.sub('', self.full_series[idx])
176 drop = self.guard_re.sub('', self.full_series[idx])
177 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
177 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
178 self.parse_series()
178 self.parse_series()
179 self.series_dirty = True
179 self.series_dirty = True
180
180
181 def pushable(self, idx):
181 def pushable(self, idx):
182 if isinstance(idx, str):
182 if isinstance(idx, str):
183 idx = self.series.index(idx)
183 idx = self.series.index(idx)
184 patchguards = self.series_guards[idx]
184 patchguards = self.series_guards[idx]
185 if not patchguards:
185 if not patchguards:
186 return True, None
186 return True, None
187 default = False
187 default = False
188 guards = self.active()
188 guards = self.active()
189 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
189 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
190 if exactneg:
190 if exactneg:
191 return False, exactneg[0]
191 return False, exactneg[0]
192 pos = [g for g in patchguards if g[0] == '+']
192 pos = [g for g in patchguards if g[0] == '+']
193 exactpos = [g for g in pos if g[1:] in guards]
193 exactpos = [g for g in pos if g[1:] in guards]
194 if pos:
194 if pos:
195 if exactpos:
195 if exactpos:
196 return True, exactpos[0]
196 return True, exactpos[0]
197 return False, pos
197 return False, pos
198 return True, ''
198 return True, ''
199
199
200 def explain_pushable(self, idx, all_patches=False):
200 def explain_pushable(self, idx, all_patches=False):
201 write = all_patches and self.ui.write or self.ui.warn
201 write = all_patches and self.ui.write or self.ui.warn
202 if all_patches or self.ui.verbose:
202 if all_patches or self.ui.verbose:
203 if isinstance(idx, str):
203 if isinstance(idx, str):
204 idx = self.series.index(idx)
204 idx = self.series.index(idx)
205 pushable, why = self.pushable(idx)
205 pushable, why = self.pushable(idx)
206 if all_patches and pushable:
206 if all_patches and pushable:
207 if why is None:
207 if why is None:
208 write(_('allowing %s - no guards in effect\n') %
208 write(_('allowing %s - no guards in effect\n') %
209 self.series[idx])
209 self.series[idx])
210 else:
210 else:
211 if not why:
211 if not why:
212 write(_('allowing %s - no matching negative guards\n') %
212 write(_('allowing %s - no matching negative guards\n') %
213 self.series[idx])
213 self.series[idx])
214 else:
214 else:
215 write(_('allowing %s - guarded by %r\n') %
215 write(_('allowing %s - guarded by %r\n') %
216 (self.series[idx], why))
216 (self.series[idx], why))
217 if not pushable:
217 if not pushable:
218 if why:
218 if why:
219 write(_('skipping %s - guarded by %r\n') %
219 write(_('skipping %s - guarded by %r\n') %
220 (self.series[idx], why))
220 (self.series[idx], why))
221 else:
221 else:
222 write(_('skipping %s - no matching guards\n') %
222 write(_('skipping %s - no matching guards\n') %
223 self.series[idx])
223 self.series[idx])
224
224
225 def save_dirty(self):
225 def save_dirty(self):
226 def write_list(items, path):
226 def write_list(items, path):
227 fp = self.opener(path, 'w')
227 fp = self.opener(path, 'w')
228 for i in items:
228 for i in items:
229 fp.write("%s\n" % i)
229 fp.write("%s\n" % i)
230 fp.close()
230 fp.close()
231 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
231 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
232 if self.series_dirty: write_list(self.full_series, self.series_path)
232 if self.series_dirty: write_list(self.full_series, self.series_path)
233 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
233 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
234
234
235 def readheaders(self, patch):
235 def readheaders(self, patch):
236 def eatdiff(lines):
236 def eatdiff(lines):
237 while lines:
237 while lines:
238 l = lines[-1]
238 l = lines[-1]
239 if (l.startswith("diff -") or
239 if (l.startswith("diff -") or
240 l.startswith("Index:") or
240 l.startswith("Index:") or
241 l.startswith("===========")):
241 l.startswith("===========")):
242 del lines[-1]
242 del lines[-1]
243 else:
243 else:
244 break
244 break
245 def eatempty(lines):
245 def eatempty(lines):
246 while lines:
246 while lines:
247 l = lines[-1]
247 l = lines[-1]
248 if re.match('\s*$', l):
248 if re.match('\s*$', l):
249 del lines[-1]
249 del lines[-1]
250 else:
250 else:
251 break
251 break
252
252
253 pf = self.join(patch)
253 pf = self.join(patch)
254 message = []
254 message = []
255 comments = []
255 comments = []
256 user = None
256 user = None
257 date = None
257 date = None
258 format = None
258 format = None
259 subject = None
259 subject = None
260 diffstart = 0
260 diffstart = 0
261
261
262 for line in file(pf):
262 for line in file(pf):
263 line = line.rstrip()
263 line = line.rstrip()
264 if line.startswith('diff --git'):
264 if line.startswith('diff --git'):
265 diffstart = 2
265 diffstart = 2
266 break
266 break
267 if diffstart:
267 if diffstart:
268 if line.startswith('+++ '):
268 if line.startswith('+++ '):
269 diffstart = 2
269 diffstart = 2
270 break
270 break
271 if line.startswith("--- "):
271 if line.startswith("--- "):
272 diffstart = 1
272 diffstart = 1
273 continue
273 continue
274 elif format == "hgpatch":
274 elif format == "hgpatch":
275 # parse values when importing the result of an hg export
275 # parse values when importing the result of an hg export
276 if line.startswith("# User "):
276 if line.startswith("# User "):
277 user = line[7:]
277 user = line[7:]
278 elif line.startswith("# Date "):
278 elif line.startswith("# Date "):
279 date = line[7:]
279 date = line[7:]
280 elif not line.startswith("# ") and line:
280 elif not line.startswith("# ") and line:
281 message.append(line)
281 message.append(line)
282 format = None
282 format = None
283 elif line == '# HG changeset patch':
283 elif line == '# HG changeset patch':
284 format = "hgpatch"
284 format = "hgpatch"
285 elif (format != "tagdone" and (line.startswith("Subject: ") or
285 elif (format != "tagdone" and (line.startswith("Subject: ") or
286 line.startswith("subject: "))):
286 line.startswith("subject: "))):
287 subject = line[9:]
287 subject = line[9:]
288 format = "tag"
288 format = "tag"
289 elif (format != "tagdone" and (line.startswith("From: ") or
289 elif (format != "tagdone" and (line.startswith("From: ") or
290 line.startswith("from: "))):
290 line.startswith("from: "))):
291 user = line[6:]
291 user = line[6:]
292 format = "tag"
292 format = "tag"
293 elif format == "tag" and line == "":
293 elif format == "tag" and line == "":
294 # when looking for tags (subject: from: etc) they
294 # when looking for tags (subject: from: etc) they
295 # end once you find a blank line in the source
295 # end once you find a blank line in the source
296 format = "tagdone"
296 format = "tagdone"
297 elif message or line:
297 elif message or line:
298 message.append(line)
298 message.append(line)
299 comments.append(line)
299 comments.append(line)
300
300
301 eatdiff(message)
301 eatdiff(message)
302 eatdiff(comments)
302 eatdiff(comments)
303 eatempty(message)
303 eatempty(message)
304 eatempty(comments)
304 eatempty(comments)
305
305
306 # make sure message isn't empty
306 # make sure message isn't empty
307 if format and format.startswith("tag") and subject:
307 if format and format.startswith("tag") and subject:
308 message.insert(0, "")
308 message.insert(0, "")
309 message.insert(0, subject)
309 message.insert(0, subject)
310 return (message, comments, user, date, diffstart > 1)
310 return (message, comments, user, date, diffstart > 1)
311
311
312 def removeundo(self, repo):
312 def removeundo(self, repo):
313 undo = repo.sjoin('undo')
313 undo = repo.sjoin('undo')
314 if not os.path.exists(undo):
314 if not os.path.exists(undo):
315 return
315 return
316 try:
316 try:
317 os.unlink(undo)
317 os.unlink(undo)
318 except OSError, inst:
318 except OSError, inst:
319 self.ui.warn('error removing undo: %s\n' % str(inst))
319 self.ui.warn('error removing undo: %s\n' % str(inst))
320
320
321 def printdiff(self, repo, node1, node2=None, files=None,
321 def printdiff(self, repo, node1, node2=None, files=None,
322 fp=None, changes=None, opts={}):
322 fp=None, changes=None, opts={}):
323 fns, matchfn, anypats = cmdutil.matchpats(repo, files, opts)
323 m = cmdutil.match(repo, files, opts)
324
324 patch.diff(repo, node1, node2, m.files(), match=m,
325 patch.diff(repo, node1, node2, fns, match=matchfn,
326 fp=fp, changes=changes, opts=self.diffopts())
325 fp=fp, changes=changes, opts=self.diffopts())
327
326
328 def mergeone(self, repo, mergeq, head, patch, rev):
327 def mergeone(self, repo, mergeq, head, patch, rev):
329 # first try just applying the patch
328 # first try just applying the patch
330 (err, n) = self.apply(repo, [ patch ], update_status=False,
329 (err, n) = self.apply(repo, [ patch ], update_status=False,
331 strict=True, merge=rev)
330 strict=True, merge=rev)
332
331
333 if err == 0:
332 if err == 0:
334 return (err, n)
333 return (err, n)
335
334
336 if n is None:
335 if n is None:
337 raise util.Abort(_("apply failed for patch %s") % patch)
336 raise util.Abort(_("apply failed for patch %s") % patch)
338
337
339 self.ui.warn("patch didn't work out, merging %s\n" % patch)
338 self.ui.warn("patch didn't work out, merging %s\n" % patch)
340
339
341 # apply failed, strip away that rev and merge.
340 # apply failed, strip away that rev and merge.
342 hg.clean(repo, head)
341 hg.clean(repo, head)
343 self.strip(repo, n, update=False, backup='strip')
342 self.strip(repo, n, update=False, backup='strip')
344
343
345 ctx = repo.changectx(rev)
344 ctx = repo.changectx(rev)
346 ret = hg.merge(repo, rev)
345 ret = hg.merge(repo, rev)
347 if ret:
346 if ret:
348 raise util.Abort(_("update returned %d") % ret)
347 raise util.Abort(_("update returned %d") % ret)
349 n = repo.commit(None, ctx.description(), ctx.user(), force=1)
348 n = repo.commit(None, ctx.description(), ctx.user(), force=1)
350 if n == None:
349 if n == None:
351 raise util.Abort(_("repo commit failed"))
350 raise util.Abort(_("repo commit failed"))
352 try:
351 try:
353 message, comments, user, date, patchfound = mergeq.readheaders(patch)
352 message, comments, user, date, patchfound = mergeq.readheaders(patch)
354 except:
353 except:
355 raise util.Abort(_("unable to read %s") % patch)
354 raise util.Abort(_("unable to read %s") % patch)
356
355
357 patchf = self.opener(patch, "w")
356 patchf = self.opener(patch, "w")
358 if comments:
357 if comments:
359 comments = "\n".join(comments) + '\n\n'
358 comments = "\n".join(comments) + '\n\n'
360 patchf.write(comments)
359 patchf.write(comments)
361 self.printdiff(repo, head, n, fp=patchf)
360 self.printdiff(repo, head, n, fp=patchf)
362 patchf.close()
361 patchf.close()
363 self.removeundo(repo)
362 self.removeundo(repo)
364 return (0, n)
363 return (0, n)
365
364
366 def qparents(self, repo, rev=None):
365 def qparents(self, repo, rev=None):
367 if rev is None:
366 if rev is None:
368 (p1, p2) = repo.dirstate.parents()
367 (p1, p2) = repo.dirstate.parents()
369 if p2 == revlog.nullid:
368 if p2 == revlog.nullid:
370 return p1
369 return p1
371 if len(self.applied) == 0:
370 if len(self.applied) == 0:
372 return None
371 return None
373 return revlog.bin(self.applied[-1].rev)
372 return revlog.bin(self.applied[-1].rev)
374 pp = repo.changelog.parents(rev)
373 pp = repo.changelog.parents(rev)
375 if pp[1] != revlog.nullid:
374 if pp[1] != revlog.nullid:
376 arevs = [ x.rev for x in self.applied ]
375 arevs = [ x.rev for x in self.applied ]
377 p0 = revlog.hex(pp[0])
376 p0 = revlog.hex(pp[0])
378 p1 = revlog.hex(pp[1])
377 p1 = revlog.hex(pp[1])
379 if p0 in arevs:
378 if p0 in arevs:
380 return pp[0]
379 return pp[0]
381 if p1 in arevs:
380 if p1 in arevs:
382 return pp[1]
381 return pp[1]
383 return pp[0]
382 return pp[0]
384
383
385 def mergepatch(self, repo, mergeq, series):
384 def mergepatch(self, repo, mergeq, series):
386 if len(self.applied) == 0:
385 if len(self.applied) == 0:
387 # each of the patches merged in will have two parents. This
386 # each of the patches merged in will have two parents. This
388 # can confuse the qrefresh, qdiff, and strip code because it
387 # can confuse the qrefresh, qdiff, and strip code because it
389 # needs to know which parent is actually in the patch queue.
388 # needs to know which parent is actually in the patch queue.
390 # so, we insert a merge marker with only one parent. This way
389 # so, we insert a merge marker with only one parent. This way
391 # the first patch in the queue is never a merge patch
390 # the first patch in the queue is never a merge patch
392 #
391 #
393 pname = ".hg.patches.merge.marker"
392 pname = ".hg.patches.merge.marker"
394 n = repo.commit(None, '[mq]: merge marker', user=None, force=1)
393 n = repo.commit(None, '[mq]: merge marker', user=None, force=1)
395 self.removeundo(repo)
394 self.removeundo(repo)
396 self.applied.append(statusentry(revlog.hex(n), pname))
395 self.applied.append(statusentry(revlog.hex(n), pname))
397 self.applied_dirty = 1
396 self.applied_dirty = 1
398
397
399 head = self.qparents(repo)
398 head = self.qparents(repo)
400
399
401 for patch in series:
400 for patch in series:
402 patch = mergeq.lookup(patch, strict=True)
401 patch = mergeq.lookup(patch, strict=True)
403 if not patch:
402 if not patch:
404 self.ui.warn("patch %s does not exist\n" % patch)
403 self.ui.warn("patch %s does not exist\n" % patch)
405 return (1, None)
404 return (1, None)
406 pushable, reason = self.pushable(patch)
405 pushable, reason = self.pushable(patch)
407 if not pushable:
406 if not pushable:
408 self.explain_pushable(patch, all_patches=True)
407 self.explain_pushable(patch, all_patches=True)
409 continue
408 continue
410 info = mergeq.isapplied(patch)
409 info = mergeq.isapplied(patch)
411 if not info:
410 if not info:
412 self.ui.warn("patch %s is not applied\n" % patch)
411 self.ui.warn("patch %s is not applied\n" % patch)
413 return (1, None)
412 return (1, None)
414 rev = revlog.bin(info[1])
413 rev = revlog.bin(info[1])
415 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
414 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
416 if head:
415 if head:
417 self.applied.append(statusentry(revlog.hex(head), patch))
416 self.applied.append(statusentry(revlog.hex(head), patch))
418 self.applied_dirty = 1
417 self.applied_dirty = 1
419 if err:
418 if err:
420 return (err, head)
419 return (err, head)
421 self.save_dirty()
420 self.save_dirty()
422 return (0, head)
421 return (0, head)
423
422
424 def patch(self, repo, patchfile):
423 def patch(self, repo, patchfile):
425 '''Apply patchfile to the working directory.
424 '''Apply patchfile to the working directory.
426 patchfile: file name of patch'''
425 patchfile: file name of patch'''
427 files = {}
426 files = {}
428 try:
427 try:
429 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
428 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
430 files=files)
429 files=files)
431 except Exception, inst:
430 except Exception, inst:
432 self.ui.note(str(inst) + '\n')
431 self.ui.note(str(inst) + '\n')
433 if not self.ui.verbose:
432 if not self.ui.verbose:
434 self.ui.warn("patch failed, unable to continue (try -v)\n")
433 self.ui.warn("patch failed, unable to continue (try -v)\n")
435 return (False, files, False)
434 return (False, files, False)
436
435
437 return (True, files, fuzz)
436 return (True, files, fuzz)
438
437
439 def apply(self, repo, series, list=False, update_status=True,
438 def apply(self, repo, series, list=False, update_status=True,
440 strict=False, patchdir=None, merge=None, all_files={}):
439 strict=False, patchdir=None, merge=None, all_files={}):
441 wlock = lock = tr = None
440 wlock = lock = tr = None
442 try:
441 try:
443 wlock = repo.wlock()
442 wlock = repo.wlock()
444 lock = repo.lock()
443 lock = repo.lock()
445 tr = repo.transaction()
444 tr = repo.transaction()
446 try:
445 try:
447 ret = self._apply(repo, series, list, update_status,
446 ret = self._apply(repo, series, list, update_status,
448 strict, patchdir, merge, all_files=all_files)
447 strict, patchdir, merge, all_files=all_files)
449 tr.close()
448 tr.close()
450 self.save_dirty()
449 self.save_dirty()
451 return ret
450 return ret
452 except:
451 except:
453 try:
452 try:
454 tr.abort()
453 tr.abort()
455 finally:
454 finally:
456 repo.invalidate()
455 repo.invalidate()
457 repo.dirstate.invalidate()
456 repo.dirstate.invalidate()
458 raise
457 raise
459 finally:
458 finally:
460 del tr, lock, wlock
459 del tr, lock, wlock
461 self.removeundo(repo)
460 self.removeundo(repo)
462
461
463 def _apply(self, repo, series, list=False, update_status=True,
462 def _apply(self, repo, series, list=False, update_status=True,
464 strict=False, patchdir=None, merge=None, all_files={}):
463 strict=False, patchdir=None, merge=None, all_files={}):
465 # TODO unify with commands.py
464 # TODO unify with commands.py
466 if not patchdir:
465 if not patchdir:
467 patchdir = self.path
466 patchdir = self.path
468 err = 0
467 err = 0
469 n = None
468 n = None
470 for patchname in series:
469 for patchname in series:
471 pushable, reason = self.pushable(patchname)
470 pushable, reason = self.pushable(patchname)
472 if not pushable:
471 if not pushable:
473 self.explain_pushable(patchname, all_patches=True)
472 self.explain_pushable(patchname, all_patches=True)
474 continue
473 continue
475 self.ui.warn("applying %s\n" % patchname)
474 self.ui.warn("applying %s\n" % patchname)
476 pf = os.path.join(patchdir, patchname)
475 pf = os.path.join(patchdir, patchname)
477
476
478 try:
477 try:
479 message, comments, user, date, patchfound = self.readheaders(patchname)
478 message, comments, user, date, patchfound = self.readheaders(patchname)
480 except:
479 except:
481 self.ui.warn("Unable to read %s\n" % patchname)
480 self.ui.warn("Unable to read %s\n" % patchname)
482 err = 1
481 err = 1
483 break
482 break
484
483
485 if not message:
484 if not message:
486 message = "imported patch %s\n" % patchname
485 message = "imported patch %s\n" % patchname
487 else:
486 else:
488 if list:
487 if list:
489 message.append("\nimported patch %s" % patchname)
488 message.append("\nimported patch %s" % patchname)
490 message = '\n'.join(message)
489 message = '\n'.join(message)
491
490
492 (patcherr, files, fuzz) = self.patch(repo, pf)
491 (patcherr, files, fuzz) = self.patch(repo, pf)
493 all_files.update(files)
492 all_files.update(files)
494 patcherr = not patcherr
493 patcherr = not patcherr
495
494
496 if merge and files:
495 if merge and files:
497 # Mark as removed/merged and update dirstate parent info
496 # Mark as removed/merged and update dirstate parent info
498 removed = []
497 removed = []
499 merged = []
498 merged = []
500 for f in files:
499 for f in files:
501 if os.path.exists(repo.wjoin(f)):
500 if os.path.exists(repo.wjoin(f)):
502 merged.append(f)
501 merged.append(f)
503 else:
502 else:
504 removed.append(f)
503 removed.append(f)
505 for f in removed:
504 for f in removed:
506 repo.dirstate.remove(f)
505 repo.dirstate.remove(f)
507 for f in merged:
506 for f in merged:
508 repo.dirstate.merge(f)
507 repo.dirstate.merge(f)
509 p1, p2 = repo.dirstate.parents()
508 p1, p2 = repo.dirstate.parents()
510 repo.dirstate.setparents(p1, merge)
509 repo.dirstate.setparents(p1, merge)
511 files = patch.updatedir(self.ui, repo, files)
510 files = patch.updatedir(self.ui, repo, files)
512 n = repo.commit(files, message, user, date, match=util.never,
511 n = repo.commit(files, message, user, date, match=util.never,
513 force=True)
512 force=True)
514
513
515 if n == None:
514 if n == None:
516 raise util.Abort(_("repo commit failed"))
515 raise util.Abort(_("repo commit failed"))
517
516
518 if update_status:
517 if update_status:
519 self.applied.append(statusentry(revlog.hex(n), patchname))
518 self.applied.append(statusentry(revlog.hex(n), patchname))
520
519
521 if patcherr:
520 if patcherr:
522 if not patchfound:
521 if not patchfound:
523 self.ui.warn("patch %s is empty\n" % patchname)
522 self.ui.warn("patch %s is empty\n" % patchname)
524 err = 0
523 err = 0
525 else:
524 else:
526 self.ui.warn("patch failed, rejects left in working dir\n")
525 self.ui.warn("patch failed, rejects left in working dir\n")
527 err = 1
526 err = 1
528 break
527 break
529
528
530 if fuzz and strict:
529 if fuzz and strict:
531 self.ui.warn("fuzz found when applying patch, stopping\n")
530 self.ui.warn("fuzz found when applying patch, stopping\n")
532 err = 1
531 err = 1
533 break
532 break
534 return (err, n)
533 return (err, n)
535
534
536 def delete(self, repo, patches, opts):
535 def delete(self, repo, patches, opts):
537 if not patches and not opts.get('rev'):
536 if not patches and not opts.get('rev'):
538 raise util.Abort(_('qdelete requires at least one revision or '
537 raise util.Abort(_('qdelete requires at least one revision or '
539 'patch name'))
538 'patch name'))
540
539
541 realpatches = []
540 realpatches = []
542 for patch in patches:
541 for patch in patches:
543 patch = self.lookup(patch, strict=True)
542 patch = self.lookup(patch, strict=True)
544 info = self.isapplied(patch)
543 info = self.isapplied(patch)
545 if info:
544 if info:
546 raise util.Abort(_("cannot delete applied patch %s") % patch)
545 raise util.Abort(_("cannot delete applied patch %s") % patch)
547 if patch not in self.series:
546 if patch not in self.series:
548 raise util.Abort(_("patch %s not in series file") % patch)
547 raise util.Abort(_("patch %s not in series file") % patch)
549 realpatches.append(patch)
548 realpatches.append(patch)
550
549
551 appliedbase = 0
550 appliedbase = 0
552 if opts.get('rev'):
551 if opts.get('rev'):
553 if not self.applied:
552 if not self.applied:
554 raise util.Abort(_('no patches applied'))
553 raise util.Abort(_('no patches applied'))
555 revs = cmdutil.revrange(repo, opts['rev'])
554 revs = cmdutil.revrange(repo, opts['rev'])
556 if len(revs) > 1 and revs[0] > revs[1]:
555 if len(revs) > 1 and revs[0] > revs[1]:
557 revs.reverse()
556 revs.reverse()
558 for rev in revs:
557 for rev in revs:
559 if appliedbase >= len(self.applied):
558 if appliedbase >= len(self.applied):
560 raise util.Abort(_("revision %d is not managed") % rev)
559 raise util.Abort(_("revision %d is not managed") % rev)
561
560
562 base = revlog.bin(self.applied[appliedbase].rev)
561 base = revlog.bin(self.applied[appliedbase].rev)
563 node = repo.changelog.node(rev)
562 node = repo.changelog.node(rev)
564 if node != base:
563 if node != base:
565 raise util.Abort(_("cannot delete revision %d above "
564 raise util.Abort(_("cannot delete revision %d above "
566 "applied patches") % rev)
565 "applied patches") % rev)
567 realpatches.append(self.applied[appliedbase].name)
566 realpatches.append(self.applied[appliedbase].name)
568 appliedbase += 1
567 appliedbase += 1
569
568
570 if not opts.get('keep'):
569 if not opts.get('keep'):
571 r = self.qrepo()
570 r = self.qrepo()
572 if r:
571 if r:
573 r.remove(realpatches, True)
572 r.remove(realpatches, True)
574 else:
573 else:
575 for p in realpatches:
574 for p in realpatches:
576 os.unlink(self.join(p))
575 os.unlink(self.join(p))
577
576
578 if appliedbase:
577 if appliedbase:
579 del self.applied[:appliedbase]
578 del self.applied[:appliedbase]
580 self.applied_dirty = 1
579 self.applied_dirty = 1
581 indices = [self.find_series(p) for p in realpatches]
580 indices = [self.find_series(p) for p in realpatches]
582 indices.sort()
581 indices.sort()
583 for i in indices[-1::-1]:
582 for i in indices[-1::-1]:
584 del self.full_series[i]
583 del self.full_series[i]
585 self.parse_series()
584 self.parse_series()
586 self.series_dirty = 1
585 self.series_dirty = 1
587
586
588 def check_toppatch(self, repo):
587 def check_toppatch(self, repo):
589 if len(self.applied) > 0:
588 if len(self.applied) > 0:
590 top = revlog.bin(self.applied[-1].rev)
589 top = revlog.bin(self.applied[-1].rev)
591 pp = repo.dirstate.parents()
590 pp = repo.dirstate.parents()
592 if top not in pp:
591 if top not in pp:
593 raise util.Abort(_("working directory revision is not qtip"))
592 raise util.Abort(_("working directory revision is not qtip"))
594 return top
593 return top
595 return None
594 return None
596 def check_localchanges(self, repo, force=False, refresh=True):
595 def check_localchanges(self, repo, force=False, refresh=True):
597 m, a, r, d = repo.status()[:4]
596 m, a, r, d = repo.status()[:4]
598 if m or a or r or d:
597 if m or a or r or d:
599 if not force:
598 if not force:
600 if refresh:
599 if refresh:
601 raise util.Abort(_("local changes found, refresh first"))
600 raise util.Abort(_("local changes found, refresh first"))
602 else:
601 else:
603 raise util.Abort(_("local changes found"))
602 raise util.Abort(_("local changes found"))
604 return m, a, r, d
603 return m, a, r, d
605
604
606 _reserved = ('series', 'status', 'guards')
605 _reserved = ('series', 'status', 'guards')
607 def check_reserved_name(self, name):
606 def check_reserved_name(self, name):
608 if (name in self._reserved or name.startswith('.hg')
607 if (name in self._reserved or name.startswith('.hg')
609 or name.startswith('.mq')):
608 or name.startswith('.mq')):
610 raise util.Abort(_('"%s" cannot be used as the name of a patch')
609 raise util.Abort(_('"%s" cannot be used as the name of a patch')
611 % name)
610 % name)
612
611
613 def new(self, repo, patch, *pats, **opts):
612 def new(self, repo, patch, *pats, **opts):
614 msg = opts.get('msg')
613 msg = opts.get('msg')
615 force = opts.get('force')
614 force = opts.get('force')
616 user = opts.get('user')
615 user = opts.get('user')
617 date = opts.get('date')
616 date = opts.get('date')
618 if date:
617 if date:
619 date = util.parsedate(date)
618 date = util.parsedate(date)
620 self.check_reserved_name(patch)
619 self.check_reserved_name(patch)
621 if os.path.exists(self.join(patch)):
620 if os.path.exists(self.join(patch)):
622 raise util.Abort(_('patch "%s" already exists') % patch)
621 raise util.Abort(_('patch "%s" already exists') % patch)
623 if opts.get('include') or opts.get('exclude') or pats:
622 if opts.get('include') or opts.get('exclude') or pats:
624 fns, match, anypats = cmdutil.matchpats(repo, pats, opts)
623 match = cmdutil.match(repo, pats, opts)
625 m, a, r, d = repo.status(files=fns, match=match)[:4]
624 m, a, r, d = repo.status(files=match.files(), match=match)[:4]
626 else:
625 else:
627 m, a, r, d = self.check_localchanges(repo, force)
626 m, a, r, d = self.check_localchanges(repo, force)
628 fns, match, anypats = cmdutil.matchpats(repo, m + a + r)
627 match = cmdutil.match(repo, m + a + r)
629 commitfiles = m + a + r
628 commitfiles = m + a + r
630 self.check_toppatch(repo)
629 self.check_toppatch(repo)
631 wlock = repo.wlock()
630 wlock = repo.wlock()
632 try:
631 try:
633 insert = self.full_series_end()
632 insert = self.full_series_end()
634 commitmsg = msg and msg or ("[mq]: %s" % patch)
633 commitmsg = msg and msg or ("[mq]: %s" % patch)
635 n = repo.commit(commitfiles, commitmsg, user, date, match=match, force=True)
634 n = repo.commit(commitfiles, commitmsg, user, date, match=match, force=True)
636 if n == None:
635 if n == None:
637 raise util.Abort(_("repo commit failed"))
636 raise util.Abort(_("repo commit failed"))
638 self.full_series[insert:insert] = [patch]
637 self.full_series[insert:insert] = [patch]
639 self.applied.append(statusentry(revlog.hex(n), patch))
638 self.applied.append(statusentry(revlog.hex(n), patch))
640 self.parse_series()
639 self.parse_series()
641 self.series_dirty = 1
640 self.series_dirty = 1
642 self.applied_dirty = 1
641 self.applied_dirty = 1
643 p = self.opener(patch, "w")
642 p = self.opener(patch, "w")
644 if date:
643 if date:
645 p.write("# HG changeset patch\n")
644 p.write("# HG changeset patch\n")
646 if user:
645 if user:
647 p.write("# User " + user + "\n")
646 p.write("# User " + user + "\n")
648 p.write("# Date %d %d\n" % date)
647 p.write("# Date %d %d\n" % date)
649 p.write("\n")
648 p.write("\n")
650 elif user:
649 elif user:
651 p.write("From: " + user + "\n")
650 p.write("From: " + user + "\n")
652 p.write("\n")
651 p.write("\n")
653 if msg:
652 if msg:
654 msg = msg + "\n"
653 msg = msg + "\n"
655 p.write(msg)
654 p.write(msg)
656 p.close()
655 p.close()
657 wlock = None
656 wlock = None
658 r = self.qrepo()
657 r = self.qrepo()
659 if r: r.add([patch])
658 if r: r.add([patch])
660 if commitfiles:
659 if commitfiles:
661 self.refresh(repo, short=True, git=opts.get('git'))
660 self.refresh(repo, short=True, git=opts.get('git'))
662 self.removeundo(repo)
661 self.removeundo(repo)
663 finally:
662 finally:
664 del wlock
663 del wlock
665
664
666 def strip(self, repo, rev, update=True, backup="all", force=None):
665 def strip(self, repo, rev, update=True, backup="all", force=None):
667 wlock = lock = None
666 wlock = lock = None
668 try:
667 try:
669 wlock = repo.wlock()
668 wlock = repo.wlock()
670 lock = repo.lock()
669 lock = repo.lock()
671
670
672 if update:
671 if update:
673 self.check_localchanges(repo, force=force, refresh=False)
672 self.check_localchanges(repo, force=force, refresh=False)
674 urev = self.qparents(repo, rev)
673 urev = self.qparents(repo, rev)
675 hg.clean(repo, urev)
674 hg.clean(repo, urev)
676 repo.dirstate.write()
675 repo.dirstate.write()
677
676
678 self.removeundo(repo)
677 self.removeundo(repo)
679 repair.strip(self.ui, repo, rev, backup)
678 repair.strip(self.ui, repo, rev, backup)
680 # strip may have unbundled a set of backed up revisions after
679 # strip may have unbundled a set of backed up revisions after
681 # the actual strip
680 # the actual strip
682 self.removeundo(repo)
681 self.removeundo(repo)
683 finally:
682 finally:
684 del lock, wlock
683 del lock, wlock
685
684
686 def isapplied(self, patch):
685 def isapplied(self, patch):
687 """returns (index, rev, patch)"""
686 """returns (index, rev, patch)"""
688 for i in xrange(len(self.applied)):
687 for i in xrange(len(self.applied)):
689 a = self.applied[i]
688 a = self.applied[i]
690 if a.name == patch:
689 if a.name == patch:
691 return (i, a.rev, a.name)
690 return (i, a.rev, a.name)
692 return None
691 return None
693
692
694 # if the exact patch name does not exist, we try a few
693 # if the exact patch name does not exist, we try a few
695 # variations. If strict is passed, we try only #1
694 # variations. If strict is passed, we try only #1
696 #
695 #
697 # 1) a number to indicate an offset in the series file
696 # 1) a number to indicate an offset in the series file
698 # 2) a unique substring of the patch name was given
697 # 2) a unique substring of the patch name was given
699 # 3) patchname[-+]num to indicate an offset in the series file
698 # 3) patchname[-+]num to indicate an offset in the series file
700 def lookup(self, patch, strict=False):
699 def lookup(self, patch, strict=False):
701 patch = patch and str(patch)
700 patch = patch and str(patch)
702
701
703 def partial_name(s):
702 def partial_name(s):
704 if s in self.series:
703 if s in self.series:
705 return s
704 return s
706 matches = [x for x in self.series if s in x]
705 matches = [x for x in self.series if s in x]
707 if len(matches) > 1:
706 if len(matches) > 1:
708 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
707 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
709 for m in matches:
708 for m in matches:
710 self.ui.warn(' %s\n' % m)
709 self.ui.warn(' %s\n' % m)
711 return None
710 return None
712 if matches:
711 if matches:
713 return matches[0]
712 return matches[0]
714 if len(self.series) > 0 and len(self.applied) > 0:
713 if len(self.series) > 0 and len(self.applied) > 0:
715 if s == 'qtip':
714 if s == 'qtip':
716 return self.series[self.series_end(True)-1]
715 return self.series[self.series_end(True)-1]
717 if s == 'qbase':
716 if s == 'qbase':
718 return self.series[0]
717 return self.series[0]
719 return None
718 return None
720 if patch == None:
719 if patch == None:
721 return None
720 return None
722
721
723 # we don't want to return a partial match until we make
722 # we don't want to return a partial match until we make
724 # sure the file name passed in does not exist (checked below)
723 # sure the file name passed in does not exist (checked below)
725 res = partial_name(patch)
724 res = partial_name(patch)
726 if res and res == patch:
725 if res and res == patch:
727 return res
726 return res
728
727
729 if not os.path.isfile(self.join(patch)):
728 if not os.path.isfile(self.join(patch)):
730 try:
729 try:
731 sno = int(patch)
730 sno = int(patch)
732 except(ValueError, OverflowError):
731 except(ValueError, OverflowError):
733 pass
732 pass
734 else:
733 else:
735 if sno < len(self.series):
734 if sno < len(self.series):
736 return self.series[sno]
735 return self.series[sno]
737 if not strict:
736 if not strict:
738 # return any partial match made above
737 # return any partial match made above
739 if res:
738 if res:
740 return res
739 return res
741 minus = patch.rfind('-')
740 minus = patch.rfind('-')
742 if minus >= 0:
741 if minus >= 0:
743 res = partial_name(patch[:minus])
742 res = partial_name(patch[:minus])
744 if res:
743 if res:
745 i = self.series.index(res)
744 i = self.series.index(res)
746 try:
745 try:
747 off = int(patch[minus+1:] or 1)
746 off = int(patch[minus+1:] or 1)
748 except(ValueError, OverflowError):
747 except(ValueError, OverflowError):
749 pass
748 pass
750 else:
749 else:
751 if i - off >= 0:
750 if i - off >= 0:
752 return self.series[i - off]
751 return self.series[i - off]
753 plus = patch.rfind('+')
752 plus = patch.rfind('+')
754 if plus >= 0:
753 if plus >= 0:
755 res = partial_name(patch[:plus])
754 res = partial_name(patch[:plus])
756 if res:
755 if res:
757 i = self.series.index(res)
756 i = self.series.index(res)
758 try:
757 try:
759 off = int(patch[plus+1:] or 1)
758 off = int(patch[plus+1:] or 1)
760 except(ValueError, OverflowError):
759 except(ValueError, OverflowError):
761 pass
760 pass
762 else:
761 else:
763 if i + off < len(self.series):
762 if i + off < len(self.series):
764 return self.series[i + off]
763 return self.series[i + off]
765 raise util.Abort(_("patch %s not in series") % patch)
764 raise util.Abort(_("patch %s not in series") % patch)
766
765
767 def push(self, repo, patch=None, force=False, list=False,
766 def push(self, repo, patch=None, force=False, list=False,
768 mergeq=None):
767 mergeq=None):
769 wlock = repo.wlock()
768 wlock = repo.wlock()
770 if repo.dirstate.parents()[0] != repo.changelog.tip():
769 if repo.dirstate.parents()[0] != repo.changelog.tip():
771 self.ui.status(_("(working directory not at tip)\n"))
770 self.ui.status(_("(working directory not at tip)\n"))
772
771
773 try:
772 try:
774 patch = self.lookup(patch)
773 patch = self.lookup(patch)
775 # Suppose our series file is: A B C and the current 'top'
774 # Suppose our series file is: A B C and the current 'top'
776 # patch is B. qpush C should be performed (moving forward)
775 # patch is B. qpush C should be performed (moving forward)
777 # qpush B is a NOP (no change) qpush A is an error (can't
776 # qpush B is a NOP (no change) qpush A is an error (can't
778 # go backwards with qpush)
777 # go backwards with qpush)
779 if patch:
778 if patch:
780 info = self.isapplied(patch)
779 info = self.isapplied(patch)
781 if info:
780 if info:
782 if info[0] < len(self.applied) - 1:
781 if info[0] < len(self.applied) - 1:
783 raise util.Abort(
782 raise util.Abort(
784 _("cannot push to a previous patch: %s") % patch)
783 _("cannot push to a previous patch: %s") % patch)
785 if info[0] < len(self.series) - 1:
784 if info[0] < len(self.series) - 1:
786 self.ui.warn(
785 self.ui.warn(
787 _('qpush: %s is already at the top\n') % patch)
786 _('qpush: %s is already at the top\n') % patch)
788 else:
787 else:
789 self.ui.warn(_('all patches are currently applied\n'))
788 self.ui.warn(_('all patches are currently applied\n'))
790 return
789 return
791
790
792 # Following the above example, starting at 'top' of B:
791 # Following the above example, starting at 'top' of B:
793 # qpush should be performed (pushes C), but a subsequent
792 # qpush should be performed (pushes C), but a subsequent
794 # qpush without an argument is an error (nothing to
793 # qpush without an argument is an error (nothing to
795 # apply). This allows a loop of "...while hg qpush..." to
794 # apply). This allows a loop of "...while hg qpush..." to
796 # work as it detects an error when done
795 # work as it detects an error when done
797 if self.series_end() == len(self.series):
796 if self.series_end() == len(self.series):
798 self.ui.warn(_('patch series already fully applied\n'))
797 self.ui.warn(_('patch series already fully applied\n'))
799 return 1
798 return 1
800 if not force:
799 if not force:
801 self.check_localchanges(repo)
800 self.check_localchanges(repo)
802
801
803 self.applied_dirty = 1;
802 self.applied_dirty = 1;
804 start = self.series_end()
803 start = self.series_end()
805 if start > 0:
804 if start > 0:
806 self.check_toppatch(repo)
805 self.check_toppatch(repo)
807 if not patch:
806 if not patch:
808 patch = self.series[start]
807 patch = self.series[start]
809 end = start + 1
808 end = start + 1
810 else:
809 else:
811 end = self.series.index(patch, start) + 1
810 end = self.series.index(patch, start) + 1
812 s = self.series[start:end]
811 s = self.series[start:end]
813 all_files = {}
812 all_files = {}
814 try:
813 try:
815 if mergeq:
814 if mergeq:
816 ret = self.mergepatch(repo, mergeq, s)
815 ret = self.mergepatch(repo, mergeq, s)
817 else:
816 else:
818 ret = self.apply(repo, s, list, all_files=all_files)
817 ret = self.apply(repo, s, list, all_files=all_files)
819 except:
818 except:
820 self.ui.warn(_('cleaning up working directory...'))
819 self.ui.warn(_('cleaning up working directory...'))
821 node = repo.dirstate.parents()[0]
820 node = repo.dirstate.parents()[0]
822 hg.revert(repo, node, None)
821 hg.revert(repo, node, None)
823 unknown = repo.status()[4]
822 unknown = repo.status()[4]
824 # only remove unknown files that we know we touched or
823 # only remove unknown files that we know we touched or
825 # created while patching
824 # created while patching
826 for f in unknown:
825 for f in unknown:
827 if f in all_files:
826 if f in all_files:
828 util.unlink(repo.wjoin(f))
827 util.unlink(repo.wjoin(f))
829 self.ui.warn(_('done\n'))
828 self.ui.warn(_('done\n'))
830 raise
829 raise
831 top = self.applied[-1].name
830 top = self.applied[-1].name
832 if ret[0]:
831 if ret[0]:
833 self.ui.write(
832 self.ui.write(
834 "Errors during apply, please fix and refresh %s\n" % top)
833 "Errors during apply, please fix and refresh %s\n" % top)
835 else:
834 else:
836 self.ui.write("Now at: %s\n" % top)
835 self.ui.write("Now at: %s\n" % top)
837 return ret[0]
836 return ret[0]
838 finally:
837 finally:
839 del wlock
838 del wlock
840
839
841 def pop(self, repo, patch=None, force=False, update=True, all=False):
840 def pop(self, repo, patch=None, force=False, update=True, all=False):
842 def getfile(f, rev, flags):
841 def getfile(f, rev, flags):
843 t = repo.file(f).read(rev)
842 t = repo.file(f).read(rev)
844 repo.wwrite(f, t, flags)
843 repo.wwrite(f, t, flags)
845
844
846 wlock = repo.wlock()
845 wlock = repo.wlock()
847 try:
846 try:
848 if patch:
847 if patch:
849 # index, rev, patch
848 # index, rev, patch
850 info = self.isapplied(patch)
849 info = self.isapplied(patch)
851 if not info:
850 if not info:
852 patch = self.lookup(patch)
851 patch = self.lookup(patch)
853 info = self.isapplied(patch)
852 info = self.isapplied(patch)
854 if not info:
853 if not info:
855 raise util.Abort(_("patch %s is not applied") % patch)
854 raise util.Abort(_("patch %s is not applied") % patch)
856
855
857 if len(self.applied) == 0:
856 if len(self.applied) == 0:
858 # Allow qpop -a to work repeatedly,
857 # Allow qpop -a to work repeatedly,
859 # but not qpop without an argument
858 # but not qpop without an argument
860 self.ui.warn(_("no patches applied\n"))
859 self.ui.warn(_("no patches applied\n"))
861 return not all
860 return not all
862
861
863 if not update:
862 if not update:
864 parents = repo.dirstate.parents()
863 parents = repo.dirstate.parents()
865 rr = [ revlog.bin(x.rev) for x in self.applied ]
864 rr = [ revlog.bin(x.rev) for x in self.applied ]
866 for p in parents:
865 for p in parents:
867 if p in rr:
866 if p in rr:
868 self.ui.warn("qpop: forcing dirstate update\n")
867 self.ui.warn("qpop: forcing dirstate update\n")
869 update = True
868 update = True
870
869
871 if not force and update:
870 if not force and update:
872 self.check_localchanges(repo)
871 self.check_localchanges(repo)
873
872
874 self.applied_dirty = 1;
873 self.applied_dirty = 1;
875 end = len(self.applied)
874 end = len(self.applied)
876 if not patch:
875 if not patch:
877 if all:
876 if all:
878 popi = 0
877 popi = 0
879 else:
878 else:
880 popi = len(self.applied) - 1
879 popi = len(self.applied) - 1
881 else:
880 else:
882 popi = info[0] + 1
881 popi = info[0] + 1
883 if popi >= end:
882 if popi >= end:
884 self.ui.warn("qpop: %s is already at the top\n" % patch)
883 self.ui.warn("qpop: %s is already at the top\n" % patch)
885 return
884 return
886 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
885 info = [ popi ] + [self.applied[popi].rev, self.applied[popi].name]
887
886
888 start = info[0]
887 start = info[0]
889 rev = revlog.bin(info[1])
888 rev = revlog.bin(info[1])
890
889
891 if update:
890 if update:
892 top = self.check_toppatch(repo)
891 top = self.check_toppatch(repo)
893
892
894 if repo.changelog.heads(rev) != [revlog.bin(self.applied[-1].rev)]:
893 if repo.changelog.heads(rev) != [revlog.bin(self.applied[-1].rev)]:
895 raise util.Abort("popping would remove a revision not "
894 raise util.Abort("popping would remove a revision not "
896 "managed by this patch queue")
895 "managed by this patch queue")
897
896
898 # we know there are no local changes, so we can make a simplified
897 # we know there are no local changes, so we can make a simplified
899 # form of hg.update.
898 # form of hg.update.
900 if update:
899 if update:
901 qp = self.qparents(repo, rev)
900 qp = self.qparents(repo, rev)
902 changes = repo.changelog.read(qp)
901 changes = repo.changelog.read(qp)
903 mmap = repo.manifest.read(changes[0])
902 mmap = repo.manifest.read(changes[0])
904 m, a, r, d, u = repo.status(qp, top)[:5]
903 m, a, r, d, u = repo.status(qp, top)[:5]
905 if d:
904 if d:
906 raise util.Abort("deletions found between repo revs")
905 raise util.Abort("deletions found between repo revs")
907 for f in m:
906 for f in m:
908 getfile(f, mmap[f], mmap.flags(f))
907 getfile(f, mmap[f], mmap.flags(f))
909 for f in r:
908 for f in r:
910 getfile(f, mmap[f], mmap.flags(f))
909 getfile(f, mmap[f], mmap.flags(f))
911 for f in m + r:
910 for f in m + r:
912 repo.dirstate.normal(f)
911 repo.dirstate.normal(f)
913 for f in a:
912 for f in a:
914 try:
913 try:
915 os.unlink(repo.wjoin(f))
914 os.unlink(repo.wjoin(f))
916 except OSError, e:
915 except OSError, e:
917 if e.errno != errno.ENOENT:
916 if e.errno != errno.ENOENT:
918 raise
917 raise
919 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
918 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
920 except: pass
919 except: pass
921 repo.dirstate.forget(f)
920 repo.dirstate.forget(f)
922 repo.dirstate.setparents(qp, revlog.nullid)
921 repo.dirstate.setparents(qp, revlog.nullid)
923 del self.applied[start:end]
922 del self.applied[start:end]
924 self.strip(repo, rev, update=False, backup='strip')
923 self.strip(repo, rev, update=False, backup='strip')
925 if len(self.applied):
924 if len(self.applied):
926 self.ui.write("Now at: %s\n" % self.applied[-1].name)
925 self.ui.write("Now at: %s\n" % self.applied[-1].name)
927 else:
926 else:
928 self.ui.write("Patch queue now empty\n")
927 self.ui.write("Patch queue now empty\n")
929 finally:
928 finally:
930 del wlock
929 del wlock
931
930
932 def diff(self, repo, pats, opts):
931 def diff(self, repo, pats, opts):
933 top = self.check_toppatch(repo)
932 top = self.check_toppatch(repo)
934 if not top:
933 if not top:
935 self.ui.write("No patches applied\n")
934 self.ui.write("No patches applied\n")
936 return
935 return
937 qp = self.qparents(repo, top)
936 qp = self.qparents(repo, top)
938 if opts.get('git'):
937 if opts.get('git'):
939 self.diffopts().git = True
938 self.diffopts().git = True
940 if opts.get('unified') is not None:
939 if opts.get('unified') is not None:
941 self.diffopts().context = opts['unified']
940 self.diffopts().context = opts['unified']
942 self.printdiff(repo, qp, files=pats, opts=opts)
941 self.printdiff(repo, qp, files=pats, opts=opts)
943
942
944 def refresh(self, repo, pats=None, **opts):
943 def refresh(self, repo, pats=None, **opts):
945 if len(self.applied) == 0:
944 if len(self.applied) == 0:
946 self.ui.write("No patches applied\n")
945 self.ui.write("No patches applied\n")
947 return 1
946 return 1
948 newdate = opts.get('date')
947 newdate = opts.get('date')
949 if newdate:
948 if newdate:
950 newdate = '%d %d' % util.parsedate(newdate)
949 newdate = '%d %d' % util.parsedate(newdate)
951 wlock = repo.wlock()
950 wlock = repo.wlock()
952 try:
951 try:
953 self.check_toppatch(repo)
952 self.check_toppatch(repo)
954 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
953 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
955 top = revlog.bin(top)
954 top = revlog.bin(top)
956 if repo.changelog.heads(top) != [top]:
955 if repo.changelog.heads(top) != [top]:
957 raise util.Abort("cannot refresh a revision with children")
956 raise util.Abort("cannot refresh a revision with children")
958 cparents = repo.changelog.parents(top)
957 cparents = repo.changelog.parents(top)
959 patchparent = self.qparents(repo, top)
958 patchparent = self.qparents(repo, top)
960 message, comments, user, date, patchfound = self.readheaders(patchfn)
959 message, comments, user, date, patchfound = self.readheaders(patchfn)
961
960
962 patchf = self.opener(patchfn, 'r+')
961 patchf = self.opener(patchfn, 'r+')
963
962
964 # if the patch was a git patch, refresh it as a git patch
963 # if the patch was a git patch, refresh it as a git patch
965 for line in patchf:
964 for line in patchf:
966 if line.startswith('diff --git'):
965 if line.startswith('diff --git'):
967 self.diffopts().git = True
966 self.diffopts().git = True
968 break
967 break
969
968
970 msg = opts.get('msg', '').rstrip()
969 msg = opts.get('msg', '').rstrip()
971 if msg and comments:
970 if msg and comments:
972 # Remove existing message, keeping the rest of the comments
971 # Remove existing message, keeping the rest of the comments
973 # fields.
972 # fields.
974 # If comments contains 'subject: ', message will prepend
973 # If comments contains 'subject: ', message will prepend
975 # the field and a blank line.
974 # the field and a blank line.
976 if message:
975 if message:
977 subj = 'subject: ' + message[0].lower()
976 subj = 'subject: ' + message[0].lower()
978 for i in xrange(len(comments)):
977 for i in xrange(len(comments)):
979 if subj == comments[i].lower():
978 if subj == comments[i].lower():
980 del comments[i]
979 del comments[i]
981 message = message[2:]
980 message = message[2:]
982 break
981 break
983 ci = 0
982 ci = 0
984 for mi in xrange(len(message)):
983 for mi in xrange(len(message)):
985 while message[mi] != comments[ci]:
984 while message[mi] != comments[ci]:
986 ci += 1
985 ci += 1
987 del comments[ci]
986 del comments[ci]
988
987
989 def setheaderfield(comments, prefixes, new):
988 def setheaderfield(comments, prefixes, new):
990 # Update all references to a field in the patch header.
989 # Update all references to a field in the patch header.
991 # If none found, add it email style.
990 # If none found, add it email style.
992 res = False
991 res = False
993 for prefix in prefixes:
992 for prefix in prefixes:
994 for i in xrange(len(comments)):
993 for i in xrange(len(comments)):
995 if comments[i].startswith(prefix):
994 if comments[i].startswith(prefix):
996 comments[i] = prefix + new
995 comments[i] = prefix + new
997 res = True
996 res = True
998 break
997 break
999 return res
998 return res
1000
999
1001 newuser = opts.get('user')
1000 newuser = opts.get('user')
1002 if newuser:
1001 if newuser:
1003 if not setheaderfield(comments, ['From: ', '# User '], newuser):
1002 if not setheaderfield(comments, ['From: ', '# User '], newuser):
1004 try:
1003 try:
1005 patchheaderat = comments.index('# HG changeset patch')
1004 patchheaderat = comments.index('# HG changeset patch')
1006 comments.insert(patchheaderat + 1,'# User ' + newuser)
1005 comments.insert(patchheaderat + 1,'# User ' + newuser)
1007 except ValueError:
1006 except ValueError:
1008 comments = ['From: ' + newuser, ''] + comments
1007 comments = ['From: ' + newuser, ''] + comments
1009 user = newuser
1008 user = newuser
1010
1009
1011 if newdate:
1010 if newdate:
1012 if setheaderfield(comments, ['# Date '], newdate):
1011 if setheaderfield(comments, ['# Date '], newdate):
1013 date = newdate
1012 date = newdate
1014
1013
1015 if msg:
1014 if msg:
1016 comments.append(msg)
1015 comments.append(msg)
1017
1016
1018 patchf.seek(0)
1017 patchf.seek(0)
1019 patchf.truncate()
1018 patchf.truncate()
1020
1019
1021 if comments:
1020 if comments:
1022 comments = "\n".join(comments) + '\n\n'
1021 comments = "\n".join(comments) + '\n\n'
1023 patchf.write(comments)
1022 patchf.write(comments)
1024
1023
1025 if opts.get('git'):
1024 if opts.get('git'):
1026 self.diffopts().git = True
1025 self.diffopts().git = True
1027 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
1026 matchfn = cmdutil.match(repo, pats, opts)
1028 tip = repo.changelog.tip()
1027 tip = repo.changelog.tip()
1029 if top == tip:
1028 if top == tip:
1030 # if the top of our patch queue is also the tip, there is an
1029 # if the top of our patch queue is also the tip, there is an
1031 # optimization here. We update the dirstate in place and strip
1030 # optimization here. We update the dirstate in place and strip
1032 # off the tip commit. Then just commit the current directory
1031 # off the tip commit. Then just commit the current directory
1033 # tree. We can also send repo.commit the list of files
1032 # tree. We can also send repo.commit the list of files
1034 # changed to speed up the diff
1033 # changed to speed up the diff
1035 #
1034 #
1036 # in short mode, we only diff the files included in the
1035 # in short mode, we only diff the files included in the
1037 # patch already
1036 # patch already
1038 #
1037 #
1039 # this should really read:
1038 # this should really read:
1040 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
1039 # mm, dd, aa, aa2, uu = repo.status(tip, patchparent)[:5]
1041 # but we do it backwards to take advantage of manifest/chlog
1040 # but we do it backwards to take advantage of manifest/chlog
1042 # caching against the next repo.status call
1041 # caching against the next repo.status call
1043 #
1042 #
1044 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
1043 mm, aa, dd, aa2, uu = repo.status(patchparent, tip)[:5]
1045 changes = repo.changelog.read(tip)
1044 changes = repo.changelog.read(tip)
1046 man = repo.manifest.read(changes[0])
1045 man = repo.manifest.read(changes[0])
1047 aaa = aa[:]
1046 aaa = aa[:]
1048 if opts.get('short'):
1047 if opts.get('short'):
1049 filelist = mm + aa + dd
1048 filelist = mm + aa + dd
1050 match = dict.fromkeys(filelist).__contains__
1049 match = dict.fromkeys(filelist).__contains__
1051 else:
1050 else:
1052 filelist = None
1051 filelist = None
1053 match = util.always
1052 match = util.always
1054 m, a, r, d, u = repo.status(files=filelist, match=match)[:5]
1053 m, a, r, d, u = repo.status(files=filelist, match=match)[:5]
1055
1054
1056 # we might end up with files that were added between
1055 # we might end up with files that were added between
1057 # tip and the dirstate parent, but then changed in the
1056 # tip and the dirstate parent, but then changed in the
1058 # local dirstate. in this case, we want them to only
1057 # local dirstate. in this case, we want them to only
1059 # show up in the added section
1058 # show up in the added section
1060 for x in m:
1059 for x in m:
1061 if x not in aa:
1060 if x not in aa:
1062 mm.append(x)
1061 mm.append(x)
1063 # we might end up with files added by the local dirstate that
1062 # we might end up with files added by the local dirstate that
1064 # were deleted by the patch. In this case, they should only
1063 # were deleted by the patch. In this case, they should only
1065 # show up in the changed section.
1064 # show up in the changed section.
1066 for x in a:
1065 for x in a:
1067 if x in dd:
1066 if x in dd:
1068 del dd[dd.index(x)]
1067 del dd[dd.index(x)]
1069 mm.append(x)
1068 mm.append(x)
1070 else:
1069 else:
1071 aa.append(x)
1070 aa.append(x)
1072 # make sure any files deleted in the local dirstate
1071 # make sure any files deleted in the local dirstate
1073 # are not in the add or change column of the patch
1072 # are not in the add or change column of the patch
1074 forget = []
1073 forget = []
1075 for x in d + r:
1074 for x in d + r:
1076 if x in aa:
1075 if x in aa:
1077 del aa[aa.index(x)]
1076 del aa[aa.index(x)]
1078 forget.append(x)
1077 forget.append(x)
1079 continue
1078 continue
1080 elif x in mm:
1079 elif x in mm:
1081 del mm[mm.index(x)]
1080 del mm[mm.index(x)]
1082 dd.append(x)
1081 dd.append(x)
1083
1082
1084 m = util.unique(mm)
1083 m = util.unique(mm)
1085 r = util.unique(dd)
1084 r = util.unique(dd)
1086 a = util.unique(aa)
1085 a = util.unique(aa)
1087 c = [filter(matchfn, l) for l in (m, a, r, [], u)]
1086 c = [filter(matchfn, l) for l in (m, a, r, [], u)]
1088 filelist = util.unique(c[0] + c[1] + c[2])
1087 filelist = util.unique(c[0] + c[1] + c[2])
1089 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1088 patch.diff(repo, patchparent, files=filelist, match=matchfn,
1090 fp=patchf, changes=c, opts=self.diffopts())
1089 fp=patchf, changes=c, opts=self.diffopts())
1091 patchf.close()
1090 patchf.close()
1092
1091
1093 repo.dirstate.setparents(*cparents)
1092 repo.dirstate.setparents(*cparents)
1094 copies = {}
1093 copies = {}
1095 for dst in a:
1094 for dst in a:
1096 src = repo.dirstate.copied(dst)
1095 src = repo.dirstate.copied(dst)
1097 if src is not None:
1096 if src is not None:
1098 copies.setdefault(src, []).append(dst)
1097 copies.setdefault(src, []).append(dst)
1099 repo.dirstate.add(dst)
1098 repo.dirstate.add(dst)
1100 # remember the copies between patchparent and tip
1099 # remember the copies between patchparent and tip
1101 # this may be slow, so don't do it if we're not tracking copies
1100 # this may be slow, so don't do it if we're not tracking copies
1102 if self.diffopts().git:
1101 if self.diffopts().git:
1103 for dst in aaa:
1102 for dst in aaa:
1104 f = repo.file(dst)
1103 f = repo.file(dst)
1105 src = f.renamed(man[dst])
1104 src = f.renamed(man[dst])
1106 if src:
1105 if src:
1107 copies[src[0]] = copies.get(dst, [])
1106 copies[src[0]] = copies.get(dst, [])
1108 if dst in a:
1107 if dst in a:
1109 copies[src[0]].append(dst)
1108 copies[src[0]].append(dst)
1110 # we can't copy a file created by the patch itself
1109 # we can't copy a file created by the patch itself
1111 if dst in copies:
1110 if dst in copies:
1112 del copies[dst]
1111 del copies[dst]
1113 for src, dsts in copies.iteritems():
1112 for src, dsts in copies.iteritems():
1114 for dst in dsts:
1113 for dst in dsts:
1115 repo.dirstate.copy(src, dst)
1114 repo.dirstate.copy(src, dst)
1116 for f in r:
1115 for f in r:
1117 repo.dirstate.remove(f)
1116 repo.dirstate.remove(f)
1118 # if the patch excludes a modified file, mark that
1117 # if the patch excludes a modified file, mark that
1119 # file with mtime=0 so status can see it.
1118 # file with mtime=0 so status can see it.
1120 mm = []
1119 mm = []
1121 for i in xrange(len(m)-1, -1, -1):
1120 for i in xrange(len(m)-1, -1, -1):
1122 if not matchfn(m[i]):
1121 if not matchfn(m[i]):
1123 mm.append(m[i])
1122 mm.append(m[i])
1124 del m[i]
1123 del m[i]
1125 for f in m:
1124 for f in m:
1126 repo.dirstate.normal(f)
1125 repo.dirstate.normal(f)
1127 for f in mm:
1126 for f in mm:
1128 repo.dirstate.normallookup(f)
1127 repo.dirstate.normallookup(f)
1129 for f in forget:
1128 for f in forget:
1130 repo.dirstate.forget(f)
1129 repo.dirstate.forget(f)
1131
1130
1132 if not msg:
1131 if not msg:
1133 if not message:
1132 if not message:
1134 message = "[mq]: %s\n" % patchfn
1133 message = "[mq]: %s\n" % patchfn
1135 else:
1134 else:
1136 message = "\n".join(message)
1135 message = "\n".join(message)
1137 else:
1136 else:
1138 message = msg
1137 message = msg
1139
1138
1140 if not user:
1139 if not user:
1141 user = changes[1]
1140 user = changes[1]
1142
1141
1143 self.applied.pop()
1142 self.applied.pop()
1144 self.applied_dirty = 1
1143 self.applied_dirty = 1
1145 self.strip(repo, top, update=False,
1144 self.strip(repo, top, update=False,
1146 backup='strip')
1145 backup='strip')
1147 n = repo.commit(filelist, message, user, date, match=matchfn,
1146 n = repo.commit(filelist, message, user, date, match=matchfn,
1148 force=1)
1147 force=1)
1149 self.applied.append(statusentry(revlog.hex(n), patchfn))
1148 self.applied.append(statusentry(revlog.hex(n), patchfn))
1150 self.removeundo(repo)
1149 self.removeundo(repo)
1151 else:
1150 else:
1152 self.printdiff(repo, patchparent, fp=patchf)
1151 self.printdiff(repo, patchparent, fp=patchf)
1153 patchf.close()
1152 patchf.close()
1154 added = repo.status()[1]
1153 added = repo.status()[1]
1155 for a in added:
1154 for a in added:
1156 f = repo.wjoin(a)
1155 f = repo.wjoin(a)
1157 try:
1156 try:
1158 os.unlink(f)
1157 os.unlink(f)
1159 except OSError, e:
1158 except OSError, e:
1160 if e.errno != errno.ENOENT:
1159 if e.errno != errno.ENOENT:
1161 raise
1160 raise
1162 try: os.removedirs(os.path.dirname(f))
1161 try: os.removedirs(os.path.dirname(f))
1163 except: pass
1162 except: pass
1164 # forget the file copies in the dirstate
1163 # forget the file copies in the dirstate
1165 # push should readd the files later on
1164 # push should readd the files later on
1166 repo.dirstate.forget(a)
1165 repo.dirstate.forget(a)
1167 self.pop(repo, force=True)
1166 self.pop(repo, force=True)
1168 self.push(repo, force=True)
1167 self.push(repo, force=True)
1169 finally:
1168 finally:
1170 del wlock
1169 del wlock
1171
1170
1172 def init(self, repo, create=False):
1171 def init(self, repo, create=False):
1173 if not create and os.path.isdir(self.path):
1172 if not create and os.path.isdir(self.path):
1174 raise util.Abort(_("patch queue directory already exists"))
1173 raise util.Abort(_("patch queue directory already exists"))
1175 try:
1174 try:
1176 os.mkdir(self.path)
1175 os.mkdir(self.path)
1177 except OSError, inst:
1176 except OSError, inst:
1178 if inst.errno != errno.EEXIST or not create:
1177 if inst.errno != errno.EEXIST or not create:
1179 raise
1178 raise
1180 if create:
1179 if create:
1181 return self.qrepo(create=True)
1180 return self.qrepo(create=True)
1182
1181
1183 def unapplied(self, repo, patch=None):
1182 def unapplied(self, repo, patch=None):
1184 if patch and patch not in self.series:
1183 if patch and patch not in self.series:
1185 raise util.Abort(_("patch %s is not in series file") % patch)
1184 raise util.Abort(_("patch %s is not in series file") % patch)
1186 if not patch:
1185 if not patch:
1187 start = self.series_end()
1186 start = self.series_end()
1188 else:
1187 else:
1189 start = self.series.index(patch) + 1
1188 start = self.series.index(patch) + 1
1190 unapplied = []
1189 unapplied = []
1191 for i in xrange(start, len(self.series)):
1190 for i in xrange(start, len(self.series)):
1192 pushable, reason = self.pushable(i)
1191 pushable, reason = self.pushable(i)
1193 if pushable:
1192 if pushable:
1194 unapplied.append((i, self.series[i]))
1193 unapplied.append((i, self.series[i]))
1195 self.explain_pushable(i)
1194 self.explain_pushable(i)
1196 return unapplied
1195 return unapplied
1197
1196
1198 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1197 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1199 summary=False):
1198 summary=False):
1200 def displayname(patchname):
1199 def displayname(patchname):
1201 if summary:
1200 if summary:
1202 msg = self.readheaders(patchname)[0]
1201 msg = self.readheaders(patchname)[0]
1203 msg = msg and ': ' + msg[0] or ': '
1202 msg = msg and ': ' + msg[0] or ': '
1204 else:
1203 else:
1205 msg = ''
1204 msg = ''
1206 return '%s%s' % (patchname, msg)
1205 return '%s%s' % (patchname, msg)
1207
1206
1208 applied = dict.fromkeys([p.name for p in self.applied])
1207 applied = dict.fromkeys([p.name for p in self.applied])
1209 if length is None:
1208 if length is None:
1210 length = len(self.series) - start
1209 length = len(self.series) - start
1211 if not missing:
1210 if not missing:
1212 for i in xrange(start, start+length):
1211 for i in xrange(start, start+length):
1213 patch = self.series[i]
1212 patch = self.series[i]
1214 if patch in applied:
1213 if patch in applied:
1215 stat = 'A'
1214 stat = 'A'
1216 elif self.pushable(i)[0]:
1215 elif self.pushable(i)[0]:
1217 stat = 'U'
1216 stat = 'U'
1218 else:
1217 else:
1219 stat = 'G'
1218 stat = 'G'
1220 pfx = ''
1219 pfx = ''
1221 if self.ui.verbose:
1220 if self.ui.verbose:
1222 pfx = '%d %s ' % (i, stat)
1221 pfx = '%d %s ' % (i, stat)
1223 elif status and status != stat:
1222 elif status and status != stat:
1224 continue
1223 continue
1225 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1224 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1226 else:
1225 else:
1227 msng_list = []
1226 msng_list = []
1228 for root, dirs, files in os.walk(self.path):
1227 for root, dirs, files in os.walk(self.path):
1229 d = root[len(self.path) + 1:]
1228 d = root[len(self.path) + 1:]
1230 for f in files:
1229 for f in files:
1231 fl = os.path.join(d, f)
1230 fl = os.path.join(d, f)
1232 if (fl not in self.series and
1231 if (fl not in self.series and
1233 fl not in (self.status_path, self.series_path,
1232 fl not in (self.status_path, self.series_path,
1234 self.guards_path)
1233 self.guards_path)
1235 and not fl.startswith('.')):
1234 and not fl.startswith('.')):
1236 msng_list.append(fl)
1235 msng_list.append(fl)
1237 msng_list.sort()
1236 msng_list.sort()
1238 for x in msng_list:
1237 for x in msng_list:
1239 pfx = self.ui.verbose and ('D ') or ''
1238 pfx = self.ui.verbose and ('D ') or ''
1240 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1239 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1241
1240
1242 def issaveline(self, l):
1241 def issaveline(self, l):
1243 if l.name == '.hg.patches.save.line':
1242 if l.name == '.hg.patches.save.line':
1244 return True
1243 return True
1245
1244
1246 def qrepo(self, create=False):
1245 def qrepo(self, create=False):
1247 if create or os.path.isdir(self.join(".hg")):
1246 if create or os.path.isdir(self.join(".hg")):
1248 return hg.repository(self.ui, path=self.path, create=create)
1247 return hg.repository(self.ui, path=self.path, create=create)
1249
1248
1250 def restore(self, repo, rev, delete=None, qupdate=None):
1249 def restore(self, repo, rev, delete=None, qupdate=None):
1251 c = repo.changelog.read(rev)
1250 c = repo.changelog.read(rev)
1252 desc = c[4].strip()
1251 desc = c[4].strip()
1253 lines = desc.splitlines()
1252 lines = desc.splitlines()
1254 i = 0
1253 i = 0
1255 datastart = None
1254 datastart = None
1256 series = []
1255 series = []
1257 applied = []
1256 applied = []
1258 qpp = None
1257 qpp = None
1259 for i in xrange(0, len(lines)):
1258 for i in xrange(0, len(lines)):
1260 if lines[i] == 'Patch Data:':
1259 if lines[i] == 'Patch Data:':
1261 datastart = i + 1
1260 datastart = i + 1
1262 elif lines[i].startswith('Dirstate:'):
1261 elif lines[i].startswith('Dirstate:'):
1263 l = lines[i].rstrip()
1262 l = lines[i].rstrip()
1264 l = l[10:].split(' ')
1263 l = l[10:].split(' ')
1265 qpp = [ bin(x) for x in l ]
1264 qpp = [ bin(x) for x in l ]
1266 elif datastart != None:
1265 elif datastart != None:
1267 l = lines[i].rstrip()
1266 l = lines[i].rstrip()
1268 se = statusentry(l)
1267 se = statusentry(l)
1269 file_ = se.name
1268 file_ = se.name
1270 if se.rev:
1269 if se.rev:
1271 applied.append(se)
1270 applied.append(se)
1272 else:
1271 else:
1273 series.append(file_)
1272 series.append(file_)
1274 if datastart == None:
1273 if datastart == None:
1275 self.ui.warn("No saved patch data found\n")
1274 self.ui.warn("No saved patch data found\n")
1276 return 1
1275 return 1
1277 self.ui.warn("restoring status: %s\n" % lines[0])
1276 self.ui.warn("restoring status: %s\n" % lines[0])
1278 self.full_series = series
1277 self.full_series = series
1279 self.applied = applied
1278 self.applied = applied
1280 self.parse_series()
1279 self.parse_series()
1281 self.series_dirty = 1
1280 self.series_dirty = 1
1282 self.applied_dirty = 1
1281 self.applied_dirty = 1
1283 heads = repo.changelog.heads()
1282 heads = repo.changelog.heads()
1284 if delete:
1283 if delete:
1285 if rev not in heads:
1284 if rev not in heads:
1286 self.ui.warn("save entry has children, leaving it alone\n")
1285 self.ui.warn("save entry has children, leaving it alone\n")
1287 else:
1286 else:
1288 self.ui.warn("removing save entry %s\n" % short(rev))
1287 self.ui.warn("removing save entry %s\n" % short(rev))
1289 pp = repo.dirstate.parents()
1288 pp = repo.dirstate.parents()
1290 if rev in pp:
1289 if rev in pp:
1291 update = True
1290 update = True
1292 else:
1291 else:
1293 update = False
1292 update = False
1294 self.strip(repo, rev, update=update, backup='strip')
1293 self.strip(repo, rev, update=update, backup='strip')
1295 if qpp:
1294 if qpp:
1296 self.ui.warn("saved queue repository parents: %s %s\n" %
1295 self.ui.warn("saved queue repository parents: %s %s\n" %
1297 (short(qpp[0]), short(qpp[1])))
1296 (short(qpp[0]), short(qpp[1])))
1298 if qupdate:
1297 if qupdate:
1299 self.ui.status(_("queue directory updating\n"))
1298 self.ui.status(_("queue directory updating\n"))
1300 r = self.qrepo()
1299 r = self.qrepo()
1301 if not r:
1300 if not r:
1302 self.ui.warn("Unable to load queue repository\n")
1301 self.ui.warn("Unable to load queue repository\n")
1303 return 1
1302 return 1
1304 hg.clean(r, qpp[0])
1303 hg.clean(r, qpp[0])
1305
1304
1306 def save(self, repo, msg=None):
1305 def save(self, repo, msg=None):
1307 if len(self.applied) == 0:
1306 if len(self.applied) == 0:
1308 self.ui.warn("save: no patches applied, exiting\n")
1307 self.ui.warn("save: no patches applied, exiting\n")
1309 return 1
1308 return 1
1310 if self.issaveline(self.applied[-1]):
1309 if self.issaveline(self.applied[-1]):
1311 self.ui.warn("status is already saved\n")
1310 self.ui.warn("status is already saved\n")
1312 return 1
1311 return 1
1313
1312
1314 ar = [ ':' + x for x in self.full_series ]
1313 ar = [ ':' + x for x in self.full_series ]
1315 if not msg:
1314 if not msg:
1316 msg = "hg patches saved state"
1315 msg = "hg patches saved state"
1317 else:
1316 else:
1318 msg = "hg patches: " + msg.rstrip('\r\n')
1317 msg = "hg patches: " + msg.rstrip('\r\n')
1319 r = self.qrepo()
1318 r = self.qrepo()
1320 if r:
1319 if r:
1321 pp = r.dirstate.parents()
1320 pp = r.dirstate.parents()
1322 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1321 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1323 msg += "\n\nPatch Data:\n"
1322 msg += "\n\nPatch Data:\n"
1324 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1323 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1325 "\n".join(ar) + '\n' or "")
1324 "\n".join(ar) + '\n' or "")
1326 n = repo.commit(None, text, user=None, force=1)
1325 n = repo.commit(None, text, user=None, force=1)
1327 if not n:
1326 if not n:
1328 self.ui.warn("repo commit failed\n")
1327 self.ui.warn("repo commit failed\n")
1329 return 1
1328 return 1
1330 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1329 self.applied.append(statusentry(revlog.hex(n),'.hg.patches.save.line'))
1331 self.applied_dirty = 1
1330 self.applied_dirty = 1
1332 self.removeundo(repo)
1331 self.removeundo(repo)
1333
1332
1334 def full_series_end(self):
1333 def full_series_end(self):
1335 if len(self.applied) > 0:
1334 if len(self.applied) > 0:
1336 p = self.applied[-1].name
1335 p = self.applied[-1].name
1337 end = self.find_series(p)
1336 end = self.find_series(p)
1338 if end == None:
1337 if end == None:
1339 return len(self.full_series)
1338 return len(self.full_series)
1340 return end + 1
1339 return end + 1
1341 return 0
1340 return 0
1342
1341
1343 def series_end(self, all_patches=False):
1342 def series_end(self, all_patches=False):
1344 """If all_patches is False, return the index of the next pushable patch
1343 """If all_patches is False, return the index of the next pushable patch
1345 in the series, or the series length. If all_patches is True, return the
1344 in the series, or the series length. If all_patches is True, return the
1346 index of the first patch past the last applied one.
1345 index of the first patch past the last applied one.
1347 """
1346 """
1348 end = 0
1347 end = 0
1349 def next(start):
1348 def next(start):
1350 if all_patches:
1349 if all_patches:
1351 return start
1350 return start
1352 i = start
1351 i = start
1353 while i < len(self.series):
1352 while i < len(self.series):
1354 p, reason = self.pushable(i)
1353 p, reason = self.pushable(i)
1355 if p:
1354 if p:
1356 break
1355 break
1357 self.explain_pushable(i)
1356 self.explain_pushable(i)
1358 i += 1
1357 i += 1
1359 return i
1358 return i
1360 if len(self.applied) > 0:
1359 if len(self.applied) > 0:
1361 p = self.applied[-1].name
1360 p = self.applied[-1].name
1362 try:
1361 try:
1363 end = self.series.index(p)
1362 end = self.series.index(p)
1364 except ValueError:
1363 except ValueError:
1365 return 0
1364 return 0
1366 return next(end + 1)
1365 return next(end + 1)
1367 return next(end)
1366 return next(end)
1368
1367
1369 def appliedname(self, index):
1368 def appliedname(self, index):
1370 pname = self.applied[index].name
1369 pname = self.applied[index].name
1371 if not self.ui.verbose:
1370 if not self.ui.verbose:
1372 p = pname
1371 p = pname
1373 else:
1372 else:
1374 p = str(self.series.index(pname)) + " " + pname
1373 p = str(self.series.index(pname)) + " " + pname
1375 return p
1374 return p
1376
1375
1377 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1376 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1378 force=None, git=False):
1377 force=None, git=False):
1379 def checkseries(patchname):
1378 def checkseries(patchname):
1380 if patchname in self.series:
1379 if patchname in self.series:
1381 raise util.Abort(_('patch %s is already in the series file')
1380 raise util.Abort(_('patch %s is already in the series file')
1382 % patchname)
1381 % patchname)
1383 def checkfile(patchname):
1382 def checkfile(patchname):
1384 if not force and os.path.exists(self.join(patchname)):
1383 if not force and os.path.exists(self.join(patchname)):
1385 raise util.Abort(_('patch "%s" already exists')
1384 raise util.Abort(_('patch "%s" already exists')
1386 % patchname)
1385 % patchname)
1387
1386
1388 if rev:
1387 if rev:
1389 if files:
1388 if files:
1390 raise util.Abort(_('option "-r" not valid when importing '
1389 raise util.Abort(_('option "-r" not valid when importing '
1391 'files'))
1390 'files'))
1392 rev = cmdutil.revrange(repo, rev)
1391 rev = cmdutil.revrange(repo, rev)
1393 rev.sort(lambda x, y: cmp(y, x))
1392 rev.sort(lambda x, y: cmp(y, x))
1394 if (len(files) > 1 or len(rev) > 1) and patchname:
1393 if (len(files) > 1 or len(rev) > 1) and patchname:
1395 raise util.Abort(_('option "-n" not valid when importing multiple '
1394 raise util.Abort(_('option "-n" not valid when importing multiple '
1396 'patches'))
1395 'patches'))
1397 i = 0
1396 i = 0
1398 added = []
1397 added = []
1399 if rev:
1398 if rev:
1400 # If mq patches are applied, we can only import revisions
1399 # If mq patches are applied, we can only import revisions
1401 # that form a linear path to qbase.
1400 # that form a linear path to qbase.
1402 # Otherwise, they should form a linear path to a head.
1401 # Otherwise, they should form a linear path to a head.
1403 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1402 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1404 if len(heads) > 1:
1403 if len(heads) > 1:
1405 raise util.Abort(_('revision %d is the root of more than one '
1404 raise util.Abort(_('revision %d is the root of more than one '
1406 'branch') % rev[-1])
1405 'branch') % rev[-1])
1407 if self.applied:
1406 if self.applied:
1408 base = revlog.hex(repo.changelog.node(rev[0]))
1407 base = revlog.hex(repo.changelog.node(rev[0]))
1409 if base in [n.rev for n in self.applied]:
1408 if base in [n.rev for n in self.applied]:
1410 raise util.Abort(_('revision %d is already managed')
1409 raise util.Abort(_('revision %d is already managed')
1411 % rev[0])
1410 % rev[0])
1412 if heads != [revlog.bin(self.applied[-1].rev)]:
1411 if heads != [revlog.bin(self.applied[-1].rev)]:
1413 raise util.Abort(_('revision %d is not the parent of '
1412 raise util.Abort(_('revision %d is not the parent of '
1414 'the queue') % rev[0])
1413 'the queue') % rev[0])
1415 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1414 base = repo.changelog.rev(revlog.bin(self.applied[0].rev))
1416 lastparent = repo.changelog.parentrevs(base)[0]
1415 lastparent = repo.changelog.parentrevs(base)[0]
1417 else:
1416 else:
1418 if heads != [repo.changelog.node(rev[0])]:
1417 if heads != [repo.changelog.node(rev[0])]:
1419 raise util.Abort(_('revision %d has unmanaged children')
1418 raise util.Abort(_('revision %d has unmanaged children')
1420 % rev[0])
1419 % rev[0])
1421 lastparent = None
1420 lastparent = None
1422
1421
1423 if git:
1422 if git:
1424 self.diffopts().git = True
1423 self.diffopts().git = True
1425
1424
1426 for r in rev:
1425 for r in rev:
1427 p1, p2 = repo.changelog.parentrevs(r)
1426 p1, p2 = repo.changelog.parentrevs(r)
1428 n = repo.changelog.node(r)
1427 n = repo.changelog.node(r)
1429 if p2 != revlog.nullrev:
1428 if p2 != revlog.nullrev:
1430 raise util.Abort(_('cannot import merge revision %d') % r)
1429 raise util.Abort(_('cannot import merge revision %d') % r)
1431 if lastparent and lastparent != r:
1430 if lastparent and lastparent != r:
1432 raise util.Abort(_('revision %d is not the parent of %d')
1431 raise util.Abort(_('revision %d is not the parent of %d')
1433 % (r, lastparent))
1432 % (r, lastparent))
1434 lastparent = p1
1433 lastparent = p1
1435
1434
1436 if not patchname:
1435 if not patchname:
1437 patchname = normname('%d.diff' % r)
1436 patchname = normname('%d.diff' % r)
1438 self.check_reserved_name(patchname)
1437 self.check_reserved_name(patchname)
1439 checkseries(patchname)
1438 checkseries(patchname)
1440 checkfile(patchname)
1439 checkfile(patchname)
1441 self.full_series.insert(0, patchname)
1440 self.full_series.insert(0, patchname)
1442
1441
1443 patchf = self.opener(patchname, "w")
1442 patchf = self.opener(patchname, "w")
1444 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1443 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1445 patchf.close()
1444 patchf.close()
1446
1445
1447 se = statusentry(revlog.hex(n), patchname)
1446 se = statusentry(revlog.hex(n), patchname)
1448 self.applied.insert(0, se)
1447 self.applied.insert(0, se)
1449
1448
1450 added.append(patchname)
1449 added.append(patchname)
1451 patchname = None
1450 patchname = None
1452 self.parse_series()
1451 self.parse_series()
1453 self.applied_dirty = 1
1452 self.applied_dirty = 1
1454
1453
1455 for filename in files:
1454 for filename in files:
1456 if existing:
1455 if existing:
1457 if filename == '-':
1456 if filename == '-':
1458 raise util.Abort(_('-e is incompatible with import from -'))
1457 raise util.Abort(_('-e is incompatible with import from -'))
1459 if not patchname:
1458 if not patchname:
1460 patchname = normname(filename)
1459 patchname = normname(filename)
1461 self.check_reserved_name(patchname)
1460 self.check_reserved_name(patchname)
1462 if not os.path.isfile(self.join(patchname)):
1461 if not os.path.isfile(self.join(patchname)):
1463 raise util.Abort(_("patch %s does not exist") % patchname)
1462 raise util.Abort(_("patch %s does not exist") % patchname)
1464 else:
1463 else:
1465 try:
1464 try:
1466 if filename == '-':
1465 if filename == '-':
1467 if not patchname:
1466 if not patchname:
1468 raise util.Abort(_('need --name to import a patch from -'))
1467 raise util.Abort(_('need --name to import a patch from -'))
1469 text = sys.stdin.read()
1468 text = sys.stdin.read()
1470 else:
1469 else:
1471 text = file(filename, 'rb').read()
1470 text = file(filename, 'rb').read()
1472 except IOError:
1471 except IOError:
1473 raise util.Abort(_("unable to read %s") % patchname)
1472 raise util.Abort(_("unable to read %s") % patchname)
1474 if not patchname:
1473 if not patchname:
1475 patchname = normname(os.path.basename(filename))
1474 patchname = normname(os.path.basename(filename))
1476 self.check_reserved_name(patchname)
1475 self.check_reserved_name(patchname)
1477 checkfile(patchname)
1476 checkfile(patchname)
1478 patchf = self.opener(patchname, "w")
1477 patchf = self.opener(patchname, "w")
1479 patchf.write(text)
1478 patchf.write(text)
1480 checkseries(patchname)
1479 checkseries(patchname)
1481 index = self.full_series_end() + i
1480 index = self.full_series_end() + i
1482 self.full_series[index:index] = [patchname]
1481 self.full_series[index:index] = [patchname]
1483 self.parse_series()
1482 self.parse_series()
1484 self.ui.warn("adding %s to series file\n" % patchname)
1483 self.ui.warn("adding %s to series file\n" % patchname)
1485 i += 1
1484 i += 1
1486 added.append(patchname)
1485 added.append(patchname)
1487 patchname = None
1486 patchname = None
1488 self.series_dirty = 1
1487 self.series_dirty = 1
1489 qrepo = self.qrepo()
1488 qrepo = self.qrepo()
1490 if qrepo:
1489 if qrepo:
1491 qrepo.add(added)
1490 qrepo.add(added)
1492
1491
1493 def delete(ui, repo, *patches, **opts):
1492 def delete(ui, repo, *patches, **opts):
1494 """remove patches from queue
1493 """remove patches from queue
1495
1494
1496 The patches must not be applied, unless they are arguments to
1495 The patches must not be applied, unless they are arguments to
1497 the --rev parameter. At least one patch or revision is required.
1496 the --rev parameter. At least one patch or revision is required.
1498
1497
1499 With --rev, mq will stop managing the named revisions (converting
1498 With --rev, mq will stop managing the named revisions (converting
1500 them to regular mercurial changesets). The patches must be applied
1499 them to regular mercurial changesets). The patches must be applied
1501 and at the base of the stack. This option is useful when the patches
1500 and at the base of the stack. This option is useful when the patches
1502 have been applied upstream.
1501 have been applied upstream.
1503
1502
1504 With --keep, the patch files are preserved in the patch directory."""
1503 With --keep, the patch files are preserved in the patch directory."""
1505 q = repo.mq
1504 q = repo.mq
1506 q.delete(repo, patches, opts)
1505 q.delete(repo, patches, opts)
1507 q.save_dirty()
1506 q.save_dirty()
1508 return 0
1507 return 0
1509
1508
1510 def applied(ui, repo, patch=None, **opts):
1509 def applied(ui, repo, patch=None, **opts):
1511 """print the patches already applied"""
1510 """print the patches already applied"""
1512 q = repo.mq
1511 q = repo.mq
1513 if patch:
1512 if patch:
1514 if patch not in q.series:
1513 if patch not in q.series:
1515 raise util.Abort(_("patch %s is not in series file") % patch)
1514 raise util.Abort(_("patch %s is not in series file") % patch)
1516 end = q.series.index(patch) + 1
1515 end = q.series.index(patch) + 1
1517 else:
1516 else:
1518 end = q.series_end(True)
1517 end = q.series_end(True)
1519 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1518 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1520
1519
1521 def unapplied(ui, repo, patch=None, **opts):
1520 def unapplied(ui, repo, patch=None, **opts):
1522 """print the patches not yet applied"""
1521 """print the patches not yet applied"""
1523 q = repo.mq
1522 q = repo.mq
1524 if patch:
1523 if patch:
1525 if patch not in q.series:
1524 if patch not in q.series:
1526 raise util.Abort(_("patch %s is not in series file") % patch)
1525 raise util.Abort(_("patch %s is not in series file") % patch)
1527 start = q.series.index(patch) + 1
1526 start = q.series.index(patch) + 1
1528 else:
1527 else:
1529 start = q.series_end(True)
1528 start = q.series_end(True)
1530 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1529 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1531
1530
1532 def qimport(ui, repo, *filename, **opts):
1531 def qimport(ui, repo, *filename, **opts):
1533 """import a patch
1532 """import a patch
1534
1533
1535 The patch will have the same name as its source file unless you
1534 The patch will have the same name as its source file unless you
1536 give it a new one with --name.
1535 give it a new one with --name.
1537
1536
1538 You can register an existing patch inside the patch directory
1537 You can register an existing patch inside the patch directory
1539 with the --existing flag.
1538 with the --existing flag.
1540
1539
1541 With --force, an existing patch of the same name will be overwritten.
1540 With --force, an existing patch of the same name will be overwritten.
1542
1541
1543 An existing changeset may be placed under mq control with --rev
1542 An existing changeset may be placed under mq control with --rev
1544 (e.g. qimport --rev tip -n patch will place tip under mq control).
1543 (e.g. qimport --rev tip -n patch will place tip under mq control).
1545 With --git, patches imported with --rev will use the git diff
1544 With --git, patches imported with --rev will use the git diff
1546 format.
1545 format.
1547 """
1546 """
1548 q = repo.mq
1547 q = repo.mq
1549 q.qimport(repo, filename, patchname=opts['name'],
1548 q.qimport(repo, filename, patchname=opts['name'],
1550 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1549 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1551 git=opts['git'])
1550 git=opts['git'])
1552 q.save_dirty()
1551 q.save_dirty()
1553 return 0
1552 return 0
1554
1553
1555 def init(ui, repo, **opts):
1554 def init(ui, repo, **opts):
1556 """init a new queue repository
1555 """init a new queue repository
1557
1556
1558 The queue repository is unversioned by default. If -c is
1557 The queue repository is unversioned by default. If -c is
1559 specified, qinit will create a separate nested repository
1558 specified, qinit will create a separate nested repository
1560 for patches (qinit -c may also be run later to convert
1559 for patches (qinit -c may also be run later to convert
1561 an unversioned patch repository into a versioned one).
1560 an unversioned patch repository into a versioned one).
1562 You can use qcommit to commit changes to this queue repository."""
1561 You can use qcommit to commit changes to this queue repository."""
1563 q = repo.mq
1562 q = repo.mq
1564 r = q.init(repo, create=opts['create_repo'])
1563 r = q.init(repo, create=opts['create_repo'])
1565 q.save_dirty()
1564 q.save_dirty()
1566 if r:
1565 if r:
1567 if not os.path.exists(r.wjoin('.hgignore')):
1566 if not os.path.exists(r.wjoin('.hgignore')):
1568 fp = r.wopener('.hgignore', 'w')
1567 fp = r.wopener('.hgignore', 'w')
1569 fp.write('^\\.hg\n')
1568 fp.write('^\\.hg\n')
1570 fp.write('^\\.mq\n')
1569 fp.write('^\\.mq\n')
1571 fp.write('syntax: glob\n')
1570 fp.write('syntax: glob\n')
1572 fp.write('status\n')
1571 fp.write('status\n')
1573 fp.write('guards\n')
1572 fp.write('guards\n')
1574 fp.close()
1573 fp.close()
1575 if not os.path.exists(r.wjoin('series')):
1574 if not os.path.exists(r.wjoin('series')):
1576 r.wopener('series', 'w').close()
1575 r.wopener('series', 'w').close()
1577 r.add(['.hgignore', 'series'])
1576 r.add(['.hgignore', 'series'])
1578 commands.add(ui, r)
1577 commands.add(ui, r)
1579 return 0
1578 return 0
1580
1579
1581 def clone(ui, source, dest=None, **opts):
1580 def clone(ui, source, dest=None, **opts):
1582 '''clone main and patch repository at same time
1581 '''clone main and patch repository at same time
1583
1582
1584 If source is local, destination will have no patches applied. If
1583 If source is local, destination will have no patches applied. If
1585 source is remote, this command can not check if patches are
1584 source is remote, this command can not check if patches are
1586 applied in source, so cannot guarantee that patches are not
1585 applied in source, so cannot guarantee that patches are not
1587 applied in destination. If you clone remote repository, be sure
1586 applied in destination. If you clone remote repository, be sure
1588 before that it has no patches applied.
1587 before that it has no patches applied.
1589
1588
1590 Source patch repository is looked for in <src>/.hg/patches by
1589 Source patch repository is looked for in <src>/.hg/patches by
1591 default. Use -p <url> to change.
1590 default. Use -p <url> to change.
1592
1591
1593 The patch directory must be a nested mercurial repository, as
1592 The patch directory must be a nested mercurial repository, as
1594 would be created by qinit -c.
1593 would be created by qinit -c.
1595 '''
1594 '''
1596 def patchdir(repo):
1595 def patchdir(repo):
1597 url = repo.url()
1596 url = repo.url()
1598 if url.endswith('/'):
1597 if url.endswith('/'):
1599 url = url[:-1]
1598 url = url[:-1]
1600 return url + '/.hg/patches'
1599 return url + '/.hg/patches'
1601 cmdutil.setremoteconfig(ui, opts)
1600 cmdutil.setremoteconfig(ui, opts)
1602 if dest is None:
1601 if dest is None:
1603 dest = hg.defaultdest(source)
1602 dest = hg.defaultdest(source)
1604 sr = hg.repository(ui, ui.expandpath(source))
1603 sr = hg.repository(ui, ui.expandpath(source))
1605 patchespath = opts['patches'] or patchdir(sr)
1604 patchespath = opts['patches'] or patchdir(sr)
1606 try:
1605 try:
1607 pr = hg.repository(ui, patchespath)
1606 pr = hg.repository(ui, patchespath)
1608 except RepoError:
1607 except RepoError:
1609 raise util.Abort(_('versioned patch repository not found'
1608 raise util.Abort(_('versioned patch repository not found'
1610 ' (see qinit -c)'))
1609 ' (see qinit -c)'))
1611 qbase, destrev = None, None
1610 qbase, destrev = None, None
1612 if sr.local():
1611 if sr.local():
1613 if sr.mq.applied:
1612 if sr.mq.applied:
1614 qbase = revlog.bin(sr.mq.applied[0].rev)
1613 qbase = revlog.bin(sr.mq.applied[0].rev)
1615 if not hg.islocal(dest):
1614 if not hg.islocal(dest):
1616 heads = dict.fromkeys(sr.heads())
1615 heads = dict.fromkeys(sr.heads())
1617 for h in sr.heads(qbase):
1616 for h in sr.heads(qbase):
1618 del heads[h]
1617 del heads[h]
1619 destrev = heads.keys()
1618 destrev = heads.keys()
1620 destrev.append(sr.changelog.parents(qbase)[0])
1619 destrev.append(sr.changelog.parents(qbase)[0])
1621 elif sr.capable('lookup'):
1620 elif sr.capable('lookup'):
1622 try:
1621 try:
1623 qbase = sr.lookup('qbase')
1622 qbase = sr.lookup('qbase')
1624 except RepoError:
1623 except RepoError:
1625 pass
1624 pass
1626 ui.note(_('cloning main repo\n'))
1625 ui.note(_('cloning main repo\n'))
1627 sr, dr = hg.clone(ui, sr.url(), dest,
1626 sr, dr = hg.clone(ui, sr.url(), dest,
1628 pull=opts['pull'],
1627 pull=opts['pull'],
1629 rev=destrev,
1628 rev=destrev,
1630 update=False,
1629 update=False,
1631 stream=opts['uncompressed'])
1630 stream=opts['uncompressed'])
1632 ui.note(_('cloning patch repo\n'))
1631 ui.note(_('cloning patch repo\n'))
1633 spr, dpr = hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1632 spr, dpr = hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1634 pull=opts['pull'], update=not opts['noupdate'],
1633 pull=opts['pull'], update=not opts['noupdate'],
1635 stream=opts['uncompressed'])
1634 stream=opts['uncompressed'])
1636 if dr.local():
1635 if dr.local():
1637 if qbase:
1636 if qbase:
1638 ui.note(_('stripping applied patches from destination repo\n'))
1637 ui.note(_('stripping applied patches from destination repo\n'))
1639 dr.mq.strip(dr, qbase, update=False, backup=None)
1638 dr.mq.strip(dr, qbase, update=False, backup=None)
1640 if not opts['noupdate']:
1639 if not opts['noupdate']:
1641 ui.note(_('updating destination repo\n'))
1640 ui.note(_('updating destination repo\n'))
1642 hg.update(dr, dr.changelog.tip())
1641 hg.update(dr, dr.changelog.tip())
1643
1642
1644 def commit(ui, repo, *pats, **opts):
1643 def commit(ui, repo, *pats, **opts):
1645 """commit changes in the queue repository"""
1644 """commit changes in the queue repository"""
1646 q = repo.mq
1645 q = repo.mq
1647 r = q.qrepo()
1646 r = q.qrepo()
1648 if not r: raise util.Abort('no queue repository')
1647 if not r: raise util.Abort('no queue repository')
1649 commands.commit(r.ui, r, *pats, **opts)
1648 commands.commit(r.ui, r, *pats, **opts)
1650
1649
1651 def series(ui, repo, **opts):
1650 def series(ui, repo, **opts):
1652 """print the entire series file"""
1651 """print the entire series file"""
1653 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1652 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1654 return 0
1653 return 0
1655
1654
1656 def top(ui, repo, **opts):
1655 def top(ui, repo, **opts):
1657 """print the name of the current patch"""
1656 """print the name of the current patch"""
1658 q = repo.mq
1657 q = repo.mq
1659 t = q.applied and q.series_end(True) or 0
1658 t = q.applied and q.series_end(True) or 0
1660 if t:
1659 if t:
1661 return q.qseries(repo, start=t-1, length=1, status='A',
1660 return q.qseries(repo, start=t-1, length=1, status='A',
1662 summary=opts.get('summary'))
1661 summary=opts.get('summary'))
1663 else:
1662 else:
1664 ui.write("No patches applied\n")
1663 ui.write("No patches applied\n")
1665 return 1
1664 return 1
1666
1665
1667 def next(ui, repo, **opts):
1666 def next(ui, repo, **opts):
1668 """print the name of the next patch"""
1667 """print the name of the next patch"""
1669 q = repo.mq
1668 q = repo.mq
1670 end = q.series_end()
1669 end = q.series_end()
1671 if end == len(q.series):
1670 if end == len(q.series):
1672 ui.write("All patches applied\n")
1671 ui.write("All patches applied\n")
1673 return 1
1672 return 1
1674 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1673 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1675
1674
1676 def prev(ui, repo, **opts):
1675 def prev(ui, repo, **opts):
1677 """print the name of the previous patch"""
1676 """print the name of the previous patch"""
1678 q = repo.mq
1677 q = repo.mq
1679 l = len(q.applied)
1678 l = len(q.applied)
1680 if l == 1:
1679 if l == 1:
1681 ui.write("Only one patch applied\n")
1680 ui.write("Only one patch applied\n")
1682 return 1
1681 return 1
1683 if not l:
1682 if not l:
1684 ui.write("No patches applied\n")
1683 ui.write("No patches applied\n")
1685 return 1
1684 return 1
1686 return q.qseries(repo, start=l-2, length=1, status='A',
1685 return q.qseries(repo, start=l-2, length=1, status='A',
1687 summary=opts.get('summary'))
1686 summary=opts.get('summary'))
1688
1687
1689 def setupheaderopts(ui, opts):
1688 def setupheaderopts(ui, opts):
1690 def do(opt,val):
1689 def do(opt,val):
1691 if not opts[opt] and opts['current' + opt]:
1690 if not opts[opt] and opts['current' + opt]:
1692 opts[opt] = val
1691 opts[opt] = val
1693 do('user', ui.username())
1692 do('user', ui.username())
1694 do('date', "%d %d" % util.makedate())
1693 do('date', "%d %d" % util.makedate())
1695
1694
1696 def new(ui, repo, patch, *args, **opts):
1695 def new(ui, repo, patch, *args, **opts):
1697 """create a new patch
1696 """create a new patch
1698
1697
1699 qnew creates a new patch on top of the currently-applied patch
1698 qnew creates a new patch on top of the currently-applied patch
1700 (if any). It will refuse to run if there are any outstanding
1699 (if any). It will refuse to run if there are any outstanding
1701 changes unless -f is specified, in which case the patch will
1700 changes unless -f is specified, in which case the patch will
1702 be initialised with them. You may also use -I, -X, and/or a list of
1701 be initialised with them. You may also use -I, -X, and/or a list of
1703 files after the patch name to add only changes to matching files
1702 files after the patch name to add only changes to matching files
1704 to the new patch, leaving the rest as uncommitted modifications.
1703 to the new patch, leaving the rest as uncommitted modifications.
1705
1704
1706 -e, -m or -l set the patch header as well as the commit message.
1705 -e, -m or -l set the patch header as well as the commit message.
1707 If none is specified, the patch header is empty and the
1706 If none is specified, the patch header is empty and the
1708 commit message is '[mq]: PATCH'"""
1707 commit message is '[mq]: PATCH'"""
1709 q = repo.mq
1708 q = repo.mq
1710 message = cmdutil.logmessage(opts)
1709 message = cmdutil.logmessage(opts)
1711 if opts['edit']:
1710 if opts['edit']:
1712 message = ui.edit(message, ui.username())
1711 message = ui.edit(message, ui.username())
1713 opts['msg'] = message
1712 opts['msg'] = message
1714 setupheaderopts(ui, opts)
1713 setupheaderopts(ui, opts)
1715 q.new(repo, patch, *args, **opts)
1714 q.new(repo, patch, *args, **opts)
1716 q.save_dirty()
1715 q.save_dirty()
1717 return 0
1716 return 0
1718
1717
1719 def refresh(ui, repo, *pats, **opts):
1718 def refresh(ui, repo, *pats, **opts):
1720 """update the current patch
1719 """update the current patch
1721
1720
1722 If any file patterns are provided, the refreshed patch will contain only
1721 If any file patterns are provided, the refreshed patch will contain only
1723 the modifications that match those patterns; the remaining modifications
1722 the modifications that match those patterns; the remaining modifications
1724 will remain in the working directory.
1723 will remain in the working directory.
1725
1724
1726 hg add/remove/copy/rename work as usual, though you might want to use
1725 hg add/remove/copy/rename work as usual, though you might want to use
1727 git-style patches (--git or [diff] git=1) to track copies and renames.
1726 git-style patches (--git or [diff] git=1) to track copies and renames.
1728 """
1727 """
1729 q = repo.mq
1728 q = repo.mq
1730 message = cmdutil.logmessage(opts)
1729 message = cmdutil.logmessage(opts)
1731 if opts['edit']:
1730 if opts['edit']:
1732 if not q.applied:
1731 if not q.applied:
1733 ui.write(_("No patches applied\n"))
1732 ui.write(_("No patches applied\n"))
1734 return 1
1733 return 1
1735 if message:
1734 if message:
1736 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1735 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1737 patch = q.applied[-1].name
1736 patch = q.applied[-1].name
1738 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1737 (message, comment, user, date, hasdiff) = q.readheaders(patch)
1739 message = ui.edit('\n'.join(message), user or ui.username())
1738 message = ui.edit('\n'.join(message), user or ui.username())
1740 setupheaderopts(ui, opts)
1739 setupheaderopts(ui, opts)
1741 ret = q.refresh(repo, pats, msg=message, **opts)
1740 ret = q.refresh(repo, pats, msg=message, **opts)
1742 q.save_dirty()
1741 q.save_dirty()
1743 return ret
1742 return ret
1744
1743
1745 def diff(ui, repo, *pats, **opts):
1744 def diff(ui, repo, *pats, **opts):
1746 """diff of the current patch"""
1745 """diff of the current patch"""
1747 repo.mq.diff(repo, pats, opts)
1746 repo.mq.diff(repo, pats, opts)
1748 return 0
1747 return 0
1749
1748
1750 def fold(ui, repo, *files, **opts):
1749 def fold(ui, repo, *files, **opts):
1751 """fold the named patches into the current patch
1750 """fold the named patches into the current patch
1752
1751
1753 Patches must not yet be applied. Each patch will be successively
1752 Patches must not yet be applied. Each patch will be successively
1754 applied to the current patch in the order given. If all the
1753 applied to the current patch in the order given. If all the
1755 patches apply successfully, the current patch will be refreshed
1754 patches apply successfully, the current patch will be refreshed
1756 with the new cumulative patch, and the folded patches will
1755 with the new cumulative patch, and the folded patches will
1757 be deleted. With -k/--keep, the folded patch files will not
1756 be deleted. With -k/--keep, the folded patch files will not
1758 be removed afterwards.
1757 be removed afterwards.
1759
1758
1760 The header for each folded patch will be concatenated with
1759 The header for each folded patch will be concatenated with
1761 the current patch header, separated by a line of '* * *'."""
1760 the current patch header, separated by a line of '* * *'."""
1762
1761
1763 q = repo.mq
1762 q = repo.mq
1764
1763
1765 if not files:
1764 if not files:
1766 raise util.Abort(_('qfold requires at least one patch name'))
1765 raise util.Abort(_('qfold requires at least one patch name'))
1767 if not q.check_toppatch(repo):
1766 if not q.check_toppatch(repo):
1768 raise util.Abort(_('No patches applied'))
1767 raise util.Abort(_('No patches applied'))
1769
1768
1770 message = cmdutil.logmessage(opts)
1769 message = cmdutil.logmessage(opts)
1771 if opts['edit']:
1770 if opts['edit']:
1772 if message:
1771 if message:
1773 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1772 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1774
1773
1775 parent = q.lookup('qtip')
1774 parent = q.lookup('qtip')
1776 patches = []
1775 patches = []
1777 messages = []
1776 messages = []
1778 for f in files:
1777 for f in files:
1779 p = q.lookup(f)
1778 p = q.lookup(f)
1780 if p in patches or p == parent:
1779 if p in patches or p == parent:
1781 ui.warn(_('Skipping already folded patch %s') % p)
1780 ui.warn(_('Skipping already folded patch %s') % p)
1782 if q.isapplied(p):
1781 if q.isapplied(p):
1783 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1782 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1784 patches.append(p)
1783 patches.append(p)
1785
1784
1786 for p in patches:
1785 for p in patches:
1787 if not message:
1786 if not message:
1788 messages.append(q.readheaders(p)[0])
1787 messages.append(q.readheaders(p)[0])
1789 pf = q.join(p)
1788 pf = q.join(p)
1790 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1789 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1791 if not patchsuccess:
1790 if not patchsuccess:
1792 raise util.Abort(_('Error folding patch %s') % p)
1791 raise util.Abort(_('Error folding patch %s') % p)
1793 patch.updatedir(ui, repo, files)
1792 patch.updatedir(ui, repo, files)
1794
1793
1795 if not message:
1794 if not message:
1796 message, comments, user = q.readheaders(parent)[0:3]
1795 message, comments, user = q.readheaders(parent)[0:3]
1797 for msg in messages:
1796 for msg in messages:
1798 message.append('* * *')
1797 message.append('* * *')
1799 message.extend(msg)
1798 message.extend(msg)
1800 message = '\n'.join(message)
1799 message = '\n'.join(message)
1801
1800
1802 if opts['edit']:
1801 if opts['edit']:
1803 message = ui.edit(message, user or ui.username())
1802 message = ui.edit(message, user or ui.username())
1804
1803
1805 q.refresh(repo, msg=message)
1804 q.refresh(repo, msg=message)
1806 q.delete(repo, patches, opts)
1805 q.delete(repo, patches, opts)
1807 q.save_dirty()
1806 q.save_dirty()
1808
1807
1809 def goto(ui, repo, patch, **opts):
1808 def goto(ui, repo, patch, **opts):
1810 '''push or pop patches until named patch is at top of stack'''
1809 '''push or pop patches until named patch is at top of stack'''
1811 q = repo.mq
1810 q = repo.mq
1812 patch = q.lookup(patch)
1811 patch = q.lookup(patch)
1813 if q.isapplied(patch):
1812 if q.isapplied(patch):
1814 ret = q.pop(repo, patch, force=opts['force'])
1813 ret = q.pop(repo, patch, force=opts['force'])
1815 else:
1814 else:
1816 ret = q.push(repo, patch, force=opts['force'])
1815 ret = q.push(repo, patch, force=opts['force'])
1817 q.save_dirty()
1816 q.save_dirty()
1818 return ret
1817 return ret
1819
1818
1820 def guard(ui, repo, *args, **opts):
1819 def guard(ui, repo, *args, **opts):
1821 '''set or print guards for a patch
1820 '''set or print guards for a patch
1822
1821
1823 Guards control whether a patch can be pushed. A patch with no
1822 Guards control whether a patch can be pushed. A patch with no
1824 guards is always pushed. A patch with a positive guard ("+foo") is
1823 guards is always pushed. A patch with a positive guard ("+foo") is
1825 pushed only if the qselect command has activated it. A patch with
1824 pushed only if the qselect command has activated it. A patch with
1826 a negative guard ("-foo") is never pushed if the qselect command
1825 a negative guard ("-foo") is never pushed if the qselect command
1827 has activated it.
1826 has activated it.
1828
1827
1829 With no arguments, print the currently active guards.
1828 With no arguments, print the currently active guards.
1830 With arguments, set guards for the named patch.
1829 With arguments, set guards for the named patch.
1831
1830
1832 To set a negative guard "-foo" on topmost patch ("--" is needed so
1831 To set a negative guard "-foo" on topmost patch ("--" is needed so
1833 hg will not interpret "-foo" as an option):
1832 hg will not interpret "-foo" as an option):
1834 hg qguard -- -foo
1833 hg qguard -- -foo
1835
1834
1836 To set guards on another patch:
1835 To set guards on another patch:
1837 hg qguard other.patch +2.6.17 -stable
1836 hg qguard other.patch +2.6.17 -stable
1838 '''
1837 '''
1839 def status(idx):
1838 def status(idx):
1840 guards = q.series_guards[idx] or ['unguarded']
1839 guards = q.series_guards[idx] or ['unguarded']
1841 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1840 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
1842 q = repo.mq
1841 q = repo.mq
1843 patch = None
1842 patch = None
1844 args = list(args)
1843 args = list(args)
1845 if opts['list']:
1844 if opts['list']:
1846 if args or opts['none']:
1845 if args or opts['none']:
1847 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1846 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
1848 for i in xrange(len(q.series)):
1847 for i in xrange(len(q.series)):
1849 status(i)
1848 status(i)
1850 return
1849 return
1851 if not args or args[0][0:1] in '-+':
1850 if not args or args[0][0:1] in '-+':
1852 if not q.applied:
1851 if not q.applied:
1853 raise util.Abort(_('no patches applied'))
1852 raise util.Abort(_('no patches applied'))
1854 patch = q.applied[-1].name
1853 patch = q.applied[-1].name
1855 if patch is None and args[0][0:1] not in '-+':
1854 if patch is None and args[0][0:1] not in '-+':
1856 patch = args.pop(0)
1855 patch = args.pop(0)
1857 if patch is None:
1856 if patch is None:
1858 raise util.Abort(_('no patch to work with'))
1857 raise util.Abort(_('no patch to work with'))
1859 if args or opts['none']:
1858 if args or opts['none']:
1860 idx = q.find_series(patch)
1859 idx = q.find_series(patch)
1861 if idx is None:
1860 if idx is None:
1862 raise util.Abort(_('no patch named %s') % patch)
1861 raise util.Abort(_('no patch named %s') % patch)
1863 q.set_guards(idx, args)
1862 q.set_guards(idx, args)
1864 q.save_dirty()
1863 q.save_dirty()
1865 else:
1864 else:
1866 status(q.series.index(q.lookup(patch)))
1865 status(q.series.index(q.lookup(patch)))
1867
1866
1868 def header(ui, repo, patch=None):
1867 def header(ui, repo, patch=None):
1869 """Print the header of the topmost or specified patch"""
1868 """Print the header of the topmost or specified patch"""
1870 q = repo.mq
1869 q = repo.mq
1871
1870
1872 if patch:
1871 if patch:
1873 patch = q.lookup(patch)
1872 patch = q.lookup(patch)
1874 else:
1873 else:
1875 if not q.applied:
1874 if not q.applied:
1876 ui.write('No patches applied\n')
1875 ui.write('No patches applied\n')
1877 return 1
1876 return 1
1878 patch = q.lookup('qtip')
1877 patch = q.lookup('qtip')
1879 message = repo.mq.readheaders(patch)[0]
1878 message = repo.mq.readheaders(patch)[0]
1880
1879
1881 ui.write('\n'.join(message) + '\n')
1880 ui.write('\n'.join(message) + '\n')
1882
1881
1883 def lastsavename(path):
1882 def lastsavename(path):
1884 (directory, base) = os.path.split(path)
1883 (directory, base) = os.path.split(path)
1885 names = os.listdir(directory)
1884 names = os.listdir(directory)
1886 namere = re.compile("%s.([0-9]+)" % base)
1885 namere = re.compile("%s.([0-9]+)" % base)
1887 maxindex = None
1886 maxindex = None
1888 maxname = None
1887 maxname = None
1889 for f in names:
1888 for f in names:
1890 m = namere.match(f)
1889 m = namere.match(f)
1891 if m:
1890 if m:
1892 index = int(m.group(1))
1891 index = int(m.group(1))
1893 if maxindex == None or index > maxindex:
1892 if maxindex == None or index > maxindex:
1894 maxindex = index
1893 maxindex = index
1895 maxname = f
1894 maxname = f
1896 if maxname:
1895 if maxname:
1897 return (os.path.join(directory, maxname), maxindex)
1896 return (os.path.join(directory, maxname), maxindex)
1898 return (None, None)
1897 return (None, None)
1899
1898
1900 def savename(path):
1899 def savename(path):
1901 (last, index) = lastsavename(path)
1900 (last, index) = lastsavename(path)
1902 if last is None:
1901 if last is None:
1903 index = 0
1902 index = 0
1904 newpath = path + ".%d" % (index + 1)
1903 newpath = path + ".%d" % (index + 1)
1905 return newpath
1904 return newpath
1906
1905
1907 def push(ui, repo, patch=None, **opts):
1906 def push(ui, repo, patch=None, **opts):
1908 """push the next patch onto the stack
1907 """push the next patch onto the stack
1909
1908
1910 When --force is applied, all local changes in patched files will be lost.
1909 When --force is applied, all local changes in patched files will be lost.
1911 """
1910 """
1912 q = repo.mq
1911 q = repo.mq
1913 mergeq = None
1912 mergeq = None
1914
1913
1915 if opts['all']:
1914 if opts['all']:
1916 if not q.series:
1915 if not q.series:
1917 ui.warn(_('no patches in series\n'))
1916 ui.warn(_('no patches in series\n'))
1918 return 0
1917 return 0
1919 patch = q.series[-1]
1918 patch = q.series[-1]
1920 if opts['merge']:
1919 if opts['merge']:
1921 if opts['name']:
1920 if opts['name']:
1922 newpath = opts['name']
1921 newpath = opts['name']
1923 else:
1922 else:
1924 newpath, i = lastsavename(q.path)
1923 newpath, i = lastsavename(q.path)
1925 if not newpath:
1924 if not newpath:
1926 ui.warn("no saved queues found, please use -n\n")
1925 ui.warn("no saved queues found, please use -n\n")
1927 return 1
1926 return 1
1928 mergeq = queue(ui, repo.join(""), newpath)
1927 mergeq = queue(ui, repo.join(""), newpath)
1929 ui.warn("merging with queue at: %s\n" % mergeq.path)
1928 ui.warn("merging with queue at: %s\n" % mergeq.path)
1930 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1929 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
1931 mergeq=mergeq)
1930 mergeq=mergeq)
1932 return ret
1931 return ret
1933
1932
1934 def pop(ui, repo, patch=None, **opts):
1933 def pop(ui, repo, patch=None, **opts):
1935 """pop the current patch off the stack"""
1934 """pop the current patch off the stack"""
1936 localupdate = True
1935 localupdate = True
1937 if opts['name']:
1936 if opts['name']:
1938 q = queue(ui, repo.join(""), repo.join(opts['name']))
1937 q = queue(ui, repo.join(""), repo.join(opts['name']))
1939 ui.warn('using patch queue: %s\n' % q.path)
1938 ui.warn('using patch queue: %s\n' % q.path)
1940 localupdate = False
1939 localupdate = False
1941 else:
1940 else:
1942 q = repo.mq
1941 q = repo.mq
1943 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
1942 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
1944 all=opts['all'])
1943 all=opts['all'])
1945 q.save_dirty()
1944 q.save_dirty()
1946 return ret
1945 return ret
1947
1946
1948 def rename(ui, repo, patch, name=None, **opts):
1947 def rename(ui, repo, patch, name=None, **opts):
1949 """rename a patch
1948 """rename a patch
1950
1949
1951 With one argument, renames the current patch to PATCH1.
1950 With one argument, renames the current patch to PATCH1.
1952 With two arguments, renames PATCH1 to PATCH2."""
1951 With two arguments, renames PATCH1 to PATCH2."""
1953
1952
1954 q = repo.mq
1953 q = repo.mq
1955
1954
1956 if not name:
1955 if not name:
1957 name = patch
1956 name = patch
1958 patch = None
1957 patch = None
1959
1958
1960 if patch:
1959 if patch:
1961 patch = q.lookup(patch)
1960 patch = q.lookup(patch)
1962 else:
1961 else:
1963 if not q.applied:
1962 if not q.applied:
1964 ui.write(_('No patches applied\n'))
1963 ui.write(_('No patches applied\n'))
1965 return
1964 return
1966 patch = q.lookup('qtip')
1965 patch = q.lookup('qtip')
1967 absdest = q.join(name)
1966 absdest = q.join(name)
1968 if os.path.isdir(absdest):
1967 if os.path.isdir(absdest):
1969 name = normname(os.path.join(name, os.path.basename(patch)))
1968 name = normname(os.path.join(name, os.path.basename(patch)))
1970 absdest = q.join(name)
1969 absdest = q.join(name)
1971 if os.path.exists(absdest):
1970 if os.path.exists(absdest):
1972 raise util.Abort(_('%s already exists') % absdest)
1971 raise util.Abort(_('%s already exists') % absdest)
1973
1972
1974 if name in q.series:
1973 if name in q.series:
1975 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1974 raise util.Abort(_('A patch named %s already exists in the series file') % name)
1976
1975
1977 if ui.verbose:
1976 if ui.verbose:
1978 ui.write('Renaming %s to %s\n' % (patch, name))
1977 ui.write('Renaming %s to %s\n' % (patch, name))
1979 i = q.find_series(patch)
1978 i = q.find_series(patch)
1980 guards = q.guard_re.findall(q.full_series[i])
1979 guards = q.guard_re.findall(q.full_series[i])
1981 q.full_series[i] = name + ''.join([' #' + g for g in guards])
1980 q.full_series[i] = name + ''.join([' #' + g for g in guards])
1982 q.parse_series()
1981 q.parse_series()
1983 q.series_dirty = 1
1982 q.series_dirty = 1
1984
1983
1985 info = q.isapplied(patch)
1984 info = q.isapplied(patch)
1986 if info:
1985 if info:
1987 q.applied[info[0]] = statusentry(info[1], name)
1986 q.applied[info[0]] = statusentry(info[1], name)
1988 q.applied_dirty = 1
1987 q.applied_dirty = 1
1989
1988
1990 util.rename(q.join(patch), absdest)
1989 util.rename(q.join(patch), absdest)
1991 r = q.qrepo()
1990 r = q.qrepo()
1992 if r:
1991 if r:
1993 wlock = r.wlock()
1992 wlock = r.wlock()
1994 try:
1993 try:
1995 if r.dirstate[name] == 'r':
1994 if r.dirstate[name] == 'r':
1996 r.undelete([name])
1995 r.undelete([name])
1997 r.copy(patch, name)
1996 r.copy(patch, name)
1998 r.remove([patch], False)
1997 r.remove([patch], False)
1999 finally:
1998 finally:
2000 del wlock
1999 del wlock
2001
2000
2002 q.save_dirty()
2001 q.save_dirty()
2003
2002
2004 def restore(ui, repo, rev, **opts):
2003 def restore(ui, repo, rev, **opts):
2005 """restore the queue state saved by a rev"""
2004 """restore the queue state saved by a rev"""
2006 rev = repo.lookup(rev)
2005 rev = repo.lookup(rev)
2007 q = repo.mq
2006 q = repo.mq
2008 q.restore(repo, rev, delete=opts['delete'],
2007 q.restore(repo, rev, delete=opts['delete'],
2009 qupdate=opts['update'])
2008 qupdate=opts['update'])
2010 q.save_dirty()
2009 q.save_dirty()
2011 return 0
2010 return 0
2012
2011
2013 def save(ui, repo, **opts):
2012 def save(ui, repo, **opts):
2014 """save current queue state"""
2013 """save current queue state"""
2015 q = repo.mq
2014 q = repo.mq
2016 message = cmdutil.logmessage(opts)
2015 message = cmdutil.logmessage(opts)
2017 ret = q.save(repo, msg=message)
2016 ret = q.save(repo, msg=message)
2018 if ret:
2017 if ret:
2019 return ret
2018 return ret
2020 q.save_dirty()
2019 q.save_dirty()
2021 if opts['copy']:
2020 if opts['copy']:
2022 path = q.path
2021 path = q.path
2023 if opts['name']:
2022 if opts['name']:
2024 newpath = os.path.join(q.basepath, opts['name'])
2023 newpath = os.path.join(q.basepath, opts['name'])
2025 if os.path.exists(newpath):
2024 if os.path.exists(newpath):
2026 if not os.path.isdir(newpath):
2025 if not os.path.isdir(newpath):
2027 raise util.Abort(_('destination %s exists and is not '
2026 raise util.Abort(_('destination %s exists and is not '
2028 'a directory') % newpath)
2027 'a directory') % newpath)
2029 if not opts['force']:
2028 if not opts['force']:
2030 raise util.Abort(_('destination %s exists, '
2029 raise util.Abort(_('destination %s exists, '
2031 'use -f to force') % newpath)
2030 'use -f to force') % newpath)
2032 else:
2031 else:
2033 newpath = savename(path)
2032 newpath = savename(path)
2034 ui.warn("copy %s to %s\n" % (path, newpath))
2033 ui.warn("copy %s to %s\n" % (path, newpath))
2035 util.copyfiles(path, newpath)
2034 util.copyfiles(path, newpath)
2036 if opts['empty']:
2035 if opts['empty']:
2037 try:
2036 try:
2038 os.unlink(q.join(q.status_path))
2037 os.unlink(q.join(q.status_path))
2039 except:
2038 except:
2040 pass
2039 pass
2041 return 0
2040 return 0
2042
2041
2043 def strip(ui, repo, rev, **opts):
2042 def strip(ui, repo, rev, **opts):
2044 """strip a revision and all later revs on the same branch"""
2043 """strip a revision and all later revs on the same branch"""
2045 rev = repo.lookup(rev)
2044 rev = repo.lookup(rev)
2046 backup = 'all'
2045 backup = 'all'
2047 if opts['backup']:
2046 if opts['backup']:
2048 backup = 'strip'
2047 backup = 'strip'
2049 elif opts['nobackup']:
2048 elif opts['nobackup']:
2050 backup = 'none'
2049 backup = 'none'
2051 update = repo.dirstate.parents()[0] != revlog.nullid
2050 update = repo.dirstate.parents()[0] != revlog.nullid
2052 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2051 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2053 return 0
2052 return 0
2054
2053
2055 def select(ui, repo, *args, **opts):
2054 def select(ui, repo, *args, **opts):
2056 '''set or print guarded patches to push
2055 '''set or print guarded patches to push
2057
2056
2058 Use the qguard command to set or print guards on patch, then use
2057 Use the qguard command to set or print guards on patch, then use
2059 qselect to tell mq which guards to use. A patch will be pushed if it
2058 qselect to tell mq which guards to use. A patch will be pushed if it
2060 has no guards or any positive guards match the currently selected guard,
2059 has no guards or any positive guards match the currently selected guard,
2061 but will not be pushed if any negative guards match the current guard.
2060 but will not be pushed if any negative guards match the current guard.
2062 For example:
2061 For example:
2063
2062
2064 qguard foo.patch -stable (negative guard)
2063 qguard foo.patch -stable (negative guard)
2065 qguard bar.patch +stable (positive guard)
2064 qguard bar.patch +stable (positive guard)
2066 qselect stable
2065 qselect stable
2067
2066
2068 This activates the "stable" guard. mq will skip foo.patch (because
2067 This activates the "stable" guard. mq will skip foo.patch (because
2069 it has a negative match) but push bar.patch (because it
2068 it has a negative match) but push bar.patch (because it
2070 has a positive match).
2069 has a positive match).
2071
2070
2072 With no arguments, prints the currently active guards.
2071 With no arguments, prints the currently active guards.
2073 With one argument, sets the active guard.
2072 With one argument, sets the active guard.
2074
2073
2075 Use -n/--none to deactivate guards (no other arguments needed).
2074 Use -n/--none to deactivate guards (no other arguments needed).
2076 When no guards are active, patches with positive guards are skipped
2075 When no guards are active, patches with positive guards are skipped
2077 and patches with negative guards are pushed.
2076 and patches with negative guards are pushed.
2078
2077
2079 qselect can change the guards on applied patches. It does not pop
2078 qselect can change the guards on applied patches. It does not pop
2080 guarded patches by default. Use --pop to pop back to the last applied
2079 guarded patches by default. Use --pop to pop back to the last applied
2081 patch that is not guarded. Use --reapply (which implies --pop) to push
2080 patch that is not guarded. Use --reapply (which implies --pop) to push
2082 back to the current patch afterwards, but skip guarded patches.
2081 back to the current patch afterwards, but skip guarded patches.
2083
2082
2084 Use -s/--series to print a list of all guards in the series file (no
2083 Use -s/--series to print a list of all guards in the series file (no
2085 other arguments needed). Use -v for more information.'''
2084 other arguments needed). Use -v for more information.'''
2086
2085
2087 q = repo.mq
2086 q = repo.mq
2088 guards = q.active()
2087 guards = q.active()
2089 if args or opts['none']:
2088 if args or opts['none']:
2090 old_unapplied = q.unapplied(repo)
2089 old_unapplied = q.unapplied(repo)
2091 old_guarded = [i for i in xrange(len(q.applied)) if
2090 old_guarded = [i for i in xrange(len(q.applied)) if
2092 not q.pushable(i)[0]]
2091 not q.pushable(i)[0]]
2093 q.set_active(args)
2092 q.set_active(args)
2094 q.save_dirty()
2093 q.save_dirty()
2095 if not args:
2094 if not args:
2096 ui.status(_('guards deactivated\n'))
2095 ui.status(_('guards deactivated\n'))
2097 if not opts['pop'] and not opts['reapply']:
2096 if not opts['pop'] and not opts['reapply']:
2098 unapplied = q.unapplied(repo)
2097 unapplied = q.unapplied(repo)
2099 guarded = [i for i in xrange(len(q.applied))
2098 guarded = [i for i in xrange(len(q.applied))
2100 if not q.pushable(i)[0]]
2099 if not q.pushable(i)[0]]
2101 if len(unapplied) != len(old_unapplied):
2100 if len(unapplied) != len(old_unapplied):
2102 ui.status(_('number of unguarded, unapplied patches has '
2101 ui.status(_('number of unguarded, unapplied patches has '
2103 'changed from %d to %d\n') %
2102 'changed from %d to %d\n') %
2104 (len(old_unapplied), len(unapplied)))
2103 (len(old_unapplied), len(unapplied)))
2105 if len(guarded) != len(old_guarded):
2104 if len(guarded) != len(old_guarded):
2106 ui.status(_('number of guarded, applied patches has changed '
2105 ui.status(_('number of guarded, applied patches has changed '
2107 'from %d to %d\n') %
2106 'from %d to %d\n') %
2108 (len(old_guarded), len(guarded)))
2107 (len(old_guarded), len(guarded)))
2109 elif opts['series']:
2108 elif opts['series']:
2110 guards = {}
2109 guards = {}
2111 noguards = 0
2110 noguards = 0
2112 for gs in q.series_guards:
2111 for gs in q.series_guards:
2113 if not gs:
2112 if not gs:
2114 noguards += 1
2113 noguards += 1
2115 for g in gs:
2114 for g in gs:
2116 guards.setdefault(g, 0)
2115 guards.setdefault(g, 0)
2117 guards[g] += 1
2116 guards[g] += 1
2118 if ui.verbose:
2117 if ui.verbose:
2119 guards['NONE'] = noguards
2118 guards['NONE'] = noguards
2120 guards = guards.items()
2119 guards = guards.items()
2121 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2120 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2122 if guards:
2121 if guards:
2123 ui.note(_('guards in series file:\n'))
2122 ui.note(_('guards in series file:\n'))
2124 for guard, count in guards:
2123 for guard, count in guards:
2125 ui.note('%2d ' % count)
2124 ui.note('%2d ' % count)
2126 ui.write(guard, '\n')
2125 ui.write(guard, '\n')
2127 else:
2126 else:
2128 ui.note(_('no guards in series file\n'))
2127 ui.note(_('no guards in series file\n'))
2129 else:
2128 else:
2130 if guards:
2129 if guards:
2131 ui.note(_('active guards:\n'))
2130 ui.note(_('active guards:\n'))
2132 for g in guards:
2131 for g in guards:
2133 ui.write(g, '\n')
2132 ui.write(g, '\n')
2134 else:
2133 else:
2135 ui.write(_('no active guards\n'))
2134 ui.write(_('no active guards\n'))
2136 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2135 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2137 popped = False
2136 popped = False
2138 if opts['pop'] or opts['reapply']:
2137 if opts['pop'] or opts['reapply']:
2139 for i in xrange(len(q.applied)):
2138 for i in xrange(len(q.applied)):
2140 pushable, reason = q.pushable(i)
2139 pushable, reason = q.pushable(i)
2141 if not pushable:
2140 if not pushable:
2142 ui.status(_('popping guarded patches\n'))
2141 ui.status(_('popping guarded patches\n'))
2143 popped = True
2142 popped = True
2144 if i == 0:
2143 if i == 0:
2145 q.pop(repo, all=True)
2144 q.pop(repo, all=True)
2146 else:
2145 else:
2147 q.pop(repo, i-1)
2146 q.pop(repo, i-1)
2148 break
2147 break
2149 if popped:
2148 if popped:
2150 try:
2149 try:
2151 if reapply:
2150 if reapply:
2152 ui.status(_('reapplying unguarded patches\n'))
2151 ui.status(_('reapplying unguarded patches\n'))
2153 q.push(repo, reapply)
2152 q.push(repo, reapply)
2154 finally:
2153 finally:
2155 q.save_dirty()
2154 q.save_dirty()
2156
2155
2157 def reposetup(ui, repo):
2156 def reposetup(ui, repo):
2158 class mqrepo(repo.__class__):
2157 class mqrepo(repo.__class__):
2159 def abort_if_wdir_patched(self, errmsg, force=False):
2158 def abort_if_wdir_patched(self, errmsg, force=False):
2160 if self.mq.applied and not force:
2159 if self.mq.applied and not force:
2161 parent = revlog.hex(self.dirstate.parents()[0])
2160 parent = revlog.hex(self.dirstate.parents()[0])
2162 if parent in [s.rev for s in self.mq.applied]:
2161 if parent in [s.rev for s in self.mq.applied]:
2163 raise util.Abort(errmsg)
2162 raise util.Abort(errmsg)
2164
2163
2165 def commit(self, *args, **opts):
2164 def commit(self, *args, **opts):
2166 if len(args) >= 6:
2165 if len(args) >= 6:
2167 force = args[5]
2166 force = args[5]
2168 else:
2167 else:
2169 force = opts.get('force')
2168 force = opts.get('force')
2170 self.abort_if_wdir_patched(
2169 self.abort_if_wdir_patched(
2171 _('cannot commit over an applied mq patch'),
2170 _('cannot commit over an applied mq patch'),
2172 force)
2171 force)
2173
2172
2174 return super(mqrepo, self).commit(*args, **opts)
2173 return super(mqrepo, self).commit(*args, **opts)
2175
2174
2176 def push(self, remote, force=False, revs=None):
2175 def push(self, remote, force=False, revs=None):
2177 if self.mq.applied and not force and not revs:
2176 if self.mq.applied and not force and not revs:
2178 raise util.Abort(_('source has mq patches applied'))
2177 raise util.Abort(_('source has mq patches applied'))
2179 return super(mqrepo, self).push(remote, force, revs)
2178 return super(mqrepo, self).push(remote, force, revs)
2180
2179
2181 def tags(self):
2180 def tags(self):
2182 if self.tagscache:
2181 if self.tagscache:
2183 return self.tagscache
2182 return self.tagscache
2184
2183
2185 tagscache = super(mqrepo, self).tags()
2184 tagscache = super(mqrepo, self).tags()
2186
2185
2187 q = self.mq
2186 q = self.mq
2188 if not q.applied:
2187 if not q.applied:
2189 return tagscache
2188 return tagscache
2190
2189
2191 mqtags = [(revlog.bin(patch.rev), patch.name) for patch in q.applied]
2190 mqtags = [(revlog.bin(patch.rev), patch.name) for patch in q.applied]
2192
2191
2193 if mqtags[-1][0] not in self.changelog.nodemap:
2192 if mqtags[-1][0] not in self.changelog.nodemap:
2194 self.ui.warn('mq status file refers to unknown node %s\n'
2193 self.ui.warn('mq status file refers to unknown node %s\n'
2195 % revlog.short(mqtags[-1][0]))
2194 % revlog.short(mqtags[-1][0]))
2196 return tagscache
2195 return tagscache
2197
2196
2198 mqtags.append((mqtags[-1][0], 'qtip'))
2197 mqtags.append((mqtags[-1][0], 'qtip'))
2199 mqtags.append((mqtags[0][0], 'qbase'))
2198 mqtags.append((mqtags[0][0], 'qbase'))
2200 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2199 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2201 for patch in mqtags:
2200 for patch in mqtags:
2202 if patch[1] in tagscache:
2201 if patch[1] in tagscache:
2203 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2202 self.ui.warn('Tag %s overrides mq patch of the same name\n' % patch[1])
2204 else:
2203 else:
2205 tagscache[patch[1]] = patch[0]
2204 tagscache[patch[1]] = patch[0]
2206
2205
2207 return tagscache
2206 return tagscache
2208
2207
2209 def _branchtags(self, partial, lrev):
2208 def _branchtags(self, partial, lrev):
2210 q = self.mq
2209 q = self.mq
2211 if not q.applied:
2210 if not q.applied:
2212 return super(mqrepo, self)._branchtags(partial, lrev)
2211 return super(mqrepo, self)._branchtags(partial, lrev)
2213
2212
2214 cl = self.changelog
2213 cl = self.changelog
2215 qbasenode = revlog.bin(q.applied[0].rev)
2214 qbasenode = revlog.bin(q.applied[0].rev)
2216 if qbasenode not in cl.nodemap:
2215 if qbasenode not in cl.nodemap:
2217 self.ui.warn('mq status file refers to unknown node %s\n'
2216 self.ui.warn('mq status file refers to unknown node %s\n'
2218 % revlog.short(qbasenode))
2217 % revlog.short(qbasenode))
2219 return super(mqrepo, self)._branchtags(partial, lrev)
2218 return super(mqrepo, self)._branchtags(partial, lrev)
2220
2219
2221 qbase = cl.rev(qbasenode)
2220 qbase = cl.rev(qbasenode)
2222 start = lrev + 1
2221 start = lrev + 1
2223 if start < qbase:
2222 if start < qbase:
2224 # update the cache (excluding the patches) and save it
2223 # update the cache (excluding the patches) and save it
2225 self._updatebranchcache(partial, lrev+1, qbase)
2224 self._updatebranchcache(partial, lrev+1, qbase)
2226 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2225 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2227 start = qbase
2226 start = qbase
2228 # if start = qbase, the cache is as updated as it should be.
2227 # if start = qbase, the cache is as updated as it should be.
2229 # if start > qbase, the cache includes (part of) the patches.
2228 # if start > qbase, the cache includes (part of) the patches.
2230 # we might as well use it, but we won't save it.
2229 # we might as well use it, but we won't save it.
2231
2230
2232 # update the cache up to the tip
2231 # update the cache up to the tip
2233 self._updatebranchcache(partial, start, cl.count())
2232 self._updatebranchcache(partial, start, cl.count())
2234
2233
2235 return partial
2234 return partial
2236
2235
2237 if repo.local():
2236 if repo.local():
2238 repo.__class__ = mqrepo
2237 repo.__class__ = mqrepo
2239 repo.mq = queue(ui, repo.join(""))
2238 repo.mq = queue(ui, repo.join(""))
2240
2239
2241 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2240 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2242
2241
2243 headeropts = [
2242 headeropts = [
2244 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2243 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2245 ('u', 'user', '', _('add "From: <given user>" to patch')),
2244 ('u', 'user', '', _('add "From: <given user>" to patch')),
2246 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2245 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2247 ('d', 'date', '', _('add "Date: <given date>" to patch'))]
2246 ('d', 'date', '', _('add "Date: <given date>" to patch'))]
2248
2247
2249 cmdtable = {
2248 cmdtable = {
2250 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2249 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2251 "qclone":
2250 "qclone":
2252 (clone,
2251 (clone,
2253 [('', 'pull', None, _('use pull protocol to copy metadata')),
2252 [('', 'pull', None, _('use pull protocol to copy metadata')),
2254 ('U', 'noupdate', None, _('do not update the new working directories')),
2253 ('U', 'noupdate', None, _('do not update the new working directories')),
2255 ('', 'uncompressed', None,
2254 ('', 'uncompressed', None,
2256 _('use uncompressed transfer (fast over LAN)')),
2255 _('use uncompressed transfer (fast over LAN)')),
2257 ('p', 'patches', '', _('location of source patch repo')),
2256 ('p', 'patches', '', _('location of source patch repo')),
2258 ] + commands.remoteopts,
2257 ] + commands.remoteopts,
2259 _('hg qclone [OPTION]... SOURCE [DEST]')),
2258 _('hg qclone [OPTION]... SOURCE [DEST]')),
2260 "qcommit|qci":
2259 "qcommit|qci":
2261 (commit,
2260 (commit,
2262 commands.table["^commit|ci"][1],
2261 commands.table["^commit|ci"][1],
2263 _('hg qcommit [OPTION]... [FILE]...')),
2262 _('hg qcommit [OPTION]... [FILE]...')),
2264 "^qdiff":
2263 "^qdiff":
2265 (diff,
2264 (diff,
2266 [('g', 'git', None, _('use git extended diff format')),
2265 [('g', 'git', None, _('use git extended diff format')),
2267 ('U', 'unified', 3, _('number of lines of context to show')),
2266 ('U', 'unified', 3, _('number of lines of context to show')),
2268 ] + commands.walkopts,
2267 ] + commands.walkopts,
2269 _('hg qdiff [-I] [-X] [-U NUM] [-g] [FILE]...')),
2268 _('hg qdiff [-I] [-X] [-U NUM] [-g] [FILE]...')),
2270 "qdelete|qremove|qrm":
2269 "qdelete|qremove|qrm":
2271 (delete,
2270 (delete,
2272 [('k', 'keep', None, _('keep patch file')),
2271 [('k', 'keep', None, _('keep patch file')),
2273 ('r', 'rev', [], _('stop managing a revision'))],
2272 ('r', 'rev', [], _('stop managing a revision'))],
2274 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2273 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2275 'qfold':
2274 'qfold':
2276 (fold,
2275 (fold,
2277 [('e', 'edit', None, _('edit patch header')),
2276 [('e', 'edit', None, _('edit patch header')),
2278 ('k', 'keep', None, _('keep folded patch files')),
2277 ('k', 'keep', None, _('keep folded patch files')),
2279 ] + commands.commitopts,
2278 ] + commands.commitopts,
2280 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2279 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2281 'qgoto':
2280 'qgoto':
2282 (goto,
2281 (goto,
2283 [('f', 'force', None, _('overwrite any local changes'))],
2282 [('f', 'force', None, _('overwrite any local changes'))],
2284 _('hg qgoto [OPTION]... PATCH')),
2283 _('hg qgoto [OPTION]... PATCH')),
2285 'qguard':
2284 'qguard':
2286 (guard,
2285 (guard,
2287 [('l', 'list', None, _('list all patches and guards')),
2286 [('l', 'list', None, _('list all patches and guards')),
2288 ('n', 'none', None, _('drop all guards'))],
2287 ('n', 'none', None, _('drop all guards'))],
2289 _('hg qguard [-l] [-n] [PATCH] [+GUARD]... [-GUARD]...')),
2288 _('hg qguard [-l] [-n] [PATCH] [+GUARD]... [-GUARD]...')),
2290 'qheader': (header, [], _('hg qheader [PATCH]')),
2289 'qheader': (header, [], _('hg qheader [PATCH]')),
2291 "^qimport":
2290 "^qimport":
2292 (qimport,
2291 (qimport,
2293 [('e', 'existing', None, 'import file in patch dir'),
2292 [('e', 'existing', None, 'import file in patch dir'),
2294 ('n', 'name', '', 'patch file name'),
2293 ('n', 'name', '', 'patch file name'),
2295 ('f', 'force', None, 'overwrite existing files'),
2294 ('f', 'force', None, 'overwrite existing files'),
2296 ('r', 'rev', [], 'place existing revisions under mq control'),
2295 ('r', 'rev', [], 'place existing revisions under mq control'),
2297 ('g', 'git', None, _('use git extended diff format'))],
2296 ('g', 'git', None, _('use git extended diff format'))],
2298 _('hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...')),
2297 _('hg qimport [-e] [-n NAME] [-f] [-g] [-r REV]... FILE...')),
2299 "^qinit":
2298 "^qinit":
2300 (init,
2299 (init,
2301 [('c', 'create-repo', None, 'create queue repository')],
2300 [('c', 'create-repo', None, 'create queue repository')],
2302 _('hg qinit [-c]')),
2301 _('hg qinit [-c]')),
2303 "qnew":
2302 "qnew":
2304 (new,
2303 (new,
2305 [('e', 'edit', None, _('edit commit message')),
2304 [('e', 'edit', None, _('edit commit message')),
2306 ('f', 'force', None, _('import uncommitted changes into patch')),
2305 ('f', 'force', None, _('import uncommitted changes into patch')),
2307 ('g', 'git', None, _('use git extended diff format')),
2306 ('g', 'git', None, _('use git extended diff format')),
2308 ] + commands.walkopts + commands.commitopts + headeropts,
2307 ] + commands.walkopts + commands.commitopts + headeropts,
2309 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2308 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2310 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2309 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2311 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2310 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2312 "^qpop":
2311 "^qpop":
2313 (pop,
2312 (pop,
2314 [('a', 'all', None, _('pop all patches')),
2313 [('a', 'all', None, _('pop all patches')),
2315 ('n', 'name', '', _('queue name to pop')),
2314 ('n', 'name', '', _('queue name to pop')),
2316 ('f', 'force', None, _('forget any local changes'))],
2315 ('f', 'force', None, _('forget any local changes'))],
2317 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2316 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2318 "^qpush":
2317 "^qpush":
2319 (push,
2318 (push,
2320 [('f', 'force', None, _('apply if the patch has rejects')),
2319 [('f', 'force', None, _('apply if the patch has rejects')),
2321 ('l', 'list', None, _('list patch name in commit text')),
2320 ('l', 'list', None, _('list patch name in commit text')),
2322 ('a', 'all', None, _('apply all patches')),
2321 ('a', 'all', None, _('apply all patches')),
2323 ('m', 'merge', None, _('merge from another queue')),
2322 ('m', 'merge', None, _('merge from another queue')),
2324 ('n', 'name', '', _('merge queue name'))],
2323 ('n', 'name', '', _('merge queue name'))],
2325 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2324 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2326 "^qrefresh":
2325 "^qrefresh":
2327 (refresh,
2326 (refresh,
2328 [('e', 'edit', None, _('edit commit message')),
2327 [('e', 'edit', None, _('edit commit message')),
2329 ('g', 'git', None, _('use git extended diff format')),
2328 ('g', 'git', None, _('use git extended diff format')),
2330 ('s', 'short', None, _('refresh only files already in the patch')),
2329 ('s', 'short', None, _('refresh only files already in the patch')),
2331 ] + commands.walkopts + commands.commitopts + headeropts,
2330 ] + commands.walkopts + commands.commitopts + headeropts,
2332 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2331 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2333 'qrename|qmv':
2332 'qrename|qmv':
2334 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2333 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2335 "qrestore":
2334 "qrestore":
2336 (restore,
2335 (restore,
2337 [('d', 'delete', None, _('delete save entry')),
2336 [('d', 'delete', None, _('delete save entry')),
2338 ('u', 'update', None, _('update queue working dir'))],
2337 ('u', 'update', None, _('update queue working dir'))],
2339 _('hg qrestore [-d] [-u] REV')),
2338 _('hg qrestore [-d] [-u] REV')),
2340 "qsave":
2339 "qsave":
2341 (save,
2340 (save,
2342 [('c', 'copy', None, _('copy patch directory')),
2341 [('c', 'copy', None, _('copy patch directory')),
2343 ('n', 'name', '', _('copy directory name')),
2342 ('n', 'name', '', _('copy directory name')),
2344 ('e', 'empty', None, _('clear queue status file')),
2343 ('e', 'empty', None, _('clear queue status file')),
2345 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2344 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2346 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2345 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2347 "qselect":
2346 "qselect":
2348 (select,
2347 (select,
2349 [('n', 'none', None, _('disable all guards')),
2348 [('n', 'none', None, _('disable all guards')),
2350 ('s', 'series', None, _('list all guards in series file')),
2349 ('s', 'series', None, _('list all guards in series file')),
2351 ('', 'pop', None, _('pop to before first guarded applied patch')),
2350 ('', 'pop', None, _('pop to before first guarded applied patch')),
2352 ('', 'reapply', None, _('pop, then reapply patches'))],
2351 ('', 'reapply', None, _('pop, then reapply patches'))],
2353 _('hg qselect [OPTION]... [GUARD]...')),
2352 _('hg qselect [OPTION]... [GUARD]...')),
2354 "qseries":
2353 "qseries":
2355 (series,
2354 (series,
2356 [('m', 'missing', None, _('print patches not in series')),
2355 [('m', 'missing', None, _('print patches not in series')),
2357 ] + seriesopts,
2356 ] + seriesopts,
2358 _('hg qseries [-ms]')),
2357 _('hg qseries [-ms]')),
2359 "^strip":
2358 "^strip":
2360 (strip,
2359 (strip,
2361 [('f', 'force', None, _('force removal with local changes')),
2360 [('f', 'force', None, _('force removal with local changes')),
2362 ('b', 'backup', None, _('bundle unrelated changesets')),
2361 ('b', 'backup', None, _('bundle unrelated changesets')),
2363 ('n', 'nobackup', None, _('no backups'))],
2362 ('n', 'nobackup', None, _('no backups'))],
2364 _('hg strip [-f] [-b] [-n] REV')),
2363 _('hg strip [-f] [-b] [-n] REV')),
2365 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2364 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2366 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2365 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2367 }
2366 }
@@ -1,142 +1,142 b''
1 # Copyright (C) 2006 - Marco Barisione <marco@barisione.org>
1 # Copyright (C) 2006 - Marco Barisione <marco@barisione.org>
2 #
2 #
3 # This is a small extension for Mercurial (http://www.selenic.com/mercurial)
3 # This is a small extension for Mercurial (http://www.selenic.com/mercurial)
4 # that removes files not known to mercurial
4 # that removes files not known to mercurial
5 #
5 #
6 # This program was inspired by the "cvspurge" script contained in CVS utilities
6 # This program was inspired by the "cvspurge" script contained in CVS utilities
7 # (http://www.red-bean.com/cvsutils/).
7 # (http://www.red-bean.com/cvsutils/).
8 #
8 #
9 # To enable the "purge" extension put these lines in your ~/.hgrc:
9 # To enable the "purge" extension put these lines in your ~/.hgrc:
10 # [extensions]
10 # [extensions]
11 # hgext.purge =
11 # hgext.purge =
12 #
12 #
13 # For help on the usage of "hg purge" use:
13 # For help on the usage of "hg purge" use:
14 # hg help purge
14 # hg help purge
15 #
15 #
16 # This program is free software; you can redistribute it and/or modify
16 # This program is free software; you can redistribute it and/or modify
17 # it under the terms of the GNU General Public License as published by
17 # it under the terms of the GNU General Public License as published by
18 # the Free Software Foundation; either version 2 of the License, or
18 # the Free Software Foundation; either version 2 of the License, or
19 # (at your option) any later version.
19 # (at your option) any later version.
20 #
20 #
21 # This program is distributed in the hope that it will be useful,
21 # This program is distributed in the hope that it will be useful,
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
22 # but WITHOUT ANY WARRANTY; without even the implied warranty of
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 # GNU General Public License for more details.
24 # GNU General Public License for more details.
25 #
25 #
26 # You should have received a copy of the GNU General Public License
26 # You should have received a copy of the GNU General Public License
27 # along with this program; if not, write to the Free Software
27 # along with this program; if not, write to the Free Software
28 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
28 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
29
29
30 from mercurial import util, commands, cmdutil
30 from mercurial import util, commands, cmdutil
31 from mercurial.i18n import _
31 from mercurial.i18n import _
32 import os
32 import os
33
33
34 def purge(ui, repo, *dirs, **opts):
34 def purge(ui, repo, *dirs, **opts):
35 '''removes files not tracked by mercurial
35 '''removes files not tracked by mercurial
36
36
37 Delete files not known to mercurial, this is useful to test local and
37 Delete files not known to mercurial, this is useful to test local and
38 uncommitted changes in the otherwise clean source tree.
38 uncommitted changes in the otherwise clean source tree.
39
39
40 This means that purge will delete:
40 This means that purge will delete:
41 - Unknown files: files marked with "?" by "hg status"
41 - Unknown files: files marked with "?" by "hg status"
42 - Ignored files: files usually ignored by Mercurial because they match
42 - Ignored files: files usually ignored by Mercurial because they match
43 a pattern in a ".hgignore" file
43 a pattern in a ".hgignore" file
44 - Empty directories: in fact Mercurial ignores directories unless they
44 - Empty directories: in fact Mercurial ignores directories unless they
45 contain files under source control managment
45 contain files under source control managment
46 But it will leave untouched:
46 But it will leave untouched:
47 - Unmodified tracked files
47 - Unmodified tracked files
48 - Modified tracked files
48 - Modified tracked files
49 - New files added to the repository (with "hg add")
49 - New files added to the repository (with "hg add")
50
50
51 If directories are given on the command line, only files in these
51 If directories are given on the command line, only files in these
52 directories are considered.
52 directories are considered.
53
53
54 Be careful with purge, you could irreversibly delete some files you
54 Be careful with purge, you could irreversibly delete some files you
55 forgot to add to the repository. If you only want to print the list of
55 forgot to add to the repository. If you only want to print the list of
56 files that this program would delete use the --print option.
56 files that this program would delete use the --print option.
57 '''
57 '''
58 act = not opts['print']
58 act = not opts['print']
59 ignored = bool(opts['all'])
59 ignored = bool(opts['all'])
60 abort_on_err = bool(opts['abort_on_err'])
60 abort_on_err = bool(opts['abort_on_err'])
61 eol = opts['print0'] and '\0' or '\n'
61 eol = opts['print0'] and '\0' or '\n'
62 if eol == '\0':
62 if eol == '\0':
63 # --print0 implies --print
63 # --print0 implies --print
64 act = False
64 act = False
65 force = bool(opts['force'])
65 force = bool(opts['force'])
66
66
67 def error(msg):
67 def error(msg):
68 if abort_on_err:
68 if abort_on_err:
69 raise util.Abort(msg)
69 raise util.Abort(msg)
70 else:
70 else:
71 ui.warn(_('warning: %s\n') % msg)
71 ui.warn(_('warning: %s\n') % msg)
72
72
73 def remove(remove_func, name):
73 def remove(remove_func, name):
74 if act:
74 if act:
75 try:
75 try:
76 remove_func(os.path.join(repo.root, name))
76 remove_func(os.path.join(repo.root, name))
77 except OSError, e:
77 except OSError, e:
78 error(_('%s cannot be removed') % name)
78 error(_('%s cannot be removed') % name)
79 else:
79 else:
80 ui.write('%s%s' % (name, eol))
80 ui.write('%s%s' % (name, eol))
81
81
82 if not force:
82 if not force:
83 _check_fs(ui, repo)
83 _check_fs(ui, repo)
84
84
85 directories = []
85 directories = []
86 files = []
86 files = []
87 missing = []
87 missing = []
88 roots, match, anypats = cmdutil.matchpats(repo, dirs, opts)
88 match = cmdutil.match(repo, dirs, opts)
89 for src, f, st in repo.dirstate.statwalk(roots, match,
89 for src, f, st in repo.dirstate.statwalk(match.files(), match,
90 ignored=ignored, directories=True):
90 ignored=ignored, directories=True):
91 if src == 'd':
91 if src == 'd':
92 directories.append(f)
92 directories.append(f)
93 elif src == 'm':
93 elif src == 'm':
94 missing.append(f)
94 missing.append(f)
95 elif src == 'f' and f not in repo.dirstate:
95 elif src == 'f' and f not in repo.dirstate:
96 files.append(f)
96 files.append(f)
97
97
98 directories.sort()
98 directories.sort()
99
99
100 for f in files:
100 for f in files:
101 if f not in repo.dirstate:
101 if f not in repo.dirstate:
102 ui.note(_('Removing file %s\n') % f)
102 ui.note(_('Removing file %s\n') % f)
103 remove(os.remove, f)
103 remove(os.remove, f)
104
104
105 for f in directories[::-1]:
105 for f in directories[::-1]:
106 if match(f) and not os.listdir(repo.wjoin(f)):
106 if match(f) and not os.listdir(repo.wjoin(f)):
107 ui.note(_('Removing directory %s\n') % f)
107 ui.note(_('Removing directory %s\n') % f)
108 remove(os.rmdir, f)
108 remove(os.rmdir, f)
109
109
110 def _check_fs(ui, repo):
110 def _check_fs(ui, repo):
111 """Abort if there is the chance of having problems with name-mangling fs
111 """Abort if there is the chance of having problems with name-mangling fs
112
112
113 In a name mangling filesystem (e.g. a case insensitive one)
113 In a name mangling filesystem (e.g. a case insensitive one)
114 dirstate.walk() can yield filenames different from the ones
114 dirstate.walk() can yield filenames different from the ones
115 stored in the dirstate. This already confuses the status and
115 stored in the dirstate. This already confuses the status and
116 add commands, but with purge this may cause data loss.
116 add commands, but with purge this may cause data loss.
117
117
118 To prevent this, this function will abort if there are uncommitted
118 To prevent this, this function will abort if there are uncommitted
119 changes.
119 changes.
120 """
120 """
121
121
122 # We can't use (files, match) to do a partial walk here - we wouldn't
122 # We can't use (files, match) to do a partial walk here - we wouldn't
123 # notice a modified README file if the user ran "hg purge readme"
123 # notice a modified README file if the user ran "hg purge readme"
124 modified, added, removed, deleted = repo.status()[:4]
124 modified, added, removed, deleted = repo.status()[:4]
125 if modified or added or removed or deleted:
125 if modified or added or removed or deleted:
126 if not util.checkfolding(repo.path) and not ui.quiet:
126 if not util.checkfolding(repo.path) and not ui.quiet:
127 ui.warn(_("Purging on name mangling filesystems is not "
127 ui.warn(_("Purging on name mangling filesystems is not "
128 "fully supported.\n"))
128 "fully supported.\n"))
129 raise util.Abort(_("outstanding uncommitted changes"))
129 raise util.Abort(_("outstanding uncommitted changes"))
130
130
131 cmdtable = {
131 cmdtable = {
132 'purge|clean':
132 'purge|clean':
133 (purge,
133 (purge,
134 [('a', 'abort-on-err', None, _('abort if an error occurs')),
134 [('a', 'abort-on-err', None, _('abort if an error occurs')),
135 ('', 'all', None, _('purge ignored files too')),
135 ('', 'all', None, _('purge ignored files too')),
136 ('f', 'force', None, _('purge even when there are uncommitted changes')),
136 ('f', 'force', None, _('purge even when there are uncommitted changes')),
137 ('p', 'print', None, _('print the file names instead of deleting them')),
137 ('p', 'print', None, _('print the file names instead of deleting them')),
138 ('0', 'print0', None, _('end filenames with NUL, for use with xargs'
138 ('0', 'print0', None, _('end filenames with NUL, for use with xargs'
139 ' (implies -p)')),
139 ' (implies -p)')),
140 ] + commands.walkopts,
140 ] + commands.walkopts,
141 _('hg purge [OPTION]... [DIR]...'))
141 _('hg purge [OPTION]... [DIR]...'))
142 }
142 }
@@ -1,1187 +1,1183 b''
1 # cmdutil.py - help for command processing in mercurial
1 # cmdutil.py - help for command processing in mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from i18n import _
9 from i18n import _
10 import os, sys, bisect, stat
10 import os, sys, bisect, stat
11 import mdiff, bdiff, util, templater, templatefilters, patch, errno
11 import mdiff, bdiff, util, templater, templatefilters, patch, errno
12 import match as _match
12 import match as _match
13
13
14 revrangesep = ':'
14 revrangesep = ':'
15
15
16 class UnknownCommand(Exception):
16 class UnknownCommand(Exception):
17 """Exception raised if command is not in the command table."""
17 """Exception raised if command is not in the command table."""
18 class AmbiguousCommand(Exception):
18 class AmbiguousCommand(Exception):
19 """Exception raised if command shortcut matches more than one command."""
19 """Exception raised if command shortcut matches more than one command."""
20
20
21 def findpossible(ui, cmd, table):
21 def findpossible(ui, cmd, table):
22 """
22 """
23 Return cmd -> (aliases, command table entry)
23 Return cmd -> (aliases, command table entry)
24 for each matching command.
24 for each matching command.
25 Return debug commands (or their aliases) only if no normal command matches.
25 Return debug commands (or their aliases) only if no normal command matches.
26 """
26 """
27 choice = {}
27 choice = {}
28 debugchoice = {}
28 debugchoice = {}
29 for e in table.keys():
29 for e in table.keys():
30 aliases = e.lstrip("^").split("|")
30 aliases = e.lstrip("^").split("|")
31 found = None
31 found = None
32 if cmd in aliases:
32 if cmd in aliases:
33 found = cmd
33 found = cmd
34 elif not ui.config("ui", "strict"):
34 elif not ui.config("ui", "strict"):
35 for a in aliases:
35 for a in aliases:
36 if a.startswith(cmd):
36 if a.startswith(cmd):
37 found = a
37 found = a
38 break
38 break
39 if found is not None:
39 if found is not None:
40 if aliases[0].startswith("debug") or found.startswith("debug"):
40 if aliases[0].startswith("debug") or found.startswith("debug"):
41 debugchoice[found] = (aliases, table[e])
41 debugchoice[found] = (aliases, table[e])
42 else:
42 else:
43 choice[found] = (aliases, table[e])
43 choice[found] = (aliases, table[e])
44
44
45 if not choice and debugchoice:
45 if not choice and debugchoice:
46 choice = debugchoice
46 choice = debugchoice
47
47
48 return choice
48 return choice
49
49
50 def findcmd(ui, cmd, table):
50 def findcmd(ui, cmd, table):
51 """Return (aliases, command table entry) for command string."""
51 """Return (aliases, command table entry) for command string."""
52 choice = findpossible(ui, cmd, table)
52 choice = findpossible(ui, cmd, table)
53
53
54 if cmd in choice:
54 if cmd in choice:
55 return choice[cmd]
55 return choice[cmd]
56
56
57 if len(choice) > 1:
57 if len(choice) > 1:
58 clist = choice.keys()
58 clist = choice.keys()
59 clist.sort()
59 clist.sort()
60 raise AmbiguousCommand(cmd, clist)
60 raise AmbiguousCommand(cmd, clist)
61
61
62 if choice:
62 if choice:
63 return choice.values()[0]
63 return choice.values()[0]
64
64
65 raise UnknownCommand(cmd)
65 raise UnknownCommand(cmd)
66
66
67 def bail_if_changed(repo):
67 def bail_if_changed(repo):
68 if repo.dirstate.parents()[1] != nullid:
68 if repo.dirstate.parents()[1] != nullid:
69 raise util.Abort(_('outstanding uncommitted merge'))
69 raise util.Abort(_('outstanding uncommitted merge'))
70 modified, added, removed, deleted = repo.status()[:4]
70 modified, added, removed, deleted = repo.status()[:4]
71 if modified or added or removed or deleted:
71 if modified or added or removed or deleted:
72 raise util.Abort(_("outstanding uncommitted changes"))
72 raise util.Abort(_("outstanding uncommitted changes"))
73
73
74 def logmessage(opts):
74 def logmessage(opts):
75 """ get the log message according to -m and -l option """
75 """ get the log message according to -m and -l option """
76 message = opts['message']
76 message = opts['message']
77 logfile = opts['logfile']
77 logfile = opts['logfile']
78
78
79 if message and logfile:
79 if message and logfile:
80 raise util.Abort(_('options --message and --logfile are mutually '
80 raise util.Abort(_('options --message and --logfile are mutually '
81 'exclusive'))
81 'exclusive'))
82 if not message and logfile:
82 if not message and logfile:
83 try:
83 try:
84 if logfile == '-':
84 if logfile == '-':
85 message = sys.stdin.read()
85 message = sys.stdin.read()
86 else:
86 else:
87 message = open(logfile).read()
87 message = open(logfile).read()
88 except IOError, inst:
88 except IOError, inst:
89 raise util.Abort(_("can't read commit message '%s': %s") %
89 raise util.Abort(_("can't read commit message '%s': %s") %
90 (logfile, inst.strerror))
90 (logfile, inst.strerror))
91 return message
91 return message
92
92
93 def loglimit(opts):
93 def loglimit(opts):
94 """get the log limit according to option -l/--limit"""
94 """get the log limit according to option -l/--limit"""
95 limit = opts.get('limit')
95 limit = opts.get('limit')
96 if limit:
96 if limit:
97 try:
97 try:
98 limit = int(limit)
98 limit = int(limit)
99 except ValueError:
99 except ValueError:
100 raise util.Abort(_('limit must be a positive integer'))
100 raise util.Abort(_('limit must be a positive integer'))
101 if limit <= 0: raise util.Abort(_('limit must be positive'))
101 if limit <= 0: raise util.Abort(_('limit must be positive'))
102 else:
102 else:
103 limit = sys.maxint
103 limit = sys.maxint
104 return limit
104 return limit
105
105
106 def setremoteconfig(ui, opts):
106 def setremoteconfig(ui, opts):
107 "copy remote options to ui tree"
107 "copy remote options to ui tree"
108 if opts.get('ssh'):
108 if opts.get('ssh'):
109 ui.setconfig("ui", "ssh", opts['ssh'])
109 ui.setconfig("ui", "ssh", opts['ssh'])
110 if opts.get('remotecmd'):
110 if opts.get('remotecmd'):
111 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
111 ui.setconfig("ui", "remotecmd", opts['remotecmd'])
112
112
113 def revpair(repo, revs):
113 def revpair(repo, revs):
114 '''return pair of nodes, given list of revisions. second item can
114 '''return pair of nodes, given list of revisions. second item can
115 be None, meaning use working dir.'''
115 be None, meaning use working dir.'''
116
116
117 def revfix(repo, val, defval):
117 def revfix(repo, val, defval):
118 if not val and val != 0 and defval is not None:
118 if not val and val != 0 and defval is not None:
119 val = defval
119 val = defval
120 return repo.lookup(val)
120 return repo.lookup(val)
121
121
122 if not revs:
122 if not revs:
123 return repo.dirstate.parents()[0], None
123 return repo.dirstate.parents()[0], None
124 end = None
124 end = None
125 if len(revs) == 1:
125 if len(revs) == 1:
126 if revrangesep in revs[0]:
126 if revrangesep in revs[0]:
127 start, end = revs[0].split(revrangesep, 1)
127 start, end = revs[0].split(revrangesep, 1)
128 start = revfix(repo, start, 0)
128 start = revfix(repo, start, 0)
129 end = revfix(repo, end, repo.changelog.count() - 1)
129 end = revfix(repo, end, repo.changelog.count() - 1)
130 else:
130 else:
131 start = revfix(repo, revs[0], None)
131 start = revfix(repo, revs[0], None)
132 elif len(revs) == 2:
132 elif len(revs) == 2:
133 if revrangesep in revs[0] or revrangesep in revs[1]:
133 if revrangesep in revs[0] or revrangesep in revs[1]:
134 raise util.Abort(_('too many revisions specified'))
134 raise util.Abort(_('too many revisions specified'))
135 start = revfix(repo, revs[0], None)
135 start = revfix(repo, revs[0], None)
136 end = revfix(repo, revs[1], None)
136 end = revfix(repo, revs[1], None)
137 else:
137 else:
138 raise util.Abort(_('too many revisions specified'))
138 raise util.Abort(_('too many revisions specified'))
139 return start, end
139 return start, end
140
140
141 def revrange(repo, revs):
141 def revrange(repo, revs):
142 """Yield revision as strings from a list of revision specifications."""
142 """Yield revision as strings from a list of revision specifications."""
143
143
144 def revfix(repo, val, defval):
144 def revfix(repo, val, defval):
145 if not val and val != 0 and defval is not None:
145 if not val and val != 0 and defval is not None:
146 return defval
146 return defval
147 return repo.changelog.rev(repo.lookup(val))
147 return repo.changelog.rev(repo.lookup(val))
148
148
149 seen, l = {}, []
149 seen, l = {}, []
150 for spec in revs:
150 for spec in revs:
151 if revrangesep in spec:
151 if revrangesep in spec:
152 start, end = spec.split(revrangesep, 1)
152 start, end = spec.split(revrangesep, 1)
153 start = revfix(repo, start, 0)
153 start = revfix(repo, start, 0)
154 end = revfix(repo, end, repo.changelog.count() - 1)
154 end = revfix(repo, end, repo.changelog.count() - 1)
155 step = start > end and -1 or 1
155 step = start > end and -1 or 1
156 for rev in xrange(start, end+step, step):
156 for rev in xrange(start, end+step, step):
157 if rev in seen:
157 if rev in seen:
158 continue
158 continue
159 seen[rev] = 1
159 seen[rev] = 1
160 l.append(rev)
160 l.append(rev)
161 else:
161 else:
162 rev = revfix(repo, spec, None)
162 rev = revfix(repo, spec, None)
163 if rev in seen:
163 if rev in seen:
164 continue
164 continue
165 seen[rev] = 1
165 seen[rev] = 1
166 l.append(rev)
166 l.append(rev)
167
167
168 return l
168 return l
169
169
170 def make_filename(repo, pat, node,
170 def make_filename(repo, pat, node,
171 total=None, seqno=None, revwidth=None, pathname=None):
171 total=None, seqno=None, revwidth=None, pathname=None):
172 node_expander = {
172 node_expander = {
173 'H': lambda: hex(node),
173 'H': lambda: hex(node),
174 'R': lambda: str(repo.changelog.rev(node)),
174 'R': lambda: str(repo.changelog.rev(node)),
175 'h': lambda: short(node),
175 'h': lambda: short(node),
176 }
176 }
177 expander = {
177 expander = {
178 '%': lambda: '%',
178 '%': lambda: '%',
179 'b': lambda: os.path.basename(repo.root),
179 'b': lambda: os.path.basename(repo.root),
180 }
180 }
181
181
182 try:
182 try:
183 if node:
183 if node:
184 expander.update(node_expander)
184 expander.update(node_expander)
185 if node:
185 if node:
186 expander['r'] = (lambda:
186 expander['r'] = (lambda:
187 str(repo.changelog.rev(node)).zfill(revwidth or 0))
187 str(repo.changelog.rev(node)).zfill(revwidth or 0))
188 if total is not None:
188 if total is not None:
189 expander['N'] = lambda: str(total)
189 expander['N'] = lambda: str(total)
190 if seqno is not None:
190 if seqno is not None:
191 expander['n'] = lambda: str(seqno)
191 expander['n'] = lambda: str(seqno)
192 if total is not None and seqno is not None:
192 if total is not None and seqno is not None:
193 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
193 expander['n'] = lambda: str(seqno).zfill(len(str(total)))
194 if pathname is not None:
194 if pathname is not None:
195 expander['s'] = lambda: os.path.basename(pathname)
195 expander['s'] = lambda: os.path.basename(pathname)
196 expander['d'] = lambda: os.path.dirname(pathname) or '.'
196 expander['d'] = lambda: os.path.dirname(pathname) or '.'
197 expander['p'] = lambda: pathname
197 expander['p'] = lambda: pathname
198
198
199 newname = []
199 newname = []
200 patlen = len(pat)
200 patlen = len(pat)
201 i = 0
201 i = 0
202 while i < patlen:
202 while i < patlen:
203 c = pat[i]
203 c = pat[i]
204 if c == '%':
204 if c == '%':
205 i += 1
205 i += 1
206 c = pat[i]
206 c = pat[i]
207 c = expander[c]()
207 c = expander[c]()
208 newname.append(c)
208 newname.append(c)
209 i += 1
209 i += 1
210 return ''.join(newname)
210 return ''.join(newname)
211 except KeyError, inst:
211 except KeyError, inst:
212 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
212 raise util.Abort(_("invalid format spec '%%%s' in output file name") %
213 inst.args[0])
213 inst.args[0])
214
214
215 def make_file(repo, pat, node=None,
215 def make_file(repo, pat, node=None,
216 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
216 total=None, seqno=None, revwidth=None, mode='wb', pathname=None):
217 if not pat or pat == '-':
217 if not pat or pat == '-':
218 return 'w' in mode and sys.stdout or sys.stdin
218 return 'w' in mode and sys.stdout or sys.stdin
219 if hasattr(pat, 'write') and 'w' in mode:
219 if hasattr(pat, 'write') and 'w' in mode:
220 return pat
220 return pat
221 if hasattr(pat, 'read') and 'r' in mode:
221 if hasattr(pat, 'read') and 'r' in mode:
222 return pat
222 return pat
223 return open(make_filename(repo, pat, node, total, seqno, revwidth,
223 return open(make_filename(repo, pat, node, total, seqno, revwidth,
224 pathname),
224 pathname),
225 mode)
225 mode)
226
226
227 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
227 def match(repo, pats=[], opts={}, globbed=False, default='relpath'):
228 if not globbed and default == 'relpath':
228 if not globbed and default == 'relpath':
229 pats = util.expand_glob(pats or [])
229 pats = util.expand_glob(pats or [])
230 m = _match.match(repo.root, repo.getcwd(), pats,
230 m = _match.match(repo.root, repo.getcwd(), pats,
231 opts.get('include'), opts.get('exclude'), default)
231 opts.get('include'), opts.get('exclude'), default)
232 def badfn(f, msg):
232 def badfn(f, msg):
233 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
233 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
234 return False
234 return False
235 m.bad = badfn
235 m.bad = badfn
236 return m
236 return m
237
237
238 def matchpats(repo, pats=[], opts={}, globbed=False, default='relpath'):
239 m = match(repo, pats, opts, globbed, default)
240 return m.files(), m, m.anypats()
241
242 def walk(repo, match, node=None):
238 def walk(repo, match, node=None):
243 for src, fn in repo.walk(node, match):
239 for src, fn in repo.walk(node, match):
244 yield src, fn, match.rel(fn), match.exact(fn)
240 yield src, fn, match.rel(fn), match.exact(fn)
245
241
246 def findrenames(repo, added=None, removed=None, threshold=0.5):
242 def findrenames(repo, added=None, removed=None, threshold=0.5):
247 '''find renamed files -- yields (before, after, score) tuples'''
243 '''find renamed files -- yields (before, after, score) tuples'''
248 if added is None or removed is None:
244 if added is None or removed is None:
249 added, removed = repo.status()[1:3]
245 added, removed = repo.status()[1:3]
250 ctx = repo.changectx()
246 ctx = repo.changectx()
251 for a in added:
247 for a in added:
252 aa = repo.wread(a)
248 aa = repo.wread(a)
253 bestname, bestscore = None, threshold
249 bestname, bestscore = None, threshold
254 for r in removed:
250 for r in removed:
255 rr = ctx.filectx(r).data()
251 rr = ctx.filectx(r).data()
256
252
257 # bdiff.blocks() returns blocks of matching lines
253 # bdiff.blocks() returns blocks of matching lines
258 # count the number of bytes in each
254 # count the number of bytes in each
259 equal = 0
255 equal = 0
260 alines = mdiff.splitnewlines(aa)
256 alines = mdiff.splitnewlines(aa)
261 matches = bdiff.blocks(aa, rr)
257 matches = bdiff.blocks(aa, rr)
262 for x1,x2,y1,y2 in matches:
258 for x1,x2,y1,y2 in matches:
263 for line in alines[x1:x2]:
259 for line in alines[x1:x2]:
264 equal += len(line)
260 equal += len(line)
265
261
266 lengths = len(aa) + len(rr)
262 lengths = len(aa) + len(rr)
267 if lengths:
263 if lengths:
268 myscore = equal*2.0 / lengths
264 myscore = equal*2.0 / lengths
269 if myscore >= bestscore:
265 if myscore >= bestscore:
270 bestname, bestscore = r, myscore
266 bestname, bestscore = r, myscore
271 if bestname:
267 if bestname:
272 yield bestname, a, bestscore
268 yield bestname, a, bestscore
273
269
274 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
270 def addremove(repo, pats=[], opts={}, dry_run=None, similarity=None):
275 if dry_run is None:
271 if dry_run is None:
276 dry_run = opts.get('dry_run')
272 dry_run = opts.get('dry_run')
277 if similarity is None:
273 if similarity is None:
278 similarity = float(opts.get('similarity') or 0)
274 similarity = float(opts.get('similarity') or 0)
279 add, remove = [], []
275 add, remove = [], []
280 mapping = {}
276 mapping = {}
281 m = match(repo, pats, opts)
277 m = match(repo, pats, opts)
282 for src, abs, rel, exact in walk(repo, m):
278 for src, abs, rel, exact in walk(repo, m):
283 target = repo.wjoin(abs)
279 target = repo.wjoin(abs)
284 if src == 'f' and abs not in repo.dirstate:
280 if src == 'f' and abs not in repo.dirstate:
285 add.append(abs)
281 add.append(abs)
286 mapping[abs] = rel, exact
282 mapping[abs] = rel, exact
287 if repo.ui.verbose or not exact:
283 if repo.ui.verbose or not exact:
288 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
284 repo.ui.status(_('adding %s\n') % ((pats and rel) or abs))
289 if repo.dirstate[abs] != 'r' and (not util.lexists(target)
285 if repo.dirstate[abs] != 'r' and (not util.lexists(target)
290 or (os.path.isdir(target) and not os.path.islink(target))):
286 or (os.path.isdir(target) and not os.path.islink(target))):
291 remove.append(abs)
287 remove.append(abs)
292 mapping[abs] = rel, exact
288 mapping[abs] = rel, exact
293 if repo.ui.verbose or not exact:
289 if repo.ui.verbose or not exact:
294 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
290 repo.ui.status(_('removing %s\n') % ((pats and rel) or abs))
295 if not dry_run:
291 if not dry_run:
296 repo.remove(remove)
292 repo.remove(remove)
297 repo.add(add)
293 repo.add(add)
298 if similarity > 0:
294 if similarity > 0:
299 for old, new, score in findrenames(repo, add, remove, similarity):
295 for old, new, score in findrenames(repo, add, remove, similarity):
300 oldrel, oldexact = mapping[old]
296 oldrel, oldexact = mapping[old]
301 newrel, newexact = mapping[new]
297 newrel, newexact = mapping[new]
302 if repo.ui.verbose or not oldexact or not newexact:
298 if repo.ui.verbose or not oldexact or not newexact:
303 repo.ui.status(_('recording removal of %s as rename to %s '
299 repo.ui.status(_('recording removal of %s as rename to %s '
304 '(%d%% similar)\n') %
300 '(%d%% similar)\n') %
305 (oldrel, newrel, score * 100))
301 (oldrel, newrel, score * 100))
306 if not dry_run:
302 if not dry_run:
307 repo.copy(old, new)
303 repo.copy(old, new)
308
304
309 def copy(ui, repo, pats, opts, rename=False):
305 def copy(ui, repo, pats, opts, rename=False):
310 # called with the repo lock held
306 # called with the repo lock held
311 #
307 #
312 # hgsep => pathname that uses "/" to separate directories
308 # hgsep => pathname that uses "/" to separate directories
313 # ossep => pathname that uses os.sep to separate directories
309 # ossep => pathname that uses os.sep to separate directories
314 cwd = repo.getcwd()
310 cwd = repo.getcwd()
315 targets = {}
311 targets = {}
316 after = opts.get("after")
312 after = opts.get("after")
317 dryrun = opts.get("dry_run")
313 dryrun = opts.get("dry_run")
318
314
319 def walkpat(pat):
315 def walkpat(pat):
320 srcs = []
316 srcs = []
321 m = match(repo, [pat], opts, globbed=True)
317 m = match(repo, [pat], opts, globbed=True)
322 for tag, abs, rel, exact in walk(repo, m):
318 for tag, abs, rel, exact in walk(repo, m):
323 state = repo.dirstate[abs]
319 state = repo.dirstate[abs]
324 if state in '?r':
320 if state in '?r':
325 if exact and state == '?':
321 if exact and state == '?':
326 ui.warn(_('%s: not copying - file is not managed\n') % rel)
322 ui.warn(_('%s: not copying - file is not managed\n') % rel)
327 if exact and state == 'r':
323 if exact and state == 'r':
328 ui.warn(_('%s: not copying - file has been marked for'
324 ui.warn(_('%s: not copying - file has been marked for'
329 ' remove\n') % rel)
325 ' remove\n') % rel)
330 continue
326 continue
331 # abs: hgsep
327 # abs: hgsep
332 # rel: ossep
328 # rel: ossep
333 srcs.append((abs, rel, exact))
329 srcs.append((abs, rel, exact))
334 return srcs
330 return srcs
335
331
336 # abssrc: hgsep
332 # abssrc: hgsep
337 # relsrc: ossep
333 # relsrc: ossep
338 # otarget: ossep
334 # otarget: ossep
339 def copyfile(abssrc, relsrc, otarget, exact):
335 def copyfile(abssrc, relsrc, otarget, exact):
340 abstarget = util.canonpath(repo.root, cwd, otarget)
336 abstarget = util.canonpath(repo.root, cwd, otarget)
341 reltarget = repo.pathto(abstarget, cwd)
337 reltarget = repo.pathto(abstarget, cwd)
342 target = repo.wjoin(abstarget)
338 target = repo.wjoin(abstarget)
343 src = repo.wjoin(abssrc)
339 src = repo.wjoin(abssrc)
344 state = repo.dirstate[abstarget]
340 state = repo.dirstate[abstarget]
345
341
346 # check for collisions
342 # check for collisions
347 prevsrc = targets.get(abstarget)
343 prevsrc = targets.get(abstarget)
348 if prevsrc is not None:
344 if prevsrc is not None:
349 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
345 ui.warn(_('%s: not overwriting - %s collides with %s\n') %
350 (reltarget, repo.pathto(abssrc, cwd),
346 (reltarget, repo.pathto(abssrc, cwd),
351 repo.pathto(prevsrc, cwd)))
347 repo.pathto(prevsrc, cwd)))
352 return
348 return
353
349
354 # check for overwrites
350 # check for overwrites
355 exists = os.path.exists(target)
351 exists = os.path.exists(target)
356 if (not after and exists or after and state in 'mn'):
352 if (not after and exists or after and state in 'mn'):
357 if not opts['force']:
353 if not opts['force']:
358 ui.warn(_('%s: not overwriting - file exists\n') %
354 ui.warn(_('%s: not overwriting - file exists\n') %
359 reltarget)
355 reltarget)
360 return
356 return
361
357
362 if after:
358 if after:
363 if not exists:
359 if not exists:
364 return
360 return
365 elif not dryrun:
361 elif not dryrun:
366 try:
362 try:
367 if exists:
363 if exists:
368 os.unlink(target)
364 os.unlink(target)
369 targetdir = os.path.dirname(target) or '.'
365 targetdir = os.path.dirname(target) or '.'
370 if not os.path.isdir(targetdir):
366 if not os.path.isdir(targetdir):
371 os.makedirs(targetdir)
367 os.makedirs(targetdir)
372 util.copyfile(src, target)
368 util.copyfile(src, target)
373 except IOError, inst:
369 except IOError, inst:
374 if inst.errno == errno.ENOENT:
370 if inst.errno == errno.ENOENT:
375 ui.warn(_('%s: deleted in working copy\n') % relsrc)
371 ui.warn(_('%s: deleted in working copy\n') % relsrc)
376 else:
372 else:
377 ui.warn(_('%s: cannot copy - %s\n') %
373 ui.warn(_('%s: cannot copy - %s\n') %
378 (relsrc, inst.strerror))
374 (relsrc, inst.strerror))
379 return True # report a failure
375 return True # report a failure
380
376
381 if ui.verbose or not exact:
377 if ui.verbose or not exact:
382 action = rename and "moving" or "copying"
378 action = rename and "moving" or "copying"
383 ui.status(_('%s %s to %s\n') % (action, relsrc, reltarget))
379 ui.status(_('%s %s to %s\n') % (action, relsrc, reltarget))
384
380
385 targets[abstarget] = abssrc
381 targets[abstarget] = abssrc
386
382
387 # fix up dirstate
383 # fix up dirstate
388 origsrc = repo.dirstate.copied(abssrc) or abssrc
384 origsrc = repo.dirstate.copied(abssrc) or abssrc
389 if abstarget == origsrc: # copying back a copy?
385 if abstarget == origsrc: # copying back a copy?
390 if state not in 'mn' and not dryrun:
386 if state not in 'mn' and not dryrun:
391 repo.dirstate.normallookup(abstarget)
387 repo.dirstate.normallookup(abstarget)
392 else:
388 else:
393 if repo.dirstate[origsrc] == 'a':
389 if repo.dirstate[origsrc] == 'a':
394 if not ui.quiet:
390 if not ui.quiet:
395 ui.warn(_("%s has not been committed yet, so no copy "
391 ui.warn(_("%s has not been committed yet, so no copy "
396 "data will be stored for %s.\n")
392 "data will be stored for %s.\n")
397 % (repo.pathto(origsrc, cwd), reltarget))
393 % (repo.pathto(origsrc, cwd), reltarget))
398 if abstarget not in repo.dirstate and not dryrun:
394 if abstarget not in repo.dirstate and not dryrun:
399 repo.add([abstarget])
395 repo.add([abstarget])
400 elif not dryrun:
396 elif not dryrun:
401 repo.copy(origsrc, abstarget)
397 repo.copy(origsrc, abstarget)
402
398
403 if rename and not dryrun:
399 if rename and not dryrun:
404 repo.remove([abssrc], not after)
400 repo.remove([abssrc], not after)
405
401
406 # pat: ossep
402 # pat: ossep
407 # dest ossep
403 # dest ossep
408 # srcs: list of (hgsep, hgsep, ossep, bool)
404 # srcs: list of (hgsep, hgsep, ossep, bool)
409 # return: function that takes hgsep and returns ossep
405 # return: function that takes hgsep and returns ossep
410 def targetpathfn(pat, dest, srcs):
406 def targetpathfn(pat, dest, srcs):
411 if os.path.isdir(pat):
407 if os.path.isdir(pat):
412 abspfx = util.canonpath(repo.root, cwd, pat)
408 abspfx = util.canonpath(repo.root, cwd, pat)
413 abspfx = util.localpath(abspfx)
409 abspfx = util.localpath(abspfx)
414 if destdirexists:
410 if destdirexists:
415 striplen = len(os.path.split(abspfx)[0])
411 striplen = len(os.path.split(abspfx)[0])
416 else:
412 else:
417 striplen = len(abspfx)
413 striplen = len(abspfx)
418 if striplen:
414 if striplen:
419 striplen += len(os.sep)
415 striplen += len(os.sep)
420 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
416 res = lambda p: os.path.join(dest, util.localpath(p)[striplen:])
421 elif destdirexists:
417 elif destdirexists:
422 res = lambda p: os.path.join(dest,
418 res = lambda p: os.path.join(dest,
423 os.path.basename(util.localpath(p)))
419 os.path.basename(util.localpath(p)))
424 else:
420 else:
425 res = lambda p: dest
421 res = lambda p: dest
426 return res
422 return res
427
423
428 # pat: ossep
424 # pat: ossep
429 # dest ossep
425 # dest ossep
430 # srcs: list of (hgsep, hgsep, ossep, bool)
426 # srcs: list of (hgsep, hgsep, ossep, bool)
431 # return: function that takes hgsep and returns ossep
427 # return: function that takes hgsep and returns ossep
432 def targetpathafterfn(pat, dest, srcs):
428 def targetpathafterfn(pat, dest, srcs):
433 if util.patkind(pat, None)[0]:
429 if util.patkind(pat, None)[0]:
434 # a mercurial pattern
430 # a mercurial pattern
435 res = lambda p: os.path.join(dest,
431 res = lambda p: os.path.join(dest,
436 os.path.basename(util.localpath(p)))
432 os.path.basename(util.localpath(p)))
437 else:
433 else:
438 abspfx = util.canonpath(repo.root, cwd, pat)
434 abspfx = util.canonpath(repo.root, cwd, pat)
439 if len(abspfx) < len(srcs[0][0]):
435 if len(abspfx) < len(srcs[0][0]):
440 # A directory. Either the target path contains the last
436 # A directory. Either the target path contains the last
441 # component of the source path or it does not.
437 # component of the source path or it does not.
442 def evalpath(striplen):
438 def evalpath(striplen):
443 score = 0
439 score = 0
444 for s in srcs:
440 for s in srcs:
445 t = os.path.join(dest, util.localpath(s[0])[striplen:])
441 t = os.path.join(dest, util.localpath(s[0])[striplen:])
446 if os.path.exists(t):
442 if os.path.exists(t):
447 score += 1
443 score += 1
448 return score
444 return score
449
445
450 abspfx = util.localpath(abspfx)
446 abspfx = util.localpath(abspfx)
451 striplen = len(abspfx)
447 striplen = len(abspfx)
452 if striplen:
448 if striplen:
453 striplen += len(os.sep)
449 striplen += len(os.sep)
454 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
450 if os.path.isdir(os.path.join(dest, os.path.split(abspfx)[1])):
455 score = evalpath(striplen)
451 score = evalpath(striplen)
456 striplen1 = len(os.path.split(abspfx)[0])
452 striplen1 = len(os.path.split(abspfx)[0])
457 if striplen1:
453 if striplen1:
458 striplen1 += len(os.sep)
454 striplen1 += len(os.sep)
459 if evalpath(striplen1) > score:
455 if evalpath(striplen1) > score:
460 striplen = striplen1
456 striplen = striplen1
461 res = lambda p: os.path.join(dest,
457 res = lambda p: os.path.join(dest,
462 util.localpath(p)[striplen:])
458 util.localpath(p)[striplen:])
463 else:
459 else:
464 # a file
460 # a file
465 if destdirexists:
461 if destdirexists:
466 res = lambda p: os.path.join(dest,
462 res = lambda p: os.path.join(dest,
467 os.path.basename(util.localpath(p)))
463 os.path.basename(util.localpath(p)))
468 else:
464 else:
469 res = lambda p: dest
465 res = lambda p: dest
470 return res
466 return res
471
467
472
468
473 pats = util.expand_glob(pats)
469 pats = util.expand_glob(pats)
474 if not pats:
470 if not pats:
475 raise util.Abort(_('no source or destination specified'))
471 raise util.Abort(_('no source or destination specified'))
476 if len(pats) == 1:
472 if len(pats) == 1:
477 raise util.Abort(_('no destination specified'))
473 raise util.Abort(_('no destination specified'))
478 dest = pats.pop()
474 dest = pats.pop()
479 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
475 destdirexists = os.path.isdir(dest) and not os.path.islink(dest)
480 if not destdirexists:
476 if not destdirexists:
481 if len(pats) > 1 or util.patkind(pats[0], None)[0]:
477 if len(pats) > 1 or util.patkind(pats[0], None)[0]:
482 raise util.Abort(_('with multiple sources, destination must be an '
478 raise util.Abort(_('with multiple sources, destination must be an '
483 'existing directory'))
479 'existing directory'))
484 if util.endswithsep(dest):
480 if util.endswithsep(dest):
485 raise util.Abort(_('destination %s is not a directory') % dest)
481 raise util.Abort(_('destination %s is not a directory') % dest)
486
482
487 tfn = targetpathfn
483 tfn = targetpathfn
488 if after:
484 if after:
489 tfn = targetpathafterfn
485 tfn = targetpathafterfn
490 copylist = []
486 copylist = []
491 for pat in pats:
487 for pat in pats:
492 srcs = walkpat(pat)
488 srcs = walkpat(pat)
493 if not srcs:
489 if not srcs:
494 continue
490 continue
495 copylist.append((tfn(pat, dest, srcs), srcs))
491 copylist.append((tfn(pat, dest, srcs), srcs))
496 if not copylist:
492 if not copylist:
497 raise util.Abort(_('no files to copy'))
493 raise util.Abort(_('no files to copy'))
498
494
499 errors = 0
495 errors = 0
500 for targetpath, srcs in copylist:
496 for targetpath, srcs in copylist:
501 for abssrc, relsrc, exact in srcs:
497 for abssrc, relsrc, exact in srcs:
502 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
498 if copyfile(abssrc, relsrc, targetpath(abssrc), exact):
503 errors += 1
499 errors += 1
504
500
505 if errors:
501 if errors:
506 ui.warn(_('(consider using --after)\n'))
502 ui.warn(_('(consider using --after)\n'))
507
503
508 return errors
504 return errors
509
505
510 def service(opts, parentfn=None, initfn=None, runfn=None):
506 def service(opts, parentfn=None, initfn=None, runfn=None):
511 '''Run a command as a service.'''
507 '''Run a command as a service.'''
512
508
513 if opts['daemon'] and not opts['daemon_pipefds']:
509 if opts['daemon'] and not opts['daemon_pipefds']:
514 rfd, wfd = os.pipe()
510 rfd, wfd = os.pipe()
515 args = sys.argv[:]
511 args = sys.argv[:]
516 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
512 args.append('--daemon-pipefds=%d,%d' % (rfd, wfd))
517 # Don't pass --cwd to the child process, because we've already
513 # Don't pass --cwd to the child process, because we've already
518 # changed directory.
514 # changed directory.
519 for i in xrange(1,len(args)):
515 for i in xrange(1,len(args)):
520 if args[i].startswith('--cwd='):
516 if args[i].startswith('--cwd='):
521 del args[i]
517 del args[i]
522 break
518 break
523 elif args[i].startswith('--cwd'):
519 elif args[i].startswith('--cwd'):
524 del args[i:i+2]
520 del args[i:i+2]
525 break
521 break
526 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
522 pid = os.spawnvp(os.P_NOWAIT | getattr(os, 'P_DETACH', 0),
527 args[0], args)
523 args[0], args)
528 os.close(wfd)
524 os.close(wfd)
529 os.read(rfd, 1)
525 os.read(rfd, 1)
530 if parentfn:
526 if parentfn:
531 return parentfn(pid)
527 return parentfn(pid)
532 else:
528 else:
533 os._exit(0)
529 os._exit(0)
534
530
535 if initfn:
531 if initfn:
536 initfn()
532 initfn()
537
533
538 if opts['pid_file']:
534 if opts['pid_file']:
539 fp = open(opts['pid_file'], 'w')
535 fp = open(opts['pid_file'], 'w')
540 fp.write(str(os.getpid()) + '\n')
536 fp.write(str(os.getpid()) + '\n')
541 fp.close()
537 fp.close()
542
538
543 if opts['daemon_pipefds']:
539 if opts['daemon_pipefds']:
544 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
540 rfd, wfd = [int(x) for x in opts['daemon_pipefds'].split(',')]
545 os.close(rfd)
541 os.close(rfd)
546 try:
542 try:
547 os.setsid()
543 os.setsid()
548 except AttributeError:
544 except AttributeError:
549 pass
545 pass
550 os.write(wfd, 'y')
546 os.write(wfd, 'y')
551 os.close(wfd)
547 os.close(wfd)
552 sys.stdout.flush()
548 sys.stdout.flush()
553 sys.stderr.flush()
549 sys.stderr.flush()
554 fd = os.open(util.nulldev, os.O_RDWR)
550 fd = os.open(util.nulldev, os.O_RDWR)
555 if fd != 0: os.dup2(fd, 0)
551 if fd != 0: os.dup2(fd, 0)
556 if fd != 1: os.dup2(fd, 1)
552 if fd != 1: os.dup2(fd, 1)
557 if fd != 2: os.dup2(fd, 2)
553 if fd != 2: os.dup2(fd, 2)
558 if fd not in (0, 1, 2): os.close(fd)
554 if fd not in (0, 1, 2): os.close(fd)
559
555
560 if runfn:
556 if runfn:
561 return runfn()
557 return runfn()
562
558
563 class changeset_printer(object):
559 class changeset_printer(object):
564 '''show changeset information when templating not requested.'''
560 '''show changeset information when templating not requested.'''
565
561
566 def __init__(self, ui, repo, patch, buffered):
562 def __init__(self, ui, repo, patch, buffered):
567 self.ui = ui
563 self.ui = ui
568 self.repo = repo
564 self.repo = repo
569 self.buffered = buffered
565 self.buffered = buffered
570 self.patch = patch
566 self.patch = patch
571 self.header = {}
567 self.header = {}
572 self.hunk = {}
568 self.hunk = {}
573 self.lastheader = None
569 self.lastheader = None
574
570
575 def flush(self, rev):
571 def flush(self, rev):
576 if rev in self.header:
572 if rev in self.header:
577 h = self.header[rev]
573 h = self.header[rev]
578 if h != self.lastheader:
574 if h != self.lastheader:
579 self.lastheader = h
575 self.lastheader = h
580 self.ui.write(h)
576 self.ui.write(h)
581 del self.header[rev]
577 del self.header[rev]
582 if rev in self.hunk:
578 if rev in self.hunk:
583 self.ui.write(self.hunk[rev])
579 self.ui.write(self.hunk[rev])
584 del self.hunk[rev]
580 del self.hunk[rev]
585 return 1
581 return 1
586 return 0
582 return 0
587
583
588 def show(self, rev=0, changenode=None, copies=(), **props):
584 def show(self, rev=0, changenode=None, copies=(), **props):
589 if self.buffered:
585 if self.buffered:
590 self.ui.pushbuffer()
586 self.ui.pushbuffer()
591 self._show(rev, changenode, copies, props)
587 self._show(rev, changenode, copies, props)
592 self.hunk[rev] = self.ui.popbuffer()
588 self.hunk[rev] = self.ui.popbuffer()
593 else:
589 else:
594 self._show(rev, changenode, copies, props)
590 self._show(rev, changenode, copies, props)
595
591
596 def _show(self, rev, changenode, copies, props):
592 def _show(self, rev, changenode, copies, props):
597 '''show a single changeset or file revision'''
593 '''show a single changeset or file revision'''
598 log = self.repo.changelog
594 log = self.repo.changelog
599 if changenode is None:
595 if changenode is None:
600 changenode = log.node(rev)
596 changenode = log.node(rev)
601 elif not rev:
597 elif not rev:
602 rev = log.rev(changenode)
598 rev = log.rev(changenode)
603
599
604 if self.ui.quiet:
600 if self.ui.quiet:
605 self.ui.write("%d:%s\n" % (rev, short(changenode)))
601 self.ui.write("%d:%s\n" % (rev, short(changenode)))
606 return
602 return
607
603
608 changes = log.read(changenode)
604 changes = log.read(changenode)
609 date = util.datestr(changes[2])
605 date = util.datestr(changes[2])
610 extra = changes[5]
606 extra = changes[5]
611 branch = extra.get("branch")
607 branch = extra.get("branch")
612
608
613 hexfunc = self.ui.debugflag and hex or short
609 hexfunc = self.ui.debugflag and hex or short
614
610
615 parents = [(p, hexfunc(log.node(p)))
611 parents = [(p, hexfunc(log.node(p)))
616 for p in self._meaningful_parentrevs(log, rev)]
612 for p in self._meaningful_parentrevs(log, rev)]
617
613
618 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
614 self.ui.write(_("changeset: %d:%s\n") % (rev, hexfunc(changenode)))
619
615
620 # don't show the default branch name
616 # don't show the default branch name
621 if branch != 'default':
617 if branch != 'default':
622 branch = util.tolocal(branch)
618 branch = util.tolocal(branch)
623 self.ui.write(_("branch: %s\n") % branch)
619 self.ui.write(_("branch: %s\n") % branch)
624 for tag in self.repo.nodetags(changenode):
620 for tag in self.repo.nodetags(changenode):
625 self.ui.write(_("tag: %s\n") % tag)
621 self.ui.write(_("tag: %s\n") % tag)
626 for parent in parents:
622 for parent in parents:
627 self.ui.write(_("parent: %d:%s\n") % parent)
623 self.ui.write(_("parent: %d:%s\n") % parent)
628
624
629 if self.ui.debugflag:
625 if self.ui.debugflag:
630 self.ui.write(_("manifest: %d:%s\n") %
626 self.ui.write(_("manifest: %d:%s\n") %
631 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
627 (self.repo.manifest.rev(changes[0]), hex(changes[0])))
632 self.ui.write(_("user: %s\n") % changes[1])
628 self.ui.write(_("user: %s\n") % changes[1])
633 self.ui.write(_("date: %s\n") % date)
629 self.ui.write(_("date: %s\n") % date)
634
630
635 if self.ui.debugflag:
631 if self.ui.debugflag:
636 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
632 files = self.repo.status(log.parents(changenode)[0], changenode)[:3]
637 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
633 for key, value in zip([_("files:"), _("files+:"), _("files-:")],
638 files):
634 files):
639 if value:
635 if value:
640 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
636 self.ui.write("%-12s %s\n" % (key, " ".join(value)))
641 elif changes[3] and self.ui.verbose:
637 elif changes[3] and self.ui.verbose:
642 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
638 self.ui.write(_("files: %s\n") % " ".join(changes[3]))
643 if copies and self.ui.verbose:
639 if copies and self.ui.verbose:
644 copies = ['%s (%s)' % c for c in copies]
640 copies = ['%s (%s)' % c for c in copies]
645 self.ui.write(_("copies: %s\n") % ' '.join(copies))
641 self.ui.write(_("copies: %s\n") % ' '.join(copies))
646
642
647 if extra and self.ui.debugflag:
643 if extra and self.ui.debugflag:
648 extraitems = extra.items()
644 extraitems = extra.items()
649 extraitems.sort()
645 extraitems.sort()
650 for key, value in extraitems:
646 for key, value in extraitems:
651 self.ui.write(_("extra: %s=%s\n")
647 self.ui.write(_("extra: %s=%s\n")
652 % (key, value.encode('string_escape')))
648 % (key, value.encode('string_escape')))
653
649
654 description = changes[4].strip()
650 description = changes[4].strip()
655 if description:
651 if description:
656 if self.ui.verbose:
652 if self.ui.verbose:
657 self.ui.write(_("description:\n"))
653 self.ui.write(_("description:\n"))
658 self.ui.write(description)
654 self.ui.write(description)
659 self.ui.write("\n\n")
655 self.ui.write("\n\n")
660 else:
656 else:
661 self.ui.write(_("summary: %s\n") %
657 self.ui.write(_("summary: %s\n") %
662 description.splitlines()[0])
658 description.splitlines()[0])
663 self.ui.write("\n")
659 self.ui.write("\n")
664
660
665 self.showpatch(changenode)
661 self.showpatch(changenode)
666
662
667 def showpatch(self, node):
663 def showpatch(self, node):
668 if self.patch:
664 if self.patch:
669 prev = self.repo.changelog.parents(node)[0]
665 prev = self.repo.changelog.parents(node)[0]
670 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui,
666 patch.diff(self.repo, prev, node, match=self.patch, fp=self.ui,
671 opts=patch.diffopts(self.ui))
667 opts=patch.diffopts(self.ui))
672 self.ui.write("\n")
668 self.ui.write("\n")
673
669
674 def _meaningful_parentrevs(self, log, rev):
670 def _meaningful_parentrevs(self, log, rev):
675 """Return list of meaningful (or all if debug) parentrevs for rev.
671 """Return list of meaningful (or all if debug) parentrevs for rev.
676
672
677 For merges (two non-nullrev revisions) both parents are meaningful.
673 For merges (two non-nullrev revisions) both parents are meaningful.
678 Otherwise the first parent revision is considered meaningful if it
674 Otherwise the first parent revision is considered meaningful if it
679 is not the preceding revision.
675 is not the preceding revision.
680 """
676 """
681 parents = log.parentrevs(rev)
677 parents = log.parentrevs(rev)
682 if not self.ui.debugflag and parents[1] == nullrev:
678 if not self.ui.debugflag and parents[1] == nullrev:
683 if parents[0] >= rev - 1:
679 if parents[0] >= rev - 1:
684 parents = []
680 parents = []
685 else:
681 else:
686 parents = [parents[0]]
682 parents = [parents[0]]
687 return parents
683 return parents
688
684
689
685
690 class changeset_templater(changeset_printer):
686 class changeset_templater(changeset_printer):
691 '''format changeset information.'''
687 '''format changeset information.'''
692
688
693 def __init__(self, ui, repo, patch, mapfile, buffered):
689 def __init__(self, ui, repo, patch, mapfile, buffered):
694 changeset_printer.__init__(self, ui, repo, patch, buffered)
690 changeset_printer.__init__(self, ui, repo, patch, buffered)
695 filters = templatefilters.filters.copy()
691 filters = templatefilters.filters.copy()
696 filters['formatnode'] = (ui.debugflag and (lambda x: x)
692 filters['formatnode'] = (ui.debugflag and (lambda x: x)
697 or (lambda x: x[:12]))
693 or (lambda x: x[:12]))
698 self.t = templater.templater(mapfile, filters,
694 self.t = templater.templater(mapfile, filters,
699 cache={
695 cache={
700 'parent': '{rev}:{node|formatnode} ',
696 'parent': '{rev}:{node|formatnode} ',
701 'manifest': '{rev}:{node|formatnode}',
697 'manifest': '{rev}:{node|formatnode}',
702 'filecopy': '{name} ({source})'})
698 'filecopy': '{name} ({source})'})
703
699
704 def use_template(self, t):
700 def use_template(self, t):
705 '''set template string to use'''
701 '''set template string to use'''
706 self.t.cache['changeset'] = t
702 self.t.cache['changeset'] = t
707
703
708 def _show(self, rev, changenode, copies, props):
704 def _show(self, rev, changenode, copies, props):
709 '''show a single changeset or file revision'''
705 '''show a single changeset or file revision'''
710 log = self.repo.changelog
706 log = self.repo.changelog
711 if changenode is None:
707 if changenode is None:
712 changenode = log.node(rev)
708 changenode = log.node(rev)
713 elif not rev:
709 elif not rev:
714 rev = log.rev(changenode)
710 rev = log.rev(changenode)
715
711
716 changes = log.read(changenode)
712 changes = log.read(changenode)
717
713
718 def showlist(name, values, plural=None, **args):
714 def showlist(name, values, plural=None, **args):
719 '''expand set of values.
715 '''expand set of values.
720 name is name of key in template map.
716 name is name of key in template map.
721 values is list of strings or dicts.
717 values is list of strings or dicts.
722 plural is plural of name, if not simply name + 's'.
718 plural is plural of name, if not simply name + 's'.
723
719
724 expansion works like this, given name 'foo'.
720 expansion works like this, given name 'foo'.
725
721
726 if values is empty, expand 'no_foos'.
722 if values is empty, expand 'no_foos'.
727
723
728 if 'foo' not in template map, return values as a string,
724 if 'foo' not in template map, return values as a string,
729 joined by space.
725 joined by space.
730
726
731 expand 'start_foos'.
727 expand 'start_foos'.
732
728
733 for each value, expand 'foo'. if 'last_foo' in template
729 for each value, expand 'foo'. if 'last_foo' in template
734 map, expand it instead of 'foo' for last key.
730 map, expand it instead of 'foo' for last key.
735
731
736 expand 'end_foos'.
732 expand 'end_foos'.
737 '''
733 '''
738 if plural: names = plural
734 if plural: names = plural
739 else: names = name + 's'
735 else: names = name + 's'
740 if not values:
736 if not values:
741 noname = 'no_' + names
737 noname = 'no_' + names
742 if noname in self.t:
738 if noname in self.t:
743 yield self.t(noname, **args)
739 yield self.t(noname, **args)
744 return
740 return
745 if name not in self.t:
741 if name not in self.t:
746 if isinstance(values[0], str):
742 if isinstance(values[0], str):
747 yield ' '.join(values)
743 yield ' '.join(values)
748 else:
744 else:
749 for v in values:
745 for v in values:
750 yield dict(v, **args)
746 yield dict(v, **args)
751 return
747 return
752 startname = 'start_' + names
748 startname = 'start_' + names
753 if startname in self.t:
749 if startname in self.t:
754 yield self.t(startname, **args)
750 yield self.t(startname, **args)
755 vargs = args.copy()
751 vargs = args.copy()
756 def one(v, tag=name):
752 def one(v, tag=name):
757 try:
753 try:
758 vargs.update(v)
754 vargs.update(v)
759 except (AttributeError, ValueError):
755 except (AttributeError, ValueError):
760 try:
756 try:
761 for a, b in v:
757 for a, b in v:
762 vargs[a] = b
758 vargs[a] = b
763 except ValueError:
759 except ValueError:
764 vargs[name] = v
760 vargs[name] = v
765 return self.t(tag, **vargs)
761 return self.t(tag, **vargs)
766 lastname = 'last_' + name
762 lastname = 'last_' + name
767 if lastname in self.t:
763 if lastname in self.t:
768 last = values.pop()
764 last = values.pop()
769 else:
765 else:
770 last = None
766 last = None
771 for v in values:
767 for v in values:
772 yield one(v)
768 yield one(v)
773 if last is not None:
769 if last is not None:
774 yield one(last, tag=lastname)
770 yield one(last, tag=lastname)
775 endname = 'end_' + names
771 endname = 'end_' + names
776 if endname in self.t:
772 if endname in self.t:
777 yield self.t(endname, **args)
773 yield self.t(endname, **args)
778
774
779 def showbranches(**args):
775 def showbranches(**args):
780 branch = changes[5].get("branch")
776 branch = changes[5].get("branch")
781 if branch != 'default':
777 if branch != 'default':
782 branch = util.tolocal(branch)
778 branch = util.tolocal(branch)
783 return showlist('branch', [branch], plural='branches', **args)
779 return showlist('branch', [branch], plural='branches', **args)
784
780
785 def showparents(**args):
781 def showparents(**args):
786 parents = [[('rev', p), ('node', hex(log.node(p)))]
782 parents = [[('rev', p), ('node', hex(log.node(p)))]
787 for p in self._meaningful_parentrevs(log, rev)]
783 for p in self._meaningful_parentrevs(log, rev)]
788 return showlist('parent', parents, **args)
784 return showlist('parent', parents, **args)
789
785
790 def showtags(**args):
786 def showtags(**args):
791 return showlist('tag', self.repo.nodetags(changenode), **args)
787 return showlist('tag', self.repo.nodetags(changenode), **args)
792
788
793 def showextras(**args):
789 def showextras(**args):
794 extras = changes[5].items()
790 extras = changes[5].items()
795 extras.sort()
791 extras.sort()
796 for key, value in extras:
792 for key, value in extras:
797 args = args.copy()
793 args = args.copy()
798 args.update(dict(key=key, value=value))
794 args.update(dict(key=key, value=value))
799 yield self.t('extra', **args)
795 yield self.t('extra', **args)
800
796
801 def showcopies(**args):
797 def showcopies(**args):
802 c = [{'name': x[0], 'source': x[1]} for x in copies]
798 c = [{'name': x[0], 'source': x[1]} for x in copies]
803 return showlist('file_copy', c, plural='file_copies', **args)
799 return showlist('file_copy', c, plural='file_copies', **args)
804
800
805 files = []
801 files = []
806 def getfiles():
802 def getfiles():
807 if not files:
803 if not files:
808 files[:] = self.repo.status(
804 files[:] = self.repo.status(
809 log.parents(changenode)[0], changenode)[:3]
805 log.parents(changenode)[0], changenode)[:3]
810 return files
806 return files
811 def showfiles(**args):
807 def showfiles(**args):
812 return showlist('file', changes[3], **args)
808 return showlist('file', changes[3], **args)
813 def showmods(**args):
809 def showmods(**args):
814 return showlist('file_mod', getfiles()[0], **args)
810 return showlist('file_mod', getfiles()[0], **args)
815 def showadds(**args):
811 def showadds(**args):
816 return showlist('file_add', getfiles()[1], **args)
812 return showlist('file_add', getfiles()[1], **args)
817 def showdels(**args):
813 def showdels(**args):
818 return showlist('file_del', getfiles()[2], **args)
814 return showlist('file_del', getfiles()[2], **args)
819 def showmanifest(**args):
815 def showmanifest(**args):
820 args = args.copy()
816 args = args.copy()
821 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
817 args.update(dict(rev=self.repo.manifest.rev(changes[0]),
822 node=hex(changes[0])))
818 node=hex(changes[0])))
823 return self.t('manifest', **args)
819 return self.t('manifest', **args)
824
820
825 defprops = {
821 defprops = {
826 'author': changes[1],
822 'author': changes[1],
827 'branches': showbranches,
823 'branches': showbranches,
828 'date': changes[2],
824 'date': changes[2],
829 'desc': changes[4].strip(),
825 'desc': changes[4].strip(),
830 'file_adds': showadds,
826 'file_adds': showadds,
831 'file_dels': showdels,
827 'file_dels': showdels,
832 'file_mods': showmods,
828 'file_mods': showmods,
833 'files': showfiles,
829 'files': showfiles,
834 'file_copies': showcopies,
830 'file_copies': showcopies,
835 'manifest': showmanifest,
831 'manifest': showmanifest,
836 'node': hex(changenode),
832 'node': hex(changenode),
837 'parents': showparents,
833 'parents': showparents,
838 'rev': rev,
834 'rev': rev,
839 'tags': showtags,
835 'tags': showtags,
840 'extras': showextras,
836 'extras': showextras,
841 }
837 }
842 props = props.copy()
838 props = props.copy()
843 props.update(defprops)
839 props.update(defprops)
844
840
845 try:
841 try:
846 if self.ui.debugflag and 'header_debug' in self.t:
842 if self.ui.debugflag and 'header_debug' in self.t:
847 key = 'header_debug'
843 key = 'header_debug'
848 elif self.ui.quiet and 'header_quiet' in self.t:
844 elif self.ui.quiet and 'header_quiet' in self.t:
849 key = 'header_quiet'
845 key = 'header_quiet'
850 elif self.ui.verbose and 'header_verbose' in self.t:
846 elif self.ui.verbose and 'header_verbose' in self.t:
851 key = 'header_verbose'
847 key = 'header_verbose'
852 elif 'header' in self.t:
848 elif 'header' in self.t:
853 key = 'header'
849 key = 'header'
854 else:
850 else:
855 key = ''
851 key = ''
856 if key:
852 if key:
857 h = templater.stringify(self.t(key, **props))
853 h = templater.stringify(self.t(key, **props))
858 if self.buffered:
854 if self.buffered:
859 self.header[rev] = h
855 self.header[rev] = h
860 else:
856 else:
861 self.ui.write(h)
857 self.ui.write(h)
862 if self.ui.debugflag and 'changeset_debug' in self.t:
858 if self.ui.debugflag and 'changeset_debug' in self.t:
863 key = 'changeset_debug'
859 key = 'changeset_debug'
864 elif self.ui.quiet and 'changeset_quiet' in self.t:
860 elif self.ui.quiet and 'changeset_quiet' in self.t:
865 key = 'changeset_quiet'
861 key = 'changeset_quiet'
866 elif self.ui.verbose and 'changeset_verbose' in self.t:
862 elif self.ui.verbose and 'changeset_verbose' in self.t:
867 key = 'changeset_verbose'
863 key = 'changeset_verbose'
868 else:
864 else:
869 key = 'changeset'
865 key = 'changeset'
870 self.ui.write(templater.stringify(self.t(key, **props)))
866 self.ui.write(templater.stringify(self.t(key, **props)))
871 self.showpatch(changenode)
867 self.showpatch(changenode)
872 except KeyError, inst:
868 except KeyError, inst:
873 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
869 raise util.Abort(_("%s: no key named '%s'") % (self.t.mapfile,
874 inst.args[0]))
870 inst.args[0]))
875 except SyntaxError, inst:
871 except SyntaxError, inst:
876 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
872 raise util.Abort(_('%s: %s') % (self.t.mapfile, inst.args[0]))
877
873
878 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
874 def show_changeset(ui, repo, opts, buffered=False, matchfn=False):
879 """show one changeset using template or regular display.
875 """show one changeset using template or regular display.
880
876
881 Display format will be the first non-empty hit of:
877 Display format will be the first non-empty hit of:
882 1. option 'template'
878 1. option 'template'
883 2. option 'style'
879 2. option 'style'
884 3. [ui] setting 'logtemplate'
880 3. [ui] setting 'logtemplate'
885 4. [ui] setting 'style'
881 4. [ui] setting 'style'
886 If all of these values are either the unset or the empty string,
882 If all of these values are either the unset or the empty string,
887 regular display via changeset_printer() is done.
883 regular display via changeset_printer() is done.
888 """
884 """
889 # options
885 # options
890 patch = False
886 patch = False
891 if opts.get('patch'):
887 if opts.get('patch'):
892 patch = matchfn or util.always
888 patch = matchfn or util.always
893
889
894 tmpl = opts.get('template')
890 tmpl = opts.get('template')
895 mapfile = None
891 mapfile = None
896 if tmpl:
892 if tmpl:
897 tmpl = templater.parsestring(tmpl, quoted=False)
893 tmpl = templater.parsestring(tmpl, quoted=False)
898 else:
894 else:
899 mapfile = opts.get('style')
895 mapfile = opts.get('style')
900 # ui settings
896 # ui settings
901 if not mapfile:
897 if not mapfile:
902 tmpl = ui.config('ui', 'logtemplate')
898 tmpl = ui.config('ui', 'logtemplate')
903 if tmpl:
899 if tmpl:
904 tmpl = templater.parsestring(tmpl)
900 tmpl = templater.parsestring(tmpl)
905 else:
901 else:
906 mapfile = ui.config('ui', 'style')
902 mapfile = ui.config('ui', 'style')
907
903
908 if tmpl or mapfile:
904 if tmpl or mapfile:
909 if mapfile:
905 if mapfile:
910 if not os.path.split(mapfile)[0]:
906 if not os.path.split(mapfile)[0]:
911 mapname = (templater.templatepath('map-cmdline.' + mapfile)
907 mapname = (templater.templatepath('map-cmdline.' + mapfile)
912 or templater.templatepath(mapfile))
908 or templater.templatepath(mapfile))
913 if mapname: mapfile = mapname
909 if mapname: mapfile = mapname
914 try:
910 try:
915 t = changeset_templater(ui, repo, patch, mapfile, buffered)
911 t = changeset_templater(ui, repo, patch, mapfile, buffered)
916 except SyntaxError, inst:
912 except SyntaxError, inst:
917 raise util.Abort(inst.args[0])
913 raise util.Abort(inst.args[0])
918 if tmpl: t.use_template(tmpl)
914 if tmpl: t.use_template(tmpl)
919 return t
915 return t
920 return changeset_printer(ui, repo, patch, buffered)
916 return changeset_printer(ui, repo, patch, buffered)
921
917
922 def finddate(ui, repo, date):
918 def finddate(ui, repo, date):
923 """Find the tipmost changeset that matches the given date spec"""
919 """Find the tipmost changeset that matches the given date spec"""
924 df = util.matchdate(date)
920 df = util.matchdate(date)
925 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
921 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
926 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
922 changeiter, matchfn = walkchangerevs(ui, repo, [], get, {'rev':None})
927 results = {}
923 results = {}
928 for st, rev, fns in changeiter:
924 for st, rev, fns in changeiter:
929 if st == 'add':
925 if st == 'add':
930 d = get(rev)[2]
926 d = get(rev)[2]
931 if df(d[0]):
927 if df(d[0]):
932 results[rev] = d
928 results[rev] = d
933 elif st == 'iter':
929 elif st == 'iter':
934 if rev in results:
930 if rev in results:
935 ui.status("Found revision %s from %s\n" %
931 ui.status("Found revision %s from %s\n" %
936 (rev, util.datestr(results[rev])))
932 (rev, util.datestr(results[rev])))
937 return str(rev)
933 return str(rev)
938
934
939 raise util.Abort(_("revision matching date not found"))
935 raise util.Abort(_("revision matching date not found"))
940
936
941 def walkchangerevs(ui, repo, pats, change, opts):
937 def walkchangerevs(ui, repo, pats, change, opts):
942 '''Iterate over files and the revs they changed in.
938 '''Iterate over files and the revs they changed in.
943
939
944 Callers most commonly need to iterate backwards over the history
940 Callers most commonly need to iterate backwards over the history
945 it is interested in. Doing so has awful (quadratic-looking)
941 it is interested in. Doing so has awful (quadratic-looking)
946 performance, so we use iterators in a "windowed" way.
942 performance, so we use iterators in a "windowed" way.
947
943
948 We walk a window of revisions in the desired order. Within the
944 We walk a window of revisions in the desired order. Within the
949 window, we first walk forwards to gather data, then in the desired
945 window, we first walk forwards to gather data, then in the desired
950 order (usually backwards) to display it.
946 order (usually backwards) to display it.
951
947
952 This function returns an (iterator, matchfn) tuple. The iterator
948 This function returns an (iterator, matchfn) tuple. The iterator
953 yields 3-tuples. They will be of one of the following forms:
949 yields 3-tuples. They will be of one of the following forms:
954
950
955 "window", incrementing, lastrev: stepping through a window,
951 "window", incrementing, lastrev: stepping through a window,
956 positive if walking forwards through revs, last rev in the
952 positive if walking forwards through revs, last rev in the
957 sequence iterated over - use to reset state for the current window
953 sequence iterated over - use to reset state for the current window
958
954
959 "add", rev, fns: out-of-order traversal of the given file names
955 "add", rev, fns: out-of-order traversal of the given file names
960 fns, which changed during revision rev - use to gather data for
956 fns, which changed during revision rev - use to gather data for
961 possible display
957 possible display
962
958
963 "iter", rev, None: in-order traversal of the revs earlier iterated
959 "iter", rev, None: in-order traversal of the revs earlier iterated
964 over with "add" - use to display data'''
960 over with "add" - use to display data'''
965
961
966 def increasing_windows(start, end, windowsize=8, sizelimit=512):
962 def increasing_windows(start, end, windowsize=8, sizelimit=512):
967 if start < end:
963 if start < end:
968 while start < end:
964 while start < end:
969 yield start, min(windowsize, end-start)
965 yield start, min(windowsize, end-start)
970 start += windowsize
966 start += windowsize
971 if windowsize < sizelimit:
967 if windowsize < sizelimit:
972 windowsize *= 2
968 windowsize *= 2
973 else:
969 else:
974 while start > end:
970 while start > end:
975 yield start, min(windowsize, start-end-1)
971 yield start, min(windowsize, start-end-1)
976 start -= windowsize
972 start -= windowsize
977 if windowsize < sizelimit:
973 if windowsize < sizelimit:
978 windowsize *= 2
974 windowsize *= 2
979
975
980 m = match(repo, pats, opts)
976 m = match(repo, pats, opts)
981 follow = opts.get('follow') or opts.get('follow_first')
977 follow = opts.get('follow') or opts.get('follow_first')
982
978
983 if repo.changelog.count() == 0:
979 if repo.changelog.count() == 0:
984 return [], m
980 return [], m
985
981
986 if follow:
982 if follow:
987 defrange = '%s:0' % repo.changectx().rev()
983 defrange = '%s:0' % repo.changectx().rev()
988 else:
984 else:
989 defrange = '-1:0'
985 defrange = '-1:0'
990 revs = revrange(repo, opts['rev'] or [defrange])
986 revs = revrange(repo, opts['rev'] or [defrange])
991 wanted = {}
987 wanted = {}
992 slowpath = m.anypats() or opts.get('removed')
988 slowpath = m.anypats() or opts.get('removed')
993 fncache = {}
989 fncache = {}
994
990
995 if not slowpath and not m.files():
991 if not slowpath and not m.files():
996 # No files, no patterns. Display all revs.
992 # No files, no patterns. Display all revs.
997 wanted = dict.fromkeys(revs)
993 wanted = dict.fromkeys(revs)
998 copies = []
994 copies = []
999 if not slowpath:
995 if not slowpath:
1000 # Only files, no patterns. Check the history of each file.
996 # Only files, no patterns. Check the history of each file.
1001 def filerevgen(filelog, node):
997 def filerevgen(filelog, node):
1002 cl_count = repo.changelog.count()
998 cl_count = repo.changelog.count()
1003 if node is None:
999 if node is None:
1004 last = filelog.count() - 1
1000 last = filelog.count() - 1
1005 else:
1001 else:
1006 last = filelog.rev(node)
1002 last = filelog.rev(node)
1007 for i, window in increasing_windows(last, nullrev):
1003 for i, window in increasing_windows(last, nullrev):
1008 revs = []
1004 revs = []
1009 for j in xrange(i - window, i + 1):
1005 for j in xrange(i - window, i + 1):
1010 n = filelog.node(j)
1006 n = filelog.node(j)
1011 revs.append((filelog.linkrev(n),
1007 revs.append((filelog.linkrev(n),
1012 follow and filelog.renamed(n)))
1008 follow and filelog.renamed(n)))
1013 revs.reverse()
1009 revs.reverse()
1014 for rev in revs:
1010 for rev in revs:
1015 # only yield rev for which we have the changelog, it can
1011 # only yield rev for which we have the changelog, it can
1016 # happen while doing "hg log" during a pull or commit
1012 # happen while doing "hg log" during a pull or commit
1017 if rev[0] < cl_count:
1013 if rev[0] < cl_count:
1018 yield rev
1014 yield rev
1019 def iterfiles():
1015 def iterfiles():
1020 for filename in m.files():
1016 for filename in m.files():
1021 yield filename, None
1017 yield filename, None
1022 for filename_node in copies:
1018 for filename_node in copies:
1023 yield filename_node
1019 yield filename_node
1024 minrev, maxrev = min(revs), max(revs)
1020 minrev, maxrev = min(revs), max(revs)
1025 for file_, node in iterfiles():
1021 for file_, node in iterfiles():
1026 filelog = repo.file(file_)
1022 filelog = repo.file(file_)
1027 if filelog.count() == 0:
1023 if filelog.count() == 0:
1028 if node is None:
1024 if node is None:
1029 # A zero count may be a directory or deleted file, so
1025 # A zero count may be a directory or deleted file, so
1030 # try to find matching entries on the slow path.
1026 # try to find matching entries on the slow path.
1031 slowpath = True
1027 slowpath = True
1032 break
1028 break
1033 else:
1029 else:
1034 ui.warn(_('%s:%s copy source revision cannot be found!\n')
1030 ui.warn(_('%s:%s copy source revision cannot be found!\n')
1035 % (file_, short(node)))
1031 % (file_, short(node)))
1036 continue
1032 continue
1037 for rev, copied in filerevgen(filelog, node):
1033 for rev, copied in filerevgen(filelog, node):
1038 if rev <= maxrev:
1034 if rev <= maxrev:
1039 if rev < minrev:
1035 if rev < minrev:
1040 break
1036 break
1041 fncache.setdefault(rev, [])
1037 fncache.setdefault(rev, [])
1042 fncache[rev].append(file_)
1038 fncache[rev].append(file_)
1043 wanted[rev] = 1
1039 wanted[rev] = 1
1044 if follow and copied:
1040 if follow and copied:
1045 copies.append(copied)
1041 copies.append(copied)
1046 if slowpath:
1042 if slowpath:
1047 if follow:
1043 if follow:
1048 raise util.Abort(_('can only follow copies/renames for explicit '
1044 raise util.Abort(_('can only follow copies/renames for explicit '
1049 'file names'))
1045 'file names'))
1050
1046
1051 # The slow path checks files modified in every changeset.
1047 # The slow path checks files modified in every changeset.
1052 def changerevgen():
1048 def changerevgen():
1053 for i, window in increasing_windows(repo.changelog.count()-1,
1049 for i, window in increasing_windows(repo.changelog.count()-1,
1054 nullrev):
1050 nullrev):
1055 for j in xrange(i - window, i + 1):
1051 for j in xrange(i - window, i + 1):
1056 yield j, change(j)[3]
1052 yield j, change(j)[3]
1057
1053
1058 for rev, changefiles in changerevgen():
1054 for rev, changefiles in changerevgen():
1059 matches = filter(m, changefiles)
1055 matches = filter(m, changefiles)
1060 if matches:
1056 if matches:
1061 fncache[rev] = matches
1057 fncache[rev] = matches
1062 wanted[rev] = 1
1058 wanted[rev] = 1
1063
1059
1064 class followfilter:
1060 class followfilter:
1065 def __init__(self, onlyfirst=False):
1061 def __init__(self, onlyfirst=False):
1066 self.startrev = nullrev
1062 self.startrev = nullrev
1067 self.roots = []
1063 self.roots = []
1068 self.onlyfirst = onlyfirst
1064 self.onlyfirst = onlyfirst
1069
1065
1070 def match(self, rev):
1066 def match(self, rev):
1071 def realparents(rev):
1067 def realparents(rev):
1072 if self.onlyfirst:
1068 if self.onlyfirst:
1073 return repo.changelog.parentrevs(rev)[0:1]
1069 return repo.changelog.parentrevs(rev)[0:1]
1074 else:
1070 else:
1075 return filter(lambda x: x != nullrev,
1071 return filter(lambda x: x != nullrev,
1076 repo.changelog.parentrevs(rev))
1072 repo.changelog.parentrevs(rev))
1077
1073
1078 if self.startrev == nullrev:
1074 if self.startrev == nullrev:
1079 self.startrev = rev
1075 self.startrev = rev
1080 return True
1076 return True
1081
1077
1082 if rev > self.startrev:
1078 if rev > self.startrev:
1083 # forward: all descendants
1079 # forward: all descendants
1084 if not self.roots:
1080 if not self.roots:
1085 self.roots.append(self.startrev)
1081 self.roots.append(self.startrev)
1086 for parent in realparents(rev):
1082 for parent in realparents(rev):
1087 if parent in self.roots:
1083 if parent in self.roots:
1088 self.roots.append(rev)
1084 self.roots.append(rev)
1089 return True
1085 return True
1090 else:
1086 else:
1091 # backwards: all parents
1087 # backwards: all parents
1092 if not self.roots:
1088 if not self.roots:
1093 self.roots.extend(realparents(self.startrev))
1089 self.roots.extend(realparents(self.startrev))
1094 if rev in self.roots:
1090 if rev in self.roots:
1095 self.roots.remove(rev)
1091 self.roots.remove(rev)
1096 self.roots.extend(realparents(rev))
1092 self.roots.extend(realparents(rev))
1097 return True
1093 return True
1098
1094
1099 return False
1095 return False
1100
1096
1101 # it might be worthwhile to do this in the iterator if the rev range
1097 # it might be worthwhile to do this in the iterator if the rev range
1102 # is descending and the prune args are all within that range
1098 # is descending and the prune args are all within that range
1103 for rev in opts.get('prune', ()):
1099 for rev in opts.get('prune', ()):
1104 rev = repo.changelog.rev(repo.lookup(rev))
1100 rev = repo.changelog.rev(repo.lookup(rev))
1105 ff = followfilter()
1101 ff = followfilter()
1106 stop = min(revs[0], revs[-1])
1102 stop = min(revs[0], revs[-1])
1107 for x in xrange(rev, stop-1, -1):
1103 for x in xrange(rev, stop-1, -1):
1108 if ff.match(x) and x in wanted:
1104 if ff.match(x) and x in wanted:
1109 del wanted[x]
1105 del wanted[x]
1110
1106
1111 def iterate():
1107 def iterate():
1112 if follow and not m.files():
1108 if follow and not m.files():
1113 ff = followfilter(onlyfirst=opts.get('follow_first'))
1109 ff = followfilter(onlyfirst=opts.get('follow_first'))
1114 def want(rev):
1110 def want(rev):
1115 if ff.match(rev) and rev in wanted:
1111 if ff.match(rev) and rev in wanted:
1116 return True
1112 return True
1117 return False
1113 return False
1118 else:
1114 else:
1119 def want(rev):
1115 def want(rev):
1120 return rev in wanted
1116 return rev in wanted
1121
1117
1122 for i, window in increasing_windows(0, len(revs)):
1118 for i, window in increasing_windows(0, len(revs)):
1123 yield 'window', revs[0] < revs[-1], revs[-1]
1119 yield 'window', revs[0] < revs[-1], revs[-1]
1124 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1120 nrevs = [rev for rev in revs[i:i+window] if want(rev)]
1125 srevs = list(nrevs)
1121 srevs = list(nrevs)
1126 srevs.sort()
1122 srevs.sort()
1127 for rev in srevs:
1123 for rev in srevs:
1128 fns = fncache.get(rev)
1124 fns = fncache.get(rev)
1129 if not fns:
1125 if not fns:
1130 def fns_generator():
1126 def fns_generator():
1131 for f in change(rev)[3]:
1127 for f in change(rev)[3]:
1132 if m(f):
1128 if m(f):
1133 yield f
1129 yield f
1134 fns = fns_generator()
1130 fns = fns_generator()
1135 yield 'add', rev, fns
1131 yield 'add', rev, fns
1136 for rev in nrevs:
1132 for rev in nrevs:
1137 yield 'iter', rev, None
1133 yield 'iter', rev, None
1138 return iterate(), m
1134 return iterate(), m
1139
1135
1140 def commit(ui, repo, commitfunc, pats, opts):
1136 def commit(ui, repo, commitfunc, pats, opts):
1141 '''commit the specified files or all outstanding changes'''
1137 '''commit the specified files or all outstanding changes'''
1142 date = opts.get('date')
1138 date = opts.get('date')
1143 if date:
1139 if date:
1144 opts['date'] = util.parsedate(date)
1140 opts['date'] = util.parsedate(date)
1145 message = logmessage(opts)
1141 message = logmessage(opts)
1146
1142
1147 # extract addremove carefully -- this function can be called from a command
1143 # extract addremove carefully -- this function can be called from a command
1148 # that doesn't support addremove
1144 # that doesn't support addremove
1149 if opts.get('addremove'):
1145 if opts.get('addremove'):
1150 addremove(repo, pats, opts)
1146 addremove(repo, pats, opts)
1151
1147
1152 m = match(repo, pats, opts)
1148 m = match(repo, pats, opts)
1153 if pats:
1149 if pats:
1154 status = repo.status(files=m.files(), match=m)
1150 status = repo.status(files=m.files(), match=m)
1155 modified, added, removed, deleted, unknown = status[:5]
1151 modified, added, removed, deleted, unknown = status[:5]
1156 files = modified + added + removed
1152 files = modified + added + removed
1157 slist = None
1153 slist = None
1158 for f in m.files():
1154 for f in m.files():
1159 if f == '.':
1155 if f == '.':
1160 continue
1156 continue
1161 if f not in files:
1157 if f not in files:
1162 rf = repo.wjoin(f)
1158 rf = repo.wjoin(f)
1163 rel = repo.pathto(f)
1159 rel = repo.pathto(f)
1164 try:
1160 try:
1165 mode = os.lstat(rf)[stat.ST_MODE]
1161 mode = os.lstat(rf)[stat.ST_MODE]
1166 except OSError:
1162 except OSError:
1167 raise util.Abort(_("file %s not found!") % rel)
1163 raise util.Abort(_("file %s not found!") % rel)
1168 if stat.S_ISDIR(mode):
1164 if stat.S_ISDIR(mode):
1169 name = f + '/'
1165 name = f + '/'
1170 if slist is None:
1166 if slist is None:
1171 slist = list(files)
1167 slist = list(files)
1172 slist.sort()
1168 slist.sort()
1173 i = bisect.bisect(slist, name)
1169 i = bisect.bisect(slist, name)
1174 if i >= len(slist) or not slist[i].startswith(name):
1170 if i >= len(slist) or not slist[i].startswith(name):
1175 raise util.Abort(_("no match under directory %s!")
1171 raise util.Abort(_("no match under directory %s!")
1176 % rel)
1172 % rel)
1177 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
1173 elif not (stat.S_ISREG(mode) or stat.S_ISLNK(mode)):
1178 raise util.Abort(_("can't commit %s: "
1174 raise util.Abort(_("can't commit %s: "
1179 "unsupported file type!") % rel)
1175 "unsupported file type!") % rel)
1180 elif f not in repo.dirstate:
1176 elif f not in repo.dirstate:
1181 raise util.Abort(_("file %s not tracked!") % rel)
1177 raise util.Abort(_("file %s not tracked!") % rel)
1182 else:
1178 else:
1183 files = []
1179 files = []
1184 try:
1180 try:
1185 return commitfunc(ui, repo, files, message, match, opts)
1181 return commitfunc(ui, repo, files, message, m, opts)
1186 except ValueError, inst:
1182 except ValueError, inst:
1187 raise util.Abort(str(inst))
1183 raise util.Abort(str(inst))
@@ -1,3335 +1,3332 b''
1 # commands.py - command processing for mercurial
1 # commands.py - command processing for mercurial
2 #
2 #
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
3 # Copyright 2005-2007 Matt Mackall <mpm@selenic.com>
4 #
4 #
5 # This software may be used and distributed according to the terms
5 # This software may be used and distributed according to the terms
6 # of the GNU General Public License, incorporated herein by reference.
6 # of the GNU General Public License, incorporated herein by reference.
7
7
8 from node import hex, nullid, nullrev, short
8 from node import hex, nullid, nullrev, short
9 from repo import RepoError, NoCapability
9 from repo import RepoError, NoCapability
10 from i18n import _
10 from i18n import _
11 import os, re, sys, urllib
11 import os, re, sys, urllib
12 import hg, util, revlog, bundlerepo, extensions, copies
12 import hg, util, revlog, bundlerepo, extensions, copies
13 import difflib, patch, time, help, mdiff, tempfile
13 import difflib, patch, time, help, mdiff, tempfile
14 import version, socket
14 import version, socket
15 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
15 import archival, changegroup, cmdutil, hgweb.server, sshserver, hbisect
16 import merge as merge_
16 import merge as merge_
17
17
18 # Commands start here, listed alphabetically
18 # Commands start here, listed alphabetically
19
19
20 def add(ui, repo, *pats, **opts):
20 def add(ui, repo, *pats, **opts):
21 """add the specified files on the next commit
21 """add the specified files on the next commit
22
22
23 Schedule files to be version controlled and added to the repository.
23 Schedule files to be version controlled and added to the repository.
24
24
25 The files will be added to the repository at the next commit. To
25 The files will be added to the repository at the next commit. To
26 undo an add before that, see hg revert.
26 undo an add before that, see hg revert.
27
27
28 If no names are given, add all files in the repository.
28 If no names are given, add all files in the repository.
29 """
29 """
30
30
31 rejected = None
31 rejected = None
32 exacts = {}
32 exacts = {}
33 names = []
33 names = []
34 m = cmdutil.match(repo, pats, opts)
34 m = cmdutil.match(repo, pats, opts)
35 m.bad = lambda x,y: True
35 m.bad = lambda x,y: True
36 for src, abs, rel, exact in cmdutil.walk(repo, m):
36 for src, abs, rel, exact in cmdutil.walk(repo, m):
37 if exact:
37 if exact:
38 if ui.verbose:
38 if ui.verbose:
39 ui.status(_('adding %s\n') % rel)
39 ui.status(_('adding %s\n') % rel)
40 names.append(abs)
40 names.append(abs)
41 exacts[abs] = 1
41 exacts[abs] = 1
42 elif abs not in repo.dirstate:
42 elif abs not in repo.dirstate:
43 ui.status(_('adding %s\n') % rel)
43 ui.status(_('adding %s\n') % rel)
44 names.append(abs)
44 names.append(abs)
45 if not opts.get('dry_run'):
45 if not opts.get('dry_run'):
46 rejected = repo.add(names)
46 rejected = repo.add(names)
47 rejected = [p for p in rejected if p in exacts]
47 rejected = [p for p in rejected if p in exacts]
48 return rejected and 1 or 0
48 return rejected and 1 or 0
49
49
50 def addremove(ui, repo, *pats, **opts):
50 def addremove(ui, repo, *pats, **opts):
51 """add all new files, delete all missing files
51 """add all new files, delete all missing files
52
52
53 Add all new files and remove all missing files from the repository.
53 Add all new files and remove all missing files from the repository.
54
54
55 New files are ignored if they match any of the patterns in .hgignore. As
55 New files are ignored if they match any of the patterns in .hgignore. As
56 with add, these changes take effect at the next commit.
56 with add, these changes take effect at the next commit.
57
57
58 Use the -s option to detect renamed files. With a parameter > 0,
58 Use the -s option to detect renamed files. With a parameter > 0,
59 this compares every removed file with every added file and records
59 this compares every removed file with every added file and records
60 those similar enough as renames. This option takes a percentage
60 those similar enough as renames. This option takes a percentage
61 between 0 (disabled) and 100 (files must be identical) as its
61 between 0 (disabled) and 100 (files must be identical) as its
62 parameter. Detecting renamed files this way can be expensive.
62 parameter. Detecting renamed files this way can be expensive.
63 """
63 """
64 try:
64 try:
65 sim = float(opts.get('similarity') or 0)
65 sim = float(opts.get('similarity') or 0)
66 except ValueError:
66 except ValueError:
67 raise util.Abort(_('similarity must be a number'))
67 raise util.Abort(_('similarity must be a number'))
68 if sim < 0 or sim > 100:
68 if sim < 0 or sim > 100:
69 raise util.Abort(_('similarity must be between 0 and 100'))
69 raise util.Abort(_('similarity must be between 0 and 100'))
70 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
70 return cmdutil.addremove(repo, pats, opts, similarity=sim/100.)
71
71
72 def annotate(ui, repo, *pats, **opts):
72 def annotate(ui, repo, *pats, **opts):
73 """show changeset information per file line
73 """show changeset information per file line
74
74
75 List changes in files, showing the revision id responsible for each line
75 List changes in files, showing the revision id responsible for each line
76
76
77 This command is useful to discover who did a change or when a change took
77 This command is useful to discover who did a change or when a change took
78 place.
78 place.
79
79
80 Without the -a option, annotate will avoid processing files it
80 Without the -a option, annotate will avoid processing files it
81 detects as binary. With -a, annotate will generate an annotation
81 detects as binary. With -a, annotate will generate an annotation
82 anyway, probably with undesirable results.
82 anyway, probably with undesirable results.
83 """
83 """
84 datefunc = ui.quiet and util.shortdate or util.datestr
84 datefunc = ui.quiet and util.shortdate or util.datestr
85 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
85 getdate = util.cachefunc(lambda x: datefunc(x[0].date()))
86
86
87 if not pats:
87 if not pats:
88 raise util.Abort(_('at least one file name or pattern required'))
88 raise util.Abort(_('at least one file name or pattern required'))
89
89
90 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
90 opmap = [('user', lambda x: ui.shortuser(x[0].user())),
91 ('number', lambda x: str(x[0].rev())),
91 ('number', lambda x: str(x[0].rev())),
92 ('changeset', lambda x: short(x[0].node())),
92 ('changeset', lambda x: short(x[0].node())),
93 ('date', getdate),
93 ('date', getdate),
94 ('follow', lambda x: x[0].path()),
94 ('follow', lambda x: x[0].path()),
95 ]
95 ]
96
96
97 if (not opts['user'] and not opts['changeset'] and not opts['date']
97 if (not opts['user'] and not opts['changeset'] and not opts['date']
98 and not opts['follow']):
98 and not opts['follow']):
99 opts['number'] = 1
99 opts['number'] = 1
100
100
101 linenumber = opts.get('line_number') is not None
101 linenumber = opts.get('line_number') is not None
102 if (linenumber and (not opts['changeset']) and (not opts['number'])):
102 if (linenumber and (not opts['changeset']) and (not opts['number'])):
103 raise util.Abort(_('at least one of -n/-c is required for -l'))
103 raise util.Abort(_('at least one of -n/-c is required for -l'))
104
104
105 funcmap = [func for op, func in opmap if opts.get(op)]
105 funcmap = [func for op, func in opmap if opts.get(op)]
106 if linenumber:
106 if linenumber:
107 lastfunc = funcmap[-1]
107 lastfunc = funcmap[-1]
108 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
108 funcmap[-1] = lambda x: "%s:%s" % (lastfunc(x), x[1])
109
109
110 ctx = repo.changectx(opts['rev'])
110 ctx = repo.changectx(opts['rev'])
111
111
112 m = cmdutil.match(repo, pats, opts)
112 m = cmdutil.match(repo, pats, opts)
113 for src, abs, rel, exact in cmdutil.walk(repo, m, ctx.node()):
113 for src, abs, rel, exact in cmdutil.walk(repo, m, ctx.node()):
114 fctx = ctx.filectx(abs)
114 fctx = ctx.filectx(abs)
115 if not opts['text'] and util.binary(fctx.data()):
115 if not opts['text'] and util.binary(fctx.data()):
116 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
116 ui.write(_("%s: binary file\n") % ((pats and rel) or abs))
117 continue
117 continue
118
118
119 lines = fctx.annotate(follow=opts.get('follow'),
119 lines = fctx.annotate(follow=opts.get('follow'),
120 linenumber=linenumber)
120 linenumber=linenumber)
121 pieces = []
121 pieces = []
122
122
123 for f in funcmap:
123 for f in funcmap:
124 l = [f(n) for n, dummy in lines]
124 l = [f(n) for n, dummy in lines]
125 if l:
125 if l:
126 m = max(map(len, l))
126 m = max(map(len, l))
127 pieces.append(["%*s" % (m, x) for x in l])
127 pieces.append(["%*s" % (m, x) for x in l])
128
128
129 if pieces:
129 if pieces:
130 for p, l in zip(zip(*pieces), lines):
130 for p, l in zip(zip(*pieces), lines):
131 ui.write("%s: %s" % (" ".join(p), l[1]))
131 ui.write("%s: %s" % (" ".join(p), l[1]))
132
132
133 def archive(ui, repo, dest, **opts):
133 def archive(ui, repo, dest, **opts):
134 '''create unversioned archive of a repository revision
134 '''create unversioned archive of a repository revision
135
135
136 By default, the revision used is the parent of the working
136 By default, the revision used is the parent of the working
137 directory; use "-r" to specify a different revision.
137 directory; use "-r" to specify a different revision.
138
138
139 To specify the type of archive to create, use "-t". Valid
139 To specify the type of archive to create, use "-t". Valid
140 types are:
140 types are:
141
141
142 "files" (default): a directory full of files
142 "files" (default): a directory full of files
143 "tar": tar archive, uncompressed
143 "tar": tar archive, uncompressed
144 "tbz2": tar archive, compressed using bzip2
144 "tbz2": tar archive, compressed using bzip2
145 "tgz": tar archive, compressed using gzip
145 "tgz": tar archive, compressed using gzip
146 "uzip": zip archive, uncompressed
146 "uzip": zip archive, uncompressed
147 "zip": zip archive, compressed using deflate
147 "zip": zip archive, compressed using deflate
148
148
149 The exact name of the destination archive or directory is given
149 The exact name of the destination archive or directory is given
150 using a format string; see "hg help export" for details.
150 using a format string; see "hg help export" for details.
151
151
152 Each member added to an archive file has a directory prefix
152 Each member added to an archive file has a directory prefix
153 prepended. Use "-p" to specify a format string for the prefix.
153 prepended. Use "-p" to specify a format string for the prefix.
154 The default is the basename of the archive, with suffixes removed.
154 The default is the basename of the archive, with suffixes removed.
155 '''
155 '''
156
156
157 ctx = repo.changectx(opts['rev'])
157 ctx = repo.changectx(opts['rev'])
158 if not ctx:
158 if not ctx:
159 raise util.Abort(_('repository has no revisions'))
159 raise util.Abort(_('repository has no revisions'))
160 node = ctx.node()
160 node = ctx.node()
161 dest = cmdutil.make_filename(repo, dest, node)
161 dest = cmdutil.make_filename(repo, dest, node)
162 if os.path.realpath(dest) == repo.root:
162 if os.path.realpath(dest) == repo.root:
163 raise util.Abort(_('repository root cannot be destination'))
163 raise util.Abort(_('repository root cannot be destination'))
164 matchfn = cmdutil.match(repo, [], opts)
164 matchfn = cmdutil.match(repo, [], opts)
165 kind = opts.get('type') or 'files'
165 kind = opts.get('type') or 'files'
166 prefix = opts['prefix']
166 prefix = opts['prefix']
167 if dest == '-':
167 if dest == '-':
168 if kind == 'files':
168 if kind == 'files':
169 raise util.Abort(_('cannot archive plain files to stdout'))
169 raise util.Abort(_('cannot archive plain files to stdout'))
170 dest = sys.stdout
170 dest = sys.stdout
171 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
171 if not prefix: prefix = os.path.basename(repo.root) + '-%h'
172 prefix = cmdutil.make_filename(repo, prefix, node)
172 prefix = cmdutil.make_filename(repo, prefix, node)
173 archival.archive(repo, dest, node, kind, not opts['no_decode'],
173 archival.archive(repo, dest, node, kind, not opts['no_decode'],
174 matchfn, prefix)
174 matchfn, prefix)
175
175
176 def backout(ui, repo, node=None, rev=None, **opts):
176 def backout(ui, repo, node=None, rev=None, **opts):
177 '''reverse effect of earlier changeset
177 '''reverse effect of earlier changeset
178
178
179 Commit the backed out changes as a new changeset. The new
179 Commit the backed out changes as a new changeset. The new
180 changeset is a child of the backed out changeset.
180 changeset is a child of the backed out changeset.
181
181
182 If you back out a changeset other than the tip, a new head is
182 If you back out a changeset other than the tip, a new head is
183 created. This head will be the new tip and you should merge this
183 created. This head will be the new tip and you should merge this
184 backout changeset with another head (current one by default).
184 backout changeset with another head (current one by default).
185
185
186 The --merge option remembers the parent of the working directory
186 The --merge option remembers the parent of the working directory
187 before starting the backout, then merges the new head with that
187 before starting the backout, then merges the new head with that
188 changeset afterwards. This saves you from doing the merge by
188 changeset afterwards. This saves you from doing the merge by
189 hand. The result of this merge is not committed, as for a normal
189 hand. The result of this merge is not committed, as for a normal
190 merge.
190 merge.
191
191
192 See 'hg help dates' for a list of formats valid for -d/--date.
192 See 'hg help dates' for a list of formats valid for -d/--date.
193 '''
193 '''
194 if rev and node:
194 if rev and node:
195 raise util.Abort(_("please specify just one revision"))
195 raise util.Abort(_("please specify just one revision"))
196
196
197 if not rev:
197 if not rev:
198 rev = node
198 rev = node
199
199
200 if not rev:
200 if not rev:
201 raise util.Abort(_("please specify a revision to backout"))
201 raise util.Abort(_("please specify a revision to backout"))
202
202
203 date = opts.get('date')
203 date = opts.get('date')
204 if date:
204 if date:
205 opts['date'] = util.parsedate(date)
205 opts['date'] = util.parsedate(date)
206
206
207 cmdutil.bail_if_changed(repo)
207 cmdutil.bail_if_changed(repo)
208 node = repo.lookup(rev)
208 node = repo.lookup(rev)
209
209
210 op1, op2 = repo.dirstate.parents()
210 op1, op2 = repo.dirstate.parents()
211 a = repo.changelog.ancestor(op1, node)
211 a = repo.changelog.ancestor(op1, node)
212 if a != node:
212 if a != node:
213 raise util.Abort(_('cannot back out change on a different branch'))
213 raise util.Abort(_('cannot back out change on a different branch'))
214
214
215 p1, p2 = repo.changelog.parents(node)
215 p1, p2 = repo.changelog.parents(node)
216 if p1 == nullid:
216 if p1 == nullid:
217 raise util.Abort(_('cannot back out a change with no parents'))
217 raise util.Abort(_('cannot back out a change with no parents'))
218 if p2 != nullid:
218 if p2 != nullid:
219 if not opts['parent']:
219 if not opts['parent']:
220 raise util.Abort(_('cannot back out a merge changeset without '
220 raise util.Abort(_('cannot back out a merge changeset without '
221 '--parent'))
221 '--parent'))
222 p = repo.lookup(opts['parent'])
222 p = repo.lookup(opts['parent'])
223 if p not in (p1, p2):
223 if p not in (p1, p2):
224 raise util.Abort(_('%s is not a parent of %s') %
224 raise util.Abort(_('%s is not a parent of %s') %
225 (short(p), short(node)))
225 (short(p), short(node)))
226 parent = p
226 parent = p
227 else:
227 else:
228 if opts['parent']:
228 if opts['parent']:
229 raise util.Abort(_('cannot use --parent on non-merge changeset'))
229 raise util.Abort(_('cannot use --parent on non-merge changeset'))
230 parent = p1
230 parent = p1
231
231
232 # the backout should appear on the same branch
232 # the backout should appear on the same branch
233 branch = repo.dirstate.branch()
233 branch = repo.dirstate.branch()
234 hg.clean(repo, node, show_stats=False)
234 hg.clean(repo, node, show_stats=False)
235 repo.dirstate.setbranch(branch)
235 repo.dirstate.setbranch(branch)
236 revert_opts = opts.copy()
236 revert_opts = opts.copy()
237 revert_opts['date'] = None
237 revert_opts['date'] = None
238 revert_opts['all'] = True
238 revert_opts['all'] = True
239 revert_opts['rev'] = hex(parent)
239 revert_opts['rev'] = hex(parent)
240 revert_opts['no_backup'] = None
240 revert_opts['no_backup'] = None
241 revert(ui, repo, **revert_opts)
241 revert(ui, repo, **revert_opts)
242 commit_opts = opts.copy()
242 commit_opts = opts.copy()
243 commit_opts['addremove'] = False
243 commit_opts['addremove'] = False
244 if not commit_opts['message'] and not commit_opts['logfile']:
244 if not commit_opts['message'] and not commit_opts['logfile']:
245 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
245 commit_opts['message'] = _("Backed out changeset %s") % (short(node))
246 commit_opts['force_editor'] = True
246 commit_opts['force_editor'] = True
247 commit(ui, repo, **commit_opts)
247 commit(ui, repo, **commit_opts)
248 def nice(node):
248 def nice(node):
249 return '%d:%s' % (repo.changelog.rev(node), short(node))
249 return '%d:%s' % (repo.changelog.rev(node), short(node))
250 ui.status(_('changeset %s backs out changeset %s\n') %
250 ui.status(_('changeset %s backs out changeset %s\n') %
251 (nice(repo.changelog.tip()), nice(node)))
251 (nice(repo.changelog.tip()), nice(node)))
252 if op1 != node:
252 if op1 != node:
253 hg.clean(repo, op1, show_stats=False)
253 hg.clean(repo, op1, show_stats=False)
254 if opts['merge']:
254 if opts['merge']:
255 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
255 ui.status(_('merging with changeset %s\n') % nice(repo.changelog.tip()))
256 hg.merge(repo, hex(repo.changelog.tip()))
256 hg.merge(repo, hex(repo.changelog.tip()))
257 else:
257 else:
258 ui.status(_('the backout changeset is a new head - '
258 ui.status(_('the backout changeset is a new head - '
259 'do not forget to merge\n'))
259 'do not forget to merge\n'))
260 ui.status(_('(use "backout --merge" '
260 ui.status(_('(use "backout --merge" '
261 'if you want to auto-merge)\n'))
261 'if you want to auto-merge)\n'))
262
262
263 def bisect(ui, repo, rev=None, extra=None,
263 def bisect(ui, repo, rev=None, extra=None,
264 reset=None, good=None, bad=None, skip=None, noupdate=None):
264 reset=None, good=None, bad=None, skip=None, noupdate=None):
265 """subdivision search of changesets
265 """subdivision search of changesets
266
266
267 This command helps to find changesets which introduce problems.
267 This command helps to find changesets which introduce problems.
268 To use, mark the earliest changeset you know exhibits the problem
268 To use, mark the earliest changeset you know exhibits the problem
269 as bad, then mark the latest changeset which is free from the
269 as bad, then mark the latest changeset which is free from the
270 problem as good. Bisect will update your working directory to a
270 problem as good. Bisect will update your working directory to a
271 revision for testing. Once you have performed tests, mark the
271 revision for testing. Once you have performed tests, mark the
272 working directory as bad or good and bisect will either update to
272 working directory as bad or good and bisect will either update to
273 another candidate changeset or announce that it has found the bad
273 another candidate changeset or announce that it has found the bad
274 revision.
274 revision.
275 """
275 """
276 # backward compatibility
276 # backward compatibility
277 if rev in "good bad reset init".split():
277 if rev in "good bad reset init".split():
278 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
278 ui.warn(_("(use of 'hg bisect <cmd>' is deprecated)\n"))
279 cmd, rev, extra = rev, extra, None
279 cmd, rev, extra = rev, extra, None
280 if cmd == "good":
280 if cmd == "good":
281 good = True
281 good = True
282 elif cmd == "bad":
282 elif cmd == "bad":
283 bad = True
283 bad = True
284 else:
284 else:
285 reset = True
285 reset = True
286 elif extra or good + bad + skip + reset > 1:
286 elif extra or good + bad + skip + reset > 1:
287 raise util.Abort("Incompatible arguments")
287 raise util.Abort("Incompatible arguments")
288
288
289 if reset:
289 if reset:
290 p = repo.join("bisect.state")
290 p = repo.join("bisect.state")
291 if os.path.exists(p):
291 if os.path.exists(p):
292 os.unlink(p)
292 os.unlink(p)
293 return
293 return
294
294
295 # load state
295 # load state
296 state = {'good': [], 'bad': [], 'skip': []}
296 state = {'good': [], 'bad': [], 'skip': []}
297 if os.path.exists(repo.join("bisect.state")):
297 if os.path.exists(repo.join("bisect.state")):
298 for l in repo.opener("bisect.state"):
298 for l in repo.opener("bisect.state"):
299 kind, node = l[:-1].split()
299 kind, node = l[:-1].split()
300 node = repo.lookup(node)
300 node = repo.lookup(node)
301 if kind not in state:
301 if kind not in state:
302 raise util.Abort(_("unknown bisect kind %s") % kind)
302 raise util.Abort(_("unknown bisect kind %s") % kind)
303 state[kind].append(node)
303 state[kind].append(node)
304
304
305 # update state
305 # update state
306 node = repo.lookup(rev or '.')
306 node = repo.lookup(rev or '.')
307 if good:
307 if good:
308 state['good'].append(node)
308 state['good'].append(node)
309 elif bad:
309 elif bad:
310 state['bad'].append(node)
310 state['bad'].append(node)
311 elif skip:
311 elif skip:
312 state['skip'].append(node)
312 state['skip'].append(node)
313
313
314 # save state
314 # save state
315 f = repo.opener("bisect.state", "w", atomictemp=True)
315 f = repo.opener("bisect.state", "w", atomictemp=True)
316 wlock = repo.wlock()
316 wlock = repo.wlock()
317 try:
317 try:
318 for kind in state:
318 for kind in state:
319 for node in state[kind]:
319 for node in state[kind]:
320 f.write("%s %s\n" % (kind, hex(node)))
320 f.write("%s %s\n" % (kind, hex(node)))
321 f.rename()
321 f.rename()
322 finally:
322 finally:
323 del wlock
323 del wlock
324
324
325 if not state['good'] or not state['bad']:
325 if not state['good'] or not state['bad']:
326 return
326 return
327
327
328 # actually bisect
328 # actually bisect
329 node, changesets, good = hbisect.bisect(repo.changelog, state)
329 node, changesets, good = hbisect.bisect(repo.changelog, state)
330 if changesets == 0:
330 if changesets == 0:
331 ui.write(_("The first %s revision is:\n") % (good and "good" or "bad"))
331 ui.write(_("The first %s revision is:\n") % (good and "good" or "bad"))
332 displayer = cmdutil.show_changeset(ui, repo, {})
332 displayer = cmdutil.show_changeset(ui, repo, {})
333 displayer.show(changenode=node)
333 displayer.show(changenode=node)
334 elif node is not None:
334 elif node is not None:
335 # compute the approximate number of remaining tests
335 # compute the approximate number of remaining tests
336 tests, size = 0, 2
336 tests, size = 0, 2
337 while size <= changesets:
337 while size <= changesets:
338 tests, size = tests + 1, size * 2
338 tests, size = tests + 1, size * 2
339 rev = repo.changelog.rev(node)
339 rev = repo.changelog.rev(node)
340 ui.write(_("Testing changeset %s:%s "
340 ui.write(_("Testing changeset %s:%s "
341 "(%s changesets remaining, ~%s tests)\n")
341 "(%s changesets remaining, ~%s tests)\n")
342 % (rev, short(node), changesets, tests))
342 % (rev, short(node), changesets, tests))
343 if not noupdate:
343 if not noupdate:
344 cmdutil.bail_if_changed(repo)
344 cmdutil.bail_if_changed(repo)
345 return hg.clean(repo, node)
345 return hg.clean(repo, node)
346
346
347 def branch(ui, repo, label=None, **opts):
347 def branch(ui, repo, label=None, **opts):
348 """set or show the current branch name
348 """set or show the current branch name
349
349
350 With no argument, show the current branch name. With one argument,
350 With no argument, show the current branch name. With one argument,
351 set the working directory branch name (the branch does not exist in
351 set the working directory branch name (the branch does not exist in
352 the repository until the next commit).
352 the repository until the next commit).
353
353
354 Unless --force is specified, branch will not let you set a
354 Unless --force is specified, branch will not let you set a
355 branch name that shadows an existing branch.
355 branch name that shadows an existing branch.
356
356
357 Use the command 'hg update' to switch to an existing branch.
357 Use the command 'hg update' to switch to an existing branch.
358 """
358 """
359
359
360 if label:
360 if label:
361 if not opts.get('force') and label in repo.branchtags():
361 if not opts.get('force') and label in repo.branchtags():
362 if label not in [p.branch() for p in repo.workingctx().parents()]:
362 if label not in [p.branch() for p in repo.workingctx().parents()]:
363 raise util.Abort(_('a branch of the same name already exists'
363 raise util.Abort(_('a branch of the same name already exists'
364 ' (use --force to override)'))
364 ' (use --force to override)'))
365 repo.dirstate.setbranch(util.fromlocal(label))
365 repo.dirstate.setbranch(util.fromlocal(label))
366 ui.status(_('marked working directory as branch %s\n') % label)
366 ui.status(_('marked working directory as branch %s\n') % label)
367 else:
367 else:
368 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
368 ui.write("%s\n" % util.tolocal(repo.dirstate.branch()))
369
369
370 def branches(ui, repo, active=False):
370 def branches(ui, repo, active=False):
371 """list repository named branches
371 """list repository named branches
372
372
373 List the repository's named branches, indicating which ones are
373 List the repository's named branches, indicating which ones are
374 inactive. If active is specified, only show active branches.
374 inactive. If active is specified, only show active branches.
375
375
376 A branch is considered active if it contains unmerged heads.
376 A branch is considered active if it contains unmerged heads.
377
377
378 Use the command 'hg update' to switch to an existing branch.
378 Use the command 'hg update' to switch to an existing branch.
379 """
379 """
380 b = repo.branchtags()
380 b = repo.branchtags()
381 heads = dict.fromkeys(repo.heads(), 1)
381 heads = dict.fromkeys(repo.heads(), 1)
382 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
382 l = [((n in heads), repo.changelog.rev(n), n, t) for t, n in b.items()]
383 l.sort()
383 l.sort()
384 l.reverse()
384 l.reverse()
385 for ishead, r, n, t in l:
385 for ishead, r, n, t in l:
386 if active and not ishead:
386 if active and not ishead:
387 # If we're only displaying active branches, abort the loop on
387 # If we're only displaying active branches, abort the loop on
388 # encountering the first inactive head
388 # encountering the first inactive head
389 break
389 break
390 else:
390 else:
391 hexfunc = ui.debugflag and hex or short
391 hexfunc = ui.debugflag and hex or short
392 if ui.quiet:
392 if ui.quiet:
393 ui.write("%s\n" % t)
393 ui.write("%s\n" % t)
394 else:
394 else:
395 spaces = " " * (30 - util.locallen(t))
395 spaces = " " * (30 - util.locallen(t))
396 # The code only gets here if inactive branches are being
396 # The code only gets here if inactive branches are being
397 # displayed or the branch is active.
397 # displayed or the branch is active.
398 isinactive = ((not ishead) and " (inactive)") or ''
398 isinactive = ((not ishead) and " (inactive)") or ''
399 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
399 ui.write("%s%s %s:%s%s\n" % (t, spaces, r, hexfunc(n), isinactive))
400
400
401 def bundle(ui, repo, fname, dest=None, **opts):
401 def bundle(ui, repo, fname, dest=None, **opts):
402 """create a changegroup file
402 """create a changegroup file
403
403
404 Generate a compressed changegroup file collecting changesets not
404 Generate a compressed changegroup file collecting changesets not
405 found in the other repository.
405 found in the other repository.
406
406
407 If no destination repository is specified the destination is
407 If no destination repository is specified the destination is
408 assumed to have all the nodes specified by one or more --base
408 assumed to have all the nodes specified by one or more --base
409 parameters. To create a bundle containing all changesets, use
409 parameters. To create a bundle containing all changesets, use
410 --all (or --base null). To change the compression method applied,
410 --all (or --base null). To change the compression method applied,
411 use the -t option (by default, bundles are compressed using bz2).
411 use the -t option (by default, bundles are compressed using bz2).
412
412
413 The bundle file can then be transferred using conventional means and
413 The bundle file can then be transferred using conventional means and
414 applied to another repository with the unbundle or pull command.
414 applied to another repository with the unbundle or pull command.
415 This is useful when direct push and pull are not available or when
415 This is useful when direct push and pull are not available or when
416 exporting an entire repository is undesirable.
416 exporting an entire repository is undesirable.
417
417
418 Applying bundles preserves all changeset contents including
418 Applying bundles preserves all changeset contents including
419 permissions, copy/rename information, and revision history.
419 permissions, copy/rename information, and revision history.
420 """
420 """
421 revs = opts.get('rev') or None
421 revs = opts.get('rev') or None
422 if revs:
422 if revs:
423 revs = [repo.lookup(rev) for rev in revs]
423 revs = [repo.lookup(rev) for rev in revs]
424 if opts.get('all'):
424 if opts.get('all'):
425 base = ['null']
425 base = ['null']
426 else:
426 else:
427 base = opts.get('base')
427 base = opts.get('base')
428 if base:
428 if base:
429 if dest:
429 if dest:
430 raise util.Abort(_("--base is incompatible with specifiying "
430 raise util.Abort(_("--base is incompatible with specifiying "
431 "a destination"))
431 "a destination"))
432 base = [repo.lookup(rev) for rev in base]
432 base = [repo.lookup(rev) for rev in base]
433 # create the right base
433 # create the right base
434 # XXX: nodesbetween / changegroup* should be "fixed" instead
434 # XXX: nodesbetween / changegroup* should be "fixed" instead
435 o = []
435 o = []
436 has = {nullid: None}
436 has = {nullid: None}
437 for n in base:
437 for n in base:
438 has.update(repo.changelog.reachable(n))
438 has.update(repo.changelog.reachable(n))
439 if revs:
439 if revs:
440 visit = list(revs)
440 visit = list(revs)
441 else:
441 else:
442 visit = repo.changelog.heads()
442 visit = repo.changelog.heads()
443 seen = {}
443 seen = {}
444 while visit:
444 while visit:
445 n = visit.pop(0)
445 n = visit.pop(0)
446 parents = [p for p in repo.changelog.parents(n) if p not in has]
446 parents = [p for p in repo.changelog.parents(n) if p not in has]
447 if len(parents) == 0:
447 if len(parents) == 0:
448 o.insert(0, n)
448 o.insert(0, n)
449 else:
449 else:
450 for p in parents:
450 for p in parents:
451 if p not in seen:
451 if p not in seen:
452 seen[p] = 1
452 seen[p] = 1
453 visit.append(p)
453 visit.append(p)
454 else:
454 else:
455 cmdutil.setremoteconfig(ui, opts)
455 cmdutil.setremoteconfig(ui, opts)
456 dest, revs, checkout = hg.parseurl(
456 dest, revs, checkout = hg.parseurl(
457 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
457 ui.expandpath(dest or 'default-push', dest or 'default'), revs)
458 other = hg.repository(ui, dest)
458 other = hg.repository(ui, dest)
459 o = repo.findoutgoing(other, force=opts['force'])
459 o = repo.findoutgoing(other, force=opts['force'])
460
460
461 if revs:
461 if revs:
462 cg = repo.changegroupsubset(o, revs, 'bundle')
462 cg = repo.changegroupsubset(o, revs, 'bundle')
463 else:
463 else:
464 cg = repo.changegroup(o, 'bundle')
464 cg = repo.changegroup(o, 'bundle')
465
465
466 bundletype = opts.get('type', 'bzip2').lower()
466 bundletype = opts.get('type', 'bzip2').lower()
467 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
467 btypes = {'none': 'HG10UN', 'bzip2': 'HG10BZ', 'gzip': 'HG10GZ'}
468 bundletype = btypes.get(bundletype)
468 bundletype = btypes.get(bundletype)
469 if bundletype not in changegroup.bundletypes:
469 if bundletype not in changegroup.bundletypes:
470 raise util.Abort(_('unknown bundle type specified with --type'))
470 raise util.Abort(_('unknown bundle type specified with --type'))
471
471
472 changegroup.writebundle(cg, fname, bundletype)
472 changegroup.writebundle(cg, fname, bundletype)
473
473
474 def cat(ui, repo, file1, *pats, **opts):
474 def cat(ui, repo, file1, *pats, **opts):
475 """output the current or given revision of files
475 """output the current or given revision of files
476
476
477 Print the specified files as they were at the given revision.
477 Print the specified files as they were at the given revision.
478 If no revision is given, the parent of the working directory is used,
478 If no revision is given, the parent of the working directory is used,
479 or tip if no revision is checked out.
479 or tip if no revision is checked out.
480
480
481 Output may be to a file, in which case the name of the file is
481 Output may be to a file, in which case the name of the file is
482 given using a format string. The formatting rules are the same as
482 given using a format string. The formatting rules are the same as
483 for the export command, with the following additions:
483 for the export command, with the following additions:
484
484
485 %s basename of file being printed
485 %s basename of file being printed
486 %d dirname of file being printed, or '.' if in repo root
486 %d dirname of file being printed, or '.' if in repo root
487 %p root-relative path name of file being printed
487 %p root-relative path name of file being printed
488 """
488 """
489 ctx = repo.changectx(opts['rev'])
489 ctx = repo.changectx(opts['rev'])
490 err = 1
490 err = 1
491 m = cmdutil.match(repo, (file1,) + pats, opts)
491 m = cmdutil.match(repo, (file1,) + pats, opts)
492 for src, abs, rel, exact in cmdutil.walk(repo, m, ctx.node()):
492 for src, abs, rel, exact in cmdutil.walk(repo, m, ctx.node()):
493 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
493 fp = cmdutil.make_file(repo, opts['output'], ctx.node(), pathname=abs)
494 data = ctx.filectx(abs).data()
494 data = ctx.filectx(abs).data()
495 if opts.get('decode'):
495 if opts.get('decode'):
496 data = repo.wwritedata(abs, data)
496 data = repo.wwritedata(abs, data)
497 fp.write(data)
497 fp.write(data)
498 err = 0
498 err = 0
499 return err
499 return err
500
500
501 def clone(ui, source, dest=None, **opts):
501 def clone(ui, source, dest=None, **opts):
502 """make a copy of an existing repository
502 """make a copy of an existing repository
503
503
504 Create a copy of an existing repository in a new directory.
504 Create a copy of an existing repository in a new directory.
505
505
506 If no destination directory name is specified, it defaults to the
506 If no destination directory name is specified, it defaults to the
507 basename of the source.
507 basename of the source.
508
508
509 The location of the source is added to the new repository's
509 The location of the source is added to the new repository's
510 .hg/hgrc file, as the default to be used for future pulls.
510 .hg/hgrc file, as the default to be used for future pulls.
511
511
512 For efficiency, hardlinks are used for cloning whenever the source
512 For efficiency, hardlinks are used for cloning whenever the source
513 and destination are on the same filesystem (note this applies only
513 and destination are on the same filesystem (note this applies only
514 to the repository data, not to the checked out files). Some
514 to the repository data, not to the checked out files). Some
515 filesystems, such as AFS, implement hardlinking incorrectly, but
515 filesystems, such as AFS, implement hardlinking incorrectly, but
516 do not report errors. In these cases, use the --pull option to
516 do not report errors. In these cases, use the --pull option to
517 avoid hardlinking.
517 avoid hardlinking.
518
518
519 In some cases, you can clone repositories and checked out files
519 In some cases, you can clone repositories and checked out files
520 using full hardlinks with
520 using full hardlinks with
521
521
522 $ cp -al REPO REPOCLONE
522 $ cp -al REPO REPOCLONE
523
523
524 This is the fastest way to clone, but it is not always safe. The
524 This is the fastest way to clone, but it is not always safe. The
525 operation is not atomic (making sure REPO is not modified during
525 operation is not atomic (making sure REPO is not modified during
526 the operation is up to you) and you have to make sure your editor
526 the operation is up to you) and you have to make sure your editor
527 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
527 breaks hardlinks (Emacs and most Linux Kernel tools do so). Also,
528 this is not compatible with certain extensions that place their
528 this is not compatible with certain extensions that place their
529 metadata under the .hg directory, such as mq.
529 metadata under the .hg directory, such as mq.
530
530
531 If you use the -r option to clone up to a specific revision, no
531 If you use the -r option to clone up to a specific revision, no
532 subsequent revisions will be present in the cloned repository.
532 subsequent revisions will be present in the cloned repository.
533 This option implies --pull, even on local repositories.
533 This option implies --pull, even on local repositories.
534
534
535 See pull for valid source format details.
535 See pull for valid source format details.
536
536
537 It is possible to specify an ssh:// URL as the destination, but no
537 It is possible to specify an ssh:// URL as the destination, but no
538 .hg/hgrc and working directory will be created on the remote side.
538 .hg/hgrc and working directory will be created on the remote side.
539 Look at the help text for the pull command for important details
539 Look at the help text for the pull command for important details
540 about ssh:// URLs.
540 about ssh:// URLs.
541 """
541 """
542 cmdutil.setremoteconfig(ui, opts)
542 cmdutil.setremoteconfig(ui, opts)
543 hg.clone(ui, source, dest,
543 hg.clone(ui, source, dest,
544 pull=opts['pull'],
544 pull=opts['pull'],
545 stream=opts['uncompressed'],
545 stream=opts['uncompressed'],
546 rev=opts['rev'],
546 rev=opts['rev'],
547 update=not opts['noupdate'])
547 update=not opts['noupdate'])
548
548
549 def commit(ui, repo, *pats, **opts):
549 def commit(ui, repo, *pats, **opts):
550 """commit the specified files or all outstanding changes
550 """commit the specified files or all outstanding changes
551
551
552 Commit changes to the given files into the repository.
552 Commit changes to the given files into the repository.
553
553
554 If a list of files is omitted, all changes reported by "hg status"
554 If a list of files is omitted, all changes reported by "hg status"
555 will be committed.
555 will be committed.
556
556
557 If you are committing the result of a merge, do not provide any
557 If you are committing the result of a merge, do not provide any
558 file names or -I/-X filters.
558 file names or -I/-X filters.
559
559
560 If no commit message is specified, the configured editor is started to
560 If no commit message is specified, the configured editor is started to
561 enter a message.
561 enter a message.
562
562
563 See 'hg help dates' for a list of formats valid for -d/--date.
563 See 'hg help dates' for a list of formats valid for -d/--date.
564 """
564 """
565 def commitfunc(ui, repo, files, message, match, opts):
565 def commitfunc(ui, repo, files, message, match, opts):
566 return repo.commit(files, message, opts['user'], opts['date'], match,
566 return repo.commit(files, message, opts['user'], opts['date'], match,
567 force_editor=opts.get('force_editor'))
567 force_editor=opts.get('force_editor'))
568
568
569 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
569 node = cmdutil.commit(ui, repo, commitfunc, pats, opts)
570 if not node:
570 if not node:
571 return
571 return
572 cl = repo.changelog
572 cl = repo.changelog
573 rev = cl.rev(node)
573 rev = cl.rev(node)
574 parents = cl.parentrevs(rev)
574 parents = cl.parentrevs(rev)
575 if rev - 1 in parents:
575 if rev - 1 in parents:
576 # one of the parents was the old tip
576 # one of the parents was the old tip
577 return
577 return
578 if (parents == (nullrev, nullrev) or
578 if (parents == (nullrev, nullrev) or
579 len(cl.heads(cl.node(parents[0]))) > 1 and
579 len(cl.heads(cl.node(parents[0]))) > 1 and
580 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
580 (parents[1] == nullrev or len(cl.heads(cl.node(parents[1]))) > 1)):
581 ui.status(_('created new head\n'))
581 ui.status(_('created new head\n'))
582
582
583 def copy(ui, repo, *pats, **opts):
583 def copy(ui, repo, *pats, **opts):
584 """mark files as copied for the next commit
584 """mark files as copied for the next commit
585
585
586 Mark dest as having copies of source files. If dest is a
586 Mark dest as having copies of source files. If dest is a
587 directory, copies are put in that directory. If dest is a file,
587 directory, copies are put in that directory. If dest is a file,
588 there can only be one source.
588 there can only be one source.
589
589
590 By default, this command copies the contents of files as they
590 By default, this command copies the contents of files as they
591 stand in the working directory. If invoked with --after, the
591 stand in the working directory. If invoked with --after, the
592 operation is recorded, but no copying is performed.
592 operation is recorded, but no copying is performed.
593
593
594 This command takes effect in the next commit. To undo a copy
594 This command takes effect in the next commit. To undo a copy
595 before that, see hg revert.
595 before that, see hg revert.
596 """
596 """
597 wlock = repo.wlock(False)
597 wlock = repo.wlock(False)
598 try:
598 try:
599 return cmdutil.copy(ui, repo, pats, opts)
599 return cmdutil.copy(ui, repo, pats, opts)
600 finally:
600 finally:
601 del wlock
601 del wlock
602
602
603 def debugancestor(ui, repo, *args):
603 def debugancestor(ui, repo, *args):
604 """find the ancestor revision of two revisions in a given index"""
604 """find the ancestor revision of two revisions in a given index"""
605 if len(args) == 3:
605 if len(args) == 3:
606 index, rev1, rev2 = args
606 index, rev1, rev2 = args
607 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
607 r = revlog.revlog(util.opener(os.getcwd(), audit=False), index)
608 lookup = r.lookup
608 lookup = r.lookup
609 elif len(args) == 2:
609 elif len(args) == 2:
610 if not repo:
610 if not repo:
611 raise util.Abort(_("There is no Mercurial repository here "
611 raise util.Abort(_("There is no Mercurial repository here "
612 "(.hg not found)"))
612 "(.hg not found)"))
613 rev1, rev2 = args
613 rev1, rev2 = args
614 r = repo.changelog
614 r = repo.changelog
615 lookup = repo.lookup
615 lookup = repo.lookup
616 else:
616 else:
617 raise util.Abort(_('either two or three arguments required'))
617 raise util.Abort(_('either two or three arguments required'))
618 a = r.ancestor(lookup(rev1), lookup(rev2))
618 a = r.ancestor(lookup(rev1), lookup(rev2))
619 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
619 ui.write("%d:%s\n" % (r.rev(a), hex(a)))
620
620
621 def debugcomplete(ui, cmd='', **opts):
621 def debugcomplete(ui, cmd='', **opts):
622 """returns the completion list associated with the given command"""
622 """returns the completion list associated with the given command"""
623
623
624 if opts['options']:
624 if opts['options']:
625 options = []
625 options = []
626 otables = [globalopts]
626 otables = [globalopts]
627 if cmd:
627 if cmd:
628 aliases, entry = cmdutil.findcmd(ui, cmd, table)
628 aliases, entry = cmdutil.findcmd(ui, cmd, table)
629 otables.append(entry[1])
629 otables.append(entry[1])
630 for t in otables:
630 for t in otables:
631 for o in t:
631 for o in t:
632 if o[0]:
632 if o[0]:
633 options.append('-%s' % o[0])
633 options.append('-%s' % o[0])
634 options.append('--%s' % o[1])
634 options.append('--%s' % o[1])
635 ui.write("%s\n" % "\n".join(options))
635 ui.write("%s\n" % "\n".join(options))
636 return
636 return
637
637
638 clist = cmdutil.findpossible(ui, cmd, table).keys()
638 clist = cmdutil.findpossible(ui, cmd, table).keys()
639 clist.sort()
639 clist.sort()
640 ui.write("%s\n" % "\n".join(clist))
640 ui.write("%s\n" % "\n".join(clist))
641
641
642 def debugfsinfo(ui, path = "."):
642 def debugfsinfo(ui, path = "."):
643 file('.debugfsinfo', 'w').write('')
643 file('.debugfsinfo', 'w').write('')
644 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
644 ui.write('exec: %s\n' % (util.checkexec(path) and 'yes' or 'no'))
645 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
645 ui.write('symlink: %s\n' % (util.checklink(path) and 'yes' or 'no'))
646 ui.write('case-sensitive: %s\n' % (util.checkfolding('.debugfsinfo')
646 ui.write('case-sensitive: %s\n' % (util.checkfolding('.debugfsinfo')
647 and 'yes' or 'no'))
647 and 'yes' or 'no'))
648 os.unlink('.debugfsinfo')
648 os.unlink('.debugfsinfo')
649
649
650 def debugrebuildstate(ui, repo, rev=""):
650 def debugrebuildstate(ui, repo, rev=""):
651 """rebuild the dirstate as it would look like for the given revision"""
651 """rebuild the dirstate as it would look like for the given revision"""
652 if rev == "":
652 if rev == "":
653 rev = repo.changelog.tip()
653 rev = repo.changelog.tip()
654 ctx = repo.changectx(rev)
654 ctx = repo.changectx(rev)
655 files = ctx.manifest()
655 files = ctx.manifest()
656 wlock = repo.wlock()
656 wlock = repo.wlock()
657 try:
657 try:
658 repo.dirstate.rebuild(rev, files)
658 repo.dirstate.rebuild(rev, files)
659 finally:
659 finally:
660 del wlock
660 del wlock
661
661
662 def debugcheckstate(ui, repo):
662 def debugcheckstate(ui, repo):
663 """validate the correctness of the current dirstate"""
663 """validate the correctness of the current dirstate"""
664 parent1, parent2 = repo.dirstate.parents()
664 parent1, parent2 = repo.dirstate.parents()
665 m1 = repo.changectx(parent1).manifest()
665 m1 = repo.changectx(parent1).manifest()
666 m2 = repo.changectx(parent2).manifest()
666 m2 = repo.changectx(parent2).manifest()
667 errors = 0
667 errors = 0
668 for f in repo.dirstate:
668 for f in repo.dirstate:
669 state = repo.dirstate[f]
669 state = repo.dirstate[f]
670 if state in "nr" and f not in m1:
670 if state in "nr" and f not in m1:
671 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
671 ui.warn(_("%s in state %s, but not in manifest1\n") % (f, state))
672 errors += 1
672 errors += 1
673 if state in "a" and f in m1:
673 if state in "a" and f in m1:
674 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
674 ui.warn(_("%s in state %s, but also in manifest1\n") % (f, state))
675 errors += 1
675 errors += 1
676 if state in "m" and f not in m1 and f not in m2:
676 if state in "m" and f not in m1 and f not in m2:
677 ui.warn(_("%s in state %s, but not in either manifest\n") %
677 ui.warn(_("%s in state %s, but not in either manifest\n") %
678 (f, state))
678 (f, state))
679 errors += 1
679 errors += 1
680 for f in m1:
680 for f in m1:
681 state = repo.dirstate[f]
681 state = repo.dirstate[f]
682 if state not in "nrm":
682 if state not in "nrm":
683 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
683 ui.warn(_("%s in manifest1, but listed as state %s") % (f, state))
684 errors += 1
684 errors += 1
685 if errors:
685 if errors:
686 error = _(".hg/dirstate inconsistent with current parent's manifest")
686 error = _(".hg/dirstate inconsistent with current parent's manifest")
687 raise util.Abort(error)
687 raise util.Abort(error)
688
688
689 def showconfig(ui, repo, *values, **opts):
689 def showconfig(ui, repo, *values, **opts):
690 """show combined config settings from all hgrc files
690 """show combined config settings from all hgrc files
691
691
692 With no args, print names and values of all config items.
692 With no args, print names and values of all config items.
693
693
694 With one arg of the form section.name, print just the value of
694 With one arg of the form section.name, print just the value of
695 that config item.
695 that config item.
696
696
697 With multiple args, print names and values of all config items
697 With multiple args, print names and values of all config items
698 with matching section names."""
698 with matching section names."""
699
699
700 untrusted = bool(opts.get('untrusted'))
700 untrusted = bool(opts.get('untrusted'))
701 if values:
701 if values:
702 if len([v for v in values if '.' in v]) > 1:
702 if len([v for v in values if '.' in v]) > 1:
703 raise util.Abort(_('only one config item permitted'))
703 raise util.Abort(_('only one config item permitted'))
704 for section, name, value in ui.walkconfig(untrusted=untrusted):
704 for section, name, value in ui.walkconfig(untrusted=untrusted):
705 sectname = section + '.' + name
705 sectname = section + '.' + name
706 if values:
706 if values:
707 for v in values:
707 for v in values:
708 if v == section:
708 if v == section:
709 ui.write('%s=%s\n' % (sectname, value))
709 ui.write('%s=%s\n' % (sectname, value))
710 elif v == sectname:
710 elif v == sectname:
711 ui.write(value, '\n')
711 ui.write(value, '\n')
712 else:
712 else:
713 ui.write('%s=%s\n' % (sectname, value))
713 ui.write('%s=%s\n' % (sectname, value))
714
714
715 def debugsetparents(ui, repo, rev1, rev2=None):
715 def debugsetparents(ui, repo, rev1, rev2=None):
716 """manually set the parents of the current working directory
716 """manually set the parents of the current working directory
717
717
718 This is useful for writing repository conversion tools, but should
718 This is useful for writing repository conversion tools, but should
719 be used with care.
719 be used with care.
720 """
720 """
721
721
722 if not rev2:
722 if not rev2:
723 rev2 = hex(nullid)
723 rev2 = hex(nullid)
724
724
725 wlock = repo.wlock()
725 wlock = repo.wlock()
726 try:
726 try:
727 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
727 repo.dirstate.setparents(repo.lookup(rev1), repo.lookup(rev2))
728 finally:
728 finally:
729 del wlock
729 del wlock
730
730
731 def debugstate(ui, repo, nodates=None):
731 def debugstate(ui, repo, nodates=None):
732 """show the contents of the current dirstate"""
732 """show the contents of the current dirstate"""
733 k = repo.dirstate._map.items()
733 k = repo.dirstate._map.items()
734 k.sort()
734 k.sort()
735 timestr = ""
735 timestr = ""
736 showdate = not nodates
736 showdate = not nodates
737 for file_, ent in k:
737 for file_, ent in k:
738 if showdate:
738 if showdate:
739 if ent[3] == -1:
739 if ent[3] == -1:
740 # Pad or slice to locale representation
740 # Pad or slice to locale representation
741 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
741 locale_len = len(time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(0)))
742 timestr = 'unset'
742 timestr = 'unset'
743 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
743 timestr = timestr[:locale_len] + ' '*(locale_len - len(timestr))
744 else:
744 else:
745 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
745 timestr = time.strftime("%Y-%m-%d %H:%M:%S ", time.localtime(ent[3]))
746 if ent[1] & 020000:
746 if ent[1] & 020000:
747 mode = 'lnk'
747 mode = 'lnk'
748 else:
748 else:
749 mode = '%3o' % (ent[1] & 0777)
749 mode = '%3o' % (ent[1] & 0777)
750 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
750 ui.write("%c %s %10d %s%s\n" % (ent[0], mode, ent[2], timestr, file_))
751 for f in repo.dirstate.copies():
751 for f in repo.dirstate.copies():
752 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
752 ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f))
753
753
754 def debugdata(ui, file_, rev):
754 def debugdata(ui, file_, rev):
755 """dump the contents of a data file revision"""
755 """dump the contents of a data file revision"""
756 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
756 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i")
757 try:
757 try:
758 ui.write(r.revision(r.lookup(rev)))
758 ui.write(r.revision(r.lookup(rev)))
759 except KeyError:
759 except KeyError:
760 raise util.Abort(_('invalid revision identifier %s') % rev)
760 raise util.Abort(_('invalid revision identifier %s') % rev)
761
761
762 def debugdate(ui, date, range=None, **opts):
762 def debugdate(ui, date, range=None, **opts):
763 """parse and display a date"""
763 """parse and display a date"""
764 if opts["extended"]:
764 if opts["extended"]:
765 d = util.parsedate(date, util.extendeddateformats)
765 d = util.parsedate(date, util.extendeddateformats)
766 else:
766 else:
767 d = util.parsedate(date)
767 d = util.parsedate(date)
768 ui.write("internal: %s %s\n" % d)
768 ui.write("internal: %s %s\n" % d)
769 ui.write("standard: %s\n" % util.datestr(d))
769 ui.write("standard: %s\n" % util.datestr(d))
770 if range:
770 if range:
771 m = util.matchdate(range)
771 m = util.matchdate(range)
772 ui.write("match: %s\n" % m(d[0]))
772 ui.write("match: %s\n" % m(d[0]))
773
773
774 def debugindex(ui, file_):
774 def debugindex(ui, file_):
775 """dump the contents of an index file"""
775 """dump the contents of an index file"""
776 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
776 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
777 ui.write(" rev offset length base linkrev" +
777 ui.write(" rev offset length base linkrev" +
778 " nodeid p1 p2\n")
778 " nodeid p1 p2\n")
779 for i in xrange(r.count()):
779 for i in xrange(r.count()):
780 node = r.node(i)
780 node = r.node(i)
781 try:
781 try:
782 pp = r.parents(node)
782 pp = r.parents(node)
783 except:
783 except:
784 pp = [nullid, nullid]
784 pp = [nullid, nullid]
785 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
785 ui.write("% 6d % 9d % 7d % 6d % 7d %s %s %s\n" % (
786 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
786 i, r.start(i), r.length(i), r.base(i), r.linkrev(node),
787 short(node), short(pp[0]), short(pp[1])))
787 short(node), short(pp[0]), short(pp[1])))
788
788
789 def debugindexdot(ui, file_):
789 def debugindexdot(ui, file_):
790 """dump an index DAG as a .dot file"""
790 """dump an index DAG as a .dot file"""
791 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
791 r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_)
792 ui.write("digraph G {\n")
792 ui.write("digraph G {\n")
793 for i in xrange(r.count()):
793 for i in xrange(r.count()):
794 node = r.node(i)
794 node = r.node(i)
795 pp = r.parents(node)
795 pp = r.parents(node)
796 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
796 ui.write("\t%d -> %d\n" % (r.rev(pp[0]), i))
797 if pp[1] != nullid:
797 if pp[1] != nullid:
798 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
798 ui.write("\t%d -> %d\n" % (r.rev(pp[1]), i))
799 ui.write("}\n")
799 ui.write("}\n")
800
800
801 def debuginstall(ui):
801 def debuginstall(ui):
802 '''test Mercurial installation'''
802 '''test Mercurial installation'''
803
803
804 def writetemp(contents):
804 def writetemp(contents):
805 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
805 (fd, name) = tempfile.mkstemp(prefix="hg-debuginstall-")
806 f = os.fdopen(fd, "wb")
806 f = os.fdopen(fd, "wb")
807 f.write(contents)
807 f.write(contents)
808 f.close()
808 f.close()
809 return name
809 return name
810
810
811 problems = 0
811 problems = 0
812
812
813 # encoding
813 # encoding
814 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
814 ui.status(_("Checking encoding (%s)...\n") % util._encoding)
815 try:
815 try:
816 util.fromlocal("test")
816 util.fromlocal("test")
817 except util.Abort, inst:
817 except util.Abort, inst:
818 ui.write(" %s\n" % inst)
818 ui.write(" %s\n" % inst)
819 ui.write(_(" (check that your locale is properly set)\n"))
819 ui.write(_(" (check that your locale is properly set)\n"))
820 problems += 1
820 problems += 1
821
821
822 # compiled modules
822 # compiled modules
823 ui.status(_("Checking extensions...\n"))
823 ui.status(_("Checking extensions...\n"))
824 try:
824 try:
825 import bdiff, mpatch, base85
825 import bdiff, mpatch, base85
826 except Exception, inst:
826 except Exception, inst:
827 ui.write(" %s\n" % inst)
827 ui.write(" %s\n" % inst)
828 ui.write(_(" One or more extensions could not be found"))
828 ui.write(_(" One or more extensions could not be found"))
829 ui.write(_(" (check that you compiled the extensions)\n"))
829 ui.write(_(" (check that you compiled the extensions)\n"))
830 problems += 1
830 problems += 1
831
831
832 # templates
832 # templates
833 ui.status(_("Checking templates...\n"))
833 ui.status(_("Checking templates...\n"))
834 try:
834 try:
835 import templater
835 import templater
836 t = templater.templater(templater.templatepath("map-cmdline.default"))
836 t = templater.templater(templater.templatepath("map-cmdline.default"))
837 except Exception, inst:
837 except Exception, inst:
838 ui.write(" %s\n" % inst)
838 ui.write(" %s\n" % inst)
839 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
839 ui.write(_(" (templates seem to have been installed incorrectly)\n"))
840 problems += 1
840 problems += 1
841
841
842 # patch
842 # patch
843 ui.status(_("Checking patch...\n"))
843 ui.status(_("Checking patch...\n"))
844 patchproblems = 0
844 patchproblems = 0
845 a = "1\n2\n3\n4\n"
845 a = "1\n2\n3\n4\n"
846 b = "1\n2\n3\ninsert\n4\n"
846 b = "1\n2\n3\ninsert\n4\n"
847 fa = writetemp(a)
847 fa = writetemp(a)
848 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
848 d = mdiff.unidiff(a, None, b, None, os.path.basename(fa),
849 os.path.basename(fa))
849 os.path.basename(fa))
850 fd = writetemp(d)
850 fd = writetemp(d)
851
851
852 files = {}
852 files = {}
853 try:
853 try:
854 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
854 patch.patch(fd, ui, cwd=os.path.dirname(fa), files=files)
855 except util.Abort, e:
855 except util.Abort, e:
856 ui.write(_(" patch call failed:\n"))
856 ui.write(_(" patch call failed:\n"))
857 ui.write(" " + str(e) + "\n")
857 ui.write(" " + str(e) + "\n")
858 patchproblems += 1
858 patchproblems += 1
859 else:
859 else:
860 if list(files) != [os.path.basename(fa)]:
860 if list(files) != [os.path.basename(fa)]:
861 ui.write(_(" unexpected patch output!\n"))
861 ui.write(_(" unexpected patch output!\n"))
862 patchproblems += 1
862 patchproblems += 1
863 a = file(fa).read()
863 a = file(fa).read()
864 if a != b:
864 if a != b:
865 ui.write(_(" patch test failed!\n"))
865 ui.write(_(" patch test failed!\n"))
866 patchproblems += 1
866 patchproblems += 1
867
867
868 if patchproblems:
868 if patchproblems:
869 if ui.config('ui', 'patch'):
869 if ui.config('ui', 'patch'):
870 ui.write(_(" (Current patch tool may be incompatible with patch,"
870 ui.write(_(" (Current patch tool may be incompatible with patch,"
871 " or misconfigured. Please check your .hgrc file)\n"))
871 " or misconfigured. Please check your .hgrc file)\n"))
872 else:
872 else:
873 ui.write(_(" Internal patcher failure, please report this error"
873 ui.write(_(" Internal patcher failure, please report this error"
874 " to http://www.selenic.com/mercurial/bts\n"))
874 " to http://www.selenic.com/mercurial/bts\n"))
875 problems += patchproblems
875 problems += patchproblems
876
876
877 os.unlink(fa)
877 os.unlink(fa)
878 os.unlink(fd)
878 os.unlink(fd)
879
879
880 # editor
880 # editor
881 ui.status(_("Checking commit editor...\n"))
881 ui.status(_("Checking commit editor...\n"))
882 editor = ui.geteditor()
882 editor = ui.geteditor()
883 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
883 cmdpath = util.find_exe(editor) or util.find_exe(editor.split()[0])
884 if not cmdpath:
884 if not cmdpath:
885 if editor == 'vi':
885 if editor == 'vi':
886 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
886 ui.write(_(" No commit editor set and can't find vi in PATH\n"))
887 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
887 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
888 else:
888 else:
889 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
889 ui.write(_(" Can't find editor '%s' in PATH\n") % editor)
890 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
890 ui.write(_(" (specify a commit editor in your .hgrc file)\n"))
891 problems += 1
891 problems += 1
892
892
893 # check username
893 # check username
894 ui.status(_("Checking username...\n"))
894 ui.status(_("Checking username...\n"))
895 user = os.environ.get("HGUSER")
895 user = os.environ.get("HGUSER")
896 if user is None:
896 if user is None:
897 user = ui.config("ui", "username")
897 user = ui.config("ui", "username")
898 if user is None:
898 if user is None:
899 user = os.environ.get("EMAIL")
899 user = os.environ.get("EMAIL")
900 if not user:
900 if not user:
901 ui.warn(" ")
901 ui.warn(" ")
902 ui.username()
902 ui.username()
903 ui.write(_(" (specify a username in your .hgrc file)\n"))
903 ui.write(_(" (specify a username in your .hgrc file)\n"))
904
904
905 if not problems:
905 if not problems:
906 ui.status(_("No problems detected\n"))
906 ui.status(_("No problems detected\n"))
907 else:
907 else:
908 ui.write(_("%s problems detected,"
908 ui.write(_("%s problems detected,"
909 " please check your install!\n") % problems)
909 " please check your install!\n") % problems)
910
910
911 return problems
911 return problems
912
912
913 def debugrename(ui, repo, file1, *pats, **opts):
913 def debugrename(ui, repo, file1, *pats, **opts):
914 """dump rename information"""
914 """dump rename information"""
915
915
916 ctx = repo.changectx(opts.get('rev', 'tip'))
916 ctx = repo.changectx(opts.get('rev', 'tip'))
917 m = cmdutil.match(repo, (file1,) + pats, opts)
917 m = cmdutil.match(repo, (file1,) + pats, opts)
918 for src, abs, rel, exact in cmdutil.walk(repo, m, ctx.node()):
918 for src, abs, rel, exact in cmdutil.walk(repo, m, ctx.node()):
919 fctx = ctx.filectx(abs)
919 fctx = ctx.filectx(abs)
920 o = fctx.filelog().renamed(fctx.filenode())
920 o = fctx.filelog().renamed(fctx.filenode())
921 if o:
921 if o:
922 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
922 ui.write(_("%s renamed from %s:%s\n") % (rel, o[0], hex(o[1])))
923 else:
923 else:
924 ui.write(_("%s not renamed\n") % rel)
924 ui.write(_("%s not renamed\n") % rel)
925
925
926 def debugwalk(ui, repo, *pats, **opts):
926 def debugwalk(ui, repo, *pats, **opts):
927 """show how files match on given patterns"""
927 """show how files match on given patterns"""
928 m = cmdutil.match(repo, pats, opts)
928 m = cmdutil.match(repo, pats, opts)
929 items = list(cmdutil.walk(repo, m))
929 items = list(cmdutil.walk(repo, m))
930 if not items:
930 if not items:
931 return
931 return
932 fmt = '%%s %%-%ds %%-%ds %%s' % (
932 fmt = '%%s %%-%ds %%-%ds %%s' % (
933 max([len(abs) for (src, abs, rel, exact) in items]),
933 max([len(abs) for (src, abs, rel, exact) in items]),
934 max([len(rel) for (src, abs, rel, exact) in items]))
934 max([len(rel) for (src, abs, rel, exact) in items]))
935 for src, abs, rel, exact in items:
935 for src, abs, rel, exact in items:
936 line = fmt % (src, abs, rel, exact and 'exact' or '')
936 line = fmt % (src, abs, rel, exact and 'exact' or '')
937 ui.write("%s\n" % line.rstrip())
937 ui.write("%s\n" % line.rstrip())
938
938
939 def diff(ui, repo, *pats, **opts):
939 def diff(ui, repo, *pats, **opts):
940 """diff repository (or selected files)
940 """diff repository (or selected files)
941
941
942 Show differences between revisions for the specified files.
942 Show differences between revisions for the specified files.
943
943
944 Differences between files are shown using the unified diff format.
944 Differences between files are shown using the unified diff format.
945
945
946 NOTE: diff may generate unexpected results for merges, as it will
946 NOTE: diff may generate unexpected results for merges, as it will
947 default to comparing against the working directory's first parent
947 default to comparing against the working directory's first parent
948 changeset if no revisions are specified.
948 changeset if no revisions are specified.
949
949
950 When two revision arguments are given, then changes are shown
950 When two revision arguments are given, then changes are shown
951 between those revisions. If only one revision is specified then
951 between those revisions. If only one revision is specified then
952 that revision is compared to the working directory, and, when no
952 that revision is compared to the working directory, and, when no
953 revisions are specified, the working directory files are compared
953 revisions are specified, the working directory files are compared
954 to its parent.
954 to its parent.
955
955
956 Without the -a option, diff will avoid generating diffs of files
956 Without the -a option, diff will avoid generating diffs of files
957 it detects as binary. With -a, diff will generate a diff anyway,
957 it detects as binary. With -a, diff will generate a diff anyway,
958 probably with undesirable results.
958 probably with undesirable results.
959 """
959 """
960 node1, node2 = cmdutil.revpair(repo, opts['rev'])
960 node1, node2 = cmdutil.revpair(repo, opts['rev'])
961
961
962 fns, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
962 m = cmdutil.match(repo, pats, opts)
963
963 patch.diff(repo, node1, node2, m.files(), match=m,
964 patch.diff(repo, node1, node2, fns, match=matchfn,
965 opts=patch.diffopts(ui, opts))
964 opts=patch.diffopts(ui, opts))
966
965
967 def export(ui, repo, *changesets, **opts):
966 def export(ui, repo, *changesets, **opts):
968 """dump the header and diffs for one or more changesets
967 """dump the header and diffs for one or more changesets
969
968
970 Print the changeset header and diffs for one or more revisions.
969 Print the changeset header and diffs for one or more revisions.
971
970
972 The information shown in the changeset header is: author,
971 The information shown in the changeset header is: author,
973 changeset hash, parent(s) and commit comment.
972 changeset hash, parent(s) and commit comment.
974
973
975 NOTE: export may generate unexpected diff output for merge changesets,
974 NOTE: export may generate unexpected diff output for merge changesets,
976 as it will compare the merge changeset against its first parent only.
975 as it will compare the merge changeset against its first parent only.
977
976
978 Output may be to a file, in which case the name of the file is
977 Output may be to a file, in which case the name of the file is
979 given using a format string. The formatting rules are as follows:
978 given using a format string. The formatting rules are as follows:
980
979
981 %% literal "%" character
980 %% literal "%" character
982 %H changeset hash (40 bytes of hexadecimal)
981 %H changeset hash (40 bytes of hexadecimal)
983 %N number of patches being generated
982 %N number of patches being generated
984 %R changeset revision number
983 %R changeset revision number
985 %b basename of the exporting repository
984 %b basename of the exporting repository
986 %h short-form changeset hash (12 bytes of hexadecimal)
985 %h short-form changeset hash (12 bytes of hexadecimal)
987 %n zero-padded sequence number, starting at 1
986 %n zero-padded sequence number, starting at 1
988 %r zero-padded changeset revision number
987 %r zero-padded changeset revision number
989
988
990 Without the -a option, export will avoid generating diffs of files
989 Without the -a option, export will avoid generating diffs of files
991 it detects as binary. With -a, export will generate a diff anyway,
990 it detects as binary. With -a, export will generate a diff anyway,
992 probably with undesirable results.
991 probably with undesirable results.
993
992
994 With the --switch-parent option, the diff will be against the second
993 With the --switch-parent option, the diff will be against the second
995 parent. It can be useful to review a merge.
994 parent. It can be useful to review a merge.
996 """
995 """
997 if not changesets:
996 if not changesets:
998 raise util.Abort(_("export requires at least one changeset"))
997 raise util.Abort(_("export requires at least one changeset"))
999 revs = cmdutil.revrange(repo, changesets)
998 revs = cmdutil.revrange(repo, changesets)
1000 if len(revs) > 1:
999 if len(revs) > 1:
1001 ui.note(_('exporting patches:\n'))
1000 ui.note(_('exporting patches:\n'))
1002 else:
1001 else:
1003 ui.note(_('exporting patch:\n'))
1002 ui.note(_('exporting patch:\n'))
1004 patch.export(repo, revs, template=opts['output'],
1003 patch.export(repo, revs, template=opts['output'],
1005 switch_parent=opts['switch_parent'],
1004 switch_parent=opts['switch_parent'],
1006 opts=patch.diffopts(ui, opts))
1005 opts=patch.diffopts(ui, opts))
1007
1006
1008 def grep(ui, repo, pattern, *pats, **opts):
1007 def grep(ui, repo, pattern, *pats, **opts):
1009 """search for a pattern in specified files and revisions
1008 """search for a pattern in specified files and revisions
1010
1009
1011 Search revisions of files for a regular expression.
1010 Search revisions of files for a regular expression.
1012
1011
1013 This command behaves differently than Unix grep. It only accepts
1012 This command behaves differently than Unix grep. It only accepts
1014 Python/Perl regexps. It searches repository history, not the
1013 Python/Perl regexps. It searches repository history, not the
1015 working directory. It always prints the revision number in which
1014 working directory. It always prints the revision number in which
1016 a match appears.
1015 a match appears.
1017
1016
1018 By default, grep only prints output for the first revision of a
1017 By default, grep only prints output for the first revision of a
1019 file in which it finds a match. To get it to print every revision
1018 file in which it finds a match. To get it to print every revision
1020 that contains a change in match status ("-" for a match that
1019 that contains a change in match status ("-" for a match that
1021 becomes a non-match, or "+" for a non-match that becomes a match),
1020 becomes a non-match, or "+" for a non-match that becomes a match),
1022 use the --all flag.
1021 use the --all flag.
1023 """
1022 """
1024 reflags = 0
1023 reflags = 0
1025 if opts['ignore_case']:
1024 if opts['ignore_case']:
1026 reflags |= re.I
1025 reflags |= re.I
1027 try:
1026 try:
1028 regexp = re.compile(pattern, reflags)
1027 regexp = re.compile(pattern, reflags)
1029 except Exception, inst:
1028 except Exception, inst:
1030 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1029 ui.warn(_("grep: invalid match pattern: %s\n") % inst)
1031 return None
1030 return None
1032 sep, eol = ':', '\n'
1031 sep, eol = ':', '\n'
1033 if opts['print0']:
1032 if opts['print0']:
1034 sep = eol = '\0'
1033 sep = eol = '\0'
1035
1034
1036 fcache = {}
1035 fcache = {}
1037 def getfile(fn):
1036 def getfile(fn):
1038 if fn not in fcache:
1037 if fn not in fcache:
1039 fcache[fn] = repo.file(fn)
1038 fcache[fn] = repo.file(fn)
1040 return fcache[fn]
1039 return fcache[fn]
1041
1040
1042 def matchlines(body):
1041 def matchlines(body):
1043 begin = 0
1042 begin = 0
1044 linenum = 0
1043 linenum = 0
1045 while True:
1044 while True:
1046 match = regexp.search(body, begin)
1045 match = regexp.search(body, begin)
1047 if not match:
1046 if not match:
1048 break
1047 break
1049 mstart, mend = match.span()
1048 mstart, mend = match.span()
1050 linenum += body.count('\n', begin, mstart) + 1
1049 linenum += body.count('\n', begin, mstart) + 1
1051 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1050 lstart = body.rfind('\n', begin, mstart) + 1 or begin
1052 lend = body.find('\n', mend)
1051 lend = body.find('\n', mend)
1053 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1052 yield linenum, mstart - lstart, mend - lstart, body[lstart:lend]
1054 begin = lend + 1
1053 begin = lend + 1
1055
1054
1056 class linestate(object):
1055 class linestate(object):
1057 def __init__(self, line, linenum, colstart, colend):
1056 def __init__(self, line, linenum, colstart, colend):
1058 self.line = line
1057 self.line = line
1059 self.linenum = linenum
1058 self.linenum = linenum
1060 self.colstart = colstart
1059 self.colstart = colstart
1061 self.colend = colend
1060 self.colend = colend
1062
1061
1063 def __hash__(self):
1062 def __hash__(self):
1064 return hash((self.linenum, self.line))
1063 return hash((self.linenum, self.line))
1065
1064
1066 def __eq__(self, other):
1065 def __eq__(self, other):
1067 return self.line == other.line
1066 return self.line == other.line
1068
1067
1069 matches = {}
1068 matches = {}
1070 copies = {}
1069 copies = {}
1071 def grepbody(fn, rev, body):
1070 def grepbody(fn, rev, body):
1072 matches[rev].setdefault(fn, [])
1071 matches[rev].setdefault(fn, [])
1073 m = matches[rev][fn]
1072 m = matches[rev][fn]
1074 for lnum, cstart, cend, line in matchlines(body):
1073 for lnum, cstart, cend, line in matchlines(body):
1075 s = linestate(line, lnum, cstart, cend)
1074 s = linestate(line, lnum, cstart, cend)
1076 m.append(s)
1075 m.append(s)
1077
1076
1078 def difflinestates(a, b):
1077 def difflinestates(a, b):
1079 sm = difflib.SequenceMatcher(None, a, b)
1078 sm = difflib.SequenceMatcher(None, a, b)
1080 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1079 for tag, alo, ahi, blo, bhi in sm.get_opcodes():
1081 if tag == 'insert':
1080 if tag == 'insert':
1082 for i in xrange(blo, bhi):
1081 for i in xrange(blo, bhi):
1083 yield ('+', b[i])
1082 yield ('+', b[i])
1084 elif tag == 'delete':
1083 elif tag == 'delete':
1085 for i in xrange(alo, ahi):
1084 for i in xrange(alo, ahi):
1086 yield ('-', a[i])
1085 yield ('-', a[i])
1087 elif tag == 'replace':
1086 elif tag == 'replace':
1088 for i in xrange(alo, ahi):
1087 for i in xrange(alo, ahi):
1089 yield ('-', a[i])
1088 yield ('-', a[i])
1090 for i in xrange(blo, bhi):
1089 for i in xrange(blo, bhi):
1091 yield ('+', b[i])
1090 yield ('+', b[i])
1092
1091
1093 prev = {}
1092 prev = {}
1094 def display(fn, rev, states, prevstates):
1093 def display(fn, rev, states, prevstates):
1095 datefunc = ui.quiet and util.shortdate or util.datestr
1094 datefunc = ui.quiet and util.shortdate or util.datestr
1096 found = False
1095 found = False
1097 filerevmatches = {}
1096 filerevmatches = {}
1098 r = prev.get(fn, -1)
1097 r = prev.get(fn, -1)
1099 if opts['all']:
1098 if opts['all']:
1100 iter = difflinestates(states, prevstates)
1099 iter = difflinestates(states, prevstates)
1101 else:
1100 else:
1102 iter = [('', l) for l in prevstates]
1101 iter = [('', l) for l in prevstates]
1103 for change, l in iter:
1102 for change, l in iter:
1104 cols = [fn, str(r)]
1103 cols = [fn, str(r)]
1105 if opts['line_number']:
1104 if opts['line_number']:
1106 cols.append(str(l.linenum))
1105 cols.append(str(l.linenum))
1107 if opts['all']:
1106 if opts['all']:
1108 cols.append(change)
1107 cols.append(change)
1109 if opts['user']:
1108 if opts['user']:
1110 cols.append(ui.shortuser(get(r)[1]))
1109 cols.append(ui.shortuser(get(r)[1]))
1111 if opts.get('date'):
1110 if opts.get('date'):
1112 cols.append(datefunc(get(r)[2]))
1111 cols.append(datefunc(get(r)[2]))
1113 if opts['files_with_matches']:
1112 if opts['files_with_matches']:
1114 c = (fn, r)
1113 c = (fn, r)
1115 if c in filerevmatches:
1114 if c in filerevmatches:
1116 continue
1115 continue
1117 filerevmatches[c] = 1
1116 filerevmatches[c] = 1
1118 else:
1117 else:
1119 cols.append(l.line)
1118 cols.append(l.line)
1120 ui.write(sep.join(cols), eol)
1119 ui.write(sep.join(cols), eol)
1121 found = True
1120 found = True
1122 return found
1121 return found
1123
1122
1124 fstate = {}
1123 fstate = {}
1125 skip = {}
1124 skip = {}
1126 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1125 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1127 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1126 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1128 found = False
1127 found = False
1129 follow = opts.get('follow')
1128 follow = opts.get('follow')
1130 for st, rev, fns in changeiter:
1129 for st, rev, fns in changeiter:
1131 if st == 'window':
1130 if st == 'window':
1132 matches.clear()
1131 matches.clear()
1133 elif st == 'add':
1132 elif st == 'add':
1134 ctx = repo.changectx(rev)
1133 ctx = repo.changectx(rev)
1135 matches[rev] = {}
1134 matches[rev] = {}
1136 for fn in fns:
1135 for fn in fns:
1137 if fn in skip:
1136 if fn in skip:
1138 continue
1137 continue
1139 try:
1138 try:
1140 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1139 grepbody(fn, rev, getfile(fn).read(ctx.filenode(fn)))
1141 fstate.setdefault(fn, [])
1140 fstate.setdefault(fn, [])
1142 if follow:
1141 if follow:
1143 copied = getfile(fn).renamed(ctx.filenode(fn))
1142 copied = getfile(fn).renamed(ctx.filenode(fn))
1144 if copied:
1143 if copied:
1145 copies.setdefault(rev, {})[fn] = copied[0]
1144 copies.setdefault(rev, {})[fn] = copied[0]
1146 except revlog.LookupError:
1145 except revlog.LookupError:
1147 pass
1146 pass
1148 elif st == 'iter':
1147 elif st == 'iter':
1149 states = matches[rev].items()
1148 states = matches[rev].items()
1150 states.sort()
1149 states.sort()
1151 for fn, m in states:
1150 for fn, m in states:
1152 copy = copies.get(rev, {}).get(fn)
1151 copy = copies.get(rev, {}).get(fn)
1153 if fn in skip:
1152 if fn in skip:
1154 if copy:
1153 if copy:
1155 skip[copy] = True
1154 skip[copy] = True
1156 continue
1155 continue
1157 if fn in prev or fstate[fn]:
1156 if fn in prev or fstate[fn]:
1158 r = display(fn, rev, m, fstate[fn])
1157 r = display(fn, rev, m, fstate[fn])
1159 found = found or r
1158 found = found or r
1160 if r and not opts['all']:
1159 if r and not opts['all']:
1161 skip[fn] = True
1160 skip[fn] = True
1162 if copy:
1161 if copy:
1163 skip[copy] = True
1162 skip[copy] = True
1164 fstate[fn] = m
1163 fstate[fn] = m
1165 if copy:
1164 if copy:
1166 fstate[copy] = m
1165 fstate[copy] = m
1167 prev[fn] = rev
1166 prev[fn] = rev
1168
1167
1169 fstate = fstate.items()
1168 fstate = fstate.items()
1170 fstate.sort()
1169 fstate.sort()
1171 for fn, state in fstate:
1170 for fn, state in fstate:
1172 if fn in skip:
1171 if fn in skip:
1173 continue
1172 continue
1174 if fn not in copies.get(prev[fn], {}):
1173 if fn not in copies.get(prev[fn], {}):
1175 found = display(fn, rev, {}, state) or found
1174 found = display(fn, rev, {}, state) or found
1176 return (not found and 1) or 0
1175 return (not found and 1) or 0
1177
1176
1178 def heads(ui, repo, *branchrevs, **opts):
1177 def heads(ui, repo, *branchrevs, **opts):
1179 """show current repository heads or show branch heads
1178 """show current repository heads or show branch heads
1180
1179
1181 With no arguments, show all repository head changesets.
1180 With no arguments, show all repository head changesets.
1182
1181
1183 If branch or revisions names are given this will show the heads of
1182 If branch or revisions names are given this will show the heads of
1184 the specified branches or the branches those revisions are tagged
1183 the specified branches or the branches those revisions are tagged
1185 with.
1184 with.
1186
1185
1187 Repository "heads" are changesets that don't have child
1186 Repository "heads" are changesets that don't have child
1188 changesets. They are where development generally takes place and
1187 changesets. They are where development generally takes place and
1189 are the usual targets for update and merge operations.
1188 are the usual targets for update and merge operations.
1190
1189
1191 Branch heads are changesets that have a given branch tag, but have
1190 Branch heads are changesets that have a given branch tag, but have
1192 no child changesets with that tag. They are usually where
1191 no child changesets with that tag. They are usually where
1193 development on the given branch takes place.
1192 development on the given branch takes place.
1194 """
1193 """
1195 if opts['rev']:
1194 if opts['rev']:
1196 start = repo.lookup(opts['rev'])
1195 start = repo.lookup(opts['rev'])
1197 else:
1196 else:
1198 start = None
1197 start = None
1199 if not branchrevs:
1198 if not branchrevs:
1200 # Assume we're looking repo-wide heads if no revs were specified.
1199 # Assume we're looking repo-wide heads if no revs were specified.
1201 heads = repo.heads(start)
1200 heads = repo.heads(start)
1202 else:
1201 else:
1203 heads = []
1202 heads = []
1204 visitedset = util.set()
1203 visitedset = util.set()
1205 for branchrev in branchrevs:
1204 for branchrev in branchrevs:
1206 branch = repo.changectx(branchrev).branch()
1205 branch = repo.changectx(branchrev).branch()
1207 if branch in visitedset:
1206 if branch in visitedset:
1208 continue
1207 continue
1209 visitedset.add(branch)
1208 visitedset.add(branch)
1210 bheads = repo.branchheads(branch, start)
1209 bheads = repo.branchheads(branch, start)
1211 if not bheads:
1210 if not bheads:
1212 if branch != branchrev:
1211 if branch != branchrev:
1213 ui.warn(_("no changes on branch %s containing %s are "
1212 ui.warn(_("no changes on branch %s containing %s are "
1214 "reachable from %s\n")
1213 "reachable from %s\n")
1215 % (branch, branchrev, opts['rev']))
1214 % (branch, branchrev, opts['rev']))
1216 else:
1215 else:
1217 ui.warn(_("no changes on branch %s are reachable from %s\n")
1216 ui.warn(_("no changes on branch %s are reachable from %s\n")
1218 % (branch, opts['rev']))
1217 % (branch, opts['rev']))
1219 heads.extend(bheads)
1218 heads.extend(bheads)
1220 if not heads:
1219 if not heads:
1221 return 1
1220 return 1
1222 displayer = cmdutil.show_changeset(ui, repo, opts)
1221 displayer = cmdutil.show_changeset(ui, repo, opts)
1223 for n in heads:
1222 for n in heads:
1224 displayer.show(changenode=n)
1223 displayer.show(changenode=n)
1225
1224
1226 def help_(ui, name=None, with_version=False):
1225 def help_(ui, name=None, with_version=False):
1227 """show help for a command, extension, or list of commands
1226 """show help for a command, extension, or list of commands
1228
1227
1229 With no arguments, print a list of commands and short help.
1228 With no arguments, print a list of commands and short help.
1230
1229
1231 Given a command name, print help for that command.
1230 Given a command name, print help for that command.
1232
1231
1233 Given an extension name, print help for that extension, and the
1232 Given an extension name, print help for that extension, and the
1234 commands it provides."""
1233 commands it provides."""
1235 option_lists = []
1234 option_lists = []
1236
1235
1237 def addglobalopts(aliases):
1236 def addglobalopts(aliases):
1238 if ui.verbose:
1237 if ui.verbose:
1239 option_lists.append((_("global options:"), globalopts))
1238 option_lists.append((_("global options:"), globalopts))
1240 if name == 'shortlist':
1239 if name == 'shortlist':
1241 option_lists.append((_('use "hg help" for the full list '
1240 option_lists.append((_('use "hg help" for the full list '
1242 'of commands'), ()))
1241 'of commands'), ()))
1243 else:
1242 else:
1244 if name == 'shortlist':
1243 if name == 'shortlist':
1245 msg = _('use "hg help" for the full list of commands '
1244 msg = _('use "hg help" for the full list of commands '
1246 'or "hg -v" for details')
1245 'or "hg -v" for details')
1247 elif aliases:
1246 elif aliases:
1248 msg = _('use "hg -v help%s" to show aliases and '
1247 msg = _('use "hg -v help%s" to show aliases and '
1249 'global options') % (name and " " + name or "")
1248 'global options') % (name and " " + name or "")
1250 else:
1249 else:
1251 msg = _('use "hg -v help %s" to show global options') % name
1250 msg = _('use "hg -v help %s" to show global options') % name
1252 option_lists.append((msg, ()))
1251 option_lists.append((msg, ()))
1253
1252
1254 def helpcmd(name):
1253 def helpcmd(name):
1255 if with_version:
1254 if with_version:
1256 version_(ui)
1255 version_(ui)
1257 ui.write('\n')
1256 ui.write('\n')
1258 aliases, i = cmdutil.findcmd(ui, name, table)
1257 aliases, i = cmdutil.findcmd(ui, name, table)
1259 # synopsis
1258 # synopsis
1260 ui.write("%s\n" % i[2])
1259 ui.write("%s\n" % i[2])
1261
1260
1262 # aliases
1261 # aliases
1263 if not ui.quiet and len(aliases) > 1:
1262 if not ui.quiet and len(aliases) > 1:
1264 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1263 ui.write(_("\naliases: %s\n") % ', '.join(aliases[1:]))
1265
1264
1266 # description
1265 # description
1267 doc = i[0].__doc__
1266 doc = i[0].__doc__
1268 if not doc:
1267 if not doc:
1269 doc = _("(No help text available)")
1268 doc = _("(No help text available)")
1270 if ui.quiet:
1269 if ui.quiet:
1271 doc = doc.splitlines(0)[0]
1270 doc = doc.splitlines(0)[0]
1272 ui.write("\n%s\n" % doc.rstrip())
1271 ui.write("\n%s\n" % doc.rstrip())
1273
1272
1274 if not ui.quiet:
1273 if not ui.quiet:
1275 # options
1274 # options
1276 if i[1]:
1275 if i[1]:
1277 option_lists.append((_("options:\n"), i[1]))
1276 option_lists.append((_("options:\n"), i[1]))
1278
1277
1279 addglobalopts(False)
1278 addglobalopts(False)
1280
1279
1281 def helplist(header, select=None):
1280 def helplist(header, select=None):
1282 h = {}
1281 h = {}
1283 cmds = {}
1282 cmds = {}
1284 for c, e in table.items():
1283 for c, e in table.items():
1285 f = c.split("|", 1)[0]
1284 f = c.split("|", 1)[0]
1286 if select and not select(f):
1285 if select and not select(f):
1287 continue
1286 continue
1288 if name == "shortlist" and not f.startswith("^"):
1287 if name == "shortlist" and not f.startswith("^"):
1289 continue
1288 continue
1290 f = f.lstrip("^")
1289 f = f.lstrip("^")
1291 if not ui.debugflag and f.startswith("debug"):
1290 if not ui.debugflag and f.startswith("debug"):
1292 continue
1291 continue
1293 doc = e[0].__doc__
1292 doc = e[0].__doc__
1294 if not doc:
1293 if not doc:
1295 doc = _("(No help text available)")
1294 doc = _("(No help text available)")
1296 h[f] = doc.splitlines(0)[0].rstrip()
1295 h[f] = doc.splitlines(0)[0].rstrip()
1297 cmds[f] = c.lstrip("^")
1296 cmds[f] = c.lstrip("^")
1298
1297
1299 if not h:
1298 if not h:
1300 ui.status(_('no commands defined\n'))
1299 ui.status(_('no commands defined\n'))
1301 return
1300 return
1302
1301
1303 ui.status(header)
1302 ui.status(header)
1304 fns = h.keys()
1303 fns = h.keys()
1305 fns.sort()
1304 fns.sort()
1306 m = max(map(len, fns))
1305 m = max(map(len, fns))
1307 for f in fns:
1306 for f in fns:
1308 if ui.verbose:
1307 if ui.verbose:
1309 commands = cmds[f].replace("|",", ")
1308 commands = cmds[f].replace("|",", ")
1310 ui.write(" %s:\n %s\n"%(commands, h[f]))
1309 ui.write(" %s:\n %s\n"%(commands, h[f]))
1311 else:
1310 else:
1312 ui.write(' %-*s %s\n' % (m, f, h[f]))
1311 ui.write(' %-*s %s\n' % (m, f, h[f]))
1313
1312
1314 if not ui.quiet:
1313 if not ui.quiet:
1315 addglobalopts(True)
1314 addglobalopts(True)
1316
1315
1317 def helptopic(name):
1316 def helptopic(name):
1318 v = None
1317 v = None
1319 for i in help.helptable:
1318 for i in help.helptable:
1320 l = i.split('|')
1319 l = i.split('|')
1321 if name in l:
1320 if name in l:
1322 v = i
1321 v = i
1323 header = l[-1]
1322 header = l[-1]
1324 if not v:
1323 if not v:
1325 raise cmdutil.UnknownCommand(name)
1324 raise cmdutil.UnknownCommand(name)
1326
1325
1327 # description
1326 # description
1328 doc = help.helptable[v]
1327 doc = help.helptable[v]
1329 if not doc:
1328 if not doc:
1330 doc = _("(No help text available)")
1329 doc = _("(No help text available)")
1331 if callable(doc):
1330 if callable(doc):
1332 doc = doc()
1331 doc = doc()
1333
1332
1334 ui.write("%s\n" % header)
1333 ui.write("%s\n" % header)
1335 ui.write("%s\n" % doc.rstrip())
1334 ui.write("%s\n" % doc.rstrip())
1336
1335
1337 def helpext(name):
1336 def helpext(name):
1338 try:
1337 try:
1339 mod = extensions.find(name)
1338 mod = extensions.find(name)
1340 except KeyError:
1339 except KeyError:
1341 raise cmdutil.UnknownCommand(name)
1340 raise cmdutil.UnknownCommand(name)
1342
1341
1343 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1342 doc = (mod.__doc__ or _('No help text available')).splitlines(0)
1344 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1343 ui.write(_('%s extension - %s\n') % (name.split('.')[-1], doc[0]))
1345 for d in doc[1:]:
1344 for d in doc[1:]:
1346 ui.write(d, '\n')
1345 ui.write(d, '\n')
1347
1346
1348 ui.status('\n')
1347 ui.status('\n')
1349
1348
1350 try:
1349 try:
1351 ct = mod.cmdtable
1350 ct = mod.cmdtable
1352 except AttributeError:
1351 except AttributeError:
1353 ct = {}
1352 ct = {}
1354
1353
1355 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1354 modcmds = dict.fromkeys([c.split('|', 1)[0] for c in ct])
1356 helplist(_('list of commands:\n\n'), modcmds.has_key)
1355 helplist(_('list of commands:\n\n'), modcmds.has_key)
1357
1356
1358 if name and name != 'shortlist':
1357 if name and name != 'shortlist':
1359 i = None
1358 i = None
1360 for f in (helpcmd, helptopic, helpext):
1359 for f in (helpcmd, helptopic, helpext):
1361 try:
1360 try:
1362 f(name)
1361 f(name)
1363 i = None
1362 i = None
1364 break
1363 break
1365 except cmdutil.UnknownCommand, inst:
1364 except cmdutil.UnknownCommand, inst:
1366 i = inst
1365 i = inst
1367 if i:
1366 if i:
1368 raise i
1367 raise i
1369
1368
1370 else:
1369 else:
1371 # program name
1370 # program name
1372 if ui.verbose or with_version:
1371 if ui.verbose or with_version:
1373 version_(ui)
1372 version_(ui)
1374 else:
1373 else:
1375 ui.status(_("Mercurial Distributed SCM\n"))
1374 ui.status(_("Mercurial Distributed SCM\n"))
1376 ui.status('\n')
1375 ui.status('\n')
1377
1376
1378 # list of commands
1377 # list of commands
1379 if name == "shortlist":
1378 if name == "shortlist":
1380 header = _('basic commands:\n\n')
1379 header = _('basic commands:\n\n')
1381 else:
1380 else:
1382 header = _('list of commands:\n\n')
1381 header = _('list of commands:\n\n')
1383
1382
1384 helplist(header)
1383 helplist(header)
1385
1384
1386 # list all option lists
1385 # list all option lists
1387 opt_output = []
1386 opt_output = []
1388 for title, options in option_lists:
1387 for title, options in option_lists:
1389 opt_output.append(("\n%s" % title, None))
1388 opt_output.append(("\n%s" % title, None))
1390 for shortopt, longopt, default, desc in options:
1389 for shortopt, longopt, default, desc in options:
1391 if "DEPRECATED" in desc and not ui.verbose: continue
1390 if "DEPRECATED" in desc and not ui.verbose: continue
1392 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1391 opt_output.append(("%2s%s" % (shortopt and "-%s" % shortopt,
1393 longopt and " --%s" % longopt),
1392 longopt and " --%s" % longopt),
1394 "%s%s" % (desc,
1393 "%s%s" % (desc,
1395 default
1394 default
1396 and _(" (default: %s)") % default
1395 and _(" (default: %s)") % default
1397 or "")))
1396 or "")))
1398
1397
1399 if opt_output:
1398 if opt_output:
1400 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1399 opts_len = max([len(line[0]) for line in opt_output if line[1]] or [0])
1401 for first, second in opt_output:
1400 for first, second in opt_output:
1402 if second:
1401 if second:
1403 ui.write(" %-*s %s\n" % (opts_len, first, second))
1402 ui.write(" %-*s %s\n" % (opts_len, first, second))
1404 else:
1403 else:
1405 ui.write("%s\n" % first)
1404 ui.write("%s\n" % first)
1406
1405
1407 def identify(ui, repo, source=None,
1406 def identify(ui, repo, source=None,
1408 rev=None, num=None, id=None, branch=None, tags=None):
1407 rev=None, num=None, id=None, branch=None, tags=None):
1409 """identify the working copy or specified revision
1408 """identify the working copy or specified revision
1410
1409
1411 With no revision, print a summary of the current state of the repo.
1410 With no revision, print a summary of the current state of the repo.
1412
1411
1413 With a path, do a lookup in another repository.
1412 With a path, do a lookup in another repository.
1414
1413
1415 This summary identifies the repository state using one or two parent
1414 This summary identifies the repository state using one or two parent
1416 hash identifiers, followed by a "+" if there are uncommitted changes
1415 hash identifiers, followed by a "+" if there are uncommitted changes
1417 in the working directory, a list of tags for this revision and a branch
1416 in the working directory, a list of tags for this revision and a branch
1418 name for non-default branches.
1417 name for non-default branches.
1419 """
1418 """
1420
1419
1421 if not repo and not source:
1420 if not repo and not source:
1422 raise util.Abort(_("There is no Mercurial repository here "
1421 raise util.Abort(_("There is no Mercurial repository here "
1423 "(.hg not found)"))
1422 "(.hg not found)"))
1424
1423
1425 hexfunc = ui.debugflag and hex or short
1424 hexfunc = ui.debugflag and hex or short
1426 default = not (num or id or branch or tags)
1425 default = not (num or id or branch or tags)
1427 output = []
1426 output = []
1428
1427
1429 if source:
1428 if source:
1430 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1429 source, revs, checkout = hg.parseurl(ui.expandpath(source), [])
1431 srepo = hg.repository(ui, source)
1430 srepo = hg.repository(ui, source)
1432 if not rev and revs:
1431 if not rev and revs:
1433 rev = revs[0]
1432 rev = revs[0]
1434 if not rev:
1433 if not rev:
1435 rev = "tip"
1434 rev = "tip"
1436 if num or branch or tags:
1435 if num or branch or tags:
1437 raise util.Abort(
1436 raise util.Abort(
1438 "can't query remote revision number, branch, or tags")
1437 "can't query remote revision number, branch, or tags")
1439 output = [hexfunc(srepo.lookup(rev))]
1438 output = [hexfunc(srepo.lookup(rev))]
1440 elif not rev:
1439 elif not rev:
1441 ctx = repo.workingctx()
1440 ctx = repo.workingctx()
1442 parents = ctx.parents()
1441 parents = ctx.parents()
1443 changed = False
1442 changed = False
1444 if default or id or num:
1443 if default or id or num:
1445 changed = ctx.files() + ctx.deleted()
1444 changed = ctx.files() + ctx.deleted()
1446 if default or id:
1445 if default or id:
1447 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1446 output = ["%s%s" % ('+'.join([hexfunc(p.node()) for p in parents]),
1448 (changed) and "+" or "")]
1447 (changed) and "+" or "")]
1449 if num:
1448 if num:
1450 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1449 output.append("%s%s" % ('+'.join([str(p.rev()) for p in parents]),
1451 (changed) and "+" or ""))
1450 (changed) and "+" or ""))
1452 else:
1451 else:
1453 ctx = repo.changectx(rev)
1452 ctx = repo.changectx(rev)
1454 if default or id:
1453 if default or id:
1455 output = [hexfunc(ctx.node())]
1454 output = [hexfunc(ctx.node())]
1456 if num:
1455 if num:
1457 output.append(str(ctx.rev()))
1456 output.append(str(ctx.rev()))
1458
1457
1459 if not source and default and not ui.quiet:
1458 if not source and default and not ui.quiet:
1460 b = util.tolocal(ctx.branch())
1459 b = util.tolocal(ctx.branch())
1461 if b != 'default':
1460 if b != 'default':
1462 output.append("(%s)" % b)
1461 output.append("(%s)" % b)
1463
1462
1464 # multiple tags for a single parent separated by '/'
1463 # multiple tags for a single parent separated by '/'
1465 t = "/".join(ctx.tags())
1464 t = "/".join(ctx.tags())
1466 if t:
1465 if t:
1467 output.append(t)
1466 output.append(t)
1468
1467
1469 if branch:
1468 if branch:
1470 output.append(util.tolocal(ctx.branch()))
1469 output.append(util.tolocal(ctx.branch()))
1471
1470
1472 if tags:
1471 if tags:
1473 output.extend(ctx.tags())
1472 output.extend(ctx.tags())
1474
1473
1475 ui.write("%s\n" % ' '.join(output))
1474 ui.write("%s\n" % ' '.join(output))
1476
1475
1477 def import_(ui, repo, patch1, *patches, **opts):
1476 def import_(ui, repo, patch1, *patches, **opts):
1478 """import an ordered set of patches
1477 """import an ordered set of patches
1479
1478
1480 Import a list of patches and commit them individually.
1479 Import a list of patches and commit them individually.
1481
1480
1482 If there are outstanding changes in the working directory, import
1481 If there are outstanding changes in the working directory, import
1483 will abort unless given the -f flag.
1482 will abort unless given the -f flag.
1484
1483
1485 You can import a patch straight from a mail message. Even patches
1484 You can import a patch straight from a mail message. Even patches
1486 as attachments work (body part must be type text/plain or
1485 as attachments work (body part must be type text/plain or
1487 text/x-patch to be used). From and Subject headers of email
1486 text/x-patch to be used). From and Subject headers of email
1488 message are used as default committer and commit message. All
1487 message are used as default committer and commit message. All
1489 text/plain body parts before first diff are added to commit
1488 text/plain body parts before first diff are added to commit
1490 message.
1489 message.
1491
1490
1492 If the imported patch was generated by hg export, user and description
1491 If the imported patch was generated by hg export, user and description
1493 from patch override values from message headers and body. Values
1492 from patch override values from message headers and body. Values
1494 given on command line with -m and -u override these.
1493 given on command line with -m and -u override these.
1495
1494
1496 If --exact is specified, import will set the working directory
1495 If --exact is specified, import will set the working directory
1497 to the parent of each patch before applying it, and will abort
1496 to the parent of each patch before applying it, and will abort
1498 if the resulting changeset has a different ID than the one
1497 if the resulting changeset has a different ID than the one
1499 recorded in the patch. This may happen due to character set
1498 recorded in the patch. This may happen due to character set
1500 problems or other deficiencies in the text patch format.
1499 problems or other deficiencies in the text patch format.
1501
1500
1502 To read a patch from standard input, use patch name "-".
1501 To read a patch from standard input, use patch name "-".
1503 See 'hg help dates' for a list of formats valid for -d/--date.
1502 See 'hg help dates' for a list of formats valid for -d/--date.
1504 """
1503 """
1505 patches = (patch1,) + patches
1504 patches = (patch1,) + patches
1506
1505
1507 date = opts.get('date')
1506 date = opts.get('date')
1508 if date:
1507 if date:
1509 opts['date'] = util.parsedate(date)
1508 opts['date'] = util.parsedate(date)
1510
1509
1511 if opts.get('exact') or not opts['force']:
1510 if opts.get('exact') or not opts['force']:
1512 cmdutil.bail_if_changed(repo)
1511 cmdutil.bail_if_changed(repo)
1513
1512
1514 d = opts["base"]
1513 d = opts["base"]
1515 strip = opts["strip"]
1514 strip = opts["strip"]
1516 wlock = lock = None
1515 wlock = lock = None
1517 try:
1516 try:
1518 wlock = repo.wlock()
1517 wlock = repo.wlock()
1519 lock = repo.lock()
1518 lock = repo.lock()
1520 for p in patches:
1519 for p in patches:
1521 pf = os.path.join(d, p)
1520 pf = os.path.join(d, p)
1522
1521
1523 if pf == '-':
1522 if pf == '-':
1524 ui.status(_("applying patch from stdin\n"))
1523 ui.status(_("applying patch from stdin\n"))
1525 data = patch.extract(ui, sys.stdin)
1524 data = patch.extract(ui, sys.stdin)
1526 else:
1525 else:
1527 ui.status(_("applying %s\n") % p)
1526 ui.status(_("applying %s\n") % p)
1528 if os.path.exists(pf):
1527 if os.path.exists(pf):
1529 data = patch.extract(ui, file(pf, 'rb'))
1528 data = patch.extract(ui, file(pf, 'rb'))
1530 else:
1529 else:
1531 data = patch.extract(ui, urllib.urlopen(pf))
1530 data = patch.extract(ui, urllib.urlopen(pf))
1532 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1531 tmpname, message, user, date, branch, nodeid, p1, p2 = data
1533
1532
1534 if tmpname is None:
1533 if tmpname is None:
1535 raise util.Abort(_('no diffs found'))
1534 raise util.Abort(_('no diffs found'))
1536
1535
1537 try:
1536 try:
1538 cmdline_message = cmdutil.logmessage(opts)
1537 cmdline_message = cmdutil.logmessage(opts)
1539 if cmdline_message:
1538 if cmdline_message:
1540 # pickup the cmdline msg
1539 # pickup the cmdline msg
1541 message = cmdline_message
1540 message = cmdline_message
1542 elif message:
1541 elif message:
1543 # pickup the patch msg
1542 # pickup the patch msg
1544 message = message.strip()
1543 message = message.strip()
1545 else:
1544 else:
1546 # launch the editor
1545 # launch the editor
1547 message = None
1546 message = None
1548 ui.debug(_('message:\n%s\n') % message)
1547 ui.debug(_('message:\n%s\n') % message)
1549
1548
1550 wp = repo.workingctx().parents()
1549 wp = repo.workingctx().parents()
1551 if opts.get('exact'):
1550 if opts.get('exact'):
1552 if not nodeid or not p1:
1551 if not nodeid or not p1:
1553 raise util.Abort(_('not a mercurial patch'))
1552 raise util.Abort(_('not a mercurial patch'))
1554 p1 = repo.lookup(p1)
1553 p1 = repo.lookup(p1)
1555 p2 = repo.lookup(p2 or hex(nullid))
1554 p2 = repo.lookup(p2 or hex(nullid))
1556
1555
1557 if p1 != wp[0].node():
1556 if p1 != wp[0].node():
1558 hg.clean(repo, p1)
1557 hg.clean(repo, p1)
1559 repo.dirstate.setparents(p1, p2)
1558 repo.dirstate.setparents(p1, p2)
1560 elif p2:
1559 elif p2:
1561 try:
1560 try:
1562 p1 = repo.lookup(p1)
1561 p1 = repo.lookup(p1)
1563 p2 = repo.lookup(p2)
1562 p2 = repo.lookup(p2)
1564 if p1 == wp[0].node():
1563 if p1 == wp[0].node():
1565 repo.dirstate.setparents(p1, p2)
1564 repo.dirstate.setparents(p1, p2)
1566 except RepoError:
1565 except RepoError:
1567 pass
1566 pass
1568 if opts.get('exact') or opts.get('import_branch'):
1567 if opts.get('exact') or opts.get('import_branch'):
1569 repo.dirstate.setbranch(branch or 'default')
1568 repo.dirstate.setbranch(branch or 'default')
1570
1569
1571 files = {}
1570 files = {}
1572 try:
1571 try:
1573 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1572 fuzz = patch.patch(tmpname, ui, strip=strip, cwd=repo.root,
1574 files=files)
1573 files=files)
1575 finally:
1574 finally:
1576 files = patch.updatedir(ui, repo, files)
1575 files = patch.updatedir(ui, repo, files)
1577 if not opts.get('no_commit'):
1576 if not opts.get('no_commit'):
1578 n = repo.commit(files, message, opts.get('user') or user,
1577 n = repo.commit(files, message, opts.get('user') or user,
1579 opts.get('date') or date)
1578 opts.get('date') or date)
1580 if opts.get('exact'):
1579 if opts.get('exact'):
1581 if hex(n) != nodeid:
1580 if hex(n) != nodeid:
1582 repo.rollback()
1581 repo.rollback()
1583 raise util.Abort(_('patch is damaged'
1582 raise util.Abort(_('patch is damaged'
1584 ' or loses information'))
1583 ' or loses information'))
1585 # Force a dirstate write so that the next transaction
1584 # Force a dirstate write so that the next transaction
1586 # backups an up-do-date file.
1585 # backups an up-do-date file.
1587 repo.dirstate.write()
1586 repo.dirstate.write()
1588 finally:
1587 finally:
1589 os.unlink(tmpname)
1588 os.unlink(tmpname)
1590 finally:
1589 finally:
1591 del lock, wlock
1590 del lock, wlock
1592
1591
1593 def incoming(ui, repo, source="default", **opts):
1592 def incoming(ui, repo, source="default", **opts):
1594 """show new changesets found in source
1593 """show new changesets found in source
1595
1594
1596 Show new changesets found in the specified path/URL or the default
1595 Show new changesets found in the specified path/URL or the default
1597 pull location. These are the changesets that would be pulled if a pull
1596 pull location. These are the changesets that would be pulled if a pull
1598 was requested.
1597 was requested.
1599
1598
1600 For remote repository, using --bundle avoids downloading the changesets
1599 For remote repository, using --bundle avoids downloading the changesets
1601 twice if the incoming is followed by a pull.
1600 twice if the incoming is followed by a pull.
1602
1601
1603 See pull for valid source format details.
1602 See pull for valid source format details.
1604 """
1603 """
1605 limit = cmdutil.loglimit(opts)
1604 limit = cmdutil.loglimit(opts)
1606 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
1605 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
1607 cmdutil.setremoteconfig(ui, opts)
1606 cmdutil.setremoteconfig(ui, opts)
1608
1607
1609 other = hg.repository(ui, source)
1608 other = hg.repository(ui, source)
1610 ui.status(_('comparing with %s\n') % util.hidepassword(source))
1609 ui.status(_('comparing with %s\n') % util.hidepassword(source))
1611 if revs:
1610 if revs:
1612 revs = [other.lookup(rev) for rev in revs]
1611 revs = [other.lookup(rev) for rev in revs]
1613 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1612 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
1614 if not incoming:
1613 if not incoming:
1615 try:
1614 try:
1616 os.unlink(opts["bundle"])
1615 os.unlink(opts["bundle"])
1617 except:
1616 except:
1618 pass
1617 pass
1619 ui.status(_("no changes found\n"))
1618 ui.status(_("no changes found\n"))
1620 return 1
1619 return 1
1621
1620
1622 cleanup = None
1621 cleanup = None
1623 try:
1622 try:
1624 fname = opts["bundle"]
1623 fname = opts["bundle"]
1625 if fname or not other.local():
1624 if fname or not other.local():
1626 # create a bundle (uncompressed if other repo is not local)
1625 # create a bundle (uncompressed if other repo is not local)
1627 if revs is None:
1626 if revs is None:
1628 cg = other.changegroup(incoming, "incoming")
1627 cg = other.changegroup(incoming, "incoming")
1629 else:
1628 else:
1630 cg = other.changegroupsubset(incoming, revs, 'incoming')
1629 cg = other.changegroupsubset(incoming, revs, 'incoming')
1631 bundletype = other.local() and "HG10BZ" or "HG10UN"
1630 bundletype = other.local() and "HG10BZ" or "HG10UN"
1632 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1631 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
1633 # keep written bundle?
1632 # keep written bundle?
1634 if opts["bundle"]:
1633 if opts["bundle"]:
1635 cleanup = None
1634 cleanup = None
1636 if not other.local():
1635 if not other.local():
1637 # use the created uncompressed bundlerepo
1636 # use the created uncompressed bundlerepo
1638 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1637 other = bundlerepo.bundlerepository(ui, repo.root, fname)
1639
1638
1640 o = other.changelog.nodesbetween(incoming, revs)[0]
1639 o = other.changelog.nodesbetween(incoming, revs)[0]
1641 if opts['newest_first']:
1640 if opts['newest_first']:
1642 o.reverse()
1641 o.reverse()
1643 displayer = cmdutil.show_changeset(ui, other, opts)
1642 displayer = cmdutil.show_changeset(ui, other, opts)
1644 count = 0
1643 count = 0
1645 for n in o:
1644 for n in o:
1646 if count >= limit:
1645 if count >= limit:
1647 break
1646 break
1648 parents = [p for p in other.changelog.parents(n) if p != nullid]
1647 parents = [p for p in other.changelog.parents(n) if p != nullid]
1649 if opts['no_merges'] and len(parents) == 2:
1648 if opts['no_merges'] and len(parents) == 2:
1650 continue
1649 continue
1651 count += 1
1650 count += 1
1652 displayer.show(changenode=n)
1651 displayer.show(changenode=n)
1653 finally:
1652 finally:
1654 if hasattr(other, 'close'):
1653 if hasattr(other, 'close'):
1655 other.close()
1654 other.close()
1656 if cleanup:
1655 if cleanup:
1657 os.unlink(cleanup)
1656 os.unlink(cleanup)
1658
1657
1659 def init(ui, dest=".", **opts):
1658 def init(ui, dest=".", **opts):
1660 """create a new repository in the given directory
1659 """create a new repository in the given directory
1661
1660
1662 Initialize a new repository in the given directory. If the given
1661 Initialize a new repository in the given directory. If the given
1663 directory does not exist, it is created.
1662 directory does not exist, it is created.
1664
1663
1665 If no directory is given, the current directory is used.
1664 If no directory is given, the current directory is used.
1666
1665
1667 It is possible to specify an ssh:// URL as the destination.
1666 It is possible to specify an ssh:// URL as the destination.
1668 Look at the help text for the pull command for important details
1667 Look at the help text for the pull command for important details
1669 about ssh:// URLs.
1668 about ssh:// URLs.
1670 """
1669 """
1671 cmdutil.setremoteconfig(ui, opts)
1670 cmdutil.setremoteconfig(ui, opts)
1672 hg.repository(ui, dest, create=1)
1671 hg.repository(ui, dest, create=1)
1673
1672
1674 def locate(ui, repo, *pats, **opts):
1673 def locate(ui, repo, *pats, **opts):
1675 """locate files matching specific patterns
1674 """locate files matching specific patterns
1676
1675
1677 Print all files under Mercurial control whose names match the
1676 Print all files under Mercurial control whose names match the
1678 given patterns.
1677 given patterns.
1679
1678
1680 This command searches the entire repository by default. To search
1679 This command searches the entire repository by default. To search
1681 just the current directory and its subdirectories, use
1680 just the current directory and its subdirectories, use
1682 "--include .".
1681 "--include .".
1683
1682
1684 If no patterns are given to match, this command prints all file
1683 If no patterns are given to match, this command prints all file
1685 names.
1684 names.
1686
1685
1687 If you want to feed the output of this command into the "xargs"
1686 If you want to feed the output of this command into the "xargs"
1688 command, use the "-0" option to both this command and "xargs".
1687 command, use the "-0" option to both this command and "xargs".
1689 This will avoid the problem of "xargs" treating single filenames
1688 This will avoid the problem of "xargs" treating single filenames
1690 that contain white space as multiple filenames.
1689 that contain white space as multiple filenames.
1691 """
1690 """
1692 end = opts['print0'] and '\0' or '\n'
1691 end = opts['print0'] and '\0' or '\n'
1693 rev = opts['rev']
1692 rev = opts['rev']
1694 if rev:
1693 if rev:
1695 node = repo.lookup(rev)
1694 node = repo.lookup(rev)
1696 else:
1695 else:
1697 node = None
1696 node = None
1698
1697
1699 ret = 1
1698 ret = 1
1700 m = cmdutil.match(repo, pats, opts, default='relglob')
1699 m = cmdutil.match(repo, pats, opts, default='relglob')
1701 m.bad = lambda x,y: True
1700 m.bad = lambda x,y: True
1702 for src, abs, rel, exact in cmdutil.walk(repo, m, node):
1701 for src, abs, rel, exact in cmdutil.walk(repo, m, node):
1703 if src == 'b':
1702 if src == 'b':
1704 continue
1703 continue
1705 if not node and abs not in repo.dirstate:
1704 if not node and abs not in repo.dirstate:
1706 continue
1705 continue
1707 if opts['fullpath']:
1706 if opts['fullpath']:
1708 ui.write(os.path.join(repo.root, abs), end)
1707 ui.write(os.path.join(repo.root, abs), end)
1709 else:
1708 else:
1710 ui.write(((pats and rel) or abs), end)
1709 ui.write(((pats and rel) or abs), end)
1711 ret = 0
1710 ret = 0
1712
1711
1713 return ret
1712 return ret
1714
1713
1715 def log(ui, repo, *pats, **opts):
1714 def log(ui, repo, *pats, **opts):
1716 """show revision history of entire repository or files
1715 """show revision history of entire repository or files
1717
1716
1718 Print the revision history of the specified files or the entire
1717 Print the revision history of the specified files or the entire
1719 project.
1718 project.
1720
1719
1721 File history is shown without following rename or copy history of
1720 File history is shown without following rename or copy history of
1722 files. Use -f/--follow with a file name to follow history across
1721 files. Use -f/--follow with a file name to follow history across
1723 renames and copies. --follow without a file name will only show
1722 renames and copies. --follow without a file name will only show
1724 ancestors or descendants of the starting revision. --follow-first
1723 ancestors or descendants of the starting revision. --follow-first
1725 only follows the first parent of merge revisions.
1724 only follows the first parent of merge revisions.
1726
1725
1727 If no revision range is specified, the default is tip:0 unless
1726 If no revision range is specified, the default is tip:0 unless
1728 --follow is set, in which case the working directory parent is
1727 --follow is set, in which case the working directory parent is
1729 used as the starting revision.
1728 used as the starting revision.
1730
1729
1731 See 'hg help dates' for a list of formats valid for -d/--date.
1730 See 'hg help dates' for a list of formats valid for -d/--date.
1732
1731
1733 By default this command outputs: changeset id and hash, tags,
1732 By default this command outputs: changeset id and hash, tags,
1734 non-trivial parents, user, date and time, and a summary for each
1733 non-trivial parents, user, date and time, and a summary for each
1735 commit. When the -v/--verbose switch is used, the list of changed
1734 commit. When the -v/--verbose switch is used, the list of changed
1736 files and full commit message is shown.
1735 files and full commit message is shown.
1737
1736
1738 NOTE: log -p may generate unexpected diff output for merge
1737 NOTE: log -p may generate unexpected diff output for merge
1739 changesets, as it will compare the merge changeset against its
1738 changesets, as it will compare the merge changeset against its
1740 first parent only. Also, the files: list will only reflect files
1739 first parent only. Also, the files: list will only reflect files
1741 that are different from BOTH parents.
1740 that are different from BOTH parents.
1742
1741
1743 """
1742 """
1744
1743
1745 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1744 get = util.cachefunc(lambda r: repo.changectx(r).changeset())
1746 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1745 changeiter, matchfn = cmdutil.walkchangerevs(ui, repo, pats, get, opts)
1747
1746
1748 limit = cmdutil.loglimit(opts)
1747 limit = cmdutil.loglimit(opts)
1749 count = 0
1748 count = 0
1750
1749
1751 if opts['copies'] and opts['rev']:
1750 if opts['copies'] and opts['rev']:
1752 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1751 endrev = max(cmdutil.revrange(repo, opts['rev'])) + 1
1753 else:
1752 else:
1754 endrev = repo.changelog.count()
1753 endrev = repo.changelog.count()
1755 rcache = {}
1754 rcache = {}
1756 ncache = {}
1755 ncache = {}
1757 def getrenamed(fn, rev):
1756 def getrenamed(fn, rev):
1758 '''looks up all renames for a file (up to endrev) the first
1757 '''looks up all renames for a file (up to endrev) the first
1759 time the file is given. It indexes on the changerev and only
1758 time the file is given. It indexes on the changerev and only
1760 parses the manifest if linkrev != changerev.
1759 parses the manifest if linkrev != changerev.
1761 Returns rename info for fn at changerev rev.'''
1760 Returns rename info for fn at changerev rev.'''
1762 if fn not in rcache:
1761 if fn not in rcache:
1763 rcache[fn] = {}
1762 rcache[fn] = {}
1764 ncache[fn] = {}
1763 ncache[fn] = {}
1765 fl = repo.file(fn)
1764 fl = repo.file(fn)
1766 for i in xrange(fl.count()):
1765 for i in xrange(fl.count()):
1767 node = fl.node(i)
1766 node = fl.node(i)
1768 lr = fl.linkrev(node)
1767 lr = fl.linkrev(node)
1769 renamed = fl.renamed(node)
1768 renamed = fl.renamed(node)
1770 rcache[fn][lr] = renamed
1769 rcache[fn][lr] = renamed
1771 if renamed:
1770 if renamed:
1772 ncache[fn][node] = renamed
1771 ncache[fn][node] = renamed
1773 if lr >= endrev:
1772 if lr >= endrev:
1774 break
1773 break
1775 if rev in rcache[fn]:
1774 if rev in rcache[fn]:
1776 return rcache[fn][rev]
1775 return rcache[fn][rev]
1777
1776
1778 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1777 # If linkrev != rev (i.e. rev not found in rcache) fallback to
1779 # filectx logic.
1778 # filectx logic.
1780
1779
1781 try:
1780 try:
1782 return repo.changectx(rev).filectx(fn).renamed()
1781 return repo.changectx(rev).filectx(fn).renamed()
1783 except revlog.LookupError:
1782 except revlog.LookupError:
1784 pass
1783 pass
1785 return None
1784 return None
1786
1785
1787 df = False
1786 df = False
1788 if opts["date"]:
1787 if opts["date"]:
1789 df = util.matchdate(opts["date"])
1788 df = util.matchdate(opts["date"])
1790
1789
1791 only_branches = opts['only_branch']
1790 only_branches = opts['only_branch']
1792
1791
1793 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1792 displayer = cmdutil.show_changeset(ui, repo, opts, True, matchfn)
1794 for st, rev, fns in changeiter:
1793 for st, rev, fns in changeiter:
1795 if st == 'add':
1794 if st == 'add':
1796 changenode = repo.changelog.node(rev)
1795 changenode = repo.changelog.node(rev)
1797 parents = [p for p in repo.changelog.parentrevs(rev)
1796 parents = [p for p in repo.changelog.parentrevs(rev)
1798 if p != nullrev]
1797 if p != nullrev]
1799 if opts['no_merges'] and len(parents) == 2:
1798 if opts['no_merges'] and len(parents) == 2:
1800 continue
1799 continue
1801 if opts['only_merges'] and len(parents) != 2:
1800 if opts['only_merges'] and len(parents) != 2:
1802 continue
1801 continue
1803
1802
1804 if only_branches:
1803 if only_branches:
1805 revbranch = get(rev)[5]['branch']
1804 revbranch = get(rev)[5]['branch']
1806 if revbranch not in only_branches:
1805 if revbranch not in only_branches:
1807 continue
1806 continue
1808
1807
1809 if df:
1808 if df:
1810 changes = get(rev)
1809 changes = get(rev)
1811 if not df(changes[2][0]):
1810 if not df(changes[2][0]):
1812 continue
1811 continue
1813
1812
1814 if opts['keyword']:
1813 if opts['keyword']:
1815 changes = get(rev)
1814 changes = get(rev)
1816 miss = 0
1815 miss = 0
1817 for k in [kw.lower() for kw in opts['keyword']]:
1816 for k in [kw.lower() for kw in opts['keyword']]:
1818 if not (k in changes[1].lower() or
1817 if not (k in changes[1].lower() or
1819 k in changes[4].lower() or
1818 k in changes[4].lower() or
1820 k in " ".join(changes[3]).lower()):
1819 k in " ".join(changes[3]).lower()):
1821 miss = 1
1820 miss = 1
1822 break
1821 break
1823 if miss:
1822 if miss:
1824 continue
1823 continue
1825
1824
1826 copies = []
1825 copies = []
1827 if opts.get('copies') and rev:
1826 if opts.get('copies') and rev:
1828 for fn in get(rev)[3]:
1827 for fn in get(rev)[3]:
1829 rename = getrenamed(fn, rev)
1828 rename = getrenamed(fn, rev)
1830 if rename:
1829 if rename:
1831 copies.append((fn, rename[0]))
1830 copies.append((fn, rename[0]))
1832 displayer.show(rev, changenode, copies=copies)
1831 displayer.show(rev, changenode, copies=copies)
1833 elif st == 'iter':
1832 elif st == 'iter':
1834 if count == limit: break
1833 if count == limit: break
1835 if displayer.flush(rev):
1834 if displayer.flush(rev):
1836 count += 1
1835 count += 1
1837
1836
1838 def manifest(ui, repo, node=None, rev=None):
1837 def manifest(ui, repo, node=None, rev=None):
1839 """output the current or given revision of the project manifest
1838 """output the current or given revision of the project manifest
1840
1839
1841 Print a list of version controlled files for the given revision.
1840 Print a list of version controlled files for the given revision.
1842 If no revision is given, the parent of the working directory is used,
1841 If no revision is given, the parent of the working directory is used,
1843 or tip if no revision is checked out.
1842 or tip if no revision is checked out.
1844
1843
1845 The manifest is the list of files being version controlled. If no revision
1844 The manifest is the list of files being version controlled. If no revision
1846 is given then the first parent of the working directory is used.
1845 is given then the first parent of the working directory is used.
1847
1846
1848 With -v flag, print file permissions, symlink and executable bits. With
1847 With -v flag, print file permissions, symlink and executable bits. With
1849 --debug flag, print file revision hashes.
1848 --debug flag, print file revision hashes.
1850 """
1849 """
1851
1850
1852 if rev and node:
1851 if rev and node:
1853 raise util.Abort(_("please specify just one revision"))
1852 raise util.Abort(_("please specify just one revision"))
1854
1853
1855 if not node:
1854 if not node:
1856 node = rev
1855 node = rev
1857
1856
1858 m = repo.changectx(node).manifest()
1857 m = repo.changectx(node).manifest()
1859 files = m.keys()
1858 files = m.keys()
1860 files.sort()
1859 files.sort()
1861
1860
1862 for f in files:
1861 for f in files:
1863 if ui.debugflag:
1862 if ui.debugflag:
1864 ui.write("%40s " % hex(m[f]))
1863 ui.write("%40s " % hex(m[f]))
1865 if ui.verbose:
1864 if ui.verbose:
1866 type = m.execf(f) and "*" or m.linkf(f) and "@" or " "
1865 type = m.execf(f) and "*" or m.linkf(f) and "@" or " "
1867 perm = m.execf(f) and "755" or "644"
1866 perm = m.execf(f) and "755" or "644"
1868 ui.write("%3s %1s " % (perm, type))
1867 ui.write("%3s %1s " % (perm, type))
1869 ui.write("%s\n" % f)
1868 ui.write("%s\n" % f)
1870
1869
1871 def merge(ui, repo, node=None, force=None, rev=None):
1870 def merge(ui, repo, node=None, force=None, rev=None):
1872 """merge working directory with another revision
1871 """merge working directory with another revision
1873
1872
1874 Merge the contents of the current working directory and the
1873 Merge the contents of the current working directory and the
1875 requested revision. Files that changed between either parent are
1874 requested revision. Files that changed between either parent are
1876 marked as changed for the next commit and a commit must be
1875 marked as changed for the next commit and a commit must be
1877 performed before any further updates are allowed.
1876 performed before any further updates are allowed.
1878
1877
1879 If no revision is specified, the working directory's parent is a
1878 If no revision is specified, the working directory's parent is a
1880 head revision, and the repository contains exactly one other head,
1879 head revision, and the repository contains exactly one other head,
1881 the other head is merged with by default. Otherwise, an explicit
1880 the other head is merged with by default. Otherwise, an explicit
1882 revision to merge with must be provided.
1881 revision to merge with must be provided.
1883 """
1882 """
1884
1883
1885 if rev and node:
1884 if rev and node:
1886 raise util.Abort(_("please specify just one revision"))
1885 raise util.Abort(_("please specify just one revision"))
1887 if not node:
1886 if not node:
1888 node = rev
1887 node = rev
1889
1888
1890 if not node:
1889 if not node:
1891 heads = repo.heads()
1890 heads = repo.heads()
1892 if len(heads) > 2:
1891 if len(heads) > 2:
1893 raise util.Abort(_('repo has %d heads - '
1892 raise util.Abort(_('repo has %d heads - '
1894 'please merge with an explicit rev') %
1893 'please merge with an explicit rev') %
1895 len(heads))
1894 len(heads))
1896 parent = repo.dirstate.parents()[0]
1895 parent = repo.dirstate.parents()[0]
1897 if len(heads) == 1:
1896 if len(heads) == 1:
1898 msg = _('there is nothing to merge')
1897 msg = _('there is nothing to merge')
1899 if parent != repo.lookup(repo.workingctx().branch()):
1898 if parent != repo.lookup(repo.workingctx().branch()):
1900 msg = _('%s - use "hg update" instead') % msg
1899 msg = _('%s - use "hg update" instead') % msg
1901 raise util.Abort(msg)
1900 raise util.Abort(msg)
1902
1901
1903 if parent not in heads:
1902 if parent not in heads:
1904 raise util.Abort(_('working dir not at a head rev - '
1903 raise util.Abort(_('working dir not at a head rev - '
1905 'use "hg update" or merge with an explicit rev'))
1904 'use "hg update" or merge with an explicit rev'))
1906 node = parent == heads[0] and heads[-1] or heads[0]
1905 node = parent == heads[0] and heads[-1] or heads[0]
1907 return hg.merge(repo, node, force=force)
1906 return hg.merge(repo, node, force=force)
1908
1907
1909 def outgoing(ui, repo, dest=None, **opts):
1908 def outgoing(ui, repo, dest=None, **opts):
1910 """show changesets not found in destination
1909 """show changesets not found in destination
1911
1910
1912 Show changesets not found in the specified destination repository or
1911 Show changesets not found in the specified destination repository or
1913 the default push location. These are the changesets that would be pushed
1912 the default push location. These are the changesets that would be pushed
1914 if a push was requested.
1913 if a push was requested.
1915
1914
1916 See pull for valid destination format details.
1915 See pull for valid destination format details.
1917 """
1916 """
1918 limit = cmdutil.loglimit(opts)
1917 limit = cmdutil.loglimit(opts)
1919 dest, revs, checkout = hg.parseurl(
1918 dest, revs, checkout = hg.parseurl(
1920 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1919 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
1921 cmdutil.setremoteconfig(ui, opts)
1920 cmdutil.setremoteconfig(ui, opts)
1922 if revs:
1921 if revs:
1923 revs = [repo.lookup(rev) for rev in revs]
1922 revs = [repo.lookup(rev) for rev in revs]
1924
1923
1925 other = hg.repository(ui, dest)
1924 other = hg.repository(ui, dest)
1926 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
1925 ui.status(_('comparing with %s\n') % util.hidepassword(dest))
1927 o = repo.findoutgoing(other, force=opts['force'])
1926 o = repo.findoutgoing(other, force=opts['force'])
1928 if not o:
1927 if not o:
1929 ui.status(_("no changes found\n"))
1928 ui.status(_("no changes found\n"))
1930 return 1
1929 return 1
1931 o = repo.changelog.nodesbetween(o, revs)[0]
1930 o = repo.changelog.nodesbetween(o, revs)[0]
1932 if opts['newest_first']:
1931 if opts['newest_first']:
1933 o.reverse()
1932 o.reverse()
1934 displayer = cmdutil.show_changeset(ui, repo, opts)
1933 displayer = cmdutil.show_changeset(ui, repo, opts)
1935 count = 0
1934 count = 0
1936 for n in o:
1935 for n in o:
1937 if count >= limit:
1936 if count >= limit:
1938 break
1937 break
1939 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1938 parents = [p for p in repo.changelog.parents(n) if p != nullid]
1940 if opts['no_merges'] and len(parents) == 2:
1939 if opts['no_merges'] and len(parents) == 2:
1941 continue
1940 continue
1942 count += 1
1941 count += 1
1943 displayer.show(changenode=n)
1942 displayer.show(changenode=n)
1944
1943
1945 def parents(ui, repo, file_=None, **opts):
1944 def parents(ui, repo, file_=None, **opts):
1946 """show the parents of the working dir or revision
1945 """show the parents of the working dir or revision
1947
1946
1948 Print the working directory's parent revisions. If a
1947 Print the working directory's parent revisions. If a
1949 revision is given via --rev, the parent of that revision
1948 revision is given via --rev, the parent of that revision
1950 will be printed. If a file argument is given, revision in
1949 will be printed. If a file argument is given, revision in
1951 which the file was last changed (before the working directory
1950 which the file was last changed (before the working directory
1952 revision or the argument to --rev if given) is printed.
1951 revision or the argument to --rev if given) is printed.
1953 """
1952 """
1954 rev = opts.get('rev')
1953 rev = opts.get('rev')
1955 if rev:
1954 if rev:
1956 ctx = repo.changectx(rev)
1955 ctx = repo.changectx(rev)
1957 else:
1956 else:
1958 ctx = repo.workingctx()
1957 ctx = repo.workingctx()
1959
1958
1960 if file_:
1959 if file_:
1961 files, match, anypats = cmdutil.matchpats(repo, (file_,), opts)
1960 m = cmdutil.match(repo, (file_,), opts)
1962 if anypats or len(files) != 1:
1961 if m.anypats() or len(m.files()) != 1:
1963 raise util.Abort(_('can only specify an explicit file name'))
1962 raise util.Abort(_('can only specify an explicit file name'))
1964 file_ = files[0]
1963 file_ = m.files()[0]
1965 filenodes = []
1964 filenodes = []
1966 for cp in ctx.parents():
1965 for cp in ctx.parents():
1967 if not cp:
1966 if not cp:
1968 continue
1967 continue
1969 try:
1968 try:
1970 filenodes.append(cp.filenode(file_))
1969 filenodes.append(cp.filenode(file_))
1971 except revlog.LookupError:
1970 except revlog.LookupError:
1972 pass
1971 pass
1973 if not filenodes:
1972 if not filenodes:
1974 raise util.Abort(_("'%s' not found in manifest!") % file_)
1973 raise util.Abort(_("'%s' not found in manifest!") % file_)
1975 fl = repo.file(file_)
1974 fl = repo.file(file_)
1976 p = [repo.lookup(fl.linkrev(fn)) for fn in filenodes]
1975 p = [repo.lookup(fl.linkrev(fn)) for fn in filenodes]
1977 else:
1976 else:
1978 p = [cp.node() for cp in ctx.parents()]
1977 p = [cp.node() for cp in ctx.parents()]
1979
1978
1980 displayer = cmdutil.show_changeset(ui, repo, opts)
1979 displayer = cmdutil.show_changeset(ui, repo, opts)
1981 for n in p:
1980 for n in p:
1982 if n != nullid:
1981 if n != nullid:
1983 displayer.show(changenode=n)
1982 displayer.show(changenode=n)
1984
1983
1985 def paths(ui, repo, search=None):
1984 def paths(ui, repo, search=None):
1986 """show definition of symbolic path names
1985 """show definition of symbolic path names
1987
1986
1988 Show definition of symbolic path name NAME. If no name is given, show
1987 Show definition of symbolic path name NAME. If no name is given, show
1989 definition of available names.
1988 definition of available names.
1990
1989
1991 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1990 Path names are defined in the [paths] section of /etc/mercurial/hgrc
1992 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1991 and $HOME/.hgrc. If run inside a repository, .hg/hgrc is used, too.
1993 """
1992 """
1994 if search:
1993 if search:
1995 for name, path in ui.configitems("paths"):
1994 for name, path in ui.configitems("paths"):
1996 if name == search:
1995 if name == search:
1997 ui.write("%s\n" % util.hidepassword(path))
1996 ui.write("%s\n" % util.hidepassword(path))
1998 return
1997 return
1999 ui.warn(_("not found!\n"))
1998 ui.warn(_("not found!\n"))
2000 return 1
1999 return 1
2001 else:
2000 else:
2002 for name, path in ui.configitems("paths"):
2001 for name, path in ui.configitems("paths"):
2003 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
2002 ui.write("%s = %s\n" % (name, util.hidepassword(path)))
2004
2003
2005 def postincoming(ui, repo, modheads, optupdate, checkout):
2004 def postincoming(ui, repo, modheads, optupdate, checkout):
2006 if modheads == 0:
2005 if modheads == 0:
2007 return
2006 return
2008 if optupdate:
2007 if optupdate:
2009 if modheads <= 1 or checkout:
2008 if modheads <= 1 or checkout:
2010 return hg.update(repo, checkout)
2009 return hg.update(repo, checkout)
2011 else:
2010 else:
2012 ui.status(_("not updating, since new heads added\n"))
2011 ui.status(_("not updating, since new heads added\n"))
2013 if modheads > 1:
2012 if modheads > 1:
2014 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2013 ui.status(_("(run 'hg heads' to see heads, 'hg merge' to merge)\n"))
2015 else:
2014 else:
2016 ui.status(_("(run 'hg update' to get a working copy)\n"))
2015 ui.status(_("(run 'hg update' to get a working copy)\n"))
2017
2016
2018 def pull(ui, repo, source="default", **opts):
2017 def pull(ui, repo, source="default", **opts):
2019 """pull changes from the specified source
2018 """pull changes from the specified source
2020
2019
2021 Pull changes from a remote repository to a local one.
2020 Pull changes from a remote repository to a local one.
2022
2021
2023 This finds all changes from the repository at the specified path
2022 This finds all changes from the repository at the specified path
2024 or URL and adds them to the local repository. By default, this
2023 or URL and adds them to the local repository. By default, this
2025 does not update the copy of the project in the working directory.
2024 does not update the copy of the project in the working directory.
2026
2025
2027 Valid URLs are of the form:
2026 Valid URLs are of the form:
2028
2027
2029 local/filesystem/path (or file://local/filesystem/path)
2028 local/filesystem/path (or file://local/filesystem/path)
2030 http://[user@]host[:port]/[path]
2029 http://[user@]host[:port]/[path]
2031 https://[user@]host[:port]/[path]
2030 https://[user@]host[:port]/[path]
2032 ssh://[user@]host[:port]/[path]
2031 ssh://[user@]host[:port]/[path]
2033 static-http://host[:port]/[path]
2032 static-http://host[:port]/[path]
2034
2033
2035 Paths in the local filesystem can either point to Mercurial
2034 Paths in the local filesystem can either point to Mercurial
2036 repositories or to bundle files (as created by 'hg bundle' or
2035 repositories or to bundle files (as created by 'hg bundle' or
2037 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2036 'hg incoming --bundle'). The static-http:// protocol, albeit slow,
2038 allows access to a Mercurial repository where you simply use a web
2037 allows access to a Mercurial repository where you simply use a web
2039 server to publish the .hg directory as static content.
2038 server to publish the .hg directory as static content.
2040
2039
2041 An optional identifier after # indicates a particular branch, tag,
2040 An optional identifier after # indicates a particular branch, tag,
2042 or changeset to pull.
2041 or changeset to pull.
2043
2042
2044 Some notes about using SSH with Mercurial:
2043 Some notes about using SSH with Mercurial:
2045 - SSH requires an accessible shell account on the destination machine
2044 - SSH requires an accessible shell account on the destination machine
2046 and a copy of hg in the remote path or specified with as remotecmd.
2045 and a copy of hg in the remote path or specified with as remotecmd.
2047 - path is relative to the remote user's home directory by default.
2046 - path is relative to the remote user's home directory by default.
2048 Use an extra slash at the start of a path to specify an absolute path:
2047 Use an extra slash at the start of a path to specify an absolute path:
2049 ssh://example.com//tmp/repository
2048 ssh://example.com//tmp/repository
2050 - Mercurial doesn't use its own compression via SSH; the right thing
2049 - Mercurial doesn't use its own compression via SSH; the right thing
2051 to do is to configure it in your ~/.ssh/config, e.g.:
2050 to do is to configure it in your ~/.ssh/config, e.g.:
2052 Host *.mylocalnetwork.example.com
2051 Host *.mylocalnetwork.example.com
2053 Compression no
2052 Compression no
2054 Host *
2053 Host *
2055 Compression yes
2054 Compression yes
2056 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2055 Alternatively specify "ssh -C" as your ssh command in your hgrc or
2057 with the --ssh command line option.
2056 with the --ssh command line option.
2058 """
2057 """
2059 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
2058 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts['rev'])
2060 cmdutil.setremoteconfig(ui, opts)
2059 cmdutil.setremoteconfig(ui, opts)
2061
2060
2062 other = hg.repository(ui, source)
2061 other = hg.repository(ui, source)
2063 ui.status(_('pulling from %s\n') % util.hidepassword(source))
2062 ui.status(_('pulling from %s\n') % util.hidepassword(source))
2064 if revs:
2063 if revs:
2065 try:
2064 try:
2066 revs = [other.lookup(rev) for rev in revs]
2065 revs = [other.lookup(rev) for rev in revs]
2067 except NoCapability:
2066 except NoCapability:
2068 error = _("Other repository doesn't support revision lookup, "
2067 error = _("Other repository doesn't support revision lookup, "
2069 "so a rev cannot be specified.")
2068 "so a rev cannot be specified.")
2070 raise util.Abort(error)
2069 raise util.Abort(error)
2071
2070
2072 modheads = repo.pull(other, heads=revs, force=opts['force'])
2071 modheads = repo.pull(other, heads=revs, force=opts['force'])
2073 return postincoming(ui, repo, modheads, opts['update'], checkout)
2072 return postincoming(ui, repo, modheads, opts['update'], checkout)
2074
2073
2075 def push(ui, repo, dest=None, **opts):
2074 def push(ui, repo, dest=None, **opts):
2076 """push changes to the specified destination
2075 """push changes to the specified destination
2077
2076
2078 Push changes from the local repository to the given destination.
2077 Push changes from the local repository to the given destination.
2079
2078
2080 This is the symmetrical operation for pull. It helps to move
2079 This is the symmetrical operation for pull. It helps to move
2081 changes from the current repository to a different one. If the
2080 changes from the current repository to a different one. If the
2082 destination is local this is identical to a pull in that directory
2081 destination is local this is identical to a pull in that directory
2083 from the current one.
2082 from the current one.
2084
2083
2085 By default, push will refuse to run if it detects the result would
2084 By default, push will refuse to run if it detects the result would
2086 increase the number of remote heads. This generally indicates the
2085 increase the number of remote heads. This generally indicates the
2087 the client has forgotten to sync and merge before pushing.
2086 the client has forgotten to sync and merge before pushing.
2088
2087
2089 Valid URLs are of the form:
2088 Valid URLs are of the form:
2090
2089
2091 local/filesystem/path (or file://local/filesystem/path)
2090 local/filesystem/path (or file://local/filesystem/path)
2092 ssh://[user@]host[:port]/[path]
2091 ssh://[user@]host[:port]/[path]
2093 http://[user@]host[:port]/[path]
2092 http://[user@]host[:port]/[path]
2094 https://[user@]host[:port]/[path]
2093 https://[user@]host[:port]/[path]
2095
2094
2096 An optional identifier after # indicates a particular branch, tag,
2095 An optional identifier after # indicates a particular branch, tag,
2097 or changeset to push.
2096 or changeset to push.
2098
2097
2099 Look at the help text for the pull command for important details
2098 Look at the help text for the pull command for important details
2100 about ssh:// URLs.
2099 about ssh:// URLs.
2101
2100
2102 Pushing to http:// and https:// URLs is only possible, if this
2101 Pushing to http:// and https:// URLs is only possible, if this
2103 feature is explicitly enabled on the remote Mercurial server.
2102 feature is explicitly enabled on the remote Mercurial server.
2104 """
2103 """
2105 dest, revs, checkout = hg.parseurl(
2104 dest, revs, checkout = hg.parseurl(
2106 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2105 ui.expandpath(dest or 'default-push', dest or 'default'), opts['rev'])
2107 cmdutil.setremoteconfig(ui, opts)
2106 cmdutil.setremoteconfig(ui, opts)
2108
2107
2109 other = hg.repository(ui, dest)
2108 other = hg.repository(ui, dest)
2110 ui.status('pushing to %s\n' % util.hidepassword(dest))
2109 ui.status('pushing to %s\n' % util.hidepassword(dest))
2111 if revs:
2110 if revs:
2112 revs = [repo.lookup(rev) for rev in revs]
2111 revs = [repo.lookup(rev) for rev in revs]
2113 r = repo.push(other, opts['force'], revs=revs)
2112 r = repo.push(other, opts['force'], revs=revs)
2114 return r == 0
2113 return r == 0
2115
2114
2116 def rawcommit(ui, repo, *pats, **opts):
2115 def rawcommit(ui, repo, *pats, **opts):
2117 """raw commit interface (DEPRECATED)
2116 """raw commit interface (DEPRECATED)
2118
2117
2119 (DEPRECATED)
2118 (DEPRECATED)
2120 Lowlevel commit, for use in helper scripts.
2119 Lowlevel commit, for use in helper scripts.
2121
2120
2122 This command is not intended to be used by normal users, as it is
2121 This command is not intended to be used by normal users, as it is
2123 primarily useful for importing from other SCMs.
2122 primarily useful for importing from other SCMs.
2124
2123
2125 This command is now deprecated and will be removed in a future
2124 This command is now deprecated and will be removed in a future
2126 release, please use debugsetparents and commit instead.
2125 release, please use debugsetparents and commit instead.
2127 """
2126 """
2128
2127
2129 ui.warn(_("(the rawcommit command is deprecated)\n"))
2128 ui.warn(_("(the rawcommit command is deprecated)\n"))
2130
2129
2131 message = cmdutil.logmessage(opts)
2130 message = cmdutil.logmessage(opts)
2132
2131
2133 files, match, anypats = cmdutil.matchpats(repo, pats, opts)
2132 files = cmdutil.match(repo, pats, opts).files()
2134 if opts['files']:
2133 if opts['files']:
2135 files += open(opts['files']).read().splitlines()
2134 files += open(opts['files']).read().splitlines()
2136
2135
2137 parents = [repo.lookup(p) for p in opts['parent']]
2136 parents = [repo.lookup(p) for p in opts['parent']]
2138
2137
2139 try:
2138 try:
2140 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2139 repo.rawcommit(files, message, opts['user'], opts['date'], *parents)
2141 except ValueError, inst:
2140 except ValueError, inst:
2142 raise util.Abort(str(inst))
2141 raise util.Abort(str(inst))
2143
2142
2144 def recover(ui, repo):
2143 def recover(ui, repo):
2145 """roll back an interrupted transaction
2144 """roll back an interrupted transaction
2146
2145
2147 Recover from an interrupted commit or pull.
2146 Recover from an interrupted commit or pull.
2148
2147
2149 This command tries to fix the repository status after an interrupted
2148 This command tries to fix the repository status after an interrupted
2150 operation. It should only be necessary when Mercurial suggests it.
2149 operation. It should only be necessary when Mercurial suggests it.
2151 """
2150 """
2152 if repo.recover():
2151 if repo.recover():
2153 return hg.verify(repo)
2152 return hg.verify(repo)
2154 return 1
2153 return 1
2155
2154
2156 def remove(ui, repo, *pats, **opts):
2155 def remove(ui, repo, *pats, **opts):
2157 """remove the specified files on the next commit
2156 """remove the specified files on the next commit
2158
2157
2159 Schedule the indicated files for removal from the repository.
2158 Schedule the indicated files for removal from the repository.
2160
2159
2161 This only removes files from the current branch, not from the entire
2160 This only removes files from the current branch, not from the entire
2162 project history. -A can be used to remove only files that have already
2161 project history. -A can be used to remove only files that have already
2163 been deleted, -f can be used to force deletion, and -Af can be used
2162 been deleted, -f can be used to force deletion, and -Af can be used
2164 to remove files from the next revision without deleting them.
2163 to remove files from the next revision without deleting them.
2165
2164
2166 The following table details the behavior of remove for different file
2165 The following table details the behavior of remove for different file
2167 states (columns) and option combinations (rows). The file states are
2166 states (columns) and option combinations (rows). The file states are
2168 Added, Clean, Modified and Missing (as reported by hg status). The
2167 Added, Clean, Modified and Missing (as reported by hg status). The
2169 actions are Warn, Remove (from branch) and Delete (from disk).
2168 actions are Warn, Remove (from branch) and Delete (from disk).
2170
2169
2171 A C M !
2170 A C M !
2172 none W RD W R
2171 none W RD W R
2173 -f R RD RD R
2172 -f R RD RD R
2174 -A W W W R
2173 -A W W W R
2175 -Af R R R R
2174 -Af R R R R
2176
2175
2177 This command schedules the files to be removed at the next commit.
2176 This command schedules the files to be removed at the next commit.
2178 To undo a remove before that, see hg revert.
2177 To undo a remove before that, see hg revert.
2179 """
2178 """
2180
2179
2181 after, force = opts.get('after'), opts.get('force')
2180 after, force = opts.get('after'), opts.get('force')
2182 if not pats and not after:
2181 if not pats and not after:
2183 raise util.Abort(_('no files specified'))
2182 raise util.Abort(_('no files specified'))
2184
2183
2185 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2184 m = cmdutil.match(repo, pats, opts)
2186 mardu = map(dict.fromkeys, repo.status(files=files, match=matchfn))[:5]
2185 mardu = map(dict.fromkeys, repo.status(files=m.files(), match=m))[:5]
2187 modified, added, removed, deleted, unknown = mardu
2186 modified, added, removed, deleted, unknown = mardu
2188
2187
2189 remove, forget = [], []
2188 remove, forget = [], []
2190 m = cmdutil.match(repo, pats, opts)
2191 for src, abs, rel, exact in cmdutil.walk(repo, m):
2189 for src, abs, rel, exact in cmdutil.walk(repo, m):
2192
2190
2193 reason = None
2191 reason = None
2194 if abs in removed or abs in unknown:
2192 if abs in removed or abs in unknown:
2195 continue
2193 continue
2196
2194
2197 # last column
2195 # last column
2198 elif abs in deleted:
2196 elif abs in deleted:
2199 remove.append(abs)
2197 remove.append(abs)
2200
2198
2201 # rest of the third row
2199 # rest of the third row
2202 elif after and not force:
2200 elif after and not force:
2203 reason = _('still exists (use -f to force removal)')
2201 reason = _('still exists (use -f to force removal)')
2204
2202
2205 # rest of the first column
2203 # rest of the first column
2206 elif abs in added:
2204 elif abs in added:
2207 if not force:
2205 if not force:
2208 reason = _('has been marked for add (use -f to force removal)')
2206 reason = _('has been marked for add (use -f to force removal)')
2209 else:
2207 else:
2210 forget.append(abs)
2208 forget.append(abs)
2211
2209
2212 # rest of the third column
2210 # rest of the third column
2213 elif abs in modified:
2211 elif abs in modified:
2214 if not force:
2212 if not force:
2215 reason = _('is modified (use -f to force removal)')
2213 reason = _('is modified (use -f to force removal)')
2216 else:
2214 else:
2217 remove.append(abs)
2215 remove.append(abs)
2218
2216
2219 # rest of the second column
2217 # rest of the second column
2220 elif not reason:
2218 elif not reason:
2221 remove.append(abs)
2219 remove.append(abs)
2222
2220
2223 if reason:
2221 if reason:
2224 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2222 ui.warn(_('not removing %s: file %s\n') % (rel, reason))
2225 elif ui.verbose or not exact:
2223 elif ui.verbose or not exact:
2226 ui.status(_('removing %s\n') % rel)
2224 ui.status(_('removing %s\n') % rel)
2227
2225
2228 repo.forget(forget)
2226 repo.forget(forget)
2229 repo.remove(remove, unlink=not after)
2227 repo.remove(remove, unlink=not after)
2230
2228
2231 def rename(ui, repo, *pats, **opts):
2229 def rename(ui, repo, *pats, **opts):
2232 """rename files; equivalent of copy + remove
2230 """rename files; equivalent of copy + remove
2233
2231
2234 Mark dest as copies of sources; mark sources for deletion. If
2232 Mark dest as copies of sources; mark sources for deletion. If
2235 dest is a directory, copies are put in that directory. If dest is
2233 dest is a directory, copies are put in that directory. If dest is
2236 a file, there can only be one source.
2234 a file, there can only be one source.
2237
2235
2238 By default, this command copies the contents of files as they
2236 By default, this command copies the contents of files as they
2239 stand in the working directory. If invoked with --after, the
2237 stand in the working directory. If invoked with --after, the
2240 operation is recorded, but no copying is performed.
2238 operation is recorded, but no copying is performed.
2241
2239
2242 This command takes effect in the next commit. To undo a rename
2240 This command takes effect in the next commit. To undo a rename
2243 before that, see hg revert.
2241 before that, see hg revert.
2244 """
2242 """
2245 wlock = repo.wlock(False)
2243 wlock = repo.wlock(False)
2246 try:
2244 try:
2247 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2245 return cmdutil.copy(ui, repo, pats, opts, rename=True)
2248 finally:
2246 finally:
2249 del wlock
2247 del wlock
2250
2248
2251 def resolve(ui, repo, *pats, **opts):
2249 def resolve(ui, repo, *pats, **opts):
2252 """resolve file merges from a branch merge or update
2250 """resolve file merges from a branch merge or update
2253
2251
2254 This command will attempt to resolve unresolved merges from the
2252 This command will attempt to resolve unresolved merges from the
2255 last update or merge command. This will use the local file
2253 last update or merge command. This will use the local file
2256 revision preserved at the last update or merge to cleanly retry
2254 revision preserved at the last update or merge to cleanly retry
2257 the file merge attempt. With no file or options specified, this
2255 the file merge attempt. With no file or options specified, this
2258 command will attempt to resolve all unresolved files.
2256 command will attempt to resolve all unresolved files.
2259 """
2257 """
2260
2258
2261 if len([x for x in opts if opts[x]]) > 1:
2259 if len([x for x in opts if opts[x]]) > 1:
2262 raise util.Abort(_("too many options specified"))
2260 raise util.Abort(_("too many options specified"))
2263
2261
2264 ms = merge_.mergestate(repo)
2262 ms = merge_.mergestate(repo)
2265 mf = util.matcher(repo.root, "", pats, [], [])[1]
2263 mf = util.matcher(repo.root, "", pats, [], [])[1]
2266
2264
2267 for f in ms:
2265 for f in ms:
2268 if mf(f):
2266 if mf(f):
2269 if opts.get("list"):
2267 if opts.get("list"):
2270 ui.write("%s %s\n" % (ms[f].upper(), f))
2268 ui.write("%s %s\n" % (ms[f].upper(), f))
2271 elif opts.get("mark"):
2269 elif opts.get("mark"):
2272 ms.mark(f, "r")
2270 ms.mark(f, "r")
2273 elif opts.get("unmark"):
2271 elif opts.get("unmark"):
2274 ms.mark(f, "u")
2272 ms.mark(f, "u")
2275 else:
2273 else:
2276 wctx = repo.workingctx()
2274 wctx = repo.workingctx()
2277 mctx = wctx.parents()[-1]
2275 mctx = wctx.parents()[-1]
2278 ms.resolve(f, wctx, mctx)
2276 ms.resolve(f, wctx, mctx)
2279
2277
2280 def revert(ui, repo, *pats, **opts):
2278 def revert(ui, repo, *pats, **opts):
2281 """restore individual files or dirs to an earlier state
2279 """restore individual files or dirs to an earlier state
2282
2280
2283 (use update -r to check out earlier revisions, revert does not
2281 (use update -r to check out earlier revisions, revert does not
2284 change the working dir parents)
2282 change the working dir parents)
2285
2283
2286 With no revision specified, revert the named files or directories
2284 With no revision specified, revert the named files or directories
2287 to the contents they had in the parent of the working directory.
2285 to the contents they had in the parent of the working directory.
2288 This restores the contents of the affected files to an unmodified
2286 This restores the contents of the affected files to an unmodified
2289 state and unschedules adds, removes, copies, and renames. If the
2287 state and unschedules adds, removes, copies, and renames. If the
2290 working directory has two parents, you must explicitly specify the
2288 working directory has two parents, you must explicitly specify the
2291 revision to revert to.
2289 revision to revert to.
2292
2290
2293 Using the -r option, revert the given files or directories to their
2291 Using the -r option, revert the given files or directories to their
2294 contents as of a specific revision. This can be helpful to "roll
2292 contents as of a specific revision. This can be helpful to "roll
2295 back" some or all of an earlier change.
2293 back" some or all of an earlier change.
2296 See 'hg help dates' for a list of formats valid for -d/--date.
2294 See 'hg help dates' for a list of formats valid for -d/--date.
2297
2295
2298 Revert modifies the working directory. It does not commit any
2296 Revert modifies the working directory. It does not commit any
2299 changes, or change the parent of the working directory. If you
2297 changes, or change the parent of the working directory. If you
2300 revert to a revision other than the parent of the working
2298 revert to a revision other than the parent of the working
2301 directory, the reverted files will thus appear modified
2299 directory, the reverted files will thus appear modified
2302 afterwards.
2300 afterwards.
2303
2301
2304 If a file has been deleted, it is restored. If the executable
2302 If a file has been deleted, it is restored. If the executable
2305 mode of a file was changed, it is reset.
2303 mode of a file was changed, it is reset.
2306
2304
2307 If names are given, all files matching the names are reverted.
2305 If names are given, all files matching the names are reverted.
2308 If no arguments are given, no files are reverted.
2306 If no arguments are given, no files are reverted.
2309
2307
2310 Modified files are saved with a .orig suffix before reverting.
2308 Modified files are saved with a .orig suffix before reverting.
2311 To disable these backups, use --no-backup.
2309 To disable these backups, use --no-backup.
2312 """
2310 """
2313
2311
2314 if opts["date"]:
2312 if opts["date"]:
2315 if opts["rev"]:
2313 if opts["rev"]:
2316 raise util.Abort(_("you can't specify a revision and a date"))
2314 raise util.Abort(_("you can't specify a revision and a date"))
2317 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2315 opts["rev"] = cmdutil.finddate(ui, repo, opts["date"])
2318
2316
2319 if not pats and not opts['all']:
2317 if not pats and not opts['all']:
2320 raise util.Abort(_('no files or directories specified; '
2318 raise util.Abort(_('no files or directories specified; '
2321 'use --all to revert the whole repo'))
2319 'use --all to revert the whole repo'))
2322
2320
2323 parent, p2 = repo.dirstate.parents()
2321 parent, p2 = repo.dirstate.parents()
2324 if not opts['rev'] and p2 != nullid:
2322 if not opts['rev'] and p2 != nullid:
2325 raise util.Abort(_('uncommitted merge - please provide a '
2323 raise util.Abort(_('uncommitted merge - please provide a '
2326 'specific revision'))
2324 'specific revision'))
2327 ctx = repo.changectx(opts['rev'])
2325 ctx = repo.changectx(opts['rev'])
2328 node = ctx.node()
2326 node = ctx.node()
2329 mf = ctx.manifest()
2327 mf = ctx.manifest()
2330 if node == parent:
2328 if node == parent:
2331 pmf = mf
2329 pmf = mf
2332 else:
2330 else:
2333 pmf = None
2331 pmf = None
2334
2332
2335 # need all matching names in dirstate and manifest of target rev,
2333 # need all matching names in dirstate and manifest of target rev,
2336 # so have to walk both. do not print errors if files exist in one
2334 # so have to walk both. do not print errors if files exist in one
2337 # but not other.
2335 # but not other.
2338
2336
2339 names = {}
2337 names = {}
2340
2338
2341 wlock = repo.wlock()
2339 wlock = repo.wlock()
2342 try:
2340 try:
2343 # walk dirstate.
2341 # walk dirstate.
2344 files = []
2342 files = []
2345
2343
2346 m = cmdutil.match(repo, pats, opts)
2344 m = cmdutil.match(repo, pats, opts)
2347 def bad(f, msg):
2345 def bad(f, msg):
2348 if f not in mf:
2346 if f not in mf:
2349 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
2347 repo.ui.warn("%s: %s\n" % (m.rel(f), msg))
2350 return False
2348 return False
2351 m.bad = bad
2349 m.bad = bad
2352 for src, abs, rel, exact in cmdutil.walk(repo, m):
2350 for src, abs, rel, exact in cmdutil.walk(repo, m):
2353 names[abs] = (rel, exact)
2351 names[abs] = (rel, exact)
2354
2352
2355 # walk target manifest.
2353 # walk target manifest.
2356
2354
2357 def badfn(path, msg):
2355 def badfn(path, msg):
2358 if path in names:
2356 if path in names:
2359 return True
2357 return True
2360 path_ = path + '/'
2358 path_ = path + '/'
2361 for f in names:
2359 for f in names:
2362 if f.startswith(path_):
2360 if f.startswith(path_):
2363 return True
2361 return True
2364 return False
2362 return False
2365
2363
2366 m = cmdutil.match(repo, pats, opts)
2364 m = cmdutil.match(repo, pats, opts)
2367 m.bad = badfn
2365 m.bad = badfn
2368 for src, abs, rel, exact in cmdutil.walk(repo, m, node=node):
2366 for src, abs, rel, exact in cmdutil.walk(repo, m, node=node):
2369 if abs in names or src == 'b':
2367 if abs in names or src == 'b':
2370 continue
2368 continue
2371 names[abs] = (rel, exact)
2369 names[abs] = (rel, exact)
2372
2370
2373 changes = repo.status(files=files, match=names.has_key)[:4]
2371 changes = repo.status(files=files, match=names.has_key)[:4]
2374 modified, added, removed, deleted = map(dict.fromkeys, changes)
2372 modified, added, removed, deleted = map(dict.fromkeys, changes)
2375
2373
2376 # if f is a rename, also revert the source
2374 # if f is a rename, also revert the source
2377 cwd = repo.getcwd()
2375 cwd = repo.getcwd()
2378 for f in added:
2376 for f in added:
2379 src = repo.dirstate.copied(f)
2377 src = repo.dirstate.copied(f)
2380 if src and src not in names and repo.dirstate[src] == 'r':
2378 if src and src not in names and repo.dirstate[src] == 'r':
2381 removed[src] = None
2379 removed[src] = None
2382 names[src] = (repo.pathto(src, cwd), True)
2380 names[src] = (repo.pathto(src, cwd), True)
2383
2381
2384 def removeforget(abs):
2382 def removeforget(abs):
2385 if repo.dirstate[abs] == 'a':
2383 if repo.dirstate[abs] == 'a':
2386 return _('forgetting %s\n')
2384 return _('forgetting %s\n')
2387 return _('removing %s\n')
2385 return _('removing %s\n')
2388
2386
2389 revert = ([], _('reverting %s\n'))
2387 revert = ([], _('reverting %s\n'))
2390 add = ([], _('adding %s\n'))
2388 add = ([], _('adding %s\n'))
2391 remove = ([], removeforget)
2389 remove = ([], removeforget)
2392 undelete = ([], _('undeleting %s\n'))
2390 undelete = ([], _('undeleting %s\n'))
2393
2391
2394 disptable = (
2392 disptable = (
2395 # dispatch table:
2393 # dispatch table:
2396 # file state
2394 # file state
2397 # action if in target manifest
2395 # action if in target manifest
2398 # action if not in target manifest
2396 # action if not in target manifest
2399 # make backup if in target manifest
2397 # make backup if in target manifest
2400 # make backup if not in target manifest
2398 # make backup if not in target manifest
2401 (modified, revert, remove, True, True),
2399 (modified, revert, remove, True, True),
2402 (added, revert, remove, True, False),
2400 (added, revert, remove, True, False),
2403 (removed, undelete, None, False, False),
2401 (removed, undelete, None, False, False),
2404 (deleted, revert, remove, False, False),
2402 (deleted, revert, remove, False, False),
2405 )
2403 )
2406
2404
2407 entries = names.items()
2405 entries = names.items()
2408 entries.sort()
2406 entries.sort()
2409
2407
2410 for abs, (rel, exact) in entries:
2408 for abs, (rel, exact) in entries:
2411 mfentry = mf.get(abs)
2409 mfentry = mf.get(abs)
2412 target = repo.wjoin(abs)
2410 target = repo.wjoin(abs)
2413 def handle(xlist, dobackup):
2411 def handle(xlist, dobackup):
2414 xlist[0].append(abs)
2412 xlist[0].append(abs)
2415 if dobackup and not opts['no_backup'] and util.lexists(target):
2413 if dobackup and not opts['no_backup'] and util.lexists(target):
2416 bakname = "%s.orig" % rel
2414 bakname = "%s.orig" % rel
2417 ui.note(_('saving current version of %s as %s\n') %
2415 ui.note(_('saving current version of %s as %s\n') %
2418 (rel, bakname))
2416 (rel, bakname))
2419 if not opts.get('dry_run'):
2417 if not opts.get('dry_run'):
2420 util.copyfile(target, bakname)
2418 util.copyfile(target, bakname)
2421 if ui.verbose or not exact:
2419 if ui.verbose or not exact:
2422 msg = xlist[1]
2420 msg = xlist[1]
2423 if not isinstance(msg, basestring):
2421 if not isinstance(msg, basestring):
2424 msg = msg(abs)
2422 msg = msg(abs)
2425 ui.status(msg % rel)
2423 ui.status(msg % rel)
2426 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2424 for table, hitlist, misslist, backuphit, backupmiss in disptable:
2427 if abs not in table: continue
2425 if abs not in table: continue
2428 # file has changed in dirstate
2426 # file has changed in dirstate
2429 if mfentry:
2427 if mfentry:
2430 handle(hitlist, backuphit)
2428 handle(hitlist, backuphit)
2431 elif misslist is not None:
2429 elif misslist is not None:
2432 handle(misslist, backupmiss)
2430 handle(misslist, backupmiss)
2433 break
2431 break
2434 else:
2432 else:
2435 if abs not in repo.dirstate:
2433 if abs not in repo.dirstate:
2436 if mfentry:
2434 if mfentry:
2437 handle(add, True)
2435 handle(add, True)
2438 elif exact:
2436 elif exact:
2439 ui.warn(_('file not managed: %s\n') % rel)
2437 ui.warn(_('file not managed: %s\n') % rel)
2440 continue
2438 continue
2441 # file has not changed in dirstate
2439 # file has not changed in dirstate
2442 if node == parent:
2440 if node == parent:
2443 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2441 if exact: ui.warn(_('no changes needed to %s\n') % rel)
2444 continue
2442 continue
2445 if pmf is None:
2443 if pmf is None:
2446 # only need parent manifest in this unlikely case,
2444 # only need parent manifest in this unlikely case,
2447 # so do not read by default
2445 # so do not read by default
2448 pmf = repo.changectx(parent).manifest()
2446 pmf = repo.changectx(parent).manifest()
2449 if abs in pmf:
2447 if abs in pmf:
2450 if mfentry:
2448 if mfentry:
2451 # if version of file is same in parent and target
2449 # if version of file is same in parent and target
2452 # manifests, do nothing
2450 # manifests, do nothing
2453 if (pmf[abs] != mfentry or
2451 if (pmf[abs] != mfentry or
2454 pmf.flags(abs) != mf.flags(abs)):
2452 pmf.flags(abs) != mf.flags(abs)):
2455 handle(revert, False)
2453 handle(revert, False)
2456 else:
2454 else:
2457 handle(remove, False)
2455 handle(remove, False)
2458
2456
2459 if not opts.get('dry_run'):
2457 if not opts.get('dry_run'):
2460 def checkout(f):
2458 def checkout(f):
2461 fc = ctx[f]
2459 fc = ctx[f]
2462 repo.wwrite(f, fc.data(), fc.fileflags())
2460 repo.wwrite(f, fc.data(), fc.fileflags())
2463
2461
2464 audit_path = util.path_auditor(repo.root)
2462 audit_path = util.path_auditor(repo.root)
2465 for f in remove[0]:
2463 for f in remove[0]:
2466 if repo.dirstate[f] == 'a':
2464 if repo.dirstate[f] == 'a':
2467 repo.dirstate.forget(f)
2465 repo.dirstate.forget(f)
2468 continue
2466 continue
2469 audit_path(f)
2467 audit_path(f)
2470 try:
2468 try:
2471 util.unlink(repo.wjoin(f))
2469 util.unlink(repo.wjoin(f))
2472 except OSError:
2470 except OSError:
2473 pass
2471 pass
2474 repo.dirstate.remove(f)
2472 repo.dirstate.remove(f)
2475
2473
2476 normal = None
2474 normal = None
2477 if node == parent:
2475 if node == parent:
2478 # We're reverting to our parent. If possible, we'd like status
2476 # We're reverting to our parent. If possible, we'd like status
2479 # to report the file as clean. We have to use normallookup for
2477 # to report the file as clean. We have to use normallookup for
2480 # merges to avoid losing information about merged/dirty files.
2478 # merges to avoid losing information about merged/dirty files.
2481 if p2 != nullid:
2479 if p2 != nullid:
2482 normal = repo.dirstate.normallookup
2480 normal = repo.dirstate.normallookup
2483 else:
2481 else:
2484 normal = repo.dirstate.normal
2482 normal = repo.dirstate.normal
2485 for f in revert[0]:
2483 for f in revert[0]:
2486 checkout(f)
2484 checkout(f)
2487 if normal:
2485 if normal:
2488 normal(f)
2486 normal(f)
2489
2487
2490 for f in add[0]:
2488 for f in add[0]:
2491 checkout(f)
2489 checkout(f)
2492 repo.dirstate.add(f)
2490 repo.dirstate.add(f)
2493
2491
2494 normal = repo.dirstate.normallookup
2492 normal = repo.dirstate.normallookup
2495 if node == parent and p2 == nullid:
2493 if node == parent and p2 == nullid:
2496 normal = repo.dirstate.normal
2494 normal = repo.dirstate.normal
2497 for f in undelete[0]:
2495 for f in undelete[0]:
2498 checkout(f)
2496 checkout(f)
2499 normal(f)
2497 normal(f)
2500
2498
2501 finally:
2499 finally:
2502 del wlock
2500 del wlock
2503
2501
2504 def rollback(ui, repo):
2502 def rollback(ui, repo):
2505 """roll back the last transaction
2503 """roll back the last transaction
2506
2504
2507 This command should be used with care. There is only one level of
2505 This command should be used with care. There is only one level of
2508 rollback, and there is no way to undo a rollback. It will also
2506 rollback, and there is no way to undo a rollback. It will also
2509 restore the dirstate at the time of the last transaction, losing
2507 restore the dirstate at the time of the last transaction, losing
2510 any dirstate changes since that time.
2508 any dirstate changes since that time.
2511
2509
2512 Transactions are used to encapsulate the effects of all commands
2510 Transactions are used to encapsulate the effects of all commands
2513 that create new changesets or propagate existing changesets into a
2511 that create new changesets or propagate existing changesets into a
2514 repository. For example, the following commands are transactional,
2512 repository. For example, the following commands are transactional,
2515 and their effects can be rolled back:
2513 and their effects can be rolled back:
2516
2514
2517 commit
2515 commit
2518 import
2516 import
2519 pull
2517 pull
2520 push (with this repository as destination)
2518 push (with this repository as destination)
2521 unbundle
2519 unbundle
2522
2520
2523 This command is not intended for use on public repositories. Once
2521 This command is not intended for use on public repositories. Once
2524 changes are visible for pull by other users, rolling a transaction
2522 changes are visible for pull by other users, rolling a transaction
2525 back locally is ineffective (someone else may already have pulled
2523 back locally is ineffective (someone else may already have pulled
2526 the changes). Furthermore, a race is possible with readers of the
2524 the changes). Furthermore, a race is possible with readers of the
2527 repository; for example an in-progress pull from the repository
2525 repository; for example an in-progress pull from the repository
2528 may fail if a rollback is performed.
2526 may fail if a rollback is performed.
2529 """
2527 """
2530 repo.rollback()
2528 repo.rollback()
2531
2529
2532 def root(ui, repo):
2530 def root(ui, repo):
2533 """print the root (top) of the current working dir
2531 """print the root (top) of the current working dir
2534
2532
2535 Print the root directory of the current repository.
2533 Print the root directory of the current repository.
2536 """
2534 """
2537 ui.write(repo.root + "\n")
2535 ui.write(repo.root + "\n")
2538
2536
2539 def serve(ui, repo, **opts):
2537 def serve(ui, repo, **opts):
2540 """export the repository via HTTP
2538 """export the repository via HTTP
2541
2539
2542 Start a local HTTP repository browser and pull server.
2540 Start a local HTTP repository browser and pull server.
2543
2541
2544 By default, the server logs accesses to stdout and errors to
2542 By default, the server logs accesses to stdout and errors to
2545 stderr. Use the "-A" and "-E" options to log to files.
2543 stderr. Use the "-A" and "-E" options to log to files.
2546 """
2544 """
2547
2545
2548 if opts["stdio"]:
2546 if opts["stdio"]:
2549 if repo is None:
2547 if repo is None:
2550 raise RepoError(_("There is no Mercurial repository here"
2548 raise RepoError(_("There is no Mercurial repository here"
2551 " (.hg not found)"))
2549 " (.hg not found)"))
2552 s = sshserver.sshserver(ui, repo)
2550 s = sshserver.sshserver(ui, repo)
2553 s.serve_forever()
2551 s.serve_forever()
2554
2552
2555 parentui = ui.parentui or ui
2553 parentui = ui.parentui or ui
2556 optlist = ("name templates style address port prefix ipv6"
2554 optlist = ("name templates style address port prefix ipv6"
2557 " accesslog errorlog webdir_conf certificate")
2555 " accesslog errorlog webdir_conf certificate")
2558 for o in optlist.split():
2556 for o in optlist.split():
2559 if opts[o]:
2557 if opts[o]:
2560 parentui.setconfig("web", o, str(opts[o]))
2558 parentui.setconfig("web", o, str(opts[o]))
2561 if (repo is not None) and (repo.ui != parentui):
2559 if (repo is not None) and (repo.ui != parentui):
2562 repo.ui.setconfig("web", o, str(opts[o]))
2560 repo.ui.setconfig("web", o, str(opts[o]))
2563
2561
2564 if repo is None and not ui.config("web", "webdir_conf"):
2562 if repo is None and not ui.config("web", "webdir_conf"):
2565 raise RepoError(_("There is no Mercurial repository here"
2563 raise RepoError(_("There is no Mercurial repository here"
2566 " (.hg not found)"))
2564 " (.hg not found)"))
2567
2565
2568 class service:
2566 class service:
2569 def init(self):
2567 def init(self):
2570 util.set_signal_handler()
2568 util.set_signal_handler()
2571 self.httpd = hgweb.server.create_server(parentui, repo)
2569 self.httpd = hgweb.server.create_server(parentui, repo)
2572
2570
2573 if not ui.verbose: return
2571 if not ui.verbose: return
2574
2572
2575 if self.httpd.prefix:
2573 if self.httpd.prefix:
2576 prefix = self.httpd.prefix.strip('/') + '/'
2574 prefix = self.httpd.prefix.strip('/') + '/'
2577 else:
2575 else:
2578 prefix = ''
2576 prefix = ''
2579
2577
2580 port = ':%d' % self.httpd.port
2578 port = ':%d' % self.httpd.port
2581 if port == ':80':
2579 if port == ':80':
2582 port = ''
2580 port = ''
2583
2581
2584 bindaddr = self.httpd.addr
2582 bindaddr = self.httpd.addr
2585 if bindaddr == '0.0.0.0':
2583 if bindaddr == '0.0.0.0':
2586 bindaddr = '*'
2584 bindaddr = '*'
2587 elif ':' in bindaddr: # IPv6
2585 elif ':' in bindaddr: # IPv6
2588 bindaddr = '[%s]' % bindaddr
2586 bindaddr = '[%s]' % bindaddr
2589
2587
2590 fqaddr = self.httpd.fqaddr
2588 fqaddr = self.httpd.fqaddr
2591 if ':' in fqaddr:
2589 if ':' in fqaddr:
2592 fqaddr = '[%s]' % fqaddr
2590 fqaddr = '[%s]' % fqaddr
2593 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2591 ui.status(_('listening at http://%s%s/%s (bound to %s:%d)\n') %
2594 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2592 (fqaddr, port, prefix, bindaddr, self.httpd.port))
2595
2593
2596 def run(self):
2594 def run(self):
2597 self.httpd.serve_forever()
2595 self.httpd.serve_forever()
2598
2596
2599 service = service()
2597 service = service()
2600
2598
2601 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2599 cmdutil.service(opts, initfn=service.init, runfn=service.run)
2602
2600
2603 def status(ui, repo, *pats, **opts):
2601 def status(ui, repo, *pats, **opts):
2604 """show changed files in the working directory
2602 """show changed files in the working directory
2605
2603
2606 Show status of files in the repository. If names are given, only
2604 Show status of files in the repository. If names are given, only
2607 files that match are shown. Files that are clean or ignored or
2605 files that match are shown. Files that are clean or ignored or
2608 source of a copy/move operation, are not listed unless -c (clean),
2606 source of a copy/move operation, are not listed unless -c (clean),
2609 -i (ignored), -C (copies) or -A is given. Unless options described
2607 -i (ignored), -C (copies) or -A is given. Unless options described
2610 with "show only ..." are given, the options -mardu are used.
2608 with "show only ..." are given, the options -mardu are used.
2611
2609
2612 Option -q/--quiet hides untracked (unknown and ignored) files
2610 Option -q/--quiet hides untracked (unknown and ignored) files
2613 unless explicitly requested with -u/--unknown or -i/-ignored.
2611 unless explicitly requested with -u/--unknown or -i/-ignored.
2614
2612
2615 NOTE: status may appear to disagree with diff if permissions have
2613 NOTE: status may appear to disagree with diff if permissions have
2616 changed or a merge has occurred. The standard diff format does not
2614 changed or a merge has occurred. The standard diff format does not
2617 report permission changes and diff only reports changes relative
2615 report permission changes and diff only reports changes relative
2618 to one merge parent.
2616 to one merge parent.
2619
2617
2620 If one revision is given, it is used as the base revision.
2618 If one revision is given, it is used as the base revision.
2621 If two revisions are given, the difference between them is shown.
2619 If two revisions are given, the difference between them is shown.
2622
2620
2623 The codes used to show the status of files are:
2621 The codes used to show the status of files are:
2624 M = modified
2622 M = modified
2625 A = added
2623 A = added
2626 R = removed
2624 R = removed
2627 C = clean
2625 C = clean
2628 ! = deleted, but still tracked
2626 ! = deleted, but still tracked
2629 ? = not tracked
2627 ? = not tracked
2630 I = ignored
2628 I = ignored
2631 = the previous added file was copied from here
2629 = the previous added file was copied from here
2632 """
2630 """
2633
2631
2634 all = opts['all']
2632 all = opts['all']
2635 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2633 node1, node2 = cmdutil.revpair(repo, opts.get('rev'))
2636
2634
2637 files, matchfn, anypats = cmdutil.matchpats(repo, pats, opts)
2635 matcher = cmdutil.match(repo, pats, opts)
2638 cwd = (pats and repo.getcwd()) or ''
2636 cwd = (pats and repo.getcwd()) or ''
2639 modified, added, removed, deleted, unknown, ignored, clean = [
2637 modified, added, removed, deleted, unknown, ignored, clean = [
2640 n for n in repo.status(node1=node1, node2=node2, files=files,
2638 n for n in repo.status(node1, node2, matcher.files(), matcher,
2641 match=matchfn,
2642 list_ignored=opts['ignored']
2639 list_ignored=opts['ignored']
2643 or all and not ui.quiet,
2640 or all and not ui.quiet,
2644 list_clean=opts['clean'] or all,
2641 list_clean=opts['clean'] or all,
2645 list_unknown=opts['unknown']
2642 list_unknown=opts['unknown']
2646 or not (ui.quiet or
2643 or not (ui.quiet or
2647 opts['modified'] or
2644 opts['modified'] or
2648 opts['added'] or
2645 opts['added'] or
2649 opts['removed'] or
2646 opts['removed'] or
2650 opts['deleted'] or
2647 opts['deleted'] or
2651 opts['ignored']))]
2648 opts['ignored']))]
2652
2649
2653 changetypes = (('modified', 'M', modified),
2650 changetypes = (('modified', 'M', modified),
2654 ('added', 'A', added),
2651 ('added', 'A', added),
2655 ('removed', 'R', removed),
2652 ('removed', 'R', removed),
2656 ('deleted', '!', deleted),
2653 ('deleted', '!', deleted),
2657 ('unknown', '?', unknown),
2654 ('unknown', '?', unknown),
2658 ('ignored', 'I', ignored))
2655 ('ignored', 'I', ignored))
2659
2656
2660 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2657 explicit_changetypes = changetypes + (('clean', 'C', clean),)
2661
2658
2662 copy = {}
2659 copy = {}
2663 showcopy = {}
2660 showcopy = {}
2664 if ((all or opts.get('copies')) and not opts.get('no_status')):
2661 if ((all or opts.get('copies')) and not opts.get('no_status')):
2665 if opts.get('rev') == []:
2662 if opts.get('rev') == []:
2666 # fast path, more correct with merge parents
2663 # fast path, more correct with merge parents
2667 showcopy = copy = repo.dirstate.copies().copy()
2664 showcopy = copy = repo.dirstate.copies().copy()
2668 else:
2665 else:
2669 ctxn = repo.changectx(nullid)
2666 ctxn = repo.changectx(nullid)
2670 ctx1 = repo.changectx(node1)
2667 ctx1 = repo.changectx(node1)
2671 ctx2 = repo.changectx(node2)
2668 ctx2 = repo.changectx(node2)
2672 if node2 is None:
2669 if node2 is None:
2673 ctx2 = repo.workingctx()
2670 ctx2 = repo.workingctx()
2674 copy, diverge = copies.copies(repo, ctx1, ctx2, ctxn)
2671 copy, diverge = copies.copies(repo, ctx1, ctx2, ctxn)
2675 for k, v in copy.items():
2672 for k, v in copy.items():
2676 copy[v] = k
2673 copy[v] = k
2677
2674
2678 end = opts['print0'] and '\0' or '\n'
2675 end = opts['print0'] and '\0' or '\n'
2679
2676
2680 for opt, char, changes in ([ct for ct in explicit_changetypes
2677 for opt, char, changes in ([ct for ct in explicit_changetypes
2681 if all or opts[ct[0]]]
2678 if all or opts[ct[0]]]
2682 or changetypes):
2679 or changetypes):
2683
2680
2684 if opts['no_status']:
2681 if opts['no_status']:
2685 format = "%%s%s" % end
2682 format = "%%s%s" % end
2686 else:
2683 else:
2687 format = "%s %%s%s" % (char, end)
2684 format = "%s %%s%s" % (char, end)
2688
2685
2689 for f in changes:
2686 for f in changes:
2690 ui.write(format % repo.pathto(f, cwd))
2687 ui.write(format % repo.pathto(f, cwd))
2691 if f in copy and (f in added or f in showcopy):
2688 if f in copy and (f in added or f in showcopy):
2692 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2689 ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
2693
2690
2694 def tag(ui, repo, name1, *names, **opts):
2691 def tag(ui, repo, name1, *names, **opts):
2695 """add one or more tags for the current or given revision
2692 """add one or more tags for the current or given revision
2696
2693
2697 Name a particular revision using <name>.
2694 Name a particular revision using <name>.
2698
2695
2699 Tags are used to name particular revisions of the repository and are
2696 Tags are used to name particular revisions of the repository and are
2700 very useful to compare different revisions, to go back to significant
2697 very useful to compare different revisions, to go back to significant
2701 earlier versions or to mark branch points as releases, etc.
2698 earlier versions or to mark branch points as releases, etc.
2702
2699
2703 If no revision is given, the parent of the working directory is used,
2700 If no revision is given, the parent of the working directory is used,
2704 or tip if no revision is checked out.
2701 or tip if no revision is checked out.
2705
2702
2706 To facilitate version control, distribution, and merging of tags,
2703 To facilitate version control, distribution, and merging of tags,
2707 they are stored as a file named ".hgtags" which is managed
2704 they are stored as a file named ".hgtags" which is managed
2708 similarly to other project files and can be hand-edited if
2705 similarly to other project files and can be hand-edited if
2709 necessary. The file '.hg/localtags' is used for local tags (not
2706 necessary. The file '.hg/localtags' is used for local tags (not
2710 shared among repositories).
2707 shared among repositories).
2711
2708
2712 See 'hg help dates' for a list of formats valid for -d/--date.
2709 See 'hg help dates' for a list of formats valid for -d/--date.
2713 """
2710 """
2714
2711
2715 rev_ = None
2712 rev_ = None
2716 names = (name1,) + names
2713 names = (name1,) + names
2717 if len(names) != len(dict.fromkeys(names)):
2714 if len(names) != len(dict.fromkeys(names)):
2718 raise util.Abort(_('tag names must be unique'))
2715 raise util.Abort(_('tag names must be unique'))
2719 for n in names:
2716 for n in names:
2720 if n in ['tip', '.', 'null']:
2717 if n in ['tip', '.', 'null']:
2721 raise util.Abort(_('the name \'%s\' is reserved') % n)
2718 raise util.Abort(_('the name \'%s\' is reserved') % n)
2722 if opts['rev'] and opts['remove']:
2719 if opts['rev'] and opts['remove']:
2723 raise util.Abort(_("--rev and --remove are incompatible"))
2720 raise util.Abort(_("--rev and --remove are incompatible"))
2724 if opts['rev']:
2721 if opts['rev']:
2725 rev_ = opts['rev']
2722 rev_ = opts['rev']
2726 message = opts['message']
2723 message = opts['message']
2727 if opts['remove']:
2724 if opts['remove']:
2728 expectedtype = opts['local'] and 'local' or 'global'
2725 expectedtype = opts['local'] and 'local' or 'global'
2729 for n in names:
2726 for n in names:
2730 if not repo.tagtype(n):
2727 if not repo.tagtype(n):
2731 raise util.Abort(_('tag \'%s\' does not exist') % n)
2728 raise util.Abort(_('tag \'%s\' does not exist') % n)
2732 if repo.tagtype(n) != expectedtype:
2729 if repo.tagtype(n) != expectedtype:
2733 raise util.Abort(_('tag \'%s\' is not a %s tag') %
2730 raise util.Abort(_('tag \'%s\' is not a %s tag') %
2734 (n, expectedtype))
2731 (n, expectedtype))
2735 rev_ = nullid
2732 rev_ = nullid
2736 if not message:
2733 if not message:
2737 message = _('Removed tag %s') % ', '.join(names)
2734 message = _('Removed tag %s') % ', '.join(names)
2738 elif not opts['force']:
2735 elif not opts['force']:
2739 for n in names:
2736 for n in names:
2740 if n in repo.tags():
2737 if n in repo.tags():
2741 raise util.Abort(_('tag \'%s\' already exists '
2738 raise util.Abort(_('tag \'%s\' already exists '
2742 '(use -f to force)') % n)
2739 '(use -f to force)') % n)
2743 if not rev_ and repo.dirstate.parents()[1] != nullid:
2740 if not rev_ and repo.dirstate.parents()[1] != nullid:
2744 raise util.Abort(_('uncommitted merge - please provide a '
2741 raise util.Abort(_('uncommitted merge - please provide a '
2745 'specific revision'))
2742 'specific revision'))
2746 r = repo.changectx(rev_).node()
2743 r = repo.changectx(rev_).node()
2747
2744
2748 if not message:
2745 if not message:
2749 message = (_('Added tag %s for changeset %s') %
2746 message = (_('Added tag %s for changeset %s') %
2750 (', '.join(names), short(r)))
2747 (', '.join(names), short(r)))
2751
2748
2752 date = opts.get('date')
2749 date = opts.get('date')
2753 if date:
2750 if date:
2754 date = util.parsedate(date)
2751 date = util.parsedate(date)
2755
2752
2756 repo.tag(names, r, message, opts['local'], opts['user'], date)
2753 repo.tag(names, r, message, opts['local'], opts['user'], date)
2757
2754
2758 def tags(ui, repo):
2755 def tags(ui, repo):
2759 """list repository tags
2756 """list repository tags
2760
2757
2761 List the repository tags.
2758 List the repository tags.
2762
2759
2763 This lists both regular and local tags. When the -v/--verbose switch
2760 This lists both regular and local tags. When the -v/--verbose switch
2764 is used, a third column "local" is printed for local tags.
2761 is used, a third column "local" is printed for local tags.
2765 """
2762 """
2766
2763
2767 l = repo.tagslist()
2764 l = repo.tagslist()
2768 l.reverse()
2765 l.reverse()
2769 hexfunc = ui.debugflag and hex or short
2766 hexfunc = ui.debugflag and hex or short
2770 tagtype = ""
2767 tagtype = ""
2771
2768
2772 for t, n in l:
2769 for t, n in l:
2773 if ui.quiet:
2770 if ui.quiet:
2774 ui.write("%s\n" % t)
2771 ui.write("%s\n" % t)
2775 continue
2772 continue
2776
2773
2777 try:
2774 try:
2778 hn = hexfunc(n)
2775 hn = hexfunc(n)
2779 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2776 r = "%5d:%s" % (repo.changelog.rev(n), hn)
2780 except revlog.LookupError:
2777 except revlog.LookupError:
2781 r = " ?:%s" % hn
2778 r = " ?:%s" % hn
2782 else:
2779 else:
2783 spaces = " " * (30 - util.locallen(t))
2780 spaces = " " * (30 - util.locallen(t))
2784 if ui.verbose:
2781 if ui.verbose:
2785 if repo.tagtype(t) == 'local':
2782 if repo.tagtype(t) == 'local':
2786 tagtype = " local"
2783 tagtype = " local"
2787 else:
2784 else:
2788 tagtype = ""
2785 tagtype = ""
2789 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2786 ui.write("%s%s %s%s\n" % (t, spaces, r, tagtype))
2790
2787
2791 def tip(ui, repo, **opts):
2788 def tip(ui, repo, **opts):
2792 """show the tip revision
2789 """show the tip revision
2793
2790
2794 The tip revision (usually just called the tip) is the most
2791 The tip revision (usually just called the tip) is the most
2795 recently added changeset in the repository, the most recently
2792 recently added changeset in the repository, the most recently
2796 changed head.
2793 changed head.
2797
2794
2798 If you have just made a commit, that commit will be the tip. If
2795 If you have just made a commit, that commit will be the tip. If
2799 you have just pulled changes from another repository, the tip of
2796 you have just pulled changes from another repository, the tip of
2800 that repository becomes the current tip. The "tip" tag is special
2797 that repository becomes the current tip. The "tip" tag is special
2801 and cannot be renamed or assigned to a different changeset.
2798 and cannot be renamed or assigned to a different changeset.
2802 """
2799 """
2803 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2800 cmdutil.show_changeset(ui, repo, opts).show(nullrev+repo.changelog.count())
2804
2801
2805 def unbundle(ui, repo, fname1, *fnames, **opts):
2802 def unbundle(ui, repo, fname1, *fnames, **opts):
2806 """apply one or more changegroup files
2803 """apply one or more changegroup files
2807
2804
2808 Apply one or more compressed changegroup files generated by the
2805 Apply one or more compressed changegroup files generated by the
2809 bundle command.
2806 bundle command.
2810 """
2807 """
2811 fnames = (fname1,) + fnames
2808 fnames = (fname1,) + fnames
2812
2809
2813 lock = None
2810 lock = None
2814 try:
2811 try:
2815 lock = repo.lock()
2812 lock = repo.lock()
2816 for fname in fnames:
2813 for fname in fnames:
2817 if os.path.exists(fname):
2814 if os.path.exists(fname):
2818 f = open(fname, "rb")
2815 f = open(fname, "rb")
2819 else:
2816 else:
2820 f = urllib.urlopen(fname)
2817 f = urllib.urlopen(fname)
2821 gen = changegroup.readbundle(f, fname)
2818 gen = changegroup.readbundle(f, fname)
2822 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2819 modheads = repo.addchangegroup(gen, 'unbundle', 'bundle:' + fname)
2823 finally:
2820 finally:
2824 del lock
2821 del lock
2825
2822
2826 return postincoming(ui, repo, modheads, opts['update'], None)
2823 return postincoming(ui, repo, modheads, opts['update'], None)
2827
2824
2828 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2825 def update(ui, repo, node=None, rev=None, clean=False, date=None):
2829 """update working directory
2826 """update working directory
2830
2827
2831 Update the working directory to the specified revision, or the
2828 Update the working directory to the specified revision, or the
2832 tip of the current branch if none is specified.
2829 tip of the current branch if none is specified.
2833
2830
2834 If the requested revision is a descendant of the working
2831 If the requested revision is a descendant of the working
2835 directory, any outstanding changes in the working directory will
2832 directory, any outstanding changes in the working directory will
2836 be merged into the result. If it is not directly descended but is
2833 be merged into the result. If it is not directly descended but is
2837 on the same named branch, update aborts with a suggestion to use
2834 on the same named branch, update aborts with a suggestion to use
2838 merge or update -C instead.
2835 merge or update -C instead.
2839
2836
2840 If the requested revision is on a different named branch and the
2837 If the requested revision is on a different named branch and the
2841 working directory is clean, update quietly switches branches.
2838 working directory is clean, update quietly switches branches.
2842
2839
2843 See 'hg help dates' for a list of formats valid for --date.
2840 See 'hg help dates' for a list of formats valid for --date.
2844 """
2841 """
2845 if rev and node:
2842 if rev and node:
2846 raise util.Abort(_("please specify just one revision"))
2843 raise util.Abort(_("please specify just one revision"))
2847
2844
2848 if not rev:
2845 if not rev:
2849 rev = node
2846 rev = node
2850
2847
2851 if date:
2848 if date:
2852 if rev:
2849 if rev:
2853 raise util.Abort(_("you can't specify a revision and a date"))
2850 raise util.Abort(_("you can't specify a revision and a date"))
2854 rev = cmdutil.finddate(ui, repo, date)
2851 rev = cmdutil.finddate(ui, repo, date)
2855
2852
2856 if clean:
2853 if clean:
2857 return hg.clean(repo, rev)
2854 return hg.clean(repo, rev)
2858 else:
2855 else:
2859 return hg.update(repo, rev)
2856 return hg.update(repo, rev)
2860
2857
2861 def verify(ui, repo):
2858 def verify(ui, repo):
2862 """verify the integrity of the repository
2859 """verify the integrity of the repository
2863
2860
2864 Verify the integrity of the current repository.
2861 Verify the integrity of the current repository.
2865
2862
2866 This will perform an extensive check of the repository's
2863 This will perform an extensive check of the repository's
2867 integrity, validating the hashes and checksums of each entry in
2864 integrity, validating the hashes and checksums of each entry in
2868 the changelog, manifest, and tracked files, as well as the
2865 the changelog, manifest, and tracked files, as well as the
2869 integrity of their crosslinks and indices.
2866 integrity of their crosslinks and indices.
2870 """
2867 """
2871 return hg.verify(repo)
2868 return hg.verify(repo)
2872
2869
2873 def version_(ui):
2870 def version_(ui):
2874 """output version and copyright information"""
2871 """output version and copyright information"""
2875 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2872 ui.write(_("Mercurial Distributed SCM (version %s)\n")
2876 % version.get_version())
2873 % version.get_version())
2877 ui.status(_(
2874 ui.status(_(
2878 "\nCopyright (C) 2005-2008 Matt Mackall <mpm@selenic.com> and others\n"
2875 "\nCopyright (C) 2005-2008 Matt Mackall <mpm@selenic.com> and others\n"
2879 "This is free software; see the source for copying conditions. "
2876 "This is free software; see the source for copying conditions. "
2880 "There is NO\nwarranty; "
2877 "There is NO\nwarranty; "
2881 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2878 "not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
2882 ))
2879 ))
2883
2880
2884 # Command options and aliases are listed here, alphabetically
2881 # Command options and aliases are listed here, alphabetically
2885
2882
2886 globalopts = [
2883 globalopts = [
2887 ('R', 'repository', '',
2884 ('R', 'repository', '',
2888 _('repository root directory or symbolic path name')),
2885 _('repository root directory or symbolic path name')),
2889 ('', 'cwd', '', _('change working directory')),
2886 ('', 'cwd', '', _('change working directory')),
2890 ('y', 'noninteractive', None,
2887 ('y', 'noninteractive', None,
2891 _('do not prompt, assume \'yes\' for any required answers')),
2888 _('do not prompt, assume \'yes\' for any required answers')),
2892 ('q', 'quiet', None, _('suppress output')),
2889 ('q', 'quiet', None, _('suppress output')),
2893 ('v', 'verbose', None, _('enable additional output')),
2890 ('v', 'verbose', None, _('enable additional output')),
2894 ('', 'config', [], _('set/override config option')),
2891 ('', 'config', [], _('set/override config option')),
2895 ('', 'debug', None, _('enable debugging output')),
2892 ('', 'debug', None, _('enable debugging output')),
2896 ('', 'debugger', None, _('start debugger')),
2893 ('', 'debugger', None, _('start debugger')),
2897 ('', 'encoding', util._encoding, _('set the charset encoding')),
2894 ('', 'encoding', util._encoding, _('set the charset encoding')),
2898 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2895 ('', 'encodingmode', util._encodingmode, _('set the charset encoding mode')),
2899 ('', 'lsprof', None, _('print improved command execution profile')),
2896 ('', 'lsprof', None, _('print improved command execution profile')),
2900 ('', 'traceback', None, _('print traceback on exception')),
2897 ('', 'traceback', None, _('print traceback on exception')),
2901 ('', 'time', None, _('time how long the command takes')),
2898 ('', 'time', None, _('time how long the command takes')),
2902 ('', 'profile', None, _('print command execution profile')),
2899 ('', 'profile', None, _('print command execution profile')),
2903 ('', 'version', None, _('output version information and exit')),
2900 ('', 'version', None, _('output version information and exit')),
2904 ('h', 'help', None, _('display help and exit')),
2901 ('h', 'help', None, _('display help and exit')),
2905 ]
2902 ]
2906
2903
2907 dryrunopts = [('n', 'dry-run', None,
2904 dryrunopts = [('n', 'dry-run', None,
2908 _('do not perform actions, just print output'))]
2905 _('do not perform actions, just print output'))]
2909
2906
2910 remoteopts = [
2907 remoteopts = [
2911 ('e', 'ssh', '', _('specify ssh command to use')),
2908 ('e', 'ssh', '', _('specify ssh command to use')),
2912 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2909 ('', 'remotecmd', '', _('specify hg command to run on the remote side')),
2913 ]
2910 ]
2914
2911
2915 walkopts = [
2912 walkopts = [
2916 ('I', 'include', [], _('include names matching the given patterns')),
2913 ('I', 'include', [], _('include names matching the given patterns')),
2917 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2914 ('X', 'exclude', [], _('exclude names matching the given patterns')),
2918 ]
2915 ]
2919
2916
2920 commitopts = [
2917 commitopts = [
2921 ('m', 'message', '', _('use <text> as commit message')),
2918 ('m', 'message', '', _('use <text> as commit message')),
2922 ('l', 'logfile', '', _('read commit message from <file>')),
2919 ('l', 'logfile', '', _('read commit message from <file>')),
2923 ]
2920 ]
2924
2921
2925 commitopts2 = [
2922 commitopts2 = [
2926 ('d', 'date', '', _('record datecode as commit date')),
2923 ('d', 'date', '', _('record datecode as commit date')),
2927 ('u', 'user', '', _('record user as committer')),
2924 ('u', 'user', '', _('record user as committer')),
2928 ]
2925 ]
2929
2926
2930 templateopts = [
2927 templateopts = [
2931 ('', 'style', '', _('display using template map file')),
2928 ('', 'style', '', _('display using template map file')),
2932 ('', 'template', '', _('display with template')),
2929 ('', 'template', '', _('display with template')),
2933 ]
2930 ]
2934
2931
2935 logopts = [
2932 logopts = [
2936 ('p', 'patch', None, _('show patch')),
2933 ('p', 'patch', None, _('show patch')),
2937 ('l', 'limit', '', _('limit number of changes displayed')),
2934 ('l', 'limit', '', _('limit number of changes displayed')),
2938 ('M', 'no-merges', None, _('do not show merges')),
2935 ('M', 'no-merges', None, _('do not show merges')),
2939 ] + templateopts
2936 ] + templateopts
2940
2937
2941 table = {
2938 table = {
2942 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2939 "^add": (add, walkopts + dryrunopts, _('hg add [OPTION]... [FILE]...')),
2943 "addremove":
2940 "addremove":
2944 (addremove,
2941 (addremove,
2945 [('s', 'similarity', '',
2942 [('s', 'similarity', '',
2946 _('guess renamed files by similarity (0<=s<=100)')),
2943 _('guess renamed files by similarity (0<=s<=100)')),
2947 ] + walkopts + dryrunopts,
2944 ] + walkopts + dryrunopts,
2948 _('hg addremove [OPTION]... [FILE]...')),
2945 _('hg addremove [OPTION]... [FILE]...')),
2949 "^annotate|blame":
2946 "^annotate|blame":
2950 (annotate,
2947 (annotate,
2951 [('r', 'rev', '', _('annotate the specified revision')),
2948 [('r', 'rev', '', _('annotate the specified revision')),
2952 ('f', 'follow', None, _('follow file copies and renames')),
2949 ('f', 'follow', None, _('follow file copies and renames')),
2953 ('a', 'text', None, _('treat all files as text')),
2950 ('a', 'text', None, _('treat all files as text')),
2954 ('u', 'user', None, _('list the author (long with -v)')),
2951 ('u', 'user', None, _('list the author (long with -v)')),
2955 ('d', 'date', None, _('list the date (short with -q)')),
2952 ('d', 'date', None, _('list the date (short with -q)')),
2956 ('n', 'number', None, _('list the revision number (default)')),
2953 ('n', 'number', None, _('list the revision number (default)')),
2957 ('c', 'changeset', None, _('list the changeset')),
2954 ('c', 'changeset', None, _('list the changeset')),
2958 ('l', 'line-number', None,
2955 ('l', 'line-number', None,
2959 _('show line number at the first appearance'))
2956 _('show line number at the first appearance'))
2960 ] + walkopts,
2957 ] + walkopts,
2961 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
2958 _('hg annotate [-r REV] [-f] [-a] [-u] [-d] [-n] [-c] [-l] FILE...')),
2962 "archive":
2959 "archive":
2963 (archive,
2960 (archive,
2964 [('', 'no-decode', None, _('do not pass files through decoders')),
2961 [('', 'no-decode', None, _('do not pass files through decoders')),
2965 ('p', 'prefix', '', _('directory prefix for files in archive')),
2962 ('p', 'prefix', '', _('directory prefix for files in archive')),
2966 ('r', 'rev', '', _('revision to distribute')),
2963 ('r', 'rev', '', _('revision to distribute')),
2967 ('t', 'type', '', _('type of distribution to create')),
2964 ('t', 'type', '', _('type of distribution to create')),
2968 ] + walkopts,
2965 ] + walkopts,
2969 _('hg archive [OPTION]... DEST')),
2966 _('hg archive [OPTION]... DEST')),
2970 "backout":
2967 "backout":
2971 (backout,
2968 (backout,
2972 [('', 'merge', None,
2969 [('', 'merge', None,
2973 _('merge with old dirstate parent after backout')),
2970 _('merge with old dirstate parent after backout')),
2974 ('', 'parent', '', _('parent to choose when backing out merge')),
2971 ('', 'parent', '', _('parent to choose when backing out merge')),
2975 ('r', 'rev', '', _('revision to backout')),
2972 ('r', 'rev', '', _('revision to backout')),
2976 ] + walkopts + commitopts + commitopts2,
2973 ] + walkopts + commitopts + commitopts2,
2977 _('hg backout [OPTION]... [-r] REV')),
2974 _('hg backout [OPTION]... [-r] REV')),
2978 "bisect":
2975 "bisect":
2979 (bisect,
2976 (bisect,
2980 [('r', 'reset', False, _('reset bisect state')),
2977 [('r', 'reset', False, _('reset bisect state')),
2981 ('g', 'good', False, _('mark changeset good')),
2978 ('g', 'good', False, _('mark changeset good')),
2982 ('b', 'bad', False, _('mark changeset bad')),
2979 ('b', 'bad', False, _('mark changeset bad')),
2983 ('s', 'skip', False, _('skip testing changeset')),
2980 ('s', 'skip', False, _('skip testing changeset')),
2984 ('U', 'noupdate', False, _('do not update to target'))],
2981 ('U', 'noupdate', False, _('do not update to target'))],
2985 _("hg bisect [-gbsr] [REV]")),
2982 _("hg bisect [-gbsr] [REV]")),
2986 "branch":
2983 "branch":
2987 (branch,
2984 (branch,
2988 [('f', 'force', None,
2985 [('f', 'force', None,
2989 _('set branch name even if it shadows an existing branch'))],
2986 _('set branch name even if it shadows an existing branch'))],
2990 _('hg branch [-f] [NAME]')),
2987 _('hg branch [-f] [NAME]')),
2991 "branches":
2988 "branches":
2992 (branches,
2989 (branches,
2993 [('a', 'active', False,
2990 [('a', 'active', False,
2994 _('show only branches that have unmerged heads'))],
2991 _('show only branches that have unmerged heads'))],
2995 _('hg branches [-a]')),
2992 _('hg branches [-a]')),
2996 "bundle":
2993 "bundle":
2997 (bundle,
2994 (bundle,
2998 [('f', 'force', None,
2995 [('f', 'force', None,
2999 _('run even when remote repository is unrelated')),
2996 _('run even when remote repository is unrelated')),
3000 ('r', 'rev', [],
2997 ('r', 'rev', [],
3001 _('a changeset up to which you would like to bundle')),
2998 _('a changeset up to which you would like to bundle')),
3002 ('', 'base', [],
2999 ('', 'base', [],
3003 _('a base changeset to specify instead of a destination')),
3000 _('a base changeset to specify instead of a destination')),
3004 ('a', 'all', None, _('bundle all changesets in the repository')),
3001 ('a', 'all', None, _('bundle all changesets in the repository')),
3005 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3002 ('t', 'type', 'bzip2', _('bundle compression type to use')),
3006 ] + remoteopts,
3003 ] + remoteopts,
3007 _('hg bundle [-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3004 _('hg bundle [-f] [-a] [-r REV]... [--base REV]... FILE [DEST]')),
3008 "cat":
3005 "cat":
3009 (cat,
3006 (cat,
3010 [('o', 'output', '', _('print output to file with formatted name')),
3007 [('o', 'output', '', _('print output to file with formatted name')),
3011 ('r', 'rev', '', _('print the given revision')),
3008 ('r', 'rev', '', _('print the given revision')),
3012 ('', 'decode', None, _('apply any matching decode filter')),
3009 ('', 'decode', None, _('apply any matching decode filter')),
3013 ] + walkopts,
3010 ] + walkopts,
3014 _('hg cat [OPTION]... FILE...')),
3011 _('hg cat [OPTION]... FILE...')),
3015 "^clone":
3012 "^clone":
3016 (clone,
3013 (clone,
3017 [('U', 'noupdate', None, _('do not update the new working directory')),
3014 [('U', 'noupdate', None, _('do not update the new working directory')),
3018 ('r', 'rev', [],
3015 ('r', 'rev', [],
3019 _('a changeset you would like to have after cloning')),
3016 _('a changeset you would like to have after cloning')),
3020 ('', 'pull', None, _('use pull protocol to copy metadata')),
3017 ('', 'pull', None, _('use pull protocol to copy metadata')),
3021 ('', 'uncompressed', None,
3018 ('', 'uncompressed', None,
3022 _('use uncompressed transfer (fast over LAN)')),
3019 _('use uncompressed transfer (fast over LAN)')),
3023 ] + remoteopts,
3020 ] + remoteopts,
3024 _('hg clone [OPTION]... SOURCE [DEST]')),
3021 _('hg clone [OPTION]... SOURCE [DEST]')),
3025 "^commit|ci":
3022 "^commit|ci":
3026 (commit,
3023 (commit,
3027 [('A', 'addremove', None,
3024 [('A', 'addremove', None,
3028 _('mark new/missing files as added/removed before committing')),
3025 _('mark new/missing files as added/removed before committing')),
3029 ] + walkopts + commitopts + commitopts2,
3026 ] + walkopts + commitopts + commitopts2,
3030 _('hg commit [OPTION]... [FILE]...')),
3027 _('hg commit [OPTION]... [FILE]...')),
3031 "copy|cp":
3028 "copy|cp":
3032 (copy,
3029 (copy,
3033 [('A', 'after', None, _('record a copy that has already occurred')),
3030 [('A', 'after', None, _('record a copy that has already occurred')),
3034 ('f', 'force', None,
3031 ('f', 'force', None,
3035 _('forcibly copy over an existing managed file')),
3032 _('forcibly copy over an existing managed file')),
3036 ] + walkopts + dryrunopts,
3033 ] + walkopts + dryrunopts,
3037 _('hg copy [OPTION]... [SOURCE]... DEST')),
3034 _('hg copy [OPTION]... [SOURCE]... DEST')),
3038 "debugancestor": (debugancestor, [],
3035 "debugancestor": (debugancestor, [],
3039 _('hg debugancestor [INDEX] REV1 REV2')),
3036 _('hg debugancestor [INDEX] REV1 REV2')),
3040 "debugcheckstate": (debugcheckstate, [], _('hg debugcheckstate')),
3037 "debugcheckstate": (debugcheckstate, [], _('hg debugcheckstate')),
3041 "debugcomplete":
3038 "debugcomplete":
3042 (debugcomplete,
3039 (debugcomplete,
3043 [('o', 'options', None, _('show the command options'))],
3040 [('o', 'options', None, _('show the command options'))],
3044 _('hg debugcomplete [-o] CMD')),
3041 _('hg debugcomplete [-o] CMD')),
3045 "debugdate":
3042 "debugdate":
3046 (debugdate,
3043 (debugdate,
3047 [('e', 'extended', None, _('try extended date formats'))],
3044 [('e', 'extended', None, _('try extended date formats'))],
3048 _('hg debugdate [-e] DATE [RANGE]')),
3045 _('hg debugdate [-e] DATE [RANGE]')),
3049 "debugdata": (debugdata, [], _('hg debugdata FILE REV')),
3046 "debugdata": (debugdata, [], _('hg debugdata FILE REV')),
3050 "debugfsinfo": (debugfsinfo, [], _('hg debugfsinfo [PATH]')),
3047 "debugfsinfo": (debugfsinfo, [], _('hg debugfsinfo [PATH]')),
3051 "debugindex": (debugindex, [], _('hg debugindex FILE')),
3048 "debugindex": (debugindex, [], _('hg debugindex FILE')),
3052 "debugindexdot": (debugindexdot, [], _('hg debugindexdot FILE')),
3049 "debugindexdot": (debugindexdot, [], _('hg debugindexdot FILE')),
3053 "debuginstall": (debuginstall, [], _('hg debuginstall')),
3050 "debuginstall": (debuginstall, [], _('hg debuginstall')),
3054 "debugrawcommit|rawcommit":
3051 "debugrawcommit|rawcommit":
3055 (rawcommit,
3052 (rawcommit,
3056 [('p', 'parent', [], _('parent')),
3053 [('p', 'parent', [], _('parent')),
3057 ('F', 'files', '', _('file list'))
3054 ('F', 'files', '', _('file list'))
3058 ] + commitopts + commitopts2,
3055 ] + commitopts + commitopts2,
3059 _('hg debugrawcommit [OPTION]... [FILE]...')),
3056 _('hg debugrawcommit [OPTION]... [FILE]...')),
3060 "debugrebuildstate":
3057 "debugrebuildstate":
3061 (debugrebuildstate,
3058 (debugrebuildstate,
3062 [('r', 'rev', '', _('revision to rebuild to'))],
3059 [('r', 'rev', '', _('revision to rebuild to'))],
3063 _('hg debugrebuildstate [-r REV] [REV]')),
3060 _('hg debugrebuildstate [-r REV] [REV]')),
3064 "debugrename":
3061 "debugrename":
3065 (debugrename,
3062 (debugrename,
3066 [('r', 'rev', '', _('revision to debug'))],
3063 [('r', 'rev', '', _('revision to debug'))],
3067 _('hg debugrename [-r REV] FILE')),
3064 _('hg debugrename [-r REV] FILE')),
3068 "debugsetparents":
3065 "debugsetparents":
3069 (debugsetparents,
3066 (debugsetparents,
3070 [],
3067 [],
3071 _('hg debugsetparents REV1 [REV2]')),
3068 _('hg debugsetparents REV1 [REV2]')),
3072 "debugstate":
3069 "debugstate":
3073 (debugstate,
3070 (debugstate,
3074 [('', 'nodates', None, _('do not display the saved mtime'))],
3071 [('', 'nodates', None, _('do not display the saved mtime'))],
3075 _('hg debugstate [OPTS]')),
3072 _('hg debugstate [OPTS]')),
3076 "debugwalk": (debugwalk, walkopts, _('hg debugwalk [OPTION]... [FILE]...')),
3073 "debugwalk": (debugwalk, walkopts, _('hg debugwalk [OPTION]... [FILE]...')),
3077 "^diff":
3074 "^diff":
3078 (diff,
3075 (diff,
3079 [('r', 'rev', [], _('revision')),
3076 [('r', 'rev', [], _('revision')),
3080 ('a', 'text', None, _('treat all files as text')),
3077 ('a', 'text', None, _('treat all files as text')),
3081 ('p', 'show-function', None,
3078 ('p', 'show-function', None,
3082 _('show which function each change is in')),
3079 _('show which function each change is in')),
3083 ('g', 'git', None, _('use git extended diff format')),
3080 ('g', 'git', None, _('use git extended diff format')),
3084 ('', 'nodates', None, _("don't include dates in diff headers")),
3081 ('', 'nodates', None, _("don't include dates in diff headers")),
3085 ('w', 'ignore-all-space', None,
3082 ('w', 'ignore-all-space', None,
3086 _('ignore white space when comparing lines')),
3083 _('ignore white space when comparing lines')),
3087 ('b', 'ignore-space-change', None,
3084 ('b', 'ignore-space-change', None,
3088 _('ignore changes in the amount of white space')),
3085 _('ignore changes in the amount of white space')),
3089 ('B', 'ignore-blank-lines', None,
3086 ('B', 'ignore-blank-lines', None,
3090 _('ignore changes whose lines are all blank')),
3087 _('ignore changes whose lines are all blank')),
3091 ('U', 'unified', '',
3088 ('U', 'unified', '',
3092 _('number of lines of context to show'))
3089 _('number of lines of context to show'))
3093 ] + walkopts,
3090 ] + walkopts,
3094 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3091 _('hg diff [OPTION]... [-r REV1 [-r REV2]] [FILE]...')),
3095 "^export":
3092 "^export":
3096 (export,
3093 (export,
3097 [('o', 'output', '', _('print output to file with formatted name')),
3094 [('o', 'output', '', _('print output to file with formatted name')),
3098 ('a', 'text', None, _('treat all files as text')),
3095 ('a', 'text', None, _('treat all files as text')),
3099 ('g', 'git', None, _('use git extended diff format')),
3096 ('g', 'git', None, _('use git extended diff format')),
3100 ('', 'nodates', None, _("don't include dates in diff headers")),
3097 ('', 'nodates', None, _("don't include dates in diff headers")),
3101 ('', 'switch-parent', None, _('diff against the second parent'))],
3098 ('', 'switch-parent', None, _('diff against the second parent'))],
3102 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
3099 _('hg export [OPTION]... [-o OUTFILESPEC] REV...')),
3103 "grep":
3100 "grep":
3104 (grep,
3101 (grep,
3105 [('0', 'print0', None, _('end fields with NUL')),
3102 [('0', 'print0', None, _('end fields with NUL')),
3106 ('', 'all', None, _('print all revisions that match')),
3103 ('', 'all', None, _('print all revisions that match')),
3107 ('f', 'follow', None,
3104 ('f', 'follow', None,
3108 _('follow changeset history, or file history across copies and renames')),
3105 _('follow changeset history, or file history across copies and renames')),
3109 ('i', 'ignore-case', None, _('ignore case when matching')),
3106 ('i', 'ignore-case', None, _('ignore case when matching')),
3110 ('l', 'files-with-matches', None,
3107 ('l', 'files-with-matches', None,
3111 _('print only filenames and revs that match')),
3108 _('print only filenames and revs that match')),
3112 ('n', 'line-number', None, _('print matching line numbers')),
3109 ('n', 'line-number', None, _('print matching line numbers')),
3113 ('r', 'rev', [], _('search in given revision range')),
3110 ('r', 'rev', [], _('search in given revision range')),
3114 ('u', 'user', None, _('list the author (long with -v)')),
3111 ('u', 'user', None, _('list the author (long with -v)')),
3115 ('d', 'date', None, _('list the date (short with -q)')),
3112 ('d', 'date', None, _('list the date (short with -q)')),
3116 ] + walkopts,
3113 ] + walkopts,
3117 _('hg grep [OPTION]... PATTERN [FILE]...')),
3114 _('hg grep [OPTION]... PATTERN [FILE]...')),
3118 "heads":
3115 "heads":
3119 (heads,
3116 (heads,
3120 [('r', 'rev', '', _('show only heads which are descendants of rev')),
3117 [('r', 'rev', '', _('show only heads which are descendants of rev')),
3121 ] + templateopts,
3118 ] + templateopts,
3122 _('hg heads [-r REV] [REV]...')),
3119 _('hg heads [-r REV] [REV]...')),
3123 "help": (help_, [], _('hg help [COMMAND]')),
3120 "help": (help_, [], _('hg help [COMMAND]')),
3124 "identify|id":
3121 "identify|id":
3125 (identify,
3122 (identify,
3126 [('r', 'rev', '', _('identify the specified rev')),
3123 [('r', 'rev', '', _('identify the specified rev')),
3127 ('n', 'num', None, _('show local revision number')),
3124 ('n', 'num', None, _('show local revision number')),
3128 ('i', 'id', None, _('show global revision id')),
3125 ('i', 'id', None, _('show global revision id')),
3129 ('b', 'branch', None, _('show branch')),
3126 ('b', 'branch', None, _('show branch')),
3130 ('t', 'tags', None, _('show tags'))],
3127 ('t', 'tags', None, _('show tags'))],
3131 _('hg identify [-nibt] [-r REV] [SOURCE]')),
3128 _('hg identify [-nibt] [-r REV] [SOURCE]')),
3132 "import|patch":
3129 "import|patch":
3133 (import_,
3130 (import_,
3134 [('p', 'strip', 1,
3131 [('p', 'strip', 1,
3135 _('directory strip option for patch. This has the same\n'
3132 _('directory strip option for patch. This has the same\n'
3136 'meaning as the corresponding patch option')),
3133 'meaning as the corresponding patch option')),
3137 ('b', 'base', '', _('base path')),
3134 ('b', 'base', '', _('base path')),
3138 ('f', 'force', None,
3135 ('f', 'force', None,
3139 _('skip check for outstanding uncommitted changes')),
3136 _('skip check for outstanding uncommitted changes')),
3140 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3137 ('', 'no-commit', None, _("don't commit, just update the working directory")),
3141 ('', 'exact', None,
3138 ('', 'exact', None,
3142 _('apply patch to the nodes from which it was generated')),
3139 _('apply patch to the nodes from which it was generated')),
3143 ('', 'import-branch', None,
3140 ('', 'import-branch', None,
3144 _('Use any branch information in patch (implied by --exact)'))] +
3141 _('Use any branch information in patch (implied by --exact)'))] +
3145 commitopts + commitopts2,
3142 commitopts + commitopts2,
3146 _('hg import [OPTION]... PATCH...')),
3143 _('hg import [OPTION]... PATCH...')),
3147 "incoming|in":
3144 "incoming|in":
3148 (incoming,
3145 (incoming,
3149 [('f', 'force', None,
3146 [('f', 'force', None,
3150 _('run even when remote repository is unrelated')),
3147 _('run even when remote repository is unrelated')),
3151 ('n', 'newest-first', None, _('show newest record first')),
3148 ('n', 'newest-first', None, _('show newest record first')),
3152 ('', 'bundle', '', _('file to store the bundles into')),
3149 ('', 'bundle', '', _('file to store the bundles into')),
3153 ('r', 'rev', [],
3150 ('r', 'rev', [],
3154 _('a specific revision up to which you would like to pull')),
3151 _('a specific revision up to which you would like to pull')),
3155 ] + logopts + remoteopts,
3152 ] + logopts + remoteopts,
3156 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
3153 _('hg incoming [-p] [-n] [-M] [-f] [-r REV]...'
3157 ' [--bundle FILENAME] [SOURCE]')),
3154 ' [--bundle FILENAME] [SOURCE]')),
3158 "^init":
3155 "^init":
3159 (init,
3156 (init,
3160 remoteopts,
3157 remoteopts,
3161 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
3158 _('hg init [-e CMD] [--remotecmd CMD] [DEST]')),
3162 "locate":
3159 "locate":
3163 (locate,
3160 (locate,
3164 [('r', 'rev', '', _('search the repository as it stood at rev')),
3161 [('r', 'rev', '', _('search the repository as it stood at rev')),
3165 ('0', 'print0', None,
3162 ('0', 'print0', None,
3166 _('end filenames with NUL, for use with xargs')),
3163 _('end filenames with NUL, for use with xargs')),
3167 ('f', 'fullpath', None,
3164 ('f', 'fullpath', None,
3168 _('print complete paths from the filesystem root')),
3165 _('print complete paths from the filesystem root')),
3169 ] + walkopts,
3166 ] + walkopts,
3170 _('hg locate [OPTION]... [PATTERN]...')),
3167 _('hg locate [OPTION]... [PATTERN]...')),
3171 "^log|history":
3168 "^log|history":
3172 (log,
3169 (log,
3173 [('f', 'follow', None,
3170 [('f', 'follow', None,
3174 _('follow changeset history, or file history across copies and renames')),
3171 _('follow changeset history, or file history across copies and renames')),
3175 ('', 'follow-first', None,
3172 ('', 'follow-first', None,
3176 _('only follow the first parent of merge changesets')),
3173 _('only follow the first parent of merge changesets')),
3177 ('d', 'date', '', _('show revs matching date spec')),
3174 ('d', 'date', '', _('show revs matching date spec')),
3178 ('C', 'copies', None, _('show copied files')),
3175 ('C', 'copies', None, _('show copied files')),
3179 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3176 ('k', 'keyword', [], _('do case-insensitive search for a keyword')),
3180 ('r', 'rev', [], _('show the specified revision or range')),
3177 ('r', 'rev', [], _('show the specified revision or range')),
3181 ('', 'removed', None, _('include revs where files were removed')),
3178 ('', 'removed', None, _('include revs where files were removed')),
3182 ('m', 'only-merges', None, _('show only merges')),
3179 ('m', 'only-merges', None, _('show only merges')),
3183 ('b', 'only-branch', [],
3180 ('b', 'only-branch', [],
3184 _('show only changesets within the given named branch')),
3181 _('show only changesets within the given named branch')),
3185 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3182 ('P', 'prune', [], _('do not display revision or any of its ancestors')),
3186 ] + logopts + walkopts,
3183 ] + logopts + walkopts,
3187 _('hg log [OPTION]... [FILE]')),
3184 _('hg log [OPTION]... [FILE]')),
3188 "manifest":
3185 "manifest":
3189 (manifest,
3186 (manifest,
3190 [('r', 'rev', '', _('revision to display'))],
3187 [('r', 'rev', '', _('revision to display'))],
3191 _('hg manifest [-r REV]')),
3188 _('hg manifest [-r REV]')),
3192 "^merge":
3189 "^merge":
3193 (merge,
3190 (merge,
3194 [('f', 'force', None, _('force a merge with outstanding changes')),
3191 [('f', 'force', None, _('force a merge with outstanding changes')),
3195 ('r', 'rev', '', _('revision to merge')),
3192 ('r', 'rev', '', _('revision to merge')),
3196 ],
3193 ],
3197 _('hg merge [-f] [[-r] REV]')),
3194 _('hg merge [-f] [[-r] REV]')),
3198 "outgoing|out":
3195 "outgoing|out":
3199 (outgoing,
3196 (outgoing,
3200 [('f', 'force', None,
3197 [('f', 'force', None,
3201 _('run even when remote repository is unrelated')),
3198 _('run even when remote repository is unrelated')),
3202 ('r', 'rev', [],
3199 ('r', 'rev', [],
3203 _('a specific revision up to which you would like to push')),
3200 _('a specific revision up to which you would like to push')),
3204 ('n', 'newest-first', None, _('show newest record first')),
3201 ('n', 'newest-first', None, _('show newest record first')),
3205 ] + logopts + remoteopts,
3202 ] + logopts + remoteopts,
3206 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3203 _('hg outgoing [-M] [-p] [-n] [-f] [-r REV]... [DEST]')),
3207 "^parents":
3204 "^parents":
3208 (parents,
3205 (parents,
3209 [('r', 'rev', '', _('show parents from the specified rev')),
3206 [('r', 'rev', '', _('show parents from the specified rev')),
3210 ] + templateopts,
3207 ] + templateopts,
3211 _('hg parents [-r REV] [FILE]')),
3208 _('hg parents [-r REV] [FILE]')),
3212 "paths": (paths, [], _('hg paths [NAME]')),
3209 "paths": (paths, [], _('hg paths [NAME]')),
3213 "^pull":
3210 "^pull":
3214 (pull,
3211 (pull,
3215 [('u', 'update', None,
3212 [('u', 'update', None,
3216 _('update to new tip if changesets were pulled')),
3213 _('update to new tip if changesets were pulled')),
3217 ('f', 'force', None,
3214 ('f', 'force', None,
3218 _('run even when remote repository is unrelated')),
3215 _('run even when remote repository is unrelated')),
3219 ('r', 'rev', [],
3216 ('r', 'rev', [],
3220 _('a specific revision up to which you would like to pull')),
3217 _('a specific revision up to which you would like to pull')),
3221 ] + remoteopts,
3218 ] + remoteopts,
3222 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3219 _('hg pull [-u] [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [SOURCE]')),
3223 "^push":
3220 "^push":
3224 (push,
3221 (push,
3225 [('f', 'force', None, _('force push')),
3222 [('f', 'force', None, _('force push')),
3226 ('r', 'rev', [],
3223 ('r', 'rev', [],
3227 _('a specific revision up to which you would like to push')),
3224 _('a specific revision up to which you would like to push')),
3228 ] + remoteopts,
3225 ] + remoteopts,
3229 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3226 _('hg push [-f] [-r REV]... [-e CMD] [--remotecmd CMD] [DEST]')),
3230 "recover": (recover, [], _('hg recover')),
3227 "recover": (recover, [], _('hg recover')),
3231 "^remove|rm":
3228 "^remove|rm":
3232 (remove,
3229 (remove,
3233 [('A', 'after', None, _('record delete for missing files')),
3230 [('A', 'after', None, _('record delete for missing files')),
3234 ('f', 'force', None,
3231 ('f', 'force', None,
3235 _('remove (and delete) file even if added or modified')),
3232 _('remove (and delete) file even if added or modified')),
3236 ] + walkopts,
3233 ] + walkopts,
3237 _('hg remove [OPTION]... FILE...')),
3234 _('hg remove [OPTION]... FILE...')),
3238 "rename|mv":
3235 "rename|mv":
3239 (rename,
3236 (rename,
3240 [('A', 'after', None, _('record a rename that has already occurred')),
3237 [('A', 'after', None, _('record a rename that has already occurred')),
3241 ('f', 'force', None,
3238 ('f', 'force', None,
3242 _('forcibly copy over an existing managed file')),
3239 _('forcibly copy over an existing managed file')),
3243 ] + walkopts + dryrunopts,
3240 ] + walkopts + dryrunopts,
3244 _('hg rename [OPTION]... SOURCE... DEST')),
3241 _('hg rename [OPTION]... SOURCE... DEST')),
3245 "resolve":
3242 "resolve":
3246 (resolve,
3243 (resolve,
3247 [('l', 'list', None, _('list state of files needing merge')),
3244 [('l', 'list', None, _('list state of files needing merge')),
3248 ('m', 'mark', None, _('mark files as resolved')),
3245 ('m', 'mark', None, _('mark files as resolved')),
3249 ('u', 'unmark', None, _('unmark files as resolved'))],
3246 ('u', 'unmark', None, _('unmark files as resolved'))],
3250 ('hg resolve [OPTION] [FILES...]')),
3247 ('hg resolve [OPTION] [FILES...]')),
3251 "revert":
3248 "revert":
3252 (revert,
3249 (revert,
3253 [('a', 'all', None, _('revert all changes when no arguments given')),
3250 [('a', 'all', None, _('revert all changes when no arguments given')),
3254 ('d', 'date', '', _('tipmost revision matching date')),
3251 ('d', 'date', '', _('tipmost revision matching date')),
3255 ('r', 'rev', '', _('revision to revert to')),
3252 ('r', 'rev', '', _('revision to revert to')),
3256 ('', 'no-backup', None, _('do not save backup copies of files')),
3253 ('', 'no-backup', None, _('do not save backup copies of files')),
3257 ] + walkopts + dryrunopts,
3254 ] + walkopts + dryrunopts,
3258 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3255 _('hg revert [OPTION]... [-r REV] [NAME]...')),
3259 "rollback": (rollback, [], _('hg rollback')),
3256 "rollback": (rollback, [], _('hg rollback')),
3260 "root": (root, [], _('hg root')),
3257 "root": (root, [], _('hg root')),
3261 "^serve":
3258 "^serve":
3262 (serve,
3259 (serve,
3263 [('A', 'accesslog', '', _('name of access log file to write to')),
3260 [('A', 'accesslog', '', _('name of access log file to write to')),
3264 ('d', 'daemon', None, _('run server in background')),
3261 ('d', 'daemon', None, _('run server in background')),
3265 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3262 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
3266 ('E', 'errorlog', '', _('name of error log file to write to')),
3263 ('E', 'errorlog', '', _('name of error log file to write to')),
3267 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3264 ('p', 'port', 0, _('port to listen on (default: 8000)')),
3268 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3265 ('a', 'address', '', _('address to listen on (default: all interfaces)')),
3269 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3266 ('', 'prefix', '', _('prefix path to serve from (default: server root)')),
3270 ('n', 'name', '',
3267 ('n', 'name', '',
3271 _('name to show in web pages (default: working dir)')),
3268 _('name to show in web pages (default: working dir)')),
3272 ('', 'webdir-conf', '', _('name of the webdir config file'
3269 ('', 'webdir-conf', '', _('name of the webdir config file'
3273 ' (serve more than one repo)')),
3270 ' (serve more than one repo)')),
3274 ('', 'pid-file', '', _('name of file to write process ID to')),
3271 ('', 'pid-file', '', _('name of file to write process ID to')),
3275 ('', 'stdio', None, _('for remote clients')),
3272 ('', 'stdio', None, _('for remote clients')),
3276 ('t', 'templates', '', _('web templates to use')),
3273 ('t', 'templates', '', _('web templates to use')),
3277 ('', 'style', '', _('template style to use')),
3274 ('', 'style', '', _('template style to use')),
3278 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3275 ('6', 'ipv6', None, _('use IPv6 in addition to IPv4')),
3279 ('', 'certificate', '', _('SSL certificate file'))],
3276 ('', 'certificate', '', _('SSL certificate file'))],
3280 _('hg serve [OPTION]...')),
3277 _('hg serve [OPTION]...')),
3281 "showconfig|debugconfig":
3278 "showconfig|debugconfig":
3282 (showconfig,
3279 (showconfig,
3283 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3280 [('u', 'untrusted', None, _('show untrusted configuration options'))],
3284 _('hg showconfig [-u] [NAME]...')),
3281 _('hg showconfig [-u] [NAME]...')),
3285 "^status|st":
3282 "^status|st":
3286 (status,
3283 (status,
3287 [('A', 'all', None, _('show status of all files')),
3284 [('A', 'all', None, _('show status of all files')),
3288 ('m', 'modified', None, _('show only modified files')),
3285 ('m', 'modified', None, _('show only modified files')),
3289 ('a', 'added', None, _('show only added files')),
3286 ('a', 'added', None, _('show only added files')),
3290 ('r', 'removed', None, _('show only removed files')),
3287 ('r', 'removed', None, _('show only removed files')),
3291 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3288 ('d', 'deleted', None, _('show only deleted (but tracked) files')),
3292 ('c', 'clean', None, _('show only files without changes')),
3289 ('c', 'clean', None, _('show only files without changes')),
3293 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3290 ('u', 'unknown', None, _('show only unknown (not tracked) files')),
3294 ('i', 'ignored', None, _('show only ignored files')),
3291 ('i', 'ignored', None, _('show only ignored files')),
3295 ('n', 'no-status', None, _('hide status prefix')),
3292 ('n', 'no-status', None, _('hide status prefix')),
3296 ('C', 'copies', None, _('show source of copied files')),
3293 ('C', 'copies', None, _('show source of copied files')),
3297 ('0', 'print0', None,
3294 ('0', 'print0', None,
3298 _('end filenames with NUL, for use with xargs')),
3295 _('end filenames with NUL, for use with xargs')),
3299 ('', 'rev', [], _('show difference from revision')),
3296 ('', 'rev', [], _('show difference from revision')),
3300 ] + walkopts,
3297 ] + walkopts,
3301 _('hg status [OPTION]... [FILE]...')),
3298 _('hg status [OPTION]... [FILE]...')),
3302 "tag":
3299 "tag":
3303 (tag,
3300 (tag,
3304 [('f', 'force', None, _('replace existing tag')),
3301 [('f', 'force', None, _('replace existing tag')),
3305 ('l', 'local', None, _('make the tag local')),
3302 ('l', 'local', None, _('make the tag local')),
3306 ('r', 'rev', '', _('revision to tag')),
3303 ('r', 'rev', '', _('revision to tag')),
3307 ('', 'remove', None, _('remove a tag')),
3304 ('', 'remove', None, _('remove a tag')),
3308 # -l/--local is already there, commitopts cannot be used
3305 # -l/--local is already there, commitopts cannot be used
3309 ('m', 'message', '', _('use <text> as commit message')),
3306 ('m', 'message', '', _('use <text> as commit message')),
3310 ] + commitopts2,
3307 ] + commitopts2,
3311 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3308 _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
3312 "tags": (tags, [], _('hg tags')),
3309 "tags": (tags, [], _('hg tags')),
3313 "tip":
3310 "tip":
3314 (tip,
3311 (tip,
3315 [('p', 'patch', None, _('show patch')),
3312 [('p', 'patch', None, _('show patch')),
3316 ] + templateopts,
3313 ] + templateopts,
3317 _('hg tip [-p]')),
3314 _('hg tip [-p]')),
3318 "unbundle":
3315 "unbundle":
3319 (unbundle,
3316 (unbundle,
3320 [('u', 'update', None,
3317 [('u', 'update', None,
3321 _('update to new tip if changesets were unbundled'))],
3318 _('update to new tip if changesets were unbundled'))],
3322 _('hg unbundle [-u] FILE...')),
3319 _('hg unbundle [-u] FILE...')),
3323 "^update|up|checkout|co":
3320 "^update|up|checkout|co":
3324 (update,
3321 (update,
3325 [('C', 'clean', None, _('overwrite locally modified files')),
3322 [('C', 'clean', None, _('overwrite locally modified files')),
3326 ('d', 'date', '', _('tipmost revision matching date')),
3323 ('d', 'date', '', _('tipmost revision matching date')),
3327 ('r', 'rev', '', _('revision'))],
3324 ('r', 'rev', '', _('revision'))],
3328 _('hg update [-C] [-d DATE] [[-r] REV]')),
3325 _('hg update [-C] [-d DATE] [[-r] REV]')),
3329 "verify": (verify, [], _('hg verify')),
3326 "verify": (verify, [], _('hg verify')),
3330 "version": (version_, [], _('hg version')),
3327 "version": (version_, [], _('hg version')),
3331 }
3328 }
3332
3329
3333 norepo = ("clone init version help debugcomplete debugdata"
3330 norepo = ("clone init version help debugcomplete debugdata"
3334 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3331 " debugindex debugindexdot debugdate debuginstall debugfsinfo")
3335 optionalrepo = ("identify paths serve showconfig debugancestor")
3332 optionalrepo = ("identify paths serve showconfig debugancestor")
General Comments 0
You need to be logged in to leave comments. Login now