##// END OF EJS Templates
extensions: fix up description lines some more
Dirkjan Ochtman -
r8932:f8788432 default
parent child Browse files
Show More
@@ -1,288 +1,288 b''
1 # convert.py Foreign SCM converter
1 # convert.py Foreign SCM converter
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 of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 '''import from foreign VCS repositories into Mercurial'''
8 '''import revisions from foreign VCS repositories into Mercurial'''
9
9
10 import convcmd
10 import convcmd
11 import cvsps
11 import cvsps
12 import subversion
12 import subversion
13 from mercurial import commands
13 from mercurial import commands
14 from mercurial.i18n import _
14 from mercurial.i18n import _
15
15
16 # Commands definition was moved elsewhere to ease demandload job.
16 # Commands definition was moved elsewhere to ease demandload job.
17
17
18 def convert(ui, src, dest=None, revmapfile=None, **opts):
18 def convert(ui, src, dest=None, revmapfile=None, **opts):
19 """convert a foreign SCM repository to a Mercurial one.
19 """convert a foreign SCM repository to a Mercurial one.
20
20
21 Accepted source formats [identifiers]:
21 Accepted source formats [identifiers]:
22 - Mercurial [hg]
22 - Mercurial [hg]
23 - CVS [cvs]
23 - CVS [cvs]
24 - Darcs [darcs]
24 - Darcs [darcs]
25 - git [git]
25 - git [git]
26 - Subversion [svn]
26 - Subversion [svn]
27 - Monotone [mtn]
27 - Monotone [mtn]
28 - GNU Arch [gnuarch]
28 - GNU Arch [gnuarch]
29 - Bazaar [bzr]
29 - Bazaar [bzr]
30 - Perforce [p4]
30 - Perforce [p4]
31
31
32 Accepted destination formats [identifiers]:
32 Accepted destination formats [identifiers]:
33 - Mercurial [hg]
33 - Mercurial [hg]
34 - Subversion [svn] (history on branches is not preserved)
34 - Subversion [svn] (history on branches is not preserved)
35
35
36 If no revision is given, all revisions will be converted.
36 If no revision is given, all revisions will be converted.
37 Otherwise, convert will only import up to the named revision
37 Otherwise, convert will only import up to the named revision
38 (given in a format understood by the source).
38 (given in a format understood by the source).
39
39
40 If no destination directory name is specified, it defaults to the
40 If no destination directory name is specified, it defaults to the
41 basename of the source with '-hg' appended. If the destination
41 basename of the source with '-hg' appended. If the destination
42 repository doesn't exist, it will be created.
42 repository doesn't exist, it will be created.
43
43
44 By default, all sources except Mercurial will use
44 By default, all sources except Mercurial will use
45 --branchsort. Mercurial uses --sourcesort to preserve original
45 --branchsort. Mercurial uses --sourcesort to preserve original
46 revision numbers order. Sort modes have the following effects:
46 revision numbers order. Sort modes have the following effects:
47 --branchsort: convert from parent to child revision when
47 --branchsort: convert from parent to child revision when
48 possible, which means branches are usually converted one after
48 possible, which means branches are usually converted one after
49 the other. It generates more compact repositories.
49 the other. It generates more compact repositories.
50 --datesort: sort revisions by date. Converted repositories have
50 --datesort: sort revisions by date. Converted repositories have
51 good-looking changelogs but are often an order of magnitude
51 good-looking changelogs but are often an order of magnitude
52 larger than the same ones generated by --branchsort.
52 larger than the same ones generated by --branchsort.
53 --sourcesort: try to preserve source revisions order, only
53 --sourcesort: try to preserve source revisions order, only
54 supported by Mercurial sources.
54 supported by Mercurial sources.
55
55
56 If <REVMAP> isn't given, it will be put in a default location
56 If <REVMAP> isn't given, it will be put in a default location
57 (<dest>/.hg/shamap by default). The <REVMAP> is a simple text file
57 (<dest>/.hg/shamap by default). The <REVMAP> is a simple text file
58 that maps each source commit ID to the destination ID for that
58 that maps each source commit ID to the destination ID for that
59 revision, like so:
59 revision, like so:
60 <source ID> <destination ID>
60 <source ID> <destination ID>
61
61
62 If the file doesn't exist, it's automatically created. It's
62 If the file doesn't exist, it's automatically created. It's
63 updated on each commit copied, so convert-repo can be interrupted
63 updated on each commit copied, so convert-repo can be interrupted
64 and can be run repeatedly to copy new commits.
64 and can be run repeatedly to copy new commits.
65
65
66 The [username mapping] file is a simple text file that maps each
66 The [username mapping] file is a simple text file that maps each
67 source commit author to a destination commit author. It is handy
67 source commit author to a destination commit author. It is handy
68 for source SCMs that use unix logins to identify authors (eg:
68 for source SCMs that use unix logins to identify authors (eg:
69 CVS). One line per author mapping and the line format is:
69 CVS). One line per author mapping and the line format is:
70 srcauthor=whatever string you want
70 srcauthor=whatever string you want
71
71
72 The filemap is a file that allows filtering and remapping of files
72 The filemap is a file that allows filtering and remapping of files
73 and directories. Comment lines start with '#'. Each line can
73 and directories. Comment lines start with '#'. Each line can
74 contain one of the following directives:
74 contain one of the following directives:
75
75
76 include path/to/file
76 include path/to/file
77
77
78 exclude path/to/file
78 exclude path/to/file
79
79
80 rename from/file to/file
80 rename from/file to/file
81
81
82 The 'include' directive causes a file, or all files under a
82 The 'include' directive causes a file, or all files under a
83 directory, to be included in the destination repository, and the
83 directory, to be included in the destination repository, and the
84 exclusion of all other files and directories not explicitly included.
84 exclusion of all other files and directories not explicitly included.
85 The 'exclude' directive causes files or directories to be omitted.
85 The 'exclude' directive causes files or directories to be omitted.
86 The 'rename' directive renames a file or directory. To rename from
86 The 'rename' directive renames a file or directory. To rename from
87 a subdirectory into the root of the repository, use '.' as the
87 a subdirectory into the root of the repository, use '.' as the
88 path to rename to.
88 path to rename to.
89
89
90 The splicemap is a file that allows insertion of synthetic
90 The splicemap is a file that allows insertion of synthetic
91 history, letting you specify the parents of a revision. This is
91 history, letting you specify the parents of a revision. This is
92 useful if you want to e.g. give a Subversion merge two parents, or
92 useful if you want to e.g. give a Subversion merge two parents, or
93 graft two disconnected series of history together. Each entry
93 graft two disconnected series of history together. Each entry
94 contains a key, followed by a space, followed by one or two
94 contains a key, followed by a space, followed by one or two
95 comma-separated values. The key is the revision ID in the source
95 comma-separated values. The key is the revision ID in the source
96 revision control system whose parents should be modified (same
96 revision control system whose parents should be modified (same
97 format as a key in .hg/shamap). The values are the revision IDs
97 format as a key in .hg/shamap). The values are the revision IDs
98 (in either the source or destination revision control system) that
98 (in either the source or destination revision control system) that
99 should be used as the new parents for that node.
99 should be used as the new parents for that node.
100
100
101 The branchmap is a file that allows you to rename a branch when it is
101 The branchmap is a file that allows you to rename a branch when it is
102 being brought in from whatever external repository. When used in
102 being brought in from whatever external repository. When used in
103 conjunction with a splicemap, it allows for a powerful combination
103 conjunction with a splicemap, it allows for a powerful combination
104 to help fix even the most badly mismanaged repositories and turn them
104 to help fix even the most badly mismanaged repositories and turn them
105 into nicely structured Mercurial repositories. The branchmap contains
105 into nicely structured Mercurial repositories. The branchmap contains
106 lines of the form "original_branch_name new_branch_name".
106 lines of the form "original_branch_name new_branch_name".
107 "original_branch_name" is the name of the branch in the source
107 "original_branch_name" is the name of the branch in the source
108 repository, and "new_branch_name" is the name of the branch is the
108 repository, and "new_branch_name" is the name of the branch is the
109 destination repository. This can be used to (for instance) move code
109 destination repository. This can be used to (for instance) move code
110 in one repository from "default" to a named branch.
110 in one repository from "default" to a named branch.
111
111
112 Mercurial Source
112 Mercurial Source
113 -----------------
113 -----------------
114
114
115 --config convert.hg.ignoreerrors=False (boolean)
115 --config convert.hg.ignoreerrors=False (boolean)
116 ignore integrity errors when reading. Use it to fix Mercurial
116 ignore integrity errors when reading. Use it to fix Mercurial
117 repositories with missing revlogs, by converting from and to
117 repositories with missing revlogs, by converting from and to
118 Mercurial.
118 Mercurial.
119 --config convert.hg.saverev=False (boolean)
119 --config convert.hg.saverev=False (boolean)
120 store original revision ID in changeset (forces target IDs to
120 store original revision ID in changeset (forces target IDs to
121 change)
121 change)
122 --config convert.hg.startrev=0 (hg revision identifier)
122 --config convert.hg.startrev=0 (hg revision identifier)
123 convert start revision and its descendants
123 convert start revision and its descendants
124
124
125 CVS Source
125 CVS Source
126 ----------
126 ----------
127
127
128 CVS source will use a sandbox (i.e. a checked-out copy) from CVS
128 CVS source will use a sandbox (i.e. a checked-out copy) from CVS
129 to indicate the starting point of what will be converted. Direct
129 to indicate the starting point of what will be converted. Direct
130 access to the repository files is not needed, unless of course the
130 access to the repository files is not needed, unless of course the
131 repository is :local:. The conversion uses the top level directory
131 repository is :local:. The conversion uses the top level directory
132 in the sandbox to find the CVS repository, and then uses CVS rlog
132 in the sandbox to find the CVS repository, and then uses CVS rlog
133 commands to find files to convert. This means that unless a
133 commands to find files to convert. This means that unless a
134 filemap is given, all files under the starting directory will be
134 filemap is given, all files under the starting directory will be
135 converted, and that any directory reorganization in the CVS
135 converted, and that any directory reorganization in the CVS
136 sandbox is ignored.
136 sandbox is ignored.
137
137
138 Because CVS does not have changesets, it is necessary to collect
138 Because CVS does not have changesets, it is necessary to collect
139 individual commits to CVS and merge them into changesets. CVS
139 individual commits to CVS and merge them into changesets. CVS
140 source uses its internal changeset merging code by default but can
140 source uses its internal changeset merging code by default but can
141 be configured to call the external 'cvsps' program by setting:
141 be configured to call the external 'cvsps' program by setting:
142 --config convert.cvsps='cvsps -A -u --cvs-direct -q'
142 --config convert.cvsps='cvsps -A -u --cvs-direct -q'
143 This option is deprecated and will be removed in Mercurial 1.4.
143 This option is deprecated and will be removed in Mercurial 1.4.
144
144
145 The options shown are the defaults.
145 The options shown are the defaults.
146
146
147 Internal cvsps is selected by setting
147 Internal cvsps is selected by setting
148 --config convert.cvsps=builtin
148 --config convert.cvsps=builtin
149 and has a few more configurable options:
149 and has a few more configurable options:
150 --config convert.cvsps.cache=True (boolean)
150 --config convert.cvsps.cache=True (boolean)
151 Set to False to disable remote log caching, for testing and
151 Set to False to disable remote log caching, for testing and
152 debugging purposes.
152 debugging purposes.
153 --config convert.cvsps.fuzz=60 (integer)
153 --config convert.cvsps.fuzz=60 (integer)
154 Specify the maximum time (in seconds) that is allowed
154 Specify the maximum time (in seconds) that is allowed
155 between commits with identical user and log message in a
155 between commits with identical user and log message in a
156 single changeset. When very large files were checked in as
156 single changeset. When very large files were checked in as
157 part of a changeset then the default may not be long
157 part of a changeset then the default may not be long
158 enough.
158 enough.
159 --config convert.cvsps.mergeto='{{mergetobranch ([-\\w]+)}}'
159 --config convert.cvsps.mergeto='{{mergetobranch ([-\\w]+)}}'
160 Specify a regular expression to which commit log messages
160 Specify a regular expression to which commit log messages
161 are matched. If a match occurs, then the conversion
161 are matched. If a match occurs, then the conversion
162 process will insert a dummy revision merging the branch on
162 process will insert a dummy revision merging the branch on
163 which this log message occurs to the branch indicated in
163 which this log message occurs to the branch indicated in
164 the regex.
164 the regex.
165 --config convert.cvsps.mergefrom='{{mergefrombranch ([-\\w]+)}}'
165 --config convert.cvsps.mergefrom='{{mergefrombranch ([-\\w]+)}}'
166 Specify a regular expression to which commit log messages
166 Specify a regular expression to which commit log messages
167 are matched. If a match occurs, then the conversion
167 are matched. If a match occurs, then the conversion
168 process will add the most recent revision on the branch
168 process will add the most recent revision on the branch
169 indicated in the regex as the second parent of the
169 indicated in the regex as the second parent of the
170 changeset.
170 changeset.
171
171
172 The hgext/convert/cvsps wrapper script allows the builtin
172 The hgext/convert/cvsps wrapper script allows the builtin
173 changeset merging code to be run without doing a conversion. Its
173 changeset merging code to be run without doing a conversion. Its
174 parameters and output are similar to that of cvsps 2.1.
174 parameters and output are similar to that of cvsps 2.1.
175
175
176 Subversion Source
176 Subversion Source
177 -----------------
177 -----------------
178
178
179 Subversion source detects classical trunk/branches/tags layouts.
179 Subversion source detects classical trunk/branches/tags layouts.
180 By default, the supplied "svn://repo/path/" source URL is
180 By default, the supplied "svn://repo/path/" source URL is
181 converted as a single branch. If "svn://repo/path/trunk" exists it
181 converted as a single branch. If "svn://repo/path/trunk" exists it
182 replaces the default branch. If "svn://repo/path/branches" exists,
182 replaces the default branch. If "svn://repo/path/branches" exists,
183 its subdirectories are listed as possible branches. If
183 its subdirectories are listed as possible branches. If
184 "svn://repo/path/tags" exists, it is looked for tags referencing
184 "svn://repo/path/tags" exists, it is looked for tags referencing
185 converted branches. Default "trunk", "branches" and "tags" values
185 converted branches. Default "trunk", "branches" and "tags" values
186 can be overridden with following options. Set them to paths
186 can be overridden with following options. Set them to paths
187 relative to the source URL, or leave them blank to disable auto
187 relative to the source URL, or leave them blank to disable auto
188 detection.
188 detection.
189
189
190 --config convert.svn.branches=branches (directory name)
190 --config convert.svn.branches=branches (directory name)
191 specify the directory containing branches
191 specify the directory containing branches
192 --config convert.svn.tags=tags (directory name)
192 --config convert.svn.tags=tags (directory name)
193 specify the directory containing tags
193 specify the directory containing tags
194 --config convert.svn.trunk=trunk (directory name)
194 --config convert.svn.trunk=trunk (directory name)
195 specify the name of the trunk branch
195 specify the name of the trunk branch
196
196
197 Source history can be retrieved starting at a specific revision,
197 Source history can be retrieved starting at a specific revision,
198 instead of being integrally converted. Only single branch
198 instead of being integrally converted. Only single branch
199 conversions are supported.
199 conversions are supported.
200
200
201 --config convert.svn.startrev=0 (svn revision number)
201 --config convert.svn.startrev=0 (svn revision number)
202 specify start Subversion revision.
202 specify start Subversion revision.
203
203
204 Perforce Source
204 Perforce Source
205 ---------------
205 ---------------
206
206
207 The Perforce (P4) importer can be given a p4 depot path or a
207 The Perforce (P4) importer can be given a p4 depot path or a
208 client specification as source. It will convert all files in the
208 client specification as source. It will convert all files in the
209 source to a flat Mercurial repository, ignoring labels, branches
209 source to a flat Mercurial repository, ignoring labels, branches
210 and integrations. Note that when a depot path is given you then
210 and integrations. Note that when a depot path is given you then
211 usually should specify a target directory, because otherwise the
211 usually should specify a target directory, because otherwise the
212 target may be named ...-hg.
212 target may be named ...-hg.
213
213
214 It is possible to limit the amount of source history to be
214 It is possible to limit the amount of source history to be
215 converted by specifying an initial Perforce revision.
215 converted by specifying an initial Perforce revision.
216
216
217 --config convert.p4.startrev=0 (perforce changelist number)
217 --config convert.p4.startrev=0 (perforce changelist number)
218 specify initial Perforce revision.
218 specify initial Perforce revision.
219
219
220
220
221 Mercurial Destination
221 Mercurial Destination
222 ---------------------
222 ---------------------
223
223
224 --config convert.hg.clonebranches=False (boolean)
224 --config convert.hg.clonebranches=False (boolean)
225 dispatch source branches in separate clones.
225 dispatch source branches in separate clones.
226 --config convert.hg.tagsbranch=default (branch name)
226 --config convert.hg.tagsbranch=default (branch name)
227 tag revisions branch name
227 tag revisions branch name
228 --config convert.hg.usebranchnames=True (boolean)
228 --config convert.hg.usebranchnames=True (boolean)
229 preserve branch names
229 preserve branch names
230
230
231 """
231 """
232 return convcmd.convert(ui, src, dest, revmapfile, **opts)
232 return convcmd.convert(ui, src, dest, revmapfile, **opts)
233
233
234 def debugsvnlog(ui, **opts):
234 def debugsvnlog(ui, **opts):
235 return subversion.debugsvnlog(ui, **opts)
235 return subversion.debugsvnlog(ui, **opts)
236
236
237 def debugcvsps(ui, *args, **opts):
237 def debugcvsps(ui, *args, **opts):
238 '''create changeset information from CVS
238 '''create changeset information from CVS
239
239
240 This command is intended as a debugging tool for the CVS to
240 This command is intended as a debugging tool for the CVS to
241 Mercurial converter, and can be used as a direct replacement for
241 Mercurial converter, and can be used as a direct replacement for
242 cvsps.
242 cvsps.
243
243
244 Hg debugcvsps reads the CVS rlog for current directory (or any
244 Hg debugcvsps reads the CVS rlog for current directory (or any
245 named directory) in the CVS repository, and converts the log to a
245 named directory) in the CVS repository, and converts the log to a
246 series of changesets based on matching commit log entries and
246 series of changesets based on matching commit log entries and
247 dates.'''
247 dates.'''
248 return cvsps.debugcvsps(ui, *args, **opts)
248 return cvsps.debugcvsps(ui, *args, **opts)
249
249
250 commands.norepo += " convert debugsvnlog debugcvsps"
250 commands.norepo += " convert debugsvnlog debugcvsps"
251
251
252 cmdtable = {
252 cmdtable = {
253 "convert":
253 "convert":
254 (convert,
254 (convert,
255 [('A', 'authors', '', _('username mapping filename')),
255 [('A', 'authors', '', _('username mapping filename')),
256 ('d', 'dest-type', '', _('destination repository type')),
256 ('d', 'dest-type', '', _('destination repository type')),
257 ('', 'filemap', '', _('remap file names using contents of file')),
257 ('', 'filemap', '', _('remap file names using contents of file')),
258 ('r', 'rev', '', _('import up to target revision REV')),
258 ('r', 'rev', '', _('import up to target revision REV')),
259 ('s', 'source-type', '', _('source repository type')),
259 ('s', 'source-type', '', _('source repository type')),
260 ('', 'splicemap', '', _('splice synthesized history into place')),
260 ('', 'splicemap', '', _('splice synthesized history into place')),
261 ('', 'branchmap', '', _('change branch names while converting')),
261 ('', 'branchmap', '', _('change branch names while converting')),
262 ('', 'branchsort', None, _('try to sort changesets by branches')),
262 ('', 'branchsort', None, _('try to sort changesets by branches')),
263 ('', 'datesort', None, _('try to sort changesets by date')),
263 ('', 'datesort', None, _('try to sort changesets by date')),
264 ('', 'sourcesort', None, _('preserve source changesets order'))],
264 ('', 'sourcesort', None, _('preserve source changesets order'))],
265 _('hg convert [OPTION]... SOURCE [DEST [REVMAP]]')),
265 _('hg convert [OPTION]... SOURCE [DEST [REVMAP]]')),
266 "debugsvnlog":
266 "debugsvnlog":
267 (debugsvnlog,
267 (debugsvnlog,
268 [],
268 [],
269 'hg debugsvnlog'),
269 'hg debugsvnlog'),
270 "debugcvsps":
270 "debugcvsps":
271 (debugcvsps,
271 (debugcvsps,
272 [
272 [
273 # Main options shared with cvsps-2.1
273 # Main options shared with cvsps-2.1
274 ('b', 'branches', [], _('only return changes on specified branches')),
274 ('b', 'branches', [], _('only return changes on specified branches')),
275 ('p', 'prefix', '', _('prefix to remove from file names')),
275 ('p', 'prefix', '', _('prefix to remove from file names')),
276 ('r', 'revisions', [], _('only return changes after or between specified tags')),
276 ('r', 'revisions', [], _('only return changes after or between specified tags')),
277 ('u', 'update-cache', None, _("update cvs log cache")),
277 ('u', 'update-cache', None, _("update cvs log cache")),
278 ('x', 'new-cache', None, _("create new cvs log cache")),
278 ('x', 'new-cache', None, _("create new cvs log cache")),
279 ('z', 'fuzz', 60, _('set commit time fuzz in seconds')),
279 ('z', 'fuzz', 60, _('set commit time fuzz in seconds')),
280 ('', 'root', '', _('specify cvsroot')),
280 ('', 'root', '', _('specify cvsroot')),
281 # Options specific to builtin cvsps
281 # Options specific to builtin cvsps
282 ('', 'parents', '', _('show parent changesets')),
282 ('', 'parents', '', _('show parent changesets')),
283 ('', 'ancestors', '', _('show current changeset in ancestor branches')),
283 ('', 'ancestors', '', _('show current changeset in ancestor branches')),
284 # Options that are ignored for compatibility with cvsps-2.1
284 # Options that are ignored for compatibility with cvsps-2.1
285 ('A', 'cvs-direct', None, _('ignored for compatibility')),
285 ('A', 'cvs-direct', None, _('ignored for compatibility')),
286 ],
286 ],
287 _('hg debugcvsps [OPTION]... [PATH]...')),
287 _('hg debugcvsps [OPTION]... [PATH]...')),
288 }
288 }
@@ -1,376 +1,376 b''
1 # ASCII graph log extension for Mercurial
1 # ASCII graph log extension for Mercurial
2 #
2 #
3 # Copyright 2007 Joel Rosdahl <joel@rosdahl.net>
3 # Copyright 2007 Joel Rosdahl <joel@rosdahl.net>
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 '''show revision graphs in terminals
8 '''view revision graphs from a shell
9
9
10 This extension adds a --graph option to the incoming, outgoing and log
10 This extension adds a --graph option to the incoming, outgoing and log
11 commands. When this options is given, an ASCII representation of the
11 commands. When this options is given, an ASCII representation of the
12 revision graph is also shown.
12 revision graph is also shown.
13 '''
13 '''
14
14
15 import os, sys
15 import os, sys
16 from mercurial.cmdutil import revrange, show_changeset
16 from mercurial.cmdutil import revrange, show_changeset
17 from mercurial.commands import templateopts
17 from mercurial.commands import templateopts
18 from mercurial.i18n import _
18 from mercurial.i18n import _
19 from mercurial.node import nullrev
19 from mercurial.node import nullrev
20 from mercurial import bundlerepo, changegroup, cmdutil, commands, extensions
20 from mercurial import bundlerepo, changegroup, cmdutil, commands, extensions
21 from mercurial import hg, url, util, graphmod
21 from mercurial import hg, url, util, graphmod
22
22
23 ASCIIDATA = 'ASC'
23 ASCIIDATA = 'ASC'
24
24
25 def asciiformat(ui, repo, revdag, opts):
25 def asciiformat(ui, repo, revdag, opts):
26 """formats a changelog DAG walk for ASCII output"""
26 """formats a changelog DAG walk for ASCII output"""
27 showparents = [ctx.node() for ctx in repo[None].parents()]
27 showparents = [ctx.node() for ctx in repo[None].parents()]
28 displayer = show_changeset(ui, repo, opts, buffered=True)
28 displayer = show_changeset(ui, repo, opts, buffered=True)
29 for (id, type, ctx, parentids) in revdag:
29 for (id, type, ctx, parentids) in revdag:
30 if type != graphmod.CHANGESET:
30 if type != graphmod.CHANGESET:
31 continue
31 continue
32 displayer.show(ctx)
32 displayer.show(ctx)
33 lines = displayer.hunk.pop(ctx.rev()).split('\n')[:-1]
33 lines = displayer.hunk.pop(ctx.rev()).split('\n')[:-1]
34 char = ctx.node() in showparents and '@' or 'o'
34 char = ctx.node() in showparents and '@' or 'o'
35 yield (id, ASCIIDATA, (char, lines), parentids)
35 yield (id, ASCIIDATA, (char, lines), parentids)
36
36
37 def asciiedges(nodes):
37 def asciiedges(nodes):
38 """adds edge info to changelog DAG walk suitable for ascii()"""
38 """adds edge info to changelog DAG walk suitable for ascii()"""
39 seen = []
39 seen = []
40 for node, type, data, parents in nodes:
40 for node, type, data, parents in nodes:
41 if node not in seen:
41 if node not in seen:
42 seen.append(node)
42 seen.append(node)
43 nodeidx = seen.index(node)
43 nodeidx = seen.index(node)
44
44
45 knownparents = []
45 knownparents = []
46 newparents = []
46 newparents = []
47 for parent in parents:
47 for parent in parents:
48 if parent in seen:
48 if parent in seen:
49 knownparents.append(parent)
49 knownparents.append(parent)
50 else:
50 else:
51 newparents.append(parent)
51 newparents.append(parent)
52
52
53 ncols = len(seen)
53 ncols = len(seen)
54 nextseen = seen[:]
54 nextseen = seen[:]
55 nextseen[nodeidx:nodeidx + 1] = newparents
55 nextseen[nodeidx:nodeidx + 1] = newparents
56 edges = [(nodeidx, nextseen.index(p)) for p in knownparents]
56 edges = [(nodeidx, nextseen.index(p)) for p in knownparents]
57
57
58 if len(newparents) > 0:
58 if len(newparents) > 0:
59 edges.append((nodeidx, nodeidx))
59 edges.append((nodeidx, nodeidx))
60 if len(newparents) > 1:
60 if len(newparents) > 1:
61 edges.append((nodeidx, nodeidx + 1))
61 edges.append((nodeidx, nodeidx + 1))
62 nmorecols = len(nextseen) - ncols
62 nmorecols = len(nextseen) - ncols
63 seen = nextseen
63 seen = nextseen
64 yield (nodeidx, type, data, edges, ncols, nmorecols)
64 yield (nodeidx, type, data, edges, ncols, nmorecols)
65
65
66 def fix_long_right_edges(edges):
66 def fix_long_right_edges(edges):
67 for (i, (start, end)) in enumerate(edges):
67 for (i, (start, end)) in enumerate(edges):
68 if end > start:
68 if end > start:
69 edges[i] = (start, end + 1)
69 edges[i] = (start, end + 1)
70
70
71 def get_nodeline_edges_tail(
71 def get_nodeline_edges_tail(
72 node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail):
72 node_index, p_node_index, n_columns, n_columns_diff, p_diff, fix_tail):
73 if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0:
73 if fix_tail and n_columns_diff == p_diff and n_columns_diff != 0:
74 # Still going in the same non-vertical direction.
74 # Still going in the same non-vertical direction.
75 if n_columns_diff == -1:
75 if n_columns_diff == -1:
76 start = max(node_index + 1, p_node_index)
76 start = max(node_index + 1, p_node_index)
77 tail = ["|", " "] * (start - node_index - 1)
77 tail = ["|", " "] * (start - node_index - 1)
78 tail.extend(["/", " "] * (n_columns - start))
78 tail.extend(["/", " "] * (n_columns - start))
79 return tail
79 return tail
80 else:
80 else:
81 return ["\\", " "] * (n_columns - node_index - 1)
81 return ["\\", " "] * (n_columns - node_index - 1)
82 else:
82 else:
83 return ["|", " "] * (n_columns - node_index - 1)
83 return ["|", " "] * (n_columns - node_index - 1)
84
84
85 def draw_edges(edges, nodeline, interline):
85 def draw_edges(edges, nodeline, interline):
86 for (start, end) in edges:
86 for (start, end) in edges:
87 if start == end + 1:
87 if start == end + 1:
88 interline[2 * end + 1] = "/"
88 interline[2 * end + 1] = "/"
89 elif start == end - 1:
89 elif start == end - 1:
90 interline[2 * start + 1] = "\\"
90 interline[2 * start + 1] = "\\"
91 elif start == end:
91 elif start == end:
92 interline[2 * start] = "|"
92 interline[2 * start] = "|"
93 else:
93 else:
94 nodeline[2 * end] = "+"
94 nodeline[2 * end] = "+"
95 if start > end:
95 if start > end:
96 (start, end) = (end,start)
96 (start, end) = (end,start)
97 for i in range(2 * start + 1, 2 * end):
97 for i in range(2 * start + 1, 2 * end):
98 if nodeline[i] != "+":
98 if nodeline[i] != "+":
99 nodeline[i] = "-"
99 nodeline[i] = "-"
100
100
101 def get_padding_line(ni, n_columns, edges):
101 def get_padding_line(ni, n_columns, edges):
102 line = []
102 line = []
103 line.extend(["|", " "] * ni)
103 line.extend(["|", " "] * ni)
104 if (ni, ni - 1) in edges or (ni, ni) in edges:
104 if (ni, ni - 1) in edges or (ni, ni) in edges:
105 # (ni, ni - 1) (ni, ni)
105 # (ni, ni - 1) (ni, ni)
106 # | | | | | | | |
106 # | | | | | | | |
107 # +---o | | o---+
107 # +---o | | o---+
108 # | | c | | c | |
108 # | | c | | c | |
109 # | |/ / | |/ /
109 # | |/ / | |/ /
110 # | | | | | |
110 # | | | | | |
111 c = "|"
111 c = "|"
112 else:
112 else:
113 c = " "
113 c = " "
114 line.extend([c, " "])
114 line.extend([c, " "])
115 line.extend(["|", " "] * (n_columns - ni - 1))
115 line.extend(["|", " "] * (n_columns - ni - 1))
116 return line
116 return line
117
117
118 def ascii(ui, dag):
118 def ascii(ui, dag):
119 """prints an ASCII graph of the DAG
119 """prints an ASCII graph of the DAG
120
120
121 dag is a generator that emits tuples with the following elements:
121 dag is a generator that emits tuples with the following elements:
122
122
123 - Column of the current node in the set of ongoing edges.
123 - Column of the current node in the set of ongoing edges.
124 - Type indicator of node data == ASCIIDATA.
124 - Type indicator of node data == ASCIIDATA.
125 - Payload: (char, lines):
125 - Payload: (char, lines):
126 - Character to use as node's symbol.
126 - Character to use as node's symbol.
127 - List of lines to display as the node's text.
127 - List of lines to display as the node's text.
128 - Edges; a list of (col, next_col) indicating the edges between
128 - Edges; a list of (col, next_col) indicating the edges between
129 the current node and its parents.
129 the current node and its parents.
130 - Number of columns (ongoing edges) in the current revision.
130 - Number of columns (ongoing edges) in the current revision.
131 - The difference between the number of columns (ongoing edges)
131 - The difference between the number of columns (ongoing edges)
132 in the next revision and the number of columns (ongoing edges)
132 in the next revision and the number of columns (ongoing edges)
133 in the current revision. That is: -1 means one column removed;
133 in the current revision. That is: -1 means one column removed;
134 0 means no columns added or removed; 1 means one column added.
134 0 means no columns added or removed; 1 means one column added.
135 """
135 """
136 prev_n_columns_diff = 0
136 prev_n_columns_diff = 0
137 prev_node_index = 0
137 prev_node_index = 0
138 for (node_index, type, (node_ch, node_lines), edges, n_columns, n_columns_diff) in dag:
138 for (node_index, type, (node_ch, node_lines), edges, n_columns, n_columns_diff) in dag:
139
139
140 assert -2 < n_columns_diff < 2
140 assert -2 < n_columns_diff < 2
141 if n_columns_diff == -1:
141 if n_columns_diff == -1:
142 # Transform
142 # Transform
143 #
143 #
144 # | | | | | |
144 # | | | | | |
145 # o | | into o---+
145 # o | | into o---+
146 # |X / |/ /
146 # |X / |/ /
147 # | | | |
147 # | | | |
148 fix_long_right_edges(edges)
148 fix_long_right_edges(edges)
149
149
150 # add_padding_line says whether to rewrite
150 # add_padding_line says whether to rewrite
151 #
151 #
152 # | | | | | | | |
152 # | | | | | | | |
153 # | o---+ into | o---+
153 # | o---+ into | o---+
154 # | / / | | | # <--- padding line
154 # | / / | | | # <--- padding line
155 # o | | | / /
155 # o | | | / /
156 # o | |
156 # o | |
157 add_padding_line = (len(node_lines) > 2 and
157 add_padding_line = (len(node_lines) > 2 and
158 n_columns_diff == -1 and
158 n_columns_diff == -1 and
159 [x for (x, y) in edges if x + 1 < y])
159 [x for (x, y) in edges if x + 1 < y])
160
160
161 # fix_nodeline_tail says whether to rewrite
161 # fix_nodeline_tail says whether to rewrite
162 #
162 #
163 # | | o | | | | o | |
163 # | | o | | | | o | |
164 # | | |/ / | | |/ /
164 # | | |/ / | | |/ /
165 # | o | | into | o / / # <--- fixed nodeline tail
165 # | o | | into | o / / # <--- fixed nodeline tail
166 # | |/ / | |/ /
166 # | |/ / | |/ /
167 # o | | o | |
167 # o | | o | |
168 fix_nodeline_tail = len(node_lines) <= 2 and not add_padding_line
168 fix_nodeline_tail = len(node_lines) <= 2 and not add_padding_line
169
169
170 # nodeline is the line containing the node character (typically o)
170 # nodeline is the line containing the node character (typically o)
171 nodeline = ["|", " "] * node_index
171 nodeline = ["|", " "] * node_index
172 nodeline.extend([node_ch, " "])
172 nodeline.extend([node_ch, " "])
173
173
174 nodeline.extend(
174 nodeline.extend(
175 get_nodeline_edges_tail(
175 get_nodeline_edges_tail(
176 node_index, prev_node_index, n_columns, n_columns_diff,
176 node_index, prev_node_index, n_columns, n_columns_diff,
177 prev_n_columns_diff, fix_nodeline_tail))
177 prev_n_columns_diff, fix_nodeline_tail))
178
178
179 # shift_interline is the line containing the non-vertical
179 # shift_interline is the line containing the non-vertical
180 # edges between this entry and the next
180 # edges between this entry and the next
181 shift_interline = ["|", " "] * node_index
181 shift_interline = ["|", " "] * node_index
182 if n_columns_diff == -1:
182 if n_columns_diff == -1:
183 n_spaces = 1
183 n_spaces = 1
184 edge_ch = "/"
184 edge_ch = "/"
185 elif n_columns_diff == 0:
185 elif n_columns_diff == 0:
186 n_spaces = 2
186 n_spaces = 2
187 edge_ch = "|"
187 edge_ch = "|"
188 else:
188 else:
189 n_spaces = 3
189 n_spaces = 3
190 edge_ch = "\\"
190 edge_ch = "\\"
191 shift_interline.extend(n_spaces * [" "])
191 shift_interline.extend(n_spaces * [" "])
192 shift_interline.extend([edge_ch, " "] * (n_columns - node_index - 1))
192 shift_interline.extend([edge_ch, " "] * (n_columns - node_index - 1))
193
193
194 # draw edges from the current node to its parents
194 # draw edges from the current node to its parents
195 draw_edges(edges, nodeline, shift_interline)
195 draw_edges(edges, nodeline, shift_interline)
196
196
197 # lines is the list of all graph lines to print
197 # lines is the list of all graph lines to print
198 lines = [nodeline]
198 lines = [nodeline]
199 if add_padding_line:
199 if add_padding_line:
200 lines.append(get_padding_line(node_index, n_columns, edges))
200 lines.append(get_padding_line(node_index, n_columns, edges))
201 lines.append(shift_interline)
201 lines.append(shift_interline)
202
202
203 # make sure that there are as many graph lines as there are
203 # make sure that there are as many graph lines as there are
204 # log strings
204 # log strings
205 while len(node_lines) < len(lines):
205 while len(node_lines) < len(lines):
206 node_lines.append("")
206 node_lines.append("")
207 if len(lines) < len(node_lines):
207 if len(lines) < len(node_lines):
208 extra_interline = ["|", " "] * (n_columns + n_columns_diff)
208 extra_interline = ["|", " "] * (n_columns + n_columns_diff)
209 while len(lines) < len(node_lines):
209 while len(lines) < len(node_lines):
210 lines.append(extra_interline)
210 lines.append(extra_interline)
211
211
212 # print lines
212 # print lines
213 indentation_level = max(n_columns, n_columns + n_columns_diff)
213 indentation_level = max(n_columns, n_columns + n_columns_diff)
214 for (line, logstr) in zip(lines, node_lines):
214 for (line, logstr) in zip(lines, node_lines):
215 ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr)
215 ln = "%-*s %s" % (2 * indentation_level, "".join(line), logstr)
216 ui.write(ln.rstrip() + '\n')
216 ui.write(ln.rstrip() + '\n')
217
217
218 # ... and start over
218 # ... and start over
219 prev_node_index = node_index
219 prev_node_index = node_index
220 prev_n_columns_diff = n_columns_diff
220 prev_n_columns_diff = n_columns_diff
221
221
222 def get_revs(repo, rev_opt):
222 def get_revs(repo, rev_opt):
223 if rev_opt:
223 if rev_opt:
224 revs = revrange(repo, rev_opt)
224 revs = revrange(repo, rev_opt)
225 return (max(revs), min(revs))
225 return (max(revs), min(revs))
226 else:
226 else:
227 return (len(repo) - 1, 0)
227 return (len(repo) - 1, 0)
228
228
229 def check_unsupported_flags(opts):
229 def check_unsupported_flags(opts):
230 for op in ["follow", "follow_first", "date", "copies", "keyword", "remove",
230 for op in ["follow", "follow_first", "date", "copies", "keyword", "remove",
231 "only_merges", "user", "only_branch", "prune", "newest_first",
231 "only_merges", "user", "only_branch", "prune", "newest_first",
232 "no_merges", "include", "exclude"]:
232 "no_merges", "include", "exclude"]:
233 if op in opts and opts[op]:
233 if op in opts and opts[op]:
234 raise util.Abort(_("--graph option is incompatible with --%s") % op)
234 raise util.Abort(_("--graph option is incompatible with --%s") % op)
235
235
236 def graphlog(ui, repo, path=None, **opts):
236 def graphlog(ui, repo, path=None, **opts):
237 """show revision history alongside an ASCII revision graph
237 """show revision history alongside an ASCII revision graph
238
238
239 Print a revision history alongside a revision graph drawn with
239 Print a revision history alongside a revision graph drawn with
240 ASCII characters.
240 ASCII characters.
241
241
242 Nodes printed as an @ character are parents of the working
242 Nodes printed as an @ character are parents of the working
243 directory.
243 directory.
244 """
244 """
245
245
246 check_unsupported_flags(opts)
246 check_unsupported_flags(opts)
247 limit = cmdutil.loglimit(opts)
247 limit = cmdutil.loglimit(opts)
248 start, stop = get_revs(repo, opts["rev"])
248 start, stop = get_revs(repo, opts["rev"])
249 stop = max(stop, start - limit + 1)
249 stop = max(stop, start - limit + 1)
250 if start == nullrev:
250 if start == nullrev:
251 return
251 return
252
252
253 if path:
253 if path:
254 path = util.canonpath(repo.root, os.getcwd(), path)
254 path = util.canonpath(repo.root, os.getcwd(), path)
255 if path: # could be reset in canonpath
255 if path: # could be reset in canonpath
256 revdag = graphmod.filerevs(repo, path, start, stop)
256 revdag = graphmod.filerevs(repo, path, start, stop)
257 else:
257 else:
258 revdag = graphmod.revisions(repo, start, stop)
258 revdag = graphmod.revisions(repo, start, stop)
259
259
260 fmtdag = asciiformat(ui, repo, revdag, opts)
260 fmtdag = asciiformat(ui, repo, revdag, opts)
261 ascii(ui, asciiedges(fmtdag))
261 ascii(ui, asciiedges(fmtdag))
262
262
263 def graphrevs(repo, nodes, opts):
263 def graphrevs(repo, nodes, opts):
264 limit = cmdutil.loglimit(opts)
264 limit = cmdutil.loglimit(opts)
265 nodes.reverse()
265 nodes.reverse()
266 if limit < sys.maxint:
266 if limit < sys.maxint:
267 nodes = nodes[:limit]
267 nodes = nodes[:limit]
268 return graphmod.nodes(repo, nodes)
268 return graphmod.nodes(repo, nodes)
269
269
270 def goutgoing(ui, repo, dest=None, **opts):
270 def goutgoing(ui, repo, dest=None, **opts):
271 """show the outgoing changesets alongside an ASCII revision graph
271 """show the outgoing changesets alongside an ASCII revision graph
272
272
273 Print the outgoing changesets alongside a revision graph drawn with
273 Print the outgoing changesets alongside a revision graph drawn with
274 ASCII characters.
274 ASCII characters.
275
275
276 Nodes printed as an @ character are parents of the working
276 Nodes printed as an @ character are parents of the working
277 directory.
277 directory.
278 """
278 """
279
279
280 check_unsupported_flags(opts)
280 check_unsupported_flags(opts)
281 dest, revs, checkout = hg.parseurl(
281 dest, revs, checkout = hg.parseurl(
282 ui.expandpath(dest or 'default-push', dest or 'default'),
282 ui.expandpath(dest or 'default-push', dest or 'default'),
283 opts.get('rev'))
283 opts.get('rev'))
284 if revs:
284 if revs:
285 revs = [repo.lookup(rev) for rev in revs]
285 revs = [repo.lookup(rev) for rev in revs]
286 other = hg.repository(cmdutil.remoteui(ui, opts), dest)
286 other = hg.repository(cmdutil.remoteui(ui, opts), dest)
287 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
287 ui.status(_('comparing with %s\n') % url.hidepassword(dest))
288 o = repo.findoutgoing(other, force=opts.get('force'))
288 o = repo.findoutgoing(other, force=opts.get('force'))
289 if not o:
289 if not o:
290 ui.status(_("no changes found\n"))
290 ui.status(_("no changes found\n"))
291 return
291 return
292
292
293 o = repo.changelog.nodesbetween(o, revs)[0]
293 o = repo.changelog.nodesbetween(o, revs)[0]
294 revdag = graphrevs(repo, o, opts)
294 revdag = graphrevs(repo, o, opts)
295 fmtdag = asciiformat(ui, repo, revdag, opts)
295 fmtdag = asciiformat(ui, repo, revdag, opts)
296 ascii(ui, asciiedges(fmtdag))
296 ascii(ui, asciiedges(fmtdag))
297
297
298 def gincoming(ui, repo, source="default", **opts):
298 def gincoming(ui, repo, source="default", **opts):
299 """show the incoming changesets alongside an ASCII revision graph
299 """show the incoming changesets alongside an ASCII revision graph
300
300
301 Print the incoming changesets alongside a revision graph drawn with
301 Print the incoming changesets alongside a revision graph drawn with
302 ASCII characters.
302 ASCII characters.
303
303
304 Nodes printed as an @ character are parents of the working
304 Nodes printed as an @ character are parents of the working
305 directory.
305 directory.
306 """
306 """
307
307
308 check_unsupported_flags(opts)
308 check_unsupported_flags(opts)
309 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
309 source, revs, checkout = hg.parseurl(ui.expandpath(source), opts.get('rev'))
310 other = hg.repository(cmdutil.remoteui(repo, opts), source)
310 other = hg.repository(cmdutil.remoteui(repo, opts), source)
311 ui.status(_('comparing with %s\n') % url.hidepassword(source))
311 ui.status(_('comparing with %s\n') % url.hidepassword(source))
312 if revs:
312 if revs:
313 revs = [other.lookup(rev) for rev in revs]
313 revs = [other.lookup(rev) for rev in revs]
314 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
314 incoming = repo.findincoming(other, heads=revs, force=opts["force"])
315 if not incoming:
315 if not incoming:
316 try:
316 try:
317 os.unlink(opts["bundle"])
317 os.unlink(opts["bundle"])
318 except:
318 except:
319 pass
319 pass
320 ui.status(_("no changes found\n"))
320 ui.status(_("no changes found\n"))
321 return
321 return
322
322
323 cleanup = None
323 cleanup = None
324 try:
324 try:
325
325
326 fname = opts["bundle"]
326 fname = opts["bundle"]
327 if fname or not other.local():
327 if fname or not other.local():
328 # create a bundle (uncompressed if other repo is not local)
328 # create a bundle (uncompressed if other repo is not local)
329 if revs is None:
329 if revs is None:
330 cg = other.changegroup(incoming, "incoming")
330 cg = other.changegroup(incoming, "incoming")
331 else:
331 else:
332 cg = other.changegroupsubset(incoming, revs, 'incoming')
332 cg = other.changegroupsubset(incoming, revs, 'incoming')
333 bundletype = other.local() and "HG10BZ" or "HG10UN"
333 bundletype = other.local() and "HG10BZ" or "HG10UN"
334 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
334 fname = cleanup = changegroup.writebundle(cg, fname, bundletype)
335 # keep written bundle?
335 # keep written bundle?
336 if opts["bundle"]:
336 if opts["bundle"]:
337 cleanup = None
337 cleanup = None
338 if not other.local():
338 if not other.local():
339 # use the created uncompressed bundlerepo
339 # use the created uncompressed bundlerepo
340 other = bundlerepo.bundlerepository(ui, repo.root, fname)
340 other = bundlerepo.bundlerepository(ui, repo.root, fname)
341
341
342 chlist = other.changelog.nodesbetween(incoming, revs)[0]
342 chlist = other.changelog.nodesbetween(incoming, revs)[0]
343 revdag = graphrevs(other, chlist, opts)
343 revdag = graphrevs(other, chlist, opts)
344 fmtdag = asciiformat(ui, repo, revdag, opts)
344 fmtdag = asciiformat(ui, repo, revdag, opts)
345 ascii(ui, asciiedges(fmtdag))
345 ascii(ui, asciiedges(fmtdag))
346
346
347 finally:
347 finally:
348 if hasattr(other, 'close'):
348 if hasattr(other, 'close'):
349 other.close()
349 other.close()
350 if cleanup:
350 if cleanup:
351 os.unlink(cleanup)
351 os.unlink(cleanup)
352
352
353 def uisetup(ui):
353 def uisetup(ui):
354 '''Initialize the extension.'''
354 '''Initialize the extension.'''
355 _wrapcmd(ui, 'log', commands.table, graphlog)
355 _wrapcmd(ui, 'log', commands.table, graphlog)
356 _wrapcmd(ui, 'incoming', commands.table, gincoming)
356 _wrapcmd(ui, 'incoming', commands.table, gincoming)
357 _wrapcmd(ui, 'outgoing', commands.table, goutgoing)
357 _wrapcmd(ui, 'outgoing', commands.table, goutgoing)
358
358
359 def _wrapcmd(ui, cmd, table, wrapfn):
359 def _wrapcmd(ui, cmd, table, wrapfn):
360 '''wrap the command'''
360 '''wrap the command'''
361 def graph(orig, *args, **kwargs):
361 def graph(orig, *args, **kwargs):
362 if kwargs['graph']:
362 if kwargs['graph']:
363 return wrapfn(*args, **kwargs)
363 return wrapfn(*args, **kwargs)
364 return orig(*args, **kwargs)
364 return orig(*args, **kwargs)
365 entry = extensions.wrapcommand(table, cmd, graph)
365 entry = extensions.wrapcommand(table, cmd, graph)
366 entry[1].append(('G', 'graph', None, _("show the revision DAG")))
366 entry[1].append(('G', 'graph', None, _("show the revision DAG")))
367
367
368 cmdtable = {
368 cmdtable = {
369 "glog":
369 "glog":
370 (graphlog,
370 (graphlog,
371 [('l', 'limit', '', _('limit number of changes displayed')),
371 [('l', 'limit', '', _('limit number of changes displayed')),
372 ('p', 'patch', False, _('show patch')),
372 ('p', 'patch', False, _('show patch')),
373 ('r', 'rev', [], _('show the specified revision or range')),
373 ('r', 'rev', [], _('show the specified revision or range')),
374 ] + templateopts,
374 ] + templateopts,
375 _('hg glog [OPTION]... [FILE]')),
375 _('hg glog [OPTION]... [FILE]')),
376 }
376 }
@@ -1,62 +1,62 b''
1 # highlight - syntax highlighting in hgweb, based on Pygments
1 # highlight - syntax highlighting in hgweb, based on Pygments
2 #
2 #
3 # Copyright 2008, 2009 Patrick Mezard <pmezard@gmail.com> and others
3 # Copyright 2008, 2009 Patrick Mezard <pmezard@gmail.com> and others
4 #
4 #
5 # This software may be used and distributed according to the terms of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7 #
7 #
8 # The original module was split in an interface and an implementation
8 # The original module was split in an interface and an implementation
9 # file to defer pygments loading and speedup extension setup.
9 # file to defer pygments loading and speedup extension setup.
10
10
11 """syntax highlighting for hgweb
11 """syntax highlighting for hgweb (requires Pygments)
12
12
13 It depends on the Pygments syntax highlighting library:
13 It depends on the Pygments syntax highlighting library:
14 http://pygments.org/
14 http://pygments.org/
15
15
16 There is a single configuration option:
16 There is a single configuration option:
17
17
18 [web]
18 [web]
19 pygments_style = <style>
19 pygments_style = <style>
20
20
21 The default is 'colorful'.
21 The default is 'colorful'.
22
22
23 -- Adam Hupp <adam@hupp.org>
23 -- Adam Hupp <adam@hupp.org>
24 """
24 """
25
25
26 import highlight
26 import highlight
27 from mercurial.hgweb import webcommands, webutil, common
27 from mercurial.hgweb import webcommands, webutil, common
28 from mercurial import extensions, encoding
28 from mercurial import extensions, encoding
29
29
30 def filerevision_highlight(orig, web, tmpl, fctx):
30 def filerevision_highlight(orig, web, tmpl, fctx):
31 mt = ''.join(tmpl('mimetype', encoding=encoding.encoding))
31 mt = ''.join(tmpl('mimetype', encoding=encoding.encoding))
32 # only pygmentize for mimetype containing 'html' so we both match
32 # only pygmentize for mimetype containing 'html' so we both match
33 # 'text/html' and possibly 'application/xhtml+xml' in the future
33 # 'text/html' and possibly 'application/xhtml+xml' in the future
34 # so that we don't have to touch the extension when the mimetype
34 # so that we don't have to touch the extension when the mimetype
35 # for a template changes; also hgweb optimizes the case that a
35 # for a template changes; also hgweb optimizes the case that a
36 # raw file is sent using rawfile() and doesn't call us, so we
36 # raw file is sent using rawfile() and doesn't call us, so we
37 # can't clash with the file's content-type here in case we
37 # can't clash with the file's content-type here in case we
38 # pygmentize a html file
38 # pygmentize a html file
39 if 'html' in mt:
39 if 'html' in mt:
40 style = web.config('web', 'pygments_style', 'colorful')
40 style = web.config('web', 'pygments_style', 'colorful')
41 highlight.pygmentize('fileline', fctx, style, tmpl)
41 highlight.pygmentize('fileline', fctx, style, tmpl)
42 return orig(web, tmpl, fctx)
42 return orig(web, tmpl, fctx)
43
43
44 def annotate_highlight(orig, web, req, tmpl):
44 def annotate_highlight(orig, web, req, tmpl):
45 mt = ''.join(tmpl('mimetype', encoding=encoding.encoding))
45 mt = ''.join(tmpl('mimetype', encoding=encoding.encoding))
46 if 'html' in mt:
46 if 'html' in mt:
47 fctx = webutil.filectx(web.repo, req)
47 fctx = webutil.filectx(web.repo, req)
48 style = web.config('web', 'pygments_style', 'colorful')
48 style = web.config('web', 'pygments_style', 'colorful')
49 highlight.pygmentize('annotateline', fctx, style, tmpl)
49 highlight.pygmentize('annotateline', fctx, style, tmpl)
50 return orig(web, req, tmpl)
50 return orig(web, req, tmpl)
51
51
52 def generate_css(web, req, tmpl):
52 def generate_css(web, req, tmpl):
53 pg_style = web.config('web', 'pygments_style', 'colorful')
53 pg_style = web.config('web', 'pygments_style', 'colorful')
54 fmter = highlight.HtmlFormatter(style = pg_style)
54 fmter = highlight.HtmlFormatter(style = pg_style)
55 req.respond(common.HTTP_OK, 'text/css')
55 req.respond(common.HTTP_OK, 'text/css')
56 return ['/* pygments_style = %s */\n\n' % pg_style, fmter.get_style_defs('')]
56 return ['/* pygments_style = %s */\n\n' % pg_style, fmter.get_style_defs('')]
57
57
58 # monkeypatch in the new version
58 # monkeypatch in the new version
59 extensions.wrapfunction(webcommands, '_filerevision', filerevision_highlight)
59 extensions.wrapfunction(webcommands, '_filerevision', filerevision_highlight)
60 extensions.wrapfunction(webcommands, 'annotate', annotate_highlight)
60 extensions.wrapfunction(webcommands, 'annotate', annotate_highlight)
61 webcommands.highlightcss = generate_css
61 webcommands.highlightcss = generate_css
62 webcommands.__all__.append('highlightcss')
62 webcommands.__all__.append('highlightcss')
@@ -1,111 +1,111 b''
1 # __init__.py - inotify-based status acceleration for Linux
1 # __init__.py - inotify-based status acceleration for Linux
2 #
2 #
3 # Copyright 2006, 2007, 2008 Bryan O'Sullivan <bos@serpentine.com>
3 # Copyright 2006, 2007, 2008 Bryan O'Sullivan <bos@serpentine.com>
4 # Copyright 2007, 2008 Brendan Cully <brendan@kublai.com>
4 # Copyright 2007, 2008 Brendan Cully <brendan@kublai.com>
5 #
5 #
6 # This software may be used and distributed according to the terms of the
6 # This software may be used and distributed according to the terms of the
7 # GNU General Public License version 2, incorporated herein by reference.
7 # GNU General Public License version 2, incorporated herein by reference.
8
8
9 '''accelerate status report using system level services'''
9 '''accelerate status report using Linux's inotify service'''
10
10
11 # todo: socket permissions
11 # todo: socket permissions
12
12
13 from mercurial.i18n import _
13 from mercurial.i18n import _
14 from mercurial import cmdutil, util
14 from mercurial import cmdutil, util
15 import server
15 import server
16 from weakref import proxy
16 from weakref import proxy
17 from client import client, QueryFailed
17 from client import client, QueryFailed
18
18
19 def serve(ui, repo, **opts):
19 def serve(ui, repo, **opts):
20 '''start an inotify server for this repository'''
20 '''start an inotify server for this repository'''
21 timeout = opts.get('timeout')
21 timeout = opts.get('timeout')
22 if timeout:
22 if timeout:
23 timeout = float(timeout) * 1e3
23 timeout = float(timeout) * 1e3
24
24
25 class service(object):
25 class service(object):
26 def init(self):
26 def init(self):
27 try:
27 try:
28 self.master = server.master(ui, repo, timeout)
28 self.master = server.master(ui, repo, timeout)
29 except server.AlreadyStartedException, inst:
29 except server.AlreadyStartedException, inst:
30 raise util.Abort(str(inst))
30 raise util.Abort(str(inst))
31
31
32 def run(self):
32 def run(self):
33 try:
33 try:
34 self.master.run()
34 self.master.run()
35 finally:
35 finally:
36 self.master.shutdown()
36 self.master.shutdown()
37
37
38 service = service()
38 service = service()
39 logfile = ui.config('inotify', 'log')
39 logfile = ui.config('inotify', 'log')
40 cmdutil.service(opts, initfn=service.init, runfn=service.run,
40 cmdutil.service(opts, initfn=service.init, runfn=service.run,
41 logfile=logfile)
41 logfile=logfile)
42
42
43 def debuginotify(ui, repo, **opts):
43 def debuginotify(ui, repo, **opts):
44 '''debugging information for inotify extension
44 '''debugging information for inotify extension
45
45
46 Prints the list of directories being watched by the inotify server.
46 Prints the list of directories being watched by the inotify server.
47 '''
47 '''
48 cli = client(ui, repo)
48 cli = client(ui, repo)
49 response = cli.debugquery()
49 response = cli.debugquery()
50
50
51 ui.write(_('directories being watched:\n'))
51 ui.write(_('directories being watched:\n'))
52 for path in response:
52 for path in response:
53 ui.write((' %s/\n') % path)
53 ui.write((' %s/\n') % path)
54
54
55 def reposetup(ui, repo):
55 def reposetup(ui, repo):
56 if not hasattr(repo, 'dirstate'):
56 if not hasattr(repo, 'dirstate'):
57 return
57 return
58
58
59 # XXX: weakref until hg stops relying on __del__
59 # XXX: weakref until hg stops relying on __del__
60 repo = proxy(repo)
60 repo = proxy(repo)
61
61
62 class inotifydirstate(repo.dirstate.__class__):
62 class inotifydirstate(repo.dirstate.__class__):
63
63
64 # We'll set this to false after an unsuccessful attempt so that
64 # We'll set this to false after an unsuccessful attempt so that
65 # next calls of status() within the same instance don't try again
65 # next calls of status() within the same instance don't try again
66 # to start an inotify server if it won't start.
66 # to start an inotify server if it won't start.
67 _inotifyon = True
67 _inotifyon = True
68
68
69 def status(self, match, ignored, clean, unknown=True):
69 def status(self, match, ignored, clean, unknown=True):
70 files = match.files()
70 files = match.files()
71 if '.' in files:
71 if '.' in files:
72 files = []
72 files = []
73 if self._inotifyon and not ignored:
73 if self._inotifyon and not ignored:
74 cli = client(ui, repo)
74 cli = client(ui, repo)
75 try:
75 try:
76 result = cli.statusquery(files, match, False,
76 result = cli.statusquery(files, match, False,
77 clean, unknown)
77 clean, unknown)
78 except QueryFailed, instr:
78 except QueryFailed, instr:
79 ui.debug(str(instr))
79 ui.debug(str(instr))
80 # don't retry within the same hg instance
80 # don't retry within the same hg instance
81 inotifydirstate._inotifyon = False
81 inotifydirstate._inotifyon = False
82 pass
82 pass
83 else:
83 else:
84 if ui.config('inotify', 'debug'):
84 if ui.config('inotify', 'debug'):
85 r2 = super(inotifydirstate, self).status(
85 r2 = super(inotifydirstate, self).status(
86 match, False, clean, unknown)
86 match, False, clean, unknown)
87 for c,a,b in zip('LMARDUIC', result, r2):
87 for c,a,b in zip('LMARDUIC', result, r2):
88 for f in a:
88 for f in a:
89 if f not in b:
89 if f not in b:
90 ui.warn('*** inotify: %s +%s\n' % (c, f))
90 ui.warn('*** inotify: %s +%s\n' % (c, f))
91 for f in b:
91 for f in b:
92 if f not in a:
92 if f not in a:
93 ui.warn('*** inotify: %s -%s\n' % (c, f))
93 ui.warn('*** inotify: %s -%s\n' % (c, f))
94 result = r2
94 result = r2
95 return result
95 return result
96 return super(inotifydirstate, self).status(
96 return super(inotifydirstate, self).status(
97 match, ignored, clean, unknown)
97 match, ignored, clean, unknown)
98
98
99 repo.dirstate.__class__ = inotifydirstate
99 repo.dirstate.__class__ = inotifydirstate
100
100
101 cmdtable = {
101 cmdtable = {
102 'debuginotify':
102 'debuginotify':
103 (debuginotify, [], ('hg debuginotify')),
103 (debuginotify, [], ('hg debuginotify')),
104 '^inserve':
104 '^inserve':
105 (serve,
105 (serve,
106 [('d', 'daemon', None, _('run server in background')),
106 [('d', 'daemon', None, _('run server in background')),
107 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
107 ('', 'daemon-pipefds', '', _('used internally by daemon mode')),
108 ('t', 'idle-timeout', '', _('minutes to sit idle before exiting')),
108 ('t', 'idle-timeout', '', _('minutes to sit idle before exiting')),
109 ('', 'pid-file', '', _('name of file to write process ID to'))],
109 ('', 'pid-file', '', _('name of file to write process ID to'))],
110 _('hg inserve [OPT]...')),
110 _('hg inserve [OPT]...')),
111 }
111 }
@@ -1,2625 +1,2625 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 of the
5 # This software may be used and distributed according to the terms of the
6 # GNU General Public License version 2, incorporated herein by reference.
6 # GNU General Public License version 2, incorporated herein by reference.
7
7
8 '''work with a stack of patches
8 '''manage a stack of patches
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, nullid, nullrev
33 from mercurial.node import bin, hex, short, nullid, nullrev
34 from mercurial.lock import release
34 from mercurial.lock import release
35 from mercurial import commands, cmdutil, hg, patch, util
35 from mercurial import commands, cmdutil, hg, patch, util
36 from mercurial import repair, extensions, url, error
36 from mercurial import repair, extensions, url, error
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(object):
45 class statusentry(object):
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 patchheader(object):
59 class patchheader(object):
60 def __init__(self, pf):
60 def __init__(self, pf):
61 def eatdiff(lines):
61 def eatdiff(lines):
62 while lines:
62 while lines:
63 l = lines[-1]
63 l = lines[-1]
64 if (l.startswith("diff -") or
64 if (l.startswith("diff -") or
65 l.startswith("Index:") or
65 l.startswith("Index:") or
66 l.startswith("===========")):
66 l.startswith("===========")):
67 del lines[-1]
67 del lines[-1]
68 else:
68 else:
69 break
69 break
70 def eatempty(lines):
70 def eatempty(lines):
71 while lines:
71 while lines:
72 l = lines[-1]
72 l = lines[-1]
73 if re.match('\s*$', l):
73 if re.match('\s*$', l):
74 del lines[-1]
74 del lines[-1]
75 else:
75 else:
76 break
76 break
77
77
78 message = []
78 message = []
79 comments = []
79 comments = []
80 user = None
80 user = None
81 date = None
81 date = None
82 format = None
82 format = None
83 subject = None
83 subject = None
84 diffstart = 0
84 diffstart = 0
85
85
86 for line in file(pf):
86 for line in file(pf):
87 line = line.rstrip()
87 line = line.rstrip()
88 if line.startswith('diff --git'):
88 if line.startswith('diff --git'):
89 diffstart = 2
89 diffstart = 2
90 break
90 break
91 if diffstart:
91 if diffstart:
92 if line.startswith('+++ '):
92 if line.startswith('+++ '):
93 diffstart = 2
93 diffstart = 2
94 break
94 break
95 if line.startswith("--- "):
95 if line.startswith("--- "):
96 diffstart = 1
96 diffstart = 1
97 continue
97 continue
98 elif format == "hgpatch":
98 elif format == "hgpatch":
99 # parse values when importing the result of an hg export
99 # parse values when importing the result of an hg export
100 if line.startswith("# User "):
100 if line.startswith("# User "):
101 user = line[7:]
101 user = line[7:]
102 elif line.startswith("# Date "):
102 elif line.startswith("# Date "):
103 date = line[7:]
103 date = line[7:]
104 elif not line.startswith("# ") and line:
104 elif not line.startswith("# ") and line:
105 message.append(line)
105 message.append(line)
106 format = None
106 format = None
107 elif line == '# HG changeset patch':
107 elif line == '# HG changeset patch':
108 format = "hgpatch"
108 format = "hgpatch"
109 elif (format != "tagdone" and (line.startswith("Subject: ") or
109 elif (format != "tagdone" and (line.startswith("Subject: ") or
110 line.startswith("subject: "))):
110 line.startswith("subject: "))):
111 subject = line[9:]
111 subject = line[9:]
112 format = "tag"
112 format = "tag"
113 elif (format != "tagdone" and (line.startswith("From: ") or
113 elif (format != "tagdone" and (line.startswith("From: ") or
114 line.startswith("from: "))):
114 line.startswith("from: "))):
115 user = line[6:]
115 user = line[6:]
116 format = "tag"
116 format = "tag"
117 elif format == "tag" and line == "":
117 elif format == "tag" and line == "":
118 # when looking for tags (subject: from: etc) they
118 # when looking for tags (subject: from: etc) they
119 # end once you find a blank line in the source
119 # end once you find a blank line in the source
120 format = "tagdone"
120 format = "tagdone"
121 elif message or line:
121 elif message or line:
122 message.append(line)
122 message.append(line)
123 comments.append(line)
123 comments.append(line)
124
124
125 eatdiff(message)
125 eatdiff(message)
126 eatdiff(comments)
126 eatdiff(comments)
127 eatempty(message)
127 eatempty(message)
128 eatempty(comments)
128 eatempty(comments)
129
129
130 # make sure message isn't empty
130 # make sure message isn't empty
131 if format and format.startswith("tag") and subject:
131 if format and format.startswith("tag") and subject:
132 message.insert(0, "")
132 message.insert(0, "")
133 message.insert(0, subject)
133 message.insert(0, subject)
134
134
135 self.message = message
135 self.message = message
136 self.comments = comments
136 self.comments = comments
137 self.user = user
137 self.user = user
138 self.date = date
138 self.date = date
139 self.haspatch = diffstart > 1
139 self.haspatch = diffstart > 1
140
140
141 def setuser(self, user):
141 def setuser(self, user):
142 if not self.updateheader(['From: ', '# User '], user):
142 if not self.updateheader(['From: ', '# User '], user):
143 try:
143 try:
144 patchheaderat = self.comments.index('# HG changeset patch')
144 patchheaderat = self.comments.index('# HG changeset patch')
145 self.comments.insert(patchheaderat + 1,'# User ' + user)
145 self.comments.insert(patchheaderat + 1,'# User ' + user)
146 except ValueError:
146 except ValueError:
147 self.comments = ['From: ' + user, ''] + self.comments
147 self.comments = ['From: ' + user, ''] + self.comments
148 self.user = user
148 self.user = user
149
149
150 def setdate(self, date):
150 def setdate(self, date):
151 if self.updateheader(['# Date '], date):
151 if self.updateheader(['# Date '], date):
152 self.date = date
152 self.date = date
153
153
154 def setmessage(self, message):
154 def setmessage(self, message):
155 if self.comments:
155 if self.comments:
156 self._delmsg()
156 self._delmsg()
157 self.message = [message]
157 self.message = [message]
158 self.comments += self.message
158 self.comments += self.message
159
159
160 def updateheader(self, prefixes, new):
160 def updateheader(self, prefixes, new):
161 '''Update all references to a field in the patch header.
161 '''Update all references to a field in the patch header.
162 Return whether the field is present.'''
162 Return whether the field is present.'''
163 res = False
163 res = False
164 for prefix in prefixes:
164 for prefix in prefixes:
165 for i in xrange(len(self.comments)):
165 for i in xrange(len(self.comments)):
166 if self.comments[i].startswith(prefix):
166 if self.comments[i].startswith(prefix):
167 self.comments[i] = prefix + new
167 self.comments[i] = prefix + new
168 res = True
168 res = True
169 break
169 break
170 return res
170 return res
171
171
172 def __str__(self):
172 def __str__(self):
173 if not self.comments:
173 if not self.comments:
174 return ''
174 return ''
175 return '\n'.join(self.comments) + '\n\n'
175 return '\n'.join(self.comments) + '\n\n'
176
176
177 def _delmsg(self):
177 def _delmsg(self):
178 '''Remove existing message, keeping the rest of the comments fields.
178 '''Remove existing message, keeping the rest of the comments fields.
179 If comments contains 'subject: ', message will prepend
179 If comments contains 'subject: ', message will prepend
180 the field and a blank line.'''
180 the field and a blank line.'''
181 if self.message:
181 if self.message:
182 subj = 'subject: ' + self.message[0].lower()
182 subj = 'subject: ' + self.message[0].lower()
183 for i in xrange(len(self.comments)):
183 for i in xrange(len(self.comments)):
184 if subj == self.comments[i].lower():
184 if subj == self.comments[i].lower():
185 del self.comments[i]
185 del self.comments[i]
186 self.message = self.message[2:]
186 self.message = self.message[2:]
187 break
187 break
188 ci = 0
188 ci = 0
189 for mi in self.message:
189 for mi in self.message:
190 while mi != self.comments[ci]:
190 while mi != self.comments[ci]:
191 ci += 1
191 ci += 1
192 del self.comments[ci]
192 del self.comments[ci]
193
193
194 class queue(object):
194 class queue(object):
195 def __init__(self, ui, path, patchdir=None):
195 def __init__(self, ui, path, patchdir=None):
196 self.basepath = path
196 self.basepath = path
197 self.path = patchdir or os.path.join(path, "patches")
197 self.path = patchdir or os.path.join(path, "patches")
198 self.opener = util.opener(self.path)
198 self.opener = util.opener(self.path)
199 self.ui = ui
199 self.ui = ui
200 self.applied_dirty = 0
200 self.applied_dirty = 0
201 self.series_dirty = 0
201 self.series_dirty = 0
202 self.series_path = "series"
202 self.series_path = "series"
203 self.status_path = "status"
203 self.status_path = "status"
204 self.guards_path = "guards"
204 self.guards_path = "guards"
205 self.active_guards = None
205 self.active_guards = None
206 self.guards_dirty = False
206 self.guards_dirty = False
207 self._diffopts = None
207 self._diffopts = None
208
208
209 @util.propertycache
209 @util.propertycache
210 def applied(self):
210 def applied(self):
211 if os.path.exists(self.join(self.status_path)):
211 if os.path.exists(self.join(self.status_path)):
212 lines = self.opener(self.status_path).read().splitlines()
212 lines = self.opener(self.status_path).read().splitlines()
213 return [statusentry(l) for l in lines]
213 return [statusentry(l) for l in lines]
214 return []
214 return []
215
215
216 @util.propertycache
216 @util.propertycache
217 def full_series(self):
217 def full_series(self):
218 if os.path.exists(self.join(self.series_path)):
218 if os.path.exists(self.join(self.series_path)):
219 return self.opener(self.series_path).read().splitlines()
219 return self.opener(self.series_path).read().splitlines()
220 return []
220 return []
221
221
222 @util.propertycache
222 @util.propertycache
223 def series(self):
223 def series(self):
224 self.parse_series()
224 self.parse_series()
225 return self.series
225 return self.series
226
226
227 @util.propertycache
227 @util.propertycache
228 def series_guards(self):
228 def series_guards(self):
229 self.parse_series()
229 self.parse_series()
230 return self.series_guards
230 return self.series_guards
231
231
232 def invalidate(self):
232 def invalidate(self):
233 for a in 'applied full_series series series_guards'.split():
233 for a in 'applied full_series series series_guards'.split():
234 if a in self.__dict__:
234 if a in self.__dict__:
235 delattr(self, a)
235 delattr(self, a)
236 self.applied_dirty = 0
236 self.applied_dirty = 0
237 self.series_dirty = 0
237 self.series_dirty = 0
238 self.guards_dirty = False
238 self.guards_dirty = False
239 self.active_guards = None
239 self.active_guards = None
240
240
241 def diffopts(self):
241 def diffopts(self):
242 if self._diffopts is None:
242 if self._diffopts is None:
243 self._diffopts = patch.diffopts(self.ui)
243 self._diffopts = patch.diffopts(self.ui)
244 return self._diffopts
244 return self._diffopts
245
245
246 def join(self, *p):
246 def join(self, *p):
247 return os.path.join(self.path, *p)
247 return os.path.join(self.path, *p)
248
248
249 def find_series(self, patch):
249 def find_series(self, patch):
250 pre = re.compile("(\s*)([^#]+)")
250 pre = re.compile("(\s*)([^#]+)")
251 index = 0
251 index = 0
252 for l in self.full_series:
252 for l in self.full_series:
253 m = pre.match(l)
253 m = pre.match(l)
254 if m:
254 if m:
255 s = m.group(2)
255 s = m.group(2)
256 s = s.rstrip()
256 s = s.rstrip()
257 if s == patch:
257 if s == patch:
258 return index
258 return index
259 index += 1
259 index += 1
260 return None
260 return None
261
261
262 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
262 guard_re = re.compile(r'\s?#([-+][^-+# \t\r\n\f][^# \t\r\n\f]*)')
263
263
264 def parse_series(self):
264 def parse_series(self):
265 self.series = []
265 self.series = []
266 self.series_guards = []
266 self.series_guards = []
267 for l in self.full_series:
267 for l in self.full_series:
268 h = l.find('#')
268 h = l.find('#')
269 if h == -1:
269 if h == -1:
270 patch = l
270 patch = l
271 comment = ''
271 comment = ''
272 elif h == 0:
272 elif h == 0:
273 continue
273 continue
274 else:
274 else:
275 patch = l[:h]
275 patch = l[:h]
276 comment = l[h:]
276 comment = l[h:]
277 patch = patch.strip()
277 patch = patch.strip()
278 if patch:
278 if patch:
279 if patch in self.series:
279 if patch in self.series:
280 raise util.Abort(_('%s appears more than once in %s') %
280 raise util.Abort(_('%s appears more than once in %s') %
281 (patch, self.join(self.series_path)))
281 (patch, self.join(self.series_path)))
282 self.series.append(patch)
282 self.series.append(patch)
283 self.series_guards.append(self.guard_re.findall(comment))
283 self.series_guards.append(self.guard_re.findall(comment))
284
284
285 def check_guard(self, guard):
285 def check_guard(self, guard):
286 if not guard:
286 if not guard:
287 return _('guard cannot be an empty string')
287 return _('guard cannot be an empty string')
288 bad_chars = '# \t\r\n\f'
288 bad_chars = '# \t\r\n\f'
289 first = guard[0]
289 first = guard[0]
290 if first in '-+':
290 if first in '-+':
291 return (_('guard %r starts with invalid character: %r') %
291 return (_('guard %r starts with invalid character: %r') %
292 (guard, first))
292 (guard, first))
293 for c in bad_chars:
293 for c in bad_chars:
294 if c in guard:
294 if c in guard:
295 return _('invalid character in guard %r: %r') % (guard, c)
295 return _('invalid character in guard %r: %r') % (guard, c)
296
296
297 def set_active(self, guards):
297 def set_active(self, guards):
298 for guard in guards:
298 for guard in guards:
299 bad = self.check_guard(guard)
299 bad = self.check_guard(guard)
300 if bad:
300 if bad:
301 raise util.Abort(bad)
301 raise util.Abort(bad)
302 guards = sorted(set(guards))
302 guards = sorted(set(guards))
303 self.ui.debug(_('active guards: %s\n') % ' '.join(guards))
303 self.ui.debug(_('active guards: %s\n') % ' '.join(guards))
304 self.active_guards = guards
304 self.active_guards = guards
305 self.guards_dirty = True
305 self.guards_dirty = True
306
306
307 def active(self):
307 def active(self):
308 if self.active_guards is None:
308 if self.active_guards is None:
309 self.active_guards = []
309 self.active_guards = []
310 try:
310 try:
311 guards = self.opener(self.guards_path).read().split()
311 guards = self.opener(self.guards_path).read().split()
312 except IOError, err:
312 except IOError, err:
313 if err.errno != errno.ENOENT: raise
313 if err.errno != errno.ENOENT: raise
314 guards = []
314 guards = []
315 for i, guard in enumerate(guards):
315 for i, guard in enumerate(guards):
316 bad = self.check_guard(guard)
316 bad = self.check_guard(guard)
317 if bad:
317 if bad:
318 self.ui.warn('%s:%d: %s\n' %
318 self.ui.warn('%s:%d: %s\n' %
319 (self.join(self.guards_path), i + 1, bad))
319 (self.join(self.guards_path), i + 1, bad))
320 else:
320 else:
321 self.active_guards.append(guard)
321 self.active_guards.append(guard)
322 return self.active_guards
322 return self.active_guards
323
323
324 def set_guards(self, idx, guards):
324 def set_guards(self, idx, guards):
325 for g in guards:
325 for g in guards:
326 if len(g) < 2:
326 if len(g) < 2:
327 raise util.Abort(_('guard %r too short') % g)
327 raise util.Abort(_('guard %r too short') % g)
328 if g[0] not in '-+':
328 if g[0] not in '-+':
329 raise util.Abort(_('guard %r starts with invalid char') % g)
329 raise util.Abort(_('guard %r starts with invalid char') % g)
330 bad = self.check_guard(g[1:])
330 bad = self.check_guard(g[1:])
331 if bad:
331 if bad:
332 raise util.Abort(bad)
332 raise util.Abort(bad)
333 drop = self.guard_re.sub('', self.full_series[idx])
333 drop = self.guard_re.sub('', self.full_series[idx])
334 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
334 self.full_series[idx] = drop + ''.join([' #' + g for g in guards])
335 self.parse_series()
335 self.parse_series()
336 self.series_dirty = True
336 self.series_dirty = True
337
337
338 def pushable(self, idx):
338 def pushable(self, idx):
339 if isinstance(idx, str):
339 if isinstance(idx, str):
340 idx = self.series.index(idx)
340 idx = self.series.index(idx)
341 patchguards = self.series_guards[idx]
341 patchguards = self.series_guards[idx]
342 if not patchguards:
342 if not patchguards:
343 return True, None
343 return True, None
344 guards = self.active()
344 guards = self.active()
345 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
345 exactneg = [g for g in patchguards if g[0] == '-' and g[1:] in guards]
346 if exactneg:
346 if exactneg:
347 return False, exactneg[0]
347 return False, exactneg[0]
348 pos = [g for g in patchguards if g[0] == '+']
348 pos = [g for g in patchguards if g[0] == '+']
349 exactpos = [g for g in pos if g[1:] in guards]
349 exactpos = [g for g in pos if g[1:] in guards]
350 if pos:
350 if pos:
351 if exactpos:
351 if exactpos:
352 return True, exactpos[0]
352 return True, exactpos[0]
353 return False, pos
353 return False, pos
354 return True, ''
354 return True, ''
355
355
356 def explain_pushable(self, idx, all_patches=False):
356 def explain_pushable(self, idx, all_patches=False):
357 write = all_patches and self.ui.write or self.ui.warn
357 write = all_patches and self.ui.write or self.ui.warn
358 if all_patches or self.ui.verbose:
358 if all_patches or self.ui.verbose:
359 if isinstance(idx, str):
359 if isinstance(idx, str):
360 idx = self.series.index(idx)
360 idx = self.series.index(idx)
361 pushable, why = self.pushable(idx)
361 pushable, why = self.pushable(idx)
362 if all_patches and pushable:
362 if all_patches and pushable:
363 if why is None:
363 if why is None:
364 write(_('allowing %s - no guards in effect\n') %
364 write(_('allowing %s - no guards in effect\n') %
365 self.series[idx])
365 self.series[idx])
366 else:
366 else:
367 if not why:
367 if not why:
368 write(_('allowing %s - no matching negative guards\n') %
368 write(_('allowing %s - no matching negative guards\n') %
369 self.series[idx])
369 self.series[idx])
370 else:
370 else:
371 write(_('allowing %s - guarded by %r\n') %
371 write(_('allowing %s - guarded by %r\n') %
372 (self.series[idx], why))
372 (self.series[idx], why))
373 if not pushable:
373 if not pushable:
374 if why:
374 if why:
375 write(_('skipping %s - guarded by %r\n') %
375 write(_('skipping %s - guarded by %r\n') %
376 (self.series[idx], why))
376 (self.series[idx], why))
377 else:
377 else:
378 write(_('skipping %s - no matching guards\n') %
378 write(_('skipping %s - no matching guards\n') %
379 self.series[idx])
379 self.series[idx])
380
380
381 def save_dirty(self):
381 def save_dirty(self):
382 def write_list(items, path):
382 def write_list(items, path):
383 fp = self.opener(path, 'w')
383 fp = self.opener(path, 'w')
384 for i in items:
384 for i in items:
385 fp.write("%s\n" % i)
385 fp.write("%s\n" % i)
386 fp.close()
386 fp.close()
387 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
387 if self.applied_dirty: write_list(map(str, self.applied), self.status_path)
388 if self.series_dirty: write_list(self.full_series, self.series_path)
388 if self.series_dirty: write_list(self.full_series, self.series_path)
389 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
389 if self.guards_dirty: write_list(self.active_guards, self.guards_path)
390
390
391 def removeundo(self, repo):
391 def removeundo(self, repo):
392 undo = repo.sjoin('undo')
392 undo = repo.sjoin('undo')
393 if not os.path.exists(undo):
393 if not os.path.exists(undo):
394 return
394 return
395 try:
395 try:
396 os.unlink(undo)
396 os.unlink(undo)
397 except OSError, inst:
397 except OSError, inst:
398 self.ui.warn(_('error removing undo: %s\n') % str(inst))
398 self.ui.warn(_('error removing undo: %s\n') % str(inst))
399
399
400 def printdiff(self, repo, node1, node2=None, files=None,
400 def printdiff(self, repo, node1, node2=None, files=None,
401 fp=None, changes=None, opts={}):
401 fp=None, changes=None, opts={}):
402 m = cmdutil.match(repo, files, opts)
402 m = cmdutil.match(repo, files, opts)
403 chunks = patch.diff(repo, node1, node2, m, changes, self.diffopts())
403 chunks = patch.diff(repo, node1, node2, m, changes, self.diffopts())
404 write = fp is None and repo.ui.write or fp.write
404 write = fp is None and repo.ui.write or fp.write
405 for chunk in chunks:
405 for chunk in chunks:
406 write(chunk)
406 write(chunk)
407
407
408 def mergeone(self, repo, mergeq, head, patch, rev):
408 def mergeone(self, repo, mergeq, head, patch, rev):
409 # first try just applying the patch
409 # first try just applying the patch
410 (err, n) = self.apply(repo, [ patch ], update_status=False,
410 (err, n) = self.apply(repo, [ patch ], update_status=False,
411 strict=True, merge=rev)
411 strict=True, merge=rev)
412
412
413 if err == 0:
413 if err == 0:
414 return (err, n)
414 return (err, n)
415
415
416 if n is None:
416 if n is None:
417 raise util.Abort(_("apply failed for patch %s") % patch)
417 raise util.Abort(_("apply failed for patch %s") % patch)
418
418
419 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
419 self.ui.warn(_("patch didn't work out, merging %s\n") % patch)
420
420
421 # apply failed, strip away that rev and merge.
421 # apply failed, strip away that rev and merge.
422 hg.clean(repo, head)
422 hg.clean(repo, head)
423 self.strip(repo, n, update=False, backup='strip')
423 self.strip(repo, n, update=False, backup='strip')
424
424
425 ctx = repo[rev]
425 ctx = repo[rev]
426 ret = hg.merge(repo, rev)
426 ret = hg.merge(repo, rev)
427 if ret:
427 if ret:
428 raise util.Abort(_("update returned %d") % ret)
428 raise util.Abort(_("update returned %d") % ret)
429 n = repo.commit(ctx.description(), ctx.user(), force=True)
429 n = repo.commit(ctx.description(), ctx.user(), force=True)
430 if n is None:
430 if n is None:
431 raise util.Abort(_("repo commit failed"))
431 raise util.Abort(_("repo commit failed"))
432 try:
432 try:
433 ph = patchheader(mergeq.join(patch))
433 ph = patchheader(mergeq.join(patch))
434 except:
434 except:
435 raise util.Abort(_("unable to read %s") % patch)
435 raise util.Abort(_("unable to read %s") % patch)
436
436
437 patchf = self.opener(patch, "w")
437 patchf = self.opener(patch, "w")
438 comments = str(ph)
438 comments = str(ph)
439 if comments:
439 if comments:
440 patchf.write(comments)
440 patchf.write(comments)
441 self.printdiff(repo, head, n, fp=patchf)
441 self.printdiff(repo, head, n, fp=patchf)
442 patchf.close()
442 patchf.close()
443 self.removeundo(repo)
443 self.removeundo(repo)
444 return (0, n)
444 return (0, n)
445
445
446 def qparents(self, repo, rev=None):
446 def qparents(self, repo, rev=None):
447 if rev is None:
447 if rev is None:
448 (p1, p2) = repo.dirstate.parents()
448 (p1, p2) = repo.dirstate.parents()
449 if p2 == nullid:
449 if p2 == nullid:
450 return p1
450 return p1
451 if len(self.applied) == 0:
451 if len(self.applied) == 0:
452 return None
452 return None
453 return bin(self.applied[-1].rev)
453 return bin(self.applied[-1].rev)
454 pp = repo.changelog.parents(rev)
454 pp = repo.changelog.parents(rev)
455 if pp[1] != nullid:
455 if pp[1] != nullid:
456 arevs = [ x.rev for x in self.applied ]
456 arevs = [ x.rev for x in self.applied ]
457 p0 = hex(pp[0])
457 p0 = hex(pp[0])
458 p1 = hex(pp[1])
458 p1 = hex(pp[1])
459 if p0 in arevs:
459 if p0 in arevs:
460 return pp[0]
460 return pp[0]
461 if p1 in arevs:
461 if p1 in arevs:
462 return pp[1]
462 return pp[1]
463 return pp[0]
463 return pp[0]
464
464
465 def mergepatch(self, repo, mergeq, series):
465 def mergepatch(self, repo, mergeq, series):
466 if len(self.applied) == 0:
466 if len(self.applied) == 0:
467 # each of the patches merged in will have two parents. This
467 # each of the patches merged in will have two parents. This
468 # can confuse the qrefresh, qdiff, and strip code because it
468 # can confuse the qrefresh, qdiff, and strip code because it
469 # needs to know which parent is actually in the patch queue.
469 # needs to know which parent is actually in the patch queue.
470 # so, we insert a merge marker with only one parent. This way
470 # so, we insert a merge marker with only one parent. This way
471 # the first patch in the queue is never a merge patch
471 # the first patch in the queue is never a merge patch
472 #
472 #
473 pname = ".hg.patches.merge.marker"
473 pname = ".hg.patches.merge.marker"
474 n = repo.commit('[mq]: merge marker', force=True)
474 n = repo.commit('[mq]: merge marker', force=True)
475 self.removeundo(repo)
475 self.removeundo(repo)
476 self.applied.append(statusentry(hex(n), pname))
476 self.applied.append(statusentry(hex(n), pname))
477 self.applied_dirty = 1
477 self.applied_dirty = 1
478
478
479 head = self.qparents(repo)
479 head = self.qparents(repo)
480
480
481 for patch in series:
481 for patch in series:
482 patch = mergeq.lookup(patch, strict=True)
482 patch = mergeq.lookup(patch, strict=True)
483 if not patch:
483 if not patch:
484 self.ui.warn(_("patch %s does not exist\n") % patch)
484 self.ui.warn(_("patch %s does not exist\n") % patch)
485 return (1, None)
485 return (1, None)
486 pushable, reason = self.pushable(patch)
486 pushable, reason = self.pushable(patch)
487 if not pushable:
487 if not pushable:
488 self.explain_pushable(patch, all_patches=True)
488 self.explain_pushable(patch, all_patches=True)
489 continue
489 continue
490 info = mergeq.isapplied(patch)
490 info = mergeq.isapplied(patch)
491 if not info:
491 if not info:
492 self.ui.warn(_("patch %s is not applied\n") % patch)
492 self.ui.warn(_("patch %s is not applied\n") % patch)
493 return (1, None)
493 return (1, None)
494 rev = bin(info[1])
494 rev = bin(info[1])
495 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
495 (err, head) = self.mergeone(repo, mergeq, head, patch, rev)
496 if head:
496 if head:
497 self.applied.append(statusentry(hex(head), patch))
497 self.applied.append(statusentry(hex(head), patch))
498 self.applied_dirty = 1
498 self.applied_dirty = 1
499 if err:
499 if err:
500 return (err, head)
500 return (err, head)
501 self.save_dirty()
501 self.save_dirty()
502 return (0, head)
502 return (0, head)
503
503
504 def patch(self, repo, patchfile):
504 def patch(self, repo, patchfile):
505 '''Apply patchfile to the working directory.
505 '''Apply patchfile to the working directory.
506 patchfile: name of patch file'''
506 patchfile: name of patch file'''
507 files = {}
507 files = {}
508 try:
508 try:
509 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
509 fuzz = patch.patch(patchfile, self.ui, strip=1, cwd=repo.root,
510 files=files, eolmode=None)
510 files=files, eolmode=None)
511 except Exception, inst:
511 except Exception, inst:
512 self.ui.note(str(inst) + '\n')
512 self.ui.note(str(inst) + '\n')
513 if not self.ui.verbose:
513 if not self.ui.verbose:
514 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
514 self.ui.warn(_("patch failed, unable to continue (try -v)\n"))
515 return (False, files, False)
515 return (False, files, False)
516
516
517 return (True, files, fuzz)
517 return (True, files, fuzz)
518
518
519 def apply(self, repo, series, list=False, update_status=True,
519 def apply(self, repo, series, list=False, update_status=True,
520 strict=False, patchdir=None, merge=None, all_files={}):
520 strict=False, patchdir=None, merge=None, all_files={}):
521 wlock = lock = tr = None
521 wlock = lock = tr = None
522 try:
522 try:
523 wlock = repo.wlock()
523 wlock = repo.wlock()
524 lock = repo.lock()
524 lock = repo.lock()
525 tr = repo.transaction()
525 tr = repo.transaction()
526 try:
526 try:
527 ret = self._apply(repo, series, list, update_status,
527 ret = self._apply(repo, series, list, update_status,
528 strict, patchdir, merge, all_files=all_files)
528 strict, patchdir, merge, all_files=all_files)
529 tr.close()
529 tr.close()
530 self.save_dirty()
530 self.save_dirty()
531 return ret
531 return ret
532 except:
532 except:
533 try:
533 try:
534 tr.abort()
534 tr.abort()
535 finally:
535 finally:
536 repo.invalidate()
536 repo.invalidate()
537 repo.dirstate.invalidate()
537 repo.dirstate.invalidate()
538 raise
538 raise
539 finally:
539 finally:
540 del tr
540 del tr
541 release(lock, wlock)
541 release(lock, wlock)
542 self.removeundo(repo)
542 self.removeundo(repo)
543
543
544 def _apply(self, repo, series, list=False, update_status=True,
544 def _apply(self, repo, series, list=False, update_status=True,
545 strict=False, patchdir=None, merge=None, all_files={}):
545 strict=False, patchdir=None, merge=None, all_files={}):
546 '''returns (error, hash)
546 '''returns (error, hash)
547 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
547 error = 1 for unable to read, 2 for patch failed, 3 for patch fuzz'''
548 # TODO unify with commands.py
548 # TODO unify with commands.py
549 if not patchdir:
549 if not patchdir:
550 patchdir = self.path
550 patchdir = self.path
551 err = 0
551 err = 0
552 n = None
552 n = None
553 for patchname in series:
553 for patchname in series:
554 pushable, reason = self.pushable(patchname)
554 pushable, reason = self.pushable(patchname)
555 if not pushable:
555 if not pushable:
556 self.explain_pushable(patchname, all_patches=True)
556 self.explain_pushable(patchname, all_patches=True)
557 continue
557 continue
558 self.ui.warn(_("applying %s\n") % patchname)
558 self.ui.warn(_("applying %s\n") % patchname)
559 pf = os.path.join(patchdir, patchname)
559 pf = os.path.join(patchdir, patchname)
560
560
561 try:
561 try:
562 ph = patchheader(self.join(patchname))
562 ph = patchheader(self.join(patchname))
563 except:
563 except:
564 self.ui.warn(_("unable to read %s\n") % patchname)
564 self.ui.warn(_("unable to read %s\n") % patchname)
565 err = 1
565 err = 1
566 break
566 break
567
567
568 message = ph.message
568 message = ph.message
569 if not message:
569 if not message:
570 message = _("imported patch %s\n") % patchname
570 message = _("imported patch %s\n") % patchname
571 else:
571 else:
572 if list:
572 if list:
573 message.append(_("\nimported patch %s") % patchname)
573 message.append(_("\nimported patch %s") % patchname)
574 message = '\n'.join(message)
574 message = '\n'.join(message)
575
575
576 if ph.haspatch:
576 if ph.haspatch:
577 (patcherr, files, fuzz) = self.patch(repo, pf)
577 (patcherr, files, fuzz) = self.patch(repo, pf)
578 all_files.update(files)
578 all_files.update(files)
579 patcherr = not patcherr
579 patcherr = not patcherr
580 else:
580 else:
581 self.ui.warn(_("patch %s is empty\n") % patchname)
581 self.ui.warn(_("patch %s is empty\n") % patchname)
582 patcherr, files, fuzz = 0, [], 0
582 patcherr, files, fuzz = 0, [], 0
583
583
584 if merge and files:
584 if merge and files:
585 # Mark as removed/merged and update dirstate parent info
585 # Mark as removed/merged and update dirstate parent info
586 removed = []
586 removed = []
587 merged = []
587 merged = []
588 for f in files:
588 for f in files:
589 if os.path.exists(repo.wjoin(f)):
589 if os.path.exists(repo.wjoin(f)):
590 merged.append(f)
590 merged.append(f)
591 else:
591 else:
592 removed.append(f)
592 removed.append(f)
593 for f in removed:
593 for f in removed:
594 repo.dirstate.remove(f)
594 repo.dirstate.remove(f)
595 for f in merged:
595 for f in merged:
596 repo.dirstate.merge(f)
596 repo.dirstate.merge(f)
597 p1, p2 = repo.dirstate.parents()
597 p1, p2 = repo.dirstate.parents()
598 repo.dirstate.setparents(p1, merge)
598 repo.dirstate.setparents(p1, merge)
599
599
600 files = patch.updatedir(self.ui, repo, files)
600 files = patch.updatedir(self.ui, repo, files)
601 match = cmdutil.matchfiles(repo, files or [])
601 match = cmdutil.matchfiles(repo, files or [])
602 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
602 n = repo.commit(message, ph.user, ph.date, match=match, force=True)
603
603
604 if n is None:
604 if n is None:
605 raise util.Abort(_("repo commit failed"))
605 raise util.Abort(_("repo commit failed"))
606
606
607 if update_status:
607 if update_status:
608 self.applied.append(statusentry(hex(n), patchname))
608 self.applied.append(statusentry(hex(n), patchname))
609
609
610 if patcherr:
610 if patcherr:
611 self.ui.warn(_("patch failed, rejects left in working dir\n"))
611 self.ui.warn(_("patch failed, rejects left in working dir\n"))
612 err = 2
612 err = 2
613 break
613 break
614
614
615 if fuzz and strict:
615 if fuzz and strict:
616 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
616 self.ui.warn(_("fuzz found when applying patch, stopping\n"))
617 err = 3
617 err = 3
618 break
618 break
619 return (err, n)
619 return (err, n)
620
620
621 def _cleanup(self, patches, numrevs, keep=False):
621 def _cleanup(self, patches, numrevs, keep=False):
622 if not keep:
622 if not keep:
623 r = self.qrepo()
623 r = self.qrepo()
624 if r:
624 if r:
625 r.remove(patches, True)
625 r.remove(patches, True)
626 else:
626 else:
627 for p in patches:
627 for p in patches:
628 os.unlink(self.join(p))
628 os.unlink(self.join(p))
629
629
630 if numrevs:
630 if numrevs:
631 del self.applied[:numrevs]
631 del self.applied[:numrevs]
632 self.applied_dirty = 1
632 self.applied_dirty = 1
633
633
634 for i in sorted([self.find_series(p) for p in patches], reverse=True):
634 for i in sorted([self.find_series(p) for p in patches], reverse=True):
635 del self.full_series[i]
635 del self.full_series[i]
636 self.parse_series()
636 self.parse_series()
637 self.series_dirty = 1
637 self.series_dirty = 1
638
638
639 def _revpatches(self, repo, revs):
639 def _revpatches(self, repo, revs):
640 firstrev = repo[self.applied[0].rev].rev()
640 firstrev = repo[self.applied[0].rev].rev()
641 patches = []
641 patches = []
642 for i, rev in enumerate(revs):
642 for i, rev in enumerate(revs):
643
643
644 if rev < firstrev:
644 if rev < firstrev:
645 raise util.Abort(_('revision %d is not managed') % rev)
645 raise util.Abort(_('revision %d is not managed') % rev)
646
646
647 ctx = repo[rev]
647 ctx = repo[rev]
648 base = bin(self.applied[i].rev)
648 base = bin(self.applied[i].rev)
649 if ctx.node() != base:
649 if ctx.node() != base:
650 msg = _('cannot delete revision %d above applied patches')
650 msg = _('cannot delete revision %d above applied patches')
651 raise util.Abort(msg % rev)
651 raise util.Abort(msg % rev)
652
652
653 patch = self.applied[i].name
653 patch = self.applied[i].name
654 for fmt in ('[mq]: %s', 'imported patch %s'):
654 for fmt in ('[mq]: %s', 'imported patch %s'):
655 if ctx.description() == fmt % patch:
655 if ctx.description() == fmt % patch:
656 msg = _('patch %s finalized without changeset message\n')
656 msg = _('patch %s finalized without changeset message\n')
657 repo.ui.status(msg % patch)
657 repo.ui.status(msg % patch)
658 break
658 break
659
659
660 patches.append(patch)
660 patches.append(patch)
661 return patches
661 return patches
662
662
663 def finish(self, repo, revs):
663 def finish(self, repo, revs):
664 patches = self._revpatches(repo, sorted(revs))
664 patches = self._revpatches(repo, sorted(revs))
665 self._cleanup(patches, len(patches))
665 self._cleanup(patches, len(patches))
666
666
667 def delete(self, repo, patches, opts):
667 def delete(self, repo, patches, opts):
668 if not patches and not opts.get('rev'):
668 if not patches and not opts.get('rev'):
669 raise util.Abort(_('qdelete requires at least one revision or '
669 raise util.Abort(_('qdelete requires at least one revision or '
670 'patch name'))
670 'patch name'))
671
671
672 realpatches = []
672 realpatches = []
673 for patch in patches:
673 for patch in patches:
674 patch = self.lookup(patch, strict=True)
674 patch = self.lookup(patch, strict=True)
675 info = self.isapplied(patch)
675 info = self.isapplied(patch)
676 if info:
676 if info:
677 raise util.Abort(_("cannot delete applied patch %s") % patch)
677 raise util.Abort(_("cannot delete applied patch %s") % patch)
678 if patch not in self.series:
678 if patch not in self.series:
679 raise util.Abort(_("patch %s not in series file") % patch)
679 raise util.Abort(_("patch %s not in series file") % patch)
680 realpatches.append(patch)
680 realpatches.append(patch)
681
681
682 numrevs = 0
682 numrevs = 0
683 if opts.get('rev'):
683 if opts.get('rev'):
684 if not self.applied:
684 if not self.applied:
685 raise util.Abort(_('no patches applied'))
685 raise util.Abort(_('no patches applied'))
686 revs = cmdutil.revrange(repo, opts['rev'])
686 revs = cmdutil.revrange(repo, opts['rev'])
687 if len(revs) > 1 and revs[0] > revs[1]:
687 if len(revs) > 1 and revs[0] > revs[1]:
688 revs.reverse()
688 revs.reverse()
689 revpatches = self._revpatches(repo, revs)
689 revpatches = self._revpatches(repo, revs)
690 realpatches += revpatches
690 realpatches += revpatches
691 numrevs = len(revpatches)
691 numrevs = len(revpatches)
692
692
693 self._cleanup(realpatches, numrevs, opts.get('keep'))
693 self._cleanup(realpatches, numrevs, opts.get('keep'))
694
694
695 def check_toppatch(self, repo):
695 def check_toppatch(self, repo):
696 if len(self.applied) > 0:
696 if len(self.applied) > 0:
697 top = bin(self.applied[-1].rev)
697 top = bin(self.applied[-1].rev)
698 pp = repo.dirstate.parents()
698 pp = repo.dirstate.parents()
699 if top not in pp:
699 if top not in pp:
700 raise util.Abort(_("working directory revision is not qtip"))
700 raise util.Abort(_("working directory revision is not qtip"))
701 return top
701 return top
702 return None
702 return None
703 def check_localchanges(self, repo, force=False, refresh=True):
703 def check_localchanges(self, repo, force=False, refresh=True):
704 m, a, r, d = repo.status()[:4]
704 m, a, r, d = repo.status()[:4]
705 if m or a or r or d:
705 if m or a or r or d:
706 if not force:
706 if not force:
707 if refresh:
707 if refresh:
708 raise util.Abort(_("local changes found, refresh first"))
708 raise util.Abort(_("local changes found, refresh first"))
709 else:
709 else:
710 raise util.Abort(_("local changes found"))
710 raise util.Abort(_("local changes found"))
711 return m, a, r, d
711 return m, a, r, d
712
712
713 _reserved = ('series', 'status', 'guards')
713 _reserved = ('series', 'status', 'guards')
714 def check_reserved_name(self, name):
714 def check_reserved_name(self, name):
715 if (name in self._reserved or name.startswith('.hg')
715 if (name in self._reserved or name.startswith('.hg')
716 or name.startswith('.mq')):
716 or name.startswith('.mq')):
717 raise util.Abort(_('"%s" cannot be used as the name of a patch')
717 raise util.Abort(_('"%s" cannot be used as the name of a patch')
718 % name)
718 % name)
719
719
720 def new(self, repo, patchfn, *pats, **opts):
720 def new(self, repo, patchfn, *pats, **opts):
721 """options:
721 """options:
722 msg: a string or a no-argument function returning a string
722 msg: a string or a no-argument function returning a string
723 """
723 """
724 msg = opts.get('msg')
724 msg = opts.get('msg')
725 force = opts.get('force')
725 force = opts.get('force')
726 user = opts.get('user')
726 user = opts.get('user')
727 date = opts.get('date')
727 date = opts.get('date')
728 if date:
728 if date:
729 date = util.parsedate(date)
729 date = util.parsedate(date)
730 self.check_reserved_name(patchfn)
730 self.check_reserved_name(patchfn)
731 if os.path.exists(self.join(patchfn)):
731 if os.path.exists(self.join(patchfn)):
732 raise util.Abort(_('patch "%s" already exists') % patchfn)
732 raise util.Abort(_('patch "%s" already exists') % patchfn)
733 if opts.get('include') or opts.get('exclude') or pats:
733 if opts.get('include') or opts.get('exclude') or pats:
734 match = cmdutil.match(repo, pats, opts)
734 match = cmdutil.match(repo, pats, opts)
735 # detect missing files in pats
735 # detect missing files in pats
736 def badfn(f, msg):
736 def badfn(f, msg):
737 raise util.Abort('%s: %s' % (f, msg))
737 raise util.Abort('%s: %s' % (f, msg))
738 match.bad = badfn
738 match.bad = badfn
739 m, a, r, d = repo.status(match=match)[:4]
739 m, a, r, d = repo.status(match=match)[:4]
740 else:
740 else:
741 m, a, r, d = self.check_localchanges(repo, force)
741 m, a, r, d = self.check_localchanges(repo, force)
742 match = cmdutil.matchfiles(repo, m + a + r)
742 match = cmdutil.matchfiles(repo, m + a + r)
743 commitfiles = m + a + r
743 commitfiles = m + a + r
744 self.check_toppatch(repo)
744 self.check_toppatch(repo)
745 insert = self.full_series_end()
745 insert = self.full_series_end()
746 wlock = repo.wlock()
746 wlock = repo.wlock()
747 try:
747 try:
748 # if patch file write fails, abort early
748 # if patch file write fails, abort early
749 p = self.opener(patchfn, "w")
749 p = self.opener(patchfn, "w")
750 try:
750 try:
751 if date:
751 if date:
752 p.write("# HG changeset patch\n")
752 p.write("# HG changeset patch\n")
753 if user:
753 if user:
754 p.write("# User " + user + "\n")
754 p.write("# User " + user + "\n")
755 p.write("# Date %d %d\n\n" % date)
755 p.write("# Date %d %d\n\n" % date)
756 elif user:
756 elif user:
757 p.write("From: " + user + "\n\n")
757 p.write("From: " + user + "\n\n")
758
758
759 if hasattr(msg, '__call__'):
759 if hasattr(msg, '__call__'):
760 msg = msg()
760 msg = msg()
761 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
761 commitmsg = msg and msg or ("[mq]: %s" % patchfn)
762 n = repo.commit(commitmsg, user, date, match=match, force=True)
762 n = repo.commit(commitmsg, user, date, match=match, force=True)
763 if n is None:
763 if n is None:
764 raise util.Abort(_("repo commit failed"))
764 raise util.Abort(_("repo commit failed"))
765 try:
765 try:
766 self.full_series[insert:insert] = [patchfn]
766 self.full_series[insert:insert] = [patchfn]
767 self.applied.append(statusentry(hex(n), patchfn))
767 self.applied.append(statusentry(hex(n), patchfn))
768 self.parse_series()
768 self.parse_series()
769 self.series_dirty = 1
769 self.series_dirty = 1
770 self.applied_dirty = 1
770 self.applied_dirty = 1
771 if msg:
771 if msg:
772 msg = msg + "\n\n"
772 msg = msg + "\n\n"
773 p.write(msg)
773 p.write(msg)
774 if commitfiles:
774 if commitfiles:
775 diffopts = self.diffopts()
775 diffopts = self.diffopts()
776 if opts.get('git'): diffopts.git = True
776 if opts.get('git'): diffopts.git = True
777 parent = self.qparents(repo, n)
777 parent = self.qparents(repo, n)
778 chunks = patch.diff(repo, node1=parent, node2=n,
778 chunks = patch.diff(repo, node1=parent, node2=n,
779 match=match, opts=diffopts)
779 match=match, opts=diffopts)
780 for chunk in chunks:
780 for chunk in chunks:
781 p.write(chunk)
781 p.write(chunk)
782 p.close()
782 p.close()
783 wlock.release()
783 wlock.release()
784 wlock = None
784 wlock = None
785 r = self.qrepo()
785 r = self.qrepo()
786 if r: r.add([patchfn])
786 if r: r.add([patchfn])
787 except:
787 except:
788 repo.rollback()
788 repo.rollback()
789 raise
789 raise
790 except Exception:
790 except Exception:
791 patchpath = self.join(patchfn)
791 patchpath = self.join(patchfn)
792 try:
792 try:
793 os.unlink(patchpath)
793 os.unlink(patchpath)
794 except:
794 except:
795 self.ui.warn(_('error unlinking %s\n') % patchpath)
795 self.ui.warn(_('error unlinking %s\n') % patchpath)
796 raise
796 raise
797 self.removeundo(repo)
797 self.removeundo(repo)
798 finally:
798 finally:
799 release(wlock)
799 release(wlock)
800
800
801 def strip(self, repo, rev, update=True, backup="all", force=None):
801 def strip(self, repo, rev, update=True, backup="all", force=None):
802 wlock = lock = None
802 wlock = lock = None
803 try:
803 try:
804 wlock = repo.wlock()
804 wlock = repo.wlock()
805 lock = repo.lock()
805 lock = repo.lock()
806
806
807 if update:
807 if update:
808 self.check_localchanges(repo, force=force, refresh=False)
808 self.check_localchanges(repo, force=force, refresh=False)
809 urev = self.qparents(repo, rev)
809 urev = self.qparents(repo, rev)
810 hg.clean(repo, urev)
810 hg.clean(repo, urev)
811 repo.dirstate.write()
811 repo.dirstate.write()
812
812
813 self.removeundo(repo)
813 self.removeundo(repo)
814 repair.strip(self.ui, repo, rev, backup)
814 repair.strip(self.ui, repo, rev, backup)
815 # strip may have unbundled a set of backed up revisions after
815 # strip may have unbundled a set of backed up revisions after
816 # the actual strip
816 # the actual strip
817 self.removeundo(repo)
817 self.removeundo(repo)
818 finally:
818 finally:
819 release(lock, wlock)
819 release(lock, wlock)
820
820
821 def isapplied(self, patch):
821 def isapplied(self, patch):
822 """returns (index, rev, patch)"""
822 """returns (index, rev, patch)"""
823 for i, a in enumerate(self.applied):
823 for i, a in enumerate(self.applied):
824 if a.name == patch:
824 if a.name == patch:
825 return (i, a.rev, a.name)
825 return (i, a.rev, a.name)
826 return None
826 return None
827
827
828 # if the exact patch name does not exist, we try a few
828 # if the exact patch name does not exist, we try a few
829 # variations. If strict is passed, we try only #1
829 # variations. If strict is passed, we try only #1
830 #
830 #
831 # 1) a number to indicate an offset in the series file
831 # 1) a number to indicate an offset in the series file
832 # 2) a unique substring of the patch name was given
832 # 2) a unique substring of the patch name was given
833 # 3) patchname[-+]num to indicate an offset in the series file
833 # 3) patchname[-+]num to indicate an offset in the series file
834 def lookup(self, patch, strict=False):
834 def lookup(self, patch, strict=False):
835 patch = patch and str(patch)
835 patch = patch and str(patch)
836
836
837 def partial_name(s):
837 def partial_name(s):
838 if s in self.series:
838 if s in self.series:
839 return s
839 return s
840 matches = [x for x in self.series if s in x]
840 matches = [x for x in self.series if s in x]
841 if len(matches) > 1:
841 if len(matches) > 1:
842 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
842 self.ui.warn(_('patch name "%s" is ambiguous:\n') % s)
843 for m in matches:
843 for m in matches:
844 self.ui.warn(' %s\n' % m)
844 self.ui.warn(' %s\n' % m)
845 return None
845 return None
846 if matches:
846 if matches:
847 return matches[0]
847 return matches[0]
848 if len(self.series) > 0 and len(self.applied) > 0:
848 if len(self.series) > 0 and len(self.applied) > 0:
849 if s == 'qtip':
849 if s == 'qtip':
850 return self.series[self.series_end(True)-1]
850 return self.series[self.series_end(True)-1]
851 if s == 'qbase':
851 if s == 'qbase':
852 return self.series[0]
852 return self.series[0]
853 return None
853 return None
854
854
855 if patch is None:
855 if patch is None:
856 return None
856 return None
857 if patch in self.series:
857 if patch in self.series:
858 return patch
858 return patch
859
859
860 if not os.path.isfile(self.join(patch)):
860 if not os.path.isfile(self.join(patch)):
861 try:
861 try:
862 sno = int(patch)
862 sno = int(patch)
863 except(ValueError, OverflowError):
863 except(ValueError, OverflowError):
864 pass
864 pass
865 else:
865 else:
866 if -len(self.series) <= sno < len(self.series):
866 if -len(self.series) <= sno < len(self.series):
867 return self.series[sno]
867 return self.series[sno]
868
868
869 if not strict:
869 if not strict:
870 res = partial_name(patch)
870 res = partial_name(patch)
871 if res:
871 if res:
872 return res
872 return res
873 minus = patch.rfind('-')
873 minus = patch.rfind('-')
874 if minus >= 0:
874 if minus >= 0:
875 res = partial_name(patch[:minus])
875 res = partial_name(patch[:minus])
876 if res:
876 if res:
877 i = self.series.index(res)
877 i = self.series.index(res)
878 try:
878 try:
879 off = int(patch[minus+1:] or 1)
879 off = int(patch[minus+1:] or 1)
880 except(ValueError, OverflowError):
880 except(ValueError, OverflowError):
881 pass
881 pass
882 else:
882 else:
883 if i - off >= 0:
883 if i - off >= 0:
884 return self.series[i - off]
884 return self.series[i - off]
885 plus = patch.rfind('+')
885 plus = patch.rfind('+')
886 if plus >= 0:
886 if plus >= 0:
887 res = partial_name(patch[:plus])
887 res = partial_name(patch[:plus])
888 if res:
888 if res:
889 i = self.series.index(res)
889 i = self.series.index(res)
890 try:
890 try:
891 off = int(patch[plus+1:] or 1)
891 off = int(patch[plus+1:] or 1)
892 except(ValueError, OverflowError):
892 except(ValueError, OverflowError):
893 pass
893 pass
894 else:
894 else:
895 if i + off < len(self.series):
895 if i + off < len(self.series):
896 return self.series[i + off]
896 return self.series[i + off]
897 raise util.Abort(_("patch %s not in series") % patch)
897 raise util.Abort(_("patch %s not in series") % patch)
898
898
899 def push(self, repo, patch=None, force=False, list=False,
899 def push(self, repo, patch=None, force=False, list=False,
900 mergeq=None, all=False):
900 mergeq=None, all=False):
901 wlock = repo.wlock()
901 wlock = repo.wlock()
902 try:
902 try:
903 if repo.dirstate.parents()[0] not in repo.heads():
903 if repo.dirstate.parents()[0] not in repo.heads():
904 self.ui.status(_("(working directory not at a head)\n"))
904 self.ui.status(_("(working directory not at a head)\n"))
905
905
906 if not self.series:
906 if not self.series:
907 self.ui.warn(_('no patches in series\n'))
907 self.ui.warn(_('no patches in series\n'))
908 return 0
908 return 0
909
909
910 patch = self.lookup(patch)
910 patch = self.lookup(patch)
911 # Suppose our series file is: A B C and the current 'top'
911 # Suppose our series file is: A B C and the current 'top'
912 # patch is B. qpush C should be performed (moving forward)
912 # patch is B. qpush C should be performed (moving forward)
913 # qpush B is a NOP (no change) qpush A is an error (can't
913 # qpush B is a NOP (no change) qpush A is an error (can't
914 # go backwards with qpush)
914 # go backwards with qpush)
915 if patch:
915 if patch:
916 info = self.isapplied(patch)
916 info = self.isapplied(patch)
917 if info:
917 if info:
918 if info[0] < len(self.applied) - 1:
918 if info[0] < len(self.applied) - 1:
919 raise util.Abort(
919 raise util.Abort(
920 _("cannot push to a previous patch: %s") % patch)
920 _("cannot push to a previous patch: %s") % patch)
921 self.ui.warn(
921 self.ui.warn(
922 _('qpush: %s is already at the top\n') % patch)
922 _('qpush: %s is already at the top\n') % patch)
923 return
923 return
924 pushable, reason = self.pushable(patch)
924 pushable, reason = self.pushable(patch)
925 if not pushable:
925 if not pushable:
926 if reason:
926 if reason:
927 reason = _('guarded by %r') % reason
927 reason = _('guarded by %r') % reason
928 else:
928 else:
929 reason = _('no matching guards')
929 reason = _('no matching guards')
930 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
930 self.ui.warn(_("cannot push '%s' - %s\n") % (patch, reason))
931 return 1
931 return 1
932 elif all:
932 elif all:
933 patch = self.series[-1]
933 patch = self.series[-1]
934 if self.isapplied(patch):
934 if self.isapplied(patch):
935 self.ui.warn(_('all patches are currently applied\n'))
935 self.ui.warn(_('all patches are currently applied\n'))
936 return 0
936 return 0
937
937
938 # Following the above example, starting at 'top' of B:
938 # Following the above example, starting at 'top' of B:
939 # qpush should be performed (pushes C), but a subsequent
939 # qpush should be performed (pushes C), but a subsequent
940 # qpush without an argument is an error (nothing to
940 # qpush without an argument is an error (nothing to
941 # apply). This allows a loop of "...while hg qpush..." to
941 # apply). This allows a loop of "...while hg qpush..." to
942 # work as it detects an error when done
942 # work as it detects an error when done
943 start = self.series_end()
943 start = self.series_end()
944 if start == len(self.series):
944 if start == len(self.series):
945 self.ui.warn(_('patch series already fully applied\n'))
945 self.ui.warn(_('patch series already fully applied\n'))
946 return 1
946 return 1
947 if not force:
947 if not force:
948 self.check_localchanges(repo)
948 self.check_localchanges(repo)
949
949
950 self.applied_dirty = 1
950 self.applied_dirty = 1
951 if start > 0:
951 if start > 0:
952 self.check_toppatch(repo)
952 self.check_toppatch(repo)
953 if not patch:
953 if not patch:
954 patch = self.series[start]
954 patch = self.series[start]
955 end = start + 1
955 end = start + 1
956 else:
956 else:
957 end = self.series.index(patch, start) + 1
957 end = self.series.index(patch, start) + 1
958
958
959 s = self.series[start:end]
959 s = self.series[start:end]
960 all_files = {}
960 all_files = {}
961 try:
961 try:
962 if mergeq:
962 if mergeq:
963 ret = self.mergepatch(repo, mergeq, s)
963 ret = self.mergepatch(repo, mergeq, s)
964 else:
964 else:
965 ret = self.apply(repo, s, list, all_files=all_files)
965 ret = self.apply(repo, s, list, all_files=all_files)
966 except:
966 except:
967 self.ui.warn(_('cleaning up working directory...'))
967 self.ui.warn(_('cleaning up working directory...'))
968 node = repo.dirstate.parents()[0]
968 node = repo.dirstate.parents()[0]
969 hg.revert(repo, node, None)
969 hg.revert(repo, node, None)
970 unknown = repo.status(unknown=True)[4]
970 unknown = repo.status(unknown=True)[4]
971 # only remove unknown files that we know we touched or
971 # only remove unknown files that we know we touched or
972 # created while patching
972 # created while patching
973 for f in unknown:
973 for f in unknown:
974 if f in all_files:
974 if f in all_files:
975 util.unlink(repo.wjoin(f))
975 util.unlink(repo.wjoin(f))
976 self.ui.warn(_('done\n'))
976 self.ui.warn(_('done\n'))
977 raise
977 raise
978
978
979 top = self.applied[-1].name
979 top = self.applied[-1].name
980 if ret[0] and ret[0] > 1:
980 if ret[0] and ret[0] > 1:
981 msg = _("errors during apply, please fix and refresh %s\n")
981 msg = _("errors during apply, please fix and refresh %s\n")
982 self.ui.write(msg % top)
982 self.ui.write(msg % top)
983 else:
983 else:
984 self.ui.write(_("now at: %s\n") % top)
984 self.ui.write(_("now at: %s\n") % top)
985 return ret[0]
985 return ret[0]
986
986
987 finally:
987 finally:
988 wlock.release()
988 wlock.release()
989
989
990 def pop(self, repo, patch=None, force=False, update=True, all=False):
990 def pop(self, repo, patch=None, force=False, update=True, all=False):
991 def getfile(f, rev, flags):
991 def getfile(f, rev, flags):
992 t = repo.file(f).read(rev)
992 t = repo.file(f).read(rev)
993 repo.wwrite(f, t, flags)
993 repo.wwrite(f, t, flags)
994
994
995 wlock = repo.wlock()
995 wlock = repo.wlock()
996 try:
996 try:
997 if patch:
997 if patch:
998 # index, rev, patch
998 # index, rev, patch
999 info = self.isapplied(patch)
999 info = self.isapplied(patch)
1000 if not info:
1000 if not info:
1001 patch = self.lookup(patch)
1001 patch = self.lookup(patch)
1002 info = self.isapplied(patch)
1002 info = self.isapplied(patch)
1003 if not info:
1003 if not info:
1004 raise util.Abort(_("patch %s is not applied") % patch)
1004 raise util.Abort(_("patch %s is not applied") % patch)
1005
1005
1006 if len(self.applied) == 0:
1006 if len(self.applied) == 0:
1007 # Allow qpop -a to work repeatedly,
1007 # Allow qpop -a to work repeatedly,
1008 # but not qpop without an argument
1008 # but not qpop without an argument
1009 self.ui.warn(_("no patches applied\n"))
1009 self.ui.warn(_("no patches applied\n"))
1010 return not all
1010 return not all
1011
1011
1012 if all:
1012 if all:
1013 start = 0
1013 start = 0
1014 elif patch:
1014 elif patch:
1015 start = info[0] + 1
1015 start = info[0] + 1
1016 else:
1016 else:
1017 start = len(self.applied) - 1
1017 start = len(self.applied) - 1
1018
1018
1019 if start >= len(self.applied):
1019 if start >= len(self.applied):
1020 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1020 self.ui.warn(_("qpop: %s is already at the top\n") % patch)
1021 return
1021 return
1022
1022
1023 if not update:
1023 if not update:
1024 parents = repo.dirstate.parents()
1024 parents = repo.dirstate.parents()
1025 rr = [ bin(x.rev) for x in self.applied ]
1025 rr = [ bin(x.rev) for x in self.applied ]
1026 for p in parents:
1026 for p in parents:
1027 if p in rr:
1027 if p in rr:
1028 self.ui.warn(_("qpop: forcing dirstate update\n"))
1028 self.ui.warn(_("qpop: forcing dirstate update\n"))
1029 update = True
1029 update = True
1030 else:
1030 else:
1031 parents = [p.hex() for p in repo[None].parents()]
1031 parents = [p.hex() for p in repo[None].parents()]
1032 needupdate = False
1032 needupdate = False
1033 for entry in self.applied[start:]:
1033 for entry in self.applied[start:]:
1034 if entry.rev in parents:
1034 if entry.rev in parents:
1035 needupdate = True
1035 needupdate = True
1036 break
1036 break
1037 update = needupdate
1037 update = needupdate
1038
1038
1039 if not force and update:
1039 if not force and update:
1040 self.check_localchanges(repo)
1040 self.check_localchanges(repo)
1041
1041
1042 self.applied_dirty = 1
1042 self.applied_dirty = 1
1043 end = len(self.applied)
1043 end = len(self.applied)
1044 rev = bin(self.applied[start].rev)
1044 rev = bin(self.applied[start].rev)
1045 if update:
1045 if update:
1046 top = self.check_toppatch(repo)
1046 top = self.check_toppatch(repo)
1047
1047
1048 try:
1048 try:
1049 heads = repo.changelog.heads(rev)
1049 heads = repo.changelog.heads(rev)
1050 except error.LookupError:
1050 except error.LookupError:
1051 node = short(rev)
1051 node = short(rev)
1052 raise util.Abort(_('trying to pop unknown node %s') % node)
1052 raise util.Abort(_('trying to pop unknown node %s') % node)
1053
1053
1054 if heads != [bin(self.applied[-1].rev)]:
1054 if heads != [bin(self.applied[-1].rev)]:
1055 raise util.Abort(_("popping would remove a revision not "
1055 raise util.Abort(_("popping would remove a revision not "
1056 "managed by this patch queue"))
1056 "managed by this patch queue"))
1057
1057
1058 # we know there are no local changes, so we can make a simplified
1058 # we know there are no local changes, so we can make a simplified
1059 # form of hg.update.
1059 # form of hg.update.
1060 if update:
1060 if update:
1061 qp = self.qparents(repo, rev)
1061 qp = self.qparents(repo, rev)
1062 changes = repo.changelog.read(qp)
1062 changes = repo.changelog.read(qp)
1063 mmap = repo.manifest.read(changes[0])
1063 mmap = repo.manifest.read(changes[0])
1064 m, a, r, d = repo.status(qp, top)[:4]
1064 m, a, r, d = repo.status(qp, top)[:4]
1065 if d:
1065 if d:
1066 raise util.Abort(_("deletions found between repo revs"))
1066 raise util.Abort(_("deletions found between repo revs"))
1067 for f in m:
1067 for f in m:
1068 getfile(f, mmap[f], mmap.flags(f))
1068 getfile(f, mmap[f], mmap.flags(f))
1069 for f in r:
1069 for f in r:
1070 getfile(f, mmap[f], mmap.flags(f))
1070 getfile(f, mmap[f], mmap.flags(f))
1071 for f in m + r:
1071 for f in m + r:
1072 repo.dirstate.normal(f)
1072 repo.dirstate.normal(f)
1073 for f in a:
1073 for f in a:
1074 try:
1074 try:
1075 os.unlink(repo.wjoin(f))
1075 os.unlink(repo.wjoin(f))
1076 except OSError, e:
1076 except OSError, e:
1077 if e.errno != errno.ENOENT:
1077 if e.errno != errno.ENOENT:
1078 raise
1078 raise
1079 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
1079 try: os.removedirs(os.path.dirname(repo.wjoin(f)))
1080 except: pass
1080 except: pass
1081 repo.dirstate.forget(f)
1081 repo.dirstate.forget(f)
1082 repo.dirstate.setparents(qp, nullid)
1082 repo.dirstate.setparents(qp, nullid)
1083 del self.applied[start:end]
1083 del self.applied[start:end]
1084 self.strip(repo, rev, update=False, backup='strip')
1084 self.strip(repo, rev, update=False, backup='strip')
1085 if len(self.applied):
1085 if len(self.applied):
1086 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1086 self.ui.write(_("now at: %s\n") % self.applied[-1].name)
1087 else:
1087 else:
1088 self.ui.write(_("patch queue now empty\n"))
1088 self.ui.write(_("patch queue now empty\n"))
1089 finally:
1089 finally:
1090 wlock.release()
1090 wlock.release()
1091
1091
1092 def diff(self, repo, pats, opts):
1092 def diff(self, repo, pats, opts):
1093 top = self.check_toppatch(repo)
1093 top = self.check_toppatch(repo)
1094 if not top:
1094 if not top:
1095 self.ui.write(_("no patches applied\n"))
1095 self.ui.write(_("no patches applied\n"))
1096 return
1096 return
1097 qp = self.qparents(repo, top)
1097 qp = self.qparents(repo, top)
1098 self._diffopts = patch.diffopts(self.ui, opts)
1098 self._diffopts = patch.diffopts(self.ui, opts)
1099 self.printdiff(repo, qp, files=pats, opts=opts)
1099 self.printdiff(repo, qp, files=pats, opts=opts)
1100
1100
1101 def refresh(self, repo, pats=None, **opts):
1101 def refresh(self, repo, pats=None, **opts):
1102 if len(self.applied) == 0:
1102 if len(self.applied) == 0:
1103 self.ui.write(_("no patches applied\n"))
1103 self.ui.write(_("no patches applied\n"))
1104 return 1
1104 return 1
1105 msg = opts.get('msg', '').rstrip()
1105 msg = opts.get('msg', '').rstrip()
1106 newuser = opts.get('user')
1106 newuser = opts.get('user')
1107 newdate = opts.get('date')
1107 newdate = opts.get('date')
1108 if newdate:
1108 if newdate:
1109 newdate = '%d %d' % util.parsedate(newdate)
1109 newdate = '%d %d' % util.parsedate(newdate)
1110 wlock = repo.wlock()
1110 wlock = repo.wlock()
1111 try:
1111 try:
1112 self.check_toppatch(repo)
1112 self.check_toppatch(repo)
1113 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
1113 (top, patchfn) = (self.applied[-1].rev, self.applied[-1].name)
1114 top = bin(top)
1114 top = bin(top)
1115 if repo.changelog.heads(top) != [top]:
1115 if repo.changelog.heads(top) != [top]:
1116 raise util.Abort(_("cannot refresh a revision with children"))
1116 raise util.Abort(_("cannot refresh a revision with children"))
1117 cparents = repo.changelog.parents(top)
1117 cparents = repo.changelog.parents(top)
1118 patchparent = self.qparents(repo, top)
1118 patchparent = self.qparents(repo, top)
1119 ph = patchheader(self.join(patchfn))
1119 ph = patchheader(self.join(patchfn))
1120
1120
1121 patchf = self.opener(patchfn, 'r')
1121 patchf = self.opener(patchfn, 'r')
1122
1122
1123 # if the patch was a git patch, refresh it as a git patch
1123 # if the patch was a git patch, refresh it as a git patch
1124 for line in patchf:
1124 for line in patchf:
1125 if line.startswith('diff --git'):
1125 if line.startswith('diff --git'):
1126 self.diffopts().git = True
1126 self.diffopts().git = True
1127 break
1127 break
1128
1128
1129 if msg:
1129 if msg:
1130 ph.setmessage(msg)
1130 ph.setmessage(msg)
1131 if newuser:
1131 if newuser:
1132 ph.setuser(newuser)
1132 ph.setuser(newuser)
1133 if newdate:
1133 if newdate:
1134 ph.setdate(newdate)
1134 ph.setdate(newdate)
1135
1135
1136 # only commit new patch when write is complete
1136 # only commit new patch when write is complete
1137 patchf = self.opener(patchfn, 'w', atomictemp=True)
1137 patchf = self.opener(patchfn, 'w', atomictemp=True)
1138
1138
1139 patchf.seek(0)
1139 patchf.seek(0)
1140 patchf.truncate()
1140 patchf.truncate()
1141
1141
1142 comments = str(ph)
1142 comments = str(ph)
1143 if comments:
1143 if comments:
1144 patchf.write(comments)
1144 patchf.write(comments)
1145
1145
1146 if opts.get('git'):
1146 if opts.get('git'):
1147 self.diffopts().git = True
1147 self.diffopts().git = True
1148 tip = repo.changelog.tip()
1148 tip = repo.changelog.tip()
1149 if top == tip:
1149 if top == tip:
1150 # if the top of our patch queue is also the tip, there is an
1150 # if the top of our patch queue is also the tip, there is an
1151 # optimization here. We update the dirstate in place and strip
1151 # optimization here. We update the dirstate in place and strip
1152 # off the tip commit. Then just commit the current directory
1152 # off the tip commit. Then just commit the current directory
1153 # tree. We can also send repo.commit the list of files
1153 # tree. We can also send repo.commit the list of files
1154 # changed to speed up the diff
1154 # changed to speed up the diff
1155 #
1155 #
1156 # in short mode, we only diff the files included in the
1156 # in short mode, we only diff the files included in the
1157 # patch already plus specified files
1157 # patch already plus specified files
1158 #
1158 #
1159 # this should really read:
1159 # this should really read:
1160 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1160 # mm, dd, aa, aa2 = repo.status(tip, patchparent)[:4]
1161 # but we do it backwards to take advantage of manifest/chlog
1161 # but we do it backwards to take advantage of manifest/chlog
1162 # caching against the next repo.status call
1162 # caching against the next repo.status call
1163 #
1163 #
1164 mm, aa, dd, aa2 = repo.status(patchparent, tip)[:4]
1164 mm, aa, dd, aa2 = repo.status(patchparent, tip)[:4]
1165 changes = repo.changelog.read(tip)
1165 changes = repo.changelog.read(tip)
1166 man = repo.manifest.read(changes[0])
1166 man = repo.manifest.read(changes[0])
1167 aaa = aa[:]
1167 aaa = aa[:]
1168 matchfn = cmdutil.match(repo, pats, opts)
1168 matchfn = cmdutil.match(repo, pats, opts)
1169 if opts.get('short'):
1169 if opts.get('short'):
1170 # if amending a patch, we start with existing
1170 # if amending a patch, we start with existing
1171 # files plus specified files - unfiltered
1171 # files plus specified files - unfiltered
1172 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1172 match = cmdutil.matchfiles(repo, mm + aa + dd + matchfn.files())
1173 # filter with inc/exl options
1173 # filter with inc/exl options
1174 matchfn = cmdutil.match(repo, opts=opts)
1174 matchfn = cmdutil.match(repo, opts=opts)
1175 else:
1175 else:
1176 match = cmdutil.matchall(repo)
1176 match = cmdutil.matchall(repo)
1177 m, a, r, d = repo.status(match=match)[:4]
1177 m, a, r, d = repo.status(match=match)[:4]
1178
1178
1179 # we might end up with files that were added between
1179 # we might end up with files that were added between
1180 # tip and the dirstate parent, but then changed in the
1180 # tip and the dirstate parent, but then changed in the
1181 # local dirstate. in this case, we want them to only
1181 # local dirstate. in this case, we want them to only
1182 # show up in the added section
1182 # show up in the added section
1183 for x in m:
1183 for x in m:
1184 if x not in aa:
1184 if x not in aa:
1185 mm.append(x)
1185 mm.append(x)
1186 # we might end up with files added by the local dirstate that
1186 # we might end up with files added by the local dirstate that
1187 # were deleted by the patch. In this case, they should only
1187 # were deleted by the patch. In this case, they should only
1188 # show up in the changed section.
1188 # show up in the changed section.
1189 for x in a:
1189 for x in a:
1190 if x in dd:
1190 if x in dd:
1191 del dd[dd.index(x)]
1191 del dd[dd.index(x)]
1192 mm.append(x)
1192 mm.append(x)
1193 else:
1193 else:
1194 aa.append(x)
1194 aa.append(x)
1195 # make sure any files deleted in the local dirstate
1195 # make sure any files deleted in the local dirstate
1196 # are not in the add or change column of the patch
1196 # are not in the add or change column of the patch
1197 forget = []
1197 forget = []
1198 for x in d + r:
1198 for x in d + r:
1199 if x in aa:
1199 if x in aa:
1200 del aa[aa.index(x)]
1200 del aa[aa.index(x)]
1201 forget.append(x)
1201 forget.append(x)
1202 continue
1202 continue
1203 elif x in mm:
1203 elif x in mm:
1204 del mm[mm.index(x)]
1204 del mm[mm.index(x)]
1205 dd.append(x)
1205 dd.append(x)
1206
1206
1207 m = list(set(mm))
1207 m = list(set(mm))
1208 r = list(set(dd))
1208 r = list(set(dd))
1209 a = list(set(aa))
1209 a = list(set(aa))
1210 c = [filter(matchfn, l) for l in (m, a, r)]
1210 c = [filter(matchfn, l) for l in (m, a, r)]
1211 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1211 match = cmdutil.matchfiles(repo, set(c[0] + c[1] + c[2]))
1212 chunks = patch.diff(repo, patchparent, match=match,
1212 chunks = patch.diff(repo, patchparent, match=match,
1213 changes=c, opts=self.diffopts())
1213 changes=c, opts=self.diffopts())
1214 for chunk in chunks:
1214 for chunk in chunks:
1215 patchf.write(chunk)
1215 patchf.write(chunk)
1216
1216
1217 try:
1217 try:
1218 if self.diffopts().git:
1218 if self.diffopts().git:
1219 copies = {}
1219 copies = {}
1220 for dst in a:
1220 for dst in a:
1221 src = repo.dirstate.copied(dst)
1221 src = repo.dirstate.copied(dst)
1222 # during qfold, the source file for copies may
1222 # during qfold, the source file for copies may
1223 # be removed. Treat this as a simple add.
1223 # be removed. Treat this as a simple add.
1224 if src is not None and src in repo.dirstate:
1224 if src is not None and src in repo.dirstate:
1225 copies.setdefault(src, []).append(dst)
1225 copies.setdefault(src, []).append(dst)
1226 repo.dirstate.add(dst)
1226 repo.dirstate.add(dst)
1227 # remember the copies between patchparent and tip
1227 # remember the copies between patchparent and tip
1228 for dst in aaa:
1228 for dst in aaa:
1229 f = repo.file(dst)
1229 f = repo.file(dst)
1230 src = f.renamed(man[dst])
1230 src = f.renamed(man[dst])
1231 if src:
1231 if src:
1232 copies.setdefault(src[0], []).extend(copies.get(dst, []))
1232 copies.setdefault(src[0], []).extend(copies.get(dst, []))
1233 if dst in a:
1233 if dst in a:
1234 copies[src[0]].append(dst)
1234 copies[src[0]].append(dst)
1235 # we can't copy a file created by the patch itself
1235 # we can't copy a file created by the patch itself
1236 if dst in copies:
1236 if dst in copies:
1237 del copies[dst]
1237 del copies[dst]
1238 for src, dsts in copies.iteritems():
1238 for src, dsts in copies.iteritems():
1239 for dst in dsts:
1239 for dst in dsts:
1240 repo.dirstate.copy(src, dst)
1240 repo.dirstate.copy(src, dst)
1241 else:
1241 else:
1242 for dst in a:
1242 for dst in a:
1243 repo.dirstate.add(dst)
1243 repo.dirstate.add(dst)
1244 # Drop useless copy information
1244 # Drop useless copy information
1245 for f in list(repo.dirstate.copies()):
1245 for f in list(repo.dirstate.copies()):
1246 repo.dirstate.copy(None, f)
1246 repo.dirstate.copy(None, f)
1247 for f in r:
1247 for f in r:
1248 repo.dirstate.remove(f)
1248 repo.dirstate.remove(f)
1249 # if the patch excludes a modified file, mark that
1249 # if the patch excludes a modified file, mark that
1250 # file with mtime=0 so status can see it.
1250 # file with mtime=0 so status can see it.
1251 mm = []
1251 mm = []
1252 for i in xrange(len(m)-1, -1, -1):
1252 for i in xrange(len(m)-1, -1, -1):
1253 if not matchfn(m[i]):
1253 if not matchfn(m[i]):
1254 mm.append(m[i])
1254 mm.append(m[i])
1255 del m[i]
1255 del m[i]
1256 for f in m:
1256 for f in m:
1257 repo.dirstate.normal(f)
1257 repo.dirstate.normal(f)
1258 for f in mm:
1258 for f in mm:
1259 repo.dirstate.normallookup(f)
1259 repo.dirstate.normallookup(f)
1260 for f in forget:
1260 for f in forget:
1261 repo.dirstate.forget(f)
1261 repo.dirstate.forget(f)
1262
1262
1263 if not msg:
1263 if not msg:
1264 if not ph.message:
1264 if not ph.message:
1265 message = "[mq]: %s\n" % patchfn
1265 message = "[mq]: %s\n" % patchfn
1266 else:
1266 else:
1267 message = "\n".join(ph.message)
1267 message = "\n".join(ph.message)
1268 else:
1268 else:
1269 message = msg
1269 message = msg
1270
1270
1271 user = ph.user or changes[1]
1271 user = ph.user or changes[1]
1272
1272
1273 # assumes strip can roll itself back if interrupted
1273 # assumes strip can roll itself back if interrupted
1274 repo.dirstate.setparents(*cparents)
1274 repo.dirstate.setparents(*cparents)
1275 self.applied.pop()
1275 self.applied.pop()
1276 self.applied_dirty = 1
1276 self.applied_dirty = 1
1277 self.strip(repo, top, update=False,
1277 self.strip(repo, top, update=False,
1278 backup='strip')
1278 backup='strip')
1279 except:
1279 except:
1280 repo.dirstate.invalidate()
1280 repo.dirstate.invalidate()
1281 raise
1281 raise
1282
1282
1283 try:
1283 try:
1284 # might be nice to attempt to roll back strip after this
1284 # might be nice to attempt to roll back strip after this
1285 patchf.rename()
1285 patchf.rename()
1286 n = repo.commit(message, user, ph.date, match=match,
1286 n = repo.commit(message, user, ph.date, match=match,
1287 force=True)
1287 force=True)
1288 self.applied.append(statusentry(hex(n), patchfn))
1288 self.applied.append(statusentry(hex(n), patchfn))
1289 except:
1289 except:
1290 ctx = repo[cparents[0]]
1290 ctx = repo[cparents[0]]
1291 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1291 repo.dirstate.rebuild(ctx.node(), ctx.manifest())
1292 self.save_dirty()
1292 self.save_dirty()
1293 self.ui.warn(_('refresh interrupted while patch was popped! '
1293 self.ui.warn(_('refresh interrupted while patch was popped! '
1294 '(revert --all, qpush to recover)\n'))
1294 '(revert --all, qpush to recover)\n'))
1295 raise
1295 raise
1296 else:
1296 else:
1297 self.printdiff(repo, patchparent, fp=patchf)
1297 self.printdiff(repo, patchparent, fp=patchf)
1298 patchf.rename()
1298 patchf.rename()
1299 added = repo.status()[1]
1299 added = repo.status()[1]
1300 for a in added:
1300 for a in added:
1301 f = repo.wjoin(a)
1301 f = repo.wjoin(a)
1302 try:
1302 try:
1303 os.unlink(f)
1303 os.unlink(f)
1304 except OSError, e:
1304 except OSError, e:
1305 if e.errno != errno.ENOENT:
1305 if e.errno != errno.ENOENT:
1306 raise
1306 raise
1307 try: os.removedirs(os.path.dirname(f))
1307 try: os.removedirs(os.path.dirname(f))
1308 except: pass
1308 except: pass
1309 # forget the file copies in the dirstate
1309 # forget the file copies in the dirstate
1310 # push should readd the files later on
1310 # push should readd the files later on
1311 repo.dirstate.forget(a)
1311 repo.dirstate.forget(a)
1312 self.pop(repo, force=True)
1312 self.pop(repo, force=True)
1313 self.push(repo, force=True)
1313 self.push(repo, force=True)
1314 finally:
1314 finally:
1315 wlock.release()
1315 wlock.release()
1316 self.removeundo(repo)
1316 self.removeundo(repo)
1317
1317
1318 def init(self, repo, create=False):
1318 def init(self, repo, create=False):
1319 if not create and os.path.isdir(self.path):
1319 if not create and os.path.isdir(self.path):
1320 raise util.Abort(_("patch queue directory already exists"))
1320 raise util.Abort(_("patch queue directory already exists"))
1321 try:
1321 try:
1322 os.mkdir(self.path)
1322 os.mkdir(self.path)
1323 except OSError, inst:
1323 except OSError, inst:
1324 if inst.errno != errno.EEXIST or not create:
1324 if inst.errno != errno.EEXIST or not create:
1325 raise
1325 raise
1326 if create:
1326 if create:
1327 return self.qrepo(create=True)
1327 return self.qrepo(create=True)
1328
1328
1329 def unapplied(self, repo, patch=None):
1329 def unapplied(self, repo, patch=None):
1330 if patch and patch not in self.series:
1330 if patch and patch not in self.series:
1331 raise util.Abort(_("patch %s is not in series file") % patch)
1331 raise util.Abort(_("patch %s is not in series file") % patch)
1332 if not patch:
1332 if not patch:
1333 start = self.series_end()
1333 start = self.series_end()
1334 else:
1334 else:
1335 start = self.series.index(patch) + 1
1335 start = self.series.index(patch) + 1
1336 unapplied = []
1336 unapplied = []
1337 for i in xrange(start, len(self.series)):
1337 for i in xrange(start, len(self.series)):
1338 pushable, reason = self.pushable(i)
1338 pushable, reason = self.pushable(i)
1339 if pushable:
1339 if pushable:
1340 unapplied.append((i, self.series[i]))
1340 unapplied.append((i, self.series[i]))
1341 self.explain_pushable(i)
1341 self.explain_pushable(i)
1342 return unapplied
1342 return unapplied
1343
1343
1344 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1344 def qseries(self, repo, missing=None, start=0, length=None, status=None,
1345 summary=False):
1345 summary=False):
1346 def displayname(patchname):
1346 def displayname(patchname):
1347 if summary:
1347 if summary:
1348 ph = patchheader(self.join(patchname))
1348 ph = patchheader(self.join(patchname))
1349 msg = ph.message
1349 msg = ph.message
1350 msg = msg and ': ' + msg[0] or ': '
1350 msg = msg and ': ' + msg[0] or ': '
1351 else:
1351 else:
1352 msg = ''
1352 msg = ''
1353 return '%s%s' % (patchname, msg)
1353 return '%s%s' % (patchname, msg)
1354
1354
1355 applied = set([p.name for p in self.applied])
1355 applied = set([p.name for p in self.applied])
1356 if length is None:
1356 if length is None:
1357 length = len(self.series) - start
1357 length = len(self.series) - start
1358 if not missing:
1358 if not missing:
1359 for i in xrange(start, start+length):
1359 for i in xrange(start, start+length):
1360 patch = self.series[i]
1360 patch = self.series[i]
1361 if patch in applied:
1361 if patch in applied:
1362 stat = 'A'
1362 stat = 'A'
1363 elif self.pushable(i)[0]:
1363 elif self.pushable(i)[0]:
1364 stat = 'U'
1364 stat = 'U'
1365 else:
1365 else:
1366 stat = 'G'
1366 stat = 'G'
1367 pfx = ''
1367 pfx = ''
1368 if self.ui.verbose:
1368 if self.ui.verbose:
1369 pfx = '%d %s ' % (i, stat)
1369 pfx = '%d %s ' % (i, stat)
1370 elif status and status != stat:
1370 elif status and status != stat:
1371 continue
1371 continue
1372 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1372 self.ui.write('%s%s\n' % (pfx, displayname(patch)))
1373 else:
1373 else:
1374 msng_list = []
1374 msng_list = []
1375 for root, dirs, files in os.walk(self.path):
1375 for root, dirs, files in os.walk(self.path):
1376 d = root[len(self.path) + 1:]
1376 d = root[len(self.path) + 1:]
1377 for f in files:
1377 for f in files:
1378 fl = os.path.join(d, f)
1378 fl = os.path.join(d, f)
1379 if (fl not in self.series and
1379 if (fl not in self.series and
1380 fl not in (self.status_path, self.series_path,
1380 fl not in (self.status_path, self.series_path,
1381 self.guards_path)
1381 self.guards_path)
1382 and not fl.startswith('.')):
1382 and not fl.startswith('.')):
1383 msng_list.append(fl)
1383 msng_list.append(fl)
1384 for x in sorted(msng_list):
1384 for x in sorted(msng_list):
1385 pfx = self.ui.verbose and ('D ') or ''
1385 pfx = self.ui.verbose and ('D ') or ''
1386 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1386 self.ui.write("%s%s\n" % (pfx, displayname(x)))
1387
1387
1388 def issaveline(self, l):
1388 def issaveline(self, l):
1389 if l.name == '.hg.patches.save.line':
1389 if l.name == '.hg.patches.save.line':
1390 return True
1390 return True
1391
1391
1392 def qrepo(self, create=False):
1392 def qrepo(self, create=False):
1393 if create or os.path.isdir(self.join(".hg")):
1393 if create or os.path.isdir(self.join(".hg")):
1394 return hg.repository(self.ui, path=self.path, create=create)
1394 return hg.repository(self.ui, path=self.path, create=create)
1395
1395
1396 def restore(self, repo, rev, delete=None, qupdate=None):
1396 def restore(self, repo, rev, delete=None, qupdate=None):
1397 c = repo.changelog.read(rev)
1397 c = repo.changelog.read(rev)
1398 desc = c[4].strip()
1398 desc = c[4].strip()
1399 lines = desc.splitlines()
1399 lines = desc.splitlines()
1400 i = 0
1400 i = 0
1401 datastart = None
1401 datastart = None
1402 series = []
1402 series = []
1403 applied = []
1403 applied = []
1404 qpp = None
1404 qpp = None
1405 for i, line in enumerate(lines):
1405 for i, line in enumerate(lines):
1406 if line == 'Patch Data:':
1406 if line == 'Patch Data:':
1407 datastart = i + 1
1407 datastart = i + 1
1408 elif line.startswith('Dirstate:'):
1408 elif line.startswith('Dirstate:'):
1409 l = line.rstrip()
1409 l = line.rstrip()
1410 l = l[10:].split(' ')
1410 l = l[10:].split(' ')
1411 qpp = [ bin(x) for x in l ]
1411 qpp = [ bin(x) for x in l ]
1412 elif datastart != None:
1412 elif datastart != None:
1413 l = line.rstrip()
1413 l = line.rstrip()
1414 se = statusentry(l)
1414 se = statusentry(l)
1415 file_ = se.name
1415 file_ = se.name
1416 if se.rev:
1416 if se.rev:
1417 applied.append(se)
1417 applied.append(se)
1418 else:
1418 else:
1419 series.append(file_)
1419 series.append(file_)
1420 if datastart is None:
1420 if datastart is None:
1421 self.ui.warn(_("No saved patch data found\n"))
1421 self.ui.warn(_("No saved patch data found\n"))
1422 return 1
1422 return 1
1423 self.ui.warn(_("restoring status: %s\n") % lines[0])
1423 self.ui.warn(_("restoring status: %s\n") % lines[0])
1424 self.full_series = series
1424 self.full_series = series
1425 self.applied = applied
1425 self.applied = applied
1426 self.parse_series()
1426 self.parse_series()
1427 self.series_dirty = 1
1427 self.series_dirty = 1
1428 self.applied_dirty = 1
1428 self.applied_dirty = 1
1429 heads = repo.changelog.heads()
1429 heads = repo.changelog.heads()
1430 if delete:
1430 if delete:
1431 if rev not in heads:
1431 if rev not in heads:
1432 self.ui.warn(_("save entry has children, leaving it alone\n"))
1432 self.ui.warn(_("save entry has children, leaving it alone\n"))
1433 else:
1433 else:
1434 self.ui.warn(_("removing save entry %s\n") % short(rev))
1434 self.ui.warn(_("removing save entry %s\n") % short(rev))
1435 pp = repo.dirstate.parents()
1435 pp = repo.dirstate.parents()
1436 if rev in pp:
1436 if rev in pp:
1437 update = True
1437 update = True
1438 else:
1438 else:
1439 update = False
1439 update = False
1440 self.strip(repo, rev, update=update, backup='strip')
1440 self.strip(repo, rev, update=update, backup='strip')
1441 if qpp:
1441 if qpp:
1442 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1442 self.ui.warn(_("saved queue repository parents: %s %s\n") %
1443 (short(qpp[0]), short(qpp[1])))
1443 (short(qpp[0]), short(qpp[1])))
1444 if qupdate:
1444 if qupdate:
1445 self.ui.status(_("queue directory updating\n"))
1445 self.ui.status(_("queue directory updating\n"))
1446 r = self.qrepo()
1446 r = self.qrepo()
1447 if not r:
1447 if not r:
1448 self.ui.warn(_("Unable to load queue repository\n"))
1448 self.ui.warn(_("Unable to load queue repository\n"))
1449 return 1
1449 return 1
1450 hg.clean(r, qpp[0])
1450 hg.clean(r, qpp[0])
1451
1451
1452 def save(self, repo, msg=None):
1452 def save(self, repo, msg=None):
1453 if len(self.applied) == 0:
1453 if len(self.applied) == 0:
1454 self.ui.warn(_("save: no patches applied, exiting\n"))
1454 self.ui.warn(_("save: no patches applied, exiting\n"))
1455 return 1
1455 return 1
1456 if self.issaveline(self.applied[-1]):
1456 if self.issaveline(self.applied[-1]):
1457 self.ui.warn(_("status is already saved\n"))
1457 self.ui.warn(_("status is already saved\n"))
1458 return 1
1458 return 1
1459
1459
1460 ar = [ ':' + x for x in self.full_series ]
1460 ar = [ ':' + x for x in self.full_series ]
1461 if not msg:
1461 if not msg:
1462 msg = _("hg patches saved state")
1462 msg = _("hg patches saved state")
1463 else:
1463 else:
1464 msg = "hg patches: " + msg.rstrip('\r\n')
1464 msg = "hg patches: " + msg.rstrip('\r\n')
1465 r = self.qrepo()
1465 r = self.qrepo()
1466 if r:
1466 if r:
1467 pp = r.dirstate.parents()
1467 pp = r.dirstate.parents()
1468 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1468 msg += "\nDirstate: %s %s" % (hex(pp[0]), hex(pp[1]))
1469 msg += "\n\nPatch Data:\n"
1469 msg += "\n\nPatch Data:\n"
1470 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1470 text = msg + "\n".join([str(x) for x in self.applied]) + '\n' + (ar and
1471 "\n".join(ar) + '\n' or "")
1471 "\n".join(ar) + '\n' or "")
1472 n = repo.commit(text, force=True)
1472 n = repo.commit(text, force=True)
1473 if not n:
1473 if not n:
1474 self.ui.warn(_("repo commit failed\n"))
1474 self.ui.warn(_("repo commit failed\n"))
1475 return 1
1475 return 1
1476 self.applied.append(statusentry(hex(n),'.hg.patches.save.line'))
1476 self.applied.append(statusentry(hex(n),'.hg.patches.save.line'))
1477 self.applied_dirty = 1
1477 self.applied_dirty = 1
1478 self.removeundo(repo)
1478 self.removeundo(repo)
1479
1479
1480 def full_series_end(self):
1480 def full_series_end(self):
1481 if len(self.applied) > 0:
1481 if len(self.applied) > 0:
1482 p = self.applied[-1].name
1482 p = self.applied[-1].name
1483 end = self.find_series(p)
1483 end = self.find_series(p)
1484 if end is None:
1484 if end is None:
1485 return len(self.full_series)
1485 return len(self.full_series)
1486 return end + 1
1486 return end + 1
1487 return 0
1487 return 0
1488
1488
1489 def series_end(self, all_patches=False):
1489 def series_end(self, all_patches=False):
1490 """If all_patches is False, return the index of the next pushable patch
1490 """If all_patches is False, return the index of the next pushable patch
1491 in the series, or the series length. If all_patches is True, return the
1491 in the series, or the series length. If all_patches is True, return the
1492 index of the first patch past the last applied one.
1492 index of the first patch past the last applied one.
1493 """
1493 """
1494 end = 0
1494 end = 0
1495 def next(start):
1495 def next(start):
1496 if all_patches:
1496 if all_patches:
1497 return start
1497 return start
1498 i = start
1498 i = start
1499 while i < len(self.series):
1499 while i < len(self.series):
1500 p, reason = self.pushable(i)
1500 p, reason = self.pushable(i)
1501 if p:
1501 if p:
1502 break
1502 break
1503 self.explain_pushable(i)
1503 self.explain_pushable(i)
1504 i += 1
1504 i += 1
1505 return i
1505 return i
1506 if len(self.applied) > 0:
1506 if len(self.applied) > 0:
1507 p = self.applied[-1].name
1507 p = self.applied[-1].name
1508 try:
1508 try:
1509 end = self.series.index(p)
1509 end = self.series.index(p)
1510 except ValueError:
1510 except ValueError:
1511 return 0
1511 return 0
1512 return next(end + 1)
1512 return next(end + 1)
1513 return next(end)
1513 return next(end)
1514
1514
1515 def appliedname(self, index):
1515 def appliedname(self, index):
1516 pname = self.applied[index].name
1516 pname = self.applied[index].name
1517 if not self.ui.verbose:
1517 if not self.ui.verbose:
1518 p = pname
1518 p = pname
1519 else:
1519 else:
1520 p = str(self.series.index(pname)) + " " + pname
1520 p = str(self.series.index(pname)) + " " + pname
1521 return p
1521 return p
1522
1522
1523 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1523 def qimport(self, repo, files, patchname=None, rev=None, existing=None,
1524 force=None, git=False):
1524 force=None, git=False):
1525 def checkseries(patchname):
1525 def checkseries(patchname):
1526 if patchname in self.series:
1526 if patchname in self.series:
1527 raise util.Abort(_('patch %s is already in the series file')
1527 raise util.Abort(_('patch %s is already in the series file')
1528 % patchname)
1528 % patchname)
1529 def checkfile(patchname):
1529 def checkfile(patchname):
1530 if not force and os.path.exists(self.join(patchname)):
1530 if not force and os.path.exists(self.join(patchname)):
1531 raise util.Abort(_('patch "%s" already exists')
1531 raise util.Abort(_('patch "%s" already exists')
1532 % patchname)
1532 % patchname)
1533
1533
1534 if rev:
1534 if rev:
1535 if files:
1535 if files:
1536 raise util.Abort(_('option "-r" not valid when importing '
1536 raise util.Abort(_('option "-r" not valid when importing '
1537 'files'))
1537 'files'))
1538 rev = cmdutil.revrange(repo, rev)
1538 rev = cmdutil.revrange(repo, rev)
1539 rev.sort(lambda x, y: cmp(y, x))
1539 rev.sort(lambda x, y: cmp(y, x))
1540 if (len(files) > 1 or len(rev) > 1) and patchname:
1540 if (len(files) > 1 or len(rev) > 1) and patchname:
1541 raise util.Abort(_('option "-n" not valid when importing multiple '
1541 raise util.Abort(_('option "-n" not valid when importing multiple '
1542 'patches'))
1542 'patches'))
1543 i = 0
1543 i = 0
1544 added = []
1544 added = []
1545 if rev:
1545 if rev:
1546 # If mq patches are applied, we can only import revisions
1546 # If mq patches are applied, we can only import revisions
1547 # that form a linear path to qbase.
1547 # that form a linear path to qbase.
1548 # Otherwise, they should form a linear path to a head.
1548 # Otherwise, they should form a linear path to a head.
1549 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1549 heads = repo.changelog.heads(repo.changelog.node(rev[-1]))
1550 if len(heads) > 1:
1550 if len(heads) > 1:
1551 raise util.Abort(_('revision %d is the root of more than one '
1551 raise util.Abort(_('revision %d is the root of more than one '
1552 'branch') % rev[-1])
1552 'branch') % rev[-1])
1553 if self.applied:
1553 if self.applied:
1554 base = hex(repo.changelog.node(rev[0]))
1554 base = hex(repo.changelog.node(rev[0]))
1555 if base in [n.rev for n in self.applied]:
1555 if base in [n.rev for n in self.applied]:
1556 raise util.Abort(_('revision %d is already managed')
1556 raise util.Abort(_('revision %d is already managed')
1557 % rev[0])
1557 % rev[0])
1558 if heads != [bin(self.applied[-1].rev)]:
1558 if heads != [bin(self.applied[-1].rev)]:
1559 raise util.Abort(_('revision %d is not the parent of '
1559 raise util.Abort(_('revision %d is not the parent of '
1560 'the queue') % rev[0])
1560 'the queue') % rev[0])
1561 base = repo.changelog.rev(bin(self.applied[0].rev))
1561 base = repo.changelog.rev(bin(self.applied[0].rev))
1562 lastparent = repo.changelog.parentrevs(base)[0]
1562 lastparent = repo.changelog.parentrevs(base)[0]
1563 else:
1563 else:
1564 if heads != [repo.changelog.node(rev[0])]:
1564 if heads != [repo.changelog.node(rev[0])]:
1565 raise util.Abort(_('revision %d has unmanaged children')
1565 raise util.Abort(_('revision %d has unmanaged children')
1566 % rev[0])
1566 % rev[0])
1567 lastparent = None
1567 lastparent = None
1568
1568
1569 if git:
1569 if git:
1570 self.diffopts().git = True
1570 self.diffopts().git = True
1571
1571
1572 for r in rev:
1572 for r in rev:
1573 p1, p2 = repo.changelog.parentrevs(r)
1573 p1, p2 = repo.changelog.parentrevs(r)
1574 n = repo.changelog.node(r)
1574 n = repo.changelog.node(r)
1575 if p2 != nullrev:
1575 if p2 != nullrev:
1576 raise util.Abort(_('cannot import merge revision %d') % r)
1576 raise util.Abort(_('cannot import merge revision %d') % r)
1577 if lastparent and lastparent != r:
1577 if lastparent and lastparent != r:
1578 raise util.Abort(_('revision %d is not the parent of %d')
1578 raise util.Abort(_('revision %d is not the parent of %d')
1579 % (r, lastparent))
1579 % (r, lastparent))
1580 lastparent = p1
1580 lastparent = p1
1581
1581
1582 if not patchname:
1582 if not patchname:
1583 patchname = normname('%d.diff' % r)
1583 patchname = normname('%d.diff' % r)
1584 self.check_reserved_name(patchname)
1584 self.check_reserved_name(patchname)
1585 checkseries(patchname)
1585 checkseries(patchname)
1586 checkfile(patchname)
1586 checkfile(patchname)
1587 self.full_series.insert(0, patchname)
1587 self.full_series.insert(0, patchname)
1588
1588
1589 patchf = self.opener(patchname, "w")
1589 patchf = self.opener(patchname, "w")
1590 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1590 patch.export(repo, [n], fp=patchf, opts=self.diffopts())
1591 patchf.close()
1591 patchf.close()
1592
1592
1593 se = statusentry(hex(n), patchname)
1593 se = statusentry(hex(n), patchname)
1594 self.applied.insert(0, se)
1594 self.applied.insert(0, se)
1595
1595
1596 added.append(patchname)
1596 added.append(patchname)
1597 patchname = None
1597 patchname = None
1598 self.parse_series()
1598 self.parse_series()
1599 self.applied_dirty = 1
1599 self.applied_dirty = 1
1600
1600
1601 for filename in files:
1601 for filename in files:
1602 if existing:
1602 if existing:
1603 if filename == '-':
1603 if filename == '-':
1604 raise util.Abort(_('-e is incompatible with import from -'))
1604 raise util.Abort(_('-e is incompatible with import from -'))
1605 if not patchname:
1605 if not patchname:
1606 patchname = normname(filename)
1606 patchname = normname(filename)
1607 self.check_reserved_name(patchname)
1607 self.check_reserved_name(patchname)
1608 if not os.path.isfile(self.join(patchname)):
1608 if not os.path.isfile(self.join(patchname)):
1609 raise util.Abort(_("patch %s does not exist") % patchname)
1609 raise util.Abort(_("patch %s does not exist") % patchname)
1610 else:
1610 else:
1611 try:
1611 try:
1612 if filename == '-':
1612 if filename == '-':
1613 if not patchname:
1613 if not patchname:
1614 raise util.Abort(_('need --name to import a patch from -'))
1614 raise util.Abort(_('need --name to import a patch from -'))
1615 text = sys.stdin.read()
1615 text = sys.stdin.read()
1616 else:
1616 else:
1617 text = url.open(self.ui, filename).read()
1617 text = url.open(self.ui, filename).read()
1618 except (OSError, IOError):
1618 except (OSError, IOError):
1619 raise util.Abort(_("unable to read %s") % filename)
1619 raise util.Abort(_("unable to read %s") % filename)
1620 if not patchname:
1620 if not patchname:
1621 patchname = normname(os.path.basename(filename))
1621 patchname = normname(os.path.basename(filename))
1622 self.check_reserved_name(patchname)
1622 self.check_reserved_name(patchname)
1623 checkfile(patchname)
1623 checkfile(patchname)
1624 patchf = self.opener(patchname, "w")
1624 patchf = self.opener(patchname, "w")
1625 patchf.write(text)
1625 patchf.write(text)
1626 if not force:
1626 if not force:
1627 checkseries(patchname)
1627 checkseries(patchname)
1628 if patchname not in self.series:
1628 if patchname not in self.series:
1629 index = self.full_series_end() + i
1629 index = self.full_series_end() + i
1630 self.full_series[index:index] = [patchname]
1630 self.full_series[index:index] = [patchname]
1631 self.parse_series()
1631 self.parse_series()
1632 self.ui.warn(_("adding %s to series file\n") % patchname)
1632 self.ui.warn(_("adding %s to series file\n") % patchname)
1633 i += 1
1633 i += 1
1634 added.append(patchname)
1634 added.append(patchname)
1635 patchname = None
1635 patchname = None
1636 self.series_dirty = 1
1636 self.series_dirty = 1
1637 qrepo = self.qrepo()
1637 qrepo = self.qrepo()
1638 if qrepo:
1638 if qrepo:
1639 qrepo.add(added)
1639 qrepo.add(added)
1640
1640
1641 def delete(ui, repo, *patches, **opts):
1641 def delete(ui, repo, *patches, **opts):
1642 """remove patches from queue
1642 """remove patches from queue
1643
1643
1644 The patches must not be applied, and at least one patch is required. With
1644 The patches must not be applied, and at least one patch is required. With
1645 -k/--keep, the patch files are preserved in the patch directory.
1645 -k/--keep, the patch files are preserved in the patch directory.
1646
1646
1647 To stop managing a patch and move it into permanent history,
1647 To stop managing a patch and move it into permanent history,
1648 use the qfinish command."""
1648 use the qfinish command."""
1649 q = repo.mq
1649 q = repo.mq
1650 q.delete(repo, patches, opts)
1650 q.delete(repo, patches, opts)
1651 q.save_dirty()
1651 q.save_dirty()
1652 return 0
1652 return 0
1653
1653
1654 def applied(ui, repo, patch=None, **opts):
1654 def applied(ui, repo, patch=None, **opts):
1655 """print the patches already applied"""
1655 """print the patches already applied"""
1656 q = repo.mq
1656 q = repo.mq
1657 if patch:
1657 if patch:
1658 if patch not in q.series:
1658 if patch not in q.series:
1659 raise util.Abort(_("patch %s is not in series file") % patch)
1659 raise util.Abort(_("patch %s is not in series file") % patch)
1660 end = q.series.index(patch) + 1
1660 end = q.series.index(patch) + 1
1661 else:
1661 else:
1662 end = q.series_end(True)
1662 end = q.series_end(True)
1663 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1663 return q.qseries(repo, length=end, status='A', summary=opts.get('summary'))
1664
1664
1665 def unapplied(ui, repo, patch=None, **opts):
1665 def unapplied(ui, repo, patch=None, **opts):
1666 """print the patches not yet applied"""
1666 """print the patches not yet applied"""
1667 q = repo.mq
1667 q = repo.mq
1668 if patch:
1668 if patch:
1669 if patch not in q.series:
1669 if patch not in q.series:
1670 raise util.Abort(_("patch %s is not in series file") % patch)
1670 raise util.Abort(_("patch %s is not in series file") % patch)
1671 start = q.series.index(patch) + 1
1671 start = q.series.index(patch) + 1
1672 else:
1672 else:
1673 start = q.series_end(True)
1673 start = q.series_end(True)
1674 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1674 q.qseries(repo, start=start, status='U', summary=opts.get('summary'))
1675
1675
1676 def qimport(ui, repo, *filename, **opts):
1676 def qimport(ui, repo, *filename, **opts):
1677 """import a patch
1677 """import a patch
1678
1678
1679 The patch is inserted into the series after the last applied
1679 The patch is inserted into the series after the last applied
1680 patch. If no patches have been applied, qimport prepends the patch
1680 patch. If no patches have been applied, qimport prepends the patch
1681 to the series.
1681 to the series.
1682
1682
1683 The patch will have the same name as its source file unless you
1683 The patch will have the same name as its source file unless you
1684 give it a new one with -n/--name.
1684 give it a new one with -n/--name.
1685
1685
1686 You can register an existing patch inside the patch directory with
1686 You can register an existing patch inside the patch directory with
1687 the -e/--existing flag.
1687 the -e/--existing flag.
1688
1688
1689 With -f/--force, an existing patch of the same name will be
1689 With -f/--force, an existing patch of the same name will be
1690 overwritten.
1690 overwritten.
1691
1691
1692 An existing changeset may be placed under mq control with -r/--rev
1692 An existing changeset may be placed under mq control with -r/--rev
1693 (e.g. qimport --rev tip -n patch will place tip under mq control).
1693 (e.g. qimport --rev tip -n patch will place tip under mq control).
1694 With -g/--git, patches imported with --rev will use the git diff
1694 With -g/--git, patches imported with --rev will use the git diff
1695 format. See the diffs help topic for information on why this is
1695 format. See the diffs help topic for information on why this is
1696 important for preserving rename/copy information and permission
1696 important for preserving rename/copy information and permission
1697 changes.
1697 changes.
1698
1698
1699 To import a patch from standard input, pass - as the patch file.
1699 To import a patch from standard input, pass - as the patch file.
1700 When importing from standard input, a patch name must be specified
1700 When importing from standard input, a patch name must be specified
1701 using the --name flag.
1701 using the --name flag.
1702 """
1702 """
1703 q = repo.mq
1703 q = repo.mq
1704 q.qimport(repo, filename, patchname=opts['name'],
1704 q.qimport(repo, filename, patchname=opts['name'],
1705 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1705 existing=opts['existing'], force=opts['force'], rev=opts['rev'],
1706 git=opts['git'])
1706 git=opts['git'])
1707 q.save_dirty()
1707 q.save_dirty()
1708
1708
1709 if opts.get('push') and not opts.get('rev'):
1709 if opts.get('push') and not opts.get('rev'):
1710 return q.push(repo, None)
1710 return q.push(repo, None)
1711 return 0
1711 return 0
1712
1712
1713 def init(ui, repo, **opts):
1713 def init(ui, repo, **opts):
1714 """init a new queue repository
1714 """init a new queue repository
1715
1715
1716 The queue repository is unversioned by default. If
1716 The queue repository is unversioned by default. If
1717 -c/--create-repo is specified, qinit will create a separate nested
1717 -c/--create-repo is specified, qinit will create a separate nested
1718 repository for patches (qinit -c may also be run later to convert
1718 repository for patches (qinit -c may also be run later to convert
1719 an unversioned patch repository into a versioned one). You can use
1719 an unversioned patch repository into a versioned one). You can use
1720 qcommit to commit changes to this queue repository."""
1720 qcommit to commit changes to this queue repository."""
1721 q = repo.mq
1721 q = repo.mq
1722 r = q.init(repo, create=opts['create_repo'])
1722 r = q.init(repo, create=opts['create_repo'])
1723 q.save_dirty()
1723 q.save_dirty()
1724 if r:
1724 if r:
1725 if not os.path.exists(r.wjoin('.hgignore')):
1725 if not os.path.exists(r.wjoin('.hgignore')):
1726 fp = r.wopener('.hgignore', 'w')
1726 fp = r.wopener('.hgignore', 'w')
1727 fp.write('^\\.hg\n')
1727 fp.write('^\\.hg\n')
1728 fp.write('^\\.mq\n')
1728 fp.write('^\\.mq\n')
1729 fp.write('syntax: glob\n')
1729 fp.write('syntax: glob\n')
1730 fp.write('status\n')
1730 fp.write('status\n')
1731 fp.write('guards\n')
1731 fp.write('guards\n')
1732 fp.close()
1732 fp.close()
1733 if not os.path.exists(r.wjoin('series')):
1733 if not os.path.exists(r.wjoin('series')):
1734 r.wopener('series', 'w').close()
1734 r.wopener('series', 'w').close()
1735 r.add(['.hgignore', 'series'])
1735 r.add(['.hgignore', 'series'])
1736 commands.add(ui, r)
1736 commands.add(ui, r)
1737 return 0
1737 return 0
1738
1738
1739 def clone(ui, source, dest=None, **opts):
1739 def clone(ui, source, dest=None, **opts):
1740 '''clone main and patch repository at same time
1740 '''clone main and patch repository at same time
1741
1741
1742 If source is local, destination will have no patches applied. If
1742 If source is local, destination will have no patches applied. If
1743 source is remote, this command can not check if patches are
1743 source is remote, this command can not check if patches are
1744 applied in source, so cannot guarantee that patches are not
1744 applied in source, so cannot guarantee that patches are not
1745 applied in destination. If you clone remote repository, be sure
1745 applied in destination. If you clone remote repository, be sure
1746 before that it has no patches applied.
1746 before that it has no patches applied.
1747
1747
1748 Source patch repository is looked for in <src>/.hg/patches by
1748 Source patch repository is looked for in <src>/.hg/patches by
1749 default. Use -p <url> to change.
1749 default. Use -p <url> to change.
1750
1750
1751 The patch directory must be a nested Mercurial repository, as
1751 The patch directory must be a nested Mercurial repository, as
1752 would be created by qinit -c.
1752 would be created by qinit -c.
1753 '''
1753 '''
1754 def patchdir(repo):
1754 def patchdir(repo):
1755 url = repo.url()
1755 url = repo.url()
1756 if url.endswith('/'):
1756 if url.endswith('/'):
1757 url = url[:-1]
1757 url = url[:-1]
1758 return url + '/.hg/patches'
1758 return url + '/.hg/patches'
1759 if dest is None:
1759 if dest is None:
1760 dest = hg.defaultdest(source)
1760 dest = hg.defaultdest(source)
1761 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1761 sr = hg.repository(cmdutil.remoteui(ui, opts), ui.expandpath(source))
1762 if opts['patches']:
1762 if opts['patches']:
1763 patchespath = ui.expandpath(opts['patches'])
1763 patchespath = ui.expandpath(opts['patches'])
1764 else:
1764 else:
1765 patchespath = patchdir(sr)
1765 patchespath = patchdir(sr)
1766 try:
1766 try:
1767 hg.repository(ui, patchespath)
1767 hg.repository(ui, patchespath)
1768 except error.RepoError:
1768 except error.RepoError:
1769 raise util.Abort(_('versioned patch repository not found'
1769 raise util.Abort(_('versioned patch repository not found'
1770 ' (see qinit -c)'))
1770 ' (see qinit -c)'))
1771 qbase, destrev = None, None
1771 qbase, destrev = None, None
1772 if sr.local():
1772 if sr.local():
1773 if sr.mq.applied:
1773 if sr.mq.applied:
1774 qbase = bin(sr.mq.applied[0].rev)
1774 qbase = bin(sr.mq.applied[0].rev)
1775 if not hg.islocal(dest):
1775 if not hg.islocal(dest):
1776 heads = set(sr.heads())
1776 heads = set(sr.heads())
1777 destrev = list(heads.difference(sr.heads(qbase)))
1777 destrev = list(heads.difference(sr.heads(qbase)))
1778 destrev.append(sr.changelog.parents(qbase)[0])
1778 destrev.append(sr.changelog.parents(qbase)[0])
1779 elif sr.capable('lookup'):
1779 elif sr.capable('lookup'):
1780 try:
1780 try:
1781 qbase = sr.lookup('qbase')
1781 qbase = sr.lookup('qbase')
1782 except error.RepoError:
1782 except error.RepoError:
1783 pass
1783 pass
1784 ui.note(_('cloning main repository\n'))
1784 ui.note(_('cloning main repository\n'))
1785 sr, dr = hg.clone(ui, sr.url(), dest,
1785 sr, dr = hg.clone(ui, sr.url(), dest,
1786 pull=opts['pull'],
1786 pull=opts['pull'],
1787 rev=destrev,
1787 rev=destrev,
1788 update=False,
1788 update=False,
1789 stream=opts['uncompressed'])
1789 stream=opts['uncompressed'])
1790 ui.note(_('cloning patch repository\n'))
1790 ui.note(_('cloning patch repository\n'))
1791 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1791 hg.clone(ui, opts['patches'] or patchdir(sr), patchdir(dr),
1792 pull=opts['pull'], update=not opts['noupdate'],
1792 pull=opts['pull'], update=not opts['noupdate'],
1793 stream=opts['uncompressed'])
1793 stream=opts['uncompressed'])
1794 if dr.local():
1794 if dr.local():
1795 if qbase:
1795 if qbase:
1796 ui.note(_('stripping applied patches from destination '
1796 ui.note(_('stripping applied patches from destination '
1797 'repository\n'))
1797 'repository\n'))
1798 dr.mq.strip(dr, qbase, update=False, backup=None)
1798 dr.mq.strip(dr, qbase, update=False, backup=None)
1799 if not opts['noupdate']:
1799 if not opts['noupdate']:
1800 ui.note(_('updating destination repository\n'))
1800 ui.note(_('updating destination repository\n'))
1801 hg.update(dr, dr.changelog.tip())
1801 hg.update(dr, dr.changelog.tip())
1802
1802
1803 def commit(ui, repo, *pats, **opts):
1803 def commit(ui, repo, *pats, **opts):
1804 """commit changes in the queue repository"""
1804 """commit changes in the queue repository"""
1805 q = repo.mq
1805 q = repo.mq
1806 r = q.qrepo()
1806 r = q.qrepo()
1807 if not r: raise util.Abort('no queue repository')
1807 if not r: raise util.Abort('no queue repository')
1808 commands.commit(r.ui, r, *pats, **opts)
1808 commands.commit(r.ui, r, *pats, **opts)
1809
1809
1810 def series(ui, repo, **opts):
1810 def series(ui, repo, **opts):
1811 """print the entire series file"""
1811 """print the entire series file"""
1812 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1812 repo.mq.qseries(repo, missing=opts['missing'], summary=opts['summary'])
1813 return 0
1813 return 0
1814
1814
1815 def top(ui, repo, **opts):
1815 def top(ui, repo, **opts):
1816 """print the name of the current patch"""
1816 """print the name of the current patch"""
1817 q = repo.mq
1817 q = repo.mq
1818 t = q.applied and q.series_end(True) or 0
1818 t = q.applied and q.series_end(True) or 0
1819 if t:
1819 if t:
1820 return q.qseries(repo, start=t-1, length=1, status='A',
1820 return q.qseries(repo, start=t-1, length=1, status='A',
1821 summary=opts.get('summary'))
1821 summary=opts.get('summary'))
1822 else:
1822 else:
1823 ui.write(_("no patches applied\n"))
1823 ui.write(_("no patches applied\n"))
1824 return 1
1824 return 1
1825
1825
1826 def next(ui, repo, **opts):
1826 def next(ui, repo, **opts):
1827 """print the name of the next patch"""
1827 """print the name of the next patch"""
1828 q = repo.mq
1828 q = repo.mq
1829 end = q.series_end()
1829 end = q.series_end()
1830 if end == len(q.series):
1830 if end == len(q.series):
1831 ui.write(_("all patches applied\n"))
1831 ui.write(_("all patches applied\n"))
1832 return 1
1832 return 1
1833 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1833 return q.qseries(repo, start=end, length=1, summary=opts.get('summary'))
1834
1834
1835 def prev(ui, repo, **opts):
1835 def prev(ui, repo, **opts):
1836 """print the name of the previous patch"""
1836 """print the name of the previous patch"""
1837 q = repo.mq
1837 q = repo.mq
1838 l = len(q.applied)
1838 l = len(q.applied)
1839 if l == 1:
1839 if l == 1:
1840 ui.write(_("only one patch applied\n"))
1840 ui.write(_("only one patch applied\n"))
1841 return 1
1841 return 1
1842 if not l:
1842 if not l:
1843 ui.write(_("no patches applied\n"))
1843 ui.write(_("no patches applied\n"))
1844 return 1
1844 return 1
1845 return q.qseries(repo, start=l-2, length=1, status='A',
1845 return q.qseries(repo, start=l-2, length=1, status='A',
1846 summary=opts.get('summary'))
1846 summary=opts.get('summary'))
1847
1847
1848 def setupheaderopts(ui, opts):
1848 def setupheaderopts(ui, opts):
1849 def do(opt,val):
1849 def do(opt,val):
1850 if not opts[opt] and opts['current' + opt]:
1850 if not opts[opt] and opts['current' + opt]:
1851 opts[opt] = val
1851 opts[opt] = val
1852 do('user', ui.username())
1852 do('user', ui.username())
1853 do('date', "%d %d" % util.makedate())
1853 do('date', "%d %d" % util.makedate())
1854
1854
1855 def new(ui, repo, patch, *args, **opts):
1855 def new(ui, repo, patch, *args, **opts):
1856 """create a new patch
1856 """create a new patch
1857
1857
1858 qnew creates a new patch on top of the currently-applied patch (if
1858 qnew creates a new patch on top of the currently-applied patch (if
1859 any). It will refuse to run if there are any outstanding changes
1859 any). It will refuse to run if there are any outstanding changes
1860 unless -f/--force is specified, in which case the patch will be
1860 unless -f/--force is specified, in which case the patch will be
1861 initialized with them. You may also use -I/--include,
1861 initialized with them. You may also use -I/--include,
1862 -X/--exclude, and/or a list of files after the patch name to add
1862 -X/--exclude, and/or a list of files after the patch name to add
1863 only changes to matching files to the new patch, leaving the rest
1863 only changes to matching files to the new patch, leaving the rest
1864 as uncommitted modifications.
1864 as uncommitted modifications.
1865
1865
1866 -u/--user and -d/--date can be used to set the (given) user and
1866 -u/--user and -d/--date can be used to set the (given) user and
1867 date, respectively. -U/--currentuser and -D/--currentdate set user
1867 date, respectively. -U/--currentuser and -D/--currentdate set user
1868 to current user and date to current date.
1868 to current user and date to current date.
1869
1869
1870 -e/--edit, -m/--message or -l/--logfile set the patch header as
1870 -e/--edit, -m/--message or -l/--logfile set the patch header as
1871 well as the commit message. If none is specified, the header is
1871 well as the commit message. If none is specified, the header is
1872 empty and the commit message is '[mq]: PATCH'.
1872 empty and the commit message is '[mq]: PATCH'.
1873
1873
1874 Use the -g/--git option to keep the patch in the git extended diff
1874 Use the -g/--git option to keep the patch in the git extended diff
1875 format. Read the diffs help topic for more information on why this
1875 format. Read the diffs help topic for more information on why this
1876 is important for preserving permission changes and copy/rename
1876 is important for preserving permission changes and copy/rename
1877 information.
1877 information.
1878 """
1878 """
1879 msg = cmdutil.logmessage(opts)
1879 msg = cmdutil.logmessage(opts)
1880 def getmsg(): return ui.edit(msg, ui.username())
1880 def getmsg(): return ui.edit(msg, ui.username())
1881 q = repo.mq
1881 q = repo.mq
1882 opts['msg'] = msg
1882 opts['msg'] = msg
1883 if opts.get('edit'):
1883 if opts.get('edit'):
1884 opts['msg'] = getmsg
1884 opts['msg'] = getmsg
1885 else:
1885 else:
1886 opts['msg'] = msg
1886 opts['msg'] = msg
1887 setupheaderopts(ui, opts)
1887 setupheaderopts(ui, opts)
1888 q.new(repo, patch, *args, **opts)
1888 q.new(repo, patch, *args, **opts)
1889 q.save_dirty()
1889 q.save_dirty()
1890 return 0
1890 return 0
1891
1891
1892 def refresh(ui, repo, *pats, **opts):
1892 def refresh(ui, repo, *pats, **opts):
1893 """update the current patch
1893 """update the current patch
1894
1894
1895 If any file patterns are provided, the refreshed patch will
1895 If any file patterns are provided, the refreshed patch will
1896 contain only the modifications that match those patterns; the
1896 contain only the modifications that match those patterns; the
1897 remaining modifications will remain in the working directory.
1897 remaining modifications will remain in the working directory.
1898
1898
1899 If -s/--short is specified, files currently included in the patch
1899 If -s/--short is specified, files currently included in the patch
1900 will be refreshed just like matched files and remain in the patch.
1900 will be refreshed just like matched files and remain in the patch.
1901
1901
1902 hg add/remove/copy/rename work as usual, though you might want to
1902 hg add/remove/copy/rename work as usual, though you might want to
1903 use git-style patches (-g/--git or [diff] git=1) to track copies
1903 use git-style patches (-g/--git or [diff] git=1) to track copies
1904 and renames. See the diffs help topic for more information on the
1904 and renames. See the diffs help topic for more information on the
1905 git diff format.
1905 git diff format.
1906 """
1906 """
1907 q = repo.mq
1907 q = repo.mq
1908 message = cmdutil.logmessage(opts)
1908 message = cmdutil.logmessage(opts)
1909 if opts['edit']:
1909 if opts['edit']:
1910 if not q.applied:
1910 if not q.applied:
1911 ui.write(_("no patches applied\n"))
1911 ui.write(_("no patches applied\n"))
1912 return 1
1912 return 1
1913 if message:
1913 if message:
1914 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1914 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1915 patch = q.applied[-1].name
1915 patch = q.applied[-1].name
1916 ph = patchheader(q.join(patch))
1916 ph = patchheader(q.join(patch))
1917 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
1917 message = ui.edit('\n'.join(ph.message), ph.user or ui.username())
1918 setupheaderopts(ui, opts)
1918 setupheaderopts(ui, opts)
1919 ret = q.refresh(repo, pats, msg=message, **opts)
1919 ret = q.refresh(repo, pats, msg=message, **opts)
1920 q.save_dirty()
1920 q.save_dirty()
1921 return ret
1921 return ret
1922
1922
1923 def diff(ui, repo, *pats, **opts):
1923 def diff(ui, repo, *pats, **opts):
1924 """diff of the current patch and subsequent modifications
1924 """diff of the current patch and subsequent modifications
1925
1925
1926 Shows a diff which includes the current patch as well as any
1926 Shows a diff which includes the current patch as well as any
1927 changes which have been made in the working directory since the
1927 changes which have been made in the working directory since the
1928 last refresh (thus showing what the current patch would become
1928 last refresh (thus showing what the current patch would become
1929 after a qrefresh).
1929 after a qrefresh).
1930
1930
1931 Use 'hg diff' if you only want to see the changes made since the
1931 Use 'hg diff' if you only want to see the changes made since the
1932 last qrefresh, or 'hg export qtip' if you want to see changes made
1932 last qrefresh, or 'hg export qtip' if you want to see changes made
1933 by the current patch without including changes made since the
1933 by the current patch without including changes made since the
1934 qrefresh.
1934 qrefresh.
1935 """
1935 """
1936 repo.mq.diff(repo, pats, opts)
1936 repo.mq.diff(repo, pats, opts)
1937 return 0
1937 return 0
1938
1938
1939 def fold(ui, repo, *files, **opts):
1939 def fold(ui, repo, *files, **opts):
1940 """fold the named patches into the current patch
1940 """fold the named patches into the current patch
1941
1941
1942 Patches must not yet be applied. Each patch will be successively
1942 Patches must not yet be applied. Each patch will be successively
1943 applied to the current patch in the order given. If all the
1943 applied to the current patch in the order given. If all the
1944 patches apply successfully, the current patch will be refreshed
1944 patches apply successfully, the current patch will be refreshed
1945 with the new cumulative patch, and the folded patches will be
1945 with the new cumulative patch, and the folded patches will be
1946 deleted. With -k/--keep, the folded patch files will not be
1946 deleted. With -k/--keep, the folded patch files will not be
1947 removed afterwards.
1947 removed afterwards.
1948
1948
1949 The header for each folded patch will be concatenated with the
1949 The header for each folded patch will be concatenated with the
1950 current patch header, separated by a line of '* * *'."""
1950 current patch header, separated by a line of '* * *'."""
1951
1951
1952 q = repo.mq
1952 q = repo.mq
1953
1953
1954 if not files:
1954 if not files:
1955 raise util.Abort(_('qfold requires at least one patch name'))
1955 raise util.Abort(_('qfold requires at least one patch name'))
1956 if not q.check_toppatch(repo):
1956 if not q.check_toppatch(repo):
1957 raise util.Abort(_('No patches applied'))
1957 raise util.Abort(_('No patches applied'))
1958 q.check_localchanges(repo)
1958 q.check_localchanges(repo)
1959
1959
1960 message = cmdutil.logmessage(opts)
1960 message = cmdutil.logmessage(opts)
1961 if opts['edit']:
1961 if opts['edit']:
1962 if message:
1962 if message:
1963 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1963 raise util.Abort(_('option "-e" incompatible with "-m" or "-l"'))
1964
1964
1965 parent = q.lookup('qtip')
1965 parent = q.lookup('qtip')
1966 patches = []
1966 patches = []
1967 messages = []
1967 messages = []
1968 for f in files:
1968 for f in files:
1969 p = q.lookup(f)
1969 p = q.lookup(f)
1970 if p in patches or p == parent:
1970 if p in patches or p == parent:
1971 ui.warn(_('Skipping already folded patch %s') % p)
1971 ui.warn(_('Skipping already folded patch %s') % p)
1972 if q.isapplied(p):
1972 if q.isapplied(p):
1973 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1973 raise util.Abort(_('qfold cannot fold already applied patch %s') % p)
1974 patches.append(p)
1974 patches.append(p)
1975
1975
1976 for p in patches:
1976 for p in patches:
1977 if not message:
1977 if not message:
1978 ph = patchheader(q.join(p))
1978 ph = patchheader(q.join(p))
1979 if ph.message:
1979 if ph.message:
1980 messages.append(ph.message)
1980 messages.append(ph.message)
1981 pf = q.join(p)
1981 pf = q.join(p)
1982 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1982 (patchsuccess, files, fuzz) = q.patch(repo, pf)
1983 if not patchsuccess:
1983 if not patchsuccess:
1984 raise util.Abort(_('Error folding patch %s') % p)
1984 raise util.Abort(_('Error folding patch %s') % p)
1985 patch.updatedir(ui, repo, files)
1985 patch.updatedir(ui, repo, files)
1986
1986
1987 if not message:
1987 if not message:
1988 ph = patchheader(q.join(parent))
1988 ph = patchheader(q.join(parent))
1989 message, user = ph.message, ph.user
1989 message, user = ph.message, ph.user
1990 for msg in messages:
1990 for msg in messages:
1991 message.append('* * *')
1991 message.append('* * *')
1992 message.extend(msg)
1992 message.extend(msg)
1993 message = '\n'.join(message)
1993 message = '\n'.join(message)
1994
1994
1995 if opts['edit']:
1995 if opts['edit']:
1996 message = ui.edit(message, user or ui.username())
1996 message = ui.edit(message, user or ui.username())
1997
1997
1998 q.refresh(repo, msg=message)
1998 q.refresh(repo, msg=message)
1999 q.delete(repo, patches, opts)
1999 q.delete(repo, patches, opts)
2000 q.save_dirty()
2000 q.save_dirty()
2001
2001
2002 def goto(ui, repo, patch, **opts):
2002 def goto(ui, repo, patch, **opts):
2003 '''push or pop patches until named patch is at top of stack'''
2003 '''push or pop patches until named patch is at top of stack'''
2004 q = repo.mq
2004 q = repo.mq
2005 patch = q.lookup(patch)
2005 patch = q.lookup(patch)
2006 if q.isapplied(patch):
2006 if q.isapplied(patch):
2007 ret = q.pop(repo, patch, force=opts['force'])
2007 ret = q.pop(repo, patch, force=opts['force'])
2008 else:
2008 else:
2009 ret = q.push(repo, patch, force=opts['force'])
2009 ret = q.push(repo, patch, force=opts['force'])
2010 q.save_dirty()
2010 q.save_dirty()
2011 return ret
2011 return ret
2012
2012
2013 def guard(ui, repo, *args, **opts):
2013 def guard(ui, repo, *args, **opts):
2014 '''set or print guards for a patch
2014 '''set or print guards for a patch
2015
2015
2016 Guards control whether a patch can be pushed. A patch with no
2016 Guards control whether a patch can be pushed. A patch with no
2017 guards is always pushed. A patch with a positive guard ("+foo") is
2017 guards is always pushed. A patch with a positive guard ("+foo") is
2018 pushed only if the qselect command has activated it. A patch with
2018 pushed only if the qselect command has activated it. A patch with
2019 a negative guard ("-foo") is never pushed if the qselect command
2019 a negative guard ("-foo") is never pushed if the qselect command
2020 has activated it.
2020 has activated it.
2021
2021
2022 With no arguments, print the currently active guards.
2022 With no arguments, print the currently active guards.
2023 With arguments, set guards for the named patch.
2023 With arguments, set guards for the named patch.
2024 NOTE: Specifying negative guards now requires '--'.
2024 NOTE: Specifying negative guards now requires '--'.
2025
2025
2026 To set guards on another patch:
2026 To set guards on another patch:
2027 hg qguard -- other.patch +2.6.17 -stable
2027 hg qguard -- other.patch +2.6.17 -stable
2028 '''
2028 '''
2029 def status(idx):
2029 def status(idx):
2030 guards = q.series_guards[idx] or ['unguarded']
2030 guards = q.series_guards[idx] or ['unguarded']
2031 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
2031 ui.write('%s: %s\n' % (q.series[idx], ' '.join(guards)))
2032 q = repo.mq
2032 q = repo.mq
2033 patch = None
2033 patch = None
2034 args = list(args)
2034 args = list(args)
2035 if opts['list']:
2035 if opts['list']:
2036 if args or opts['none']:
2036 if args or opts['none']:
2037 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2037 raise util.Abort(_('cannot mix -l/--list with options or arguments'))
2038 for i in xrange(len(q.series)):
2038 for i in xrange(len(q.series)):
2039 status(i)
2039 status(i)
2040 return
2040 return
2041 if not args or args[0][0:1] in '-+':
2041 if not args or args[0][0:1] in '-+':
2042 if not q.applied:
2042 if not q.applied:
2043 raise util.Abort(_('no patches applied'))
2043 raise util.Abort(_('no patches applied'))
2044 patch = q.applied[-1].name
2044 patch = q.applied[-1].name
2045 if patch is None and args[0][0:1] not in '-+':
2045 if patch is None and args[0][0:1] not in '-+':
2046 patch = args.pop(0)
2046 patch = args.pop(0)
2047 if patch is None:
2047 if patch is None:
2048 raise util.Abort(_('no patch to work with'))
2048 raise util.Abort(_('no patch to work with'))
2049 if args or opts['none']:
2049 if args or opts['none']:
2050 idx = q.find_series(patch)
2050 idx = q.find_series(patch)
2051 if idx is None:
2051 if idx is None:
2052 raise util.Abort(_('no patch named %s') % patch)
2052 raise util.Abort(_('no patch named %s') % patch)
2053 q.set_guards(idx, args)
2053 q.set_guards(idx, args)
2054 q.save_dirty()
2054 q.save_dirty()
2055 else:
2055 else:
2056 status(q.series.index(q.lookup(patch)))
2056 status(q.series.index(q.lookup(patch)))
2057
2057
2058 def header(ui, repo, patch=None):
2058 def header(ui, repo, patch=None):
2059 """print the header of the topmost or specified patch"""
2059 """print the header of the topmost or specified patch"""
2060 q = repo.mq
2060 q = repo.mq
2061
2061
2062 if patch:
2062 if patch:
2063 patch = q.lookup(patch)
2063 patch = q.lookup(patch)
2064 else:
2064 else:
2065 if not q.applied:
2065 if not q.applied:
2066 ui.write('no patches applied\n')
2066 ui.write('no patches applied\n')
2067 return 1
2067 return 1
2068 patch = q.lookup('qtip')
2068 patch = q.lookup('qtip')
2069 ph = patchheader(repo.mq.join(patch))
2069 ph = patchheader(repo.mq.join(patch))
2070
2070
2071 ui.write('\n'.join(ph.message) + '\n')
2071 ui.write('\n'.join(ph.message) + '\n')
2072
2072
2073 def lastsavename(path):
2073 def lastsavename(path):
2074 (directory, base) = os.path.split(path)
2074 (directory, base) = os.path.split(path)
2075 names = os.listdir(directory)
2075 names = os.listdir(directory)
2076 namere = re.compile("%s.([0-9]+)" % base)
2076 namere = re.compile("%s.([0-9]+)" % base)
2077 maxindex = None
2077 maxindex = None
2078 maxname = None
2078 maxname = None
2079 for f in names:
2079 for f in names:
2080 m = namere.match(f)
2080 m = namere.match(f)
2081 if m:
2081 if m:
2082 index = int(m.group(1))
2082 index = int(m.group(1))
2083 if maxindex is None or index > maxindex:
2083 if maxindex is None or index > maxindex:
2084 maxindex = index
2084 maxindex = index
2085 maxname = f
2085 maxname = f
2086 if maxname:
2086 if maxname:
2087 return (os.path.join(directory, maxname), maxindex)
2087 return (os.path.join(directory, maxname), maxindex)
2088 return (None, None)
2088 return (None, None)
2089
2089
2090 def savename(path):
2090 def savename(path):
2091 (last, index) = lastsavename(path)
2091 (last, index) = lastsavename(path)
2092 if last is None:
2092 if last is None:
2093 index = 0
2093 index = 0
2094 newpath = path + ".%d" % (index + 1)
2094 newpath = path + ".%d" % (index + 1)
2095 return newpath
2095 return newpath
2096
2096
2097 def push(ui, repo, patch=None, **opts):
2097 def push(ui, repo, patch=None, **opts):
2098 """push the next patch onto the stack
2098 """push the next patch onto the stack
2099
2099
2100 When -f/--force is applied, all local changes in patched files
2100 When -f/--force is applied, all local changes in patched files
2101 will be lost.
2101 will be lost.
2102 """
2102 """
2103 q = repo.mq
2103 q = repo.mq
2104 mergeq = None
2104 mergeq = None
2105
2105
2106 if opts['merge']:
2106 if opts['merge']:
2107 if opts['name']:
2107 if opts['name']:
2108 newpath = repo.join(opts['name'])
2108 newpath = repo.join(opts['name'])
2109 else:
2109 else:
2110 newpath, i = lastsavename(q.path)
2110 newpath, i = lastsavename(q.path)
2111 if not newpath:
2111 if not newpath:
2112 ui.warn(_("no saved queues found, please use -n\n"))
2112 ui.warn(_("no saved queues found, please use -n\n"))
2113 return 1
2113 return 1
2114 mergeq = queue(ui, repo.join(""), newpath)
2114 mergeq = queue(ui, repo.join(""), newpath)
2115 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2115 ui.warn(_("merging with queue at: %s\n") % mergeq.path)
2116 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2116 ret = q.push(repo, patch, force=opts['force'], list=opts['list'],
2117 mergeq=mergeq, all=opts.get('all'))
2117 mergeq=mergeq, all=opts.get('all'))
2118 return ret
2118 return ret
2119
2119
2120 def pop(ui, repo, patch=None, **opts):
2120 def pop(ui, repo, patch=None, **opts):
2121 """pop the current patch off the stack
2121 """pop the current patch off the stack
2122
2122
2123 By default, pops off the top of the patch stack. If given a patch
2123 By default, pops off the top of the patch stack. If given a patch
2124 name, keeps popping off patches until the named patch is at the
2124 name, keeps popping off patches until the named patch is at the
2125 top of the stack.
2125 top of the stack.
2126 """
2126 """
2127 localupdate = True
2127 localupdate = True
2128 if opts['name']:
2128 if opts['name']:
2129 q = queue(ui, repo.join(""), repo.join(opts['name']))
2129 q = queue(ui, repo.join(""), repo.join(opts['name']))
2130 ui.warn(_('using patch queue: %s\n') % q.path)
2130 ui.warn(_('using patch queue: %s\n') % q.path)
2131 localupdate = False
2131 localupdate = False
2132 else:
2132 else:
2133 q = repo.mq
2133 q = repo.mq
2134 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2134 ret = q.pop(repo, patch, force=opts['force'], update=localupdate,
2135 all=opts['all'])
2135 all=opts['all'])
2136 q.save_dirty()
2136 q.save_dirty()
2137 return ret
2137 return ret
2138
2138
2139 def rename(ui, repo, patch, name=None, **opts):
2139 def rename(ui, repo, patch, name=None, **opts):
2140 """rename a patch
2140 """rename a patch
2141
2141
2142 With one argument, renames the current patch to PATCH1.
2142 With one argument, renames the current patch to PATCH1.
2143 With two arguments, renames PATCH1 to PATCH2."""
2143 With two arguments, renames PATCH1 to PATCH2."""
2144
2144
2145 q = repo.mq
2145 q = repo.mq
2146
2146
2147 if not name:
2147 if not name:
2148 name = patch
2148 name = patch
2149 patch = None
2149 patch = None
2150
2150
2151 if patch:
2151 if patch:
2152 patch = q.lookup(patch)
2152 patch = q.lookup(patch)
2153 else:
2153 else:
2154 if not q.applied:
2154 if not q.applied:
2155 ui.write(_('no patches applied\n'))
2155 ui.write(_('no patches applied\n'))
2156 return
2156 return
2157 patch = q.lookup('qtip')
2157 patch = q.lookup('qtip')
2158 absdest = q.join(name)
2158 absdest = q.join(name)
2159 if os.path.isdir(absdest):
2159 if os.path.isdir(absdest):
2160 name = normname(os.path.join(name, os.path.basename(patch)))
2160 name = normname(os.path.join(name, os.path.basename(patch)))
2161 absdest = q.join(name)
2161 absdest = q.join(name)
2162 if os.path.exists(absdest):
2162 if os.path.exists(absdest):
2163 raise util.Abort(_('%s already exists') % absdest)
2163 raise util.Abort(_('%s already exists') % absdest)
2164
2164
2165 if name in q.series:
2165 if name in q.series:
2166 raise util.Abort(_('A patch named %s already exists in the series file') % name)
2166 raise util.Abort(_('A patch named %s already exists in the series file') % name)
2167
2167
2168 if ui.verbose:
2168 if ui.verbose:
2169 ui.write('renaming %s to %s\n' % (patch, name))
2169 ui.write('renaming %s to %s\n' % (patch, name))
2170 i = q.find_series(patch)
2170 i = q.find_series(patch)
2171 guards = q.guard_re.findall(q.full_series[i])
2171 guards = q.guard_re.findall(q.full_series[i])
2172 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2172 q.full_series[i] = name + ''.join([' #' + g for g in guards])
2173 q.parse_series()
2173 q.parse_series()
2174 q.series_dirty = 1
2174 q.series_dirty = 1
2175
2175
2176 info = q.isapplied(patch)
2176 info = q.isapplied(patch)
2177 if info:
2177 if info:
2178 q.applied[info[0]] = statusentry(info[1], name)
2178 q.applied[info[0]] = statusentry(info[1], name)
2179 q.applied_dirty = 1
2179 q.applied_dirty = 1
2180
2180
2181 util.rename(q.join(patch), absdest)
2181 util.rename(q.join(patch), absdest)
2182 r = q.qrepo()
2182 r = q.qrepo()
2183 if r:
2183 if r:
2184 wlock = r.wlock()
2184 wlock = r.wlock()
2185 try:
2185 try:
2186 if r.dirstate[patch] == 'a':
2186 if r.dirstate[patch] == 'a':
2187 r.dirstate.forget(patch)
2187 r.dirstate.forget(patch)
2188 r.dirstate.add(name)
2188 r.dirstate.add(name)
2189 else:
2189 else:
2190 if r.dirstate[name] == 'r':
2190 if r.dirstate[name] == 'r':
2191 r.undelete([name])
2191 r.undelete([name])
2192 r.copy(patch, name)
2192 r.copy(patch, name)
2193 r.remove([patch], False)
2193 r.remove([patch], False)
2194 finally:
2194 finally:
2195 wlock.release()
2195 wlock.release()
2196
2196
2197 q.save_dirty()
2197 q.save_dirty()
2198
2198
2199 def restore(ui, repo, rev, **opts):
2199 def restore(ui, repo, rev, **opts):
2200 """restore the queue state saved by a revision"""
2200 """restore the queue state saved by a revision"""
2201 rev = repo.lookup(rev)
2201 rev = repo.lookup(rev)
2202 q = repo.mq
2202 q = repo.mq
2203 q.restore(repo, rev, delete=opts['delete'],
2203 q.restore(repo, rev, delete=opts['delete'],
2204 qupdate=opts['update'])
2204 qupdate=opts['update'])
2205 q.save_dirty()
2205 q.save_dirty()
2206 return 0
2206 return 0
2207
2207
2208 def save(ui, repo, **opts):
2208 def save(ui, repo, **opts):
2209 """save current queue state"""
2209 """save current queue state"""
2210 q = repo.mq
2210 q = repo.mq
2211 message = cmdutil.logmessage(opts)
2211 message = cmdutil.logmessage(opts)
2212 ret = q.save(repo, msg=message)
2212 ret = q.save(repo, msg=message)
2213 if ret:
2213 if ret:
2214 return ret
2214 return ret
2215 q.save_dirty()
2215 q.save_dirty()
2216 if opts['copy']:
2216 if opts['copy']:
2217 path = q.path
2217 path = q.path
2218 if opts['name']:
2218 if opts['name']:
2219 newpath = os.path.join(q.basepath, opts['name'])
2219 newpath = os.path.join(q.basepath, opts['name'])
2220 if os.path.exists(newpath):
2220 if os.path.exists(newpath):
2221 if not os.path.isdir(newpath):
2221 if not os.path.isdir(newpath):
2222 raise util.Abort(_('destination %s exists and is not '
2222 raise util.Abort(_('destination %s exists and is not '
2223 'a directory') % newpath)
2223 'a directory') % newpath)
2224 if not opts['force']:
2224 if not opts['force']:
2225 raise util.Abort(_('destination %s exists, '
2225 raise util.Abort(_('destination %s exists, '
2226 'use -f to force') % newpath)
2226 'use -f to force') % newpath)
2227 else:
2227 else:
2228 newpath = savename(path)
2228 newpath = savename(path)
2229 ui.warn(_("copy %s to %s\n") % (path, newpath))
2229 ui.warn(_("copy %s to %s\n") % (path, newpath))
2230 util.copyfiles(path, newpath)
2230 util.copyfiles(path, newpath)
2231 if opts['empty']:
2231 if opts['empty']:
2232 try:
2232 try:
2233 os.unlink(q.join(q.status_path))
2233 os.unlink(q.join(q.status_path))
2234 except:
2234 except:
2235 pass
2235 pass
2236 return 0
2236 return 0
2237
2237
2238 def strip(ui, repo, rev, **opts):
2238 def strip(ui, repo, rev, **opts):
2239 """strip a revision and all its descendants from the repository
2239 """strip a revision and all its descendants from the repository
2240
2240
2241 If one of the working directory's parent revisions is stripped, the
2241 If one of the working directory's parent revisions is stripped, the
2242 working directory will be updated to the parent of the stripped
2242 working directory will be updated to the parent of the stripped
2243 revision.
2243 revision.
2244 """
2244 """
2245 backup = 'all'
2245 backup = 'all'
2246 if opts['backup']:
2246 if opts['backup']:
2247 backup = 'strip'
2247 backup = 'strip'
2248 elif opts['nobackup']:
2248 elif opts['nobackup']:
2249 backup = 'none'
2249 backup = 'none'
2250
2250
2251 rev = repo.lookup(rev)
2251 rev = repo.lookup(rev)
2252 p = repo.dirstate.parents()
2252 p = repo.dirstate.parents()
2253 cl = repo.changelog
2253 cl = repo.changelog
2254 update = True
2254 update = True
2255 if p[0] == nullid:
2255 if p[0] == nullid:
2256 update = False
2256 update = False
2257 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2257 elif p[1] == nullid and rev != cl.ancestor(p[0], rev):
2258 update = False
2258 update = False
2259 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2259 elif rev not in (cl.ancestor(p[0], rev), cl.ancestor(p[1], rev)):
2260 update = False
2260 update = False
2261
2261
2262 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2262 repo.mq.strip(repo, rev, backup=backup, update=update, force=opts['force'])
2263 return 0
2263 return 0
2264
2264
2265 def select(ui, repo, *args, **opts):
2265 def select(ui, repo, *args, **opts):
2266 '''set or print guarded patches to push
2266 '''set or print guarded patches to push
2267
2267
2268 Use the qguard command to set or print guards on patch, then use
2268 Use the qguard command to set or print guards on patch, then use
2269 qselect to tell mq which guards to use. A patch will be pushed if
2269 qselect to tell mq which guards to use. A patch will be pushed if
2270 it has no guards or any positive guards match the currently
2270 it has no guards or any positive guards match the currently
2271 selected guard, but will not be pushed if any negative guards
2271 selected guard, but will not be pushed if any negative guards
2272 match the current guard. For example:
2272 match the current guard. For example:
2273
2273
2274 qguard foo.patch -stable (negative guard)
2274 qguard foo.patch -stable (negative guard)
2275 qguard bar.patch +stable (positive guard)
2275 qguard bar.patch +stable (positive guard)
2276 qselect stable
2276 qselect stable
2277
2277
2278 This activates the "stable" guard. mq will skip foo.patch (because
2278 This activates the "stable" guard. mq will skip foo.patch (because
2279 it has a negative match) but push bar.patch (because it has a
2279 it has a negative match) but push bar.patch (because it has a
2280 positive match).
2280 positive match).
2281
2281
2282 With no arguments, prints the currently active guards.
2282 With no arguments, prints the currently active guards.
2283 With one argument, sets the active guard.
2283 With one argument, sets the active guard.
2284
2284
2285 Use -n/--none to deactivate guards (no other arguments needed).
2285 Use -n/--none to deactivate guards (no other arguments needed).
2286 When no guards are active, patches with positive guards are
2286 When no guards are active, patches with positive guards are
2287 skipped and patches with negative guards are pushed.
2287 skipped and patches with negative guards are pushed.
2288
2288
2289 qselect can change the guards on applied patches. It does not pop
2289 qselect can change the guards on applied patches. It does not pop
2290 guarded patches by default. Use --pop to pop back to the last
2290 guarded patches by default. Use --pop to pop back to the last
2291 applied patch that is not guarded. Use --reapply (which implies
2291 applied patch that is not guarded. Use --reapply (which implies
2292 --pop) to push back to the current patch afterwards, but skip
2292 --pop) to push back to the current patch afterwards, but skip
2293 guarded patches.
2293 guarded patches.
2294
2294
2295 Use -s/--series to print a list of all guards in the series file
2295 Use -s/--series to print a list of all guards in the series file
2296 (no other arguments needed). Use -v for more information.'''
2296 (no other arguments needed). Use -v for more information.'''
2297
2297
2298 q = repo.mq
2298 q = repo.mq
2299 guards = q.active()
2299 guards = q.active()
2300 if args or opts['none']:
2300 if args or opts['none']:
2301 old_unapplied = q.unapplied(repo)
2301 old_unapplied = q.unapplied(repo)
2302 old_guarded = [i for i in xrange(len(q.applied)) if
2302 old_guarded = [i for i in xrange(len(q.applied)) if
2303 not q.pushable(i)[0]]
2303 not q.pushable(i)[0]]
2304 q.set_active(args)
2304 q.set_active(args)
2305 q.save_dirty()
2305 q.save_dirty()
2306 if not args:
2306 if not args:
2307 ui.status(_('guards deactivated\n'))
2307 ui.status(_('guards deactivated\n'))
2308 if not opts['pop'] and not opts['reapply']:
2308 if not opts['pop'] and not opts['reapply']:
2309 unapplied = q.unapplied(repo)
2309 unapplied = q.unapplied(repo)
2310 guarded = [i for i in xrange(len(q.applied))
2310 guarded = [i for i in xrange(len(q.applied))
2311 if not q.pushable(i)[0]]
2311 if not q.pushable(i)[0]]
2312 if len(unapplied) != len(old_unapplied):
2312 if len(unapplied) != len(old_unapplied):
2313 ui.status(_('number of unguarded, unapplied patches has '
2313 ui.status(_('number of unguarded, unapplied patches has '
2314 'changed from %d to %d\n') %
2314 'changed from %d to %d\n') %
2315 (len(old_unapplied), len(unapplied)))
2315 (len(old_unapplied), len(unapplied)))
2316 if len(guarded) != len(old_guarded):
2316 if len(guarded) != len(old_guarded):
2317 ui.status(_('number of guarded, applied patches has changed '
2317 ui.status(_('number of guarded, applied patches has changed '
2318 'from %d to %d\n') %
2318 'from %d to %d\n') %
2319 (len(old_guarded), len(guarded)))
2319 (len(old_guarded), len(guarded)))
2320 elif opts['series']:
2320 elif opts['series']:
2321 guards = {}
2321 guards = {}
2322 noguards = 0
2322 noguards = 0
2323 for gs in q.series_guards:
2323 for gs in q.series_guards:
2324 if not gs:
2324 if not gs:
2325 noguards += 1
2325 noguards += 1
2326 for g in gs:
2326 for g in gs:
2327 guards.setdefault(g, 0)
2327 guards.setdefault(g, 0)
2328 guards[g] += 1
2328 guards[g] += 1
2329 if ui.verbose:
2329 if ui.verbose:
2330 guards['NONE'] = noguards
2330 guards['NONE'] = noguards
2331 guards = guards.items()
2331 guards = guards.items()
2332 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2332 guards.sort(lambda a, b: cmp(a[0][1:], b[0][1:]))
2333 if guards:
2333 if guards:
2334 ui.note(_('guards in series file:\n'))
2334 ui.note(_('guards in series file:\n'))
2335 for guard, count in guards:
2335 for guard, count in guards:
2336 ui.note('%2d ' % count)
2336 ui.note('%2d ' % count)
2337 ui.write(guard, '\n')
2337 ui.write(guard, '\n')
2338 else:
2338 else:
2339 ui.note(_('no guards in series file\n'))
2339 ui.note(_('no guards in series file\n'))
2340 else:
2340 else:
2341 if guards:
2341 if guards:
2342 ui.note(_('active guards:\n'))
2342 ui.note(_('active guards:\n'))
2343 for g in guards:
2343 for g in guards:
2344 ui.write(g, '\n')
2344 ui.write(g, '\n')
2345 else:
2345 else:
2346 ui.write(_('no active guards\n'))
2346 ui.write(_('no active guards\n'))
2347 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2347 reapply = opts['reapply'] and q.applied and q.appliedname(-1)
2348 popped = False
2348 popped = False
2349 if opts['pop'] or opts['reapply']:
2349 if opts['pop'] or opts['reapply']:
2350 for i in xrange(len(q.applied)):
2350 for i in xrange(len(q.applied)):
2351 pushable, reason = q.pushable(i)
2351 pushable, reason = q.pushable(i)
2352 if not pushable:
2352 if not pushable:
2353 ui.status(_('popping guarded patches\n'))
2353 ui.status(_('popping guarded patches\n'))
2354 popped = True
2354 popped = True
2355 if i == 0:
2355 if i == 0:
2356 q.pop(repo, all=True)
2356 q.pop(repo, all=True)
2357 else:
2357 else:
2358 q.pop(repo, i-1)
2358 q.pop(repo, i-1)
2359 break
2359 break
2360 if popped:
2360 if popped:
2361 try:
2361 try:
2362 if reapply:
2362 if reapply:
2363 ui.status(_('reapplying unguarded patches\n'))
2363 ui.status(_('reapplying unguarded patches\n'))
2364 q.push(repo, reapply)
2364 q.push(repo, reapply)
2365 finally:
2365 finally:
2366 q.save_dirty()
2366 q.save_dirty()
2367
2367
2368 def finish(ui, repo, *revrange, **opts):
2368 def finish(ui, repo, *revrange, **opts):
2369 """move applied patches into repository history
2369 """move applied patches into repository history
2370
2370
2371 Finishes the specified revisions (corresponding to applied
2371 Finishes the specified revisions (corresponding to applied
2372 patches) by moving them out of mq control into regular repository
2372 patches) by moving them out of mq control into regular repository
2373 history.
2373 history.
2374
2374
2375 Accepts a revision range or the -a/--applied option. If --applied
2375 Accepts a revision range or the -a/--applied option. If --applied
2376 is specified, all applied mq revisions are removed from mq
2376 is specified, all applied mq revisions are removed from mq
2377 control. Otherwise, the given revisions must be at the base of the
2377 control. Otherwise, the given revisions must be at the base of the
2378 stack of applied patches.
2378 stack of applied patches.
2379
2379
2380 This can be especially useful if your changes have been applied to
2380 This can be especially useful if your changes have been applied to
2381 an upstream repository, or if you are about to push your changes
2381 an upstream repository, or if you are about to push your changes
2382 to upstream.
2382 to upstream.
2383 """
2383 """
2384 if not opts['applied'] and not revrange:
2384 if not opts['applied'] and not revrange:
2385 raise util.Abort(_('no revisions specified'))
2385 raise util.Abort(_('no revisions specified'))
2386 elif opts['applied']:
2386 elif opts['applied']:
2387 revrange = ('qbase:qtip',) + revrange
2387 revrange = ('qbase:qtip',) + revrange
2388
2388
2389 q = repo.mq
2389 q = repo.mq
2390 if not q.applied:
2390 if not q.applied:
2391 ui.status(_('no patches applied\n'))
2391 ui.status(_('no patches applied\n'))
2392 return 0
2392 return 0
2393
2393
2394 revs = cmdutil.revrange(repo, revrange)
2394 revs = cmdutil.revrange(repo, revrange)
2395 q.finish(repo, revs)
2395 q.finish(repo, revs)
2396 q.save_dirty()
2396 q.save_dirty()
2397 return 0
2397 return 0
2398
2398
2399 def reposetup(ui, repo):
2399 def reposetup(ui, repo):
2400 class mqrepo(repo.__class__):
2400 class mqrepo(repo.__class__):
2401 @util.propertycache
2401 @util.propertycache
2402 def mq(self):
2402 def mq(self):
2403 return queue(self.ui, self.join(""))
2403 return queue(self.ui, self.join(""))
2404
2404
2405 def abort_if_wdir_patched(self, errmsg, force=False):
2405 def abort_if_wdir_patched(self, errmsg, force=False):
2406 if self.mq.applied and not force:
2406 if self.mq.applied and not force:
2407 parent = hex(self.dirstate.parents()[0])
2407 parent = hex(self.dirstate.parents()[0])
2408 if parent in [s.rev for s in self.mq.applied]:
2408 if parent in [s.rev for s in self.mq.applied]:
2409 raise util.Abort(errmsg)
2409 raise util.Abort(errmsg)
2410
2410
2411 def commit(self, text="", user=None, date=None, match=None,
2411 def commit(self, text="", user=None, date=None, match=None,
2412 force=False, editor=False, extra={}):
2412 force=False, editor=False, extra={}):
2413 self.abort_if_wdir_patched(
2413 self.abort_if_wdir_patched(
2414 _('cannot commit over an applied mq patch'),
2414 _('cannot commit over an applied mq patch'),
2415 force)
2415 force)
2416
2416
2417 return super(mqrepo, self).commit(text, user, date, match, force,
2417 return super(mqrepo, self).commit(text, user, date, match, force,
2418 editor, extra)
2418 editor, extra)
2419
2419
2420 def push(self, remote, force=False, revs=None):
2420 def push(self, remote, force=False, revs=None):
2421 if self.mq.applied and not force and not revs:
2421 if self.mq.applied and not force and not revs:
2422 raise util.Abort(_('source has mq patches applied'))
2422 raise util.Abort(_('source has mq patches applied'))
2423 return super(mqrepo, self).push(remote, force, revs)
2423 return super(mqrepo, self).push(remote, force, revs)
2424
2424
2425 def tags(self):
2425 def tags(self):
2426 if self.tagscache:
2426 if self.tagscache:
2427 return self.tagscache
2427 return self.tagscache
2428
2428
2429 tagscache = super(mqrepo, self).tags()
2429 tagscache = super(mqrepo, self).tags()
2430
2430
2431 q = self.mq
2431 q = self.mq
2432 if not q.applied:
2432 if not q.applied:
2433 return tagscache
2433 return tagscache
2434
2434
2435 mqtags = [(bin(patch.rev), patch.name) for patch in q.applied]
2435 mqtags = [(bin(patch.rev), patch.name) for patch in q.applied]
2436
2436
2437 if mqtags[-1][0] not in self.changelog.nodemap:
2437 if mqtags[-1][0] not in self.changelog.nodemap:
2438 self.ui.warn(_('mq status file refers to unknown node %s\n')
2438 self.ui.warn(_('mq status file refers to unknown node %s\n')
2439 % short(mqtags[-1][0]))
2439 % short(mqtags[-1][0]))
2440 return tagscache
2440 return tagscache
2441
2441
2442 mqtags.append((mqtags[-1][0], 'qtip'))
2442 mqtags.append((mqtags[-1][0], 'qtip'))
2443 mqtags.append((mqtags[0][0], 'qbase'))
2443 mqtags.append((mqtags[0][0], 'qbase'))
2444 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2444 mqtags.append((self.changelog.parents(mqtags[0][0])[0], 'qparent'))
2445 for patch in mqtags:
2445 for patch in mqtags:
2446 if patch[1] in tagscache:
2446 if patch[1] in tagscache:
2447 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2447 self.ui.warn(_('Tag %s overrides mq patch of the same name\n')
2448 % patch[1])
2448 % patch[1])
2449 else:
2449 else:
2450 tagscache[patch[1]] = patch[0]
2450 tagscache[patch[1]] = patch[0]
2451
2451
2452 return tagscache
2452 return tagscache
2453
2453
2454 def _branchtags(self, partial, lrev):
2454 def _branchtags(self, partial, lrev):
2455 q = self.mq
2455 q = self.mq
2456 if not q.applied:
2456 if not q.applied:
2457 return super(mqrepo, self)._branchtags(partial, lrev)
2457 return super(mqrepo, self)._branchtags(partial, lrev)
2458
2458
2459 cl = self.changelog
2459 cl = self.changelog
2460 qbasenode = bin(q.applied[0].rev)
2460 qbasenode = bin(q.applied[0].rev)
2461 if qbasenode not in cl.nodemap:
2461 if qbasenode not in cl.nodemap:
2462 self.ui.warn(_('mq status file refers to unknown node %s\n')
2462 self.ui.warn(_('mq status file refers to unknown node %s\n')
2463 % short(qbasenode))
2463 % short(qbasenode))
2464 return super(mqrepo, self)._branchtags(partial, lrev)
2464 return super(mqrepo, self)._branchtags(partial, lrev)
2465
2465
2466 qbase = cl.rev(qbasenode)
2466 qbase = cl.rev(qbasenode)
2467 start = lrev + 1
2467 start = lrev + 1
2468 if start < qbase:
2468 if start < qbase:
2469 # update the cache (excluding the patches) and save it
2469 # update the cache (excluding the patches) and save it
2470 self._updatebranchcache(partial, lrev+1, qbase)
2470 self._updatebranchcache(partial, lrev+1, qbase)
2471 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2471 self._writebranchcache(partial, cl.node(qbase-1), qbase-1)
2472 start = qbase
2472 start = qbase
2473 # if start = qbase, the cache is as updated as it should be.
2473 # if start = qbase, the cache is as updated as it should be.
2474 # if start > qbase, the cache includes (part of) the patches.
2474 # if start > qbase, the cache includes (part of) the patches.
2475 # we might as well use it, but we won't save it.
2475 # we might as well use it, but we won't save it.
2476
2476
2477 # update the cache up to the tip
2477 # update the cache up to the tip
2478 self._updatebranchcache(partial, start, len(cl))
2478 self._updatebranchcache(partial, start, len(cl))
2479
2479
2480 return partial
2480 return partial
2481
2481
2482 if repo.local():
2482 if repo.local():
2483 repo.__class__ = mqrepo
2483 repo.__class__ = mqrepo
2484
2484
2485 def mqimport(orig, ui, repo, *args, **kwargs):
2485 def mqimport(orig, ui, repo, *args, **kwargs):
2486 if hasattr(repo, 'abort_if_wdir_patched'):
2486 if hasattr(repo, 'abort_if_wdir_patched'):
2487 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2487 repo.abort_if_wdir_patched(_('cannot import over an applied patch'),
2488 kwargs.get('force'))
2488 kwargs.get('force'))
2489 return orig(ui, repo, *args, **kwargs)
2489 return orig(ui, repo, *args, **kwargs)
2490
2490
2491 def uisetup(ui):
2491 def uisetup(ui):
2492 extensions.wrapcommand(commands.table, 'import', mqimport)
2492 extensions.wrapcommand(commands.table, 'import', mqimport)
2493
2493
2494 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2494 seriesopts = [('s', 'summary', None, _('print first line of patch header'))]
2495
2495
2496 cmdtable = {
2496 cmdtable = {
2497 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2497 "qapplied": (applied, [] + seriesopts, _('hg qapplied [-s] [PATCH]')),
2498 "qclone":
2498 "qclone":
2499 (clone,
2499 (clone,
2500 [('', 'pull', None, _('use pull protocol to copy metadata')),
2500 [('', 'pull', None, _('use pull protocol to copy metadata')),
2501 ('U', 'noupdate', None, _('do not update the new working directories')),
2501 ('U', 'noupdate', None, _('do not update the new working directories')),
2502 ('', 'uncompressed', None,
2502 ('', 'uncompressed', None,
2503 _('use uncompressed transfer (fast over LAN)')),
2503 _('use uncompressed transfer (fast over LAN)')),
2504 ('p', 'patches', '', _('location of source patch repository')),
2504 ('p', 'patches', '', _('location of source patch repository')),
2505 ] + commands.remoteopts,
2505 ] + commands.remoteopts,
2506 _('hg qclone [OPTION]... SOURCE [DEST]')),
2506 _('hg qclone [OPTION]... SOURCE [DEST]')),
2507 "qcommit|qci":
2507 "qcommit|qci":
2508 (commit,
2508 (commit,
2509 commands.table["^commit|ci"][1],
2509 commands.table["^commit|ci"][1],
2510 _('hg qcommit [OPTION]... [FILE]...')),
2510 _('hg qcommit [OPTION]... [FILE]...')),
2511 "^qdiff":
2511 "^qdiff":
2512 (diff,
2512 (diff,
2513 commands.diffopts + commands.diffopts2 + commands.walkopts,
2513 commands.diffopts + commands.diffopts2 + commands.walkopts,
2514 _('hg qdiff [OPTION]... [FILE]...')),
2514 _('hg qdiff [OPTION]... [FILE]...')),
2515 "qdelete|qremove|qrm":
2515 "qdelete|qremove|qrm":
2516 (delete,
2516 (delete,
2517 [('k', 'keep', None, _('keep patch file')),
2517 [('k', 'keep', None, _('keep patch file')),
2518 ('r', 'rev', [], _('stop managing a revision (DEPRECATED)'))],
2518 ('r', 'rev', [], _('stop managing a revision (DEPRECATED)'))],
2519 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2519 _('hg qdelete [-k] [-r REV]... [PATCH]...')),
2520 'qfold':
2520 'qfold':
2521 (fold,
2521 (fold,
2522 [('e', 'edit', None, _('edit patch header')),
2522 [('e', 'edit', None, _('edit patch header')),
2523 ('k', 'keep', None, _('keep folded patch files')),
2523 ('k', 'keep', None, _('keep folded patch files')),
2524 ] + commands.commitopts,
2524 ] + commands.commitopts,
2525 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2525 _('hg qfold [-e] [-k] [-m TEXT] [-l FILE] PATCH...')),
2526 'qgoto':
2526 'qgoto':
2527 (goto,
2527 (goto,
2528 [('f', 'force', None, _('overwrite any local changes'))],
2528 [('f', 'force', None, _('overwrite any local changes'))],
2529 _('hg qgoto [OPTION]... PATCH')),
2529 _('hg qgoto [OPTION]... PATCH')),
2530 'qguard':
2530 'qguard':
2531 (guard,
2531 (guard,
2532 [('l', 'list', None, _('list all patches and guards')),
2532 [('l', 'list', None, _('list all patches and guards')),
2533 ('n', 'none', None, _('drop all guards'))],
2533 ('n', 'none', None, _('drop all guards'))],
2534 _('hg qguard [-l] [-n] -- [PATCH] [+GUARD]... [-GUARD]...')),
2534 _('hg qguard [-l] [-n] -- [PATCH] [+GUARD]... [-GUARD]...')),
2535 'qheader': (header, [], _('hg qheader [PATCH]')),
2535 'qheader': (header, [], _('hg qheader [PATCH]')),
2536 "^qimport":
2536 "^qimport":
2537 (qimport,
2537 (qimport,
2538 [('e', 'existing', None, _('import file in patch directory')),
2538 [('e', 'existing', None, _('import file in patch directory')),
2539 ('n', 'name', '', _('name of patch file')),
2539 ('n', 'name', '', _('name of patch file')),
2540 ('f', 'force', None, _('overwrite existing files')),
2540 ('f', 'force', None, _('overwrite existing files')),
2541 ('r', 'rev', [], _('place existing revisions under mq control')),
2541 ('r', 'rev', [], _('place existing revisions under mq control')),
2542 ('g', 'git', None, _('use git extended diff format')),
2542 ('g', 'git', None, _('use git extended diff format')),
2543 ('P', 'push', None, _('qpush after importing'))],
2543 ('P', 'push', None, _('qpush after importing'))],
2544 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2544 _('hg qimport [-e] [-n NAME] [-f] [-g] [-P] [-r REV]... FILE...')),
2545 "^qinit":
2545 "^qinit":
2546 (init,
2546 (init,
2547 [('c', 'create-repo', None, _('create queue repository'))],
2547 [('c', 'create-repo', None, _('create queue repository'))],
2548 _('hg qinit [-c]')),
2548 _('hg qinit [-c]')),
2549 "qnew":
2549 "qnew":
2550 (new,
2550 (new,
2551 [('e', 'edit', None, _('edit commit message')),
2551 [('e', 'edit', None, _('edit commit message')),
2552 ('f', 'force', None, _('import uncommitted changes into patch')),
2552 ('f', 'force', None, _('import uncommitted changes into patch')),
2553 ('g', 'git', None, _('use git extended diff format')),
2553 ('g', 'git', None, _('use git extended diff format')),
2554 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2554 ('U', 'currentuser', None, _('add "From: <current user>" to patch')),
2555 ('u', 'user', '', _('add "From: <given user>" to patch')),
2555 ('u', 'user', '', _('add "From: <given user>" to patch')),
2556 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2556 ('D', 'currentdate', None, _('add "Date: <current date>" to patch')),
2557 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2557 ('d', 'date', '', _('add "Date: <given date>" to patch'))
2558 ] + commands.walkopts + commands.commitopts,
2558 ] + commands.walkopts + commands.commitopts,
2559 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2559 _('hg qnew [-e] [-m TEXT] [-l FILE] [-f] PATCH [FILE]...')),
2560 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2560 "qnext": (next, [] + seriesopts, _('hg qnext [-s]')),
2561 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2561 "qprev": (prev, [] + seriesopts, _('hg qprev [-s]')),
2562 "^qpop":
2562 "^qpop":
2563 (pop,
2563 (pop,
2564 [('a', 'all', None, _('pop all patches')),
2564 [('a', 'all', None, _('pop all patches')),
2565 ('n', 'name', '', _('queue name to pop')),
2565 ('n', 'name', '', _('queue name to pop')),
2566 ('f', 'force', None, _('forget any local changes'))],
2566 ('f', 'force', None, _('forget any local changes'))],
2567 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2567 _('hg qpop [-a] [-n NAME] [-f] [PATCH | INDEX]')),
2568 "^qpush":
2568 "^qpush":
2569 (push,
2569 (push,
2570 [('f', 'force', None, _('apply if the patch has rejects')),
2570 [('f', 'force', None, _('apply if the patch has rejects')),
2571 ('l', 'list', None, _('list patch name in commit text')),
2571 ('l', 'list', None, _('list patch name in commit text')),
2572 ('a', 'all', None, _('apply all patches')),
2572 ('a', 'all', None, _('apply all patches')),
2573 ('m', 'merge', None, _('merge from another queue')),
2573 ('m', 'merge', None, _('merge from another queue')),
2574 ('n', 'name', '', _('merge queue name'))],
2574 ('n', 'name', '', _('merge queue name'))],
2575 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2575 _('hg qpush [-f] [-l] [-a] [-m] [-n NAME] [PATCH | INDEX]')),
2576 "^qrefresh":
2576 "^qrefresh":
2577 (refresh,
2577 (refresh,
2578 [('e', 'edit', None, _('edit commit message')),
2578 [('e', 'edit', None, _('edit commit message')),
2579 ('g', 'git', None, _('use git extended diff format')),
2579 ('g', 'git', None, _('use git extended diff format')),
2580 ('s', 'short', None, _('refresh only files already in the patch and specified files')),
2580 ('s', 'short', None, _('refresh only files already in the patch and specified files')),
2581 ('U', 'currentuser', None, _('add/update "From: <current user>" in patch')),
2581 ('U', 'currentuser', None, _('add/update "From: <current user>" in patch')),
2582 ('u', 'user', '', _('add/update "From: <given user>" in patch')),
2582 ('u', 'user', '', _('add/update "From: <given user>" in patch')),
2583 ('D', 'currentdate', None, _('update "Date: <current date>" in patch (if present)')),
2583 ('D', 'currentdate', None, _('update "Date: <current date>" in patch (if present)')),
2584 ('d', 'date', '', _('update "Date: <given date>" in patch (if present)'))
2584 ('d', 'date', '', _('update "Date: <given date>" in patch (if present)'))
2585 ] + commands.walkopts + commands.commitopts,
2585 ] + commands.walkopts + commands.commitopts,
2586 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2586 _('hg qrefresh [-I] [-X] [-e] [-m TEXT] [-l FILE] [-s] [FILE]...')),
2587 'qrename|qmv':
2587 'qrename|qmv':
2588 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2588 (rename, [], _('hg qrename PATCH1 [PATCH2]')),
2589 "qrestore":
2589 "qrestore":
2590 (restore,
2590 (restore,
2591 [('d', 'delete', None, _('delete save entry')),
2591 [('d', 'delete', None, _('delete save entry')),
2592 ('u', 'update', None, _('update queue working directory'))],
2592 ('u', 'update', None, _('update queue working directory'))],
2593 _('hg qrestore [-d] [-u] REV')),
2593 _('hg qrestore [-d] [-u] REV')),
2594 "qsave":
2594 "qsave":
2595 (save,
2595 (save,
2596 [('c', 'copy', None, _('copy patch directory')),
2596 [('c', 'copy', None, _('copy patch directory')),
2597 ('n', 'name', '', _('copy directory name')),
2597 ('n', 'name', '', _('copy directory name')),
2598 ('e', 'empty', None, _('clear queue status file')),
2598 ('e', 'empty', None, _('clear queue status file')),
2599 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2599 ('f', 'force', None, _('force copy'))] + commands.commitopts,
2600 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2600 _('hg qsave [-m TEXT] [-l FILE] [-c] [-n NAME] [-e] [-f]')),
2601 "qselect":
2601 "qselect":
2602 (select,
2602 (select,
2603 [('n', 'none', None, _('disable all guards')),
2603 [('n', 'none', None, _('disable all guards')),
2604 ('s', 'series', None, _('list all guards in series file')),
2604 ('s', 'series', None, _('list all guards in series file')),
2605 ('', 'pop', None, _('pop to before first guarded applied patch')),
2605 ('', 'pop', None, _('pop to before first guarded applied patch')),
2606 ('', 'reapply', None, _('pop, then reapply patches'))],
2606 ('', 'reapply', None, _('pop, then reapply patches'))],
2607 _('hg qselect [OPTION]... [GUARD]...')),
2607 _('hg qselect [OPTION]... [GUARD]...')),
2608 "qseries":
2608 "qseries":
2609 (series,
2609 (series,
2610 [('m', 'missing', None, _('print patches not in series')),
2610 [('m', 'missing', None, _('print patches not in series')),
2611 ] + seriesopts,
2611 ] + seriesopts,
2612 _('hg qseries [-ms]')),
2612 _('hg qseries [-ms]')),
2613 "^strip":
2613 "^strip":
2614 (strip,
2614 (strip,
2615 [('f', 'force', None, _('force removal with local changes')),
2615 [('f', 'force', None, _('force removal with local changes')),
2616 ('b', 'backup', None, _('bundle unrelated changesets')),
2616 ('b', 'backup', None, _('bundle unrelated changesets')),
2617 ('n', 'nobackup', None, _('no backups'))],
2617 ('n', 'nobackup', None, _('no backups'))],
2618 _('hg strip [-f] [-b] [-n] REV')),
2618 _('hg strip [-f] [-b] [-n] REV')),
2619 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2619 "qtop": (top, [] + seriesopts, _('hg qtop [-s]')),
2620 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2620 "qunapplied": (unapplied, [] + seriesopts, _('hg qunapplied [-s] [PATCH]')),
2621 "qfinish":
2621 "qfinish":
2622 (finish,
2622 (finish,
2623 [('a', 'applied', None, _('finish all applied changesets'))],
2623 [('a', 'applied', None, _('finish all applied changesets'))],
2624 _('hg qfinish [-a] [REV...]')),
2624 _('hg qfinish [-a] [REV...]')),
2625 }
2625 }
@@ -1,120 +1,120 b''
1 # win32mbcs.py -- MBCS filename support for Mercurial
1 # win32mbcs.py -- MBCS filename support for Mercurial
2 #
2 #
3 # Copyright (c) 2008 Shun-ichi Goto <shunichi.goto@gmail.com>
3 # Copyright (c) 2008 Shun-ichi Goto <shunichi.goto@gmail.com>
4 #
4 #
5 # Version: 0.2
5 # Version: 0.2
6 # Author: Shun-ichi Goto <shunichi.goto@gmail.com>
6 # Author: Shun-ichi Goto <shunichi.goto@gmail.com>
7 #
7 #
8 # This software may be used and distributed according to the terms of the
8 # This software may be used and distributed according to the terms of the
9 # GNU General Public License version 2, incorporated herein by reference.
9 # GNU General Public License version 2, incorporated herein by reference.
10 #
10 #
11
11
12 '''allow the use of MBCS paths with problematic encoding
12 '''allow the use of MBCS paths with problematic encodings
13
13
14 Some MBCS encodings are not good for some path operations (i.e.
14 Some MBCS encodings are not good for some path operations (i.e.
15 splitting path, case conversion, etc.) with its encoded bytes. We call
15 splitting path, case conversion, etc.) with its encoded bytes. We call
16 such a encoding (i.e. shift_jis and big5) as "problematic encoding".
16 such a encoding (i.e. shift_jis and big5) as "problematic encoding".
17 This extension can be used to fix the issue with those encodings by
17 This extension can be used to fix the issue with those encodings by
18 wrapping some functions to convert to Unicode string before path
18 wrapping some functions to convert to Unicode string before path
19 operation.
19 operation.
20
20
21 This extension is useful for:
21 This extension is useful for:
22 * Japanese Windows users using shift_jis encoding.
22 * Japanese Windows users using shift_jis encoding.
23 * Chinese Windows users using big5 encoding.
23 * Chinese Windows users using big5 encoding.
24 * All users who use a repository with one of problematic encodings on
24 * All users who use a repository with one of problematic encodings on
25 case-insensitive file system.
25 case-insensitive file system.
26
26
27 This extension is not needed for:
27 This extension is not needed for:
28 * Any user who use only ASCII chars in path.
28 * Any user who use only ASCII chars in path.
29 * Any user who do not use any of problematic encodings.
29 * Any user who do not use any of problematic encodings.
30
30
31 Note that there are some limitations on using this extension:
31 Note that there are some limitations on using this extension:
32 * You should use single encoding in one repository.
32 * You should use single encoding in one repository.
33 * You should set same encoding for the repository by locale or
33 * You should set same encoding for the repository by locale or
34 HGENCODING.
34 HGENCODING.
35
35
36 Path encoding conversion are done between Unicode and
36 Path encoding conversion are done between Unicode and
37 encoding.encoding which is decided by Mercurial from current locale
37 encoding.encoding which is decided by Mercurial from current locale
38 setting or HGENCODING.
38 setting or HGENCODING.
39 '''
39 '''
40
40
41 import os
41 import os
42 from mercurial.i18n import _
42 from mercurial.i18n import _
43 from mercurial import util, encoding
43 from mercurial import util, encoding
44
44
45 def decode(arg):
45 def decode(arg):
46 if isinstance(arg, str):
46 if isinstance(arg, str):
47 uarg = arg.decode(encoding.encoding)
47 uarg = arg.decode(encoding.encoding)
48 if arg == uarg.encode(encoding.encoding):
48 if arg == uarg.encode(encoding.encoding):
49 return uarg
49 return uarg
50 raise UnicodeError("Not local encoding")
50 raise UnicodeError("Not local encoding")
51 elif isinstance(arg, tuple):
51 elif isinstance(arg, tuple):
52 return tuple(map(decode, arg))
52 return tuple(map(decode, arg))
53 elif isinstance(arg, list):
53 elif isinstance(arg, list):
54 return map(decode, arg)
54 return map(decode, arg)
55 return arg
55 return arg
56
56
57 def encode(arg):
57 def encode(arg):
58 if isinstance(arg, unicode):
58 if isinstance(arg, unicode):
59 return arg.encode(encoding.encoding)
59 return arg.encode(encoding.encoding)
60 elif isinstance(arg, tuple):
60 elif isinstance(arg, tuple):
61 return tuple(map(encode, arg))
61 return tuple(map(encode, arg))
62 elif isinstance(arg, list):
62 elif isinstance(arg, list):
63 return map(encode, arg)
63 return map(encode, arg)
64 return arg
64 return arg
65
65
66 def wrapper(func, args):
66 def wrapper(func, args):
67 # check argument is unicode, then call original
67 # check argument is unicode, then call original
68 for arg in args:
68 for arg in args:
69 if isinstance(arg, unicode):
69 if isinstance(arg, unicode):
70 return func(*args)
70 return func(*args)
71
71
72 try:
72 try:
73 # convert arguments to unicode, call func, then convert back
73 # convert arguments to unicode, call func, then convert back
74 return encode(func(*decode(args)))
74 return encode(func(*decode(args)))
75 except UnicodeError:
75 except UnicodeError:
76 # If not encoded with encoding.encoding, report it then
76 # If not encoded with encoding.encoding, report it then
77 # continue with calling original function.
77 # continue with calling original function.
78 raise util.Abort(_("[win32mbcs] filename conversion fail with"
78 raise util.Abort(_("[win32mbcs] filename conversion fail with"
79 " %s encoding\n") % (encoding.encoding))
79 " %s encoding\n") % (encoding.encoding))
80
80
81 def wrapname(name):
81 def wrapname(name):
82 idx = name.rfind('.')
82 idx = name.rfind('.')
83 module = name[:idx]
83 module = name[:idx]
84 name = name[idx+1:]
84 name = name[idx+1:]
85 module = globals()[module]
85 module = globals()[module]
86 func = getattr(module, name)
86 func = getattr(module, name)
87 def f(*args):
87 def f(*args):
88 return wrapper(func, args)
88 return wrapper(func, args)
89 try:
89 try:
90 f.__name__ = func.__name__ # fail with python23
90 f.__name__ = func.__name__ # fail with python23
91 except Exception:
91 except Exception:
92 pass
92 pass
93 setattr(module, name, f)
93 setattr(module, name, f)
94
94
95 # List of functions to be wrapped.
95 # List of functions to be wrapped.
96 # NOTE: os.path.dirname() and os.path.basename() are safe because
96 # NOTE: os.path.dirname() and os.path.basename() are safe because
97 # they use result of os.path.split()
97 # they use result of os.path.split()
98 funcs = '''os.path.join os.path.split os.path.splitext
98 funcs = '''os.path.join os.path.split os.path.splitext
99 os.path.splitunc os.path.normpath os.path.normcase os.makedirs
99 os.path.splitunc os.path.normpath os.path.normcase os.makedirs
100 util.endswithsep util.splitpath util.checkcase util.fspath'''
100 util.endswithsep util.splitpath util.checkcase util.fspath'''
101
101
102 # codec and alias names of sjis and big5 to be faked.
102 # codec and alias names of sjis and big5 to be faked.
103 problematic_encodings = '''big5 big5-tw csbig5 big5hkscs big5-hkscs
103 problematic_encodings = '''big5 big5-tw csbig5 big5hkscs big5-hkscs
104 hkscs cp932 932 ms932 mskanji ms-kanji shift_jis csshiftjis shiftjis
104 hkscs cp932 932 ms932 mskanji ms-kanji shift_jis csshiftjis shiftjis
105 sjis s_jis shift_jis_2004 shiftjis2004 sjis_2004 sjis2004
105 sjis s_jis shift_jis_2004 shiftjis2004 sjis_2004 sjis2004
106 shift_jisx0213 shiftjisx0213 sjisx0213 s_jisx0213 950 cp950 ms950 '''
106 shift_jisx0213 shiftjisx0213 sjisx0213 s_jisx0213 950 cp950 ms950 '''
107
107
108 def reposetup(ui, repo):
108 def reposetup(ui, repo):
109 # TODO: decide use of config section for this extension
109 # TODO: decide use of config section for this extension
110 if not os.path.supports_unicode_filenames:
110 if not os.path.supports_unicode_filenames:
111 ui.warn(_("[win32mbcs] cannot activate on this platform.\n"))
111 ui.warn(_("[win32mbcs] cannot activate on this platform.\n"))
112 return
112 return
113
113
114 # fake is only for relevant environment.
114 # fake is only for relevant environment.
115 if encoding.encoding.lower() in problematic_encodings.split():
115 if encoding.encoding.lower() in problematic_encodings.split():
116 for f in funcs.split():
116 for f in funcs.split():
117 wrapname(f)
117 wrapname(f)
118 ui.debug(_("[win32mbcs] activated with encoding: %s\n")
118 ui.debug(_("[win32mbcs] activated with encoding: %s\n")
119 % encoding.encoding)
119 % encoding.encoding)
120
120
@@ -1,502 +1,502 b''
1 % help
1 % help
2 keyword extension - expand keywords in tracked files
2 keyword extension - expand keywords in tracked files
3
3
4 This extension expands RCS/CVS-like or self-customized $Keywords$ in
4 This extension expands RCS/CVS-like or self-customized $Keywords$ in
5 tracked text files selected by your configuration.
5 tracked text files selected by your configuration.
6
6
7 Keywords are only expanded in local repositories and not stored in the
7 Keywords are only expanded in local repositories and not stored in the
8 change history. The mechanism can be regarded as a convenience for the
8 change history. The mechanism can be regarded as a convenience for the
9 current user or for archive distribution.
9 current user or for archive distribution.
10
10
11 Configuration is done in the [keyword] and [keywordmaps] sections of
11 Configuration is done in the [keyword] and [keywordmaps] sections of
12 hgrc files.
12 hgrc files.
13
13
14 Example:
14 Example:
15
15
16 [keyword]
16 [keyword]
17 # expand keywords in every python file except those matching "x*"
17 # expand keywords in every python file except those matching "x*"
18 **.py =
18 **.py =
19 x* = ignore
19 x* = ignore
20
20
21 Note: the more specific you are in your filename patterns
21 Note: the more specific you are in your filename patterns
22 the less you lose speed in huge repositories.
22 the less you lose speed in huge repositories.
23
23
24 For [keywordmaps] template mapping and expansion demonstration and
24 For [keywordmaps] template mapping and expansion demonstration and
25 control run "hg kwdemo".
25 control run "hg kwdemo".
26
26
27 An additional date template filter {date|utcdate} is provided.
27 An additional date template filter {date|utcdate} is provided.
28
28
29 The default template mappings (view with "hg kwdemo -d") can be
29 The default template mappings (view with "hg kwdemo -d") can be
30 replaced with customized keywords and templates. Again, run "hg
30 replaced with customized keywords and templates. Again, run "hg
31 kwdemo" to control the results of your config changes.
31 kwdemo" to control the results of your config changes.
32
32
33 Before changing/disabling active keywords, run "hg kwshrink" to avoid
33 Before changing/disabling active keywords, run "hg kwshrink" to avoid
34 the risk of inadvertently storing expanded keywords in the change
34 the risk of inadvertently storing expanded keywords in the change
35 history.
35 history.
36
36
37 To force expansion after enabling it, or a configuration change, run
37 To force expansion after enabling it, or a configuration change, run
38 "hg kwexpand".
38 "hg kwexpand".
39
39
40 Also, when committing with the record extension or using mq's qrecord,
40 Also, when committing with the record extension or using mq's qrecord,
41 be aware that keywords cannot be updated. Again, run "hg kwexpand" on
41 be aware that keywords cannot be updated. Again, run "hg kwexpand" on
42 the files in question to update keyword expansions after all changes
42 the files in question to update keyword expansions after all changes
43 have been checked in.
43 have been checked in.
44
44
45 Expansions spanning more than one line and incremental expansions,
45 Expansions spanning more than one line and incremental expansions,
46 like CVS' $Log$, are not supported. A keyword template map
46 like CVS' $Log$, are not supported. A keyword template map
47 "Log = {desc}" expands to the first line of the changeset description.
47 "Log = {desc}" expands to the first line of the changeset description.
48
48
49 list of commands:
49 list of commands:
50
50
51 kwdemo print [keywordmaps] configuration and an expansion example
51 kwdemo print [keywordmaps] configuration and an expansion example
52 kwexpand expand keywords in the working directory
52 kwexpand expand keywords in the working directory
53 kwfiles print files currently configured for keyword expansion
53 kwfiles print files currently configured for keyword expansion
54 kwshrink revert expanded keywords in the working directory
54 kwshrink revert expanded keywords in the working directory
55
55
56 enabled extensions:
56 enabled extensions:
57
57
58 keyword expand keywords in tracked files
58 keyword expand keywords in tracked files
59 mq work with a stack of patches
59 mq manage a stack of patches
60 notify send e-mail notifications for commits/pushes
60 notify send e-mail notifications for commits/pushes
61
61
62 use "hg -v help keyword" to show aliases and global options
62 use "hg -v help keyword" to show aliases and global options
63 % hg kwdemo
63 % hg kwdemo
64 [extensions]
64 [extensions]
65 hgext.keyword =
65 hgext.keyword =
66 [keyword]
66 [keyword]
67 * =
67 * =
68 b = ignore
68 b = ignore
69 demo.txt =
69 demo.txt =
70 [keywordmaps]
70 [keywordmaps]
71 RCSFile = {file|basename},v
71 RCSFile = {file|basename},v
72 Author = {author|user}
72 Author = {author|user}
73 Header = {root}/{file},v {node|short} {date|utcdate} {author|user}
73 Header = {root}/{file},v {node|short} {date|utcdate} {author|user}
74 Source = {root}/{file},v
74 Source = {root}/{file},v
75 Date = {date|utcdate}
75 Date = {date|utcdate}
76 Id = {file|basename},v {node|short} {date|utcdate} {author|user}
76 Id = {file|basename},v {node|short} {date|utcdate} {author|user}
77 Revision = {node|short}
77 Revision = {node|short}
78 $RCSFile: demo.txt,v $
78 $RCSFile: demo.txt,v $
79 $Author: test $
79 $Author: test $
80 $Header: /TMP/demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $
80 $Header: /TMP/demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $
81 $Source: /TMP/demo.txt,v $
81 $Source: /TMP/demo.txt,v $
82 $Date: 2000/00/00 00:00:00 $
82 $Date: 2000/00/00 00:00:00 $
83 $Id: demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $
83 $Id: demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $
84 $Revision: xxxxxxxxxxxx $
84 $Revision: xxxxxxxxxxxx $
85 [extensions]
85 [extensions]
86 hgext.keyword =
86 hgext.keyword =
87 [keyword]
87 [keyword]
88 * =
88 * =
89 b = ignore
89 b = ignore
90 demo.txt =
90 demo.txt =
91 [keywordmaps]
91 [keywordmaps]
92 Branch = {branches}
92 Branch = {branches}
93 $Branch: demobranch $
93 $Branch: demobranch $
94 % kwshrink should exit silently in empty/invalid repo
94 % kwshrink should exit silently in empty/invalid repo
95 pulling from test-keyword.hg
95 pulling from test-keyword.hg
96 requesting all changes
96 requesting all changes
97 adding changesets
97 adding changesets
98 adding manifests
98 adding manifests
99 adding file changes
99 adding file changes
100 added 1 changesets with 1 changes to 1 files
100 added 1 changesets with 1 changes to 1 files
101 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
101 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
102 % cat
102 % cat
103 expand $Id$
103 expand $Id$
104 do not process $Id:
104 do not process $Id:
105 xxx $
105 xxx $
106 ignore $Id$
106 ignore $Id$
107 % addremove
107 % addremove
108 adding a
108 adding a
109 adding b
109 adding b
110 % status
110 % status
111 A a
111 A a
112 A b
112 A b
113 % default keyword expansion including commit hook
113 % default keyword expansion including commit hook
114 % interrupted commit should not change state or run commit hook
114 % interrupted commit should not change state or run commit hook
115 abort: empty commit message
115 abort: empty commit message
116 % status
116 % status
117 A a
117 A a
118 A b
118 A b
119 % commit
119 % commit
120 a
120 a
121 b
121 b
122 overwriting a expanding keywords
122 overwriting a expanding keywords
123 running hook commit.test: cp a hooktest
123 running hook commit.test: cp a hooktest
124 committed changeset 1:ef63ca68695bc9495032c6fda1350c71e6d256e9
124 committed changeset 1:ef63ca68695bc9495032c6fda1350c71e6d256e9
125 % status
125 % status
126 ? hooktest
126 ? hooktest
127 % identify
127 % identify
128 ef63ca68695b
128 ef63ca68695b
129 % cat
129 % cat
130 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
130 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
131 do not process $Id:
131 do not process $Id:
132 xxx $
132 xxx $
133 ignore $Id$
133 ignore $Id$
134 % hg cat
134 % hg cat
135 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
135 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
136 do not process $Id:
136 do not process $Id:
137 xxx $
137 xxx $
138 ignore $Id$
138 ignore $Id$
139 a
139 a
140 % diff a hooktest
140 % diff a hooktest
141 % removing commit hook from config
141 % removing commit hook from config
142 % bundle
142 % bundle
143 2 changesets found
143 2 changesets found
144 % notify on pull to check whether keywords stay as is in email
144 % notify on pull to check whether keywords stay as is in email
145 % ie. if patch.diff wrapper acts as it should
145 % ie. if patch.diff wrapper acts as it should
146 % pull from bundle
146 % pull from bundle
147 pulling from ../kw.hg
147 pulling from ../kw.hg
148 requesting all changes
148 requesting all changes
149 adding changesets
149 adding changesets
150 adding manifests
150 adding manifests
151 adding file changes
151 adding file changes
152 added 2 changesets with 3 changes to 3 files
152 added 2 changesets with 3 changes to 3 files
153
153
154 diff -r 000000000000 -r a2392c293916 sym
154 diff -r 000000000000 -r a2392c293916 sym
155 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
155 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
156 +++ b/sym Sat Feb 09 20:25:47 2008 +0100
156 +++ b/sym Sat Feb 09 20:25:47 2008 +0100
157 @@ -0,0 +1,1 @@
157 @@ -0,0 +1,1 @@
158 +a
158 +a
159 \ No newline at end of file
159 \ No newline at end of file
160
160
161 diff -r a2392c293916 -r ef63ca68695b a
161 diff -r a2392c293916 -r ef63ca68695b a
162 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
162 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
163 +++ b/a Thu Jan 01 00:00:00 1970 +0000
163 +++ b/a Thu Jan 01 00:00:00 1970 +0000
164 @@ -0,0 +1,3 @@
164 @@ -0,0 +1,3 @@
165 +expand $Id$
165 +expand $Id$
166 +do not process $Id:
166 +do not process $Id:
167 +xxx $
167 +xxx $
168 diff -r a2392c293916 -r ef63ca68695b b
168 diff -r a2392c293916 -r ef63ca68695b b
169 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
169 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
170 +++ b/b Thu Jan 01 00:00:00 1970 +0000
170 +++ b/b Thu Jan 01 00:00:00 1970 +0000
171 @@ -0,0 +1,1 @@
171 @@ -0,0 +1,1 @@
172 +ignore $Id$
172 +ignore $Id$
173 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
173 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
174 % remove notify config
174 % remove notify config
175 % touch
175 % touch
176 % status
176 % status
177 % update
177 % update
178 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
178 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
179 % cat
179 % cat
180 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
180 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
181 do not process $Id:
181 do not process $Id:
182 xxx $
182 xxx $
183 ignore $Id$
183 ignore $Id$
184 % check whether expansion is filewise
184 % check whether expansion is filewise
185 % commit c
185 % commit c
186 adding c
186 adding c
187 % force expansion
187 % force expansion
188 overwriting a expanding keywords
188 overwriting a expanding keywords
189 overwriting c expanding keywords
189 overwriting c expanding keywords
190 % compare changenodes in a c
190 % compare changenodes in a c
191 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
191 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
192 do not process $Id:
192 do not process $Id:
193 xxx $
193 xxx $
194 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
194 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
195 tests for different changenodes
195 tests for different changenodes
196 % qinit -c
196 % qinit -c
197 % qimport
197 % qimport
198 % qcommit
198 % qcommit
199 % keywords should not be expanded in patch
199 % keywords should not be expanded in patch
200 # HG changeset patch
200 # HG changeset patch
201 # User User Name <user@example.com>
201 # User User Name <user@example.com>
202 # Date 1 0
202 # Date 1 0
203 # Node ID 40a904bbbe4cd4ab0a1f28411e35db26341a40ad
203 # Node ID 40a904bbbe4cd4ab0a1f28411e35db26341a40ad
204 # Parent ef63ca68695bc9495032c6fda1350c71e6d256e9
204 # Parent ef63ca68695bc9495032c6fda1350c71e6d256e9
205 cndiff
205 cndiff
206
206
207 diff -r ef63ca68695b -r 40a904bbbe4c c
207 diff -r ef63ca68695b -r 40a904bbbe4c c
208 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
208 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
209 +++ b/c Thu Jan 01 00:00:01 1970 +0000
209 +++ b/c Thu Jan 01 00:00:01 1970 +0000
210 @@ -0,0 +1,2 @@
210 @@ -0,0 +1,2 @@
211 +$Id$
211 +$Id$
212 +tests for different changenodes
212 +tests for different changenodes
213 % qpop
213 % qpop
214 patch queue now empty
214 patch queue now empty
215 % qgoto - should imply qpush
215 % qgoto - should imply qpush
216 applying mqtest.diff
216 applying mqtest.diff
217 now at: mqtest.diff
217 now at: mqtest.diff
218 % cat
218 % cat
219 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
219 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
220 tests for different changenodes
220 tests for different changenodes
221 % qpop and move on
221 % qpop and move on
222 patch queue now empty
222 patch queue now empty
223 % copy
223 % copy
224 % kwfiles added
224 % kwfiles added
225 a
225 a
226 c
226 c
227 % commit
227 % commit
228 c
228 c
229 c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
229 c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
230 overwriting c expanding keywords
230 overwriting c expanding keywords
231 committed changeset 2:e22d299ac0c2bd8897b3df5114374b9e4d4ca62f
231 committed changeset 2:e22d299ac0c2bd8897b3df5114374b9e4d4ca62f
232 % cat a c
232 % cat a c
233 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
233 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
234 do not process $Id:
234 do not process $Id:
235 xxx $
235 xxx $
236 expand $Id: c,v e22d299ac0c2 1970/01/01 00:00:01 user $
236 expand $Id: c,v e22d299ac0c2 1970/01/01 00:00:01 user $
237 do not process $Id:
237 do not process $Id:
238 xxx $
238 xxx $
239 % touch copied c
239 % touch copied c
240 % status
240 % status
241 % kwfiles
241 % kwfiles
242 a
242 a
243 c
243 c
244 % diff --rev
244 % diff --rev
245 diff -r ef63ca68695b c
245 diff -r ef63ca68695b c
246 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
246 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
247 @@ -0,0 +1,3 @@
247 @@ -0,0 +1,3 @@
248 +expand $Id$
248 +expand $Id$
249 +do not process $Id:
249 +do not process $Id:
250 +xxx $
250 +xxx $
251 % rollback
251 % rollback
252 rolling back last transaction
252 rolling back last transaction
253 % status
253 % status
254 A c
254 A c
255 % update -C
255 % update -C
256 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
256 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
257 % custom keyword expansion
257 % custom keyword expansion
258 % try with kwdemo
258 % try with kwdemo
259 [extensions]
259 [extensions]
260 hgext.keyword =
260 hgext.keyword =
261 [keyword]
261 [keyword]
262 * =
262 * =
263 b = ignore
263 b = ignore
264 demo.txt =
264 demo.txt =
265 [keywordmaps]
265 [keywordmaps]
266 Xinfo = {author}: {desc}
266 Xinfo = {author}: {desc}
267 $Xinfo: test: hg keyword config and expansion example $
267 $Xinfo: test: hg keyword config and expansion example $
268 % cat
268 % cat
269 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
269 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
270 do not process $Id:
270 do not process $Id:
271 xxx $
271 xxx $
272 ignore $Id$
272 ignore $Id$
273 % hg cat
273 % hg cat
274 expand $Id: a ef63ca68695b Thu, 01 Jan 1970 00:00:00 +0000 user $
274 expand $Id: a ef63ca68695b Thu, 01 Jan 1970 00:00:00 +0000 user $
275 do not process $Id:
275 do not process $Id:
276 xxx $
276 xxx $
277 ignore $Id$
277 ignore $Id$
278 a
278 a
279 % interrupted commit should not change state
279 % interrupted commit should not change state
280 abort: empty commit message
280 abort: empty commit message
281 % status
281 % status
282 M a
282 M a
283 ? c
283 ? c
284 ? log
284 ? log
285 % commit
285 % commit
286 a
286 a
287 overwriting a expanding keywords
287 overwriting a expanding keywords
288 committed changeset 2:bb948857c743469b22bbf51f7ec8112279ca5d83
288 committed changeset 2:bb948857c743469b22bbf51f7ec8112279ca5d83
289 % status
289 % status
290 ? c
290 ? c
291 % verify
291 % verify
292 checking changesets
292 checking changesets
293 checking manifests
293 checking manifests
294 crosschecking files in changesets and manifests
294 crosschecking files in changesets and manifests
295 checking files
295 checking files
296 3 files, 3 changesets, 4 total revisions
296 3 files, 3 changesets, 4 total revisions
297 % cat
297 % cat
298 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
298 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
299 do not process $Id:
299 do not process $Id:
300 xxx $
300 xxx $
301 $Xinfo: User Name <user@example.com>: firstline $
301 $Xinfo: User Name <user@example.com>: firstline $
302 ignore $Id$
302 ignore $Id$
303 % hg cat
303 % hg cat
304 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
304 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
305 do not process $Id:
305 do not process $Id:
306 xxx $
306 xxx $
307 $Xinfo: User Name <user@example.com>: firstline $
307 $Xinfo: User Name <user@example.com>: firstline $
308 ignore $Id$
308 ignore $Id$
309 a
309 a
310 % annotate
310 % annotate
311 1: expand $Id$
311 1: expand $Id$
312 1: do not process $Id:
312 1: do not process $Id:
313 1: xxx $
313 1: xxx $
314 2: $Xinfo$
314 2: $Xinfo$
315 % remove
315 % remove
316 committed changeset 3:d14c712653769de926994cf7fbb06c8fbd68f012
316 committed changeset 3:d14c712653769de926994cf7fbb06c8fbd68f012
317 % status
317 % status
318 ? c
318 ? c
319 % rollback
319 % rollback
320 rolling back last transaction
320 rolling back last transaction
321 % status
321 % status
322 R a
322 R a
323 ? c
323 ? c
324 % revert a
324 % revert a
325 % cat a
325 % cat a
326 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
326 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
327 do not process $Id:
327 do not process $Id:
328 xxx $
328 xxx $
329 $Xinfo: User Name <user@example.com>: firstline $
329 $Xinfo: User Name <user@example.com>: firstline $
330 % clone to test incoming
330 % clone to test incoming
331 requesting all changes
331 requesting all changes
332 adding changesets
332 adding changesets
333 adding manifests
333 adding manifests
334 adding file changes
334 adding file changes
335 added 2 changesets with 3 changes to 3 files
335 added 2 changesets with 3 changes to 3 files
336 updating working directory
336 updating working directory
337 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
337 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
338 % incoming
338 % incoming
339 comparing with test-keyword/Test
339 comparing with test-keyword/Test
340 searching for changes
340 searching for changes
341 changeset: 2:bb948857c743
341 changeset: 2:bb948857c743
342 tag: tip
342 tag: tip
343 user: User Name <user@example.com>
343 user: User Name <user@example.com>
344 date: Thu Jan 01 00:00:02 1970 +0000
344 date: Thu Jan 01 00:00:02 1970 +0000
345 summary: firstline
345 summary: firstline
346
346
347 % commit rejecttest
347 % commit rejecttest
348 a
348 a
349 overwriting a expanding keywords
349 overwriting a expanding keywords
350 committed changeset 2:85e279d709ffc28c9fdd1b868570985fc3d87082
350 committed changeset 2:85e279d709ffc28c9fdd1b868570985fc3d87082
351 % export
351 % export
352 % import
352 % import
353 applying ../rejecttest.diff
353 applying ../rejecttest.diff
354 % cat
354 % cat
355 expand $Id: a 4e0994474d25 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
355 expand $Id: a 4e0994474d25 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
356 do not process $Id: rejecttest
356 do not process $Id: rejecttest
357 xxx $
357 xxx $
358 $Xinfo: User Name <user@example.com>: rejects? $
358 $Xinfo: User Name <user@example.com>: rejects? $
359 ignore $Id$
359 ignore $Id$
360
360
361 % rollback
361 % rollback
362 rolling back last transaction
362 rolling back last transaction
363 % clean update
363 % clean update
364 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
364 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
365 % kwexpand/kwshrink on selected files
365 % kwexpand/kwshrink on selected files
366 % copy a x/a
366 % copy a x/a
367 % kwexpand a
367 % kwexpand a
368 overwriting a expanding keywords
368 overwriting a expanding keywords
369 % kwexpand x/a should abort
369 % kwexpand x/a should abort
370 abort: outstanding uncommitted changes
370 abort: outstanding uncommitted changes
371 x/a
371 x/a
372 x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e
372 x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e
373 overwriting x/a expanding keywords
373 overwriting x/a expanding keywords
374 committed changeset 3:cfa68229c1167443337266ebac453c73b1d5d16e
374 committed changeset 3:cfa68229c1167443337266ebac453c73b1d5d16e
375 % cat a
375 % cat a
376 expand $Id: x/a cfa68229c116 Thu, 01 Jan 1970 00:00:03 +0000 user $
376 expand $Id: x/a cfa68229c116 Thu, 01 Jan 1970 00:00:03 +0000 user $
377 do not process $Id:
377 do not process $Id:
378 xxx $
378 xxx $
379 $Xinfo: User Name <user@example.com>: xa $
379 $Xinfo: User Name <user@example.com>: xa $
380 % kwshrink a inside directory x
380 % kwshrink a inside directory x
381 overwriting x/a shrinking keywords
381 overwriting x/a shrinking keywords
382 % cat a
382 % cat a
383 expand $Id$
383 expand $Id$
384 do not process $Id:
384 do not process $Id:
385 xxx $
385 xxx $
386 $Xinfo$
386 $Xinfo$
387 % kwexpand nonexistent
387 % kwexpand nonexistent
388 nonexistent:
388 nonexistent:
389 % hg serve
389 % hg serve
390 % expansion
390 % expansion
391 % hgweb file
391 % hgweb file
392 200 Script output follows
392 200 Script output follows
393
393
394 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
394 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
395 do not process $Id:
395 do not process $Id:
396 xxx $
396 xxx $
397 $Xinfo: User Name <user@example.com>: firstline $
397 $Xinfo: User Name <user@example.com>: firstline $
398 % no expansion
398 % no expansion
399 % hgweb annotate
399 % hgweb annotate
400 200 Script output follows
400 200 Script output follows
401
401
402
402
403 user@1: expand $Id$
403 user@1: expand $Id$
404 user@1: do not process $Id:
404 user@1: do not process $Id:
405 user@1: xxx $
405 user@1: xxx $
406 user@2: $Xinfo$
406 user@2: $Xinfo$
407
407
408
408
409
409
410
410
411 % hgweb changeset
411 % hgweb changeset
412 200 Script output follows
412 200 Script output follows
413
413
414
414
415 # HG changeset patch
415 # HG changeset patch
416 # User User Name <user@example.com>
416 # User User Name <user@example.com>
417 # Date 3 0
417 # Date 3 0
418 # Node ID cfa68229c1167443337266ebac453c73b1d5d16e
418 # Node ID cfa68229c1167443337266ebac453c73b1d5d16e
419 # Parent bb948857c743469b22bbf51f7ec8112279ca5d83
419 # Parent bb948857c743469b22bbf51f7ec8112279ca5d83
420 xa
420 xa
421
421
422 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
422 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
423 +++ b/x/a Thu Jan 01 00:00:03 1970 +0000
423 +++ b/x/a Thu Jan 01 00:00:03 1970 +0000
424 @@ -0,0 +1,4 @@
424 @@ -0,0 +1,4 @@
425 +expand $Id$
425 +expand $Id$
426 +do not process $Id:
426 +do not process $Id:
427 +xxx $
427 +xxx $
428 +$Xinfo$
428 +$Xinfo$
429
429
430 % hgweb filediff
430 % hgweb filediff
431 200 Script output follows
431 200 Script output follows
432
432
433
433
434 --- a/a Thu Jan 01 00:00:00 1970 +0000
434 --- a/a Thu Jan 01 00:00:00 1970 +0000
435 +++ b/a Thu Jan 01 00:00:02 1970 +0000
435 +++ b/a Thu Jan 01 00:00:02 1970 +0000
436 @@ -1,3 +1,4 @@
436 @@ -1,3 +1,4 @@
437 expand $Id$
437 expand $Id$
438 do not process $Id:
438 do not process $Id:
439 xxx $
439 xxx $
440 +$Xinfo$
440 +$Xinfo$
441
441
442
442
443
443
444
444
445 % errors encountered
445 % errors encountered
446 % merge/resolve
446 % merge/resolve
447 % simplemerge
447 % simplemerge
448 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
448 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
449 created new head
449 created new head
450 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
450 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
451 (branch merge, don't forget to commit)
451 (branch merge, don't forget to commit)
452 $Id: m 8731e1dadc99 Thu, 01 Jan 1970 00:00:00 +0000 test $
452 $Id: m 8731e1dadc99 Thu, 01 Jan 1970 00:00:00 +0000 test $
453 foo
453 foo
454 % conflict
454 % conflict
455 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
455 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
456 created new head
456 created new head
457 merging m
457 merging m
458 warning: conflicts during merge.
458 warning: conflicts during merge.
459 merging m failed!
459 merging m failed!
460 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
460 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
461 use 'hg resolve' to retry unresolved file merges or 'hg up --clean' to abandon
461 use 'hg resolve' to retry unresolved file merges or 'hg up --clean' to abandon
462 % keyword stays outside conflict zone
462 % keyword stays outside conflict zone
463 $Id$
463 $Id$
464 <<<<<<< local
464 <<<<<<< local
465 bar
465 bar
466 =======
466 =======
467 foo
467 foo
468 >>>>>>> other
468 >>>>>>> other
469 % resolve to local
469 % resolve to local
470 $Id: m 43dfd2854b5b Thu, 01 Jan 1970 00:00:00 +0000 test $
470 $Id: m 43dfd2854b5b Thu, 01 Jan 1970 00:00:00 +0000 test $
471 bar
471 bar
472 % switch off expansion
472 % switch off expansion
473 % kwshrink with unknown file u
473 % kwshrink with unknown file u
474 overwriting a shrinking keywords
474 overwriting a shrinking keywords
475 overwriting m shrinking keywords
475 overwriting m shrinking keywords
476 overwriting x/a shrinking keywords
476 overwriting x/a shrinking keywords
477 % cat
477 % cat
478 expand $Id$
478 expand $Id$
479 do not process $Id:
479 do not process $Id:
480 xxx $
480 xxx $
481 $Xinfo$
481 $Xinfo$
482 ignore $Id$
482 ignore $Id$
483 % hg cat
483 % hg cat
484 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
484 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
485 do not process $Id:
485 do not process $Id:
486 xxx $
486 xxx $
487 $Xinfo: User Name <user@example.com>: firstline $
487 $Xinfo: User Name <user@example.com>: firstline $
488 ignore $Id$
488 ignore $Id$
489 a
489 a
490 % cat
490 % cat
491 expand $Id$
491 expand $Id$
492 do not process $Id:
492 do not process $Id:
493 xxx $
493 xxx $
494 $Xinfo$
494 $Xinfo$
495 ignore $Id$
495 ignore $Id$
496 % hg cat
496 % hg cat
497 expand $Id$
497 expand $Id$
498 do not process $Id:
498 do not process $Id:
499 xxx $
499 xxx $
500 $Xinfo$
500 $Xinfo$
501 ignore $Id$
501 ignore $Id$
502 a
502 a
@@ -1,542 +1,542 b''
1 % help
1 % help
2 mq extension - work with a stack of patches
2 mq extension - manage a stack of patches
3
3
4 This extension lets you work with a stack of patches in a Mercurial
4 This extension lets you work with a stack of patches in a Mercurial
5 repository. It manages two stacks of patches - all known patches, and
5 repository. It manages two stacks of patches - all known patches, and
6 applied patches (subset of known patches).
6 applied patches (subset of known patches).
7
7
8 Known patches are represented as patch files in the .hg/patches
8 Known patches are represented as patch files in the .hg/patches
9 directory. Applied patches are both patch files and changesets.
9 directory. Applied patches are both patch files and changesets.
10
10
11 Common tasks (use "hg help command" for more details):
11 Common tasks (use "hg help command" for more details):
12
12
13 prepare repository to work with patches qinit
13 prepare repository to work with patches qinit
14 create new patch qnew
14 create new patch qnew
15 import existing patch qimport
15 import existing patch qimport
16
16
17 print patch series qseries
17 print patch series qseries
18 print applied patches qapplied
18 print applied patches qapplied
19 print name of top applied patch qtop
19 print name of top applied patch qtop
20
20
21 add known patch to applied stack qpush
21 add known patch to applied stack qpush
22 remove patch from applied stack qpop
22 remove patch from applied stack qpop
23 refresh contents of top applied patch qrefresh
23 refresh contents of top applied patch qrefresh
24
24
25 list of commands:
25 list of commands:
26
26
27 qapplied print the patches already applied
27 qapplied print the patches already applied
28 qclone clone main and patch repository at same time
28 qclone clone main and patch repository at same time
29 qcommit commit changes in the queue repository
29 qcommit commit changes in the queue repository
30 qdelete remove patches from queue
30 qdelete remove patches from queue
31 qdiff diff of the current patch and subsequent modifications
31 qdiff diff of the current patch and subsequent modifications
32 qfinish move applied patches into repository history
32 qfinish move applied patches into repository history
33 qfold fold the named patches into the current patch
33 qfold fold the named patches into the current patch
34 qgoto push or pop patches until named patch is at top of stack
34 qgoto push or pop patches until named patch is at top of stack
35 qguard set or print guards for a patch
35 qguard set or print guards for a patch
36 qheader print the header of the topmost or specified patch
36 qheader print the header of the topmost or specified patch
37 qimport import a patch
37 qimport import a patch
38 qinit init a new queue repository
38 qinit init a new queue repository
39 qnew create a new patch
39 qnew create a new patch
40 qnext print the name of the next patch
40 qnext print the name of the next patch
41 qpop pop the current patch off the stack
41 qpop pop the current patch off the stack
42 qprev print the name of the previous patch
42 qprev print the name of the previous patch
43 qpush push the next patch onto the stack
43 qpush push the next patch onto the stack
44 qrefresh update the current patch
44 qrefresh update the current patch
45 qrename rename a patch
45 qrename rename a patch
46 qrestore restore the queue state saved by a revision
46 qrestore restore the queue state saved by a revision
47 qsave save current queue state
47 qsave save current queue state
48 qselect set or print guarded patches to push
48 qselect set or print guarded patches to push
49 qseries print the entire series file
49 qseries print the entire series file
50 qtop print the name of the current patch
50 qtop print the name of the current patch
51 qunapplied print the patches not yet applied
51 qunapplied print the patches not yet applied
52 strip strip a revision and all its descendants from the repository
52 strip strip a revision and all its descendants from the repository
53
53
54 enabled extensions:
54 enabled extensions:
55
55
56 mq work with a stack of patches
56 mq manage a stack of patches
57
57
58 use "hg -v help mq" to show aliases and global options
58 use "hg -v help mq" to show aliases and global options
59 adding a
59 adding a
60 updating working directory
60 updating working directory
61 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
61 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
62 adding b/z
62 adding b/z
63 % qinit
63 % qinit
64 % -R qinit
64 % -R qinit
65 % qinit -c
65 % qinit -c
66 A .hgignore
66 A .hgignore
67 A series
67 A series
68 % qinit; qinit -c
68 % qinit; qinit -c
69 .hgignore:
69 .hgignore:
70 ^\.hg
70 ^\.hg
71 ^\.mq
71 ^\.mq
72 syntax: glob
72 syntax: glob
73 status
73 status
74 guards
74 guards
75 series:
75 series:
76 abort: repository already exists!
76 abort: repository already exists!
77 % qinit; <stuff>; qinit -c
77 % qinit; <stuff>; qinit -c
78 adding .hg/patches/A
78 adding .hg/patches/A
79 adding .hg/patches/B
79 adding .hg/patches/B
80 A .hgignore
80 A .hgignore
81 A A
81 A A
82 A B
82 A B
83 A series
83 A series
84 .hgignore:
84 .hgignore:
85 status
85 status
86 bleh
86 bleh
87 series:
87 series:
88 A
88 A
89 B
89 B
90 % qrefresh
90 % qrefresh
91 foo bar
91 foo bar
92
92
93 diff -r xa
93 diff -r xa
94 --- a/a
94 --- a/a
95 +++ b/a
95 +++ b/a
96 @@ -1,1 +1,2 @@
96 @@ -1,1 +1,2 @@
97 a
97 a
98 +a
98 +a
99 % empty qrefresh
99 % empty qrefresh
100 revision:
100 revision:
101 patch:
101 patch:
102 foo bar
102 foo bar
103
103
104 working dir diff:
104 working dir diff:
105 --- a/a
105 --- a/a
106 +++ b/a
106 +++ b/a
107 @@ -1,1 +1,2 @@
107 @@ -1,1 +1,2 @@
108 a
108 a
109 +a
109 +a
110 % qpop
110 % qpop
111 patch queue now empty
111 patch queue now empty
112 % qpush
112 % qpush
113 applying test.patch
113 applying test.patch
114 now at: test.patch
114 now at: test.patch
115 % pop/push outside repo
115 % pop/push outside repo
116 patch queue now empty
116 patch queue now empty
117 applying test.patch
117 applying test.patch
118 now at: test.patch
118 now at: test.patch
119 % qrefresh in subdir
119 % qrefresh in subdir
120 % pop/push -a in subdir
120 % pop/push -a in subdir
121 patch queue now empty
121 patch queue now empty
122 applying test.patch
122 applying test.patch
123 applying test2.patch
123 applying test2.patch
124 now at: test2.patch
124 now at: test2.patch
125 % qseries
125 % qseries
126 test.patch
126 test.patch
127 test2.patch
127 test2.patch
128 now at: test.patch
128 now at: test.patch
129 0 A test.patch: foo bar
129 0 A test.patch: foo bar
130 1 U test2.patch:
130 1 U test2.patch:
131 applying test2.patch
131 applying test2.patch
132 now at: test2.patch
132 now at: test2.patch
133 % qapplied
133 % qapplied
134 test.patch
134 test.patch
135 test2.patch
135 test2.patch
136 % qtop
136 % qtop
137 test2.patch
137 test2.patch
138 % qprev
138 % qprev
139 test.patch
139 test.patch
140 % qnext
140 % qnext
141 all patches applied
141 all patches applied
142 % pop, qnext, qprev, qapplied
142 % pop, qnext, qprev, qapplied
143 now at: test.patch
143 now at: test.patch
144 test2.patch
144 test2.patch
145 only one patch applied
145 only one patch applied
146 test.patch
146 test.patch
147 % commit should fail
147 % commit should fail
148 abort: cannot commit over an applied mq patch
148 abort: cannot commit over an applied mq patch
149 % push should fail
149 % push should fail
150 pushing to ../../k
150 pushing to ../../k
151 abort: source has mq patches applied
151 abort: source has mq patches applied
152 % import should fail
152 % import should fail
153 abort: cannot import over an applied patch
153 abort: cannot import over an applied patch
154 % qunapplied
154 % qunapplied
155 test2.patch
155 test2.patch
156 % qpush/qpop with index
156 % qpush/qpop with index
157 applying test2.patch
157 applying test2.patch
158 now at: test2.patch
158 now at: test2.patch
159 now at: test.patch
159 now at: test.patch
160 applying test1b.patch
160 applying test1b.patch
161 now at: test1b.patch
161 now at: test1b.patch
162 applying test2.patch
162 applying test2.patch
163 now at: test2.patch
163 now at: test2.patch
164 now at: test1b.patch
164 now at: test1b.patch
165 now at: test.patch
165 now at: test.patch
166 applying test1b.patch
166 applying test1b.patch
167 applying test2.patch
167 applying test2.patch
168 now at: test2.patch
168 now at: test2.patch
169 % push should succeed
169 % push should succeed
170 patch queue now empty
170 patch queue now empty
171 pushing to ../../k
171 pushing to ../../k
172 searching for changes
172 searching for changes
173 adding changesets
173 adding changesets
174 adding manifests
174 adding manifests
175 adding file changes
175 adding file changes
176 added 1 changesets with 1 changes to 1 files
176 added 1 changesets with 1 changes to 1 files
177 % qpush/qpop error codes
177 % qpush/qpop error codes
178 applying test.patch
178 applying test.patch
179 applying test1b.patch
179 applying test1b.patch
180 applying test2.patch
180 applying test2.patch
181 now at: test2.patch
181 now at: test2.patch
182 % pops all patches and succeeds
182 % pops all patches and succeeds
183 patch queue now empty
183 patch queue now empty
184 qpop -a succeeds
184 qpop -a succeeds
185 % does nothing and succeeds
185 % does nothing and succeeds
186 no patches applied
186 no patches applied
187 qpop -a succeeds
187 qpop -a succeeds
188 % fails - nothing else to pop
188 % fails - nothing else to pop
189 no patches applied
189 no patches applied
190 qpop fails
190 qpop fails
191 % pushes a patch and succeeds
191 % pushes a patch and succeeds
192 applying test.patch
192 applying test.patch
193 now at: test.patch
193 now at: test.patch
194 qpush succeeds
194 qpush succeeds
195 % pops a patch and succeeds
195 % pops a patch and succeeds
196 patch queue now empty
196 patch queue now empty
197 qpop succeeds
197 qpop succeeds
198 % pushes up to test1b.patch and succeeds
198 % pushes up to test1b.patch and succeeds
199 applying test.patch
199 applying test.patch
200 applying test1b.patch
200 applying test1b.patch
201 now at: test1b.patch
201 now at: test1b.patch
202 qpush test1b.patch succeeds
202 qpush test1b.patch succeeds
203 % does nothing and succeeds
203 % does nothing and succeeds
204 qpush: test1b.patch is already at the top
204 qpush: test1b.patch is already at the top
205 qpush test1b.patch succeeds
205 qpush test1b.patch succeeds
206 % does nothing and succeeds
206 % does nothing and succeeds
207 qpop: test1b.patch is already at the top
207 qpop: test1b.patch is already at the top
208 qpop test1b.patch succeeds
208 qpop test1b.patch succeeds
209 % fails - can't push to this patch
209 % fails - can't push to this patch
210 abort: cannot push to a previous patch: test.patch
210 abort: cannot push to a previous patch: test.patch
211 qpush test.patch fails
211 qpush test.patch fails
212 % fails - can't pop to this patch
212 % fails - can't pop to this patch
213 abort: patch test2.patch is not applied
213 abort: patch test2.patch is not applied
214 qpop test2.patch fails
214 qpop test2.patch fails
215 % pops up to test.patch and succeeds
215 % pops up to test.patch and succeeds
216 now at: test.patch
216 now at: test.patch
217 qpop test.patch succeeds
217 qpop test.patch succeeds
218 % pushes all patches and succeeds
218 % pushes all patches and succeeds
219 applying test1b.patch
219 applying test1b.patch
220 applying test2.patch
220 applying test2.patch
221 now at: test2.patch
221 now at: test2.patch
222 qpush -a succeeds
222 qpush -a succeeds
223 % does nothing and succeeds
223 % does nothing and succeeds
224 all patches are currently applied
224 all patches are currently applied
225 qpush -a succeeds
225 qpush -a succeeds
226 % fails - nothing else to push
226 % fails - nothing else to push
227 patch series already fully applied
227 patch series already fully applied
228 qpush fails
228 qpush fails
229 % does nothing and succeeds
229 % does nothing and succeeds
230 qpush: test2.patch is already at the top
230 qpush: test2.patch is already at the top
231 qpush test2.patch succeeds
231 qpush test2.patch succeeds
232 % strip
232 % strip
233 adding x
233 adding x
234 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
234 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
235 saving bundle to
235 saving bundle to
236 adding changesets
236 adding changesets
237 adding manifests
237 adding manifests
238 adding file changes
238 adding file changes
239 added 1 changesets with 1 changes to 1 files
239 added 1 changesets with 1 changes to 1 files
240 (run 'hg update' to get a working copy)
240 (run 'hg update' to get a working copy)
241 % strip with local changes, should complain
241 % strip with local changes, should complain
242 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
242 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
243 abort: local changes found
243 abort: local changes found
244 % --force strip with local changes
244 % --force strip with local changes
245 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
245 0 files updated, 0 files merged, 1 files removed, 0 files unresolved
246 saving bundle to
246 saving bundle to
247 % cd b; hg qrefresh
247 % cd b; hg qrefresh
248 adding a
248 adding a
249 foo
249 foo
250
250
251 diff -r cb9a9f314b8b a
251 diff -r cb9a9f314b8b a
252 --- a/a
252 --- a/a
253 +++ b/a
253 +++ b/a
254 @@ -1,1 +1,2 @@
254 @@ -1,1 +1,2 @@
255 a
255 a
256 +a
256 +a
257 diff -r cb9a9f314b8b b/f
257 diff -r cb9a9f314b8b b/f
258 --- /dev/null
258 --- /dev/null
259 +++ b/b/f
259 +++ b/b/f
260 @@ -0,0 +1,1 @@
260 @@ -0,0 +1,1 @@
261 +f
261 +f
262 % hg qrefresh .
262 % hg qrefresh .
263 foo
263 foo
264
264
265 diff -r cb9a9f314b8b b/f
265 diff -r cb9a9f314b8b b/f
266 --- /dev/null
266 --- /dev/null
267 +++ b/b/f
267 +++ b/b/f
268 @@ -0,0 +1,1 @@
268 @@ -0,0 +1,1 @@
269 +f
269 +f
270 M a
270 M a
271 % qpush failure
271 % qpush failure
272 patch queue now empty
272 patch queue now empty
273 applying foo
273 applying foo
274 applying bar
274 applying bar
275 file foo already exists
275 file foo already exists
276 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
276 1 out of 1 hunks FAILED -- saving rejects to file foo.rej
277 patch failed, unable to continue (try -v)
277 patch failed, unable to continue (try -v)
278 patch failed, rejects left in working dir
278 patch failed, rejects left in working dir
279 errors during apply, please fix and refresh bar
279 errors during apply, please fix and refresh bar
280 ? foo
280 ? foo
281 ? foo.rej
281 ? foo.rej
282 % mq tags
282 % mq tags
283 0 qparent
283 0 qparent
284 1 qbase foo
284 1 qbase foo
285 2 qtip bar tip
285 2 qtip bar tip
286 % bad node in status
286 % bad node in status
287 now at: foo
287 now at: foo
288 changeset: 0:cb9a9f314b8b
288 changeset: 0:cb9a9f314b8b
289 mq status file refers to unknown node
289 mq status file refers to unknown node
290 tag: tip
290 tag: tip
291 user: test
291 user: test
292 date: Thu Jan 01 00:00:00 1970 +0000
292 date: Thu Jan 01 00:00:00 1970 +0000
293 summary: a
293 summary: a
294
294
295 mq status file refers to unknown node
295 mq status file refers to unknown node
296 default 0:cb9a9f314b8b
296 default 0:cb9a9f314b8b
297 abort: trying to pop unknown node
297 abort: trying to pop unknown node
298 new file
298 new file
299
299
300 diff --git a/new b/new
300 diff --git a/new b/new
301 new file mode 100755
301 new file mode 100755
302 --- /dev/null
302 --- /dev/null
303 +++ b/new
303 +++ b/new
304 @@ -0,0 +1,1 @@
304 @@ -0,0 +1,1 @@
305 +foo
305 +foo
306 copy file
306 copy file
307
307
308 diff --git a/new b/copy
308 diff --git a/new b/copy
309 copy from new
309 copy from new
310 copy to copy
310 copy to copy
311 now at: new
311 now at: new
312 applying copy
312 applying copy
313 now at: copy
313 now at: copy
314 diff --git a/new b/copy
314 diff --git a/new b/copy
315 copy from new
315 copy from new
316 copy to copy
316 copy to copy
317 diff --git a/new b/copy
317 diff --git a/new b/copy
318 copy from new
318 copy from new
319 copy to copy
319 copy to copy
320 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
320 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
321 created new head
321 created new head
322 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
322 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
323 adding branch
323 adding branch
324 adding changesets
324 adding changesets
325 adding manifests
325 adding manifests
326 adding file changes
326 adding file changes
327 added 1 changesets with 1 changes to 1 files
327 added 1 changesets with 1 changes to 1 files
328 patch queue now empty
328 patch queue now empty
329 (working directory not at a head)
329 (working directory not at a head)
330 applying bar
330 applying bar
331 now at: bar
331 now at: bar
332 diff --git a/bar b/bar
332 diff --git a/bar b/bar
333 new file mode 100644
333 new file mode 100644
334 --- /dev/null
334 --- /dev/null
335 +++ b/bar
335 +++ b/bar
336 @@ -0,0 +1,1 @@
336 @@ -0,0 +1,1 @@
337 +bar
337 +bar
338 diff --git a/foo b/baz
338 diff --git a/foo b/baz
339 rename from foo
339 rename from foo
340 rename to baz
340 rename to baz
341 2 baz (foo)
341 2 baz (foo)
342 diff --git a/bar b/bar
342 diff --git a/bar b/bar
343 new file mode 100644
343 new file mode 100644
344 --- /dev/null
344 --- /dev/null
345 +++ b/bar
345 +++ b/bar
346 @@ -0,0 +1,1 @@
346 @@ -0,0 +1,1 @@
347 +bar
347 +bar
348 diff --git a/foo b/baz
348 diff --git a/foo b/baz
349 rename from foo
349 rename from foo
350 rename to baz
350 rename to baz
351 2 baz (foo)
351 2 baz (foo)
352 diff --git a/bar b/bar
352 diff --git a/bar b/bar
353 diff --git a/foo b/baz
353 diff --git a/foo b/baz
354
354
355 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
355 1 files updated, 0 files merged, 2 files removed, 0 files unresolved
356 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
356 2 files updated, 0 files merged, 1 files removed, 0 files unresolved
357 adding branch
357 adding branch
358 adding changesets
358 adding changesets
359 adding manifests
359 adding manifests
360 adding file changes
360 adding file changes
361 added 1 changesets with 1 changes to 1 files
361 added 1 changesets with 1 changes to 1 files
362 patch queue now empty
362 patch queue now empty
363 (working directory not at a head)
363 (working directory not at a head)
364 applying bar
364 applying bar
365 now at: bar
365 now at: bar
366 diff --git a/foo b/bleh
366 diff --git a/foo b/bleh
367 rename from foo
367 rename from foo
368 rename to bleh
368 rename to bleh
369 diff --git a/quux b/quux
369 diff --git a/quux b/quux
370 new file mode 100644
370 new file mode 100644
371 --- /dev/null
371 --- /dev/null
372 +++ b/quux
372 +++ b/quux
373 @@ -0,0 +1,1 @@
373 @@ -0,0 +1,1 @@
374 +bar
374 +bar
375 3 bleh (foo)
375 3 bleh (foo)
376 diff --git a/foo b/barney
376 diff --git a/foo b/barney
377 rename from foo
377 rename from foo
378 rename to barney
378 rename to barney
379 diff --git a/fred b/fred
379 diff --git a/fred b/fred
380 new file mode 100644
380 new file mode 100644
381 --- /dev/null
381 --- /dev/null
382 +++ b/fred
382 +++ b/fred
383 @@ -0,0 +1,1 @@
383 @@ -0,0 +1,1 @@
384 +bar
384 +bar
385 3 barney (foo)
385 3 barney (foo)
386 % refresh omitting an added file
386 % refresh omitting an added file
387 C newfile
387 C newfile
388 A newfile
388 A newfile
389 now at: bar
389 now at: bar
390 % create a git patch
390 % create a git patch
391 diff --git a/alexander b/alexander
391 diff --git a/alexander b/alexander
392 % create a git binary patch
392 % create a git binary patch
393 8ba2a2f3e77b55d03051ff9c24ad65e7 bucephalus
393 8ba2a2f3e77b55d03051ff9c24ad65e7 bucephalus
394 diff --git a/bucephalus b/bucephalus
394 diff --git a/bucephalus b/bucephalus
395 % check binary patches can be popped and pushed
395 % check binary patches can be popped and pushed
396 now at: addalexander
396 now at: addalexander
397 applying addbucephalus
397 applying addbucephalus
398 now at: addbucephalus
398 now at: addbucephalus
399 8ba2a2f3e77b55d03051ff9c24ad65e7 bucephalus
399 8ba2a2f3e77b55d03051ff9c24ad65e7 bucephalus
400 % strip again
400 % strip again
401 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
401 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
402 created new head
402 created new head
403 merging foo
403 merging foo
404 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
404 0 files updated, 1 files merged, 0 files removed, 0 files unresolved
405 (branch merge, don't forget to commit)
405 (branch merge, don't forget to commit)
406 changeset: 3:99615015637b
406 changeset: 3:99615015637b
407 tag: tip
407 tag: tip
408 parent: 2:20cbbe65cff7
408 parent: 2:20cbbe65cff7
409 parent: 1:d2871fc282d4
409 parent: 1:d2871fc282d4
410 user: test
410 user: test
411 date: Thu Jan 01 00:00:00 1970 +0000
411 date: Thu Jan 01 00:00:00 1970 +0000
412 summary: merge
412 summary: merge
413
413
414 changeset: 2:20cbbe65cff7
414 changeset: 2:20cbbe65cff7
415 parent: 0:53245c60e682
415 parent: 0:53245c60e682
416 user: test
416 user: test
417 date: Thu Jan 01 00:00:00 1970 +0000
417 date: Thu Jan 01 00:00:00 1970 +0000
418 summary: change foo 2
418 summary: change foo 2
419
419
420 changeset: 1:d2871fc282d4
420 changeset: 1:d2871fc282d4
421 user: test
421 user: test
422 date: Thu Jan 01 00:00:00 1970 +0000
422 date: Thu Jan 01 00:00:00 1970 +0000
423 summary: change foo 1
423 summary: change foo 1
424
424
425 changeset: 0:53245c60e682
425 changeset: 0:53245c60e682
426 user: test
426 user: test
427 date: Thu Jan 01 00:00:00 1970 +0000
427 date: Thu Jan 01 00:00:00 1970 +0000
428 summary: add foo
428 summary: add foo
429
429
430 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
430 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
431 saving bundle to
431 saving bundle to
432 saving bundle to
432 saving bundle to
433 adding branch
433 adding branch
434 adding changesets
434 adding changesets
435 adding manifests
435 adding manifests
436 adding file changes
436 adding file changes
437 added 1 changesets with 1 changes to 1 files
437 added 1 changesets with 1 changes to 1 files
438 changeset: 1:20cbbe65cff7
438 changeset: 1:20cbbe65cff7
439 tag: tip
439 tag: tip
440 user: test
440 user: test
441 date: Thu Jan 01 00:00:00 1970 +0000
441 date: Thu Jan 01 00:00:00 1970 +0000
442 summary: change foo 2
442 summary: change foo 2
443
443
444 changeset: 0:53245c60e682
444 changeset: 0:53245c60e682
445 user: test
445 user: test
446 date: Thu Jan 01 00:00:00 1970 +0000
446 date: Thu Jan 01 00:00:00 1970 +0000
447 summary: add foo
447 summary: add foo
448
448
449 % qclone
449 % qclone
450 abort: versioned patch repository not found (see qinit -c)
450 abort: versioned patch repository not found (see qinit -c)
451 adding .hg/patches/patch1
451 adding .hg/patches/patch1
452 main repo:
452 main repo:
453 rev 1: change foo
453 rev 1: change foo
454 rev 0: add foo
454 rev 0: add foo
455 patch repo:
455 patch repo:
456 rev 0: checkpoint
456 rev 0: checkpoint
457 updating working directory
457 updating working directory
458 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
458 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
459 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
459 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
460 main repo:
460 main repo:
461 rev 0: add foo
461 rev 0: add foo
462 patch repo:
462 patch repo:
463 rev 0: checkpoint
463 rev 0: checkpoint
464 patch queue now empty
464 patch queue now empty
465 main repo:
465 main repo:
466 rev 0: add foo
466 rev 0: add foo
467 patch repo:
467 patch repo:
468 rev 0: checkpoint
468 rev 0: checkpoint
469 updating working directory
469 updating working directory
470 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
470 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
471 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
471 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
472 main repo:
472 main repo:
473 rev 0: add foo
473 rev 0: add foo
474 patch repo:
474 patch repo:
475 rev 0: checkpoint
475 rev 0: checkpoint
476 % test applying on an empty file (issue 1033)
476 % test applying on an empty file (issue 1033)
477 adding a
477 adding a
478 patch queue now empty
478 patch queue now empty
479 applying changea
479 applying changea
480 now at: changea
480 now at: changea
481 % test qpush with --force, issue1087
481 % test qpush with --force, issue1087
482 adding bye.txt
482 adding bye.txt
483 adding hello.txt
483 adding hello.txt
484 patch queue now empty
484 patch queue now empty
485 % qpush should fail, local changes
485 % qpush should fail, local changes
486 abort: local changes found, refresh first
486 abort: local changes found, refresh first
487 % apply force, should not discard changes with empty patch
487 % apply force, should not discard changes with empty patch
488 applying empty
488 applying empty
489 patch empty is empty
489 patch empty is empty
490 now at: empty
490 now at: empty
491 diff -r bf5fc3f07a0a hello.txt
491 diff -r bf5fc3f07a0a hello.txt
492 --- a/hello.txt
492 --- a/hello.txt
493 +++ b/hello.txt
493 +++ b/hello.txt
494 @@ -1,1 +1,2 @@
494 @@ -1,1 +1,2 @@
495 hello
495 hello
496 +world
496 +world
497 diff -r 9ecee4f634e3 hello.txt
497 diff -r 9ecee4f634e3 hello.txt
498 --- a/hello.txt
498 --- a/hello.txt
499 +++ b/hello.txt
499 +++ b/hello.txt
500 @@ -1,1 +1,2 @@
500 @@ -1,1 +1,2 @@
501 hello
501 hello
502 +world
502 +world
503 changeset: 1:bf5fc3f07a0a
503 changeset: 1:bf5fc3f07a0a
504 tag: qtip
504 tag: qtip
505 tag: tip
505 tag: tip
506 tag: empty
506 tag: empty
507 tag: qbase
507 tag: qbase
508 user: test
508 user: test
509 date: Thu Jan 01 00:00:00 1970 +0000
509 date: Thu Jan 01 00:00:00 1970 +0000
510 summary: imported patch empty
510 summary: imported patch empty
511
511
512
512
513 patch queue now empty
513 patch queue now empty
514 % qpush should fail, local changes
514 % qpush should fail, local changes
515 abort: local changes found, refresh first
515 abort: local changes found, refresh first
516 % apply force, should discard changes in hello, but not bye
516 % apply force, should discard changes in hello, but not bye
517 applying empty
517 applying empty
518 now at: empty
518 now at: empty
519 M bye.txt
519 M bye.txt
520 diff -r ba252371dbc1 bye.txt
520 diff -r ba252371dbc1 bye.txt
521 --- a/bye.txt
521 --- a/bye.txt
522 +++ b/bye.txt
522 +++ b/bye.txt
523 @@ -1,1 +1,2 @@
523 @@ -1,1 +1,2 @@
524 bye
524 bye
525 +universe
525 +universe
526 diff -r 9ecee4f634e3 bye.txt
526 diff -r 9ecee4f634e3 bye.txt
527 --- a/bye.txt
527 --- a/bye.txt
528 +++ b/bye.txt
528 +++ b/bye.txt
529 @@ -1,1 +1,2 @@
529 @@ -1,1 +1,2 @@
530 bye
530 bye
531 +universe
531 +universe
532 diff -r 9ecee4f634e3 hello.txt
532 diff -r 9ecee4f634e3 hello.txt
533 --- a/hello.txt
533 --- a/hello.txt
534 +++ b/hello.txt
534 +++ b/hello.txt
535 @@ -1,1 +1,3 @@
535 @@ -1,1 +1,3 @@
536 hello
536 hello
537 +world
537 +world
538 +universe
538 +universe
539 % test popping revisions not in working dir ancestry
539 % test popping revisions not in working dir ancestry
540 0 A empty
540 0 A empty
541 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
541 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
542 patch queue now empty
542 patch queue now empty
General Comments 0
You need to be logged in to leave comments. Login now